protobuf/varint/
encode.rs

1use std::mem::MaybeUninit;
2
3use crate::varint::MAX_VARINT_ENCODED_LEN;
4
5/// Encode u64 as varint.
6/// Panics if buffer length is less than 10.
7#[inline]
8pub(crate) fn encode_varint64(mut value: u64, buf: &mut [MaybeUninit<u8>]) -> usize {
9    assert!(buf.len() >= MAX_VARINT_ENCODED_LEN);
10
11    fn iter(value: &mut u64, byte: &mut MaybeUninit<u8>) -> bool {
12        if (*value & !0x7F) > 0 {
13            byte.write(((*value & 0x7F) | 0x80) as u8);
14            *value >>= 7;
15            true
16        } else {
17            byte.write(*value as u8);
18            false
19        }
20    }
21
22    // Explicitly unroll loop to avoid either
23    // unsafe code or bound checking when writing to `buf`
24
25    if !iter(&mut value, &mut buf[0]) {
26        return 1;
27    };
28    if !iter(&mut value, &mut buf[1]) {
29        return 2;
30    };
31    if !iter(&mut value, &mut buf[2]) {
32        return 3;
33    };
34    if !iter(&mut value, &mut buf[3]) {
35        return 4;
36    };
37    if !iter(&mut value, &mut buf[4]) {
38        return 5;
39    };
40    if !iter(&mut value, &mut buf[5]) {
41        return 6;
42    };
43    if !iter(&mut value, &mut buf[6]) {
44        return 7;
45    };
46    if !iter(&mut value, &mut buf[7]) {
47        return 8;
48    };
49    if !iter(&mut value, &mut buf[8]) {
50        return 9;
51    };
52    buf[9].write(value as u8);
53    10
54}
55
56/// Encode u32 value as varint.
57/// Panics if buffer length is less than 5.
58#[inline]
59pub(crate) fn encode_varint32(mut value: u32, buf: &mut [MaybeUninit<u8>]) -> usize {
60    assert!(buf.len() >= 5);
61
62    fn iter(value: &mut u32, byte: &mut MaybeUninit<u8>) -> bool {
63        if (*value & !0x7F) > 0 {
64            byte.write(((*value & 0x7F) | 0x80) as u8);
65            *value >>= 7;
66            true
67        } else {
68            byte.write(*value as u8);
69            false
70        }
71    }
72
73    // Explicitly unroll loop to avoid either
74    // unsafe code or bound checking when writing to `buf`
75
76    if !iter(&mut value, &mut buf[0]) {
77        return 1;
78    };
79    if !iter(&mut value, &mut buf[1]) {
80        return 2;
81    };
82    if !iter(&mut value, &mut buf[2]) {
83        return 3;
84    };
85    if !iter(&mut value, &mut buf[3]) {
86        return 4;
87    };
88    buf[4].write(value as u8);
89    5
90}
91
92/// Encoded size of u64 value.
93#[inline]
94pub(crate) fn encoded_varint64_len(value: u64) -> usize {
95    // Bitwise-or'ing by 1 allows the `value = zero` case to work without
96    // affecting other cases.
97    let significant_bits = 64 - (value | 1).leading_zeros();
98    (significant_bits + 6) as usize / 7
99}
100
101#[cfg(test)]
102mod test {
103    use std::mem::MaybeUninit;
104
105    use crate::varint::encode::encode_varint64;
106    use crate::varint::encode::encoded_varint64_len;
107
108    #[test]
109    fn test_encoded_varint64_len() {
110        fn test(n: u64) {
111            let mut buf = [MaybeUninit::uninit(); 10];
112            let expected = encode_varint64(n, &mut buf);
113            assert_eq!(expected, encoded_varint64_len(n), "n={}", n);
114        }
115
116        for n in 0..1000 {
117            test(n);
118        }
119
120        for p in 0.. {
121            match 2u64.checked_pow(p) {
122                Some(n) => test(n),
123                None => break,
124            }
125        }
126
127        for p in 0.. {
128            match 3u64.checked_pow(p) {
129                Some(n) => test(n),
130                None => break,
131            }
132        }
133
134        test(u64::MAX);
135        test(u64::MAX - 1);
136        test((i64::MAX as u64) + 1);
137        test(i64::MAX as u64);
138        test((i64::MAX as u64) - 1);
139        test((u32::MAX as u64) + 1);
140        test(u32::MAX as u64);
141        test((u32::MAX as u64) - 1);
142        test((i32::MAX as u64) + 1);
143        test(i32::MAX as u64);
144        test((i32::MAX as u64) - 1);
145    }
146}