OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 | |
6 /// Utilities to encode and decode VLQ values used in source maps. | |
7 /// | |
8 /// Sourcemaps are encoded with variable length numbers as base64 encoded | |
9 /// strings with the least significant digit coming first. Each base64 digit | |
10 /// encodes a 5-bit value (0-31) and a continuation bit. Signed values can be | |
11 /// represented by using the least significant bit of the value as the sign bit. | |
12 /// | |
13 /// For more details see the source map [version 3 documentation][spec]. | |
14 /// [spec]: https://docs.google.com/a/google.com/document/d/1U1RGAehQwRypUTovF1K
RlpiOFze0b-_2gc6fAH0KY0k/edit | |
15 library source_maps.src.vlq; | |
16 | |
17 import 'dart:math'; | |
18 | |
19 const int VLQ_BASE_SHIFT = 5; | |
20 | |
21 const int VLQ_BASE_MASK = (1 << 5) - 1; | |
22 | |
23 const int VLQ_CONTINUATION_BIT = 1 << 5; | |
24 | |
25 const int VLQ_CONTINUATION_MASK = 1 << 5; | |
26 | |
27 const String BASE64_DIGITS = | |
28 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; | |
29 | |
30 final Map<String, int> _digits = () { | |
31 var map = <String, int>{}; | |
32 for (int i = 0; i < 64; i++) { | |
33 map[BASE64_DIGITS[i]] = i; | |
34 } | |
35 return map; | |
36 }(); | |
37 | |
38 final int MAX_INT32 = pow(2, 31) - 1; | |
39 final int MIN_INT32 = -pow(2, 31); | |
40 | |
41 /// Creates the VLQ encoding of [value] as a sequence of characters | |
42 Iterable<String> encodeVlq(int value) { | |
43 if (value < MIN_INT32 || value > MAX_INT32) { | |
44 throw new ArgumentError('expected 32 bit int, got: $value'); | |
45 } | |
46 var res = <String>[]; | |
47 int signBit = 0; | |
48 if (value < 0) { | |
49 signBit = 1; | |
50 value = -value; | |
51 } | |
52 value = (value << 1) | signBit; | |
53 do { | |
54 int digit = value & VLQ_BASE_MASK; | |
55 value >>= VLQ_BASE_SHIFT; | |
56 if (value > 0) { | |
57 digit |= VLQ_CONTINUATION_BIT; | |
58 } | |
59 res.add(BASE64_DIGITS[digit]); | |
60 } while (value > 0); | |
61 return res; | |
62 } | |
63 | |
64 /// Decodes a value written as a sequence of VLQ characters. The first input | |
65 /// character will be `chars.current` after calling `chars.moveNext` once. The | |
66 /// iterator is advanced until a stop character is found (a character without | |
67 /// the [VLQ_CONTINUATION_BIT]). | |
68 int decodeVlq(Iterator<String> chars) { | |
69 int result = 0; | |
70 bool stop = false; | |
71 int shift = 0; | |
72 while (!stop) { | |
73 if (!chars.moveNext()) throw new StateError('incomplete VLQ value'); | |
74 var char = chars.current; | |
75 if (!_digits.containsKey(char)) { | |
76 throw new FormatException('invalid character in VLQ encoding: $char'); | |
77 } | |
78 var digit = _digits[char]; | |
79 stop = (digit & VLQ_CONTINUATION_BIT) == 0; | |
80 digit &= VLQ_BASE_MASK; | |
81 result += (digit << shift); | |
82 shift += VLQ_BASE_SHIFT; | |
83 } | |
84 | |
85 // Result uses the least significant bit as a sign bit. We convert it into a | |
86 // two-complement value. For example, | |
87 // 2 (10 binary) becomes 1 | |
88 // 3 (11 binary) becomes -1 | |
89 // 4 (100 binary) becomes 2 | |
90 // 5 (101 binary) becomes -2 | |
91 // 6 (110 binary) becomes 3 | |
92 // 7 (111 binary) becomes -3 | |
93 bool negate = (result & 1) == 1; | |
94 result = result >> 1; | |
95 result = negate ? -result : result; | |
96 | |
97 // TODO(sigmund): can we detect this earlier? | |
98 if (result < MIN_INT32 || result > MAX_INT32) { | |
99 throw new FormatException( | |
100 'expected an encoded 32 bit int, but we got: $result'); | |
101 } | |
102 return result; | |
103 } | |
OLD | NEW |