Skip to content

Commit 3e76017

Browse files
badicsalexclaude
andcommitted
codec: simplify and inline varint decoding (SFT-8112)
Only the last byte can overflow, so it's the only one checked. Redundant encodings such as [0x80, 0x00] decode fine. Rejecting them is not really necessary, as nothing else makes encodings unique either: non-frozen structs skip unknown trailing fields. And the check costs measurably, 6-19 ns per multi-byte varint on the device. On the device, this cuts the time to decode small messages into their borrowed form by a third to a half. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 487a9b6 commit 3e76017

1 file changed

Lines changed: 20 additions & 26 deletions

File tree

‎codec/src/varint.rs‎

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ pub struct Varint<T>(pub T);
1414

1515
pub trait Primitive: Copy {
1616
const MAX_ENCODED_LEN: usize;
17+
const LAST_PAYLOAD_MAX: u8;
1718

1819
fn from_u8(value: u8) -> Self;
1920
fn low_7_bits(self) -> u8;
2021
fn shr_7(self) -> Self;
2122
fn needs_more(self) -> bool;
22-
fn checked_add_payload(self, payload: u8, shift: usize) -> Option<Self>;
23+
fn add_payload(self, payload: u8, shift: usize) -> Self;
2324
}
2425

2526
impl<T: Primitive> Varint<T> {
@@ -84,33 +85,36 @@ where
8485
out.put_u8(value.low_7_bits());
8586
}
8687

88+
#[inline]
8789
pub fn decode<T: Primitive>(reader: &mut Reader<'_>) -> Result<T, Error> {
88-
let mut value = T::from_u8(0);
90+
let first = reader.take_u8()?;
91+
if first < 0x80 {
92+
return Ok(T::from_u8(first));
93+
}
8994

90-
for index in 0..T::MAX_ENCODED_LEN {
95+
let mut value = T::from_u8(first & 0x7f);
96+
for index in 1..T::MAX_ENCODED_LEN - 1 {
9197
let byte = reader.take_u8()?;
92-
let payload = byte & 0x7f;
93-
94-
value = value
95-
.checked_add_payload(payload, index * 7)
96-
.ok_or(Error::InvalidVarint)?;
97-
98-
if byte & 0x80 == 0 {
99-
if index > 0 && payload == 0 {
100-
return Err(Error::InvalidVarint);
101-
}
98+
value = value.add_payload(byte & 0x7f, index * 7);
99+
if byte < 0x80 {
102100
return Ok(value);
103101
}
104102
}
105103

106-
Err(Error::InvalidVarint)
104+
let last = reader.take_u8()?;
105+
if last > T::LAST_PAYLOAD_MAX {
106+
return Err(Error::InvalidVarint);
107+
}
108+
Ok(value.add_payload(last, (T::MAX_ENCODED_LEN - 1) * 7))
107109
}
108110

109111
macro_rules! impl_varint {
110112
($($ty:ty),* $(,)?) => {
111113
$(
112114
impl Primitive for $ty {
113115
const MAX_ENCODED_LEN: usize = (size_of::<Self>() * 8).div_ceil(7);
116+
#[allow(clippy::cast_possible_truncation)]
117+
const LAST_PAYLOAD_MAX: u8 = (Self::MAX >> ((Self::MAX_ENCODED_LEN - 1) * 7)) as u8;
114118

115119
#[inline]
116120
fn from_u8(value: u8) -> Self {
@@ -130,17 +134,8 @@ macro_rules! impl_varint {
130134
self >= 0x80
131135
}
132136
#[inline]
133-
fn checked_add_payload(self, payload: u8, shift: usize) -> Option<Self> {
134-
let bit_width = size_of::<Self>() * 8;
135-
if shift >= bit_width {
136-
return (payload == 0).then_some(self);
137-
}
138-
let max_payload = Self::MAX >> shift;
139-
let payload = Self::from(payload);
140-
if payload > max_payload {
141-
return None;
142-
}
143-
Some(self | (payload << shift))
137+
fn add_payload(self, payload: u8, shift: usize) -> Self {
138+
self | (Self::from(payload) << shift)
144139
}
145140
}
146141
)*
@@ -198,7 +193,6 @@ mod tests {
198193
assert_eq!(decode(&mut reader), Ok(T::from_u8(127)));
199194
assert_eq!(reader.take_all(), &[0x55]);
200195

201-
assert_error::<T>(&[0x80, 0x00], Error::InvalidVarint);
202196
assert_error::<T>(&[0x80], Error::UnexpectedEof);
203197
assert_error::<T>(&vec![0xff; T::MAX_ENCODED_LEN], Error::InvalidVarint);
204198

0 commit comments

Comments
 (0)