@@ -5,7 +5,7 @@ package bluetooth
5
5
6
6
import (
7
7
"errors"
8
- "strings "
8
+ "unsafe "
9
9
)
10
10
11
11
// UUID is a single UUID as used in the Bluetooth stack. It is represented as a
@@ -112,75 +112,307 @@ func (uuid UUID) Bytes() [16]byte {
112
112
return buf
113
113
}
114
114
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().
120
152
func 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 pairity
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
126
211
}
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
137
222
}
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
141
240
}
142
- uuid [ 3 - uuidIndex / 8 ] |= uint32 (nibble ) << ( 4 * ( 7 - uuidIndex % 8 ) )
143
- uuidIndex ++
241
+ u [ i ] |= uint32 (reverseHexTable [ s [ j ]] )
242
+ j ++
144
243
}
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
149
259
}
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
+ var reverseHexTable = [256 ]uint8 {
344
+ 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
345
+ 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
346
+ 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
347
+ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 255 , 255 , 255 , 255 , 255 , 255 ,
348
+ 255 , 10 , 11 , 12 , 13 , 14 , 15 , 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 , 10 , 11 , 12 , 13 , 14 , 15 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
351
+ 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 ,
151
352
}
152
353
153
354
// String returns a human-readable version of this UUID, such as
154
355
// 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 {
356
+ func (u UUID ) String () string {
357
+ buf , _ := u .AppendText (make ([]byte , 0 , 36 ))
358
+
359
+ // pulled from the guts of string builder
360
+ return unsafe .String (unsafe .SliceData (buf ), 36 )
361
+ }
362
+
363
+ const hexDigitLower = "0123456789abcdef"
364
+
365
+ // AppendText converts and appends the uuid onto the given byte slice
366
+ // representing a human-readable version of this UUID, such as
367
+ // 00001234-0000-1000-8000-00805f9b34fb.
368
+ func (u UUID ) AppendText (buf []byte ) ([]byte , error ) {
369
+ for i := 3 ; i >= 0 ; i -- {
160
370
// Insert a hyphen at the correct locations.
161
- if i == 4 || i == 6 || i == 8 || i == 10 {
162
- s .WriteRune ('-' )
371
+ // position 4 and 8
372
+ if i != 3 && i != 0 {
373
+ buf = append (buf , '-' )
163
374
}
164
375
165
- // The character to convert to hex.
166
- c := raw [ 15 - i ]
376
+ buf = append ( buf , hexDigitLower [ byte ( u [ i ] >> 24 ) >> 4 ])
377
+ buf = append ( buf , hexDigitLower [ byte ( u [ i ] >> 24 ) & 0xF ])
167
378
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
- }
379
+ buf = append (buf , hexDigitLower [byte (u [i ]>> 16 )>> 4 ])
380
+ buf = append (buf , hexDigitLower [byte (u [i ]>> 16 )& 0xF ])
175
381
176
- // Second nibble.
177
- nibble = c & 0x0f
178
- if nibble <= 9 {
179
- s .WriteByte (nibble + '0' )
180
- } else {
181
- s .WriteByte (nibble + 'a' - 10 )
382
+ // Insert a hyphen at the correct locations.
383
+ // position 6 and 10
384
+ if i == 2 || i == 1 {
385
+ buf = append (buf , '-' )
182
386
}
387
+
388
+ buf = append (buf , hexDigitLower [byte (u [i ]>> 8 )>> 4 ])
389
+ buf = append (buf , hexDigitLower [byte (u [i ]>> 8 )& 0xF ])
390
+
391
+ buf = append (buf , hexDigitLower [byte (u [i ])>> 4 ])
392
+ buf = append (buf , hexDigitLower [byte (u [i ])& 0xF ])
393
+ }
394
+
395
+ return buf , nil
396
+ }
397
+
398
+ // MarshalText returns the converted uuid as a bytle slice
399
+ // representing a human-readable version, such as
400
+ // 00001234-0000-1000-8000-00805f9b34fb.
401
+ func (u UUID ) MarshalText () ([]byte , error ) {
402
+ return u .AppendText (make ([]byte , 0 , 36 ))
403
+ }
404
+
405
+ var ErrInvalidBinaryUUID = errors .New ("bluetooth: failed to unmarshal the given binary UUID" )
406
+
407
+ // UnmarshalBinary copies the given uuid bytes onto itself
408
+ func (u * UUID ) UnmarshalBinary (uuid []byte ) error {
409
+ if len (uuid ) != 16 {
410
+ return ErrInvalidBinaryUUID
183
411
}
184
412
185
- return s .String ()
413
+ u [0 ] = uint32 (uuid [0 ]) | uint32 (uuid [1 ])<< 8 | uint32 (uuid [2 ])<< 16 | uint32 (uuid [3 ])<< 24
414
+ u [1 ] = uint32 (uuid [4 ]) | uint32 (uuid [5 ])<< 8 | uint32 (uuid [6 ])<< 16 | uint32 (uuid [7 ])<< 24
415
+ u [2 ] = uint32 (uuid [8 ]) | uint32 (uuid [9 ])<< 8 | uint32 (uuid [10 ])<< 16 | uint32 (uuid [11 ])<< 24
416
+ u [3 ] = uint32 (uuid [12 ]) | uint32 (uuid [13 ])<< 8 | uint32 (uuid [14 ])<< 16 | uint32 (uuid [15 ])<< 24
417
+ return nil
186
418
}
0 commit comments