Skip to main content

swc_sourcemap/
vlq.rs

1//! Implements utilities for dealing with the sourcemap vlq encoding.
2use 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
264/// Parses a VLQ segment into a vector.
265pub 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
273/// Parses a VLQ segment into a pre-allocated `Vec` instead of returning a new
274/// allocation.
275pub(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
307/// Encodes a VLQ segment from a slice.
308pub 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}