Class: Cabriolet::Binary::BitstreamWriter

Inherits:
Object
  • Object
show all
Defined in:
lib/cabriolet/binary/bitstream_writer.rb

Overview

BitstreamWriter provides bit-level I/O operations for writing compressed data

Constant Summary collapse

BYTE_CONSTANTS =

Pre-computed byte constants for fast single-byte writes Avoids repeated array packing for each byte written

Array.new(256) { |i| [i].pack("C") }.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io_system, handle, buffer_size = Cabriolet.default_buffer_size, bit_order: :lsb, msb_first: false) ⇒ BitstreamWriter

Initialize a new bitstream writer

Parameters:

  • io_system (System::IOSystem)

    I/O system for writing data

  • handle (System::FileHandle, System::MemoryHandle)

    Handle to write to

  • buffer_size (Integer) (defaults to: Cabriolet.default_buffer_size)

    Size of the output buffer

  • bit_order (Symbol) (defaults to: :lsb)

    Bit ordering - :lsb (default) or :msb

  • msb_first (Boolean) (defaults to: false)

    Deprecated: use bit_order instead



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 20

def initialize(io_system, handle,
 buffer_size = Cabriolet.default_buffer_size, bit_order: :lsb, msb_first: false)
  @io_system = io_system
  @handle = handle
  @buffer_size = buffer_size

  # Support legacy msb_first parameter or new bit_order parameter
  @bit_order = msb_first ? :msb : bit_order
  @msb_first = (@bit_order == :msb)

  @bit_buffer = 0
  @bits_in_buffer = 0
  @accumulated = 0
  @bits_accumulated = 0
end

Instance Attribute Details

#buffer_sizeObject (readonly)

Returns the value of attribute buffer_size.



11
12
13
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 11

def buffer_size
  @buffer_size
end

#handleObject (readonly)

Returns the value of attribute handle.



11
12
13
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 11

def handle
  @handle
end

#io_systemObject (readonly)

Returns the value of attribute io_system.



11
12
13
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 11

def io_system
  @io_system
end

Instance Method Details

#byte_alignvoid

This method returns an undefined value.

Align to the next byte boundary by padding with zeros



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 86

def byte_align
  if @bit_order == :msb
    # MSB mode: align to 16-bit boundary (like Bitstream reader)
    return if @bits_in_buffer.zero?

    padding = (16 - @bits_in_buffer) % 16
  else
    # LSB mode: align to 8-bit boundary
    return if @bits_accumulated.zero?

    padding = (8 - @bits_accumulated) % 8
  end
  if padding.positive?
    write_bits(0, padding)
  end
end

#flushvoid

This method returns an undefined value.

Flush any remaining bits in the buffer



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 106

def flush
  # For MSB mode, use the special MSB flush
  if @bit_order == :msb
    flush_msb_internal
    return
  end

  # LSB mode flush
  # First flush any accumulated bits
  if @bits_accumulated.positive?
    byte = @accumulated & 0xFF
    write_byte(byte)
    @accumulated = 0
    @bits_accumulated = 0
  end

  # Then flush buffer
  return if @bits_in_buffer.zero?

  byte = @bit_buffer & 0xFF
  write_byte(byte)
  @bit_buffer = 0
  @bits_in_buffer = 0
end

#flush_msbvoid

This method returns an undefined value.

Flush MSB buffer (write remaining bits padded to 16-bit boundary)



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 289

def flush_msb
  return if @bits_in_buffer.zero?

  # Pad to 16-bit boundary
  padding = (16 - @bits_in_buffer) % 16
  @bit_buffer <<= padding if padding.positive?
  @bits_in_buffer += padding

  # Write final 16-bit word
  if @bits_in_buffer == 16
    word = @bit_buffer & 0xFFFF
    write_byte((word >> 8) & 0xFF)
    write_byte(word & 0xFF)
  end

  @bit_buffer = 0
  @bits_in_buffer = 0
end

#flush_msb_internalvoid

This method returns an undefined value.

Flush MSB buffer (internal implementation) Write remaining bits padded to 16-bit boundary



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 180

def flush_msb_internal
  return if @bits_in_buffer.zero?

  # Pad to 16-bit boundary
  padding = (16 - @bits_in_buffer) % 16
  @bit_buffer <<= padding if padding.positive?
  @bits_in_buffer += padding

  # Write final 16-bit word
  if @bits_in_buffer == 16
    word = @bit_buffer & 0xFFFF
    # Write little-endian (LSB byte first, then MSB byte) to match Bitstream reader
    write_byte(word & 0xFF)
    write_byte((word >> 8) & 0xFF)
  end

  @bit_buffer = 0
  @bits_in_buffer = 0
end

#write_bits(value, num_bits) ⇒ void

This method returns an undefined value.

Write specified number of bits to the stream

Parameters:

  • value (Integer)

    Value to write

  • num_bits (Integer)

    Number of bits to write (1-32)

Raises:



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
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 42

def write_bits(value, num_bits)
  if num_bits < 1 || num_bits > 32
    raise ArgumentError,
          "Can only write 1-32 bits at a time"
  end

  # Delegate to MSB method if in MSB mode
  if @bit_order == :msb
    write_bits_msb_internal(value, num_bits)
    return
  end

  # LSB-first mode (default)
  # Mask value to num_bits
  value &= (1 << num_bits) - 1

  # Accumulate bits
  @accumulated |= (value << @bits_accumulated)
  @bits_accumulated += num_bits

  # Transfer accumulated bits to buffer in 8-bit chunks
  while @bits_accumulated >= 8
    # Take the lowest 8 bits from accumulated
    byte = @accumulated & 0xFF
    @accumulated >>= 8
    @bits_accumulated -= 8

    # Add to buffer
    @bit_buffer |= (byte << @bits_in_buffer)
    @bits_in_buffer += 8

    # Flush complete bytes from buffer
    while @bits_in_buffer >= 8
      flush_byte = @bit_buffer & 0xFF
      write_byte(flush_byte)
      @bit_buffer >>= 8
      @bits_in_buffer -= 8
    end
  end
