Skip to main content Skip to main content
Pigweed's logo Pigweed
code_xml
  1. Home
  2. Reference
  3. Rust
  4. src
  5. pw_varint/pw_varint.rs

pw_varint/
pw_varint.rs

1// Copyright 2023 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15//! # pw_varint
16//!
17//! `pw_varint` provides support for encoding and decoding variable length
18//! integers.  Small values require less memory than they do with fixed
19//! encoding.  Signed integers are first zig-zag encoded to allow small
20//! negative numbers to realize the memory savings.  For more information see
21//! [Pigweed's pw_varint documentation](https://pigweed.dev/pw_varint).
22//!
23//! The encoding format used is compatible with
24//! [Protocol Buffers](https://developers.google.com/protocol-buffers/docs/encoding#varints).
25//!
26//! Encoding and decoding is provided through the [VarintEncode] and
27//! [VarintDecode] traits.
28//!
29//! # Example
30//!
31//! ```
32//! use pw_varint::{VarintEncode, VarintDecode};
33//!
34//! let mut buffer = [0u8; 64];
35//!
36//! let encoded_len = (-1i64).varint_encode(&mut buffer).unwrap();
37//!
38//! let (decoded_len, val) = i64::varint_decode(&buffer ).unwrap();
39//! ```
40
41#![cfg_attr(not(feature = "std"), no_std)]
42
43use pw_status::{Error, Result};
44
45/// Maximum encoded size in bytes of a 64-bit LEB128 varint.
46pub const MAX_VARINT_SIZE_BYTES: usize = 10;
47
48/// A trait for objects that can be decoded from a varint.
49///
50/// `pw_varint` provides implementations for [`i8`], [`u8`], [`i16`], [`u16`],
51/// [`i32`], [`u32`], [`i64`], and [`u64`].
52pub trait VarintDecode: Sized {
53    /// Decode a type from a varint encoded series of bytes.
54    ///
55    /// Signed values will be implicitly zig-zag decoded.
56    ///
57    /// # Errors
58    ///
59    /// Returns [`Error::OutOfRange`] if `data` does not contain a terminated
60    /// varint or if the decoded value exceeds the range of `Self`.
61    fn varint_decode(data: &[u8]) -> Result<(usize, Self)>;
62}
63
64/// A trait for objects that can be encoded into a varint.
65///
66/// `pw_varint` provides implementations for [`i8`], [`u8`], [`i16`], [`u16`],
67/// [`i32`], [`u32`], [`i64`], and [`u64`].
68pub trait VarintEncode: Sized {
69    /// Encode a type into a varint encoded series of bytes.
70    ///
71    /// Signed values will be implicitly zig-zag encoded.
72    ///
73    /// # Errors
74    ///
75    /// Returns [`Error::OutOfRange`] if `data` is too small to hold the encoded
76    /// varint.
77    fn varint_encode(self, data: &mut [u8]) -> Result<usize>;
78}
79
80/// ZigZag encodes a signed integer. This maps small negative numbers to small,
81/// unsigned positive numbers, which improves their density for LEB128 encoding.
82///
83/// ZigZag encoding works by moving the sign bit from the most-significant bit
84/// to the least-significant bit. For the signed k-bit integer n, the formula is
85///
86///   `(n << 1) ^ (n >> (k - 1))`
87///
88/// See the following for a description of ZigZag encoding:
89///   <https://protobuf.dev/programming-guides/encoding/#signed-ints>
90#[inline]
91#[must_use]
92pub const fn zig_zag_encode(value: i64) -> u64 {
93    (value.cast_unsigned() << 1) ^ (value >> (i64::BITS - 1)).cast_unsigned()
94}
95
96/// Decodes a zig-zag encoded 64-bit signed integer.
97#[inline]
98#[must_use]
99pub const fn zig_zag_decode(value: u64) -> i64 {
100    ((value >> 1) ^ (0u64.wrapping_sub(value & 1))).cast_signed()
101}
102
103macro_rules! unsigned_varint_impl {
104    ($t:ty) => {
105        impl VarintDecode for $t {
106            #[inline]
107            fn varint_decode(data: &[u8]) -> Result<(usize, Self)> {
108                let (decoded_len, val) = decode_u64(data)?;
109                let val = val.try_into().map_err(|_| Error::OutOfRange)?;
110                Ok((decoded_len, val))
111            }
112        }
113
114        impl VarintEncode for $t {
115            #[inline]
116            fn varint_encode(self, data: &mut [u8]) -> Result<usize> {
117                encode_u64(data, u64::from(self))
118            }
119        }
120    };
121}
122
123macro_rules! signed_varint_impl {
124    ($t:ty) => {
125        impl VarintDecode for $t {
126            #[inline]
127            fn varint_decode(data: &[u8]) -> Result<(usize, Self)> {
128                let (decoded_len, val) = decode_u64(data)?;
129                let val = zig_zag_decode(val)
130                    .try_into()
131                    .map_err(|_| Error::OutOfRange)?;
132                Ok((decoded_len, val))
133            }
134        }
135
136        impl VarintEncode for $t {
137            #[inline]
138            fn varint_encode(self, data: &mut [u8]) -> Result<usize> {
139                encode_u64(data, zig_zag_encode(i64::from(self)))
140            }
141        }
142    };
143}
144
145unsigned_varint_impl!(u8);
146unsigned_varint_impl!(u16);
147unsigned_varint_impl!(u32);
148unsigned_varint_impl!(u64);
149
150signed_varint_impl!(i8);
151signed_varint_impl!(i16);
152signed_varint_impl!(i32);
153signed_varint_impl!(i64);
154
155/// Bit mask for the MSB continuation flag on each varint byte.
156const CONTINUATION_BIT: u8 = 0x80;
157
158/// Bit mask for the 7-bit data payload on each varint byte.
159const VALUE_MASK: u8 = 0x7f;
160
161/// Number of data bits carried by each varint byte.
162const BITS_PER_VARINT_BYTE: usize = 7;
163
164/// Bit mask for the valid payload bit (bit 63 of `u64`) on the 10th byte.
165const TENTH_BYTE_VALUE_MASK: u8 = 0x01;
166
167/// Bit shift for placing the 10th varint byte's valid bit into bit 63.
168const TENTH_BYTE_BIT_SHIFT: u32 = 63;
169
170/// Decodes a 64-bit unsigned LEB128 varint with an inline fast path for 1- and
171/// 2-byte values.
172#[inline]
173fn decode_u64(data: &[u8]) -> Result<(usize, u64)> {
174    let Some((&b0, tail)) = data.split_first() else {
175        return Err(Error::OutOfRange);
176    };
177    if (b0 & CONTINUATION_BIT) == 0 {
178        return Ok((1, u64::from(b0)));
179    }
180    let Some(&b1) = tail.first() else {
181        return Err(Error::OutOfRange);
182    };
183    if (b1 & CONTINUATION_BIT) == 0 {
184        let val = u64::from(b0 & VALUE_MASK) | (u64::from(b1) << BITS_PER_VARINT_BYTE);
185        return Ok((2, val));
186    }
187    decode_u64_slow(data)
188}
189
190/// Multi-byte fallback loop for [`decode_u64`], validating 10th-byte overflow
191/// bits to prevent >64-bit shift overflow panics.
192#[inline(never)]
193fn decode_u64_slow(data: &[u8]) -> Result<(usize, u64)> {
194    let mut value: u64 = 0;
195    let (head, tail) = data.split_at(data.len().min(MAX_VARINT_SIZE_BYTES - 1));
196
197    for (i, &byte) in head.iter().enumerate() {
198        value |= u64::from(byte & VALUE_MASK) << (i * BITS_PER_VARINT_BYTE);
199
200        if (byte & CONTINUATION_BIT) == 0 {
201            return Ok((i + 1, value));
202        }
203    }
204
205    let Some(&byte) = tail.first() else {
206        return Err(Error::OutOfRange);
207    };
208
209    // 10th byte: only bit 0 is valid for a 64-bit value (9 * 7 = 63 bits
210    // previously read). Any higher bit indicates overflow or a trailing
211    // continuation byte.
212    if (byte & !TENTH_BYTE_VALUE_MASK) != 0 {
213        return Err(Error::OutOfRange);
214    }
215
216    Ok((
217        MAX_VARINT_SIZE_BYTES,
218        value | u64::from(byte) << TENTH_BYTE_BIT_SHIFT,
219    ))
220}
221
222fn encode_u64(data: &mut [u8], mut value: u64) -> Result<usize> {
223    for (i, d) in data.iter_mut().enumerate() {
224        // `VALUE_MASK` (`0x7f`) intentionally selects only the low 7 bits.
225        #[allow(clippy::cast_possible_truncation)]
226        let byte: u8 = (value as u8) & VALUE_MASK;
227        value >>= BITS_PER_VARINT_BYTE;
228        if value == 0 {
229            *d = byte;
230            return Ok(i + 1);
231        }
232        *d = byte | CONTINUATION_BIT;
233    }
234    Err(Error::OutOfRange)
235}
236
237#[cfg(test)]
238mod test {
239    use super::*;
240
241    fn success_cases_u8<T>() -> Vec<(Vec<u8>, T)>
242    where
243        T: From<u8>,
244    {
245        vec![
246            // From varint_test.cc EncodeSizeUnsigned32_SmallSingleByte.
247            (vec![0x00], 0x00.into()),
248            (vec![0x01], 0x01.into()),
249            (vec![0x02], 0x02.into()),
250            // From varint_test.cc EncodeSizeUnsigned32_LargeSingleByte.
251            (vec![0x3f], 0x3f.into()),
252            (vec![0x40], 0x40.into()),
253            (vec![0x7e], 0x7e.into()),
254            (vec![0x7f], 0x7f.into()),
255            // From varint_test.cc EncodeSizeUnsigned32_MultiByte.
256            (vec![0x80, 0x01], 128.into()),
257            (vec![0x81, 0x01], 129.into()),
258            // From https://protobuf.dev/programming-guides/encoding/.
259            (vec![0x96, 0x01], 150.into()),
260        ]
261    }
262
263    fn success_cases_i8<T>() -> Vec<(Vec<u8>, T)>
264    where
265        T: From<i8>,
266    {
267        vec![
268            // From varint_test.cc EncodeSizeSigned32_SmallSingleByte.
269            (vec![0x00], 0i8.into()),
270            (vec![0x01], (-1i8).into()),
271            (vec![0x02], 1i8.into()),
272            (vec![0x03], (-2i8).into()),
273            (vec![0x04], 2i8.into()),
274            // From varint_test.cc EncodeSizeSigned32_LargeSingleByte.
275            (vec![125], (-63i8).into()),
276            (vec![126], (63i8).into()),
277            (vec![127], (-64i8).into()),
278            // From varint_test.cc EncodeSizeSigned32_MultiByte.
279            (vec![0x80, 0x1], 64i8.into()),
280            (vec![0x81, 0x1], (-65i8).into()),
281            (vec![0x82, 0x1], 65i8.into()),
282        ]
283    }
284
285    fn success_cases_u32<T>() -> Vec<(Vec<u8>, T)>
286    where
287        T: From<u32>,
288    {
289        vec![
290            // From varint_test.cc EncodeSizeUnsigned32_MultiByte.
291            (vec![0xfe, 0xff, 0xff, 0xff, 0x0f], 0xffff_fffe.into()),
292            (vec![0xff, 0xff, 0xff, 0xff, 0x0f], 0xffff_ffff.into()),
293        ]
294    }
295
296    fn success_cases_i32<T>() -> Vec<(Vec<u8>, T)>
297    where
298        T: From<i32>,
299    {
300        vec![
301            // From varint_test.cc EncodeSizeSigned32_MultiByte.
302            (vec![0xff, 0xff, 0xff, 0xff, 0x0f], i32::MIN.into()),
303            (vec![0xfe, 0xff, 0xff, 0xff, 0x0f], i32::MAX.into()),
304        ]
305    }
306
307    #[test]
308    fn decode_test_u8() {
309        for case in success_cases_u8::<u8>() {
310            assert_eq!(u8::varint_decode(&case.0), Ok((case.0.len(), case.1)));
311        }
312
313        // Truncated varint.
314        assert_eq!(u8::varint_decode(&[0x96]), Err(Error::OutOfRange));
315        // 256 exceeds u8::MAX (255).
316        assert_eq!(u8::varint_decode(&[0x80, 0x02]), Err(Error::OutOfRange));
317    }
318
319    #[test]
320    fn decode_test_i8() {
321        for case in success_cases_i8::<i8>() {
322            assert_eq!(i8::varint_decode(&case.0), Ok((case.0.len(), case.1)));
323        }
324
325        // Truncated varint.
326        assert_eq!(i8::varint_decode(&[0x96]), Err(Error::OutOfRange));
327        // 128 and -129 exceed i8::MIN..=i8::MAX (-128..=127).
328        assert_eq!(i8::varint_decode(&[0x80, 0x02]), Err(Error::OutOfRange));
329        assert_eq!(i8::varint_decode(&[0x81, 0x02]), Err(Error::OutOfRange));
330    }
331
332    #[test]
333    fn decode_test_u16() {
334        for case in success_cases_u8::<u16>() {
335            assert_eq!(u16::varint_decode(&case.0), Ok((case.0.len(), case.1)));
336        }
337
338        assert_eq!(u16::varint_decode(&[0x96]), Err(Error::OutOfRange));
339    }
340
341    #[test]
342    fn decode_test_i16() {
343        for case in success_cases_i8::<i16>() {
344            assert_eq!(i16::varint_decode(&case.0), Ok((case.0.len(), case.1)));
345        }
346
347        assert_eq!(i16::varint_decode(&[0x96]), Err(Error::OutOfRange));
348    }
349
350    #[test]
351    fn decode_test_u32() {
352        for case in success_cases_u8::<u32>()
353            .into_iter()
354            .chain(success_cases_u32::<u32>())
355        {
356            assert_eq!(u32::varint_decode(&case.0), Ok((case.0.len(), case.1)));
357        }
358
359        assert_eq!(u32::varint_decode(&[0x96]), Err(Error::OutOfRange));
360    }
361
362    #[test]
363    fn decode_test_i32() {
364        for case in success_cases_i8::<i32>()
365            .into_iter()
366            .chain(success_cases_i32::<i32>())
367        {
368            assert_eq!(i32::varint_decode(&case.0), Ok((case.0.len(), case.1)));
369        }
370
371        assert_eq!(i32::varint_decode(&[0x96]), Err(Error::OutOfRange));
372    }
373
374    #[test]
375    fn decode_test_u64() {
376        for case in success_cases_u8::<u64>()
377            .into_iter()
378            .chain(success_cases_u32::<u64>())
379        {
380            assert_eq!(u64::varint_decode(&case.0), Ok((case.0.len(), case.1)));
381        }
382
383        assert_eq!(u64::varint_decode(&[0x96]), Err(Error::OutOfRange));
384    }
385
386    #[test]
387    fn decode_test_i64() {
388        for case in success_cases_i8::<i64>()
389            .into_iter()
390            .chain(success_cases_i32::<i64>())
391        {
392            assert_eq!(i64::varint_decode(&case.0), Ok((case.0.len(), case.1)));
393        }
394
395        assert_eq!(i64::varint_decode(&[0x96]), Err(Error::OutOfRange));
396    }
397
398    #[test]
399    fn decode_u64_empty_and_truncated() {
400        assert_eq!(u64::varint_decode(&[]), Err(Error::OutOfRange));
401        assert_eq!(u64::varint_decode(&[0x80]), Err(Error::OutOfRange));
402        assert_eq!(u64::varint_decode(&[0x80, 0x80]), Err(Error::OutOfRange));
403    }
404
405    #[test]
406    fn decode_u64_10th_byte_valid_and_overflow() {
407        let mut valid_10th = [0x80u8; MAX_VARINT_SIZE_BYTES];
408        valid_10th[MAX_VARINT_SIZE_BYTES - 1] = 0x01;
409        assert_eq!(
410            u64::varint_decode(&valid_10th),
411            Ok((MAX_VARINT_SIZE_BYTES, 1u64 << 63))
412        );
413
414        let mut u64_max_bytes = [0xffu8; MAX_VARINT_SIZE_BYTES];
415        u64_max_bytes[MAX_VARINT_SIZE_BYTES - 1] = 0x01;
416        assert_eq!(
417            u64::varint_decode(&u64_max_bytes),
418            Ok((MAX_VARINT_SIZE_BYTES, u64::MAX))
419        );
420
421        // Two-byte fast-path upper bound and three-byte slow-path lower bound.
422        assert_eq!(u64::varint_decode(&[0xff, 0x7f]), Ok((2, 0x3fff)));
423        assert_eq!(u64::varint_decode(&[0x80, 0x80, 0x01]), Ok((3, 0x4000)));
424
425        let mut overflow_bits = [0x80u8; MAX_VARINT_SIZE_BYTES];
426        overflow_bits[MAX_VARINT_SIZE_BYTES - 1] = 0x02;
427        assert_eq!(u64::varint_decode(&overflow_bits), Err(Error::OutOfRange));
428
429        let mut continuation_set = [0x80u8; MAX_VARINT_SIZE_BYTES];
430        continuation_set[MAX_VARINT_SIZE_BYTES - 1] = 0x81;
431        assert_eq!(
432            u64::varint_decode(&continuation_set),
433            Err(Error::OutOfRange)
434        );
435
436        let overlong = [0x80u8; 12];
437        assert_eq!(u64::varint_decode(&overlong), Err(Error::OutOfRange));
438    }
439
440    #[test]
441    fn encode_test_u8() {
442        for case in success_cases_u8::<u8>() {
443            let mut buffer = [0u8; 64];
444            let len = case.1.varint_encode(&mut buffer).unwrap();
445            assert_eq!(len, case.0.len());
446            assert_eq!(&buffer[0..len], case.0);
447        }
448    }
449
450    #[test]
451    fn encode_test_i8() {
452        for case in success_cases_i8::<i8>() {
453            let mut buffer = [0u8; 64];
454            let len = case.1.varint_encode(&mut buffer).unwrap();
455            assert_eq!(len, case.0.len());
456            assert_eq!(&buffer[0..len], case.0);
457        }
458    }
459
460    #[test]
461    fn encode_test_u16() {
462        for case in success_cases_u8::<u16>() {
463            let mut buffer = [0u8; 64];
464            let len = case.1.varint_encode(&mut buffer).unwrap();
465            assert_eq!(len, case.0.len());
466            assert_eq!(&buffer[0..len], case.0);
467        }
468    }
469
470    #[test]
471    fn encode_test_i16() {
472        for case in success_cases_i8::<i16>() {
473            let mut buffer = [0u8; 64];
474            let len = case.1.varint_encode(&mut buffer).unwrap();
475            assert_eq!(len, case.0.len());
476            assert_eq!(&buffer[0..len], case.0);
477        }
478    }
479
480    #[test]
481    fn encode_test_u32() {
482        for case in success_cases_u8::<u32>()
483            .into_iter()
484            .chain(success_cases_u32::<u32>())
485        {
486            let mut buffer = [0u8; 64];
487            let len = case.1.varint_encode(&mut buffer).unwrap();
488            assert_eq!(len, case.0.len());
489            assert_eq!(&buffer[0..len], case.0);
490        }
491    }
492
493    #[test]
494    fn encode_test_i32() {
495        for case in success_cases_i8::<i32>()
496            .into_iter()
497            .chain(success_cases_i32::<i32>())
498        {
499            let mut buffer = [0u8; 64];
500            let len = case.1.varint_encode(&mut buffer).unwrap();
501            assert_eq!(len, case.0.len());
502            assert_eq!(&buffer[0..len], case.0);
503        }
504    }
505
506    #[test]
507    fn encode_test_u64() {
508        for case in success_cases_u8::<u64>()
509            .into_iter()
510            .chain(success_cases_u32::<u64>())
511        {
512            let mut buffer = [0u8; 64];
513            let len = case.1.varint_encode(&mut buffer).unwrap();
514            assert_eq!(len, case.0.len());
515            assert_eq!(&buffer[0..len], case.0);
516        }
517    }
518
519    #[test]
520    fn encode_test_i64() {
521        for case in success_cases_i8::<i64>()
522            .into_iter()
523            .chain(success_cases_i32::<i64>())
524        {
525            let mut buffer = [0u8; 64];
526            let len = case.1.varint_encode(&mut buffer).unwrap();
527            assert_eq!(len, case.0.len());
528            assert_eq!(&buffer[0..len], case.0);
529        }
530    }
531
532    #[test]
533    fn zig_zag_encode_64bit_boundaries() {
534        assert_eq!(zig_zag_encode(0), 0);
535        assert_eq!(zig_zag_encode(-1), 1);
536        assert_eq!(zig_zag_encode(1), 2);
537        assert_eq!(zig_zag_encode(i64::MAX), u64::MAX - 1);
538        assert_eq!(zig_zag_encode(i64::MIN), u64::MAX);
539    }
540
541    #[test]
542    fn zig_zag_decode_64bit_boundaries() {
543        assert_eq!(zig_zag_decode(0), 0);
544        assert_eq!(zig_zag_decode(1), -1);
545        assert_eq!(zig_zag_decode(2), 1);
546        assert_eq!(zig_zag_decode(u64::MAX - 1), i64::MAX);
547        assert_eq!(zig_zag_decode(u64::MAX), i64::MIN);
548    }
549}