@@ -5,7 +5,7 @@ package bluetooth
55
66import (
77 "errors"
8- "strings "
8+ "unsafe "
99)
1010
1111// UUID is a single UUID as used in the Bluetooth stack. It is represented as a
@@ -112,75 +112,311 @@ func (uuid UUID) Bytes() [16]byte {
112112 return buf
113113}
114114
115- // ParseUUID parses the given UUID, which must be in
116- // 00001234-0000-1000-8000-00805f9b34fb format. This means that it cannot (yet)
117- // parse 16-bit UUIDs unless they are serialized as a 128-bit UUID. If the UUID
118- // cannot be parsed, an error is returned. It will always successfully parse
119- // UUIDs generated by UUID.String().
115+ // AppendBinary appends the bytes of the uuid to the given byte slice b.
116+ func (uuid UUID ) AppendBinary (b []byte ) ([]byte , error ) {
117+ return append (b ,
118+ byte (uuid [0 ]),
119+ byte (uuid [0 ]>> 8 ),
120+ byte (uuid [0 ]>> 16 ),
121+ byte (uuid [0 ]>> 24 ),
122+ byte (uuid [1 ]),
123+ byte (uuid [1 ]>> 8 ),
124+ byte (uuid [1 ]>> 16 ),
125+ byte (uuid [1 ]>> 24 ),
126+ byte (uuid [2 ]),
127+ byte (uuid [2 ]>> 8 ),
128+ byte (uuid [2 ]>> 16 ),
129+ byte (uuid [2 ]>> 24 ),
130+ byte (uuid [3 ]),
131+ byte (uuid [3 ]>> 8 ),
132+ byte (uuid [3 ]>> 16 ),
133+ byte (uuid [3 ]>> 24 ),
134+ ), nil
135+ }
136+
137+ // MarshalBinary marshals the uuid into and byte slice and returns the slice. It will not return an error
138+ func (uuid UUID ) MarshalBinary () (data []byte , err error ) {
139+ return uuid .AppendBinary (make ([]byte , 0 , 16 ))
140+ }
141+
142+ // ParseUUID parses the given UUID
143+ //
144+ // Expected formats:
145+ //
146+ // 00001234-0000-1000-8000-00805f9b34fb
147+ // 00001234
148+ // 1234
149+ //
150+ // If the UUID cannot be parsed, an error is returned.
151+ // It will always successfully parse UUIDs generated by UUID.String().
120152func ParseUUID (s string ) (uuid UUID , err error ) {
121- uuidIndex := 0
122- for i := 0 ; i < len (s ); i ++ {
123- c := s [i ]
124- if c == '-' {
125- continue
153+ err = (& uuid ).UnmarshalText ([]byte (s ))
154+ return
155+ }
156+
157+ // UnmarshalText unmarshals a text representation of a UUID.
158+ //
159+ // Expected formats:
160+ //
161+ // 00001234-0000-1000-8000-00805f9b34fb
162+ // 00001234
163+ // 1234
164+ //
165+ // If the UUID cannot be parsed, an error is returned.
166+ // It will always successfully parse UUIDs generated by UUID.String().
167+ // This method is an adaptation of hex.Decode idea of using a reverse hex table.
168+ func (u * UUID ) UnmarshalText (s []byte ) error {
169+ switch len (s ) {
170+ case 36 :
171+ return u .unmarshalText128 (s )
172+ case 8 :
173+ return u .unmarshalText32 (s )
174+ case 4 :
175+ return u .unmarshalText16 (s )
176+ default :
177+ return errInvalidUUID
178+ }
179+ }
180+
181+ // Using the reverseHexTable rebuild the UUID from the string s represented in bytes
182+ // This implementation is the inverse of MarshalText and reaches performance parity
183+ func (u * UUID ) unmarshalText128 (s []byte ) error {
184+ var j uint8
185+ for i := 3 ; i >= 0 ; i -- {
186+ // Skip hyphens
187+ if s [j ] == '-' {
188+ j ++
189+ }
190+
191+ if reverseHexTable [s [j ]] == 255 {
192+ return errInvalidUUID
193+ }
194+ u [i ] |= uint32 (reverseHexTable [s [j ]]) << 28
195+ j ++
196+
197+ if reverseHexTable [s [j ]] == 255 {
198+ return errInvalidUUID
199+ }
200+ u [i ] |= uint32 (reverseHexTable [s [j ]]) << 24
201+ j ++
202+
203+ if reverseHexTable [s [j ]] == 255 {
204+ return errInvalidUUID
205+ }
206+ u [i ] |= uint32 (reverseHexTable [s [j ]]) << 20
207+ j ++
208+
209+ if reverseHexTable [s [j ]] == 255 {
210+ return errInvalidUUID
126211 }
127- var nibble byte
128- if c >= '0' && c <= '9' {
129- nibble = c - '0' + 0x0
130- } else if c >= 'a' && c <= 'f' {
131- nibble = c - 'a' + 0xa
132- } else if c >= 'A' && c <= 'F' {
133- nibble = c - 'A' + 0xa
134- } else {
135- err = errInvalidUUID
136- return
212+ u [ i ] |= uint32 ( reverseHexTable [ s [ j ]]) << 16
213+ j ++
214+
215+ // skip hypens
216+ if s [ j ] == '-' {
217+ j ++
218+ }
219+
220+ if reverseHexTable [ s [ j ]] == 255 {
221+ return errInvalidUUID
137222 }
138- if uuidIndex > 31 {
139- err = errInvalidUUID
140- return
223+ u [i ] |= uint32 (reverseHexTable [s [j ]]) << 12
224+ j ++
225+
226+ if reverseHexTable [s [j ]] == 255 {
227+ return errInvalidUUID
228+ }
229+ u [i ] |= uint32 (reverseHexTable [s [j ]]) << 8
230+ j ++
231+
232+ if reverseHexTable [s [j ]] == 255 {
233+ return errInvalidUUID
234+ }
235+ u [i ] |= uint32 (reverseHexTable [s [j ]]) << 4
236+ j ++
237+
238+ if reverseHexTable [s [j ]] == 255 {
239+ return errInvalidUUID
141240 }
142- uuid [ 3 - uuidIndex / 8 ] |= uint32 (nibble ) << ( 4 * ( 7 - uuidIndex % 8 ) )
143- uuidIndex ++
241+ u [ i ] |= uint32 (reverseHexTable [ s [ j ]] )
242+ j ++
144243 }
145- if uuidIndex != 32 {
146- // The UUID doesn't have exactly 32 nibbles. Perhaps a 16-bit or 32-bit
147- // UUID?
148- err = errInvalidUUID
244+
245+ return nil
246+ }
247+
248+ // Using the reverseHexTable rebuild the UUID from the string s represented in bytes
249+ // This implementation is the inverse of MarshalText and reaches performance pairity
250+ func (u * UUID ) unmarshalText32 (s []byte ) error {
251+ u [0 ] = 0x5F9B34FB
252+ u [1 ] = 0x80000080
253+ u [2 ] = 0x00001000
254+
255+ var j uint8 = 0
256+
257+ if reverseHexTable [s [j ]] == 255 {
258+ return errInvalidUUID
149259 }
150- return
260+ u [3 ] |= uint32 (reverseHexTable [s [j ]]) << 28
261+ j ++
262+
263+ if reverseHexTable [s [j ]] == 255 {
264+ return errInvalidUUID
265+ }
266+ u [3 ] |= uint32 (reverseHexTable [s [j ]]) << 24
267+ j ++
268+
269+ if reverseHexTable [s [j ]] == 255 {
270+ return errInvalidUUID
271+ }
272+ u [3 ] |= uint32 (reverseHexTable [s [j ]]) << 20
273+ j ++
274+
275+ if reverseHexTable [s [j ]] == 255 {
276+ return errInvalidUUID
277+ }
278+ u [3 ] |= uint32 (reverseHexTable [s [j ]]) << 16
279+ j ++
280+
281+ if reverseHexTable [s [j ]] == 255 {
282+ return errInvalidUUID
283+ }
284+ u [3 ] |= uint32 (reverseHexTable [s [j ]]) << 12
285+ j ++
286+
287+ if reverseHexTable [s [j ]] == 255 {
288+ return errInvalidUUID
289+ }
290+ u [3 ] |= uint32 (reverseHexTable [s [j ]]) << 8
291+ j ++
292+
293+ if reverseHexTable [s [j ]] == 255 {
294+ return errInvalidUUID
295+ }
296+ u [3 ] |= uint32 (reverseHexTable [s [j ]]) << 4
297+ j ++
298+
299+ if reverseHexTable [s [j ]] == 255 {
300+ return errInvalidUUID
301+ }
302+ u [3 ] |= uint32 (reverseHexTable [s [j ]])
303+ j ++
304+
305+ return nil
306+ }
307+
308+ // Using the reverseHexTable rebuild the UUID from the string s represented in bytes
309+ // This implementation is the inverse of MarshalText and reaches performance pairity
310+ func (u * UUID ) unmarshalText16 (s []byte ) error {
311+ u [0 ] = 0x5F9B34FB
312+ u [1 ] = 0x80000080
313+ u [2 ] = 0x00001000
314+
315+ var j uint8 = 0
316+ if reverseHexTable [s [j ]] == 255 {
317+ return errInvalidUUID
318+ }
319+ u [3 ] |= uint32 (reverseHexTable [s [j ]]) << 12
320+ j ++
321+
322+ if reverseHexTable [s [j ]] == 255 {
323+ return errInvalidUUID
324+ }
325+ u [3 ] |= uint32 (reverseHexTable [s [j ]]) << 8
326+ j ++
327+
328+ if reverseHexTable [s [j ]] == 255 {
329+ return errInvalidUUID
330+ }
331+ u [3 ] |= uint32 (reverseHexTable [s [j ]]) << 4
332+ j ++
333+
334+ if reverseHexTable [s [j ]] == 255 {
335+ return errInvalidUUID
336+ }
337+ u [3 ] |= uint32 (reverseHexTable [s [j ]])
338+ j ++
339+
340+ return nil
341+ }
342+
343+ // This table is structured such that an ascii byte can be directly used as the array key
344+ // to directly look up the decoded uint8 value. This is a similar solution to
345+ // the reverseHexTable found in the hex package. 255 is a sentinel value
346+ // indicating an invalid hex byte.
347+ var reverseHexTable = [256 ]uint8 {
348+ 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
349+ 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
350+ 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
351+ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 255 , 255 , 255 , 255 , 255 , 255 ,
352+ 255 , 10 , 11 , 12 , 13 , 14 , 15 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
353+ 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
354+ 255 , 10 , 11 , 12 , 13 , 14 , 15 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
355+ 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
151356}
152357
153358// String returns a human-readable version of this UUID, such as
154359// 00001234-0000-1000-8000-00805f9b34fb.
155- func (uuid UUID ) String () string {
156- var s strings.Builder
157- s .Grow (36 )
158- raw := uuid .Bytes ()
159- for i := range raw {
360+ func (u UUID ) String () string {
361+ buf , _ := u .AppendText (make ([]byte , 0 , 36 ))
362+
363+ // pulled from the guts of string builder
364+ return unsafe .String (unsafe .SliceData (buf ), 36 )
365+ }
366+
367+ const hexDigitLower = "0123456789abcdef"
368+
369+ // AppendText converts and appends the uuid onto the given byte slice
370+ // representing a human-readable version of this UUID, such as
371+ // 00001234-0000-1000-8000-00805f9b34fb.
372+ func (u UUID ) AppendText (buf []byte ) ([]byte , error ) {
373+ for i := 3 ; i >= 0 ; i -- {
160374 // Insert a hyphen at the correct locations.
161- if i == 4 || i == 6 || i == 8 || i == 10 {
162- s .WriteRune ('-' )
375+ // position 4 and 8
376+ if i != 3 && i != 0 {
377+ buf = append (buf , '-' )
163378 }
164379
165- // The character to convert to hex.
166- c := raw [ 15 - i ]
380+ buf = append ( buf , hexDigitLower [ byte ( u [ i ] >> 24 ) >> 4 ])
381+ buf = append ( buf , hexDigitLower [ byte ( u [ i ] >> 24 ) & 0xF ])
167382
168- // First nibble.
169- nibble := c >> 4
170- if nibble <= 9 {
171- s .WriteByte (nibble + '0' )
172- } else {
173- s .WriteByte (nibble + 'a' - 10 )
174- }
383+ buf = append (buf , hexDigitLower [byte (u [i ]>> 16 )>> 4 ])
384+ buf = append (buf , hexDigitLower [byte (u [i ]>> 16 )& 0xF ])
175385
176- // Second nibble.
177- nibble = c & 0x0f
178- if nibble <= 9 {
179- s .WriteByte (nibble + '0' )
180- } else {
181- s .WriteByte (nibble + 'a' - 10 )
386+ // Insert a hyphen at the correct locations.
387+ // position 6 and 10
388+ if i == 2 || i == 1 {
389+ buf = append (buf , '-' )
182390 }
391+
392+ buf = append (buf , hexDigitLower [byte (u [i ]>> 8 )>> 4 ])
393+ buf = append (buf , hexDigitLower [byte (u [i ]>> 8 )& 0xF ])
394+
395+ buf = append (buf , hexDigitLower [byte (u [i ])>> 4 ])
396+ buf = append (buf , hexDigitLower [byte (u [i ])& 0xF ])
397+ }
398+
399+ return buf , nil
400+ }
401+
402+ // MarshalText returns the converted uuid as a bytle slice
403+ // representing a human-readable version, such as
404+ // 00001234-0000-1000-8000-00805f9b34fb.
405+ func (u UUID ) MarshalText () ([]byte , error ) {
406+ return u .AppendText (make ([]byte , 0 , 36 ))
407+ }
408+
409+ var ErrInvalidBinaryUUID = errors .New ("bluetooth: failed to unmarshal the given binary UUID" )
410+
411+ // UnmarshalBinary copies the given uuid bytes onto itself
412+ func (u * UUID ) UnmarshalBinary (uuid []byte ) error {
413+ if len (uuid ) != 16 {
414+ return ErrInvalidBinaryUUID
183415 }
184416
185- return s .String ()
417+ u [0 ] = uint32 (uuid [0 ]) | uint32 (uuid [1 ])<< 8 | uint32 (uuid [2 ])<< 16 | uint32 (uuid [3 ])<< 24
418+ u [1 ] = uint32 (uuid [4 ]) | uint32 (uuid [5 ])<< 8 | uint32 (uuid [6 ])<< 16 | uint32 (uuid [7 ])<< 24
419+ u [2 ] = uint32 (uuid [8 ]) | uint32 (uuid [9 ])<< 8 | uint32 (uuid [10 ])<< 16 | uint32 (uuid [11 ])<< 24
420+ u [3 ] = uint32 (uuid [12 ]) | uint32 (uuid [13 ])<< 8 | uint32 (uuid [14 ])<< 16 | uint32 (uuid [15 ])<< 24
421+ return nil
186422}
0 commit comments