Skip to content

Commit 6c8542a

Browse files
jgaskinsysbaddaden
andauthored
Add UUID v7 (#14732)
Implementation of [RFC 9562](https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-7) Co-authored-by: Julien Portalier <[email protected]>
1 parent 7d8a6a0 commit 6c8542a

File tree

2 files changed

+48
-2
lines changed

2 files changed

+48
-2
lines changed

spec/std/uuid_spec.cr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,4 +269,21 @@ describe "UUID" do
269269
UUID.v5_x500(data).v5?.should eq(true)
270270
end
271271
end
272+
273+
describe "v7" do
274+
it "generates a v7 UUID" do
275+
uuid = UUID.v7
276+
uuid.v7?.should eq true
277+
uuid.variant.rfc9562?.should eq true
278+
end
279+
280+
pending_wasm32 "generates UUIDs that are sortable with 1ms precision" do
281+
uuids = Array.new(10) do
282+
sleep 1.millisecond
283+
UUID.v7
284+
end
285+
286+
uuids.should eq uuids.sort
287+
end
288+
end
272289
end

src/uuid.cr

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ struct UUID
2323
NCS
2424
# Reserved for RFC 4122 Specification (default).
2525
RFC4122
26+
# Reserved for RFC 9562 Specification (default for v7).
27+
RFC9562 = RFC4122
2628
# Reserved by Microsoft for backward compatibility.
2729
Microsoft
2830
# Reserved for future expansion.
@@ -43,6 +45,8 @@ struct UUID
4345
V4 = 4
4446
# SHA1 hash and namespace.
4547
V5 = 5
48+
# Prefixed with a UNIX timestamp with millisecond precision, filled in with randomness.
49+
V7 = 7
4650
end
4751

4852
# A Domain represents a Version 2 domain (DCE security).
@@ -80,7 +84,7 @@ struct UUID
8084
# do nothing
8185
when Variant::NCS
8286
@bytes[8] = (@bytes[8] & 0x7f)
83-
when Variant::RFC4122
87+
when Variant::RFC4122, Variant::RFC9562
8488
@bytes[8] = (@bytes[8] & 0x3f) | 0x80
8589
when Variant::Microsoft
8690
@bytes[8] = (@bytes[8] & 0x1f) | 0xc0
@@ -321,6 +325,30 @@ struct UUID
321325
end
322326
{% end %}
323327

328+
# Generates an RFC9562-compatible v7 UUID, allowing the values to be sorted
329+
# chronologically (with 1ms precision) by their raw or hexstring
330+
# representation.
331+
def self.v7(random r : Random = Random::Secure)
332+
buffer = uninitialized UInt8[18]
333+
value = buffer.to_slice
334+
335+
# Generate the first 48 bits of the UUID with the current timestamp. We
336+
# allocated enough room for a 64-bit timestamp to accommodate the
337+
# NetworkEndian.encode call here, but we only need 48 bits of it so we chop
338+
# off the first 2 bytes.
339+
IO::ByteFormat::NetworkEndian.encode Time.utc.to_unix_ms, value
340+
value = value[2..]
341+
342+
# Fill in the rest with random bytes
343+
r.random_bytes(value[6..])
344+
345+
# Set the version and variant
346+
value[6] = (value[6] & 0x3F) | 0x70
347+
value[8] = (value[8] & 0x0F) | 0x80
348+
349+
new(value, variant: :rfc9562, version: :v7)
350+
end
351+
324352
# Generates an empty UUID.
325353
#
326354
# ```
@@ -375,6 +403,7 @@ struct UUID
375403
when 3 then Version::V3
376404
when 4 then Version::V4
377405
when 5 then Version::V5
406+
when 7 then Version::V7
378407
else Version::Unknown
379408
end
380409
end
@@ -442,7 +471,7 @@ struct UUID
442471
class Error < Exception
443472
end
444473

445-
{% for v in %w(1 2 3 4 5) %}
474+
{% for v in %w(1 2 3 4 5 7) %}
446475
# Returns `true` if UUID is a V{{ v.id }}, `false` otherwise.
447476
def v{{ v.id }}?
448477
variant == Variant::RFC4122 && version == Version::V{{ v.id }}

0 commit comments

Comments
 (0)