68 lines
2.6 KiB
Swift
68 lines
2.6 KiB
Swift
//
|
|
// Copyright 2025 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
extension UUID {
|
|
/// Returns a UUIDv7, as defined in [the RFC][RFC]. Notably, v7 UUIDs embed
|
|
/// a timestamp in the first 6 most-significant bytes, thereby allowing
|
|
/// callers who pass sequential timestamps to construct UUIDs that are
|
|
/// lexicographically sequential.
|
|
///
|
|
/// This sequential property can be a useful optimization when the UUIDs are
|
|
/// part of a database row and that row is indexed by the UUID. When the
|
|
/// UUIDs are ordered in the same sequence as the row insertions the
|
|
/// corresponding insertion into the index is always at the end of the
|
|
/// index (`O(1)`), whereas a random UUID might be inserted anywhere in the
|
|
/// index (`O(log n)`).
|
|
///
|
|
/// [RFC]: https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7
|
|
static func v7(timestamp: UInt64) -> UUID {
|
|
return UUID(uuid: (
|
|
// Assign the lower six bytes of the timestamp (the bytes that change
|
|
// the most) to the first six bytes of the UUID, in big-endian order
|
|
// (most-significant byte first).
|
|
|
|
// Assign timestamp to the first 6 bytes (big-endian)
|
|
// For each of the first six bytes of the
|
|
UInt8((timestamp >> 40) & 0xFF),
|
|
UInt8((timestamp >> 32) & 0xFF),
|
|
UInt8((timestamp >> 24) & 0xFF),
|
|
UInt8((timestamp >> 16) & 0xFF),
|
|
UInt8((timestamp >> 8) & 0xFF),
|
|
UInt8(timestamp & 0xFF),
|
|
|
|
// Set the next four bits to 0b0111, the required "version" for UUIDv7.
|
|
// Set the rest of that byte to random.
|
|
((0b0111 as UInt8) << 4) | UInt8.random(in: 0...((1 << 4) - 1)),
|
|
// Add a full byte of random.
|
|
UInt8.random(in: 0...0xFF),
|
|
|
|
// Set the next two bits to 0b10, the required "variant" for UUIDv7. Set
|
|
// the rest of that byte to random.
|
|
((0b10 as UInt8) << 6) | UInt8.random(in: 0...((1 << 6) - 1)),
|
|
|
|
// Set the remaining bytes to random.
|
|
UInt8.random(in: 0...0xFF),
|
|
UInt8.random(in: 0...0xFF),
|
|
UInt8.random(in: 0...0xFF),
|
|
UInt8.random(in: 0...0xFF),
|
|
UInt8.random(in: 0...0xFF),
|
|
UInt8.random(in: 0...0xFF),
|
|
UInt8.random(in: 0...0xFF),
|
|
))
|
|
}
|
|
}
|
|
|
|
extension NSUUID {
|
|
private static var sequentialCounter: UInt64 = Date().ows_millisecondsSince1970
|
|
|
|
@objc
|
|
static func sequential() -> NSUUID {
|
|
let uuid: UUID = .v7(timestamp: sequentialCounter)
|
|
sequentialCounter += 1
|
|
|
|
return uuid as NSUUID
|
|
}
|
|
}
|