-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
sourcemap.go
179 lines (151 loc) · 3.52 KB
/
sourcemap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package sourcemap
import (
"bytes"
)
type Mapping struct {
GeneratedLine int32 // 0-based
GeneratedColumn int32 // 0-based count of UTF-16 code units
SourceIndex int32 // 0-based
OriginalLine int32 // 0-based
OriginalColumn int32 // 0-based count of UTF-16 code units
}
type SourceMap struct {
Sources []string
SourcesContent []SourceContent
Mappings []Mapping
}
type SourceContent struct {
// This stores both the unquoted and the quoted values. We try to use the
// already-quoted value if possible so we don't need to re-quote it
// unnecessarily for maximum performance.
Quoted string
// But sometimes we need to re-quote the value, such as when it contains
// non-ASCII characters and we are in ASCII-only mode. In that case we quote
// this parsed UTF-16 value.
Value []uint16
}
func (sm *SourceMap) Find(line int32, column int32) *Mapping {
mappings := sm.Mappings
// Binary search
count := len(mappings)
index := 0
for count > 0 {
step := count / 2
i := index + step
mapping := mappings[i]
if mapping.GeneratedLine < line || (mapping.GeneratedLine == line && mapping.GeneratedColumn <= column) {
index = i + 1
count -= step + 1
} else {
count = step
}
}
// Handle search failure
if index > 0 {
mapping := &mappings[index-1]
// Match the behavior of the popular "source-map" library from Mozilla
if mapping.GeneratedLine == line {
return mapping
}
}
return nil
}
var base64 = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
// A single base 64 digit can contain 6 bits of data. For the base 64 variable
// length quantities we use in the source map spec, the first bit is the sign,
// the next four bits are the actual value, and the 6th bit is the continuation
// bit. The continuation bit tells us whether there are more digits in this
// value following this digit.
//
// Continuation
// | Sign
// | |
// V V
// 101011
//
func EncodeVLQ(value int) []byte {
var vlq int
if value < 0 {
vlq = ((-value) << 1) | 1
} else {
vlq = value << 1
}
// Handle the common case up front without allocations
if (vlq >> 5) == 0 {
digit := vlq & 31
return base64[digit : digit+1]
}
encoded := []byte{}
for {
digit := vlq & 31
vlq >>= 5
// If there are still more digits in this value, we must make sure the
// continuation bit is marked
if vlq != 0 {
digit |= 32
}
encoded = append(encoded, base64[digit])
if vlq == 0 {
break
}
}
return encoded
}
func DecodeVLQ(encoded []byte, start int) (int, int) {
shift := 0
vlq := 0
// Scan over the input
for {
index := bytes.IndexByte(base64, encoded[start])
if index < 0 {
break
}
// Decode a single byte
vlq |= (index & 31) << shift
start++
shift += 5
// Stop if there's no continuation bit
if (index & 32) == 0 {
break
}
}
// Recover the value
value := vlq >> 1
if (vlq & 1) != 0 {
value = -value
}
return value, start
}
func DecodeVLQUTF16(encoded []uint16) (int, int, bool) {
n := len(encoded)
if n == 0 {
return 0, 0, false
}
// Scan over the input
current := 0
shift := 0
vlq := 0
for {
if current >= n {
return 0, 0, false
}
index := bytes.IndexByte(base64, byte(encoded[current]))
if index < 0 {
return 0, 0, false
}
// Decode a single byte
vlq |= (index & 31) << shift
current++
shift += 5
// Stop if there's no continuation bit
if (index & 32) == 0 {
break
}
}
// Recover the value
var value = vlq >> 1
if (vlq & 1) != 0 {
value = -value
}
return value, current, true
}