Bitlevel streams for OCaml
at main 221 lines 11 kB view raw
1(** Tests for Bitstream library *) 2 3let test_forward_reader_bytes () = 4 let data = Bytes.of_string "\x12\x34\x56\x78" in 5 let r = Bitstream.Forward_reader.of_bytes data in 6 Alcotest.(check int) "byte 0" 0x12 (Bitstream.Forward_reader.read_byte r); 7 Alcotest.(check int) "byte 1" 0x34 (Bitstream.Forward_reader.read_byte r); 8 Alcotest.(check int) "byte 2" 0x56 (Bitstream.Forward_reader.read_byte r); 9 Alcotest.(check int) "byte 3" 0x78 (Bitstream.Forward_reader.read_byte r); 10 (* Reading past end raises End_of_stream *) 11 Alcotest.check_raises "past end" Bitstream.End_of_stream (fun () -> 12 ignore (Bitstream.Forward_reader.read_byte r)) 13 14let test_forward_reader_bits () = 15 (* 0x12 = 0001_0010, 0x34 = 0011_0100 in little-endian bits: 16 Reading 4 bits: 0010 = 2 17 Reading 4 bits: 0001 = 1 18 Reading 8 bits: 0011_0100 = 0x34 *) 19 let data = Bytes.of_string "\x12\x34" in 20 let r = Bitstream.Forward_reader.of_bytes data in 21 Alcotest.(check int) "4 bits" 0x2 (Bitstream.Forward_reader.read_bits r 4); 22 Alcotest.(check int) "4 bits" 0x1 (Bitstream.Forward_reader.read_bits r 4); 23 Alcotest.(check int) "8 bits" 0x34 (Bitstream.Forward_reader.read_bits r 8) 24 25let test_forward_reader_mixed () = 26 let data = Bytes.of_string "\xFF\x00\xAB\xCD" in 27 let r = Bitstream.Forward_reader.of_bytes data in 28 Alcotest.(check int) "16 bits LE" 0x00FF (Bitstream.Forward_reader.read_bits r 16); 29 Alcotest.(check int) "remaining bits" 16 (Bitstream.Forward_reader.remaining r); 30 Alcotest.(check int) "remaining bytes" 2 (Bitstream.Forward_reader.remaining_bytes r); 31 let rest = Bitstream.Forward_reader.get_bytes r 2 in 32 Alcotest.(check string) "get_bytes" "\xAB\xCD" (Bytes.to_string rest) 33 34let test_forward_reader_32bit () = 35 let data = Bytes.of_string "\x78\x56\x34\x12" in 36 let r = Bitstream.Forward_reader.of_bytes data in 37 Alcotest.(check int) "32 bits LE" 0x12345678 (Bitstream.Forward_reader.read_bits r 32) 38 39let test_forward_reader_rewind () = 40 let data = Bytes.of_string "\x12\x34\x56" in 41 let r = Bitstream.Forward_reader.of_bytes data in 42 let v1 = Bitstream.Forward_reader.read_bits r 12 in 43 Alcotest.(check int) "first read 12 bits" 0x412 v1; (* 0x12 + lower 4 of 0x34 *) 44 Bitstream.Forward_reader.rewind_bits r 4; 45 Alcotest.(check int) "remaining after rewind" 16 (Bitstream.Forward_reader.remaining r); 46 let v2 = Bitstream.Forward_reader.read_bits r 8 in 47 Alcotest.(check int) "read after rewind" 0x34 v2 (* now at byte 1 *) 48 49let test_forward_reader_align () = 50 let data = Bytes.of_string "\xFF\xAA\xBB" in 51 let r = Bitstream.Forward_reader.of_bytes data in 52 let _ = Bitstream.Forward_reader.read_bits r 3 in 53 Alcotest.(check bool) "not aligned" false (Bitstream.Forward_reader.is_byte_aligned r); 54 Bitstream.Forward_reader.align r; 55 Alcotest.(check bool) "aligned" true (Bitstream.Forward_reader.is_byte_aligned r); 56 Alcotest.(check int) "after align" 0xAA (Bitstream.Forward_reader.read_byte r) 57 58let test_forward_reader_sub () = 59 let data = Bytes.of_string "\x12\x34\x56\x78\x9A" in 60 let r = Bitstream.Forward_reader.of_bytes data in 61 let _ = Bitstream.Forward_reader.read_byte r in 62 let sub = Bitstream.Forward_reader.sub r 2 in 63 Alcotest.(check int) "sub byte 0" 0x34 (Bitstream.Forward_reader.read_byte sub); 64 Alcotest.(check int) "sub byte 1" 0x56 (Bitstream.Forward_reader.read_byte sub); 65 Alcotest.(check int) "parent continues" 0x78 (Bitstream.Forward_reader.read_byte r) 66 67let test_forward_writer_bytes () = 68 let buf = Bytes.create 8 in 69 let w = Bitstream.Forward_writer.of_bytes buf in 70 Bitstream.Forward_writer.write_byte w 0x12; 71 Bitstream.Forward_writer.write_byte w 0x34; 72 let len = Bitstream.Forward_writer.finalize w in 73 Alcotest.(check int) "length" 2 len; 74 Alcotest.(check int) "byte 0" 0x12 (Bytes.get_uint8 buf 0); 75 Alcotest.(check int) "byte 1" 0x34 (Bytes.get_uint8 buf 1) 76 77let test_forward_writer_bits () = 78 let buf = Bytes.create 8 in 79 let w = Bitstream.Forward_writer.of_bytes buf in 80 Bitstream.Forward_writer.write_bits w 0x2 4; (* lower 4 bits *) 81 Bitstream.Forward_writer.write_bits w 0x1 4; (* upper 4 bits *) 82 Bitstream.Forward_writer.write_bits w 0x34 8; 83 let len = Bitstream.Forward_writer.finalize w in 84 Alcotest.(check int) "length" 2 len; 85 Alcotest.(check int) "byte 0" 0x12 (Bytes.get_uint8 buf 0); 86 Alcotest.(check int) "byte 1" 0x34 (Bytes.get_uint8 buf 1) 87 88let test_forward_writer_32bit () = 89 let buf = Bytes.create 8 in 90 let w = Bitstream.Forward_writer.of_bytes buf in 91 Bitstream.Forward_writer.write_bits w 0x12345678 32; 92 let len = Bitstream.Forward_writer.finalize w in 93 Alcotest.(check int) "length" 4 len; 94 Alcotest.(check int) "byte 0" 0x78 (Bytes.get_uint8 buf 0); 95 Alcotest.(check int) "byte 1" 0x56 (Bytes.get_uint8 buf 1); 96 Alcotest.(check int) "byte 2" 0x34 (Bytes.get_uint8 buf 2); 97 Alcotest.(check int) "byte 3" 0x12 (Bytes.get_uint8 buf 3) 98 99let test_forward_roundtrip () = 100 (* Write various bit patterns, then read them back *) 101 let buf = Bytes.create 64 in 102 let w = Bitstream.Forward_writer.of_bytes buf in 103 Bitstream.Forward_writer.write_bits w 0b101 3; 104 Bitstream.Forward_writer.write_bits w 0b11001 5; 105 Bitstream.Forward_writer.write_bits w 0xABCD 16; 106 Bitstream.Forward_writer.write_bits w 0b1111 4; 107 let len = Bitstream.Forward_writer.finalize w in 108 109 let r = Bitstream.Forward_reader.create buf ~pos:0 ~len in 110 Alcotest.(check int) "3 bits" 0b101 (Bitstream.Forward_reader.read_bits r 3); 111 Alcotest.(check int) "5 bits" 0b11001 (Bitstream.Forward_reader.read_bits r 5); 112 Alcotest.(check int) "16 bits" 0xABCD (Bitstream.Forward_reader.read_bits r 16); 113 Alcotest.(check int) "4 bits" 0b1111 (Bitstream.Forward_reader.read_bits r 4) 114 115let test_backward_roundtrip () = 116 (* Backward streams are read in REVERSE order of writing. 117 This matches FSE: encode in reverse order, decode in forward order. *) 118 let w = Bitstream.Backward_writer.create 64 in 119 Bitstream.Backward_writer.write_bits w 0b101 3; 120 Bitstream.Backward_writer.write_bits w 0b11001 5; 121 Bitstream.Backward_writer.write_bits w 0xAB 8; 122 let data = Bitstream.Backward_writer.finalize w in 123 124 let r = Bitstream.Backward_reader.of_bytes data ~pos:0 ~len:(Bytes.length data) in 125 (* Read in REVERSE order (last written = first read) *) 126 Alcotest.(check int) "8 bits (written last)" 0xAB (Bitstream.Backward_reader.read_bits r 8); 127 Alcotest.(check int) "5 bits" 0b11001 (Bitstream.Backward_reader.read_bits r 5); 128 Alcotest.(check int) "3 bits (written first)" 0b101 (Bitstream.Backward_reader.read_bits r 3) 129 130let test_backward_reader_peek () = 131 (* For backward streams, bits are read from MSB to LSB within accumulated data. 132 0x5A = 0101_1010 binary. Reading 4 bits at a time gives: 133 - First 4 bits (high nibble): 0101 = 0x5 134 - Last 4 bits (low nibble): 1010 = 0xA *) 135 let w = Bitstream.Backward_writer.create 64 in 136 Bitstream.Backward_writer.write_bits w 0x5A 8; 137 let data = Bitstream.Backward_writer.finalize w in 138 139 let r = Bitstream.Backward_reader.of_bytes data ~pos:0 ~len:(Bytes.length data) in 140 Alcotest.(check int) "peek 4 (high nibble)" 0x5 (Bitstream.Backward_reader.peek_bits r 4); 141 Alcotest.(check int) "peek 4 again" 0x5 (Bitstream.Backward_reader.peek_bits r 4); 142 Alcotest.(check int) "read 4" 0x5 (Bitstream.Backward_reader.read_bits r 4); 143 Alcotest.(check int) "read 4 (low nibble)" 0xA (Bitstream.Backward_reader.read_bits r 4) 144 145let test_backward_is_empty () = 146 let w = Bitstream.Backward_writer.create 64 in 147 Bitstream.Backward_writer.write_bits w 0xFF 8; 148 let data = Bitstream.Backward_writer.finalize w in 149 150 let r = Bitstream.Backward_reader.of_bytes data ~pos:0 ~len:(Bytes.length data) in 151 Alcotest.(check bool) "not empty" false (Bitstream.Backward_reader.is_empty r); 152 let _ = Bitstream.Backward_reader.read_bits r 8 in 153 Alcotest.(check bool) "empty after read" true (Bitstream.Backward_reader.is_empty r) 154 155let test_backward_empty_stream () = 156 (* Empty stream should raise End_of_stream *) 157 Alcotest.check_raises "empty stream" Bitstream.End_of_stream (fun () -> 158 ignore (Bitstream.Backward_reader.of_bytes (Bytes.empty) ~pos:0 ~len:0)) 159 160let test_backward_invalid_padding () = 161 (* Zero byte has no padding marker - should raise *) 162 let data = Bytes.of_string "\x00" in 163 Alcotest.check_raises "zero padding" (Bitstream.Corrupted_stream "invalid padding marker") (fun () -> 164 ignore (Bitstream.Backward_reader.of_bytes data ~pos:0 ~len:1)) 165 166let test_edge_cases () = 167 (* Zero bits *) 168 let buf = Bytes.create 8 in 169 let w = Bitstream.Forward_writer.of_bytes buf in 170 Bitstream.Forward_writer.write_bits w 0 0; 171 let len = Bitstream.Forward_writer.finalize w in 172 Alcotest.(check int) "zero bits" 0 len; 173 174 (* Read zero bits *) 175 let data = Bytes.of_string "\xFF" in 176 let r = Bitstream.Forward_reader.of_bytes data in 177 Alcotest.(check int) "read 0 bits" 0 (Bitstream.Forward_reader.read_bits r 0); 178 Alcotest.(check int) "byte still available" 0xFF (Bitstream.Forward_reader.read_byte r) 179 180let test_not_aligned_errors () = 181 let data = Bytes.of_string "\xFF\xAA" in 182 let r = Bitstream.Forward_reader.of_bytes data in 183 let _ = Bitstream.Forward_reader.read_bits r 3 in 184 185 Alcotest.check_raises "read_byte not aligned" 186 (Bitstream.Invalid_state "read_byte: not byte aligned") 187 (fun () -> ignore (Bitstream.Forward_reader.read_byte r)); 188 189 Alcotest.check_raises "byte_position not aligned" 190 (Bitstream.Invalid_state "byte_position: not byte aligned") 191 (fun () -> ignore (Bitstream.Forward_reader.byte_position r)); 192 193 Alcotest.check_raises "remaining_bytes not aligned" 194 (Bitstream.Invalid_state "remaining_bytes: not byte aligned") 195 (fun () -> ignore (Bitstream.Forward_reader.remaining_bytes r)) 196 197let tests = [ 198 "forward reader bytes", `Quick, test_forward_reader_bytes; 199 "forward reader bits", `Quick, test_forward_reader_bits; 200 "forward reader mixed", `Quick, test_forward_reader_mixed; 201 "forward reader 32bit", `Quick, test_forward_reader_32bit; 202 "forward reader rewind", `Quick, test_forward_reader_rewind; 203 "forward reader align", `Quick, test_forward_reader_align; 204 "forward reader sub", `Quick, test_forward_reader_sub; 205 "forward writer bytes", `Quick, test_forward_writer_bytes; 206 "forward writer bits", `Quick, test_forward_writer_bits; 207 "forward writer 32bit", `Quick, test_forward_writer_32bit; 208 "forward roundtrip", `Quick, test_forward_roundtrip; 209 "backward roundtrip", `Quick, test_backward_roundtrip; 210 "backward reader peek", `Quick, test_backward_reader_peek; 211 "backward is_empty", `Quick, test_backward_is_empty; 212 "backward empty stream", `Quick, test_backward_empty_stream; 213 "backward invalid padding", `Quick, test_backward_invalid_padding; 214 "edge cases", `Quick, test_edge_cases; 215 "not aligned errors", `Quick, test_not_aligned_errors; 216] 217 218let () = 219 Alcotest.run "Bitstream" [ 220 "bitstream", tests; 221 ]