134 lines
2.4 KiB
Go
134 lines
2.4 KiB
Go
package html
|
|
|
|
// Note: this file is WASM-linked. Per RFC §7 the WASM build must stay under the
|
|
// 3.5 MB raw / 1 MB gzip size budget, so we deliberately avoid importing
|
|
// dappco.re/go/core here — it transitively pulls in fmt/os/log (~500 KB+).
|
|
// stdlib strings is safe for WASM.
|
|
|
|
// ParseBlockID extracts the slot sequence from a data-block ID.
|
|
// Usage example: slots := ParseBlockID("C.0.1")
|
|
// It accepts the current dotted coordinate form and the older hyphenated
|
|
// form for compatibility. Mixed separators and malformed coordinates are
|
|
// rejected.
|
|
func ParseBlockID(id string) []byte {
|
|
if id == "" {
|
|
return nil
|
|
}
|
|
|
|
tokens := make([]string, 0, 4)
|
|
sepKind := byte(0)
|
|
|
|
for i := 0; i < len(id); {
|
|
start := i
|
|
for i < len(id) && id[i] != '.' && id[i] != '-' {
|
|
i++
|
|
}
|
|
|
|
token := id[start:i]
|
|
if token == "" {
|
|
return nil
|
|
}
|
|
tokens = append(tokens, token)
|
|
|
|
if i == len(id) {
|
|
break
|
|
}
|
|
|
|
sep := id[i]
|
|
if sepKind == 0 {
|
|
sepKind = sep
|
|
} else if sepKind != sep {
|
|
return nil
|
|
}
|
|
i++
|
|
if i == len(id) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
switch sepKind {
|
|
case 0, '.':
|
|
return parseDottedBlockID(tokens)
|
|
case '-':
|
|
return parseHyphenatedBlockID(tokens)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func parseDottedBlockID(tokens []string) []byte {
|
|
if len(tokens) == 0 || !isSlotToken(tokens[0]) {
|
|
return nil
|
|
}
|
|
if len(tokens) > 1 && isSlotToken(tokens[len(tokens)-1]) {
|
|
return nil
|
|
}
|
|
|
|
slots := make([]byte, 0, len(tokens))
|
|
slots = append(slots, tokens[0][0])
|
|
|
|
prevWasSlot := true
|
|
for i := 1; i < len(tokens); i++ {
|
|
token := tokens[i]
|
|
if isSlotToken(token) {
|
|
if prevWasSlot {
|
|
return nil
|
|
}
|
|
slots = append(slots, token[0])
|
|
prevWasSlot = true
|
|
continue
|
|
}
|
|
|
|
if !allDigits(token) {
|
|
return nil
|
|
}
|
|
prevWasSlot = false
|
|
}
|
|
|
|
return slots
|
|
}
|
|
|
|
func parseHyphenatedBlockID(tokens []string) []byte {
|
|
if len(tokens) < 2 || len(tokens)%2 != 0 {
|
|
return nil
|
|
}
|
|
if !isSlotToken(tokens[0]) {
|
|
return nil
|
|
}
|
|
|
|
slots := make([]byte, 0, len(tokens)/2)
|
|
for i, token := range tokens {
|
|
switch {
|
|
case i%2 == 0:
|
|
if !isSlotToken(token) {
|
|
return nil
|
|
}
|
|
slots = append(slots, token[0])
|
|
case token != "0":
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return slots
|
|
}
|
|
|
|
func isSlotToken(token string) bool {
|
|
if len(token) != 1 {
|
|
return false
|
|
}
|
|
_, ok := slotRegistry[token[0]]
|
|
return ok
|
|
}
|
|
|
|
func allDigits(s string) bool {
|
|
if s == "" {
|
|
return false
|
|
}
|
|
for i := 0; i < len(s); i++ {
|
|
ch := s[i]
|
|
if ch < '0' || ch > '9' {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|