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<()> {
275 let mut cur = 0;
276 let mut shift = 0;
277
278 for c in segment.bytes() {
279 let enc = i64::from(B64[c as usize]);
280 let val = enc & 0b11111;
281 let cont = enc >> 5;
282 cur += val.checked_shl(shift).ok_or(Error::VlqOverflow)?;
283 shift += 5;
284
285 if cont == 0 {
286 let sign = cur & 1;
287 cur >>= 1;
288 if sign != 0 {
289 cur = -cur;
290 }
291 rv.push(cur);
292 cur = 0;
293 shift = 0;
294 }
295 }
296
297 if cur != 0 || shift != 0 {
298 Err(Error::VlqLeftover)
299 } else if rv.is_empty() {
300 Err(Error::VlqNoValues)
301 } else {
302 Ok(())
303 }
304}
305
306pub fn generate_vlq_segment(nums: &[i64]) -> Result<String> {
308 let mut rv = String::new();
309 for &num in nums {
310 encode_vlq(&mut rv, num);
311 }
312 Ok(rv)
313}
314
315pub(crate) fn encode_vlq(out: &mut String, num: i64) {
316 let mut num = if num < 0 { ((-num) << 1) + 1 } else { num << 1 };
317
318 loop {
319 let mut digit = num & 0b11111;
320 num >>= 5;
321 if num > 0 {
322 digit |= 1 << 5;
323 }
324 out.push(B64_CHARS[digit as usize] as char);
325 if num == 0 {
326 break;
327 }
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
336 fn test_vlq_decode() {
337 let rv = parse_vlq_segment("AAAA").unwrap();
338 assert_eq!(rv, vec![0, 0, 0, 0]);
339 let rv = parse_vlq_segment("GAAIA").unwrap();
340 assert_eq!(rv, vec![3, 0, 0, 4, 0]);
341 }
342
343 #[test]
344 fn test_vlq_encode() {
345 let rv = generate_vlq_segment(&[0, 0, 0, 0]).unwrap();
346 assert_eq!(rv.as_str(), "AAAA");
347 let rv = generate_vlq_segment(&[3, 0, 0, 4, 0]).unwrap();
348 assert_eq!(rv.as_str(), "GAAIA");
349 }
350
351 #[test]
352 fn test_overflow() {
353 match parse_vlq_segment("00000000000000") {
354 Err(Error::VlqOverflow) => {}
355 e => {
356 panic!("Unexpeted result: {:?}", e);
357 }
358 }
359 }
360}