///|
/// The serialization context is itself a `BinaryOutput`: codec code can call
/// `ctx.write_u8(...)`, `ctx.write_var_i32(...)` etc. directly, delegating to
/// the underlying sink (mirrors desert-rust, where the context implements
/// `BinaryOutput`).
impl BinaryOutput for SerializationContext with fn write_u8(self, byte) {
self.output.write_u8(byte)
}
///|
impl BinaryOutput for SerializationContext with fn write_bytes(self, bytes) {
self.output.write_bytes(bytes)
}
///|
impl BinaryOutput for SerializationContext with fn write_i8(self, value) {
self.output.write_i8(value)
}
///|
impl BinaryOutput for SerializationContext with fn write_u16(self, value) {
self.output.write_u16(value)
}
///|
impl BinaryOutput for SerializationContext with fn write_i16(self, value) {
self.output.write_i16(value)
}
///|
impl BinaryOutput for SerializationContext with fn write_u32(self, value) {
self.output.write_u32(value)
}
///|
impl BinaryOutput for SerializationContext with fn write_i32(self, value) {
self.output.write_i32(value)
}
///|
impl BinaryOutput for SerializationContext with fn write_u64(self, value) {
self.output.write_u64(value)
}
///|
impl BinaryOutput for SerializationContext with fn write_i64(self, value) {
self.output.write_i64(value)
}
///|
impl BinaryOutput for SerializationContext with fn write_f32(self, value) {
self.output.write_f32(value)
}
///|
impl BinaryOutput for SerializationContext with fn write_f64(self, value) {
self.output.write_f64(value)
}
///|
impl BinaryOutput for SerializationContext with fn write_var_u32(self, value) {
self.output.write_var_u32(value)
}
///|
impl BinaryOutput for SerializationContext with fn write_var_i32(self, value) {
self.output.write_var_i32(value)
}
///|
/// Write a UTF-8 encoded string payload: `var_i32(byte_length)` followed by
/// the UTF-8 bytes. This is the primitive used by the `String` codec and by
/// the evolution machinery for field names.
pub fn SerializationContext::write_string_payload(
self : SerializationContext,
s : String,
) -> Unit raise DesertFailure {
let byte_len = utf8_byte_length(s)
self.write_var_i32(byte_len)
if byte_len > 0 {
// Measured fastest: bulk transcode into an exact-size scratch array +
// one blit. Core's per-char write_string_utf8 loses (branchy loop with
// grow checks per char); returning a scan struct also regresses (extra
// GC allocation per call). See OPTIMIZATION_PLAN.md finding F10.
let scratch = FixedArray::make(byte_len, b'\x00')
let _ = write_utf8(s, scratch, 0)
self.write_bytes(Bytes::from_array(scratch).exact_view(end=byte_len))
}
}
///|
/// The deserialization context exposes the raw read operations of its
/// `BinaryInput` directly, so codec code reads like desert-rust:
/// `ctx.read_u8(...)`, `ctx.read_var_i32(...)` etc.
///|
pub fn DeserializationContext::read_u8(
self : DeserializationContext,
) -> Byte raise DesertFailure {
self.input.read_u8()
}
///|
pub fn DeserializationContext::read_bytes_view(
self : DeserializationContext,
count : Int32,
) -> BytesView raise DesertFailure {
self.input.read_bytes_view(count)
}
///|
pub fn DeserializationContext::read_bytes(
self : DeserializationContext,
count : Int32,
) -> Bytes raise DesertFailure {
self.input.read_bytes(count)
}
///|
pub fn DeserializationContext::read_i8(
self : DeserializationContext,
) -> Int32 raise DesertFailure {
self.input.read_i8()
}
///|
pub fn DeserializationContext::read_u16(
self : DeserializationContext,
) -> UInt16 raise DesertFailure {
self.input.read_u16()
}
///|
pub fn DeserializationContext::read_i16(
self : DeserializationContext,
) -> Int32 raise DesertFailure {
self.input.read_i16()
}
///|
pub fn DeserializationContext::read_u32(
self : DeserializationContext,
) -> UInt32 raise DesertFailure {
self.input.read_u32()
}
///|
pub fn DeserializationContext::read_i32(
self : DeserializationContext,
) -> Int32 raise DesertFailure {
self.input.read_i32()
}
///|
pub fn DeserializationContext::read_u64(
self : DeserializationContext,
) -> UInt64 raise DesertFailure {
self.input.read_u64()
}
///|
pub fn DeserializationContext::read_i64(
self : DeserializationContext,
) -> Int64 raise DesertFailure {
self.input.read_i64()
}
///|
pub fn DeserializationContext::read_f32(
self : DeserializationContext,
) -> Float raise DesertFailure {
self.input.read_f32()
}
///|
pub fn DeserializationContext::read_f64(
self : DeserializationContext,
) -> Double raise DesertFailure {
self.input.read_f64()
}
///|
pub fn DeserializationContext::read_var_u32(
self : DeserializationContext,
) -> UInt32 raise DesertFailure {
self.input.read_var_u32()
}
///|
pub fn DeserializationContext::read_var_i32(
self : DeserializationContext,
) -> Int32 raise DesertFailure {
self.input.read_var_i32()
}
///|
/// Read a UTF-8 encoded string payload (symmetric with
/// `write_string_payload`).
pub fn DeserializationContext::read_string_payload(
self : DeserializationContext,
) -> String raise DesertFailure {
let byte_len = self.read_var_i32()
if byte_len < 0 {
raise DeserializationFailure("Negative string length")
}
if byte_len == 0 {
return ""
}
let view = self.input.read_bytes_view(byte_len)
read_utf8(view)
}
///|
/// Promote the `BinaryOutput` operations to regular methods, so codecs can
/// write `ctx.write_u8(...)` fluently.
pub extend SerializationContext with BinaryOutput::{
write_u8,
write_string_utf8,
write_bytes,
write_i8,
write_u16,
write_i16,
write_u32,
write_i32,
write_u64,
write_i64,
write_f32,
write_f64,
write_var_u32,
write_var_i32,
}
///|
/// Reroute subsequent writes into `output`, saving the previous sink on the
/// context's stack (used by the evolution machinery to capture a record's
/// fields into per-generation chunk buffers).
pub fn SerializationContext::push_output(
self : SerializationContext,
output : &BinaryOutput,
) -> Unit {
let prev = self.output
self.output_stack.push(prev)
self.output = output
}
///|
/// Restore the previous output sink, returning the one that was active.
pub fn SerializationContext::pop_output(
self : SerializationContext,
) -> &BinaryOutput {
let current = self.output
match self.output_stack.pop() {
Some(prev) => self.output = prev
None => ()
}
current
}
///|
/// Position of the input cursor.
pub fn DeserializationContext::pos(self : DeserializationContext) -> Int32 {
self.input.position()
}
///|
/// Advance the input cursor by `count` bytes (no data is returned).
pub fn DeserializationContext::skip(
self : DeserializationContext,
count : Int32,
) -> Unit raise DesertFailure {
self.input.skip_bytes(count)
}
///|
/// Fence subsequent reads into `region`: the cursor jumps to the region's
/// start and reads beyond its end fail. The previous cursor state is saved.
pub fn DeserializationContext::push_region(
self : DeserializationContext,
start : Int32,
size : Int32,
) -> Unit {
self.input.push_region(start, size)
}
///|
/// End the current region, restoring the previous cursor state and returning
/// the region's remaining unread range (the caller stores it to continue the
/// chunk later).
pub fn DeserializationContext::pop_region(
self : DeserializationContext,
) -> (Int32, Int32) {
self.input.pop_region()
}
///|
/// Read an `Option[T]` payload (tag byte + optional value). Helper for the
/// evolution machinery and derived codecs.
pub fn[T : BinaryDeserializer] DeserializationContext::read_optional_value(
self : DeserializationContext,
) -> T? raise DesertFailure {
let _probe : T? = None
BinaryDeserializer::deserialize(self)
}