22
22
package uuid
23
23
24
24
import (
25
- "bytes"
26
- "encoding/hex"
25
+ "errors"
27
26
"fmt"
28
27
)
29
28
@@ -45,11 +44,77 @@ func FromBytesOrNil(input []byte) UUID {
45
44
return uuid
46
45
}
47
46
47
+ var errInvalidFormat = errors .New ("uuid: invalid UUID format" )
48
+
49
+ func fromHexChar (c byte ) byte {
50
+ switch {
51
+ case '0' <= c && c <= '9' :
52
+ return c - '0'
53
+ case 'a' <= c && c <= 'f' :
54
+ return c - 'a' + 10
55
+ case 'A' <= c && c <= 'F' :
56
+ return c - 'A' + 10
57
+ }
58
+ return 255
59
+ }
60
+
61
+ // Parse parses the UUID stored in the string text. Parsing and supported
62
+ // formats are the same as UnmarshalText.
63
+ func (u * UUID ) Parse (s string ) error {
64
+ switch len (s ) {
65
+ case 32 : // hash
66
+ case 36 : // canonical
67
+ case 34 , 38 :
68
+ if s [0 ] != '{' || s [len (s )- 1 ] != '}' {
69
+ return fmt .Errorf ("uuid: incorrect UUID format in string %q" , s )
70
+ }
71
+ s = s [1 : len (s )- 1 ]
72
+ case 41 , 45 :
73
+ if s [:9 ] != "urn:uuid:" {
74
+ return fmt .Errorf ("uuid: incorrect UUID format in string %q" , s [:9 ])
75
+ }
76
+ s = s [9 :]
77
+ default :
78
+ return fmt .Errorf ("uuid: incorrect UUID length %d in string %q" , len (s ), s )
79
+ }
80
+ // canonical
81
+ if len (s ) == 36 {
82
+ if s [8 ] != '-' || s [13 ] != '-' || s [18 ] != '-' || s [23 ] != '-' {
83
+ return fmt .Errorf ("uuid: incorrect UUID format in string %q" , s )
84
+ }
85
+ for i , x := range [16 ]byte {
86
+ 0 , 2 , 4 , 6 ,
87
+ 9 , 11 ,
88
+ 14 , 16 ,
89
+ 19 , 21 ,
90
+ 24 , 26 , 28 , 30 , 32 , 34 ,
91
+ } {
92
+ v1 := fromHexChar (s [x ])
93
+ v2 := fromHexChar (s [x + 1 ])
94
+ if v1 | v2 == 255 {
95
+ return errInvalidFormat
96
+ }
97
+ u [i ] = (v1 << 4 ) | v2
98
+ }
99
+ return nil
100
+ }
101
+ // hash like
102
+ for i := 0 ; i < 32 ; i += 2 {
103
+ v1 := fromHexChar (s [i ])
104
+ v2 := fromHexChar (s [i + 1 ])
105
+ if v1 | v2 == 255 {
106
+ return errInvalidFormat
107
+ }
108
+ u [i / 2 ] = (v1 << 4 ) | v2
109
+ }
110
+ return nil
111
+ }
112
+
48
113
// FromString returns a UUID parsed from the input string.
49
114
// Input is expected in a form accepted by UnmarshalText.
50
- func FromString (input string ) (UUID , error ) {
51
- u := UUID {}
52
- err := u .UnmarshalText ([] byte ( input ) )
115
+ func FromString (text string ) (UUID , error ) {
116
+ var u UUID
117
+ err := u .Parse ( text )
53
118
return u , err
54
119
}
55
120
@@ -66,7 +131,9 @@ func FromStringOrNil(input string) UUID {
66
131
// MarshalText implements the encoding.TextMarshaler interface.
67
132
// The encoding is the same as returned by the String() method.
68
133
func (u UUID ) MarshalText () ([]byte , error ) {
69
- return []byte (u .String ()), nil
134
+ var buf [36 ]byte
135
+ encodeHex (buf [:], u )
136
+ return buf [:], nil
70
137
}
71
138
72
139
// UnmarshalText implements the encoding.TextUnmarshaler interface.
@@ -103,96 +170,52 @@ func (u UUID) MarshalText() ([]byte, error) {
103
170
// braced := '{' plain '}' | '{' hashlike '}'
104
171
// urn := URN ':' UUID-NID ':' plain
105
172
//
106
- func (u * UUID ) UnmarshalText (text []byte ) error {
107
- switch len (text ) {
108
- case 32 :
109
- return u . decodeHashLike ( text )
173
+ func (u * UUID ) UnmarshalText (b []byte ) error {
174
+ switch len (b ) {
175
+ case 32 : // hash
176
+ case 36 : // canonical
110
177
case 34 , 38 :
111
- return u .decodeBraced (text )
112
- case 36 :
113
- return u .decodeCanonical (text )
178
+ if b [0 ] != '{' || b [len (b )- 1 ] != '}' {
179
+ return fmt .Errorf ("uuid: incorrect UUID format in string %q" , b )
180
+ }
181
+ b = b [1 : len (b )- 1 ]
114
182
case 41 , 45 :
115
- return u .decodeURN (text )
183
+ if string (b [:9 ]) != "urn:uuid:" {
184
+ return fmt .Errorf ("uuid: incorrect UUID format in string %q" , b [:9 ])
185
+ }
186
+ b = b [9 :]
116
187
default :
117
- return fmt .Errorf ("uuid: incorrect UUID length %d in string %q" , len (text ), text )
118
- }
119
- }
120
-
121
- // decodeCanonical decodes UUID strings that are formatted as defined in RFC-4122 (section 3):
122
- // "6ba7b810-9dad-11d1-80b4-00c04fd430c8".
123
- func (u * UUID ) decodeCanonical (t []byte ) error {
124
- if t [8 ] != '-' || t [13 ] != '-' || t [18 ] != '-' || t [23 ] != '-' {
125
- return fmt .Errorf ("uuid: incorrect UUID format in string %q" , t )
188
+ return fmt .Errorf ("uuid: incorrect UUID length %d in string %q" , len (b ), b )
126
189
}
127
-
128
- src := t
129
- dst := u [:]
130
-
131
- for i , byteGroup := range byteGroups {
132
- if i > 0 {
133
- src = src [1 :] // skip dash
190
+ if len (b ) == 36 {
191
+ if b [8 ] != '-' || b [13 ] != '-' || b [18 ] != '-' || b [23 ] != '-' {
192
+ return fmt .Errorf ("uuid: incorrect UUID format in string %q" , b )
134
193
}
135
- _ , err := hex .Decode (dst [:byteGroup / 2 ], src [:byteGroup ])
136
- if err != nil {
137
- return err
194
+ for i , x := range [16 ]byte {
195
+ 0 , 2 , 4 , 6 ,
196
+ 9 , 11 ,
197
+ 14 , 16 ,
198
+ 19 , 21 ,
199
+ 24 , 26 , 28 , 30 , 32 , 34 ,
200
+ } {
201
+ v1 := fromHexChar (b [x ])
202
+ v2 := fromHexChar (b [x + 1 ])
203
+ if v1 | v2 == 255 {
204
+ return errInvalidFormat
205
+ }
206
+ u [i ] = (v1 << 4 ) | v2
138
207
}
139
- src = src [byteGroup :]
140
- dst = dst [byteGroup / 2 :]
141
- }
142
-
143
- return nil
144
- }
145
-
146
- // decodeHashLike decodes UUID strings that are using the following format:
147
- // "6ba7b8109dad11d180b400c04fd430c8".
148
- func (u * UUID ) decodeHashLike (t []byte ) error {
149
- src := t [:]
150
- dst := u [:]
151
-
152
- _ , err := hex .Decode (dst , src )
153
- return err
154
- }
155
-
156
- // decodeBraced decodes UUID strings that are using the following formats:
157
- // "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}"
158
- // "{6ba7b8109dad11d180b400c04fd430c8}".
159
- func (u * UUID ) decodeBraced (t []byte ) error {
160
- l := len (t )
161
-
162
- if t [0 ] != '{' || t [l - 1 ] != '}' {
163
- return fmt .Errorf ("uuid: incorrect UUID format in string %q" , t )
208
+ return nil
164
209
}
165
-
166
- return u .decodePlain (t [1 : l - 1 ])
167
- }
168
-
169
- // decodeURN decodes UUID strings that are using the following formats:
170
- // "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
171
- // "urn:uuid:6ba7b8109dad11d180b400c04fd430c8".
172
- func (u * UUID ) decodeURN (t []byte ) error {
173
- total := len (t )
174
-
175
- urnUUIDPrefix := t [:9 ]
176
-
177
- if ! bytes .Equal (urnUUIDPrefix , urnPrefix ) {
178
- return fmt .Errorf ("uuid: incorrect UUID format in string %q" , t )
179
- }
180
-
181
- return u .decodePlain (t [9 :total ])
182
- }
183
-
184
- // decodePlain decodes UUID strings that are using the following formats:
185
- // "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format
186
- // "6ba7b8109dad11d180b400c04fd430c8".
187
- func (u * UUID ) decodePlain (t []byte ) error {
188
- switch len (t ) {
189
- case 32 :
190
- return u .decodeHashLike (t )
191
- case 36 :
192
- return u .decodeCanonical (t )
193
- default :
194
- return fmt .Errorf ("uuid: incorrect UUID length %d in string %q" , len (t ), t )
210
+ for i := 0 ; i < 32 ; i += 2 {
211
+ v1 := fromHexChar (b [i ])
212
+ v2 := fromHexChar (b [i + 1 ])
213
+ if v1 | v2 == 255 {
214
+ return errInvalidFormat
215
+ }
216
+ u [i / 2 ] = (v1 << 4 ) | v2
195
217
}
218
+ return nil
196
219
}
197
220
198
221
// MarshalBinary implements the encoding.BinaryMarshaler interface.
0 commit comments