From 0252a33588c9eb237cb002a6ed046cd09332fa48 Mon Sep 17 00:00:00 2001 From: Tony Holdstock-Brown Date: Sat, 10 Feb 2024 12:20:46 -0800 Subject: [PATCH 1/6] Add `uuid.Nil` and `.IsZero()` method This simplifies comparisons with empty ULIDs. Right now, most people are doing something like the following to check if a ULID is non-zero: ```go id.Compare(ulid.ULID{}) == 0 ``` This requires allocating a new empty ULID each time. Some packages avoid this by creating their own global (or private) nil ULIDs on initialization. This should be built in and simple for better perf & a cleaner API. --- ulid.go | 8 ++++++++ ulid_test.go | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/ulid.go b/ulid.go index 2064dd4..62c9bd4 100644 --- a/ulid.go +++ b/ulid.go @@ -75,6 +75,9 @@ var ( // ErrScanValue is returned when the value passed to scan cannot be unmarshaled // into the ULID. ErrScanValue = errors.New("ulid: source value must be a string or byte slice") + + // Nil is an empty ULID, all zeros, useful when comparing nil ULIDs + Nil ULID ) // MonotonicReader is an interface that should yield monotonically increasing @@ -416,6 +419,11 @@ func (id ULID) Timestamp() time.Time { return Time(id.Time()) } +// IsZero returns whether the ULID is a zero-value, ie ulid.Nil. +func (id ULID) IsZero() bool { + return bytes.Equal(id[:], Nil[:]) +} + // maxTime is the maximum Unix time in milliseconds that can be // represented in a ULID. var maxTime = ULID{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}.Time() diff --git a/ulid_test.go b/ulid_test.go index 43f81c6..510d111 100644 --- a/ulid_test.go +++ b/ulid_test.go @@ -454,6 +454,20 @@ func TestULIDTimestamp(t *testing.T) { } } +func TestZero(t *testing.T) { + t.Parallel() + + var id ulid.ULID + if ok := id.IsZero(); !ok { + t.Error(".IsZero: must return true for empty ULIDs, have false") + } + + id = ulid.MustNew(ulid.Now(), ulid.DefaultEntropy()) + if ok := id.IsZero(); ok { + t.Error(".IsZero: must return false for non-nil ULIDs, have true") + } +} + func TestEntropy(t *testing.T) { t.Parallel() From b862d24d181a389fafe313f7a50cd4ed84e6ee36 Mon Sep 17 00:00:00 2001 From: Peter Bourgon Date: Sat, 13 Apr 2024 19:52:16 +0200 Subject: [PATCH 2/6] Update ulid.go --- ulid.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ulid.go b/ulid.go index 62c9bd4..b70a5a3 100644 --- a/ulid.go +++ b/ulid.go @@ -76,8 +76,8 @@ var ( // into the ULID. ErrScanValue = errors.New("ulid: source value must be a string or byte slice") - // Nil is an empty ULID, all zeros, useful when comparing nil ULIDs - Nil ULID + // Zero is a zero-value ULID. + Zero ULID ) // MonotonicReader is an interface that should yield monotonically increasing From 54fb474afc05ba134404dc1125740926e08f4249 Mon Sep 17 00:00:00 2001 From: Peter Bourgon Date: Sat, 13 Apr 2024 19:52:21 +0200 Subject: [PATCH 3/6] Update ulid.go --- ulid.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ulid.go b/ulid.go index b70a5a3..c246d72 100644 --- a/ulid.go +++ b/ulid.go @@ -419,7 +419,7 @@ func (id ULID) Timestamp() time.Time { return Time(id.Time()) } -// IsZero returns whether the ULID is a zero-value, ie ulid.Nil. +// IsZero returns true if the ULID is a zero-value ULID, i.e. ulid.Zero. func (id ULID) IsZero() bool { return bytes.Equal(id[:], Nil[:]) } From 03d06551675b4425cdb47238b879aa405a046daf Mon Sep 17 00:00:00 2001 From: Peter Bourgon Date: Sat, 13 Apr 2024 19:52:26 +0200 Subject: [PATCH 4/6] Update ulid.go --- ulid.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ulid.go b/ulid.go index c246d72..77e9ddd 100644 --- a/ulid.go +++ b/ulid.go @@ -421,7 +421,7 @@ func (id ULID) Timestamp() time.Time { // IsZero returns true if the ULID is a zero-value ULID, i.e. ulid.Zero. func (id ULID) IsZero() bool { - return bytes.Equal(id[:], Nil[:]) + return id.Compare(Zero) == 0 } // maxTime is the maximum Unix time in milliseconds that can be From 2b07c5df70586571825594e856353572ad3b62ef Mon Sep 17 00:00:00 2001 From: Peter Bourgon Date: Sat, 13 Apr 2024 19:52:31 +0200 Subject: [PATCH 5/6] Update ulid_test.go --- ulid_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ulid_test.go b/ulid_test.go index 510d111..4e1d77b 100644 --- a/ulid_test.go +++ b/ulid_test.go @@ -459,7 +459,7 @@ func TestZero(t *testing.T) { var id ulid.ULID if ok := id.IsZero(); !ok { - t.Error(".IsZero: must return true for empty ULIDs, have false") + t.Error(".IsZero: must return true for zero-value ULIDs, have false") } id = ulid.MustNew(ulid.Now(), ulid.DefaultEntropy()) From 395a619c5b8893979ab103dbfee8e243288c2c80 Mon Sep 17 00:00:00 2001 From: Peter Bourgon Date: Sat, 13 Apr 2024 19:52:36 +0200 Subject: [PATCH 6/6] Update ulid_test.go --- ulid_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ulid_test.go b/ulid_test.go index 4e1d77b..5ca70ae 100644 --- a/ulid_test.go +++ b/ulid_test.go @@ -464,7 +464,7 @@ func TestZero(t *testing.T) { id = ulid.MustNew(ulid.Now(), ulid.DefaultEntropy()) if ok := id.IsZero(); ok { - t.Error(".IsZero: must return false for non-nil ULIDs, have true") + t.Error(".IsZero: must return false for non-zero-value ULIDs, have true") } }