| 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 |