1 (***********************************************************************)
3 (* The CamlZip library *)
5 (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
7 (* Copyright 2001 Institut National de Recherche en Informatique et *)
8 (* en Automatique. All rights reserved. This file is distributed *)
9 (* under the terms of the GNU Library General Public License. *)
11 (***********************************************************************)
15 (* Module [Gzip]: reading and writing to/from [gzip] compressed files *)
17 exception Error
of string
19 let buffer_size = 1024
22 { in_chan
: Pervasives.in_channel
;
25 mutable in_avail
: int;
27 in_stream
: Zlib.stream
;
28 mutable in_size
: int32
;
29 mutable in_crc
: int32
}
32 (* Superficial parsing of header *)
34 let id1 = input_byte ic
in
35 let id2 = input_byte ic
in
36 if id1 <> 0x1F || id2 <> 0x8B then
37 raise
(Error
("bad magic number, not a gzip file"));
38 let cm = input_byte ic
in
40 raise
(Error
("unknown compression method"));
41 let flags = input_byte ic
in
42 if flags land 0xE0 <> 0 then
43 raise
(Error
("bad flags, not a gzip file"));
44 for i
= 1 to 6 do ignore
(input_byte ic
) done;
45 if flags land 0x04 <> 0 then begin
47 let len1 = input_byte ic
in
48 let len2 = input_byte ic
in
49 for i
= 1 to len1 + len2 lsl 8 do ignore
(input_byte ic
) done
51 if flags land 0x08 <> 0 then begin
52 (* Skip original file name *)
53 while input_byte ic
<> 0 do () done
55 if flags land 0x10 <> 0 then begin
57 while input_byte ic
<> 0 do () done
59 if flags land 0x02 <> 0 then begin
61 ignore
(input_byte ic
); ignore
(input_byte ic
)
64 raise
(Error
("premature end of file, not a gzip file"))
67 in_buffer
= String.create
buffer_size;
71 in_stream
= Zlib.inflate_init
false;
75 let open_in filename
=
76 let ic = Pervasives.open_in_bin filename
in
79 with e
-> Pervasives.close_in
ic; raise e
82 if iz
.in_avail
= 0 then begin
83 let n = Pervasives.input iz
.in_chan iz
.in_buffer
0
84 (String.length iz
.in_buffer
) in
85 if n = 0 then raise End_of_file
;
89 let c = iz
.in_buffer
.[iz
.in_pos
] in
90 iz
.in_pos
<- iz
.in_pos
+ 1;
91 iz
.in_avail
<- iz
.in_avail
- 1;
95 let b1 = read_byte iz
in
96 let b2 = read_byte iz
in
97 let b3 = read_byte iz
in
98 let b4 = read_byte iz
in
99 Int32.logor
(Int32.of_int
b1)
100 (Int32.logor
(Int32.shift_left
(Int32.of_int
b2) 8)
101 (Int32.logor
(Int32.shift_left
(Int32.of_int
b3) 16)
102 (Int32.shift_left
(Int32.of_int
b4) 24)))
104 let rec input iz buf pos len
=
105 if pos
< 0 || len
< 0 || pos
+ len
> String.length buf
then
106 invalid_arg
"Gzip.input";
107 if iz
.in_eof
then 0 else begin
108 if iz
.in_avail
= 0 then begin
109 let n = Pervasives.input iz
.in_chan iz
.in_buffer
0
110 (String.length iz
.in_buffer
) in
111 if n = 0 then raise
(Error
("truncated file"));
115 let (finished
, used_in
, used_out
) =
117 Zlib.inflate iz
.in_stream iz
.in_buffer iz
.in_pos iz
.in_avail
118 buf pos len
Zlib.Z_SYNC_FLUSH
119 with Zlib.Error
(_
, _
) ->
120 raise
(Error
("error during decompression")) in
121 iz
.in_pos
<- iz
.in_pos
+ used_in
;
122 iz
.in_avail
<- iz
.in_avail
- used_in
;
123 iz
.in_crc
<- Zlib.update_crc iz
.in_crc buf pos used_out
;
124 iz
.in_size
<- Int32.add iz
.in_size
(Int32.of_int used_out
);
125 if finished
then begin
127 let crc = read_int32 iz
in
128 let size = read_int32 iz
in
129 if iz
.in_crc
<> crc then
130 raise
(Error
("CRC mismatch, data corrupted"));
131 if iz
.in_size
<> size then
132 raise
(Error
("size mismatch, data corrupted"));
136 raise
(Error
("truncated file"))
138 else if used_out
= 0 then
144 let rec really_input iz buf pos len
=
145 if len
<= 0 then () else begin
146 let n = input iz buf pos len
in
147 if n = 0 then raise End_of_file
;
148 really_input iz buf
(pos
+ n) (len
- n)
151 let char_buffer = String.create
1
154 if input iz
char_buffer 0 1 = 0 then raise End_of_file
else char_buffer.[0]
157 Char.code
(input_char iz
)
161 Zlib.inflate_end iz
.in_stream
165 Pervasives.close_in iz
.in_chan
168 { out_chan
: Pervasives.out_channel
;
170 mutable out_pos
: int;
171 mutable out_avail
: int;
172 out_stream
: Zlib.stream
;
173 mutable out_size
: int32
;
174 mutable out_crc
: int32
}
176 let open_out_chan ?
(level
= 6) oc
=
177 if level
< 1 || level
> 9 then invalid_arg
"Gzip.open_out: bad level";
178 (* Write minimal header *)
179 output_byte oc
0x1F; (* ID1 *)
180 output_byte oc
0x8B; (* ID2 *)
181 output_byte oc
8; (* compression method *)
182 output_byte oc
0; (* flags *)
183 for i
= 1 to 4 do output_byte oc
0 done; (* mtime *)
184 output_byte oc
0; (* xflags *)
185 output_byte oc
0xFF; (* OS (unknown) *)
187 out_buffer
= String.create
buffer_size;
189 out_avail
= buffer_size;
190 out_stream
= Zlib.deflate_init level
false;
191 out_size
= Int32.zero
;
192 out_crc
= Int32.zero
}
194 let open_out ?
(level
= 6) filename
=
195 open_out_chan ~level
(Pervasives.open_out_bin filename
)
197 let rec output oz buf pos len
=
198 if pos
< 0 || len
< 0 || pos
+ len
> String.length buf
then
199 invalid_arg
"Gzip.output";
200 (* If output buffer is full, flush it *)
201 if oz
.out_avail
= 0 then begin
202 Pervasives.output oz
.out_chan oz
.out_buffer
0 oz
.out_pos
;
204 oz
.out_avail
<- String.length oz
.out_buffer
206 let (_
, used_in
, used_out
) =
208 Zlib.deflate oz
.out_stream buf pos len
209 oz
.out_buffer oz
.out_pos oz
.out_avail
211 with Zlib.Error
(_
, _
) ->
212 raise
(Error
("error during compression")) in
213 oz
.out_pos
<- oz
.out_pos
+ used_out
;
214 oz
.out_avail
<- oz
.out_avail
- used_out
;
215 oz
.out_size
<- Int32.add oz
.out_size
(Int32.of_int used_in
);
216 oz
.out_crc
<- Zlib.update_crc oz
.out_crc buf pos used_in
;
217 if used_in
< len
then output oz buf
(pos
+ used_in
) (len
- used_in
)
219 let output_char oz
c =
220 char_buffer.[0] <- c;
221 output oz
char_buffer 0 1
223 let output_byte oz b
=
224 output_char oz
(Char.unsafe_chr b
)
226 let write_int32 oc
n =
229 Pervasives.output_byte oc
(Int32.to_int
!r);
230 r := Int32.shift_right_logical
!r 8
234 let rec do_flush () =
235 (* If output buffer is full, flush it *)
236 if oz
.out_avail
= 0 then begin
237 Pervasives.output oz
.out_chan oz
.out_buffer
0 oz
.out_pos
;
239 oz
.out_avail
<- String.length oz
.out_buffer
241 let (finished
, _
, used_out
) =
242 Zlib.deflate oz
.out_stream oz
.out_buffer
0 0
243 oz
.out_buffer oz
.out_pos oz
.out_avail
245 oz
.out_pos
<- oz
.out_pos
+ used_out
;
246 oz
.out_avail
<- oz
.out_avail
- used_out
;
247 if not finished
then do_flush() in
249 (* Final data flush *)
250 if oz
.out_pos
> 0 then
251 Pervasives.output oz
.out_chan oz
.out_buffer
0 oz
.out_pos
;
252 (* Write CRC and size *)
253 write_int32 oz
.out_chan oz
.out_crc
;
254 write_int32 oz
.out_chan oz
.out_size
;
255 (* Dispose of stream *)
256 Zlib.deflate_end oz
.out_stream
260 Pervasives.close_out oz
.out_chan