// Package keys holds what the mesh's kept values of more than one domain share: the version a kept value carries and // the escaping of a name into a key part (novox/hq ADR 0634 §1, §4; they were go/delivery's and are used by the // Delivery, Placement and Identity and access domains alike). It names no seat and no module. package keys import ( "encoding/hex" "fmt" "regexp" "strings" ) // Version is the version of every kept value that carries one and the only one its readers read. const Version = 1 var nameOK = regexp.MustCompile(`^[-/_=a-zA-Z0-9]+$`) // EscapeName is name as a key part: a character outside letters, digits, `-`, `/` and `_` is written `=` and its // two lowercase hex digits per byte, and `=` itself is escaped, so any non-empty name keys and a name of allowed // characters only keys as it is. UnescapeName is its inverse. func EscapeName(name string) string { var b strings.Builder for i := 0; i < len(name); i++ { c := name[i] switch { case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c >= '0' && c <= '9', c == '-', c == '/', c == '_': b.WriteByte(c) default: fmt.Fprintf(&b, "=%02x", c) } } return b.String() } // UnescapeName is the inverse of EscapeName; it refuses a part with a bare `=` or any character EscapeName leaves // escaped. func UnescapeName(part string) (string, error) { var out []byte for i := 0; i < len(part); i++ { c := part[i] if c != '=' { if !nameOK.MatchString(string(c)) { return "", fmt.Errorf("keys: %q holds %q, which a key part escapes", part, c) } out = append(out, c) continue } if i+3 > len(part) { return "", fmt.Errorf("keys: %q ends in an incomplete escape", part) } v, err := hex.DecodeString(part[i+1 : i+3]) if err != nil || part[i+1:i+3] != strings.ToLower(part[i+1:i+3]) { return "", fmt.Errorf("keys: %q holds a malformed escape %q", part, part[i:i+3]) } out = append(out, v[0]) i += 2 } return string(out), nil }