extc.ml 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. (*
  2. * Extc : C common OCaml bindings
  3. * Copyright (c)2004 Nicolas Cannasse
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  18. *)
  19. type zstream
  20. type zflush =
  21. | Z_NO_FLUSH
  22. | Z_PARTIAL_FLUSH
  23. | Z_SYNC_FLUSH
  24. | Z_FULL_FLUSH
  25. | Z_FINISH
  26. type zresult = {
  27. z_finish : bool;
  28. z_read : int;
  29. z_wrote : int;
  30. }
  31. external zlib_deflate_init2 : int -> int -> zstream = "zlib_deflate_init2"
  32. external zlib_deflate : zstream -> src:string -> spos:int -> slen:int -> dst:bytes -> dpos:int -> dlen:int -> zflush -> zresult = "zlib_deflate_bytecode" "zlib_deflate"
  33. external zlib_deflate_end : zstream -> unit = "zlib_deflate_end"
  34. external zlib_inflate_init2 : int -> zstream = "zlib_inflate_init"
  35. external zlib_inflate : zstream -> src:string -> spos:int -> slen:int -> dst:bytes -> dpos:int -> dlen:int -> zflush -> zresult = "zlib_inflate_bytecode" "zlib_inflate"
  36. external zlib_inflate_end : zstream -> unit = "zlib_inflate_end"
  37. external _executable_path : string -> string = "executable_path"
  38. external get_full_path : string -> string = "get_full_path"
  39. external get_real_path : string -> string = "get_real_path"
  40. external zlib_deflate_bound : zstream -> int -> int = "zlib_deflate_bound"
  41. external zlib_crc32 : bytes -> int -> int32 = "zlib_crc32"
  42. external time : unit -> float = "sys_time"
  43. external timestamp_ms : unit -> int64 = "sys_timestamp_ms"
  44. external getch : bool -> int = "sys_getch"
  45. external filetime : string -> float = "sys_filetime"
  46. (* support for backward compatibility *)
  47. let zlib_deflate_init lvl = zlib_deflate_init2 lvl 15
  48. let zlib_inflate_init() = zlib_inflate_init2 15
  49. let executable_path() =
  50. let p = _executable_path Sys.argv.(0) in
  51. let p1 = (try String.rindex p '/' with Not_found -> String.length p + 1) in
  52. let p2 = (try String.rindex p '\\' with Not_found -> String.length p + 1) in
  53. match min p1 p2 with
  54. | x when x = String.length p + 1 -> ""
  55. | pos ->
  56. String.sub p 0 pos ^ "/"
  57. let zlib_op op z str =
  58. let bufsize = 1 lsl 14 in
  59. let tmp = Bytes.create bufsize in
  60. let total = ref 0 in
  61. let rec loop pos len acc =
  62. let r = op z ~src:str ~spos:pos ~slen:len ~dst:tmp ~dpos:0 ~dlen:bufsize (if len = 0 then Z_FINISH else Z_SYNC_FLUSH) in
  63. total := !total + r.z_wrote;
  64. let acc = Bytes.sub tmp 0 r.z_wrote :: acc in
  65. if r.z_finish then
  66. acc
  67. else
  68. loop (pos + r.z_read) (len - r.z_read) acc
  69. in
  70. let strings = loop 0 (String.length str) [] in
  71. let big = Bytes.create !total in
  72. ignore(List.fold_left (fun p s ->
  73. let l = Bytes.length s in
  74. let p = p - l in
  75. Bytes.unsafe_blit s 0 big p l;
  76. p
  77. ) !total strings);
  78. Bytes.unsafe_to_string big
  79. let zip str =
  80. let z = zlib_deflate_init 9 in
  81. let s = zlib_op zlib_deflate z str in
  82. zlib_deflate_end z;
  83. s
  84. let unzip str =
  85. let z = zlib_inflate_init() in
  86. let s = zlib_op zlib_inflate z str in
  87. zlib_inflate_end z;
  88. s
  89. let input_zip ?(bufsize=65536) ch =
  90. let tmp_out = Bytes.create bufsize in
  91. let tmp_in = Bytes.create bufsize in
  92. let tmp_buf = Buffer.create bufsize in
  93. let buf = ref "" in
  94. let p = ref 0 in
  95. let z = zlib_inflate_init() in
  96. let fill_buffer() =
  97. let rec loop pos len =
  98. if len > 0 || pos = 0 then begin
  99. let r = zlib_inflate z ~src:(Bytes.unsafe_to_string tmp_in) ~spos:pos ~slen:len ~dst:tmp_out ~dpos:0 ~dlen:bufsize (if pos = 0 && len = 0 then Z_FINISH else Z_SYNC_FLUSH) in
  100. Buffer.add_subbytes tmp_buf tmp_out 0 r.z_wrote;
  101. loop (pos + r.z_read) (len - r.z_read);
  102. end
  103. in
  104. loop 0 (IO.input ch tmp_in 0 bufsize);
  105. p := 0;
  106. buf := Buffer.contents tmp_buf;
  107. Buffer.clear tmp_buf;
  108. in
  109. let read() =
  110. if !p = String.length !buf then fill_buffer();
  111. let c = String.unsafe_get !buf !p in
  112. incr p;
  113. c
  114. in
  115. let rec input str pos len =
  116. let b = String.length !buf - !p in
  117. if b >= len then begin
  118. String.blit !buf !p str pos len;
  119. p := !p + len;
  120. len;
  121. end else begin
  122. String.blit !buf !p str pos b;
  123. fill_buffer();
  124. if !p = String.length !buf then
  125. b
  126. else
  127. b + input str (pos + b) (len - b)
  128. end;
  129. in
  130. let close() =
  131. zlib_inflate_end z
  132. in
  133. IO.create_in ~read ~input ~close
  134. let output_zip ?(bufsize=65536) ?(level=9) ch =
  135. let z = zlib_deflate_init level in
  136. let out = Bytes.create bufsize in
  137. let tmp_out = Bytes.create bufsize in
  138. let p = ref 0 in
  139. let rec flush finish =
  140. let r = zlib_deflate z ~src:(Bytes.unsafe_to_string out) ~spos:0 ~slen:!p ~dst:tmp_out ~dpos:0 ~dlen:bufsize (if finish then Z_FINISH else Z_SYNC_FLUSH) in
  141. ignore(IO.really_output ch tmp_out 0 r.z_wrote);
  142. let remain = !p - r.z_read in
  143. Bytes.blit out r.z_read out 0 remain;
  144. p := remain;
  145. if finish && not r.z_finish then flush true
  146. in
  147. let write c =
  148. if !p = bufsize then flush false;
  149. Bytes.unsafe_set out !p c;
  150. incr p
  151. in
  152. let rec output str pos len =
  153. let b = bufsize - !p in
  154. if len <= b then begin
  155. Bytes.blit str pos out !p len;
  156. p := !p + len;
  157. len
  158. end else begin
  159. Bytes.blit str pos out !p b;
  160. p := !p + b;
  161. flush false;
  162. b + output str (pos + b) (len - b);
  163. end;
  164. in
  165. let close() =
  166. flush true;
  167. zlib_deflate_end z
  168. in
  169. IO.create_out ~write ~output ~flush:(fun() -> flush false; IO.flush ch) ~close