swim protocol in ocaml interoperable with membership lib and serf cli
at main 4.5 kB view raw
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