Codebase list golang-github-gofrs-uuid / 1225d86
Import upstream version 4.1.0 Debian Janitor 2 years ago
8 changed file(s) with 1061 addition(s) and 136 deletion(s). Raw diff Collapse all Expand all
0 name: Go
1
2 on:
3 push:
4 branches: [ master ]
5 pull_request:
6 branches: [ master ]
7
8 jobs:
9
10 build:
11 name: Build + Test Stable
12 runs-on: ubuntu-latest
13 env:
14 GO111MODULE: auto
15 steps:
16
17 - name: Build
18 uses: actions/setup-go@v2
19 with:
20 go-version: '1.17.x'
21
22 - name: Check out code into the Go module directory
23 uses: actions/checkout@v2
24
25 - name: Get Coverage
26 run: go get golang.org/x/tools/cmd/cover
27
28 - name: Build
29 run: go build -v ./...
30
31 - name: Test
32 run: go test ./... -race -coverprofile=coverage.txt -covermode=atomic
33
34 - name: Coverage
35 uses: codecov/codecov-action@v2
36
37 build-legacy:
38 name: Build + Test Previous Stable
39 runs-on: ubuntu-latest
40 env:
41 GO111MODULE: auto
42 steps:
43
44 - name: Build
45 uses: actions/setup-go@v2
46 with:
47 go-version: '1.16.x'
48
49 - name: Check out code into the Go module directory
50 uses: actions/checkout@v2
51
52 - name: Build
53 run: go build -v ./...
54
55 - name: Test
56 run: go test ./...
+0
-23
.travis.yml less more
0 language: go
1 sudo: false
2 go:
3 - 1.7.x
4 - 1.8.x
5 - 1.9.x
6 - 1.10.x
7 - 1.11.x
8 - tip
9 matrix:
10 allow_failures:
11 - go: tip
12 fast_finish: true
13 env:
14 - GO111MODULE=on
15 before_install:
16 - go get golang.org/x/tools/cmd/cover
17 script:
18 - go test ./... -race -coverprofile=coverage.txt -covermode=atomic
19 after_success:
20 - bash <(curl -s https://codecov.io/bash)
21 notifications:
22 email: false
1111
1212 This package supports the following UUID versions:
1313 * Version 1, based on timestamp and MAC address (RFC-4122)
14 * Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1)
1514 * Version 3, based on MD5 hashing of a named value (RFC-4122)
1615 * Version 4, based on random numbers (RFC-4122)
1716 * Version 5, based on SHA-1 hashing of a named value (RFC-4122)
106105
107106 * [RFC-4122](https://tools.ietf.org/html/rfc4122)
108107 * [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01)
108 * [New UUID Formats RFC Draft (Peabody) Rev 02](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-02)
113113 case 41, 45:
114114 return u.decodeURN(text)
115115 default:
116 return fmt.Errorf("uuid: incorrect UUID length: %s", text)
116 return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(text), text)
117117 }
118118 }
119119
121121 // "6ba7b810-9dad-11d1-80b4-00c04fd430c8".
122122 func (u *UUID) decodeCanonical(t []byte) error {
123123 if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' {
124 return fmt.Errorf("uuid: incorrect UUID format %s", t)
124 return fmt.Errorf("uuid: incorrect UUID format in string %q", t)
125125 }
126126
127127 src := t
159159 l := len(t)
160160
161161 if t[0] != '{' || t[l-1] != '}' {
162 return fmt.Errorf("uuid: incorrect UUID format %s", t)
162 return fmt.Errorf("uuid: incorrect UUID format in string %q", t)
163163 }
164164
165165 return u.decodePlain(t[1 : l-1])
174174 urnUUIDPrefix := t[:9]
175175
176176 if !bytes.Equal(urnUUIDPrefix, urnPrefix) {
177 return fmt.Errorf("uuid: incorrect UUID format: %s", t)
177 return fmt.Errorf("uuid: incorrect UUID format in string %q", t)
178178 }
179179
180180 return u.decodePlain(t[9:total])
190190 case 36:
191191 return u.decodeCanonical(t)
192192 default:
193 return fmt.Errorf("uuid: incorrect UUID length: %s", t)
193 return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(t), t)
194194 }
195195 }
196196
2525 "crypto/rand"
2626 "crypto/sha1"
2727 "encoding/binary"
28 "errors"
2829 "fmt"
2930 "hash"
3031 "io"
3132 "net"
32 "os"
3333 "sync"
3434 "time"
3535 )
4646 // DefaultGenerator is the default UUID Generator used by this package.
4747 var DefaultGenerator Generator = NewGen()
4848
49 var (
50 posixUID = uint32(os.Getuid())
51 posixGID = uint32(os.Getgid())
52 )
53
5449 // NewV1 returns a UUID based on the current timestamp and MAC address.
5550 func NewV1() (UUID, error) {
5651 return DefaultGenerator.NewV1()
5752 }
5853
59 // NewV2 returns a DCE Security UUID based on the POSIX UID/GID.
60 func NewV2(domain byte) (UUID, error) {
61 return DefaultGenerator.NewV2(domain)
62 }
63
6454 // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.
6555 func NewV3(ns UUID, name string) UUID {
6656 return DefaultGenerator.NewV3(ns, name)
7666 return DefaultGenerator.NewV5(ns, name)
7767 }
7868
69 // NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of
70 // pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit
71 // order being adjusted to allow the UUID to be k-sortable.
72 //
73 // This is implemented based on revision 02 of the Peabody UUID draft, and may
74 // be subject to change pending further revisions. Until the final specification
75 // revision is finished, changes required to implement updates to the spec will
76 // not be considered a breaking change. They will happen as a minor version
77 // releases until the spec is final.
78 func NewV6() (UUID, error) {
79 return DefaultGenerator.NewV6()
80 }
81
82 // NewV7 returns a k-sortable UUID based on the current UNIX epoch, with the
83 // ability to configure the timestamp's precision from millisecond all the way
84 // to nanosecond. The additional precision is supported by reducing the amount
85 // of pseudorandom data that makes up the rest of the UUID.
86 //
87 // If an unknown Precision argument is passed to this method it will panic. As
88 // such it's strongly encouraged to use the package-provided constants for this
89 // value.
90 //
91 // This is implemented based on revision 02 of the Peabody UUID draft, and may
92 // be subject to change pending further revisions. Until the final specification
93 // revision is finished, changes required to implement updates to the spec will
94 // not be considered a breaking change. They will happen as a minor version
95 // releases until the spec is final.
96 func NewV7(p Precision) (UUID, error) {
97 return DefaultGenerator.NewV7(p)
98 }
99
79100 // Generator provides an interface for generating UUIDs.
80101 type Generator interface {
81102 NewV1() (UUID, error)
82 NewV2(domain byte) (UUID, error)
83103 NewV3(ns UUID, name string) UUID
84104 NewV4() (UUID, error)
85105 NewV5(ns UUID, name string) UUID
106 NewV6() (UUID, error)
107 NewV7(Precision) (UUID, error)
86108 }
87109
88110 // Gen is a reference UUID generator based on the specifications laid out in
108130 lastTime uint64
109131 clockSequence uint16
110132 hardwareAddr [6]byte
133
134 v7LastTime uint64
135 v7LastSubsec uint64
136 v7ClockSequence uint16
111137 }
112138
113139 // interface check -- build will fail if *Gen doesn't satisfy Generator
163189 return u, nil
164190 }
165191
166 // NewV2 returns a DCE Security UUID based on the POSIX UID/GID.
167 func (g *Gen) NewV2(domain byte) (UUID, error) {
168 u, err := g.NewV1()
169 if err != nil {
170 return Nil, err
171 }
172
173 switch domain {
174 case DomainPerson:
175 binary.BigEndian.PutUint32(u[:], posixUID)
176 case DomainGroup:
177 binary.BigEndian.PutUint32(u[:], posixGID)
178 }
179
180 u[9] = domain
181
182 u.SetVersion(V2)
183 u.SetVariant(VariantRFC4122)
184
185 return u, nil
186 }
187
188192 // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.
189193 func (g *Gen) NewV3(ns UUID, name string) UUID {
190194 u := newFromHash(md5.New(), ns, name)
215219 return u
216220 }
217221
218 // Returns the epoch and clock sequence.
222 // NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of
223 // pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit
224 // order being adjusted to allow the UUID to be k-sortable.
225 //
226 // This is implemented based on revision 02 of the Peabody UUID draft, and may
227 // be subject to change pending further revisions. Until the final specification
228 // revision is finished, changes required to implement updates to the spec will
229 // not be considered a breaking change. They will happen as a minor version
230 // releases until the spec is final.
231 func (g *Gen) NewV6() (UUID, error) {
232 var u UUID
233
234 if _, err := io.ReadFull(g.rand, u[10:]); err != nil {
235 return Nil, err
236 }
237
238 timeNow, clockSeq, err := g.getClockSequence()
239 if err != nil {
240 return Nil, err
241 }
242
243 binary.BigEndian.PutUint32(u[0:], uint32(timeNow>>28)) // set time_high
244 binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>12)) // set time_mid
245 binary.BigEndian.PutUint16(u[6:], uint16(timeNow&0xfff)) // set time_low (minus four version bits)
246 binary.BigEndian.PutUint16(u[8:], clockSeq&0x3fff) // set clk_seq_hi_res (minus two variant bits)
247
248 u.SetVersion(V6)
249 u.SetVariant(VariantRFC4122)
250
251 return u, nil
252 }
253
254 // getClockSequence returns the epoch and clock sequence for V1 and V6 UUIDs.
219255 func (g *Gen) getClockSequence() (uint64, uint16, error) {
220256 var err error
221257 g.clockSequenceOnce.Do(func() {
243279 return timeNow, g.clockSequence, nil
244280 }
245281
282 // Precision is used to configure the V7 generator, to specify how precise the
283 // timestamp within the UUID should be.
284 type Precision byte
285
286 const (
287 NanosecondPrecision Precision = iota
288 MicrosecondPrecision
289 MillisecondPrecision
290 )
291
292 func (p Precision) String() string {
293 switch p {
294 case NanosecondPrecision:
295 return "nanosecond"
296
297 case MicrosecondPrecision:
298 return "microsecond"
299
300 case MillisecondPrecision:
301 return "millisecond"
302
303 default:
304 return "unknown"
305 }
306 }
307
308 // Duration returns the time.Duration for a specific precision. If the Precision
309 // value is not known, this returns 0.
310 func (p Precision) Duration() time.Duration {
311 switch p {
312 case NanosecondPrecision:
313 return time.Nanosecond
314
315 case MicrosecondPrecision:
316 return time.Microsecond
317
318 case MillisecondPrecision:
319 return time.Millisecond
320
321 default:
322 return 0
323 }
324 }
325
326 // NewV7 returns a k-sortable UUID based on the current UNIX epoch, with the
327 // ability to configure the timestamp's precision from millisecond all the way
328 // to nanosecond. The additional precision is supported by reducing the amount
329 // of pseudorandom data that makes up the rest of the UUID.
330 //
331 // If an unknown Precision argument is passed to this method it will panic. As
332 // such it's strongly encouraged to use the package-provided constants for this
333 // value.
334 //
335 // This is implemented based on revision 02 of the Peabody UUID draft, and may
336 // be subject to change pending further revisions. Until the final specification
337 // revision is finished, changes required to implement updates to the spec will
338 // not be considered a breaking change. They will happen as a minor version
339 // releases until the spec is final.
340 func (g *Gen) NewV7(p Precision) (UUID, error) {
341 var u UUID
342 var err error
343
344 switch p {
345 case NanosecondPrecision:
346 u, err = g.newV7Nano()
347
348 case MicrosecondPrecision:
349 u, err = g.newV7Micro()
350
351 case MillisecondPrecision:
352 u, err = g.newV7Milli()
353
354 default:
355 panic(fmt.Sprintf("unknown precision value %d", p))
356 }
357
358 if err != nil {
359 return Nil, err
360 }
361
362 u.SetVersion(V7)
363 u.SetVariant(VariantRFC4122)
364
365 return u, nil
366 }
367
368 func (g *Gen) newV7Milli() (UUID, error) {
369 var u UUID
370
371 if _, err := io.ReadFull(g.rand, u[8:]); err != nil {
372 return Nil, err
373 }
374
375 sec, nano, seq, err := g.getV7ClockSequence(MillisecondPrecision)
376 if err != nil {
377 return Nil, err
378 }
379
380 msec := (nano / 1000000) & 0xfff
381
382 d := (sec << 28) // set unixts field
383 d |= (msec << 16) // set msec field
384 d |= (uint64(seq) & 0xfff) // set seq field
385
386 binary.BigEndian.PutUint64(u[:], d)
387
388 return u, nil
389 }
390
391 func (g *Gen) newV7Micro() (UUID, error) {
392 var u UUID
393
394 if _, err := io.ReadFull(g.rand, u[10:]); err != nil {
395 return Nil, err
396 }
397
398 sec, nano, seq, err := g.getV7ClockSequence(MicrosecondPrecision)
399 if err != nil {
400 return Nil, err
401 }
402
403 usec := nano / 1000
404 usech := (usec << 4) & 0xfff0000
405 usecl := usec & 0xfff
406
407 d := (sec << 28) // set unixts field
408 d |= usech | usecl // set usec fields
409
410 binary.BigEndian.PutUint64(u[:], d)
411 binary.BigEndian.PutUint16(u[8:], seq)
412
413 return u, nil
414 }
415
416 func (g *Gen) newV7Nano() (UUID, error) {
417 var u UUID
418
419 if _, err := io.ReadFull(g.rand, u[11:]); err != nil {
420 return Nil, err
421 }
422
423 sec, nano, seq, err := g.getV7ClockSequence(NanosecondPrecision)
424 if err != nil {
425 return Nil, err
426 }
427
428 nano &= 0x3fffffffff
429 nanoh := nano >> 26
430 nanom := (nano >> 14) & 0xfff
431 nanol := uint16(nano & 0x3fff)
432
433 d := (sec << 28) // set unixts field
434 d |= (nanoh << 16) | nanom // set nsec high and med fields
435
436 binary.BigEndian.PutUint64(u[:], d)
437 binary.BigEndian.PutUint16(u[8:], nanol) // set nsec low field
438
439 u[10] = byte(seq) // set seq field
440
441 return u, nil
442 }
443
444 const (
445 maxSeq14 = (1 << 14) - 1
446 maxSeq12 = (1 << 12) - 1
447 maxSeq8 = (1 << 8) - 1
448 )
449
450 // getV7ClockSequence returns the unix epoch, nanoseconds of current second, and
451 // the sequence for V7 UUIDs.
452 func (g *Gen) getV7ClockSequence(p Precision) (epoch uint64, nano uint64, seq uint16, err error) {
453 g.storageMutex.Lock()
454 defer g.storageMutex.Unlock()
455
456 tn := g.epochFunc()
457 unix := uint64(tn.Unix())
458 nsec := uint64(tn.Nanosecond())
459
460 // V7 UUIDs have more precise requirements around how the clock sequence
461 // value is generated and used. Specifically they require that the sequence
462 // be zero, unless we've already generated a UUID within this unit of time
463 // (millisecond, microsecond, or nanosecond) at which point you should
464 // increment the sequence. Likewise if time has warped backwards for some reason (NTP
465 // adjustment?), we also increment the clock sequence to reduce the risk of a
466 // collision.
467 switch {
468 case unix < g.v7LastTime:
469 g.v7ClockSequence++
470
471 case unix > g.v7LastTime:
472 g.v7ClockSequence = 0
473
474 case unix == g.v7LastTime:
475 switch p {
476 case NanosecondPrecision:
477 if nsec <= g.v7LastSubsec {
478 if g.v7ClockSequence >= maxSeq8 {
479 return 0, 0, 0, errors.New("generating nanosecond precision UUIDv7s too fast: internal clock sequence would roll over")
480 }
481
482 g.v7ClockSequence++
483 } else {
484 g.v7ClockSequence = 0
485 }
486
487 case MicrosecondPrecision:
488 if nsec/1000 <= g.v7LastSubsec/1000 {
489 if g.v7ClockSequence >= maxSeq14 {
490 return 0, 0, 0, errors.New("generating microsecond precision UUIDv7s too fast: internal clock sequence would roll over")
491 }
492
493 g.v7ClockSequence++
494 } else {
495 g.v7ClockSequence = 0
496 }
497
498 case MillisecondPrecision:
499 if nsec/1000000 <= g.v7LastSubsec/1000000 {
500 if g.v7ClockSequence >= maxSeq12 {
501 return 0, 0, 0, errors.New("generating millisecond precision UUIDv7s too fast: internal clock sequence would roll over")
502 }
503
504 g.v7ClockSequence++
505 } else {
506 g.v7ClockSequence = 0
507 }
508
509 default:
510 panic(fmt.Sprintf("unknown precision value %d", p))
511 }
512 }
513
514 g.v7LastTime = unix
515 g.v7LastSubsec = nsec
516
517 return unix, nsec, g.v7ClockSequence, nil
518 }
519
246520 // Returns the hardware address.
247521 func (g *Gen) getHardwareAddr() ([]byte, error) {
248522 var err error
2323 import (
2424 "bytes"
2525 "crypto/rand"
26 "encoding/binary"
2627 "fmt"
2728 "net"
29 "strings"
2830 "testing"
2931 "time"
3032 )
3133
3234 func TestGenerator(t *testing.T) {
3335 t.Run("NewV1", testNewV1)
34 t.Run("NewV2", testNewV2)
3536 t.Run("NewV3", testNewV3)
3637 t.Run("NewV4", testNewV4)
3738 t.Run("NewV5", testNewV5)
39 t.Run("NewV6", testNewV6)
40 t.Run("NewV7", testNewV7)
3841 }
3942
4043 func testNewV1(t *testing.T) {
167170 u, err := g.NewV1()
168171 if err == nil {
169172 t.Errorf("did not error on faulty reader and missing network, got %v", u)
170 }
171 }
172
173 func testNewV2(t *testing.T) {
174 t.Run("Basic", testNewV2Basic)
175 t.Run("DifferentAcrossCalls", testNewV2DifferentAcrossCalls)
176 t.Run("FaultyRand", testNewV2FaultyRand)
177 }
178
179 func testNewV2Basic(t *testing.T) {
180 domains := []byte{
181 DomainPerson,
182 DomainGroup,
183 DomainOrg,
184 }
185 for _, domain := range domains {
186 u, err := NewV2(domain)
187 if err != nil {
188 t.Errorf("NewV2(%d): %v", domain, err)
189 }
190 if got, want := u.Version(), V2; got != want {
191 t.Errorf("NewV2(%d) generated UUID with version %d, want %d", domain, got, want)
192 }
193 if got, want := u.Variant(), VariantRFC4122; got != want {
194 t.Errorf("NewV2(%d) generated UUID with variant %d, want %d", domain, got, want)
195 }
196 }
197 }
198
199 func testNewV2DifferentAcrossCalls(t *testing.T) {
200 u1, err := NewV2(DomainOrg)
201 if err != nil {
202 t.Fatal(err)
203 }
204 u2, err := NewV2(DomainOrg)
205 if err != nil {
206 t.Fatal(err)
207 }
208 if u1 == u2 {
209 t.Errorf("generated identical UUIDs across calls: %v", u1)
210 }
211 }
212
213 func testNewV2FaultyRand(t *testing.T) {
214 g := &Gen{
215 epochFunc: time.Now,
216 hwAddrFunc: defaultHWAddrFunc,
217 rand: &faultyReader{
218 readToFail: 0, // fail immediately
219 },
220 }
221 u, err := g.NewV2(DomainPerson)
222 if err == nil {
223 t.Fatalf("got %v, want error", u)
224 }
225 if u != Nil {
226 t.Fatalf("got %v on error, want Nil", u)
227173 }
228174 }
229175
375321 }
376322 }
377323
324 func testNewV6(t *testing.T) {
325 t.Run("Basic", testNewV6Basic)
326 t.Run("DifferentAcrossCalls", testNewV6DifferentAcrossCalls)
327 t.Run("StaleEpoch", testNewV6StaleEpoch)
328 t.Run("FaultyRand", testNewV6FaultyRand)
329 t.Run("ShortRandomRead", testNewV6ShortRandomRead)
330 t.Run("KSortable", testNewV6KSortable)
331 }
332
333 func testNewV6Basic(t *testing.T) {
334 u, err := NewV6()
335 if err != nil {
336 t.Fatal(err)
337 }
338 if got, want := u.Version(), V6; got != want {
339 t.Errorf("generated UUID with version %d, want %d", got, want)
340 }
341 if got, want := u.Variant(), VariantRFC4122; got != want {
342 t.Errorf("generated UUID with variant %d, want %d", got, want)
343 }
344 }
345
346 func testNewV6DifferentAcrossCalls(t *testing.T) {
347 u1, err := NewV6()
348 if err != nil {
349 t.Fatal(err)
350 }
351 u2, err := NewV6()
352 if err != nil {
353 t.Fatal(err)
354 }
355 if u1 == u2 {
356 t.Errorf("generated identical UUIDs across calls: %v", u1)
357 }
358 }
359
360 func testNewV6StaleEpoch(t *testing.T) {
361 g := &Gen{
362 epochFunc: func() time.Time {
363 return time.Unix(0, 0)
364 },
365 hwAddrFunc: defaultHWAddrFunc,
366 rand: rand.Reader,
367 }
368 u1, err := g.NewV6()
369 if err != nil {
370 t.Fatal(err)
371 }
372 u2, err := g.NewV6()
373 if err != nil {
374 t.Fatal(err)
375 }
376 if u1 == u2 {
377 t.Errorf("generated identical UUIDs across calls: %v", u1)
378 }
379 }
380
381 func testNewV6FaultyRand(t *testing.T) {
382 t.Run("randomData", func(t *testing.T) {
383 g := &Gen{
384 epochFunc: time.Now,
385 hwAddrFunc: defaultHWAddrFunc,
386 rand: &faultyReader{
387 readToFail: 0, // fail immediately
388 },
389 }
390 u, err := g.NewV6()
391 if err == nil {
392 t.Fatalf("got %v, want error", u)
393 }
394 if u != Nil {
395 t.Fatalf("got %v on error, want Nil", u)
396 }
397 })
398
399 t.Run("clockSequence", func(t *testing.T) {
400 g := &Gen{
401 epochFunc: time.Now,
402 hwAddrFunc: defaultHWAddrFunc,
403 rand: &faultyReader{
404 readToFail: 1, // fail immediately
405 },
406 }
407 u, err := g.NewV6()
408 if err == nil {
409 t.Fatalf("got %v, want error", u)
410 }
411 if u != Nil {
412 t.Fatalf("got %v on error, want Nil", u)
413 }
414 })
415 }
416
417 func testNewV6ShortRandomRead(t *testing.T) {
418 g := &Gen{
419 epochFunc: time.Now,
420 rand: bytes.NewReader([]byte{42}),
421 }
422 u, err := g.NewV6()
423 if err == nil {
424 t.Errorf("got %v, nil error", u)
425 }
426 }
427
428 func testNewV6KSortable(t *testing.T) {
429 uuids := make([]UUID, 10)
430 for i := range uuids {
431 u, err := NewV6()
432 testErrCheck(t, "NewV6()", "", err)
433
434 uuids[i] = u
435
436 time.Sleep(time.Microsecond)
437 }
438
439 for i := 1; i < len(uuids); i++ {
440 p, n := uuids[i-1], uuids[i]
441 isLess := p.String() < n.String()
442 if !isLess {
443 t.Errorf("uuids[%d] (%s) not less than uuids[%d] (%s)", i-1, p, i, n)
444 }
445 }
446 }
447
448 func testNewV7(t *testing.T) {
449 t.Run("InvalidPrecision", testNewV7InvalidPrecision)
450
451 for _, p := range []Precision{NanosecondPrecision, MicrosecondPrecision, MillisecondPrecision} {
452 t.Run(p.String(), func(t *testing.T) {
453 t.Run("Basic", makeTestNewV7Basic(p))
454 t.Run("Basic10000000", makeTestNewV7Basic10000000(p))
455 t.Run("DifferentAcrossCalls", makeTestNewV7DifferentAcrossCalls(p))
456 t.Run("StaleEpoch", makeTestNewV7StaleEpoch(p))
457 t.Run("FaultyRand", makeTestNewV7FaultyRand(p))
458 t.Run("ShortRandomRead", makeTestNewV7ShortRandomRead(p))
459 t.Run("ClockSequenceBehaviors", makeTestNewV7ClockSequenceBehaviors(p))
460 t.Run("KSortable", makeTestNewV7KSortable(p))
461 })
462 }
463
464 t.Run("ClockSequence", testNewV7ClockSequence)
465 }
466
467 func testNewV7InvalidPrecision(t *testing.T) {
468 t.Run("NewV7", func(t *testing.T) {
469 defer func() {
470 if r := recover(); r == nil {
471 t.Fatal("call did not panic")
472 }
473 }()
474
475 NewV7(255)
476 })
477
478 t.Run("getV7ClockSequence", func(t *testing.T) {
479 defer func() {
480 if r := recover(); r == nil {
481 t.Fatal("expected panic did not occur")
482 }
483 }()
484
485 g := NewGen()
486 g.epochFunc = func() time.Time {
487 return time.Unix(0, 0)
488 }
489
490 g.getV7ClockSequence(255)
491 })
492 }
493
494 func makeTestNewV7Basic(p Precision) func(t *testing.T) {
495 return func(t *testing.T) {
496 u, err := NewV7(p)
497 if err != nil {
498 t.Fatal(err)
499 }
500 if got, want := u.Version(), V7; got != want {
501 t.Errorf("got version %d, want %d", got, want)
502 }
503 if got, want := u.Variant(), VariantRFC4122; got != want {
504 t.Errorf("got variant %d, want %d", got, want)
505 }
506 }
507 }
508
509 func makeTestNewV7Basic10000000(p Precision) func(t *testing.T) {
510 return func(t *testing.T) {
511 if testing.Short() {
512 t.Skip("skipping test in short mode.")
513 }
514
515 if p == MillisecondPrecision {
516 t.Skip("skipping test, see: https://github.com/uuid6/uuid6-ietf-draft/issues/40")
517 }
518
519 g := NewGen()
520
521 for i := 0; i < 10000000; i++ {
522 u, err := g.NewV7(p)
523 if err != nil {
524 t.Fatal(err)
525 }
526 if got, want := u.Version(), V7; got != want {
527 t.Errorf("got version %d, want %d", got, want)
528 }
529 if got, want := u.Variant(), VariantRFC4122; got != want {
530 t.Errorf("got variant %d, want %d", got, want)
531 }
532 }
533 }
534 }
535
536 func makeTestNewV7DifferentAcrossCalls(p Precision) func(t *testing.T) {
537 return func(t *testing.T) {
538 g := NewGen()
539
540 u1, err := g.NewV7(p)
541 if err != nil {
542 t.Fatal(err)
543 }
544 u2, err := g.NewV7(p)
545 if err != nil {
546 t.Fatal(err)
547 }
548 if u1 == u2 {
549 t.Errorf("generated identical UUIDs across calls: %v", u1)
550 }
551 }
552 }
553
554 func makeTestNewV7StaleEpoch(p Precision) func(t *testing.T) {
555 return func(t *testing.T) {
556 g := &Gen{
557 epochFunc: func() time.Time {
558 return time.Unix(0, 0)
559 },
560 rand: rand.Reader,
561 }
562 u1, err := g.NewV7(p)
563 if err != nil {
564 t.Fatal(err)
565 }
566 u2, err := g.NewV7(p)
567 if err != nil {
568 t.Fatal(err)
569 }
570 if u1 == u2 {
571 t.Errorf("generated identical UUIDs across calls: %v", u1)
572 }
573 }
574 }
575
576 func makeTestNewV7FaultyRand(p Precision) func(t *testing.T) {
577 return func(t *testing.T) {
578 g := &Gen{
579 epochFunc: time.Now,
580 rand: &faultyReader{
581 readToFail: 0, // fail immediately
582 },
583 }
584 u, err := g.NewV7(p)
585 if err == nil {
586 t.Errorf("got %v, nil error", u)
587 }
588 }
589 }
590
591 func makeTestNewV7ShortRandomRead(p Precision) func(t *testing.T) {
592 return func(t *testing.T) {
593 g := &Gen{
594 epochFunc: time.Now,
595 rand: bytes.NewReader([]byte{42}),
596 }
597 u, err := g.NewV7(p)
598 if err == nil {
599 t.Errorf("got %v, nil error", u)
600 }
601 }
602 }
603
604 func makeTestNewV7KSortable(p Precision) func(t *testing.T) {
605 return func(t *testing.T) {
606 uuids := make([]UUID, 10)
607 for i := range uuids {
608 u, err := NewV7(p)
609 testErrCheck(t, "NewV6()", "", err)
610
611 uuids[i] = u
612
613 time.Sleep(p.Duration())
614 }
615
616 for i := 1; i < len(uuids); i++ {
617 p, n := uuids[i-1], uuids[i]
618 isLess := p.String() < n.String()
619 if !isLess {
620 t.Errorf("uuids[%d] (%s) not less than uuids[%d] (%s)", i-1, p, i, n)
621 }
622 }
623 }
624 }
625
626 // to get 100% code coverage we need to do some glass box testing
627 func makeTestNewV7ClockSequenceBehaviors(p Precision) func(t *testing.T) {
628 return func(t *testing.T) {
629 t.Run("TimeWarp", func(t *testing.T) {
630 g := NewGen()
631 tn := time.Now()
632 unix := uint64(tn.Unix()) + 100
633 nsec := uint64(tn.Nanosecond())
634
635 g.v7LastTime = unix
636 g.v7LastSubsec = nsec
637
638 _, err := g.NewV7(p)
639 testErrCheck(t, "g.NewV7()", "", err)
640
641 if g.v7ClockSequence != 1 {
642 t.Fatalf("g.v7ClockSequence = %d, want 1", g.v7ClockSequence)
643 }
644 })
645
646 t.Run("NominalTime", func(t *testing.T) {
647 g := NewGen()
648 g.v7ClockSequence = 100
649
650 tn := time.Now()
651 unix := uint64(tn.Unix()) - 100
652 nsec := uint64(tn.Nanosecond())
653
654 g.v7LastTime = unix
655 g.v7LastSubsec = nsec
656
657 _, err := g.NewV7(p)
658 testErrCheck(t, "g.NewV7()", "", err)
659
660 if g.v7ClockSequence != 0 {
661 t.Fatalf("g.v7ClockSequence = %d, want 0", g.v7ClockSequence)
662 }
663 })
664
665 t.Run("Overflow", func(t *testing.T) {
666 if testing.Short() {
667 t.Skip("skipping test in short mode.")
668 }
669
670 wantErrStr := fmt.Sprintf("generating %s precision UUIDv7s too fast: internal clock sequence would roll over", p.String())
671
672 g := NewGen()
673
674 g.epochFunc = func() time.Time {
675 return time.Unix(0, 0)
676 }
677
678 g.v7ClockSequence = maxSeq14 + 1
679 g.v7LastTime = uint64(g.epochFunc().Unix())
680 g.v7LastSubsec = uint64(g.epochFunc().Nanosecond())
681
682 _, err := g.NewV7(p)
683 testErrCheck(t, "g.NewV7()", wantErrStr, err)
684 })
685 }
686 }
687
688 func testNewV7ClockSequence(t *testing.T) {
689 if testing.Short() {
690 t.Skip("skipping test in short mode.")
691 }
692
693 g := NewGen()
694
695 // hack to try and reduce race conditions based on when the test starts
696 nsec := time.Now().Nanosecond()
697 sleepDur := int(time.Second) - nsec
698 time.Sleep(time.Duration(sleepDur))
699
700 u1, err := g.NewV7(MillisecondPrecision)
701 if err != nil {
702 t.Fatalf("failed to generate V7 UUID #1: %v", err)
703 }
704
705 u2, err := g.NewV7(MillisecondPrecision)
706 if err != nil {
707 t.Fatalf("failed to generate V7 UUID #2: %v", err)
708 }
709
710 time.Sleep(time.Millisecond)
711
712 u3, err := g.NewV7(MillisecondPrecision)
713 if err != nil {
714 t.Fatalf("failed to generate V7 UUID #3: %v", err)
715 }
716
717 time.Sleep(time.Second)
718
719 u4, err := g.NewV7(MillisecondPrecision)
720 if err != nil {
721 t.Fatalf("failed to generate V7 UUID #3: %v", err)
722 }
723
724 s1 := binary.BigEndian.Uint16(u1[6:8]) & 0xfff
725 s2 := binary.BigEndian.Uint16(u2[6:8]) & 0xfff
726 s3 := binary.BigEndian.Uint16(u3[6:8]) & 0xfff
727 s4 := binary.BigEndian.Uint16(u4[6:8]) & 0xfff
728
729 if s1 != 0 {
730 t.Errorf("sequence 1 should be zero, was %d", s1)
731 }
732
733 if s2 != s1+1 {
734 t.Errorf("sequence 2 expected to be one above sequence 1; seq 1: %d, seq 2: %d", s1, s2)
735 }
736
737 if s3 != 0 {
738 t.Errorf("sequence 3 should be zero, was %d", s3)
739 }
740
741 if s4 != 0 {
742 t.Errorf("sequence 4 should be zero, was %d", s4)
743 }
744 }
745
746 func TestPrecision_String(t *testing.T) {
747 tests := []struct {
748 p Precision
749 want string
750 }{
751 {
752 p: NanosecondPrecision,
753 want: "nanosecond",
754 },
755 {
756 p: MillisecondPrecision,
757 want: "millisecond",
758 },
759 {
760 p: MicrosecondPrecision,
761 want: "microsecond",
762 },
763 {
764 p: 0xff,
765 want: "unknown",
766 },
767 }
768
769 for _, tt := range tests {
770 t.Run(tt.want, func(t *testing.T) {
771 if got := tt.p.String(); got != tt.want {
772 t.Errorf("got = %s, want %s", got, tt.want)
773 }
774 })
775 }
776 }
777
778 func TestPrecision_Duration(t *testing.T) {
779 tests := []struct {
780 p Precision
781 want time.Duration
782 }{
783 {
784 p: NanosecondPrecision,
785 want: time.Nanosecond,
786 },
787 {
788 p: MillisecondPrecision,
789 want: time.Millisecond,
790 },
791 {
792 p: MicrosecondPrecision,
793 want: time.Microsecond,
794 },
795 {
796 p: 0xff,
797 want: 0,
798 },
799 }
800
801 for _, tt := range tests {
802 t.Run(tt.p.String(), func(t *testing.T) {
803 if got := tt.p.Duration(); got != tt.want {
804 t.Errorf("got = %s, want %s", got, tt.want)
805 }
806 })
807 }
808 }
809
378810 func BenchmarkGenerator(b *testing.B) {
379811 b.Run("NewV1", func(b *testing.B) {
380812 for i := 0; i < b.N; i++ {
381813 NewV1()
382814 }
383815 })
384 b.Run("NewV2", func(b *testing.B) {
385 for i := 0; i < b.N; i++ {
386 NewV2(DomainOrg)
387 }
388 })
389816 b.Run("NewV3", func(b *testing.B) {
390817 for i := 0; i < b.N; i++ {
391818 NewV3(NamespaceDNS, "www.example.com")
415842 }
416843 return rand.Read(dest)
417844 }
845
846 // testErrCheck looks to see if errContains is a substring of err.Error(). If
847 // not, this calls t.Fatal(). It also calls t.Fatal() if there was an error, but
848 // errContains is empty. Returns true if you should continue running the test,
849 // or false if you should stop the test.
850 func testErrCheck(t *testing.T, name string, errContains string, err error) bool {
851 t.Helper()
852
853 if len(errContains) > 0 {
854 if err == nil {
855 t.Fatalf("%s error = <nil>, should contain %q", name, errContains)
856 return false
857 }
858
859 if errStr := err.Error(); !strings.Contains(errStr, errContains) {
860 t.Fatalf("%s error = %q, should contain %q", name, errStr, errContains)
861 return false
862 }
863
864 return false
865 }
866
867 if err != nil && len(errContains) == 0 {
868 t.Fatalf("%s unexpected error: %v", name, err)
869 return false
870 }
871
872 return true
873 }
1818 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1919 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2020
21 // Package uuid provides implementations of the Universally Unique Identifier (UUID), as specified in RFC-4122 and DCE 1.1.
22 //
23 // RFC-4122[1] provides the specification for versions 1, 3, 4, and 5.
24 //
25 // DCE 1.1[2] provides the specification for version 2.
21 // Package uuid provides implementations of the Universally Unique Identifier
22 // (UUID), as specified in RFC-4122 and the Peabody RFC Draft (revision 02).
23 //
24 // RFC-4122[1] provides the specification for versions 1, 3, 4, and 5. The
25 // Peabody UUID RFC Draft[2] provides the specification for the new k-sortable
26 // UUIDs, versions 6 and 7.
27 //
28 // DCE 1.1[3] provides the specification for version 2, but version 2 support
29 // was removed from this package in v4 due to some concerns with the
30 // specification itself. Reading the spec, it seems that it would result in
31 // generating UUIDs that aren't very unique. In having read the spec it seemed
32 // that our implementation did not meet the spec. It also seems to be at-odds
33 // with RFC 4122, meaning we would need quite a bit of special code to support
34 // it. Lastly, there were no Version 2 implementations that we could find to
35 // ensure we were understanding the specification correctly.
2636 //
2737 // [1] https://tools.ietf.org/html/rfc4122
28 // [2] http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01
38 // [2] https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-02
39 // [3] http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01
2940 package uuid
3041
3142 import (
3243 "encoding/binary"
3344 "encoding/hex"
3445 "fmt"
46 "io"
47 "strings"
3548 "time"
3649 )
3750
4558 const (
4659 _ byte = iota
4760 V1 // Version 1 (date-time and MAC address)
48 V2 // Version 2 (date-time and MAC address, DCE security version)
61 _ // Version 2 (date-time and MAC address, DCE security version) [removed]
4962 V3 // Version 3 (namespace name-based)
5063 V4 // Version 4 (random)
5164 V5 // Version 5 (namespace name-based)
65 V6 // Version 6 (k-sortable timestamp and random data) [peabody draft]
66 V7 // Version 7 (k-sortable timestamp, with configurable precision, and random data) [peabody draft]
67 _ // Version 8 (k-sortable timestamp, meant for custom implementations) [peabody draft] [not implemented]
5268 )
5369
5470 // UUID layout variants.
6783 )
6884
6985 // Timestamp is the count of 100-nanosecond intervals since 00:00:00.00,
70 // 15 October 1582 within a V1 UUID. This type has no meaning for V2-V5
71 // UUIDs since they don't have an embedded timestamp.
86 // 15 October 1582 within a V1 UUID. This type has no meaning for other
87 // UUID versions since they don't have an embedded timestamp.
7288 type Timestamp uint64
7389
7490 const _100nsPerSecond = 10000000
7793 func (t Timestamp) Time() (time.Time, error) {
7894 secs := uint64(t) / _100nsPerSecond
7995 nsecs := 100 * (uint64(t) % _100nsPerSecond)
96
8097 return time.Unix(int64(secs)-(epochStart/_100nsPerSecond), int64(nsecs)), nil
8198 }
8299
87104 err := fmt.Errorf("uuid: %s is version %d, not version 1", u, u.Version())
88105 return 0, err
89106 }
107
90108 low := binary.BigEndian.Uint32(u[0:4])
91109 mid := binary.BigEndian.Uint16(u[4:6])
92110 hi := binary.BigEndian.Uint16(u[6:8]) & 0xfff
111
93112 return Timestamp(uint64(low) + (uint64(mid) << 32) + (uint64(hi) << 48)), nil
113 }
114
115 // TimestampFromV6 returns the Timestamp embedded within a V6 UUID. This
116 // function returns an error if the UUID is any version other than 6.
117 //
118 // This is implemented based on revision 01 of the Peabody UUID draft, and may
119 // be subject to change pending further revisions. Until the final specification
120 // revision is finished, changes required to implement updates to the spec will
121 // not be considered a breaking change. They will happen as a minor version
122 // releases until the spec is final.
123 func TimestampFromV6(u UUID) (Timestamp, error) {
124 if u.Version() != 6 {
125 return 0, fmt.Errorf("uuid: %s is version %d, not version 6", u, u.Version())
126 }
127
128 hi := binary.BigEndian.Uint32(u[0:4])
129 mid := binary.BigEndian.Uint16(u[4:6])
130 low := binary.BigEndian.Uint16(u[6:8]) & 0xfff
131
132 return Timestamp(uint64(low) + (uint64(mid) << 12) + (uint64(hi) << 28)), nil
94133 }
95134
96135 // String parse helpers.
155194 return string(buf)
156195 }
157196
197 // Format implements fmt.Formatter for UUID values.
198 //
199 // The behavior is as follows:
200 // The 'x' and 'X' verbs output only the hex digits of the UUID, using a-f for 'x' and A-F for 'X'.
201 // The 'v', '+v', 's' and 'q' verbs return the canonical RFC-4122 string representation.
202 // The 'S' verb returns the RFC-4122 format, but with capital hex digits.
203 // The '#v' verb returns the "Go syntax" representation, which is a 16 byte array initializer.
204 // All other verbs not handled directly by the fmt package (like '%p') are unsupported and will return
205 // "%!verb(uuid.UUID=value)" as recommended by the fmt package.
206 func (u UUID) Format(f fmt.State, c rune) {
207 switch c {
208 case 'x', 'X':
209 s := hex.EncodeToString(u.Bytes())
210 if c == 'X' {
211 s = strings.Map(toCapitalHexDigits, s)
212 }
213 _, _ = io.WriteString(f, s)
214 case 'v':
215 var s string
216 if f.Flag('#') {
217 s = fmt.Sprintf("%#v", [Size]byte(u))
218 } else {
219 s = u.String()
220 }
221 _, _ = io.WriteString(f, s)
222 case 's', 'S':
223 s := u.String()
224 if c == 'S' {
225 s = strings.Map(toCapitalHexDigits, s)
226 }
227 _, _ = io.WriteString(f, s)
228 case 'q':
229 _, _ = io.WriteString(f, `"`+u.String()+`"`)
230 default:
231 // invalid/unsupported format verb
232 fmt.Fprintf(f, "%%!%c(uuid.UUID=%s)", c, u.String())
233 }
234 }
235
236 func toCapitalHexDigits(ch rune) rune {
237 // convert a-f hex digits to A-F
238 switch ch {
239 case 'a':
240 return 'A'
241 case 'b':
242 return 'B'
243 case 'c':
244 return 'C'
245 case 'd':
246 return 'D'
247 case 'e':
248 return 'E'
249 case 'f':
250 return 'F'
251 default:
252 return ch
253 }
254 }
255
158256 // SetVersion sets the version bits.
159257 func (u *UUID) SetVersion(v byte) {
160258 u[6] = (u[6] & 0x0f) | (v << 4)
3434 t.Run("Variant", testUUIDVariant)
3535 t.Run("SetVersion", testUUIDSetVersion)
3636 t.Run("SetVariant", testUUIDSetVariant)
37 t.Run("Format", testUUIDFormat)
3738 }
3839
3940 func testUUIDBytes(t *testing.T) {
109110 u.SetVariant(want)
110111 if got := u.Variant(); got != want {
111112 t.Errorf("%v.Variant() == %d after SetVariant(%d)", u, got, want)
113 }
114 }
115 }
116
117 func testUUIDFormat(t *testing.T) {
118 val := Must(FromString("12345678-90ab-cdef-1234-567890abcdef"))
119 tests := []struct {
120 u UUID
121 f string
122 want string
123 }{
124 {u: val, f: "%s", want: "12345678-90ab-cdef-1234-567890abcdef"},
125 {u: val, f: "%S", want: "12345678-90AB-CDEF-1234-567890ABCDEF"},
126 {u: val, f: "%q", want: `"12345678-90ab-cdef-1234-567890abcdef"`},
127 {u: val, f: "%x", want: "1234567890abcdef1234567890abcdef"},
128 {u: val, f: "%X", want: "1234567890ABCDEF1234567890ABCDEF"},
129 {u: val, f: "%v", want: "12345678-90ab-cdef-1234-567890abcdef"},
130 {u: val, f: "%+v", want: "12345678-90ab-cdef-1234-567890abcdef"},
131 {u: val, f: "%#v", want: "[16]uint8{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}"},
132 {u: val, f: "%T", want: "uuid.UUID"},
133 {u: val, f: "%t", want: "%!t(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
134 {u: val, f: "%b", want: "%!b(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
135 {u: val, f: "%c", want: "%!c(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
136 {u: val, f: "%d", want: "%!d(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
137 {u: val, f: "%e", want: "%!e(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
138 {u: val, f: "%E", want: "%!E(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
139 {u: val, f: "%f", want: "%!f(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
140 {u: val, f: "%F", want: "%!F(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
141 {u: val, f: "%g", want: "%!g(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
142 {u: val, f: "%G", want: "%!G(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
143 {u: val, f: "%o", want: "%!o(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
144 {u: val, f: "%U", want: "%!U(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"},
145 }
146 for _, tt := range tests {
147 got := fmt.Sprintf(tt.f, tt.u)
148 if tt.want != got {
149 t.Errorf(`Format("%s") got %s, want %s`, tt.f, got, tt.want)
112150 }
113151 }
114152 }
180218 }
181219 }
182220 }
221
222 func TestTimestampFromV6(t *testing.T) {
223 tests := []struct {
224 u UUID
225 want Timestamp
226 wanterr bool
227 }{
228 {u: Must(NewV1()), wanterr: true},
229 {u: Must(FromString("00000000-0000-6000-0000-000000000000")), want: 0},
230 {u: Must(FromString("1ec06cff-e9b1-621c-8627-ba3fd7e551c9")), want: 138493178941215260},
231 {u: Must(FromString("ffffffff-ffff-6fff-ffff-ffffffffffff")), want: Timestamp(1<<60 - 1)},
232 }
233
234 for _, tt := range tests {
235 got, err := TimestampFromV6(tt.u)
236
237 switch {
238 case tt.wanterr && err == nil:
239 t.Errorf("TimestampFromV6(%v) want error, got %v", tt.u, got)
240
241 case tt.want != got:
242 t.Errorf("TimestampFromV6(%v) got %v, want %v", tt.u, got, tt.want)
243 }
244 }
245 }