1use crate::errors::{Error, Result};
3
4const B64_CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
5const B64: [i8; 256] = [
6 -1,
7 -1,
8 -1,
9 -1,
10 -1,
11 -1,
12 -1,
13 -1,
14 -1,
15 -1,
16 -1,
17 -1,
18 -1,
19 -1,
20 -1,
21 -1,
22 -1,
23 -1,
24 -1,
25 -1,
26 -1,
27 -1,
28 -1,
29 -1,
30 -1,
31 -1,
32 -1,
33 -1,
34 -1,
35 -1,
36 -1,
37 -1,
38 -1,
39 -1,
40 -1,
41 -1,
42 -1,
43 -1,
44 -1,
45 -1,
46 -1,
47 -1,
48 -1,
49 62,
50 -1,
51 -1,
52 -1,
53 63,
54 52,
55 53,
56 54,
57 55,
58 56,
59 57,
60 58,
61 59,
62 60,
63 61,
64 -1,
65 -1,
66 -1,
67 -1,
68 -1,
69 -1,
70 -1,
71 0,
72 1,
73 2,
74 3,
75 4,
76 5,
77 6,
78 7,
79 8,
80 9,
81 10,
82 11,
83 12,
84 13,
85 14,
86 15,
87 16,
88 17,
89 18,
90 19,
91 20,
92 21,
93 22,
94 23,
95 24,
96 25,
97 -1,
98 -1,
99 -1,
100 -1,
101 -1,
102 -1,
103 26,
104 27,
105 28,
106 29,
107 30,
108 31,
109 32,
110 33,
111 34,
112 35,
113 36,
114 37,
115 38,
116 39,
117 40,
118 41,
119 42,
120 43,
121 44,
122 45,
123 46,
124 47,
125 48,
126 49,
127 50,
128 51,
129 -1,
130 -1,
131 -1,
132 -1,
133 -1 - 1,
134 -1,
135 -1,
136 -1,
137 -1,
138 -1,
139 -1,
140 -1,
141 -1,
142 -1,
143 -1,
144 -1,
145 -1,
146 -1,
147 -1,
148 -1,
149 -1,
150 -1,
151 -1,
152 -1,
153 -1,
154 -1,
155 -1,
156 -1,
157 -1,
158 -1,
159 -1,
160 -1,
161 -1,
162 -1,
163 -1,
164 -1,
165 -1,
166 -1,
167 -1,
168 -1,
169 -1,
170 -1,
171 -1,
172 -1,
173 -1,
174 -1,
175 -1,
176 -1,
177 -1,
178 -1,
179 -1,
180 -1,
181 -1,
182 -1,
183 -1,
184 -1,
185 -1,
186 -1,
187 -1,
188 -1,
189 -1,
190 -1,
191 -1,
192 -1,
193 -1,
194 -1,
195 -1,
196 -1,
197 -1,
198 -1,
199 -1,
200 -1,
201 -1,
202 -1,
203 -1,
204 -1,
205 -1,
206 -1,
207 -1,
208 -1,
209 -1,
210 -1,
211 -1,
212 -1,
213 -1,
214 -1,
215 -1,
216 -1,
217 -1,
218 -1,
219 -1,
220 -1,
221 -1,
222 -1,
223 -1,
224 -1,
225 -1,
226 -1,
227 -1,
228 -1,
229 -1,
230 -1,
231 -1,
232 -1,
233 -1,
234 -1,
235 -1,
236 -1,
237 -1,
238 -1,
239 -1,
240 -1,
241 -1,
242 -1,
243 -1,
244 -1,
245 -1,
246 -1,
247 -1,
248 -1,
249 -1,
250 -1,
251 -1,
252 -1,
253 -1,
254 -1,
255 -1,
256 -1,
257 -1,
258 -1,
259 -1,
260 -1,
261 -1,
262];
263
264pub fn parse_vlq_segment(segment: &str) -> Result<Vec<i64>> {
266 let mut rv = vec![];
267
268 parse_vlq_segment_into(segment, &mut rv)?;
269
270 Ok(rv)
271}
272
273pub(crate) fn parse_vlq_segment_into(segment: &str, rv: &mut Vec<i64>) -> Result<()> {
276 let mut cur = 0;
277 let mut shift = 0;
278
279 for c in segment.bytes() {
280 let enc = i64::from(B64[c as usize]);
281 let val = enc & 0b11111;
282 let cont = enc >> 5;
283 cur += val.checked_shl(shift).ok_or(Error::VlqOverflow)?;
284 shift += 5;
285
286 if cont == 0 {
287 let sign = cur & 1;
288 cur >>= 1;
289 if sign != 0 {
290 cur = -cur;
291 }
292 rv.push(cur);
293 cur = 0;
294 shift = 0;
295 }
296 }
297
298 if cur != 0 || shift != 0 {
299 Err(Error::VlqLeftover)
300 } else if rv.is_empty() {
301 Err(Error::VlqNoValues)
302 } else {
303 Ok(())
304 }
305}
306
307pub fn generate_vlq_segment(nums: &[i64]) -> Result<String> {
309 let mut rv = String::new();
310 for &num in nums {
311 encode_vlq(&mut rv, num);
312 }
313 Ok(rv)
314}
315
316pub(crate) fn encode_vlq(out: &mut String, num: i64) {
317 let mut num = if num < 0 { ((-num) << 1) + 1 } else { num << 1 };
318
319 loop {
320 let mut digit = num & 0b11111;
321 num >>= 5;
322 if num > 0 {
323 digit |= 1 << 5;
324 }
325 out.push(B64_CHARS[digit as usize] as char);
326 if num == 0 {
327 break;
328 }
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335
336 #[test]
337 fn test_vlq_decode() {
338 let rv = parse_vlq_segment("AAAA").unwrap();
339 assert_eq!(rv, vec![0, 0, 0, 0]);
340 let rv = parse_vlq_segment("GAAIA").unwrap();
341 assert_eq!(rv, vec![3, 0, 0, 4, 0]);
342 }
343
344 #[test]
345 fn test_vlq_encode() {
346 let rv = generate_vlq_segment(&[0, 0, 0, 0]).unwrap();
347 assert_eq!(rv.as_str(), "AAAA");
348 let rv = generate_vlq_segment(&[3, 0, 0, 4, 0]).unwrap();
349 assert_eq!(rv.as_str(), "GAAIA");
350 }
351
352 #[test]
353 fn test_overflow() {
354 match parse_vlq_segment("00000000000000") {
355 Err(Error::VlqOverflow) => {}
356 e => {
357 panic!("Unexpeted result: {:?}", e);
358 }
359 }
360 }
361}