Skip to content

Commit 3564d62

Browse files
committed
feat: add swift version
1 parent 4c8de5b commit 3564d62

File tree

5 files changed

+123
-0
lines changed

5 files changed

+123
-0
lines changed

.swift-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
6.0

Package.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// swift-tools-version: 6.0
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "uuidv7",
8+
targets: [
9+
// Targets are the basic building blocks of a package, defining a module or a test suite.
10+
// Targets can depend on other targets in this package and products from dependencies.
11+
.executableTarget(
12+
name: "uuidv7"),
13+
]
14+
)

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ uuidv7 > uuidv7.txt
2323
- [By the Bits](#by-the-bits)
2424
- [Build](#build)
2525
- [Go Build (and TinyGo)](#go)
26+
- [Swift)](#swift)
2627
- [Zig Build](#zig)
2728
- [License](#license)
2829

@@ -179,6 +180,14 @@ tinygo build -o uuidv7 ./cmd/.
179180
GOOS=linux GOARCH=amd64 GOAMD64=v2 tinygo build -o uuidv7 ./cmd/.
180181
```
181182

183+
## Swift
184+
185+
```sh
186+
swift build --configuration release --show-bin-path
187+
188+
./.build/arm64-apple-macosx/release/uuidv7
189+
```
190+
182191
## Zig
183192

184193
See </build.sh>.

Sources/main.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// The Swift Programming Language
2+
// https://docs.swift.org/swift-book
3+
4+
func main() {
5+
var uuidv7 = UUIDv7()
6+
print(uuidv7.generate())
7+
}
8+
9+
main()

Sources/uuidv7.swift

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import CryptoKit
2+
import Foundation
3+
4+
let UUIDSize = 16
5+
let SlideSize = 10
6+
7+
// UUIDv7 represented as 16 bytes
8+
struct UUIDv7 {
9+
var bytes: [UInt8]
10+
private var buffer: [UInt8]
11+
private var cursor: Int
12+
13+
init() {
14+
bytes = [UInt8](repeating: 0, count: UUIDSize)
15+
buffer = [UInt8](repeating: 0, count: 96) // can do 10 UUIDs before re-randomizing buffer
16+
cursor = buffer.count
17+
}
18+
19+
mutating func generate() -> String {
20+
let now = Int64(Date().timeIntervalSince1970 * 1000)
21+
return generateAt(ms: now)
22+
}
23+
24+
mutating func generateAt(ms: Int64) -> String {
25+
let bytes = generateBytesAt(ms: ms)
26+
let uuidv7 = UUIDv7.format(bytes: bytes)
27+
return uuidv7
28+
}
29+
30+
mutating func generateBytesAt(ms: Int64) -> [UInt8] {
31+
var uuid = UUIDv7.nextSubarray(buffer: &buffer, cursor: &cursor)
32+
UUIDv7.fill(&uuid, ms: ms)
33+
let bytes = Array(uuid[0 ..< UUIDSize])
34+
return bytes
35+
}
36+
37+
static func fill(_ uuid: inout [UInt8], ms: Int64) {
38+
let timeHigh = UInt32(ms >> 16) // the lower 32 bits (of the upper 48 bits)
39+
let timeLow = UInt16(ms & 0xFFFF) // the lower 16 bits
40+
41+
uuid[0] = UInt8((timeHigh >> 24) & 0xFF)
42+
uuid[1] = UInt8((timeHigh >> 16) & 0xFF)
43+
uuid[2] = UInt8((timeHigh >> 8) & 0xFF)
44+
uuid[3] = UInt8(timeHigh & 0xFF)
45+
46+
uuid[4] = UInt8((timeLow >> 8) & 0xFF)
47+
uuid[5] = UInt8(timeLow & 0xFF)
48+
49+
// set top 4 bits to 0b0111 (0x7 for UUID v7)
50+
uuid[6] = (uuid[6] & 0x0F) | 0x70
51+
52+
// set top 2 bits to 0b10 (RFC 4122 UUID variant)
53+
uuid[8] = (uuid[8] & 0x3F) | 0x80
54+
}
55+
56+
static func format(bytes: [UInt8]) -> String {
57+
let hexStr = bytes.map { String(format: "%02x", $0) }.joined()
58+
59+
let part1 = hexStr.prefix(8)
60+
let part2 = hexStr.dropFirst(8).prefix(4)
61+
let part3 = hexStr.dropFirst(12).prefix(4)
62+
let part4 = hexStr.dropFirst(16).prefix(4)
63+
let part5 = hexStr.suffix(12)
64+
65+
return "\(part1)-\(part2)-\(part3)-\(part4)-\(part5)"
66+
}
67+
68+
static func setBuffer(bytes: [UInt8], buffer: inout [UInt8], cursor: inout Int) throws {
69+
guard bytes.count >= UUIDSize else {
70+
struct BufferTooSmallError: LocalizedError {
71+
var errorDescription: String? { "Minimum UUIDv7 buffer size is \(UUIDSize) bytes" }
72+
}
73+
throw BufferTooSmallError()
74+
}
75+
buffer = bytes
76+
cursor = buffer.count
77+
}
78+
79+
// Advances the random buffer cursor, potentially refilling the buffer
80+
static func nextSubarray(buffer: inout [UInt8], cursor: inout Int) -> [UInt8] {
81+
cursor += 10
82+
var end = cursor + UUIDSize
83+
if end > buffer.count {
84+
_ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer)
85+
cursor = 0
86+
end = UUIDSize
87+
}
88+
return Array(buffer[cursor ..< cursor + UUIDSize])
89+
}
90+
}

0 commit comments

Comments
 (0)