-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparse.go
More file actions
187 lines (168 loc) · 6.07 KB
/
parse.go
File metadata and controls
187 lines (168 loc) · 6.07 KB
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
180
181
182
183
184
185
186
187
package uuid
import "fmt"
// xvalues maps hex character bytes to their values; 0xff marks invalid.
var xvalues = [256]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}
// xtob converts two hex characters into a byte.
func xtob(x1, x2 byte) (byte, bool) {
b1 := xvalues[x1]
b2 := xvalues[x2]
return b1<<4 | b2, b1 != 0xff && b2 != 0xff
}
// hexOffsets maps each UUID byte index to the position of its high hex digit
// within the 36-char hyphenated format (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
var hexOffsets = [16]int{
0, 2, 4, 6, // bytes 0–3
9, 11, // bytes 4–5
14, 16, // bytes 6–7
19, 21, // bytes 8–9
24, 26, 28, 30, 32, 34, // bytes 10–15
}
// Parse parses a UUID from the standard 36-character hyphenated form:
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
//
// For URN, braced, or compact (32-hex) forms, use [ParseLenient].
func Parse(s string) (UUID, error) {
if len(s) != 36 {
return Nil, &ParseError{Input: s, Msg: "expected 36-character hyphenated format"}
}
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return Nil, &ParseError{Input: s, Msg: "expected hyphens at positions 8, 13, 18, 23"}
}
var u UUID
for i, x := range hexOffsets {
v, ok := xtob(s[x], s[x+1])
if !ok {
return Nil, &ParseError{Input: s, Msg: "invalid hex character"}
}
u[i] = v
}
return u, nil
}
// ParseLenient parses a UUID from any of these forms:
// - Standard: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (36 chars)
// - URN: urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (45 chars)
// - Braced: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} (38 chars)
// - Compact: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (32 chars)
func ParseLenient(s string) (UUID, error) {
switch len(s) {
case 36: // standard
return parseHex(s, 0)
case 45: // urn:uuid:
if s[:9] != "urn:uuid:" {
return Nil, &ParseError{Input: s, Msg: "expected urn:uuid: prefix"}
}
return parseHex(s, 9)
case 38: // {braced}
if s[0] != '{' || s[37] != '}' {
return Nil, &ParseError{Input: s, Msg: "expected braces"}
}
return parseHex(s, 1)
case 32: // compact (no hyphens)
return parseCompact(s)
default:
return Nil, &ParseError{Input: s, Msg: "unrecognized UUID format"}
}
}
// MustParse is like [Parse] but panics if the string cannot be parsed.
// It simplifies initialization of global variables holding UUIDs.
func MustParse(s string) UUID {
id, err := Parse(s)
if err != nil {
panic(err)
}
return id
}
// FromBytes creates a UUID from a 16-byte slice.
func FromBytes(b []byte) (UUID, error) {
if len(b) != 16 {
return Nil, &LengthError{Got: len(b), Want: "16 bytes"}
}
return UUID(b), nil
}
// parseHex decodes the 32 hex digits from s starting at offset,
// skipping the hyphens at the standard positions.
func parseHex(s string, offset int) (UUID, error) {
if s[offset+8] != '-' || s[offset+13] != '-' || s[offset+18] != '-' || s[offset+23] != '-' {
return Nil, &ParseError{Input: s, Msg: "missing or misplaced hyphens"}
}
var u UUID
for i, x := range hexOffsets {
x += offset
v, ok := xtob(s[x], s[x+1])
if !ok {
return Nil, &ParseError{Input: s, Msg: "invalid hex character"}
}
u[i] = v
}
return u, nil
}
// parseCompact decodes a 32-character hex string with no hyphens.
func parseCompact(s string) (UUID, error) {
var u UUID
for i := range 16 {
v, ok := xtob(s[i*2], s[i*2+1])
if !ok {
return Nil, &ParseError{Input: s, Msg: "invalid hex character"}
}
u[i] = v
}
return u, nil
}
// parseHexBytes decodes 32 hex digits from b starting at offset,
// writing the result into u. Used by UnmarshalText to avoid string conversion.
func parseHexBytes(u *UUID, b []byte, offset int) bool {
for i, x := range hexOffsets {
x += offset
v, ok := xtob(b[x], b[x+1])
if !ok {
return false
}
u[i] = v
}
return true
}
// ParseError is returned when a UUID string cannot be parsed.
//
// Use [errors.AsType] to check for this error:
//
// if perr, ok := errors.AsType[*ParseError](err); ok {
// fmt.Println(perr.Input)
// }
type ParseError struct {
Input string // the string that failed to parse
Msg string // description of the problem
}
func (e *ParseError) Error() string {
return fmt.Sprintf("uuid: parsing %q: %s", e.Input, e.Msg)
}
// LengthError is returned when the input has an unexpected byte length.
//
// Use [errors.AsType] to check for this error:
//
// if lerr, ok := errors.AsType[*LengthError](err); ok {
// fmt.Println(lerr.Got, lerr.Want)
// }
type LengthError struct {
Got int // the actual length
Want string // description of expected length
}
func (e *LengthError) Error() string {
return fmt.Sprintf("uuid: unexpected length %d, want %s", e.Got, e.Want)
}