summaryrefslogtreecommitdiff
path: root/ocaml/lib/bitstream.ml
blob: cfe094c30cd5fd017d9f7a27bcbbf21b60918f65 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
(** Bitstream utilities for jam/cue serialization *)

(** A bitstream writer *)
type writer = {
  buf: bytes ref;          (** Buffer for bits *)
  mutable bit_pos: int;    (** Current bit position *)
}

(** A bitstream reader *)
type reader = {
  buf: bytes;              (** Buffer to read from *)
  mutable bit_pos: int;    (** Current bit position *)
  len: int;                (** Length in bits *)
}

(** Create a new bitstream writer *)
let writer_create () = {
  buf = ref (Bytes.create 1024);
  bit_pos = 0;
}

(** Grow the writer buffer if needed *)
let writer_ensure (w : writer) (bits_needed : int) : unit =
  let bytes_needed : int = (w.bit_pos + bits_needed + 7) / 8 in
  let buf_ref : bytes ref = w.buf in
  let current_buf : bytes = !buf_ref in
  if bytes_needed > (Bytes.length current_buf) then begin
    let old_buf : bytes = current_buf in
    let new_size : int = max (bytes_needed * 2) (Bytes.length old_buf * 2) in
    let new_buf : bytes = Bytes.create new_size in
    Bytes.blit old_buf 0 new_buf 0 (Bytes.length old_buf);
    buf_ref := new_buf
  end

(** Write a single bit *)
let write_bit w bit =
  writer_ensure w 1;
  let byte_pos = w.bit_pos / 8 in
  let bit_off = w.bit_pos mod 8 in
  if bit then begin
    let buf = !(w.buf) in
    let old_byte = Bytes.get_uint8 buf byte_pos in
    Bytes.set_uint8 buf byte_pos (old_byte lor (1 lsl bit_off))
  end;
  w.bit_pos <- w.bit_pos + 1

(** Write multiple bits from a Z.t value *)
let write_bits w value nbits =
  writer_ensure w nbits;
  for i = 0 to nbits - 1 do
    let bit = Z.testbit value i in
    write_bit w bit
  done

(** Get the final bytes from a writer *)
let writer_to_bytes (w : writer) : bytes =
  let byte_len = (w.bit_pos + 7) / 8 in
  let buf_ref : bytes ref = w.buf in
  let buf : bytes = !buf_ref in
  Bytes.sub buf 0 byte_len

(** Create a bitstream reader *)
let reader_create buf =
  {
    buf;
    bit_pos = 0;
    len = Bytes.length buf * 8;
  }

(** Read a single bit *)
let read_bit r =
  if r.bit_pos >= r.len then
    raise (Invalid_argument "read_bit: end of stream");
  let byte_pos = r.bit_pos / 8 in
  let bit_off = r.bit_pos mod 8 in
  let byte_val = Bytes.get_uint8 r.buf byte_pos in
  r.bit_pos <- r.bit_pos + 1;
  (byte_val lsr bit_off) land 1 = 1

(** Read multiple bits as a Z.t *)
let read_bits r nbits =
  let result = ref Z.zero in
  for i = 0 to nbits - 1 do
    if read_bit r then
      result := Z.logor !result (Z.shift_left Z.one i)
  done;
  !result

(** Peek at a bit without advancing *)
let peek_bit r =
  if r.bit_pos >= r.len then
    raise (Invalid_argument "peek_bit: end of stream");
  let byte_pos = r.bit_pos / 8 in
  let bit_off = r.bit_pos mod 8 in
  let byte_val = Bytes.get_uint8 r.buf byte_pos in
  (byte_val lsr bit_off) land 1 = 1

(** Get current bit position *)
let reader_pos r = r.bit_pos

(** Check if at end of stream *)
let reader_at_end r = r.bit_pos >= r.len