1 use std::mem::MaybeUninit;
2 
3 use crate::varint::MAX_VARINT_ENCODED_LEN;
4 
5 /// Encode u64 as varint.
6 /// Panics if buffer length is less than 10.
7 #[inline]
encode_varint64(mut value: u64, buf: &mut [MaybeUninit<u8>]) -> usize8 pub(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]
encode_varint32(mut value: u32, buf: &mut [MaybeUninit<u8>]) -> usize59 pub(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]
encoded_varint64_len(value: u64) -> usize94 pub(crate) fn encoded_varint64_len(value: u64) -> usize {
95     if value == 0 {
96         1
97     } else {
98         let significant_bits = 64 - value.leading_zeros();
99         (significant_bits + 6) as usize / 7
100     }
101 }
102 
103 #[cfg(test)]
104 mod test {
105     use std::mem::MaybeUninit;
106 
107     use crate::varint::encode::encode_varint64;
108     use crate::varint::encode::encoded_varint64_len;
109 
110     #[test]
test_encoded_varint64_len()111     fn test_encoded_varint64_len() {
112         fn test(n: u64) {
113             let mut buf = [MaybeUninit::uninit(); 10];
114             let expected = encode_varint64(n, &mut buf);
115             assert_eq!(expected, encoded_varint64_len(n), "n={}", n);
116         }
117 
118         for n in 0..1000 {
119             test(n);
120         }
121 
122         for p in 0.. {
123             match 2u64.checked_pow(p) {
124                 Some(n) => test(n),
125                 None => break,
126             }
127         }
128 
129         for p in 0.. {
130             match 3u64.checked_pow(p) {
131                 Some(n) => test(n),
132                 None => break,
133             }
134         }
135 
136         test(u64::MAX);
137         test(u64::MAX - 1);
138         test((i64::MAX as u64) + 1);
139         test(i64::MAX as u64);
140         test((i64::MAX as u64) - 1);
141         test((u32::MAX as u64) + 1);
142         test(u32::MAX as u64);
143         test((u32::MAX as u64) - 1);
144         test((i32::MAX as u64) + 1);
145         test(i32::MAX as u64);
146         test((i32::MAX as u64) - 1);
147     }
148 }
149