Class: Cabriolet::Binary::BitstreamWriter
- Inherits:
-
Object
- Object
- Cabriolet::Binary::BitstreamWriter
- 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
-
#buffer_size ⇒ Object
readonly
Returns the value of attribute buffer_size.
-
#handle ⇒ Object
readonly
Returns the value of attribute handle.
-
#io_system ⇒ Object
readonly
Returns the value of attribute io_system.
Instance Method Summary collapse
-
#byte_align ⇒ void
Align to the next byte boundary by padding with zeros.
-
#flush ⇒ void
Flush any remaining bits in the buffer.
-
#flush_msb ⇒ void
Flush MSB buffer (write remaining bits padded to 16-bit boundary).
-
#flush_msb_internal ⇒ void
Flush MSB buffer (internal implementation) Write remaining bits padded to 16-bit boundary.
-
#initialize(io_system, handle, buffer_size = Cabriolet.default_buffer_size, bit_order: :lsb, msb_first: false) ⇒ BitstreamWriter
constructor
Initialize a new bitstream writer.
-
#write_bits(value, num_bits) ⇒ void
Write specified number of bits to the stream.
-
#write_bits_be(value, num_bits) ⇒ void
Write bits in big-endian (MSB first) order.
-
#write_bits_msb(value, num_bits) ⇒ void
Write bits MSB-first (for Quantum compression) Accumulates bits and writes 16-bit words MSB-first.
-
#write_bits_msb_internal(value, num_bits) ⇒ void
Write bits in MSB-first mode (internal implementation) Matches the behavior of Bitstream’s MSB mode for reading.
-
#write_byte(byte) ⇒ void
Write a single byte to the output.
-
#write_bytes(bytes) ⇒ void
Write multiple bytes to the output.
-
#write_raw_byte(byte) ⇒ void
Write a raw byte directly (for signatures, etc.) This ensures the bit buffer is flushed first.
-
#write_uint16_le(value) ⇒ void
Write a 16-bit little-endian value.
-
#write_uint32_le(value) ⇒ void
Write a 32-bit little-endian value.
Constructor Details
#initialize(io_system, handle, buffer_size = Cabriolet.default_buffer_size, bit_order: :lsb, msb_first: false) ⇒ BitstreamWriter
Initialize a new bitstream writer
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_size ⇒ Object (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 |
#handle ⇒ Object (readonly)
Returns the value of attribute handle.
11 12 13 |
# File 'lib/cabriolet/binary/bitstream_writer.rb', line 11 def handle @handle end |
#io_system ⇒ Object (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_align ⇒ void
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 |
#flush ⇒ void
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_msb ⇒ void
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_internal ⇒ void
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
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
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
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
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
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
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
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
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
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 |