use core::str;
use crate::payload::{EncodeError, Malformed, View};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum WireType {
Varint = 0,
I64 = 1,
Len = 2,
I32 = 5,
}
impl WireType {
const fn from_tag(tag: u64) -> Option<WireType> {
match tag & 0x7 {
0 => Some(WireType::Varint),
1 => Some(WireType::I64),
2 => Some(WireType::Len),
5 => Some(WireType::I32),
_ => None,
}
}
}
const MAX_VARINT_LEN: usize = 10;
pub const fn varint_len(v: u64) -> usize {
let bits = 64 - (v | 1).leading_zeros() as usize;
bits.div_ceil(7)
}
pub const fn tag_len(number: u32) -> usize {
varint_len((number as u64) << 3)
}
pub const fn zigzag32(v: i32) -> u32 {
((v << 1) ^ (v >> 31)) as u32
}
pub const fn zigzag64(v: i64) -> u64 {
((v << 1) ^ (v >> 63)) as u64
}
pub const fn unzigzag32(v: u32) -> i32 {
((v >> 1) as i32) ^ -((v & 1) as i32)
}
pub const fn unzigzag64(v: u64) -> i64 {
((v >> 1) as i64) ^ -((v & 1) as i64)
}
pub fn check_capacity(needed: usize, out: &[u8]) -> Result<(), EncodeError> {
let available = out.len();
if needed > available {
return Err(EncodeError::Capacity { needed, available });
}
Ok(())
}
pub fn read_varint(buf: &[u8], at: &mut usize) -> Result<u64, Malformed> {
let mut value: u64 = 0;
let mut index = 0;
loop {
let byte = *at
.checked_add(index)
.and_then(|i| buf.get(i))
.ok_or(Malformed::OutOfBounds)?;
if index == MAX_VARINT_LEN - 1 {
if byte > 1 {
return Err(Malformed::Varint);
}
value |= u64::from(byte) << (7 * index);
*at += MAX_VARINT_LEN;
return Ok(value);
}
value |= u64::from(byte & 0x7f) << (7 * index);
index += 1;
if byte & 0x80 == 0 {
*at += index;
return Ok(value);
}
}
}
pub fn narrow_u32(v: u64) -> Result<u32, Malformed> {
u32::try_from(v).map_err(|_| Malformed::Varint)
}
pub fn narrow_i32(v: u64) -> Result<i32, Malformed> {
i32::try_from(v as i64).map_err(|_| Malformed::Varint)
}
pub fn unzigzag32_checked(v: u64) -> Result<i32, Malformed> {
narrow_u32(v).map(unzigzag32)
}
pub fn utf8(b: &[u8]) -> Result<&str, Malformed> {
str::from_utf8(b).map_err(|_| Malformed::Utf8)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Value<'a> {
Varint(u64),
I64(u64),
Len(&'a [u8]),
I32(u32),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Field<'a> {
pub number: u32,
pub value: Value<'a>,
}
#[derive(Clone, Debug)]
pub struct Reader<'a> {
buf: &'a [u8],
at: usize,
}
impl<'a> Reader<'a> {
pub fn new(buf: &'a [u8]) -> Self {
Reader { buf, at: 0 }
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<Result<Field<'a>, Malformed>> {
if self.at >= self.buf.len() {
return None;
}
let result = self.read_field();
if result.is_err() {
self.at = self.buf.len();
}
Some(result)
}
fn read_field(&mut self) -> Result<Field<'a>, Malformed> {
let tag = narrow_u32(read_varint(self.buf, &mut self.at)?)?;
let number = tag >> 3;
let wire = WireType::from_tag(u64::from(tag)).ok_or(Malformed::WireType)?;
if number == 0 {
return Err(Malformed::WireType);
}
let value = match wire {
WireType::Varint => Value::Varint(read_varint(self.buf, &mut self.at)?),
WireType::I64 => {
let mut bytes = [0u8; 8];
bytes.copy_from_slice(self.take(8)?);
Value::I64(u64::from_le_bytes(bytes))
}
WireType::Len => {
let len = read_varint(self.buf, &mut self.at)?;
let len = usize::try_from(len).map_err(|_| Malformed::OutOfBounds)?;
Value::Len(self.take(len)?)
}
WireType::I32 => {
let mut bytes = [0u8; 4];
bytes.copy_from_slice(self.take(4)?);
Value::I32(u32::from_le_bytes(bytes))
}
};
Ok(Field { number, value })
}
fn take(&mut self, len: usize) -> Result<&'a [u8], Malformed> {
let bytes = self
.buf
.get(self.at..)
.and_then(|rest| rest.get(..len))
.ok_or(Malformed::OutOfBounds)?;
self.at += len;
Ok(bytes)
}
}
pub struct Writer<'a> {
out: &'a mut [u8],
used: usize,
needed: usize,
}
impl<'a> Writer<'a> {
pub fn new(out: &'a mut [u8]) -> Self {
Writer {
out,
used: 0,
needed: 0,
}
}
pub fn tag(&mut self, number: u32, wire: WireType) {
self.varint((u64::from(number) << 3) | u64::from(wire as u8));
}
pub fn varint(&mut self, mut v: u64) {
loop {
let byte = (v & 0x7f) as u8;
v >>= 7;
if v == 0 {
self.push(&[byte]);
return;
}
self.push(&[byte | 0x80]);
}
}
pub fn fixed32(&mut self, v: u32) {
self.push(&v.to_le_bytes());
}
pub fn fixed64(&mut self, v: u64) {
self.push(&v.to_le_bytes());
}
pub fn bytes(&mut self, b: &[u8]) {
self.push(b);
}
pub fn finish(self) -> Result<&'a [u8], EncodeError> {
if self.needed > self.out.len() {
return Err(EncodeError::Capacity {
needed: self.needed,
available: self.out.len(),
});
}
let out: &'a [u8] = self.out;
Ok(&out[..self.used])
}
fn push(&mut self, bytes: &[u8]) {
self.needed = self.needed.saturating_add(bytes.len());
let room = self.out.len() - self.used;
let fits = if bytes.len() < room {
bytes.len()
} else {
room
};
self.out[self.used..self.used + fits].copy_from_slice(&bytes[..fits]);
self.used += fits;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Verified<'a> {
bytes: &'a [u8],
}
impl<'a> Verified<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Verified { bytes }
}
}
impl<'a> View<'a> for Verified<'a> {
fn bytes(&self) -> &'a [u8] {
self.bytes
}
}
#[cfg(test)]
mod tests {
use super::{
narrow_i32, narrow_u32, read_varint, unzigzag32, unzigzag32_checked, unzigzag64,
varint_len, zigzag32, zigzag64, Reader, Value, WireType, Writer,
};
use crate::payload::Malformed;
#[test]
fn varint_round_trips_at_every_length() {
let cases: [(u64, usize); 8] = [
(0, 1),
(1, 1),
(127, 1),
(128, 2),
(16383, 2),
(16384, 3),
(u64::from(u32::MAX), 5),
(u64::MAX, 10),
];
for (value, len) in cases {
assert_eq!(varint_len(value), len, "length of {value}");
let mut out = [0u8; 16];
let mut w = Writer::new(&mut out);
w.varint(value);
let bytes = w.finish().unwrap();
assert_eq!(bytes.len(), len, "written length of {value}");
let mut at = 0;
assert_eq!(read_varint(bytes, &mut at), Ok(value));
assert_eq!(at, len);
}
}
#[test]
fn zigzag_matches_the_protobuf_table() {
let table: [(i32, u32); 5] = [(0, 0), (-1, 1), (1, 2), (-2, 3), (i32::MIN, u32::MAX)];
for (signed, encoded) in table {
assert_eq!(zigzag32(signed), encoded);
assert_eq!(unzigzag32(encoded), signed);
assert_eq!(zigzag64(i64::from(signed)), u64::from(encoded));
assert_eq!(unzigzag64(u64::from(encoded)), i64::from(signed));
}
assert_eq!(zigzag64(i64::MIN), u64::MAX);
assert_eq!(unzigzag64(u64::MAX), i64::MIN);
}
#[test]
fn an_eleven_byte_varint_is_refused() {
let mut bytes = [0x80u8; 11];
bytes[10] = 0x00;
let mut at = 0;
assert_eq!(read_varint(&bytes, &mut at), Err(Malformed::Varint));
}
#[test]
fn a_tenth_byte_above_one_is_refused() {
let mut bytes = [0xffu8; 10];
bytes[9] = 0x02;
let mut at = 0;
assert_eq!(read_varint(&bytes, &mut at), Err(Malformed::Varint));
bytes[9] = 0x01;
let mut at = 0;
assert_eq!(read_varint(&bytes, &mut at), Ok(u64::MAX));
}
#[test]
fn a_non_minimal_varint_reads() {
let mut at = 0;
assert_eq!(read_varint(&[0x80, 0x00], &mut at), Ok(0));
assert_eq!(at, 2);
}
#[test]
fn a_truncated_varint_is_out_of_bounds() {
let mut at = 0;
assert_eq!(read_varint(&[0x80], &mut at), Err(Malformed::OutOfBounds));
let mut at = 0;
assert_eq!(read_varint(&[], &mut at), Err(Malformed::OutOfBounds));
}
#[test]
fn the_reader_refuses_field_zero_and_groups_and_wire_types_six_and_seven() {
for tag in [0x00u8, 0x0b, 0x0c, 0x0e, 0x0f] {
let buf = [tag, 0x01];
let mut r = Reader::new(&buf);
assert_eq!(r.next(), Some(Err(Malformed::WireType)), "tag {tag:#04x}");
assert_eq!(r.next(), None, "tag {tag:#04x}");
}
}
#[test]
fn the_reader_reports_a_truncated_length_as_out_of_bounds() {
let mut r = Reader::new(&[0x12, 0x05, 0x61]);
assert_eq!(r.next(), Some(Err(Malformed::OutOfBounds)));
}
#[test]
fn the_reader_yields_each_wire_type() {
let buf = [
0x08, 0x96, 0x01, 0x11, 1, 0, 0, 0, 0, 0, 0, 0, 0x1a, 0x02, 0x68, 0x69, 0x25, 2, 0, 0, 0, ];
let mut r = Reader::new(&buf);
let f = r.next().unwrap().unwrap();
assert_eq!((f.number, f.value), (1, Value::Varint(150)));
let f = r.next().unwrap().unwrap();
assert_eq!((f.number, f.value), (2, Value::I64(1)));
let f = r.next().unwrap().unwrap();
assert_eq!((f.number, f.value), (3, Value::Len(b"hi")));
let f = r.next().unwrap().unwrap();
assert_eq!((f.number, f.value), (4, Value::I32(2)));
assert_eq!(r.next(), None);
assert_eq!(r.next(), None);
}
#[test]
fn the_reader_refuses_a_tag_wider_than_u32() {
let mut buf = [0xffu8; 10];
buf[9] = 0x01;
let mut r = Reader::new(&buf);
assert_eq!(r.next(), Some(Err(Malformed::Varint)));
let mut out = [0u8; 10];
let mut w = Writer::new(&mut out);
w.varint(1 << 32);
let buf = w.finish().unwrap();
let mut r = Reader::new(buf);
assert_eq!(r.next(), Some(Err(Malformed::Varint)));
assert_eq!(r.next(), None);
}
#[test]
fn narrowing_refuses_what_does_not_fit() {
assert_eq!(narrow_u32(1 << 32), Err(Malformed::Varint));
assert_eq!(narrow_u32(u64::from(u32::MAX)), Ok(u32::MAX));
assert_eq!(
unzigzag32_checked(u64::from(u32::MAX) + 1),
Err(Malformed::Varint)
);
assert_eq!(unzigzag32_checked(u64::from(u32::MAX)), Ok(i32::MIN));
assert_eq!(narrow_i32((-3i64) as u64), Ok(-3));
assert_eq!(narrow_i32(1 << 40), Err(Malformed::Varint));
assert_eq!(narrow_i32(u64::from(u32::MAX)), Err(Malformed::Varint));
assert_eq!(narrow_i32(i32::MAX as u64), Ok(i32::MAX));
assert_eq!(narrow_i32((i32::MIN as i64) as u64), Ok(i32::MIN));
assert_eq!(
narrow_i32((i32::MIN as i64 - 1) as u64),
Err(Malformed::Varint)
);
}
#[test]
fn the_writer_writes_a_tag_and_a_packed_run() {
let mut out = [0u8; 8];
let mut w = Writer::new(&mut out);
w.tag(15, WireType::Len);
w.varint(2);
w.varint(1);
w.varint(2);
assert_eq!(w.finish().unwrap(), &[0x7a, 0x02, 0x01, 0x02]);
}
#[test]
fn the_writer_writes_fixed_widths_and_bytes() {
let mut out = [0u8; 16];
let mut w = Writer::new(&mut out);
w.fixed32(0x0403_0201);
w.fixed64(0x0807_0605_0403_0201);
w.bytes(b"ab");
assert_eq!(
w.finish().unwrap(),
&[1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, b'a', b'b']
);
}
#[test]
fn the_writer_reports_an_overflow_at_finish() {
use crate::payload::EncodeError;
let mut out = [0u8; 3];
let mut w = Writer::new(&mut out);
w.tag(1, WireType::Varint);
w.bytes(&[1, 2, 3, 4]);
w.fixed32(7);
assert_eq!(
w.finish(),
Err(EncodeError::Capacity {
needed: 9,
available: 3
})
);
assert_eq!(out, [0x08, 1, 2]);
}
#[test]
fn check_capacity_reports_the_shortfall() {
use crate::payload::EncodeError;
assert_eq!(super::check_capacity(4, &[0; 4]), Ok(()));
assert_eq!(
super::check_capacity(5, &[0; 4]),
Err(EncodeError::Capacity {
needed: 5,
available: 4
})
);
}
#[test]
fn utf8_refuses_bad_bytes() {
assert_eq!(super::utf8(b"hi"), Ok("hi"));
assert_eq!(super::utf8(&[0xff]), Err(Malformed::Utf8));
}
#[test]
fn verified_hands_back_its_bytes() {
use crate::payload::View;
let v = super::Verified::new(&[1, 2]);
assert_eq!(View::bytes(&v), &[1, 2]);
}
#[test]
fn tag_len_counts_the_shifted_number() {
assert_eq!(super::tag_len(1), 1);
assert_eq!(super::tag_len(15), 1);
assert_eq!(super::tag_len(16), 2);
assert_eq!(super::tag_len(2047), 2);
assert_eq!(super::tag_len(2048), 3);
}
}