swim protocol in ocaml interoperable with membership lib and serf cli
1type order = LSB | MSB
2type error = Invalid_code of int | Unexpected_eof | Buffer_overflow
3
4let error_to_string = function
5 | Invalid_code c -> Printf.sprintf "invalid LZW code: %d" c
6 | Unexpected_eof -> "unexpected end of compressed data"
7 | Buffer_overflow -> "decompressed data too large"
8
9let clear_code = 256
10let eof_code = 257
11let initial_dict_size = 258
12let max_code_bits = 12
13let max_dict_size = 1 lsl max_code_bits
14
15type bit_reader = {
16 data : Cstruct.t;
17 mutable pos : int;
18 mutable bits_buf : int;
19 mutable bits_count : int;
20}
21
22let make_bit_reader data = { data; pos = 0; bits_buf = 0; bits_count = 0 }
23
24let read_bits_lsb reader n =
25 while reader.bits_count < n do
26 if reader.pos >= Cstruct.length reader.data then raise Exit
27 else begin
28 let byte = Cstruct.get_uint8 reader.data reader.pos in
29 reader.bits_buf <- reader.bits_buf lor (byte lsl reader.bits_count);
30 reader.bits_count <- reader.bits_count + 8;
31 reader.pos <- reader.pos + 1
32 end
33 done;
34 let result = reader.bits_buf land ((1 lsl n) - 1) in
35 reader.bits_buf <- reader.bits_buf lsr n;
36 reader.bits_count <- reader.bits_count - n;
37 result
38
39let decompress_to_buffer ~src ~dst =
40 try
41 let reader = make_bit_reader src in
42 let out_pos = ref 0 in
43 let dst_len = Cstruct.length dst in
44
45 let dict = Array.make max_dict_size (Cstruct.empty, 0) in
46 for i = 0 to 255 do
47 dict.(i) <- (Cstruct.of_string (String.make 1 (Char.chr i)), 1)
48 done;
49 dict.(clear_code) <- (Cstruct.empty, 0);
50 dict.(eof_code) <- (Cstruct.empty, 0);
51
52 let dict_size = ref initial_dict_size in
53 let code_bits = ref 9 in
54 let prev_code = ref (-1) in
55
56 let write_entry (entry, len) =
57 if !out_pos + len > dst_len then raise (Failure "overflow");
58 Cstruct.blit entry 0 dst !out_pos len;
59 out_pos := !out_pos + len
60 in
61
62 let add_to_dict first_byte =
63 if !dict_size < max_dict_size && !prev_code >= 0 then begin
64 let prev_entry, prev_len = dict.(!prev_code) in
65 let new_entry = Cstruct.create (prev_len + 1) in
66 Cstruct.blit prev_entry 0 new_entry 0 prev_len;
67 Cstruct.set_uint8 new_entry prev_len first_byte;
68 dict.(!dict_size) <- (new_entry, prev_len + 1);
69 incr dict_size;
70 if !dict_size >= 1 lsl !code_bits && !code_bits < max_code_bits then
71 incr code_bits
72 end
73 in
74
75 let reset_dict () =
76 dict_size := initial_dict_size;
77 code_bits := 9;
78 prev_code := -1
79 in
80
81 let rec decode_loop () =
82 let code = read_bits_lsb reader !code_bits in
83 if code = eof_code then ()
84 else if code = clear_code then begin
85 reset_dict ();
86 decode_loop ()
87 end
88 else begin
89 let entry, len, first_byte =
90 if code < !dict_size then
91 let e, l = dict.(code) in
92 (e, l, Cstruct.get_uint8 e 0)
93 else if code = !dict_size && !prev_code >= 0 then (
94 let prev_entry, prev_len = dict.(!prev_code) in
95 let first = Cstruct.get_uint8 prev_entry 0 in
96 let new_entry = Cstruct.create (prev_len + 1) in
97 Cstruct.blit prev_entry 0 new_entry 0 prev_len;
98 Cstruct.set_uint8 new_entry prev_len first;
99 (new_entry, prev_len + 1, first))
100 else raise (Failure "invalid")
101 in
102 write_entry (entry, len);
103 add_to_dict first_byte;
104 prev_code := code;
105 decode_loop ()
106 end
107 in
108
109 decode_loop ();
110 Ok !out_pos
111 with
112 | Exit -> Error Unexpected_eof
113 | Failure msg when msg = "overflow" -> Error Buffer_overflow
114 | Failure msg when msg = "invalid" -> Error (Invalid_code 0)
115 | _ -> Error (Invalid_code 0)
116
117let decompress_cstruct src =
118 let estimated_size = max (Cstruct.length src * 4) 4096 in
119 let dst = Cstruct.create estimated_size in
120 match decompress_to_buffer ~src ~dst with
121 | Ok len -> Ok (Cstruct.sub dst 0 len)
122 | Error Buffer_overflow -> (
123 let larger = Cstruct.create (estimated_size * 4) in
124 match decompress_to_buffer ~src ~dst:larger with
125 | Ok len -> Ok (Cstruct.sub larger 0 len)
126 | Error e -> Error e)
127 | Error e -> Error e
128
129let decompress ?(order = LSB) ?(lit_width = 8) data =
130 if order <> LSB then Error (Invalid_code 0)
131 else if lit_width <> 8 then Error (Invalid_code 0)
132 else
133 let src = Cstruct.of_string data in
134 match decompress_cstruct src with
135 | Ok cs -> Ok (Cstruct.to_string cs)
136 | Error e -> Error e
137
138let decompress_lsb8 data = decompress ~order:LSB ~lit_width:8 data