diff options
author | polwex <polwex@sortug.com> | 2025-10-06 12:02:02 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-10-06 12:02:02 +0700 |
commit | 0ca55c93a7c21f81c8f21048889d1c9608a961c7 (patch) | |
tree | 5e43c94c6cae1f789b8892be737a38b10eddde83 /ocaml/lib/bitstream.ml | |
parent | 2d6c3bab18cf5063246fcdb869ae36132bbfe3fc (diff) |
pretty good actuallycodex
Diffstat (limited to 'ocaml/lib/bitstream.ml')
-rw-r--r-- | ocaml/lib/bitstream.ml | 66 |
1 files changed, 65 insertions, 1 deletions
diff --git a/ocaml/lib/bitstream.ml b/ocaml/lib/bitstream.ml index 39bfd6a..e758c2a 100644 --- a/ocaml/lib/bitstream.ml +++ b/ocaml/lib/bitstream.ml @@ -67,6 +67,18 @@ let reader_create buf = len = Bytes.length buf * 8; } +(* Lookup table for trailing zero counts within a byte. Value for 0 is 8 so + callers can detect the "no one-bit present" case. *) +let trailing_zeros = + let tbl = Array.make 256 8 in + for i = 1 to 255 do + let rec count n value = + if value land 1 = 1 then n else count (n + 1) (value lsr 1) + in + tbl.(i) <- count 0 i + done; + tbl + (** Read a single bit *) let read_bit r = if r.bit_pos >= r.len then @@ -80,7 +92,36 @@ let read_bit r = (** Read multiple bits as a Z.t - optimized for bulk reads *) let read_bits r nbits = if nbits = 0 then Z.zero - else if nbits <= 64 && (r.bit_pos mod 8 = 0) && nbits mod 8 = 0 then begin + else if nbits > 4096 then begin + (* Bulk path: copy bytes then convert to Z. *) + let byte_len = (nbits + 7) / 8 in + let buf = Bytes.make byte_len '\x00' in + let bits_done = ref 0 in + + while !bits_done < nbits do + if (!bits_done land 7) = 0 && (r.bit_pos land 7) = 0 then begin + let rem_bits = nbits - !bits_done in + let bytes_to_copy = rem_bits / 8 in + if bytes_to_copy > 0 then begin + Bytes.blit r.buf (r.bit_pos / 8) buf (!bits_done / 8) bytes_to_copy; + r.bit_pos <- r.bit_pos + (bytes_to_copy * 8); + bits_done := !bits_done + (bytes_to_copy * 8) + end + end; + + if !bits_done < nbits then begin + if read_bit r then begin + let byte_idx = !bits_done / 8 in + let bit_idx = !bits_done mod 8 in + let existing = Bytes.get_uint8 buf byte_idx in + Bytes.set_uint8 buf byte_idx (existing lor (1 lsl bit_idx)) + end; + incr bits_done + end + done; + + Z.of_bits (Bytes.unsafe_to_string buf) + end else if nbits <= 64 && (r.bit_pos mod 8 = 0) && nbits mod 8 = 0 then begin (* Fast path: byte-aligned, <= 8 bytes *) let byte_pos = r.bit_pos / 8 in let num_bytes = nbits / 8 in @@ -144,3 +185,26 @@ let reader_pos r = r.bit_pos (** Check if at end of stream *) let reader_at_end r = r.bit_pos >= r.len + +let count_zero_bits_until_one r = + let buf = r.buf in + let len_bits = r.len in + let rec scan count bit_pos = + if bit_pos >= len_bits then + raise (Invalid_argument "count_zero_bits_until_one: end of stream") + else begin + let byte_idx = bit_pos lsr 3 in + let bit_off = bit_pos land 7 in + let byte = Bytes.get_uint8 buf byte_idx in + let masked = byte lsr bit_off in + if masked <> 0 then begin + let tz = trailing_zeros.(masked land 0xff) in + let zeros = count + tz in + r.bit_pos <- bit_pos + tz + 1; (* skip zeros and the terminating 1 bit *) + zeros + end else + let remaining = 8 - bit_off in + scan (count + remaining) (bit_pos + remaining) + end + in + scan 0 r.bit_pos |