extc.ml 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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 getch : bool -> int = "sys_getch"
  44. external filetime : string -> float = "sys_filetime"
  45. (* support for backward compatibility *)
  46. let zlib_deflate_init lvl = zlib_deflate_init2 lvl 15
  47. let zlib_inflate_init() = zlib_inflate_init2 15
  48. let executable_path() =
  49. let p = _executable_path Sys.argv.(0) in
  50. let p1 = (try String.rindex p '/' with Not_found -> String.length p + 1) in
  51. let p2 = (try String.rindex p '\\' with Not_found -> String.length p + 1) in
  52. match min p1 p2 with
  53. | x when x = String.length p + 1 -> ""
  54. | pos ->
  55. String.sub p 0 pos ^ "/"
  56. let zlib_op op z str =
  57. let bufsize = 1 lsl 14 in
  58. let tmp = Bytes.create bufsize in
  59. let total = ref 0 in
  60. let rec loop pos len acc =
  61. 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
  62. total := !total + r.z_wrote;
  63. let acc = Bytes.sub tmp 0 r.z_wrote :: acc in
  64. if r.z_finish then
  65. acc
  66. else
  67. loop (pos + r.z_read) (len - r.z_read) acc
  68. in
  69. let strings = loop 0 (String.length str) [] in
  70. let big = Bytes.create !total in
  71. ignore(List.fold_left (fun p s ->
  72. let l = Bytes.length s in
  73. let p = p - l in
  74. Bytes.unsafe_blit s 0 big p l;
  75. p
  76. ) !total strings);
  77. Bytes.unsafe_to_string big
  78. let zip str =
  79. let z = zlib_deflate_init 9 in
  80. let s = zlib_op zlib_deflate z str in
  81. zlib_deflate_end z;
  82. s
  83. let unzip str =
  84. let z = zlib_inflate_init() in
  85. let s = zlib_op zlib_inflate z str in
  86. zlib_inflate_end z;
  87. s
  88. let input_zip ?(bufsize=65536) ch =
  89. let tmp_out = Bytes.create bufsize in
  90. let tmp_in = Bytes.create bufsize in
  91. let tmp_buf = Buffer.create bufsize in
  92. let buf = ref "" in
  93. let p = ref 0 in
  94. let z = zlib_inflate_init() in
  95. let fill_buffer() =
  96. let rec loop pos len =
  97. if len > 0 || pos = 0 then begin
  98. 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
  99. Buffer.add_subbytes tmp_buf tmp_out 0 r.z_wrote;
  100. loop (pos + r.z_read) (len - r.z_read);
  101. end
  102. in
  103. loop 0 (IO.input ch tmp_in 0 bufsize);
  104. p := 0;
  105. buf := Buffer.contents tmp_buf;
  106. Buffer.clear tmp_buf;
  107. in
  108. let read() =
  109. if !p = String.length !buf then fill_buffer();
  110. let c = String.unsafe_get !buf !p in
  111. incr p;
  112. c
  113. in
  114. let rec input str pos len =
  115. let b = String.length !buf - !p in
  116. if b >= len then begin
  117. String.blit !buf !p str pos len;
  118. p := !p + len;
  119. len;
  120. end else begin
  121. String.blit !buf !p str pos b;
  122. fill_buffer();
  123. if !p = String.length !buf then
  124. b
  125. else
  126. b + input str (pos + b) (len - b)
  127. end;
  128. in
  129. let close() =
  130. zlib_inflate_end z
  131. in
  132. IO.create_in ~read ~input ~close
  133. let output_zip ?(bufsize=65536) ?(level=9) ch =
  134. let z = zlib_deflate_init level in
  135. let out = Bytes.create bufsize in
  136. let tmp_out = Bytes.create bufsize in
  137. let p = ref 0 in
  138. let rec flush finish =
  139. 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
  140. ignore(IO.really_output ch tmp_out 0 r.z_wrote);
  141. let remain = !p - r.z_read in
  142. Bytes.blit out r.z_read out 0 remain;
  143. p := remain;
  144. if finish && not r.z_finish then flush true
  145. in
  146. let write c =
  147. if !p = bufsize then flush false;
  148. Bytes.unsafe_set out !p c;
  149. incr p
  150. in
  151. let rec output str pos len =
  152. let b = bufsize - !p in
  153. if len <= b then begin
  154. Bytes.blit str pos out !p len;
  155. p := !p + len;
  156. len
  157. end else begin
  158. Bytes.blit str pos out !p b;
  159. p := !p + b;
  160. flush false;
  161. b + output str (pos + b) (len - b);
  162. end;
  163. in
  164. let close() =
  165. flush true;
  166. zlib_deflate_end z
  167. in
  168. IO.create_out ~write ~output ~flush:(fun() -> flush false; IO.flush ch) ~close