end

#write_bits_be(value, num_bits) ⇒ void

This method returns an undefined value.

Write bits in big-endian (MSB first) order

Parameters:

  • value (Integer)

    Value to write

  • num_bits (Integer)

    Number of bits to write



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 224

def write_bits_be(value, num_bits)
  # Write full bytes first for better performance
  full_bytes = num_bits / 8
  remaining_bits = num_bits % 8

  # Write complete bytes MSB first
  full_bytes.times do |i|
    byte_shift = num_bits - 8 - (i * 8)
    byte = (value >> byte_shift) & 0xFF
    write_bits(byte, 8)
  end

  # Write remaining bits
  if remaining_bits.positive?
    remaining_value = value & ((1 << remaining_bits) - 1)
    write_bits(remaining_value, remaining_bits)
  end
end

#write_bits_msb(value, num_bits) ⇒ void

This method returns an undefined value.

Write bits MSB-first (for Quantum compression) Accumulates bits and writes 16-bit words MSB-first

Parameters:

  • value (Integer)

    Value to write

  • num_bits (Integer)

    Number of bits to write



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 266

def write_bits_msb(value, num_bits)
  if num_bits < 1 || num_bits > 32
    raise ArgumentError,
          "Can only write 1-32 bits at a time"
  end

  # Add bits to buffer (MSB first)
  @bit_buffer = (@bit_buffer << num_bits) | (value & ((1 << num_bits) - 1))
  @bits_in_buffer += num_bits

  # Flush complete 16-bit words MSB-first
  while @bits_in_buffer >= 16
    @bits_in_buffer -= 16
    word = (@bit_buffer >> @bits_in_buffer) & 0xFFFF
    # Write MSB first
    write_byte((word >> 8) & 0xFF)
    write_byte(word & 0xFF)
  end
end

#write_bits_msb_internal(value, num_bits) ⇒ void

This method returns an undefined value.

Write bits in MSB-first mode (internal implementation) Matches the behavior of Bitstream’s MSB mode for reading

Parameters:

  • value (Integer)

    Value to write

  • num_bits (Integer)

    Number of bits to write



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 153

def write_bits_msb_internal(value, num_bits)
  # Mask value to num_bits
  value &= (1 << num_bits) - 1

  # Add bits to buffer (MSB first - inject at left side)
  @bit_buffer = (@bit_buffer << num_bits) | value
  @bits_in_buffer += num_bits

  # Flush complete 16-bit words
  # The most significant bits are at the left of the buffer
  # We want to extract the highest 16 bits and keep the rest
  while @bits_in_buffer >= 16
    # Extract the highest 16 bits by shifting right by (bits_in_buffer - 16)
    # This moves the top 16 bits to positions 0-15
    @bits_in_buffer -= 16
    shift = @bits_in_buffer
    word = (@bit_buffer >> shift) & 0xFFFF
    # Write little-endian (LSB byte first, then MSB byte) to match Bitstream reader
    write_byte(word & 0xFF)
    write_byte((word >> 8) & 0xFF)
  end
end

#write_byte(byte) ⇒ void

This method returns an undefined value.

Write a single byte to the output

Parameters:

  • byte (Integer)

    Byte value to write



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 135

def write_byte(byte)
  # Use pre-encoded byte constant for better performance
  data = BYTE_CONSTANTS[byte]
  # DEBUG
  if ENV["DEBUG_BITSTREAM"]
    warn "DEBUG write_byte: pos=#{@bits_in_buffer} byte=#{byte} (#{byte.to_s(2).rjust(
      8, '0'
    )})"
  end
  @io_system.write(@handle, data)
end

#write_bytes(bytes) ⇒ void

This method returns an undefined value.

Write multiple bytes to the output

Parameters:

  • bytes (String, Array<Integer>)

    Bytes to write



214
215
216
217
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 214

def write_bytes(bytes)
  data = bytes.is_a?(String) ? bytes : bytes.pack("C*")
  @io_system.write(@handle, data)
end

#write_raw_byte(byte) ⇒ void

This method returns an undefined value.

Write a raw byte directly (for signatures, etc.) This ensures the bit buffer is flushed first

Parameters:

  • byte (Integer)

    Byte value to write



205
206
207
208
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 205

def write_raw_byte(byte)
  flush if @bits_in_buffer.positive?
  write_byte(byte)
end

#write_uint16_le(value) ⇒ void

This method returns an undefined value.

Write a 16-bit little-endian value

Parameters:

  • value (Integer)

    16-bit value



247
248
249
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 247

def write_uint16_le(value)
  write_bits(value & 0xFFFF, 16)
end

#write_uint32_le(value) ⇒ void

This method returns an undefined value.

Write a 32-bit little-endian value

Parameters:

  • value (Integer)

    32-bit value



255
256
257
258
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 255

def write_uint32_le(value)
  write_bits(value & 0xFFFF, 16)
  write_bits((value >> 16) & 0xFFFF, 16)
end