// Wire type encoding/decoding for protobuf wire format.
// Reference: https://protobuf.dev/programming-guides/encoding/
///|
/// Make a protobuf tag from field number and wire type.
/// tag = (field_number << 3) | wire_type
pub fn make_tag(field_number : UInt, wire_type : UInt) -> UInt {
(field_number << 3) | wire_type
}
///|
/// Split a protobuf tag into (field_number, wire_type).
pub fn split_tag(tag : UInt) -> (UInt, UInt) {
(tag >> 3, tag & 7)
}
///|
/// Write a protobuf tag to the buffer.
pub fn write_tag(buf : Buffer, field_number : UInt, wire_type : UInt) -> Unit {
encode_varint(buf, make_tag(field_number, wire_type).to_uint64())
}
///|
/// Write a Length-delimited field prefix: tag + length as varint.
pub fn write_length_delimited_prefix(
buf : Buffer,
field_number : UInt,
byte_length : UInt,
) -> Unit {
write_tag(buf, field_number, WIRE_LENGTH_DELIMITED)
encode_varint(buf, byte_length.to_uint64())
}
///|
/// Write a fixed32 value in little-endian format.
pub fn write_fixed32(buf : Buffer, value : UInt) -> Unit {
buf.write_byte((value & 0xFF).to_byte())
buf.write_byte(((value >> 8) & 0xFF).to_byte())
buf.write_byte(((value >> 16) & 0xFF).to_byte())
buf.write_byte(((value >> 24) & 0xFF).to_byte())
}
///|
/// Write a fixed64 value in little-endian format.
pub fn write_fixed64(buf : Buffer, value : UInt64) -> Unit {
for i in 0..<8 {
buf.write_byte(((value >> (i * 8)) & 0xFF).to_byte())
}
}
///|
/// Write a float as fixed32.
pub fn write_float(buf : Buffer, value : Float) -> Unit {
write_fixed32(buf, value.reinterpret_as_uint())
}
///|
/// Write a double as fixed64.
pub fn write_double(buf : Buffer, value : Double) -> Unit {
write_fixed64(buf, value.reinterpret_as_uint64())
}
///|
/// Read a fixed32 value from bytes at the given position.
/// Returns (value, new_position) or None if out of bounds.
pub fn read_fixed32(bytes : Bytes, pos : Int) -> (UInt, Int)? {
if pos < 0 || pos + 4 > bytes.length() {
return None
}
let value = bytes[pos].to_uint() |
(bytes[pos + 1].to_uint() << 8) |
(bytes[pos + 2].to_uint() << 16) |
(bytes[pos + 3].to_uint() << 24)
Some((value, pos + 4))
}
///|
/// Read a fixed64 value from bytes at the given position.
/// Returns (value, new_position) or None if out of bounds.
pub fn read_fixed64(bytes : Bytes, pos : Int) -> (UInt64, Int)? {
if pos < 0 || pos + 8 > bytes.length() {
return None
}
let mut value : UInt64 = 0
for i in 0..<8 {
value = value | (bytes[pos + i].to_uint64() << (i * 8))
}
Some((value, pos + 8))
}