1
0
mirror of https://github.com/google/uuid.git synced 2024-11-24 08:32:23 +02:00

fix: Monotonicity in UUIDv7 (#150)

* Monotonicity in UUIDv7

* fix Monotonicity

* fix comment

* Monotonicity 2

* lock

* fix comment

* fix comment
This commit is contained in:
MikeWang 2024-01-12 02:16:31 +08:00 committed by GitHub
parent c58770eb49
commit a2b2b32373
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 14 deletions

View File

@ -825,7 +825,7 @@ func TestVersion6(t *testing.T) {
func TestVersion7(t *testing.T) {
SetRand(nil)
m := make(map[string]bool)
for x := 1; x < 32; x++ {
for x := 1; x < 128; x++ {
uuid, err := NewV7()
if err != nil {
t.Fatalf("could not create UUID: %v", err)
@ -874,8 +874,7 @@ func TestVersion7_pooled(t *testing.T) {
func TestVersion7FromReader(t *testing.T) {
myString := "8059ddhdle77cb52"
r := bytes.NewReader([]byte(myString))
r2 := bytes.NewReader([]byte(myString))
uuid1, err := NewV7FromReader(r)
_, err := NewV7FromReader(r)
if err != nil {
t.Errorf("failed generating UUID from a reader")
}
@ -883,11 +882,17 @@ func TestVersion7FromReader(t *testing.T) {
if err == nil {
t.Errorf("expecting an error as reader has no more bytes. Got uuid. NewV7FromReader may not be using the provided reader")
}
uuid3, err := NewV7FromReader(r2)
if err != nil {
t.Errorf("failed generating UUID from a reader")
}
if uuid1 != uuid3 {
t.Errorf("expected duplicates, got %q and %q", uuid1, uuid3)
}
func TestVersion7Monotonicity(t *testing.T) {
length := 10000
u1 := Must(NewV7()).String()
for i := 0; i < length; i++ {
u2 := Must(NewV7()).String()
if u2 <= u1 {
t.Errorf("monotonicity failed at #%d: %s(next) < %s(before)", i, u2, u1)
break
}
u1 = u2
}
}

View File

@ -44,7 +44,7 @@ func NewV7FromReader(r io.Reader) (UUID, error) {
// makeV7 fill 48 bits time (uuid[0] - uuid[5]), set version b0111 (uuid[6])
// uuid[8] already has the right version number (Variant is 10)
// see function NewV7 and NewV7FromReader
// see function NewV7 and NewV7FromReader
func makeV7(uuid []byte) {
/*
0 1 2 3
@ -52,7 +52,7 @@ func makeV7(uuid []byte) {
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms | ver | rand_a |
| unix_ts_ms | ver | rand_a (12 bit seq) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -61,7 +61,7 @@ func makeV7(uuid []byte) {
*/
_ = uuid[15] // bounds check
t := timeNow().UnixMilli()
t, s := getV7Time()
uuid[0] = byte(t >> 40)
uuid[1] = byte(t >> 32)
@ -70,6 +70,35 @@ func makeV7(uuid []byte) {
uuid[4] = byte(t >> 8)
uuid[5] = byte(t)
uuid[6] = 0x70 | (uuid[6] & 0x0F)
// uuid[8] has already has right version
uuid[6] = 0x70 | (0x0F & byte(s>>8))
uuid[7] = byte(s)
}
// lastV7time is the last last time we returned stored as:
//
// 52 bits of time in milliseconds since epoch
// 12 bits of (fractional nanoseconds) >> 8
var lastV7time int64
const nanoPerMilli = 1000000
// getV7Time returns the time in milliseconds and nanoseconds / 256.
// The returned (milli << 12 + seq) is guarenteed to be greater than
// (milli << 12 + seq) returned by any previous call to getV7Time.
func getV7Time() (milli, seq int64) {
timeMu.Lock()
defer timeMu.Unlock()
nano := timeNow().UnixNano()
milli = nano / nanoPerMilli
// Sequence number is between 0 and 3906 (nanoPerMilli>>8)
seq = (nano - milli*nanoPerMilli) >> 8
now := milli<<12 + seq
if now <= lastV7time {
now = lastV7time + 1
milli = now >> 12
seq = now & 0xfff
}
lastV7time = now
return milli, seq
}