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