leb128_u64/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3#![cfg_attr(not(test), no_std)]
4
5use bytes::{Buf, BufMut};
6
7pub fn encode(mut value: u64, mut buf: impl BufMut) {
8    loop {
9        if value < 0x80 {
10            buf.put_u8(value as u8);
11            break;
12        } else {
13            buf.put_u8((value & 0x7f) as u8 | 0x80);
14            value >>= 7;
15        }
16    }
17}
18
19pub fn decode(mut buf: impl Buf) -> u64 {
20    let mut value = 0;
21    for i in 0..10.min(buf.remaining()) {
22        let byte = buf.get_u8();
23        value |= (byte as u64 & 0x7f) << (i * 7);
24        if byte < 0x80 {
25            if i == 9 && byte > 0x01 {
26                break;
27            } else {
28                return value;
29            }
30        }
31    }
32
33    panic!("encoded integer doesn't fit in u64");
34}
35
36pub fn encoded_len(value: u64) -> usize {
37    // Based on [VarintSize64][1].
38    // [1]: https://github.com/google/protobuf/blob/3.3.x/src/google/protobuf/io/coded_stream.h#L1301-L1309
39    (((value | 0x1).ilog2() * 9 + 73) / 64) as _
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use proptest::{prelude::any, prop_assert_eq, proptest};
46
47    #[test]
48    fn min() {
49        let mut buf = [0u8; 1];
50        encode(0, &mut buf[..]);
51        assert_eq!(buf, [0u8; 1]);
52
53        let v = decode(&buf[..]);
54        assert_eq!(0, v);
55    }
56
57    #[test]
58    fn max() {
59        let mut buf = [0u8; 10];
60        encode(u64::MAX, &mut buf[..]);
61        let mut expected = [255u8; 10];
62        expected[9] = 0x01;
63        assert_eq!(buf, expected);
64
65        let v = decode(&buf[..]);
66        assert_eq!(u64::MAX, v);
67    }
68
69    proptest! {
70        #[test]
71        fn random(input in any::<u64>()) {
72            let mut buf = Vec::new();
73            encode(input, &mut buf);
74            prop_assert_eq!(encoded_len(input), buf.len());
75
76            let output = decode(&buf[..]);
77            prop_assert_eq!(input, output);
78        }
79    }
80}