///|
/// Concatenate byte buffers without exposing mutable implementation details.
pub fn concat_bytes(parts : Array[Bytes]) -> Bytes {
let out : Array[Byte] = []
for part in parts {
for b in part.to_array() {
out.push(b)
}
}
Bytes::from_array(out)
}
///|
/// Encode a length-delimited payload: varint length followed by bytes.
pub fn encode_length_delimited(payload : Bytes) -> Bytes {
concat_bytes([encode_varint_u64(payload.length().to_uint64()), payload])
}
///|
/// Encode a UTF-8 string as length-delimited protobuf data.
pub fn encode_string(value : String) -> Bytes {
encode_length_delimited(@utf8.encode(value))
}
///|
/// Decode a length-delimited payload from `input`.
pub fn decode_length_delimited(
input : Bytes,
offset? : Int = 0,
) -> DecodeBytesResult {
match decode_varint_u64(input, offset~) {
U64Err(e) => BytesErr(e)
U64Ok(len64, next) => {
let len = len64.to_int()
if len < 0 {
return BytesErr(NegativeLength)
}
let arr = input.to_array()
if next + len > arr.length() {
return BytesErr(UnexpectedEof)
}
let out : Array[Byte] = []
for i = next; i < next + len; i = i + 1 {
out.push(arr[i])
}
BytesOk(Bytes::from_array(out), next + len)
}
}
}
///|
/// Decode a UTF-8 string lossily from length-delimited protobuf data.
pub fn decode_string_lossy(
input : Bytes,
offset? : Int = 0,
) -> DecodeStringResult {
match decode_length_delimited(input, offset~) {
BytesErr(e) => StringErr(e)
BytesOk(payload, next) => StringOk(@utf8.decode_lossy(payload[:]), next)
}
}
///|
/// Result of reading a string.
pub(all) enum DecodeStringResult {
StringOk(String, Int)
StringErr(DecodeError)
} derive(Debug, Eq)