///|
/// ByteWriter: an append-only byte buffer with amortized O(1) pushes.
/// `Array[Byte].push` re-allocates as the array grows; indexing into a
/// preallocated FixedArray-backed buffer makes snapshot serialization
/// substantially faster (17 MB image: 2.4 s → sub-second).
pub struct ByteWriter {
priv mut buf : FixedArray[Byte]
priv mut len : Int32
}
///|
const GROW_SHIFT : Int32 = 1
///|
pub fn ByteWriter::new(capacity~ : Int32 = 256) -> ByteWriter {
{
buf: FixedArray::make(if capacity < 16 { 16 } else { capacity }, b'\x00'),
len: 0,
}
}
///|
fn ByteWriter::grow(self : ByteWriter, needed : Int32) -> Unit {
let old_cap = self.buf.length()
if needed <= old_cap {
return
}
let mut new_cap = old_cap
while new_cap < needed {
new_cap = new_cap + (new_cap >> GROW_SHIFT) // 1.5x-ish growth
}
let next = FixedArray::make(new_cap, b'\x00')
self.buf.blit_to(next, len=self.len, src_offset=0, dst_offset=0)
self.buf = next
}
///|
pub fn ByteWriter::push(self : ByteWriter, b : Byte) -> Unit {
self.grow(self.len + 1)
self.buf[self.len] = b
self.len += 1
}
///|
pub fn ByteWriter::push_bytes(self : ByteWriter, data : Bytes) -> Unit {
let n = data.length()
self.grow(self.len + n)
self.buf.blit_from_bytes(self.len, data, 0, n)
self.len += n
}
///|
/// Append another writer's contents.
pub fn ByteWriter::append(self : ByteWriter, other : ByteWriter) -> Unit {
let n = other.len
self.grow(self.len + n)
other.buf.blit_to(self.buf, len=n, src_offset=0, dst_offset=self.len)
self.len += n
}
///|
pub fn ByteWriter::length(self : ByteWriter) -> Int32 {
self.len
}
///|
/// Snapshot the written region as an immutable Bytes.
pub fn ByteWriter::to_bytes(self : ByteWriter) -> Bytes {
self.buf.unsafe_reinterpret_as_bytes().clamped_view(end=self.len).to_owned()
}
///|
/// Varint (LEB128), string, double and u32 writers matching codec.mbt.
pub fn ByteWriter::write_varint(self : ByteWriter, value : Int32) -> Unit {
// Negative values encode as their unsigned 32-bit bit pattern, exactly
// like codec.mbt's write_varint (the two must stay byte-compatible).
let mut v = value.reinterpret_as_uint()
while v >= 0x80 {
self.push(((v & 0x7F) | 0x80).reinterpret_as_int().to_byte())
v = v >> 7
}
self.push(v.reinterpret_as_int().to_byte())
}
///|
pub fn ByteWriter::write_string(self : ByteWriter, s : String) -> Unit {
let bytes = @utf8.encode(s)
self.write_varint(bytes.length())
self.push_bytes(bytes)
}
///|
pub fn ByteWriter::write_double(self : ByteWriter, value : Double) -> Unit {
let bits = value.reinterpret_as_uint64()
for i in 0..<8 {
self.push(((bits >> (i * 8)) & 0xFFUL).to_int().to_byte())
}
}
///|
pub fn ByteWriter::write_u32_le(self : ByteWriter, value : UInt32) -> Unit {
for i in 0..<4 {
self.push(((value >> (i * 8)) & 0xFFU).to_byte())
}
}