embed.mx raw
1 package kind
2
3 import (
4 _ "embed"
5 "runtime"
6 )
7
8 //:embed kinds.json
9 var KindsJSON []byte
10
11 type KindData struct {
12 Version string `json:"version"`
13 Source string `json:"source"`
14 Ranges Ranges `json:"ranges"`
15 Kinds map[string]KindInfo `json:"kinds"`
16 Privileged []int32 `json:"privileged"`
17 Directory []int32 `json:"directory"`
18 Aliases map[string]int32 `json:"aliases"`
19 }
20
21 type Ranges struct {
22 Regular Range `json:"regular"`
23 Replaceable Range `json:"replaceable"`
24 Ephemeral Range `json:"ephemeral"`
25 Parameterized Range `json:"parameterized"`
26 }
27
28 type Range struct {
29 Start int32 `json:"start"`
30 End int32 `json:"end"`
31 Description string `json:"description"`
32 }
33
34 type KindInfo struct {
35 Name string `json:"name"`
36 NIP *string `json:"nip,omitempty"`
37 Description string `json:"description"`
38 Classification string `json:"classification,omitempty"`
39 Deprecated bool `json:"deprecated,omitempty"`
40 DeprecatedBy string `json:"deprecatedBy,omitempty"`
41 Spec string `json:"spec,omitempty"`
42 RangeEnd int32 `json:"rangeEnd,omitempty"`
43 }
44
45 var kindData *KindData
46
47 func initKindData() {
48 if kindData != nil {
49 return
50 }
51 initKinds()
52 // kindData and the map it holds are package globals: build them in the
53 // root arena, not in this call's frame arena which dies on return.
54 runtime.SovereignSetArena(runtime.RootArena())
55 kindData = &KindData{Kinds: map[string]KindInfo{}}
56 parseKindsJSON(KindsJSON)
57 runtime.SovereignRestoreArena(runtime.RootArena())
58 }
59
60 // parseKindsJSON is a minimal parser that extracts kind data from the
61 // embedded JSON. Avoids encoding/json which crashes under moxie's runtime.
62 // The body lives in initParseKindsJSON because it writes the package-level
63 // kind name map; the init-prefixed name is the rule's own exemption.
64 func parseKindsJSON(data []byte) { initParseKindsJSON(data) }
65
66 func initParseKindsJSON(data []byte) {
67 // Find "kinds": {
68 kindsKey := []byte(`"kinds"`)
69 idx := indexBytes(data, kindsKey)
70 if idx < 0 {
71 return
72 }
73 data = data[idx+len(kindsKey):]
74 // Skip to opening brace.
75 for len(data) > 0 && data[0] != '{' {
76 data = data[1:]
77 }
78 if len(data) == 0 {
79 return
80 }
81 data = data[1:] // skip {
82
83 // Parse each "N": { ... } entry.
84 for len(data) > 0 {
85 data = skipWS(data)
86 if len(data) == 0 || data[0] == '}' {
87 break
88 }
89 if data[0] == ',' {
90 data = data[1:]
91 continue
92 }
93 // Parse key (kind number as string).
94 kStr, rest := parseQuoted(data)
95 if rest == nil {
96 break
97 }
98 data = rest
99 data = skipWS(data)
100 if len(data) == 0 || data[0] != ':' {
101 break
102 }
103 data = data[1:]
104 data = skipWS(data)
105
106 // Parse value object.
107 info, rest := parseKindInfoObj(data)
108 if rest == nil {
109 break
110 }
111 data = rest
112
113 kindData.Kinds[string(kStr)] = info
114 k := atoi(kStr)
115 if k >= 0 {
116 Map[uint16(k)] = info.Name
117 }
118 }
119
120 // Parse ranges.
121 rangesKey := []byte(`"ranges"`)
122 idx = indexBytes(KindsJSON, rangesKey)
123 if idx >= 0 {
124 r := KindsJSON[idx+len(rangesKey):]
125 for len(r) > 0 && r[0] != '{' {
126 r = r[1:]
127 }
128 if len(r) > 0 {
129 r = r[1:]
130 kindData.Ranges.Regular = parseRange(r, []byte(`"regular"`))
131 kindData.Ranges.Replaceable = parseRange(r, []byte(`"replaceable"`))
132 kindData.Ranges.Ephemeral = parseRange(r, []byte(`"ephemeral"`))
133 kindData.Ranges.Parameterized = parseRange(r, []byte(`"parameterized"`))
134 }
135 }
136 }
137
138 func parseRange(data, key []byte) (r Range) {
139 idx := indexBytes(data, key)
140 if idx < 0 {
141 return Range{}
142 }
143 d := data[idx+len(key):]
144 for len(d) > 0 && d[0] != '{' {
145 d = d[1:]
146 }
147 if len(d) == 0 {
148 return Range{}
149 }
150 end := matchBrace(d)
151 obj := d[:end+1]
152 return Range{
153 Start: getIntField(obj, []byte(`"start"`)),
154 End: getIntField(obj, []byte(`"end"`)),
155 Description: string(getStringField(obj, []byte(`"description"`))),
156 }
157 }
158
159 func parseKindInfoObj(data []byte) (ki KindInfo, rem []byte) {
160 if len(data) == 0 || data[0] != '{' {
161 return KindInfo{}, nil
162 }
163 end := matchBrace(data)
164 obj := data[1:end]
165 rest := data[end+1:]
166
167 info := KindInfo{
168 Name: string(getStringField(obj, []byte(`"name"`))),
169 Description: string(getStringField(obj, []byte(`"description"`))),
170 Classification: string(getStringField(obj, []byte(`"classification"`))),
171 DeprecatedBy: string(getStringField(obj, []byte(`"deprecatedBy"`))),
172 Spec: string(getStringField(obj, []byte(`"spec"`))),
173 RangeEnd: getIntField(obj, []byte(`"rangeEnd"`)),
174 }
175 nip := getStringField(obj, []byte(`"nip"`))
176 if nip != nil {
177 s := string(nip)
178 info.NIP = &s
179 }
180 if indexBytes(obj, []byte(`"deprecated":true`)) >= 0 || indexBytes(obj, []byte(`"deprecated": true`)) >= 0 {
181 info.Deprecated = true
182 }
183 return info, rest
184 }
185
186 func matchBrace(data []byte) (n int32) {
187 depth := 0
188 inStr := false
189 for i := 0; i < len(data); i++ {
190 if inStr {
191 if data[i] == '\\' {
192 i++
193 } else if data[i] == '"' {
194 inStr = false
195 }
196 continue
197 }
198 switch data[i] {
199 case '"':
200 inStr = true
201 case '{':
202 depth++
203 case '}':
204 depth--
205 if depth == 0 {
206 return i
207 }
208 }
209 }
210 return len(data) - 1
211 }
212
213 func getStringField(obj, key []byte) (buf []byte) {
214 idx := indexBytes(obj, key)
215 if idx < 0 {
216 return nil
217 }
218 after := obj[idx+len(key):]
219 // skip : and whitespace
220 for len(after) > 0 && (after[0] == ':' || after[0] == ' ' || after[0] == '\t') {
221 after = after[1:]
222 }
223 if len(after) == 0 || after[0] != '"' {
224 return nil
225 }
226 v, _ := parseQuoted(after)
227 return v
228 }
229
230 func getIntField(obj, key []byte) (n int32) {
231 idx := indexBytes(obj, key)
232 if idx < 0 {
233 return 0
234 }
235 after := obj[idx+len(key):]
236 for len(after) > 0 && (after[0] == ':' || after[0] == ' ' || after[0] == '\t') {
237 after = after[1:]
238 }
239 n := 0
240 for len(after) > 0 && after[0] >= '0' && after[0] <= '9' {
241 n = n*10 + int32(after[0]-'0')
242 after = after[1:]
243 }
244 return n
245 }
246
247 func parseQuoted(data []byte) (val []byte, rem []byte) {
248 if len(data) == 0 || data[0] != '"' {
249 return nil, nil
250 }
251 for i := 1; i < len(data); i++ {
252 if data[i] == '\\' {
253 i++
254 continue
255 }
256 if data[i] == '"' {
257 return data[1:i], data[i+1:]
258 }
259 }
260 return nil, nil
261 }
262
263 func indexBytes(data, sep []byte) (n int32) {
264 for i := 0; i <= len(data)-len(sep); i++ {
265 match := true
266 for j := 0; j < len(sep); j++ {
267 if data[i+j] != sep[j] {
268 match = false
269 break
270 }
271 }
272 if match {
273 return i
274 }
275 }
276 return -1
277 }
278
279 func skipWS(data []byte) (buf []byte) {
280 for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\n' || data[0] == '\r') {
281 data = data[1:]
282 }
283 return data
284 }
285
286 func atoi(b []byte) (n int32) {
287 if len(b) == 0 {
288 return -1
289 }
290 n := 0
291 for _, c := range b {
292 if c < '0' || c > '9' {
293 return -1
294 }
295 n = n*10 + int32(c-'0')
296 }
297 return n
298 }
299
300 func GetKindData() (k *KindData) { initKindData(); return kindData }
301
302 func GetKindInfo(k uint16) (ki KindInfo, found bool) {
303 initKindData()
304 kStr := itoa(int32(k))
305 info, ok := kindData.Kinds[kStr]
306 return info, ok
307 }
308
309 func itoa(n int32) (s string) {
310 if n == 0 {
311 return "0"
312 }
313 var digits []byte
314 negative := n < 0
315 if negative {
316 n = -n
317 }
318 for n > 0 {
319 digits = []byte{byte('0' + n%10)} | digits
320 n /= 10
321 }
322 if negative {
323 digits = []byte{'-'} | digits
324 }
325 return string(digits)
326 }
327
328 func GetDescription(k uint16) (s string) {
329 if info, ok := GetKindInfo(k); ok {
330 return info.Description
331 }
332 return ""
333 }
334
335 func GetNIP(k uint16) (s string) {
336 if info, ok := GetKindInfo(k); ok && info.NIP != nil {
337 return *info.NIP
338 }
339 return ""
340 }
341
342 func IsDeprecated(k uint16) (ok bool) {
343 if info, found := GetKindInfo(k); found {
344 return info.Deprecated
345 }
346 return false
347 }
348
349 func GetClassification(k uint16) (s string) {
350 initKindData()
351 if info, ok := GetKindInfo(k); ok && info.Classification != "" {
352 return info.Classification
353 }
354 if k >= uint16(kindData.Ranges.Parameterized.Start) && k <= uint16(kindData.Ranges.Parameterized.End) {
355 return "parameterized"
356 }
357 if k >= uint16(kindData.Ranges.Ephemeral.Start) && k < uint16(kindData.Ranges.Ephemeral.End) {
358 return "ephemeral"
359 }
360 if k >= uint16(kindData.Ranges.Replaceable.Start) && k < uint16(kindData.Ranges.Replaceable.End) {
361 return "replaceable"
362 }
363 if k >= uint16(kindData.Ranges.Regular.Start) && k <= uint16(kindData.Ranges.Regular.End) {
364 return "regular"
365 }
366 if k == 0 || k == 3 {
367 return "replaceable"
368 }
369 return "regular"
370 }
371