///|
/// Errors raised by the bit-level reader and writer.
pub suberror BitError {
InvalidWidth
InvalidValue
OutOfBounds
} derive(Debug)
///|
/// A growable most-significant-bit-first writer.
pub struct BitWriter {
bits : Array[Bool]
}
///|
/// Create an empty bit writer.
pub fn new_bit_writer() -> BitWriter {
{ bits: [] }
}
///|
/// Return the number of bits currently written.
pub fn BitWriter::length(self : BitWriter) -> Int {
self.bits.length()
}
///|
/// Append one bit.
pub fn BitWriter::write_bit(self : BitWriter, bit : Bool) -> Unit {
self.bits.push(bit)
}
///|
/// Append an unsigned value using exactly `width` high-to-low bits.
pub fn BitWriter::write(
self : BitWriter,
value : UInt,
width : Int,
) -> Unit raise BitError {
if width < 0 || width > 32 {
raise InvalidWidth
}
if width < 32 && value >> width != 0 {
raise InvalidValue
}
for index in 0..> (width - index - 1)) & 1) == 1)
}
}
///|
/// Append a signed two's-complement value.
pub fn BitWriter::write_signed(
self : BitWriter,
value : Int,
width : Int,
) -> Unit raise BitError {
if width <= 0 || width > 31 {
raise InvalidWidth
}
let minimum = -(1 << (width - 1))
let maximum = (1 << (width - 1)) - 1
if value < minimum || value > maximum {
raise InvalidValue
}
let encoded = value.reinterpret_as_uint() & ((1 << width) - 1)
self.write(encoded, width)
}
///|
/// Add zero padding until the writer reaches a byte boundary.
pub fn BitWriter::align_byte(self : BitWriter) -> Unit {
let remainder = self.bits.length() % 8
if remainder != 0 {
for _ in 0..<(8 - remainder) {
self.bits.push(false)
}
}
}
///|
/// Return a defensive copy of the written bits.
pub fn BitWriter::finish(self : BitWriter) -> Array[Bool] {
self.bits.copy()
}
///|
/// Return the written bits packed into bytes, padding the final byte with 0.
pub fn BitWriter::to_bytes(self : BitWriter) -> Array[Byte] {
bits_to_bytes(self.bits)
}
///|
/// A sequential most-significant-bit-first reader.
pub struct BitReader {
bits : Array[Bool]
mut position : Int
}
///|
/// Create a reader over a defensive copy of the input bits.
pub fn new_bit_reader(bits : Array[Bool]) -> BitReader {
{ bits: bits.copy(), position: 0 }
}
///|
/// Create a reader directly from packed bytes.
pub fn bit_reader_from_bytes(bytes : Array[Byte]) -> BitReader {
new_bit_reader(bytes_to_bits(bytes))
}
///|
/// Return the current read offset.
pub fn BitReader::position(self : BitReader) -> Int {
self.position
}
///|
/// Return the number of unread bits.
pub fn BitReader::remaining(self : BitReader) -> Int {
self.bits.length() - self.position
}
///|
/// Read one bit.
pub fn BitReader::read_bit(self : BitReader) -> Bool raise BitError {
if self.position >= self.bits.length() {
raise OutOfBounds
}
let bit = self.bits[self.position]
self.position += 1
bit
}
///|
/// Read an unsigned value of exactly `width` bits.
pub fn BitReader::read(self : BitReader, width : Int) -> UInt raise BitError {
if width < 0 || width > 32 {
raise InvalidWidth
}
if self.remaining() < width {
raise OutOfBounds
}
let mut value : UInt = 0
for _ in 0.. Int raise BitError {
if width <= 0 || width > 31 {
raise InvalidWidth
}
let raw = self.read(width)
if ((raw >> (width - 1)) & 1) == 0 {
raw.reinterpret_as_int()
} else {
let sign_mask : UInt = 0xFFFFFFFF ^ ((1 << width) - 1)
(raw | sign_mask).reinterpret_as_int()
}
}
///|
/// Skip a number of bits.
pub fn BitReader::skip(self : BitReader, width : Int) -> Unit raise BitError {
if width < 0 || self.remaining() < width {
raise OutOfBounds
}
self.position += width
}
///|
/// Inspect a bit without advancing the reader.
pub fn BitReader::peek(self : BitReader, width : Int) -> UInt raise BitError {
let saved = self.position
let value = self.read(width)
self.position = saved
value
}
///|
/// Convert packed bytes to high-to-low bits.
pub fn bytes_to_bits(bytes : Array[Byte]) -> Array[Bool] {
let result : Array[Bool] = []
for byte in bytes {
for index in 0..<8 {
result.push(((byte.to_uint() >> (7 - index)) & 1) == 1)
}
}
result
}
///|
/// Convert bits to packed bytes. The last byte is zero padded.
pub fn bits_to_bytes(bits : Array[Bool]) -> Array[Byte] {
if bits.is_empty() {
return []
}
let byte_count = (bits.length() + 7) / 8
let result : Array[Byte] = Array::make(byte_count, 0)
for index in 0.. Bool? {
bits.get(position)
}
///|
/// Set a bit in a defensive copy. Out-of-range positions are ignored.
pub fn set_bit(bits : Array[Bool], position : Int, value : Bool) -> Array[Bool] {
let result = bits.copy()
if position >= 0 && position < result.length() {
result[position] = value
}
result
}
///|
/// Return a string made of `0` and `1` for diagnostics and golden tests.
pub fn bits_to_string(bits : Array[Bool]) -> String {
let builder = StringBuilder()
for bit in bits {
builder.write_string(if bit { "1" } else { "0" })
}
builder.to_string()
}