Skip to content

Commit 4988c4e

Browse files
committed
Optimize FIUUID.
1 parent a277647 commit 4988c4e

File tree

2 files changed

+132
-41
lines changed

2 files changed

+132
-41
lines changed

Sources/Megrez/9_SwiftImpl_Internals.swift

Lines changed: 129 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -74,59 +74,149 @@ extension StringProtocol {
7474

7575
// MARK: - FIUUID
7676

77-
/// A simple UUID v4 implementation without Foundation.
78-
/// Generates a random 128-bit UUID compliant with RFC 4122.
77+
/// A lightweight pseudo-UUID,採用兩個 UInt64 儲存 128 位元識別值,以維持標準 UUID 字串格式且降低陣列開銷。
78+
@frozen
7979
public struct FIUUID: Hashable, Codable, Sendable {
8080
// MARK: Lifecycle
8181

8282
public init() {
83-
var rng = SystemRandomNumberGenerator()
84-
self.bytes = Self.randomBytes(count: 16, using: &rng)
85-
// Set version to 4
86-
bytes[6] = (bytes[6] & 0x0F) | 0x40
87-
// Set variant to 1 (RFC 4122)
88-
bytes[8] = (bytes[8] & 0x3F) | 0x80
83+
self.highBits = UInt64.random(in: UInt64.min ... UInt64.max)
84+
self.lowBits = UInt64.random(in: UInt64.min ... UInt64.max)
85+
}
86+
87+
public init(highBits: UInt64, lowBits: UInt64) {
88+
self.highBits = highBits
89+
self.lowBits = lowBits
90+
}
91+
92+
/// 與先前 64 位元版本相容:會將值儲存在低位元,並將高位元清零。
93+
public init(rawValue: UInt64) {
94+
self.highBits = 0
95+
self.lowBits = rawValue
96+
}
97+
98+
public init(from decoder: any Decoder) throws {
99+
let container = try decoder.singleValueContainer()
100+
101+
if let stringValue = try? container.decode(String.self) {
102+
guard let decoded = FIUUID(uuidString: stringValue) else {
103+
throw DecodingError.dataCorruptedError(
104+
in: container,
105+
debugDescription: "Invalid UUID string for FIUUID."
106+
)
107+
}
108+
self = decoded
109+
return
110+
}
111+
112+
if let pair = try? container.decode([UInt64].self), pair.count == 2 {
113+
self.init(highBits: pair[0], lowBits: pair[1])
114+
return
115+
}
116+
117+
if let legacyRaw = try? container.decode(UInt64.self) {
118+
self.init(rawValue: legacyRaw)
119+
return
120+
}
121+
122+
throw DecodingError.dataCorruptedError(
123+
in: container,
124+
debugDescription: "Unsupported FIUUID encoding."
125+
)
126+
}
127+
128+
/// 依照 UUID 字串初始化識別值,支援大小寫。
129+
public init?(uuidString: String) {
130+
let filtered = uuidString.filter { $0 != "-" }
131+
guard filtered.count == 32 else { return nil }
132+
133+
var bytes: [UInt8] = []
134+
bytes.reserveCapacity(16)
135+
136+
var index = filtered.startIndex
137+
for _ in 0 ..< 16 {
138+
let highChar = filtered[index]
139+
index = filtered.index(after: index)
140+
let lowChar = filtered[index]
141+
index = filtered.index(after: index)
142+
143+
guard let high = Self.hexValue(of: highChar),
144+
let low = Self.hexValue(of: lowChar)
145+
else { return nil }
146+
147+
bytes.append((high << 4) | low)
148+
}
149+
150+
self.init(bytes: bytes)
151+
}
152+
153+
private init(bytes: [UInt8]) {
154+
self.highBits = Self.readBigEndian(from: bytes, offset: 0)
155+
self.lowBits = Self.readBigEndian(from: bytes, offset: 8)
89156
}
90157

91158
// MARK: Public
92159

93-
/// Returns the UUID as a standard hyphenated string (e.g., "123e4567-e89b-12d3-a456-426614174000").
94-
public func uuidString() -> String {
95-
let hexDigits = bytes.map { byte in
96-
let hex = String(byte, radix: 16)
97-
return hex.count == 1 ? "0" + hex : hex
160+
/// 高 64 位元與低 64 位元的原始識別值。
161+
public let highBits: UInt64
162+
public let lowBits: UInt64
163+
164+
/// 以標準 UUID 字串形式返回識別值(預設為大寫)。
165+
public func uuidString(uppercase: Bool = true) -> String {
166+
var bytes = [UInt8](repeating: 0, count: 16)
167+
Self.write(bigEndian: highBits, to: &bytes, offset: 0)
168+
Self.write(bigEndian: lowBits, to: &bytes, offset: 8)
169+
170+
let hexDigits = uppercase ? Self.upperHexDigits : Self.lowerHexDigits
171+
var characters: [Character] = []
172+
characters.reserveCapacity(36)
173+
174+
for index in 0 ..< bytes.count {
175+
if index == 4 || index == 6 || index == 8 || index == 10 {
176+
characters.append("-")
177+
}
178+
let byte = bytes[index]
179+
characters.append(hexDigits[Int(byte >> 4)])
180+
characters.append(hexDigits[Int(byte & 0x0F)])
98181
}
99-
let hexString = hexDigits.joined()
100-
let part1 = hexString.prefix(8)
101-
let part2 = hexString.dropFirst(8).prefix(4)
102-
let part3 = hexString.dropFirst(12).prefix(4)
103-
let part4 = hexString.dropFirst(16).prefix(4)
104-
let part5 = hexString.suffix(12)
105-
return "\(part1)-\(part2)-\(part3)-\(part4)-\(part5)"
182+
183+
return String(characters)
184+
}
185+
186+
public func encode(to encoder: any Encoder) throws {
187+
var container = encoder.singleValueContainer()
188+
try container.encode(uuidString())
106189
}
107190

108191
// MARK: Private
109192

110-
private var bytes: [UInt8]
111-
112-
private static func randomBytes(
113-
count: Int,
114-
using rng: inout SystemRandomNumberGenerator
115-
)
116-
-> [UInt8] {
117-
var result = [UInt8](repeating: 0, count: count)
118-
var offset = 0
119-
while offset < count {
120-
let randomValue = rng.next()
121-
withUnsafeBytes(of: randomValue) { buffer in
122-
let bytesToCopy = min(8, count - offset)
123-
let byteCount = min(bytesToCopy, buffer.count)
124-
for i in 0 ..< byteCount {
125-
result[offset + i] = buffer[i]
126-
}
127-
offset += byteCount
128-
}
193+
private static let upperHexDigits: [Character] = Array("0123456789ABCDEF")
194+
private static let lowerHexDigits: [Character] = Array("0123456789abcdef")
195+
196+
private static func write(bigEndian value: UInt64, to buffer: inout [UInt8], offset: Int) {
197+
for index in 0 ..< 8 {
198+
buffer[offset + index] = UInt8(truncatingIfNeeded: value >> (8 * (7 - index)))
199+
}
200+
}
201+
202+
private static func readBigEndian(from buffer: [UInt8], offset: Int) -> UInt64 {
203+
var result: UInt64 = 0
204+
for index in 0 ..< 8 {
205+
result = (result << 8) | UInt64(buffer[offset + index])
129206
}
130207
return result
131208
}
209+
210+
private static func hexValue(of character: Character) -> UInt8? {
211+
switch character {
212+
case "0" ... "9":
213+
return UInt8(character.unicodeScalars.first!.value - Unicode.Scalar("0").value)
214+
case "a" ... "f":
215+
return UInt8(character.unicodeScalars.first!.value - Unicode.Scalar("a").value + 10)
216+
case "A" ... "F":
217+
return UInt8(character.unicodeScalars.first!.value - Unicode.Scalar("A").value + 10)
218+
default:
219+
return nil
220+
}
221+
}
132222
}

Tests/MegrezTests/NodeOverrideStatusTests.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ final class NodeOverrideStatusTests: XCTestCase {
2323
// 確保每個 UUID 都是唯一的
2424
XCTAssertNotEqual(uuid1, uuid2)
2525

26-
// 測試 UUID 字符串格式
26+
// 測試 UUID 字串格式
2727
let uuidString = uuid1.uuidString()
28-
XCTAssertEqual(uuidString.count, 36) // 標準 UUID 格式長度
28+
XCTAssertEqual(uuidString.count, 36)
2929
XCTAssertTrue(uuidString.contains("-"))
30+
XCTAssertTrue(uuidString.allSatisfy { $0 == "-" || "0123456789ABCDEF".contains($0) })
3031

3132
// 測試 Codable
3233
let encoder = JSONEncoder()

0 commit comments

Comments
 (0)