use alloc::string::String;
use alloc::vec::Vec;
use bytes::{Buf, BufMut, Bytes};
use crate::encoding::{decode_varint, encode_varint, varint_len, Tag, WireType};
use crate::error::DecodeError;
#[allow(clippy::needless_return)]
#[inline(always)]
pub(crate) fn validate_str(bytes: &[u8]) -> Result<&str, DecodeError> {
#[cfg(feature = "fast-utf8")]
return smoothutf8::to_str(bytes).ok_or(DecodeError::InvalidUtf8);
#[cfg(not(feature = "fast-utf8"))]
return core::str::from_utf8(bytes).map_err(|_| DecodeError::InvalidUtf8);
}
#[allow(clippy::needless_return)]
#[inline(always)]
pub(crate) unsafe fn validate_str_in(buf: &[u8], len: usize) -> Result<&str, DecodeError> {
debug_assert!(len <= buf.len());
let field = unsafe { buf.get_unchecked(..len) };
#[cfg(feature = "fast-utf8")]
{
let ok = if buf.len() >= len + smoothutf8::SLACK {
unsafe { smoothutf8::verify_with_slack(buf, 0..len) }
} else {
smoothutf8::verify(field)
};
return if ok {
Ok(unsafe { core::str::from_utf8_unchecked(field) })
} else {
Err(DecodeError::InvalidUtf8)
};
}
#[cfg(not(feature = "fast-utf8"))]
return core::str::from_utf8(field).map_err(|_| DecodeError::InvalidUtf8);
}
#[inline]
pub(crate) fn zigzag_encode_i32(value: i32) -> u32 {
((value << 1) ^ (value >> 31)) as u32
}
#[inline]
pub(crate) fn zigzag_decode_i32(value: u32) -> i32 {
((value >> 1) as i32) ^ (-((value & 1) as i32))
}
#[inline]
pub(crate) fn zigzag_encode_i64(value: i64) -> u64 {
((value << 1) ^ (value >> 63)) as u64
}
#[inline]
pub(crate) fn zigzag_decode_i64(value: u64) -> i64 {
((value >> 1) as i64) ^ (-((value & 1) as i64))
}
#[inline]
pub fn encode_int32(value: i32, buf: &mut impl BufMut) {
encode_varint(value as i64 as u64, buf);
}
#[inline]
pub fn decode_int32(buf: &mut impl Buf) -> Result<i32, DecodeError> {
let v = decode_varint(buf)?;
Ok(v as i32)
}
#[inline]
pub fn encode_int64(value: i64, buf: &mut impl BufMut) {
encode_varint(value as u64, buf);
}
#[inline]
pub fn decode_int64(buf: &mut impl Buf) -> Result<i64, DecodeError> {
let v = decode_varint(buf)?;
Ok(v as i64)
}
#[inline]
pub fn encode_uint32(value: u32, buf: &mut impl BufMut) {
encode_varint(value as u64, buf);
}
#[inline]
pub fn decode_uint32(buf: &mut impl Buf) -> Result<u32, DecodeError> {
let v = decode_varint(buf)?;
Ok(v as u32)
}
#[inline]
pub fn encode_uint64(value: u64, buf: &mut impl BufMut) {
encode_varint(value, buf);
}
#[inline]
pub fn decode_uint64(buf: &mut impl Buf) -> Result<u64, DecodeError> {
decode_varint(buf)
}
#[inline]
pub fn encode_sint32(value: i32, buf: &mut impl BufMut) {
encode_varint(zigzag_encode_i32(value) as u64, buf);
}
#[inline]
pub fn decode_sint32(buf: &mut impl Buf) -> Result<i32, DecodeError> {
let v = decode_varint(buf)?;
Ok(zigzag_decode_i32(v as u32))
}
#[inline]
pub fn encode_sint64(value: i64, buf: &mut impl BufMut) {
encode_varint(zigzag_encode_i64(value), buf);
}
#[inline]
pub fn decode_sint64(buf: &mut impl Buf) -> Result<i64, DecodeError> {
let v = decode_varint(buf)?;
Ok(zigzag_decode_i64(v))
}
#[inline]
pub fn encode_bool(value: bool, buf: &mut impl BufMut) {
buf.put_u8(value as u8);
}
#[inline]
pub fn decode_bool(buf: &mut impl Buf) -> Result<bool, DecodeError> {
let v = decode_varint(buf)?;
Ok(v != 0)
}
#[inline]
pub fn int32_encoded_len(value: i32) -> usize {
varint_len(value as i64 as u64)
}
#[inline]
pub fn int64_encoded_len(value: i64) -> usize {
varint_len(value as u64)
}
#[inline]
pub fn uint32_encoded_len(value: u32) -> usize {
varint_len(value as u64)
}
#[inline]
pub fn uint64_encoded_len(value: u64) -> usize {
varint_len(value)
}
#[inline]
pub fn sint32_encoded_len(value: i32) -> usize {
varint_len(zigzag_encode_i32(value) as u64)
}
#[inline]
pub fn sint64_encoded_len(value: i64) -> usize {
varint_len(zigzag_encode_i64(value))
}
pub const BOOL_ENCODED_LEN: usize = 1;
#[inline]
pub fn encode_float(value: f32, buf: &mut impl BufMut) {
buf.put_f32_le(value);
}
#[inline]
pub fn decode_float(buf: &mut impl Buf) -> Result<f32, DecodeError> {
if buf.remaining() < 4 {
return Err(DecodeError::UnexpectedEof);
}
Ok(buf.get_f32_le())
}
#[inline]
pub fn encode_fixed32(value: u32, buf: &mut impl BufMut) {
buf.put_u32_le(value);
}
#[inline]
pub fn decode_fixed32(buf: &mut impl Buf) -> Result<u32, DecodeError> {
if buf.remaining() < 4 {
return Err(DecodeError::UnexpectedEof);
}
Ok(buf.get_u32_le())
}
#[inline]
pub fn encode_sfixed32(value: i32, buf: &mut impl BufMut) {
buf.put_i32_le(value);
}
#[inline]
pub fn decode_sfixed32(buf: &mut impl Buf) -> Result<i32, DecodeError> {
if buf.remaining() < 4 {
return Err(DecodeError::UnexpectedEof);
}
Ok(buf.get_i32_le())
}
#[inline]
pub fn encode_double(value: f64, buf: &mut impl BufMut) {
buf.put_f64_le(value);
}
#[inline]
pub fn decode_double(buf: &mut impl Buf) -> Result<f64, DecodeError> {
if buf.remaining() < 8 {
return Err(DecodeError::UnexpectedEof);
}
Ok(buf.get_f64_le())
}
#[inline]
pub fn encode_fixed64(value: u64, buf: &mut impl BufMut) {
buf.put_u64_le(value);
}
#[inline]
pub fn decode_fixed64(buf: &mut impl Buf) -> Result<u64, DecodeError> {
if buf.remaining() < 8 {
return Err(DecodeError::UnexpectedEof);
}
Ok(buf.get_u64_le())
}
#[inline]
pub fn encode_sfixed64(value: i64, buf: &mut impl BufMut) {
buf.put_i64_le(value);
}
#[inline]
pub fn decode_sfixed64(buf: &mut impl Buf) -> Result<i64, DecodeError> {
if buf.remaining() < 8 {
return Err(DecodeError::UnexpectedEof);
}
Ok(buf.get_i64_le())
}
pub const FIXED32_ENCODED_LEN: usize = 4;
pub const FIXED64_ENCODED_LEN: usize = 8;
macro_rules! put_field_fn {
($(#[$doc:meta])* $name:ident, $value:ty, $wire:expr, $encode:ident) => {
$(#[$doc])*
#[doc = concat!(
"Fused tag+payload sibling of [`", stringify!($encode), "`]; ",
"exists so generated `write_to` bodies are one call per field."
)]
#[inline(always)]
pub fn $name(field_number: u32, value: $value, buf: &mut impl BufMut) {
Tag::new(field_number, $wire).encode(buf);
$encode(value, buf);
}
};
}
put_field_fn!(
put_int32_field, i32, WireType::Varint, encode_int32
);
put_field_fn!(
put_int64_field, i64, WireType::Varint, encode_int64
);
put_field_fn!(
put_uint32_field, u32, WireType::Varint, encode_uint32
);
put_field_fn!(
put_uint64_field, u64, WireType::Varint, encode_uint64
);
put_field_fn!(
put_sint32_field, i32, WireType::Varint, encode_sint32
);
put_field_fn!(
put_sint64_field, i64, WireType::Varint, encode_sint64
);
put_field_fn!(
put_bool_field, bool, WireType::Varint, encode_bool
);
put_field_fn!(
put_fixed32_field, u32, WireType::Fixed32, encode_fixed32
);
put_field_fn!(
put_fixed64_field, u64, WireType::Fixed64, encode_fixed64
);
put_field_fn!(
put_sfixed32_field, i32, WireType::Fixed32, encode_sfixed32
);
put_field_fn!(
put_sfixed64_field, i64, WireType::Fixed64, encode_sfixed64
);
put_field_fn!(
put_float_field, f32, WireType::Fixed32, encode_float
);
put_field_fn!(
put_double_field, f64, WireType::Fixed64, encode_double
);
put_field_fn!(
put_string_field, &str, WireType::LengthDelimited, encode_string
);
put_field_fn!(
put_bytes_field, &[u8], WireType::LengthDelimited, encode_bytes
);
#[inline(always)]
pub fn put_len_delimited_header(field_number: u32, len: u32, buf: &mut impl BufMut) {
Tag::new(field_number, WireType::LengthDelimited).encode(buf);
encode_varint(len as u64, buf);
}
#[inline(always)]
pub fn put_group_start(field_number: u32, buf: &mut impl BufMut) {
Tag::new(field_number, WireType::StartGroup).encode(buf);
}
#[inline(always)]
pub fn put_group_end(field_number: u32, buf: &mut impl BufMut) {
Tag::new(field_number, WireType::EndGroup).encode(buf);
}
#[inline]
pub fn encode_string(value: &str, buf: &mut impl BufMut) {
encode_varint(value.len() as u64, buf);
buf.put_slice(value.as_bytes());
}
#[inline]
pub fn decode_string(buf: &mut impl Buf) -> Result<String, DecodeError> {
let len = decode_varint(buf)?;
let len = usize::try_from(len).map_err(|_| DecodeError::MessageTooLarge)?;
if buf.remaining() < len {
return Err(DecodeError::UnexpectedEof);
}
let chunk = buf.chunk();
if chunk.len() >= len {
let r = unsafe { validate_str_in(chunk, len) }.map(alloc::borrow::ToOwned::to_owned);
buf.advance(len);
return r;
}
#[allow(clippy::uninit_vec)]
let mut bytes = {
let mut v = alloc::vec::Vec::with_capacity(len);
unsafe { v.set_len(len) };
v
};
buf.copy_to_slice(&mut bytes);
validate_str(&bytes)?;
Ok(unsafe { String::from_utf8_unchecked(bytes) })
}
#[inline]
pub fn merge_string(value: &mut String, buf: &mut impl Buf) -> Result<(), DecodeError> {
let len = decode_varint(buf)?;
let len = usize::try_from(len).map_err(|_| DecodeError::MessageTooLarge)?;
if buf.remaining() < len {
return Err(DecodeError::UnexpectedEof);
}
let chunk = buf.chunk();
if chunk.len() >= len {
let r: Result<(), DecodeError> = match unsafe { validate_str_in(chunk, len) } {
Ok(s) => {
value.clear();
value.push_str(s);
Ok(())
}
Err(e) => {
value.clear(); Err(e)
}
};
buf.advance(len);
return r;
}
let vec = unsafe { value.as_mut_vec() };
vec.clear();
vec.reserve(len);
#[allow(clippy::uninit_vec)]
unsafe {
vec.set_len(len);
}
buf.copy_to_slice(vec);
if validate_str(vec).is_err() {
vec.clear(); return Err(DecodeError::InvalidUtf8);
}
Ok(())
}
#[derive(Debug, Clone)]
pub enum WirePayload<'a> {
Borrowed(&'a [u8]),
Owned(Bytes),
}
impl WirePayload<'_> {
#[inline]
#[must_use]
pub fn as_slice(&self) -> &[u8] {
match self {
WirePayload::Borrowed(s) => s,
WirePayload::Owned(b) => b,
}
}
#[inline]
#[must_use = "the validated `&str` is the only way to use the payload as a string"]
pub fn to_str(&self) -> Result<&str, DecodeError> {
validate_str(self.as_slice())
}
#[inline]
#[must_use]
pub fn into_bytes(self) -> Bytes {
match self {
WirePayload::Borrowed(s) => Bytes::copy_from_slice(s),
WirePayload::Owned(b) => b,
}
}
}
#[inline]
pub(crate) fn read_field_payload<R>(
buf: &mut impl Buf,
f: impl FnOnce(WirePayload<'_>) -> Result<R, DecodeError>,
) -> Result<R, DecodeError> {
let len = decode_varint(buf)?;
let len = usize::try_from(len).map_err(|_| DecodeError::MessageTooLarge)?;
if buf.remaining() < len {
return Err(DecodeError::UnexpectedEof);
}
let chunk = buf.chunk();
if chunk.len() >= len {
let r = f(WirePayload::Borrowed(&chunk[..len]))?;
buf.advance(len);
Ok(r)
} else {
f(WirePayload::Owned(buf.copy_to_bytes(len)))
}
}
#[inline]
pub fn string_encoded_len(value: &str) -> usize {
let len = value.len();
varint_len(len as u64) + len
}
#[rustversion::attr(
since(1.78),
diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as a buffa custom string type",
note = "buffa owns `ProtoString`, so a foreign type can't implement it directly (orphan rule). \
Wrap it in a crate-local newtype and implement `ProtoString` on the newtype. \
See `examples/custom-types` in the buffa repository for a template."
)
)]
pub trait ProtoString:
Clone
+ PartialEq
+ Default
+ core::fmt::Debug
+ Send
+ Sync
+ core::ops::Deref<Target = str>
+ AsRef<str>
+ From<String>
+ for<'a> From<&'a str>
{
fn from_wire(payload: WirePayload<'_>) -> Result<Self, DecodeError>;
}
impl ProtoString for String {
#[inline]
fn from_wire(payload: WirePayload<'_>) -> Result<Self, DecodeError> {
payload.to_str().map(alloc::borrow::ToOwned::to_owned)
}
}
const _: fn() = || {
fn assert_proto_string<S: ProtoString>() {}
assert_proto_string::<String>();
};
#[inline]
pub fn decode_string_to<S: ProtoString>(buf: &mut impl Buf) -> Result<S, DecodeError> {
read_field_payload(buf, S::from_wire)
}
#[inline]
pub fn encode_bytes(value: &[u8], buf: &mut impl BufMut) {
encode_varint(value.len() as u64, buf);
buf.put_slice(value);
}
#[inline]
pub fn decode_bytes(buf: &mut impl Buf) -> Result<Vec<u8>, DecodeError> {
let len = decode_varint(buf)?;
let len = usize::try_from(len).map_err(|_| DecodeError::MessageTooLarge)?;
if buf.remaining() < len {
return Err(DecodeError::UnexpectedEof);
}
#[allow(clippy::uninit_vec)]
let mut bytes = {
let mut v = alloc::vec::Vec::with_capacity(len);
unsafe { v.set_len(len) };
v
};
buf.copy_to_slice(&mut bytes);
Ok(bytes)
}
#[inline]
pub fn decode_bytes_to_bytes(buf: &mut impl Buf) -> Result<Bytes, DecodeError> {
let len = decode_varint(buf)?;
let len = usize::try_from(len).map_err(|_| DecodeError::MessageTooLarge)?;
if buf.remaining() < len {
return Err(DecodeError::UnexpectedEof);
}
Ok(buf.copy_to_bytes(len))
}
#[rustversion::attr(
since(1.78),
diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as a buffa custom bytes type",
note = "buffa owns `ProtoBytes`, so a foreign type can't implement it directly (orphan rule). \
Wrap it in a crate-local newtype and implement `ProtoBytes` on the newtype. \
See the `custom-types` example in the buffa repository for a template."
)
)]
pub trait ProtoBytes:
Clone
+ PartialEq
+ Default
+ core::fmt::Debug
+ Send
+ Sync
+ core::ops::Deref<Target = [u8]>
+ AsRef<[u8]>
+ From<Vec<u8>>
{
fn from_wire(payload: WirePayload<'_>) -> Result<Self, DecodeError>;
}
impl ProtoBytes for Vec<u8> {
#[inline]
fn from_wire(payload: WirePayload<'_>) -> Result<Self, DecodeError> {
Ok(payload.as_slice().to_vec())
}
}
impl ProtoBytes for Bytes {
#[inline]
fn from_wire(payload: WirePayload<'_>) -> Result<Self, DecodeError> {
Ok(payload.into_bytes())
}
}
const _: fn() = || {
fn assert_proto_bytes<B: ProtoBytes>() {}
assert_proto_bytes::<Vec<u8>>();
assert_proto_bytes::<Bytes>();
};
#[inline]
pub fn decode_bytes_to<B: ProtoBytes>(buf: &mut impl Buf) -> Result<B, DecodeError> {
read_field_payload(buf, B::from_wire)
}
#[rustversion::attr(
since(1.78),
diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as a buffa custom list type",
note = "buffa owns `ProtoList`, so a foreign type can't implement it directly (orphan rule). \
Wrap it in a crate-local newtype and implement `ProtoList` on the newtype. \
See the `custom-types` example in the buffa repository for a template."
)
)]
pub trait ProtoList<T>:
Default
+ Clone
+ PartialEq
+ core::fmt::Debug
+ Send
+ Sync
+ FromIterator<T>
+ From<Vec<T>>
+ core::ops::Deref<Target = [T]>
{
fn push(&mut self, value: T);
fn clear(&mut self);
#[inline]
fn reserve(&mut self, additional: usize) {
let _ = additional;
}
}
impl<T> ProtoList<T> for Vec<T>
where
T: Clone + PartialEq + core::fmt::Debug + Send + Sync,
{
#[inline]
fn push(&mut self, value: T) {
Vec::push(self, value);
}
#[inline]
fn clear(&mut self) {
Vec::clear(self);
}
#[inline]
fn reserve(&mut self, additional: usize) {
Vec::reserve(self, additional);
}
}
const _: fn() = || {
fn assert_proto_list<C: ProtoList<T>, T>() {}
assert_proto_list::<Vec<u32>, u32>();
};
#[inline]
pub fn merge_bytes(value: &mut Vec<u8>, buf: &mut impl Buf) -> Result<(), DecodeError> {
let len = decode_varint(buf)?;
let len = usize::try_from(len).map_err(|_| DecodeError::MessageTooLarge)?;
if buf.remaining() < len {
return Err(DecodeError::UnexpectedEof);
}
value.clear();
value.reserve(len);
#[allow(clippy::uninit_vec)]
unsafe {
value.set_len(len);
}
buf.copy_to_slice(value);
Ok(())
}
#[inline]
pub fn bytes_encoded_len(value: &[u8]) -> usize {
let len = value.len();
varint_len(len as u64) + len
}
#[inline]
pub fn borrow_str<'a>(buf: &mut &'a [u8]) -> Result<&'a str, DecodeError> {
let len = decode_varint(buf)?;
let len = usize::try_from(len).map_err(|_| DecodeError::MessageTooLarge)?;
if buf.len() < len {
return Err(DecodeError::UnexpectedEof);
}
let s = unsafe { validate_str_in(buf, len) };
*buf = &buf[len..];
s
}
#[inline]
pub fn borrow_bytes<'a>(buf: &mut &'a [u8]) -> Result<&'a [u8], DecodeError> {
let len = decode_varint(buf)?;
let len = usize::try_from(len).map_err(|_| DecodeError::MessageTooLarge)?;
if buf.len() < len {
return Err(DecodeError::UnexpectedEof);
}
let bytes = &buf[..len];
*buf = &buf[len..];
Ok(bytes)
}
pub fn borrow_group<'a>(
buf: &mut &'a [u8],
field_number: u32,
depth: u32,
) -> Result<&'a [u8], DecodeError> {
let start = *buf;
let mut scan: &[u8] = start;
loop {
if scan.is_empty() {
return Err(DecodeError::UnexpectedEof);
}
let before_tag = scan.len();
let tag = crate::encoding::Tag::decode(&mut scan)?;
if tag.wire_type() == crate::encoding::WireType::EndGroup {
if tag.field_number() != field_number {
return Err(DecodeError::InvalidEndGroup(tag.field_number()));
}
let body_len = start.len() - before_tag;
let body = &start[..body_len];
*buf = scan;
return Ok(body);
}
crate::encoding::skip_field_depth(tag, &mut scan, depth)?;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validate_str_accepts_valid_rejects_invalid() {
assert_eq!(validate_str(b"hello").unwrap(), "hello");
assert_eq!(validate_str("héllo 🌍".as_bytes()).unwrap(), "héllo 🌍");
assert_eq!(validate_str(b""), Ok(""));
assert!(matches!(
validate_str(&[0xC0, 0x80]), Err(DecodeError::InvalidUtf8)
));
}
#[test]
fn validate_str_in_slack_and_tail_paths_agree() {
unsafe {
let with_slack = b"hello\x00\x00\x00\x00\x00\x00\x00\x00";
assert_eq!(validate_str_in(with_slack, 5).unwrap(), "hello");
assert_eq!(validate_str_in(b"hello", 5).unwrap(), "hello");
let bad_with_slack = b"\xC0\x80padpadpa";
assert!(matches!(
validate_str_in(bad_with_slack, 2),
Err(DecodeError::InvalidUtf8)
));
assert!(matches!(
validate_str_in(&[0xC0, 0x80], 2),
Err(DecodeError::InvalidUtf8)
));
let junk_slack = b"ok\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
assert_eq!(validate_str_in(junk_slack, 2).unwrap(), "ok");
}
}
#[test]
fn borrow_str_uses_surrounding_buffer_for_slack() {
let mut wire = alloc::vec::Vec::new();
for s in ["hi", "tail-no-slack"] {
wire.push(s.len() as u8);
wire.extend_from_slice(s.as_bytes());
}
let mut cur: &[u8] = &wire;
assert_eq!(borrow_str(&mut cur).unwrap(), "hi");
assert_eq!(borrow_str(&mut cur).unwrap(), "tail-no-slack");
assert!(cur.is_empty());
}
#[test]
fn wire_payload_to_str() {
assert_eq!(WirePayload::Borrowed(b"abc").to_str().unwrap(), "abc");
assert!(matches!(
WirePayload::Borrowed(&[0xFF]).to_str(),
Err(DecodeError::InvalidUtf8)
));
}
#[derive(Clone, PartialEq, Default, Debug)]
struct Tiny(alloc::string::String);
impl core::ops::Deref for Tiny {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
impl AsRef<str> for Tiny {
fn as_ref(&self) -> &str {
&self.0
}
}
impl From<alloc::string::String> for Tiny {
fn from(s: alloc::string::String) -> Self {
Tiny(s)
}
}
impl From<&str> for Tiny {
fn from(s: &str) -> Self {
Tiny(s.into())
}
}
impl ProtoString for Tiny {
fn from_wire(p: WirePayload<'_>) -> Result<Self, DecodeError> {
let s = core::str::from_utf8(p.as_slice()).map_err(|_| DecodeError::InvalidUtf8)?;
if s.len() > 3 {
return Err(DecodeError::Custom("string too long"));
}
Ok(Tiny(s.into()))
}
}
#[test]
fn from_wire_can_surface_custom_decode_error() {
let mut buf: &[u8] = b"\x05hello";
assert_eq!(
decode_string_to::<Tiny>(&mut buf).unwrap_err(),
DecodeError::Custom("string too long"),
);
let mut ok: &[u8] = b"\x02hi";
assert_eq!(
decode_string_to::<Tiny>(&mut ok).unwrap(),
Tiny("hi".into())
);
}
#[test]
fn put_field_fns_match_tag_plus_encode() {
macro_rules! check {
($put:ident, $encode:ident, $wire:expr, $value:expr) => {{
let mut fused = Vec::new();
$put(7, $value, &mut fused);
let mut split = Vec::new();
Tag::new(7, $wire).encode(&mut split);
$encode($value, &mut split);
assert_eq!(fused, split, stringify!($put));
}};
}
check!(put_int32_field, encode_int32, WireType::Varint, -5i32);
check!(put_int64_field, encode_int64, WireType::Varint, -5i64);
check!(put_uint32_field, encode_uint32, WireType::Varint, 300u32);
check!(put_uint64_field, encode_uint64, WireType::Varint, 300u64);
check!(put_sint32_field, encode_sint32, WireType::Varint, -5i32);
check!(put_sint64_field, encode_sint64, WireType::Varint, -5i64);
check!(put_bool_field, encode_bool, WireType::Varint, true);
check!(put_fixed32_field, encode_fixed32, WireType::Fixed32, 9u32);
check!(put_fixed64_field, encode_fixed64, WireType::Fixed64, 9u64);
check!(
put_sfixed32_field,
encode_sfixed32,
WireType::Fixed32,
-9i32
);
check!(
put_sfixed64_field,
encode_sfixed64,
WireType::Fixed64,
-9i64
);
check!(put_float_field, encode_float, WireType::Fixed32, 1.5f32);
check!(put_double_field, encode_double, WireType::Fixed64, 1.5f64);
check!(
put_string_field,
encode_string,
WireType::LengthDelimited,
"hi"
);
check!(
put_bytes_field,
encode_bytes,
WireType::LengthDelimited,
&[1u8, 2][..]
);
}
#[test]
fn put_len_delimited_header_and_group_tags() {
let mut fused = Vec::new();
put_len_delimited_header(3, 5, &mut fused);
let mut split = Vec::new();
Tag::new(3, WireType::LengthDelimited).encode(&mut split);
encode_varint(5, &mut split);
assert_eq!(fused, split);
let mut fused = Vec::new();
put_group_start(4, &mut fused);
put_group_end(4, &mut fused);
let mut split = Vec::new();
Tag::new(4, WireType::StartGroup).encode(&mut split);
Tag::new(4, WireType::EndGroup).encode(&mut split);
assert_eq!(fused, split);
}
#[test]
fn test_zigzag_i32() {
assert_eq!(zigzag_encode_i32(0), 0);
assert_eq!(zigzag_encode_i32(-1), 1);
assert_eq!(zigzag_encode_i32(1), 2);
assert_eq!(zigzag_encode_i32(-2), 3);
assert_eq!(zigzag_encode_i32(2147483647), 4294967294);
assert_eq!(zigzag_encode_i32(-2147483648), 4294967295);
for v in [0, -1, 1, -2, 2, i32::MAX, i32::MIN] {
assert_eq!(zigzag_decode_i32(zigzag_encode_i32(v)), v);
}
}
#[test]
fn test_zigzag_i64() {
assert_eq!(zigzag_encode_i64(0), 0);
assert_eq!(zigzag_encode_i64(-1), 1);
assert_eq!(zigzag_encode_i64(1), 2);
assert_eq!(zigzag_encode_i64(-2), 3);
for v in [0, -1, 1, -2, 2, i64::MAX, i64::MIN] {
assert_eq!(zigzag_decode_i64(zigzag_encode_i64(v)), v);
}
}
#[test]
fn test_float_roundtrip() {
let values: &[f32] = &[
0.0,
-0.0,
1.0,
-1.0,
f32::MIN,
f32::MAX,
f32::MIN_POSITIVE,
f32::EPSILON,
f32::INFINITY,
f32::NEG_INFINITY,
core::f32::consts::PI,
1.0e-38, 1.0e38, ];
for &v in values {
let mut buf = Vec::new();
encode_float(v, &mut buf);
assert_eq!(buf.len(), 4, "float should encode to exactly 4 bytes");
let decoded = decode_float(&mut buf.as_slice()).unwrap();
assert_eq!(
v.to_bits(),
decoded.to_bits(),
"bit-exact roundtrip for {v}"
);
}
}
#[test]
fn test_float_nan_roundtrip() {
let nan = f32::NAN;
let mut buf = Vec::new();
encode_float(nan, &mut buf);
let decoded = decode_float(&mut buf.as_slice()).unwrap();
assert!(decoded.is_nan());
assert_eq!(nan.to_bits(), decoded.to_bits());
}
#[test]
fn test_float_little_endian() {
let mut buf = Vec::new();
encode_float(1.0, &mut buf);
assert_eq!(buf, &[0x00, 0x00, 0x80, 0x3F]);
}
#[test]
fn test_float_decode_truncated() {
let buf: &[u8] = &[0x00, 0x00, 0x80]; let result = decode_float(&mut &buf[..]);
assert!(result.is_err());
}
#[test]
fn test_double_roundtrip() {
let values: &[f64] = &[
0.0,
-0.0,
1.0,
-1.0,
f64::MIN,
f64::MAX,
f64::MIN_POSITIVE,
f64::EPSILON,
f64::INFINITY,
f64::NEG_INFINITY,
core::f64::consts::PI,
1.0e-307, 1.0e308, ];
for &v in values {
let mut buf = Vec::new();
encode_double(v, &mut buf);
assert_eq!(buf.len(), 8, "double should encode to exactly 8 bytes");
let decoded = decode_double(&mut buf.as_slice()).unwrap();
assert_eq!(
v.to_bits(),
decoded.to_bits(),
"bit-exact roundtrip for {v}"
);
}
}
#[test]
fn test_double_nan_roundtrip() {
let nan = f64::NAN;
let mut buf = Vec::new();
encode_double(nan, &mut buf);
let decoded = decode_double(&mut buf.as_slice()).unwrap();
assert!(decoded.is_nan());
assert_eq!(nan.to_bits(), decoded.to_bits());
}
#[test]
fn test_double_little_endian() {
let mut buf = Vec::new();
encode_double(1.0, &mut buf);
assert_eq!(buf, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F]);
}
#[test]
fn test_double_decode_truncated() {
let buf: &[u8] = &[0x00; 7]; let result = decode_double(&mut &buf[..]);
assert!(result.is_err());
}
#[test]
fn test_fixed32_roundtrip() {
let values: &[u32] = &[0, 1, 255, 256, 65535, u32::MAX, 0xDEAD_BEEF];
for &v in values {
let mut buf = Vec::new();
encode_fixed32(v, &mut buf);
assert_eq!(buf.len(), 4);
let decoded = decode_fixed32(&mut buf.as_slice()).unwrap();
assert_eq!(v, decoded);
}
}
#[test]
fn test_fixed32_little_endian() {
let mut buf = Vec::new();
encode_fixed32(0x01020304, &mut buf);
assert_eq!(buf, &[0x04, 0x03, 0x02, 0x01]);
}
#[test]
fn test_sfixed32_roundtrip() {
let values: &[i32] = &[0, 1, -1, 127, -128, i32::MAX, i32::MIN, 0x7FFF_FFFF];
for &v in values {
let mut buf = Vec::new();
encode_sfixed32(v, &mut buf);
assert_eq!(buf.len(), 4);
let decoded = decode_sfixed32(&mut buf.as_slice()).unwrap();
assert_eq!(v, decoded);
}
}
#[test]
fn test_sfixed32_negative_encoding() {
let mut buf = Vec::new();
encode_sfixed32(-1, &mut buf);
assert_eq!(buf, &[0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_fixed32_decode_truncated() {
let result = decode_fixed32(&mut &[0x01, 0x02, 0x03][..]);
assert!(result.is_err());
}
#[test]
fn test_fixed64_roundtrip() {
let values: &[u64] = &[
0,
1,
255,
65535,
u32::MAX as u64,
u64::MAX,
0xDEAD_BEEF_CAFE_BABE,
];
for &v in values {
let mut buf = Vec::new();
encode_fixed64(v, &mut buf);
assert_eq!(buf.len(), 8);
let decoded = decode_fixed64(&mut buf.as_slice()).unwrap();
assert_eq!(v, decoded);
}
}
#[test]
fn test_fixed64_little_endian() {
let mut buf = Vec::new();
encode_fixed64(0x0102030405060708, &mut buf);
assert_eq!(buf, &[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
}
#[test]
fn test_sfixed64_roundtrip() {
let values: &[i64] = &[0, 1, -1, 127, -128, i64::MAX, i64::MIN];
for &v in values {
let mut buf = Vec::new();
encode_sfixed64(v, &mut buf);
assert_eq!(buf.len(), 8);
let decoded = decode_sfixed64(&mut buf.as_slice()).unwrap();
assert_eq!(v, decoded);
}
}
#[test]
fn test_sfixed64_negative_encoding() {
let mut buf = Vec::new();
encode_sfixed64(-1, &mut buf);
assert_eq!(buf, &[0xFF; 8]);
}
#[test]
fn test_fixed64_decode_truncated() {
let result = decode_fixed64(&mut &[0x01; 7][..]);
assert!(result.is_err());
}
#[test]
fn test_decode_empty_buffer() {
let empty: &[u8] = &[];
assert!(decode_float(&mut &empty[..]).is_err());
assert!(decode_double(&mut &empty[..]).is_err());
assert!(decode_fixed32(&mut &empty[..]).is_err());
assert!(decode_fixed64(&mut &empty[..]).is_err());
assert!(decode_sfixed32(&mut &empty[..]).is_err());
assert!(decode_sfixed64(&mut &empty[..]).is_err());
assert!(decode_int32(&mut &empty[..]).is_err());
assert!(decode_int64(&mut &empty[..]).is_err());
assert!(decode_uint32(&mut &empty[..]).is_err());
assert!(decode_uint64(&mut &empty[..]).is_err());
assert!(decode_sint32(&mut &empty[..]).is_err());
assert!(decode_sint64(&mut &empty[..]).is_err());
assert!(decode_bool(&mut &empty[..]).is_err());
}
#[test]
fn test_encoded_size_constants() {
assert_eq!(FIXED32_ENCODED_LEN, 4);
assert_eq!(FIXED64_ENCODED_LEN, 8);
assert_eq!(BOOL_ENCODED_LEN, 1);
}
#[test]
fn test_int32_roundtrip() {
let values: &[i32] = &[0, 1, -1, 127, -128, i32::MAX, i32::MIN, 300, -300];
for &v in values {
let mut buf = Vec::new();
encode_int32(v, &mut buf);
let decoded = decode_int32(&mut buf.as_slice()).unwrap();
assert_eq!(v, decoded, "int32 roundtrip failed for {v}");
}
}
#[test]
fn test_int32_negative_is_ten_bytes() {
let mut buf = Vec::new();
encode_int32(-1, &mut buf);
assert_eq!(buf.len(), 10, "negative int32 should encode to 10 bytes");
assert_eq!(int32_encoded_len(-1), 10);
}
#[test]
fn test_int32_positive_size() {
assert_eq!(int32_encoded_len(0), 1);
assert_eq!(int32_encoded_len(127), 1);
assert_eq!(int32_encoded_len(128), 2);
assert_eq!(int32_encoded_len(i32::MAX), 5);
}
#[test]
fn test_int64_roundtrip() {
let values: &[i64] = &[0, 1, -1, 127, -128, i64::MAX, i64::MIN, 300, -300];
for &v in values {
let mut buf = Vec::new();
encode_int64(v, &mut buf);
let decoded = decode_int64(&mut buf.as_slice()).unwrap();
assert_eq!(v, decoded, "int64 roundtrip failed for {v}");
}
}
#[test]
fn test_int64_negative_is_ten_bytes() {
let mut buf = Vec::new();
encode_int64(-1, &mut buf);
assert_eq!(buf.len(), 10);
assert_eq!(int64_encoded_len(-1), 10);
}
#[test]
fn test_uint32_roundtrip() {
let values: &[u32] = &[0, 1, 127, 128, 255, 300, u32::MAX];
for &v in values {
let mut buf = Vec::new();
encode_uint32(v, &mut buf);
let decoded = decode_uint32(&mut buf.as_slice()).unwrap();
assert_eq!(v, decoded, "uint32 roundtrip failed for {v}");
}
}
#[test]
fn test_uint32_encoded_len() {
assert_eq!(uint32_encoded_len(0), 1);
assert_eq!(uint32_encoded_len(127), 1);
assert_eq!(uint32_encoded_len(128), 2);
assert_eq!(uint32_encoded_len(u32::MAX), 5);
}
#[test]
fn test_uint64_roundtrip() {
let values: &[u64] = &[0, 1, 127, 128, u32::MAX as u64, u64::MAX];
for &v in values {
let mut buf = Vec::new();
encode_uint64(v, &mut buf);
let decoded = decode_uint64(&mut buf.as_slice()).unwrap();
assert_eq!(v, decoded, "uint64 roundtrip failed for {v}");
}
}
#[test]
fn test_uint64_encoded_len() {
assert_eq!(uint64_encoded_len(0), 1);
assert_eq!(uint64_encoded_len(u64::MAX), 10);
}
#[test]
fn test_sint32_roundtrip() {
let values: &[i32] = &[0, -1, 1, -2, 2, i32::MAX, i32::MIN, -300, 300];
for &v in values {
let mut buf = Vec::new();
encode_sint32(v, &mut buf);
let decoded = decode_sint32(&mut buf.as_slice()).unwrap();
assert_eq!(v, decoded, "sint32 roundtrip failed for {v}");
}
}
#[test]
fn test_sint32_negative_is_compact() {
let mut buf = Vec::new();
encode_sint32(-1, &mut buf);
assert_eq!(buf.len(), 1);
assert_eq!(sint32_encoded_len(-1), 1);
assert_eq!(sint32_encoded_len(i32::MIN), 5);
}
#[test]
fn test_sint64_roundtrip() {
let values: &[i64] = &[0, -1, 1, -2, 2, i64::MAX, i64::MIN, -300, 300];
for &v in values {
let mut buf = Vec::new();
encode_sint64(v, &mut buf);
let decoded = decode_sint64(&mut buf.as_slice()).unwrap();
assert_eq!(v, decoded, "sint64 roundtrip failed for {v}");
}
}
#[test]
fn test_sint64_negative_is_compact() {
let mut buf = Vec::new();
encode_sint64(-1, &mut buf);
assert_eq!(buf.len(), 1);
assert_eq!(sint64_encoded_len(-1), 1);
}
#[test]
fn test_bool_roundtrip() {
for v in [true, false] {
let mut buf = Vec::new();
encode_bool(v, &mut buf);
assert_eq!(buf.len(), 1, "bool should encode to exactly 1 byte");
let decoded = decode_bool(&mut buf.as_slice()).unwrap();
assert_eq!(v, decoded, "bool roundtrip failed for {v}");
}
}
#[test]
fn test_bool_wire_values() {
let mut buf = Vec::new();
encode_bool(true, &mut buf);
assert_eq!(buf, &[0x01]);
buf.clear();
encode_bool(false, &mut buf);
assert_eq!(buf, &[0x00]);
}
#[test]
fn test_bool_nonzero_decodes_as_true() {
for byte in [0x02u8, 0x7F, 0xFF] {
let raw = byte & 0x7F;
let decoded = decode_bool(&mut &[raw][..]).unwrap();
assert!(decoded, "0x{raw:02X} should decode as true");
}
}
#[test]
fn test_bool_multibyte_varint_decodes_as_true() {
let decoded = decode_bool(&mut &[0x80u8, 0x01][..]).unwrap();
assert!(decoded);
}
#[test]
fn test_varint_encoded_len_consistency() {
let i32_cases: &[i32] = &[0, 1, -1, 127, 128, i32::MAX, i32::MIN];
for &v in i32_cases {
let mut buf = Vec::new();
encode_int32(v, &mut buf);
assert_eq!(
buf.len(),
int32_encoded_len(v),
"int32_encoded_len mismatch for {v}"
);
}
let i64_cases: &[i64] = &[0, 1, -1, i64::MAX, i64::MIN];
for &v in i64_cases {
let mut buf = Vec::new();
encode_int64(v, &mut buf);
assert_eq!(
buf.len(),
int64_encoded_len(v),
"int64_encoded_len mismatch for {v}"
);
}
let u32_cases: &[u32] = &[0, 1, 127, 128, u32::MAX];
for &v in u32_cases {
let mut buf = Vec::new();
encode_uint32(v, &mut buf);
assert_eq!(
buf.len(),
uint32_encoded_len(v),
"uint32_encoded_len mismatch for {v}"
);
}
let u64_cases: &[u64] = &[0, 1, 127, 128, u64::MAX];
for &v in u64_cases {
let mut buf = Vec::new();
encode_uint64(v, &mut buf);
assert_eq!(
buf.len(),
uint64_encoded_len(v),
"uint64_encoded_len mismatch for {v}"
);
}
let sint32_cases: &[i32] = &[0, -1, 1, i32::MAX, i32::MIN];
for &v in sint32_cases {
let mut buf = Vec::new();
encode_sint32(v, &mut buf);
assert_eq!(
buf.len(),
sint32_encoded_len(v),
"sint32_encoded_len mismatch for {v}"
);
}
let sint64_cases: &[i64] = &[0, -1, 1, i64::MAX, i64::MIN];
for &v in sint64_cases {
let mut buf = Vec::new();
encode_sint64(v, &mut buf);
assert_eq!(
buf.len(),
sint64_encoded_len(v),
"sint64_encoded_len mismatch for {v}"
);
}
}
#[test]
fn test_string_roundtrip() {
for s in ["", "hello", "héllo", "世界", "a".repeat(128).as_str()] {
let mut buf = Vec::new();
encode_string(s, &mut buf);
assert_eq!(
buf.len(),
string_encoded_len(s),
"string_encoded_len mismatch"
);
let decoded = decode_string(&mut buf.as_slice()).unwrap();
assert_eq!(s, decoded);
}
}
#[test]
fn test_string_empty() {
let mut buf = Vec::new();
encode_string("", &mut buf);
assert_eq!(buf, &[0x00]);
assert_eq!(string_encoded_len(""), 1);
let decoded = decode_string(&mut buf.as_slice()).unwrap();
assert_eq!(decoded, "");
}
#[test]
fn test_decode_string_to_roundtrip() {
for s in ["", "hello", "héllo", "世界", "a".repeat(128).as_str()] {
let mut buf = Vec::new();
encode_string(s, &mut buf);
let decoded: String = decode_string_to(&mut buf.as_slice()).unwrap();
assert_eq!(s, decoded);
}
}
#[test]
fn test_decode_string_to_propagates_errors() {
let bad: &[u8] = &[0x02, 0xFF, 0xFE];
assert_eq!(
decode_string_to::<String>(&mut &bad[..]),
Err(DecodeError::InvalidUtf8)
);
let short: &[u8] = &[0x04, 0x61, 0x62];
assert_eq!(
decode_string_to::<String>(&mut &short[..]),
Err(DecodeError::UnexpectedEof)
);
}
#[test]
fn test_string_invalid_utf8() {
let buf: &[u8] = &[0x02, 0xFF, 0xFE];
assert_eq!(decode_string(&mut &buf[..]), Err(DecodeError::InvalidUtf8));
}
#[test]
fn test_string_truncated() {
let buf: &[u8] = &[0x04, 0x61, 0x62];
assert_eq!(
decode_string(&mut &buf[..]),
Err(DecodeError::UnexpectedEof)
);
}
#[test]
fn test_bytes_roundtrip() {
for b in [&[][..], &[0x00, 0xFF, 0x80], &[0u8; 128]] {
let mut buf = Vec::new();
encode_bytes(b, &mut buf);
assert_eq!(
buf.len(),
bytes_encoded_len(b),
"bytes_encoded_len mismatch"
);
let decoded = decode_bytes(&mut buf.as_slice()).unwrap();
assert_eq!(b, decoded.as_slice());
}
}
#[test]
fn test_bytes_empty() {
let mut buf = Vec::new();
encode_bytes(&[], &mut buf);
assert_eq!(buf, &[0x00]);
assert_eq!(bytes_encoded_len(&[]), 1);
let decoded = decode_bytes(&mut buf.as_slice()).unwrap();
assert!(decoded.is_empty());
}
#[test]
fn test_bytes_truncated() {
let buf: &[u8] = &[0x05, 0xAA, 0xBB];
assert_eq!(decode_bytes(&mut &buf[..]), Err(DecodeError::UnexpectedEof));
}
#[test]
fn test_decode_bytes_to_bytes_zero_copy_from_bytes() {
let mut enc = Vec::new();
encode_bytes(b"\xDE\xAD\xBE\xEF", &mut enc);
let mut buf = Bytes::from(enc);
let payload_ptr = buf.as_ptr() as usize + 1;
let out = decode_bytes_to_bytes(&mut buf).unwrap();
assert_eq!(&out[..], b"\xDE\xAD\xBE\xEF");
assert_eq!(out.as_ptr() as usize, payload_ptr);
assert!(buf.is_empty());
}
#[test]
fn test_decode_bytes_to_bytes_from_slice() {
let mut enc = Vec::new();
encode_bytes(b"hello", &mut enc);
let out = decode_bytes_to_bytes(&mut enc.as_slice()).unwrap();
assert_eq!(&out[..], b"hello");
}
#[test]
fn test_decode_bytes_to_bytes_empty() {
let mut buf = Bytes::from_static(&[0x00]);
let out = decode_bytes_to_bytes(&mut buf).unwrap();
assert!(out.is_empty());
assert!(buf.is_empty());
}
#[test]
fn test_decode_bytes_to_bytes_truncated() {
let buf: &[u8] = &[0x05, 0xAA, 0xBB];
assert_eq!(
decode_bytes_to_bytes(&mut &buf[..]),
Err(DecodeError::UnexpectedEof)
);
}
#[test]
fn merge_string_reuses_allocation() {
let mut value = String::with_capacity(64);
value.push_str("old content");
let ptr_before = value.as_ptr();
let mut buf = Vec::new();
encode_string("hello", &mut buf);
merge_string(&mut value, &mut buf.as_slice()).unwrap();
assert_eq!(value, "hello");
assert_eq!(value.as_ptr(), ptr_before);
}
#[test]
fn merge_string_empty() {
let mut value = String::from("existing");
let mut buf = Vec::new();
encode_string("", &mut buf);
merge_string(&mut value, &mut buf.as_slice()).unwrap();
assert_eq!(value, "");
}
#[test]
fn merge_string_invalid_utf8() {
let mut value = String::from("valid");
let buf: &[u8] = &[0x02, 0xFF, 0xFE];
let result = merge_string(&mut value, &mut &buf[..]);
assert_eq!(result, Err(DecodeError::InvalidUtf8));
assert!(value.is_empty());
}
#[test]
fn merge_string_truncated() {
let mut value = String::from("existing");
let buf: &[u8] = &[0x04, 0x61, 0x62];
let result = merge_string(&mut value, &mut &buf[..]);
assert_eq!(result, Err(DecodeError::UnexpectedEof));
assert_eq!(value, "existing");
}
#[test]
fn merge_string_roundtrip() {
for s in ["", "hello", "héllo", "世界", &"a".repeat(128)] {
let mut buf = Vec::new();
encode_string(s, &mut buf);
let mut value = String::new();
merge_string(&mut value, &mut buf.as_slice()).unwrap();
assert_eq!(s, value);
}
}
#[test]
fn merge_bytes_reuses_allocation() {
let mut value = Vec::with_capacity(64);
value.extend_from_slice(b"old content");
let ptr_before = value.as_ptr();
let mut buf = Vec::new();
encode_bytes(&[0xDE, 0xAD], &mut buf);
merge_bytes(&mut value, &mut buf.as_slice()).unwrap();
assert_eq!(value, &[0xDE, 0xAD]);
assert_eq!(value.as_ptr(), ptr_before);
}
#[test]
fn merge_bytes_empty() {
let mut value = vec![1, 2, 3];
let mut buf = Vec::new();
encode_bytes(&[], &mut buf);
merge_bytes(&mut value, &mut buf.as_slice()).unwrap();
assert!(value.is_empty());
}
#[test]
fn merge_bytes_truncated() {
let mut value = vec![0xAA];
let buf: &[u8] = &[0x05, 0xAA, 0xBB];
let result = merge_bytes(&mut value, &mut &buf[..]);
assert_eq!(result, Err(DecodeError::UnexpectedEof));
assert_eq!(value, &[0xAA]);
}
#[test]
fn merge_bytes_roundtrip() {
for b in [&[][..], &[0x00, 0xFF, 0x80], &[0u8; 128]] {
let mut buf = Vec::new();
encode_bytes(b, &mut buf);
let mut value = Vec::new();
merge_bytes(&mut value, &mut buf.as_slice()).unwrap();
assert_eq!(b, value.as_slice());
}
}
#[test]
fn borrow_str_valid_borrows_from_source() {
let mut encoded = Vec::new();
encode_string("hello", &mut encoded);
let source: &[u8] = &encoded;
let mut cur: &[u8] = source;
let s = borrow_str(&mut cur).unwrap();
assert_eq!(s, "hello");
assert!(core::ptr::eq(s.as_bytes().as_ptr(), source[1..].as_ptr()));
assert!(cur.is_empty());
}
#[test]
fn borrow_str_empty_string() {
let mut encoded = Vec::new();
encode_string("", &mut encoded);
let mut cur: &[u8] = &encoded;
let s = borrow_str(&mut cur).unwrap();
assert_eq!(s, "");
assert!(cur.is_empty());
}
#[test]
fn borrow_str_advances_cursor() {
let mut encoded = Vec::new();
encode_string("ab", &mut encoded);
encode_string("cd", &mut encoded);
let mut cur: &[u8] = &encoded;
assert_eq!(borrow_str(&mut cur).unwrap(), "ab");
assert_eq!(borrow_str(&mut cur).unwrap(), "cd");
assert!(cur.is_empty());
}
#[test]
fn borrow_str_invalid_utf8_returns_error() {
let buf: &[u8] = &[0x02, 0xFF, 0xFE];
let mut cur: &[u8] = buf;
assert_eq!(borrow_str(&mut cur), Err(DecodeError::InvalidUtf8));
assert!(cur.is_empty());
}
#[test]
fn borrow_str_truncated_returns_eof() {
let buf: &[u8] = &[0x04, 0x61, 0x62]; let mut cur: &[u8] = buf;
assert_eq!(borrow_str(&mut cur), Err(DecodeError::UnexpectedEof));
}
#[test]
fn borrow_bytes_valid_borrows_from_source() {
let mut encoded = Vec::new();
encode_bytes(&[0xDE, 0xAD, 0xBE, 0xEF], &mut encoded);
let source: &[u8] = &encoded;
let mut cur: &[u8] = source;
let b = borrow_bytes(&mut cur).unwrap();
assert_eq!(b, &[0xDE, 0xAD, 0xBE, 0xEF]);
assert!(core::ptr::eq(b.as_ptr(), source[1..].as_ptr()));
assert!(cur.is_empty());
}
#[test]
fn borrow_bytes_empty() {
let mut encoded = Vec::new();
encode_bytes(&[], &mut encoded);
let mut cur: &[u8] = &encoded;
let b = borrow_bytes(&mut cur).unwrap();
assert!(b.is_empty());
assert!(cur.is_empty());
}
#[test]
fn borrow_bytes_advances_cursor() {
let mut encoded = Vec::new();
encode_bytes(&[1, 2], &mut encoded);
encode_bytes(&[3, 4, 5], &mut encoded);
let mut cur: &[u8] = &encoded;
assert_eq!(borrow_bytes(&mut cur).unwrap(), &[1, 2]);
assert_eq!(borrow_bytes(&mut cur).unwrap(), &[3, 4, 5]);
assert!(cur.is_empty());
}
#[test]
fn borrow_bytes_truncated_returns_eof() {
let buf: &[u8] = &[0x05, 0xAA, 0xBB]; let mut cur: &[u8] = buf;
assert_eq!(borrow_bytes(&mut cur), Err(DecodeError::UnexpectedEof));
}
fn build_group_bytes(
fields: &[(u32, crate::encoding::WireType, &[u8])],
group_field_number: u32,
) -> Vec<u8> {
use crate::encoding::Tag;
let mut buf = Vec::new();
for &(fnum, wt, data) in fields {
Tag::new(fnum, wt).encode(&mut buf);
buf.extend_from_slice(data);
}
Tag::new(group_field_number, crate::encoding::WireType::EndGroup).encode(&mut buf);
buf
}
#[test]
fn borrow_group_empty() {
let data = build_group_bytes(&[], 1);
let mut cur: &[u8] = &data;
let body = borrow_group(&mut cur, 1, crate::RECURSION_LIMIT).unwrap();
assert!(body.is_empty());
assert!(cur.is_empty());
}
#[test]
fn borrow_group_one_varint_field() {
let mut varint_buf = Vec::new();
crate::encoding::encode_varint(150, &mut varint_buf);
let data = build_group_bytes(&[(2, crate::encoding::WireType::Varint, &varint_buf)], 1);
let mut cur: &[u8] = &data;
let body = borrow_group(&mut cur, 1, crate::RECURSION_LIMIT).unwrap();
let expected_body_len = data.len() - crate::encoding::varint_len(((1u64) << 3) | 4);
assert_eq!(body.len(), expected_body_len);
assert!(cur.is_empty());
let mut body_cur = body;
let tag = crate::encoding::Tag::decode(&mut body_cur).unwrap();
assert_eq!(tag.field_number(), 2);
assert_eq!(crate::encoding::decode_varint(&mut body_cur).unwrap(), 150);
assert!(body_cur.is_empty());
}
#[test]
fn borrow_group_nested_group() {
use crate::encoding::{Tag, WireType};
let mut data = Vec::new();
Tag::new(3, WireType::StartGroup).encode(&mut data);
Tag::new(4, WireType::Varint).encode(&mut data);
crate::encoding::encode_varint(42, &mut data);
Tag::new(3, WireType::EndGroup).encode(&mut data);
Tag::new(1, WireType::EndGroup).encode(&mut data);
let mut cur: &[u8] = &data;
let body = borrow_group(&mut cur, 1, crate::RECURSION_LIMIT).unwrap();
assert!(cur.is_empty());
assert!(!body.is_empty());
let mut body_cur = body;
let inner_tag = crate::encoding::Tag::decode(&mut body_cur).unwrap();
assert_eq!(inner_tag.field_number(), 3);
assert_eq!(inner_tag.wire_type(), WireType::StartGroup);
let inner_body = borrow_group(&mut body_cur, 3, crate::RECURSION_LIMIT).unwrap();
assert!(body_cur.is_empty());
let mut inner_cur = inner_body;
let f4_tag = crate::encoding::Tag::decode(&mut inner_cur).unwrap();
assert_eq!(f4_tag.field_number(), 4);
assert_eq!(crate::encoding::decode_varint(&mut inner_cur).unwrap(), 42);
assert!(inner_cur.is_empty());
}
#[test]
fn borrow_group_mismatched_end() {
use crate::encoding::{Tag, WireType};
let mut data = Vec::new();
Tag::new(99, WireType::EndGroup).encode(&mut data);
let mut cur: &[u8] = &data;
assert_eq!(
borrow_group(&mut cur, 1, crate::RECURSION_LIMIT),
Err(DecodeError::InvalidEndGroup(99))
);
}
#[test]
fn borrow_group_truncated() {
use crate::encoding::{Tag, WireType};
let mut data = Vec::new();
Tag::new(2, WireType::Varint).encode(&mut data);
crate::encoding::encode_varint(42, &mut data);
let mut cur: &[u8] = &data;
assert_eq!(
borrow_group(&mut cur, 1, crate::RECURSION_LIMIT),
Err(DecodeError::UnexpectedEof)
);
}
#[test]
fn borrow_group_empty_buffer() {
let mut cur: &[u8] = &[];
assert_eq!(
borrow_group(&mut cur, 1, crate::RECURSION_LIMIT),
Err(DecodeError::UnexpectedEof)
);
}
#[test]
fn borrow_group_trailing_data_preserved() {
use crate::encoding::{Tag, WireType};
let mut data = Vec::new();
Tag::new(1, WireType::EndGroup).encode(&mut data);
data.extend_from_slice(&[0xDE, 0xAD]);
let mut cur: &[u8] = &data;
let body = borrow_group(&mut cur, 1, crate::RECURSION_LIMIT).unwrap();
assert!(body.is_empty());
assert_eq!(cur, &[0xDE, 0xAD]);
}
#[test]
fn borrow_group_nested_start_group_respects_depth_limit() {
use crate::encoding::{Tag, WireType};
let mut data = Vec::new();
Tag::new(2, WireType::StartGroup).encode(&mut data);
Tag::new(2, WireType::EndGroup).encode(&mut data);
Tag::new(1, WireType::EndGroup).encode(&mut data);
let mut cur: &[u8] = &data;
assert_eq!(
borrow_group(&mut cur, 1, 0),
Err(DecodeError::RecursionLimitExceeded)
);
let mut cur: &[u8] = &data;
let body = borrow_group(&mut cur, 1, 1).unwrap();
assert!(!body.is_empty());
assert!(cur.is_empty());
}
#[cfg(target_pointer_width = "32")]
const OVERSIZED_VARINT: &[u8] = &[0x80, 0x80, 0x80, 0x80, 0x10];
#[test]
#[cfg(target_pointer_width = "32")]
fn decode_string_rejects_oversized_length_on_32bit() {
let mut buf = OVERSIZED_VARINT;
assert_eq!(decode_string(&mut buf), Err(DecodeError::MessageTooLarge));
}
#[test]
#[cfg(target_pointer_width = "32")]
fn decode_bytes_rejects_oversized_length_on_32bit() {
let mut buf = OVERSIZED_VARINT;
assert_eq!(decode_bytes(&mut buf), Err(DecodeError::MessageTooLarge));
}
#[test]
#[cfg(target_pointer_width = "32")]
fn merge_string_rejects_oversized_length_on_32bit() {
let mut value = String::new();
let mut buf = OVERSIZED_VARINT;
assert_eq!(
merge_string(&mut value, &mut buf),
Err(DecodeError::MessageTooLarge)
);
}
#[test]
#[cfg(target_pointer_width = "32")]
fn merge_bytes_rejects_oversized_length_on_32bit() {
let mut value = Vec::new();
let mut buf = OVERSIZED_VARINT;
assert_eq!(
merge_bytes(&mut value, &mut buf),
Err(DecodeError::MessageTooLarge)
);
}
#[test]
#[cfg(target_pointer_width = "32")]
fn borrow_str_rejects_oversized_length_on_32bit() {
let mut buf: &[u8] = OVERSIZED_VARINT;
assert_eq!(borrow_str(&mut buf), Err(DecodeError::MessageTooLarge));
}
#[test]
#[cfg(target_pointer_width = "32")]
fn borrow_bytes_rejects_oversized_length_on_32bit() {
let mut buf: &[u8] = OVERSIZED_VARINT;
assert_eq!(borrow_bytes(&mut buf), Err(DecodeError::MessageTooLarge));
}
}