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 |