···11-(lang dune 3.21)
22-(name bitstream)
33-44-(generate_opam_files true)
55-66-(license ISC)
77-(authors "Anil Madhavapeddy <anil@recoil.org>")
88-(maintainers "Anil Madhavapeddy <anil@recoil.org>")
99-(source (tangled anil.recoil.org/ocaml-bitstream))
1010-1111-(package
1212- (name bitstream)
1313- (synopsis "Bit-level I/O for binary format parsing and generation")
1414- (description
1515- "Forward and backward bitstream reading/writing for binary formats.
1616-Supports bit-level operations required by compression algorithms like
1717-FSE, ANS, and Huffman coding.")
1818- (depends
1919- (ocaml (>= 5.2.0))
2020- (alcotest (and :with-test (>= 1.7.0)))))
-385
ocaml-bitstream/src/bitstream.ml
···11-(** Bitstream - Bit-level I/O for binary formats.
22-33- Provides forward and backward bitstream reading and writing for parsing
44- and generating binary formats that operate at the bit level.
55-66- Forward streams read/write from the start of a buffer towards the end.
77- Backward streams read/write from the end of a buffer towards the start,
88- which is required by some compression algorithms (FSE, ANS). *)
99-1010-(** {1 Slice Type} *)
1111-1212-module Slice = struct
1313- type t = {
1414- bytes : bytes;
1515- first : int;
1616- length : int;
1717- }
1818-1919- let make bytes ~first ~length =
2020- { bytes; first; length }
2121-2222- let of_bytes ?first ?length bytes =
2323- let first = Option.value first ~default:0 in
2424- let length = Option.value length ~default:(Bytes.length bytes - first) in
2525- { bytes; first; length }
2626-2727- let to_bytes t =
2828- Bytes.sub t.bytes t.first t.length
2929-3030- let is_empty t =
3131- t.length = 0
3232-3333- let sub t ~first ~length =
3434- { bytes = t.bytes; first = t.first + first; length }
3535-end
3636-3737-(** {1 Exceptions} *)
3838-3939-exception End_of_stream
4040-(** Raised when attempting to read past the end of the stream. *)
4141-4242-exception Invalid_state of string
4343-(** Raised when an operation requires a specific state (e.g., byte alignment). *)
4444-4545-exception Corrupted_stream of string
4646-(** Raised when stream data is malformed (e.g., invalid padding marker). *)
4747-4848-(** {1 Forward Bitstream Reader} *)
4949-5050-module Forward_reader = struct
5151- type t = {
5252- src : bytes;
5353- start_pos : int;
5454- limit : int;
5555- mutable byte_pos : int;
5656- mutable bit_pos : int; (* 0-7, bits consumed in current byte *)
5757- }
5858-5959- let of_slice (slice : Slice.t) =
6060- { src = slice.bytes;
6161- start_pos = slice.first;
6262- limit = slice.first + slice.length;
6363- byte_pos = slice.first;
6464- bit_pos = 0 }
6565-6666- let of_bytes src =
6767- of_slice (Slice.of_bytes src)
6868-6969- let create src ~pos ~len =
7070- of_slice (Slice.make src ~first:pos ~length:len)
7171-7272- let[@inline] remaining t =
7373- (t.limit - t.byte_pos) * 8 - t.bit_pos
7474-7575- let[@inline] is_byte_aligned t =
7676- t.bit_pos = 0
7777-7878- let[@inline] read_bits t n =
7979- if n <= 0 then 0
8080- else if n > 57 then invalid_arg "read_bits: n > 57"
8181- else begin
8282- let result = ref 0 in
8383- let bits_read = ref 0 in
8484- while !bits_read < n do
8585- if t.byte_pos >= t.limit then
8686- raise End_of_stream;
8787- let byte = Bytes.get_uint8 t.src t.byte_pos in
8888- let available = 8 - t.bit_pos in
8989- let to_read = min available (n - !bits_read) in
9090- let mask = (1 lsl to_read) - 1 in
9191- let bits = (byte lsr t.bit_pos) land mask in
9292- result := !result lor (bits lsl !bits_read);
9393- bits_read := !bits_read + to_read;
9494- t.bit_pos <- t.bit_pos + to_read;
9595- if t.bit_pos >= 8 then begin
9696- t.bit_pos <- 0;
9797- t.byte_pos <- t.byte_pos + 1
9898- end
9999- done;
100100- !result
101101- end
102102-103103- let[@inline] read_byte t =
104104- if t.bit_pos <> 0 then
105105- raise (Invalid_state "read_byte: not byte aligned");
106106- if t.byte_pos >= t.limit then
107107- raise End_of_stream;
108108- let b = Bytes.get_uint8 t.src t.byte_pos in
109109- t.byte_pos <- t.byte_pos + 1;
110110- b
111111-112112- let rewind_bits t n =
113113- let total_bits = (t.byte_pos - t.start_pos) * 8 + t.bit_pos in
114114- let new_total = total_bits - n in
115115- if new_total < 0 then
116116- raise End_of_stream;
117117- t.byte_pos <- t.start_pos + new_total / 8;
118118- t.bit_pos <- new_total mod 8
119119-120120- let align t =
121121- if t.bit_pos <> 0 then begin
122122- t.bit_pos <- 0;
123123- t.byte_pos <- t.byte_pos + 1
124124- end
125125-126126- let byte_position t =
127127- if t.bit_pos <> 0 then
128128- raise (Invalid_state "byte_position: not byte aligned");
129129- t.byte_pos
130130-131131- let get_slice t n : Slice.t =
132132- if t.bit_pos <> 0 then
133133- raise (Invalid_state "get_slice: not byte aligned");
134134- if t.byte_pos + n > t.limit then
135135- raise End_of_stream;
136136- let result = Slice.make t.src ~first:t.byte_pos ~length:n in
137137- t.byte_pos <- t.byte_pos + n;
138138- result
139139-140140- let get_bytes t n =
141141- Slice.to_bytes (get_slice t n)
142142-143143- let to_slice t : Slice.t =
144144- if t.bit_pos <> 0 then
145145- raise (Invalid_state "to_slice: not byte aligned");
146146- Slice.make t.src ~first:t.byte_pos ~length:(t.limit - t.byte_pos)
147147-148148- let advance t n =
149149- if t.bit_pos <> 0 then
150150- raise (Invalid_state "advance: not byte aligned");
151151- if t.byte_pos + n > t.limit then
152152- raise End_of_stream;
153153- t.byte_pos <- t.byte_pos + n
154154-155155- let sub t n =
156156- if t.bit_pos <> 0 then
157157- raise (Invalid_state "sub: not byte aligned");
158158- if t.byte_pos + n > t.limit then
159159- raise End_of_stream;
160160- let result = of_slice (Slice.make t.src ~first:t.byte_pos ~length:n) in
161161- t.byte_pos <- t.byte_pos + n;
162162- result
163163-164164- let remaining_bytes t =
165165- if t.bit_pos <> 0 then
166166- raise (Invalid_state "remaining_bytes: not byte aligned");
167167- t.limit - t.byte_pos
168168-169169- let skip_bits t n =
170170- ignore (read_bits t n)
171171-end
172172-173173-(** {1 Backward Bitstream Reader}
174174-175175- Reads bits from the end of a buffer towards the start. The stream
176176- starts with a padding marker (highest 1-bit indicates start of data). *)
177177-178178-module Backward_reader = struct
179179- type t = {
180180- src : bytes;
181181- start_pos : int;
182182- mutable bit_offset : int; (* Bits remaining from end, decreasing *)
183183- }
184184-185185- let of_slice (slice : Slice.t) =
186186- if slice.length = 0 then
187187- raise End_of_stream;
188188- let last_byte_pos = slice.first + slice.length - 1 in
189189- let last_byte = Bytes.get_uint8 slice.bytes last_byte_pos in
190190- if last_byte = 0 then
191191- raise (Corrupted_stream "invalid padding marker");
192192- (* Find the highest set bit - this is the padding marker *)
193193- let rec find_marker byte bit =
194194- if bit < 0 then 0
195195- else if (byte land (1 lsl bit)) <> 0 then bit
196196- else find_marker byte (bit - 1)
197197- in
198198- let padding = 8 - find_marker last_byte 7 in
199199- let bit_offset = slice.length * 8 - padding in
200200- { src = slice.bytes; start_pos = slice.first; bit_offset }
201201-202202- let of_bytes src ~pos ~len =
203203- of_slice (Slice.make src ~first:pos ~length:len)
204204-205205- let[@inline] remaining t = t.bit_offset
206206-207207- let[@inline] is_empty t = t.bit_offset <= 0
208208-209209- let[@inline] read_bits t n =
210210- if n <= 0 then 0
211211- else if n > 57 then invalid_arg "read_bits: n > 57"
212212- else begin
213213- t.bit_offset <- t.bit_offset - n;
214214- let actual_offset = max 0 t.bit_offset in
215215- let actual_bits = if t.bit_offset < 0 then n + t.bit_offset else n in
216216- if actual_bits <= 0 then 0
217217- else begin
218218- let byte_offset = t.start_pos + (actual_offset / 8) in
219219- let bit_offset = actual_offset mod 8 in
220220- let result = ref 0 in
221221- let bits_read = ref 0 in
222222- let current_byte = ref byte_offset in
223223- let current_bit = ref bit_offset in
224224- while !bits_read < actual_bits do
225225- let byte = Bytes.get_uint8 t.src !current_byte in
226226- let available = 8 - !current_bit in
227227- let to_read = min available (actual_bits - !bits_read) in
228228- let mask = (1 lsl to_read) - 1 in
229229- let bits = (byte lsr !current_bit) land mask in
230230- result := !result lor (bits lsl !bits_read);
231231- bits_read := !bits_read + to_read;
232232- current_bit := !current_bit + to_read;
233233- if !current_bit >= 8 then begin
234234- current_bit := 0;
235235- incr current_byte
236236- end
237237- done;
238238- (* If we read past the beginning, shift the result *)
239239- if t.bit_offset < 0 then
240240- !result lsl (-t.bit_offset)
241241- else
242242- !result
243243- end
244244- end
245245-246246- let peek_bits t n =
247247- let saved_offset = t.bit_offset in
248248- let result = read_bits t n in
249249- t.bit_offset <- saved_offset;
250250- result
251251-end
252252-253253-(** {1 Forward Bitstream Writer} *)
254254-255255-module Forward_writer = struct
256256- type t = {
257257- dst : bytes;
258258- start_pos : int;
259259- mutable byte_pos : int;
260260- mutable bit_pos : int; (* 0-7, bits written in current byte *)
261261- mutable current_byte : int;
262262- }
263263-264264- let of_slice (slice : Slice.t) =
265265- { dst = slice.bytes;
266266- start_pos = slice.first;
267267- byte_pos = slice.first;
268268- bit_pos = 0;
269269- current_byte = 0 }
270270-271271- let of_bytes dst =
272272- of_slice (Slice.of_bytes dst)
273273-274274- let create dst ~pos =
275275- of_slice (Slice.make dst ~first:pos ~length:(Bytes.length dst - pos))
276276-277277- let flush t =
278278- if t.bit_pos > 0 then begin
279279- Bytes.set_uint8 t.dst t.byte_pos t.current_byte;
280280- t.byte_pos <- t.byte_pos + 1;
281281- t.bit_pos <- 0;
282282- t.current_byte <- 0
283283- end
284284-285285- let[@inline] write_bits t value n =
286286- if n <= 0 then ()
287287- else if n > 57 then invalid_arg "write_bits: n > 57"
288288- else begin
289289- let value = ref value in
290290- let remaining = ref n in
291291-292292- while !remaining > 0 do
293293- let available = 8 - t.bit_pos in
294294- let to_write = min available !remaining in
295295- let mask = (1 lsl to_write) - 1 in
296296- t.current_byte <- t.current_byte lor ((!value land mask) lsl t.bit_pos);
297297- value := !value lsr to_write;
298298- remaining := !remaining - to_write;
299299- t.bit_pos <- t.bit_pos + to_write;
300300-301301- if t.bit_pos = 8 then begin
302302- Bytes.set_uint8 t.dst t.byte_pos t.current_byte;
303303- t.byte_pos <- t.byte_pos + 1;
304304- t.bit_pos <- 0;
305305- t.current_byte <- 0
306306- end
307307- done
308308- end
309309-310310- let write_byte t value =
311311- if t.bit_pos <> 0 then flush t;
312312- Bytes.set_uint8 t.dst t.byte_pos value;
313313- t.byte_pos <- t.byte_pos + 1
314314-315315- let write_slice t (slice : Slice.t) =
316316- if t.bit_pos <> 0 then flush t;
317317- Bytes.blit slice.bytes slice.first t.dst t.byte_pos slice.length;
318318- t.byte_pos <- t.byte_pos + slice.length
319319-320320- let write_bytes t src =
321321- write_slice t (Slice.of_bytes src)
322322-323323- let byte_position t =
324324- if t.bit_pos > 0 then t.byte_pos + 1 else t.byte_pos
325325-326326- let finalize t =
327327- flush t;
328328- t.byte_pos - t.start_pos
329329-330330- let to_slice t : Slice.t =
331331- flush t;
332332- Slice.make t.dst ~first:t.start_pos ~length:(t.byte_pos - t.start_pos)
333333-end
334334-335335-(** {1 Backward Bitstream Writer}
336336-337337- Accumulates bits to be read backwards. Used for FSE and Huffman encoding. *)
338338-339339-module Backward_writer = struct
340340- type t = {
341341- mutable bits : int64;
342342- mutable num_bits : int;
343343- buffer : bytes;
344344- mutable buf_pos : int;
345345- }
346346-347347- let create size =
348348- { bits = 0L; num_bits = 0; buffer = Bytes.create size; buf_pos = size }
349349-350350- let[@inline] write_bits t value n =
351351- if n > 0 then begin
352352- t.bits <- Int64.logor t.bits (Int64.shift_left (Int64.of_int value) t.num_bits);
353353- t.num_bits <- t.num_bits + n
354354- end
355355-356356- let flush_bytes t =
357357- while t.num_bits >= 8 do
358358- t.buf_pos <- t.buf_pos - 1;
359359- Bytes.set_uint8 t.buffer t.buf_pos (Int64.to_int (Int64.logand t.bits 0xFFL));
360360- t.bits <- Int64.shift_right_logical t.bits 8;
361361- t.num_bits <- t.num_bits - 8
362362- done
363363-364364- let finalize_to_slice t : Slice.t =
365365- write_bits t 1 1;
366366- if t.num_bits mod 8 <> 0 then
367367- t.num_bits <- ((t.num_bits + 7) / 8) * 8;
368368- flush_bytes t;
369369- let len = Bytes.length t.buffer - t.buf_pos in
370370- (* Reverse bytes in place so marker ends up at the end *)
371371- for i = 0 to len / 2 - 1 do
372372- let j = t.buf_pos + i in
373373- let k = t.buf_pos + len - 1 - i in
374374- let tmp = Bytes.get t.buffer j in
375375- Bytes.set t.buffer j (Bytes.get t.buffer k);
376376- Bytes.set t.buffer k tmp
377377- done;
378378- Slice.make t.buffer ~first:t.buf_pos ~length:len
379379-380380- let finalize t =
381381- Slice.to_bytes (finalize_to_slice t)
382382-383383- let current_size t =
384384- Bytes.length t.buffer - t.buf_pos + (t.num_bits + 7) / 8
385385-end
-267
ocaml-bitstream/src/bitstream.mli
···11-(** Bitstream - Bit-level I/O for binary formats.
22-33- This library provides efficient bit-level reading and writing for parsing
44- and generating binary formats. It supports both forward (start-to-end) and
55- backward (end-to-start) operations, as required by various compression
66- algorithms.
77-88- {1 Overview}
99-1010- {[
1111- (* Forward reading from a slice (zero-copy) *)
1212- let slice = { Bitstream.Slice.bytes = data; first = 0; length = n } in
1313- let r = Bitstream.Forward_reader.of_slice slice in
1414- let magic = Bitstream.Forward_reader.read_bits r 32 in
1515- let flags = Bitstream.Forward_reader.read_bits r 8 in
1616-1717- (* Get remaining data as a slice (zero-copy) *)
1818- let remaining = Bitstream.Forward_reader.to_slice r in
1919-2020- (* Backward reading - for FSE/ANS entropy decoding *)
2121- let r = Bitstream.Backward_reader.of_slice slice in
2222- let symbol = Bitstream.Backward_reader.read_bits r num_bits
2323- ]}
2424-2525- {1 Bytesrw Compatibility}
2626-2727- The {!Slice} type is structurally compatible with [Bytesrw.Bytes.Slice.t],
2828- enabling zero-copy integration with bytesrw-based streaming. All reader
2929- and writer constructors accept slices as the primary input type.
3030-3131- {1 Error Handling}
3232-3333- Operations raise exceptions on error:
3434- - {!End_of_stream}: Reading past end of stream
3535- - {!Invalid_state}: Operation requires specific state (e.g., byte alignment)
3636- - {!Corrupted_stream}: Malformed stream data *)
3737-3838-(** {1 Slice Type}
3939-4040- A slice is a view into a byte buffer. This type is structurally compatible
4141- with [Bytesrw.Bytes.Slice.t], enabling zero-copy interop. *)
4242-4343-module Slice : sig
4444- type t = {
4545- bytes : bytes;
4646- first : int;
4747- length : int;
4848- }
4949- (** A slice referencing [length] bytes starting at [first] in [bytes].
5050- This is structurally identical to [Bytesrw.Bytes.Slice.t]. *)
5151-5252- val make : bytes -> first:int -> length:int -> t
5353- (** [make bytes ~first ~length] creates a slice. *)
5454-5555- val of_bytes : ?first:int -> ?length:int -> bytes -> t
5656- (** [of_bytes bytes] creates a slice for the entire buffer.
5757- Optional [first] and [length] can restrict the range. *)
5858-5959- val to_bytes : t -> bytes
6060- (** [to_bytes t] copies the slice contents to a new buffer. *)
6161-6262- val is_empty : t -> bool
6363- (** [is_empty t] returns true if the slice has zero length. *)
6464-6565- val sub : t -> first:int -> length:int -> t
6666- (** [sub t ~first ~length] creates a sub-slice. [first] is relative to [t]. *)
6767-end
6868-6969-(** {1 Exceptions} *)
7070-7171-exception End_of_stream
7272-(** Raised when attempting to read past the end of the stream. *)
7373-7474-exception Invalid_state of string
7575-(** Raised when an operation requires a specific state (e.g., byte alignment). *)
7676-7777-exception Corrupted_stream of string
7878-(** Raised when stream data is malformed (e.g., invalid padding marker). *)
7979-8080-(** {1 Forward Bitstream Reader} *)
8181-8282-module Forward_reader : sig
8383- (** Forward bitstream reader state. *)
8484- type t
8585-8686- val of_slice : Slice.t -> t
8787- (** [of_slice slice] creates a reader from a slice. Zero-copy. *)
8888-8989- val of_bytes : bytes -> t
9090- (** [of_bytes src] creates a reader for the entire byte buffer. *)
9191-9292- val create : bytes -> pos:int -> len:int -> t
9393- (** [create src ~pos ~len] creates a reader for [len] bytes starting at [pos]. *)
9494-9595- val remaining : t -> int
9696- (** [remaining t] returns the number of unread bits. *)
9797-9898- val is_byte_aligned : t -> bool
9999- (** [is_byte_aligned t] returns true if the reader is at a byte boundary. *)
100100-101101- val read_bits : t -> int -> int
102102- (** [read_bits t n] reads and returns [n] bits (1-57) in little-endian order.
103103- @raise End_of_stream if not enough data available.
104104- @raise Invalid_argument if [n > 57]. *)
105105-106106- val read_byte : t -> int
107107- (** [read_byte t] reads and returns the next byte (0-255).
108108- @raise Invalid_state if not byte aligned.
109109- @raise End_of_stream if at end of stream. *)
110110-111111- val rewind_bits : t -> int -> unit
112112- (** [rewind_bits t n] rewinds the stream by [n] bits.
113113- @raise End_of_stream if rewinding past the start. *)
114114-115115- val align : t -> unit
116116- (** [align t] advances to the next byte boundary if not already aligned. *)
117117-118118- val byte_position : t -> int
119119- (** [byte_position t] returns the current byte position.
120120- @raise Invalid_state if not byte aligned. *)
121121-122122- val get_slice : t -> int -> Slice.t
123123- (** [get_slice t n] returns the next [n] bytes as a slice (zero-copy).
124124- The slice references the underlying buffer directly.
125125- @raise Invalid_state if not byte aligned.
126126- @raise End_of_stream if not enough data. *)
127127-128128- val get_bytes : t -> int -> bytes
129129- (** [get_bytes t n] reads and returns the next [n] bytes as a new buffer.
130130- Equivalent to [Slice.to_bytes (get_slice t n)].
131131- @raise Invalid_state if not byte aligned.
132132- @raise End_of_stream if not enough data. *)
133133-134134- val to_slice : t -> Slice.t
135135- (** [to_slice t] returns the remaining data as a slice (zero-copy).
136136- @raise Invalid_state if not byte aligned. *)
137137-138138- val advance : t -> int -> unit
139139- (** [advance t n] skips [n] bytes without returning them.
140140- @raise Invalid_state if not byte aligned.
141141- @raise End_of_stream if not enough data. *)
142142-143143- val sub : t -> int -> t
144144- (** [sub t n] creates a sub-reader for the next [n] bytes and advances [t].
145145- @raise Invalid_state if not byte aligned.
146146- @raise End_of_stream if not enough data. *)
147147-148148- val remaining_bytes : t -> int
149149- (** [remaining_bytes t] returns the number of unread bytes.
150150- @raise Invalid_state if not byte aligned. *)
151151-152152- val skip_bits : t -> int -> unit
153153- (** [skip_bits t n] skips [n] bits without returning them.
154154- @raise End_of_stream if not enough data. *)
155155-end
156156-157157-(** {1 Backward Bitstream Reader}
158158-159159- Reads bits from the end of a buffer towards the start. The stream format
160160- includes a padding marker: the highest 1-bit in the final byte indicates
161161- where actual data begins.
162162-163163- This format is used by FSE and ANS entropy coders. *)
164164-165165-module Backward_reader : sig
166166- (** Backward bitstream reader state. *)
167167- type t
168168-169169- val of_slice : Slice.t -> t
170170- (** [of_slice slice] creates a backward reader from a slice. Zero-copy.
171171- @raise End_of_stream if slice is empty.
172172- @raise Corrupted_stream if padding marker is invalid. *)
173173-174174- val of_bytes : bytes -> pos:int -> len:int -> t
175175- (** [of_bytes src ~pos ~len] creates a backward reader.
176176- The stream is read from position [pos + len - 1] towards [pos].
177177- @raise End_of_stream if [len = 0].
178178- @raise Corrupted_stream if padding marker is invalid. *)
179179-180180- val remaining : t -> int
181181- (** [remaining t] returns the number of bits remaining. *)
182182-183183- val is_empty : t -> bool
184184- (** [is_empty t] returns true if no more bits are available. *)
185185-186186- val read_bits : t -> int -> int
187187- (** [read_bits t n] reads and returns [n] bits (1-57).
188188- Returns 0 bits when reading past the beginning.
189189- @raise Invalid_argument if [n > 57]. *)
190190-191191- val peek_bits : t -> int -> int
192192- (** [peek_bits t n] returns the next [n] bits without consuming them.
193193- @raise Invalid_argument if [n > 57]. *)
194194-end
195195-196196-(** {1 Forward Bitstream Writer} *)
197197-198198-module Forward_writer : sig
199199- (** Forward bitstream writer state. *)
200200- type t
201201-202202- val of_slice : Slice.t -> t
203203- (** [of_slice slice] creates a writer into a slice. Zero-copy. *)
204204-205205- val of_bytes : bytes -> t
206206- (** [of_bytes dst] creates a writer starting at position 0. *)
207207-208208- val create : bytes -> pos:int -> t
209209- (** [create dst ~pos] creates a writer starting at [pos] in buffer [dst]. *)
210210-211211- val write_bits : t -> int -> int -> unit
212212- (** [write_bits t value n] writes the lower [n] bits (1-57) of [value]
213213- in little-endian order.
214214- @raise Invalid_argument if [n > 57]. *)
215215-216216- val write_byte : t -> int -> unit
217217- (** [write_byte t value] writes a single byte. Flushes any partial bits first. *)
218218-219219- val write_slice : t -> Slice.t -> unit
220220- (** [write_slice t slice] writes bytes from a slice. Flushes any partial bits first. *)
221221-222222- val write_bytes : t -> bytes -> unit
223223- (** [write_bytes t src] writes all bytes from [src]. Flushes any partial bits first. *)
224224-225225- val byte_position : t -> int
226226- (** [byte_position t] returns the current output position including any partial byte. *)
227227-228228- val flush : t -> unit
229229- (** [flush t] writes any accumulated bits as a partial byte. *)
230230-231231- val finalize : t -> int
232232- (** [finalize t] flushes and returns the total number of bytes written. *)
233233-234234- val to_slice : t -> Slice.t
235235- (** [to_slice t] flushes and returns the written data as a slice (zero-copy).
236236- The slice references the underlying destination buffer. *)
237237-end
238238-239239-(** {1 Backward Bitstream Writer}
240240-241241- Accumulates bits to produce output that will be read backwards.
242242- Used for FSE and Huffman encoding. *)
243243-244244-module Backward_writer : sig
245245- (** Backward bitstream writer state. *)
246246- type t
247247-248248- val create : int -> t
249249- (** [create size] creates a writer with an internal buffer of [size] bytes. *)
250250-251251- val write_bits : t -> int -> int -> unit
252252- (** [write_bits t value n] accumulates [n] bits from [value]. *)
253253-254254- val flush_bytes : t -> unit
255255- (** [flush_bytes t] flushes complete bytes to the internal buffer. *)
256256-257257- val finalize_to_slice : t -> Slice.t
258258- (** [finalize_to_slice t] adds the padding marker, flushes, and returns output
259259- as a slice (zero-copy). The slice references the internal buffer. *)
260260-261261- val finalize : t -> bytes
262262- (** [finalize t] adds the padding marker, flushes, and returns the output.
263263- Equivalent to [Slice.to_bytes (finalize_to_slice t)]. *)
264264-265265- val current_size : t -> int
266266- (** [current_size t] returns the current output size estimate. *)
267267-end