Skip to content

Instantly share code, notes, and snippets.

@wildthink
Last active November 19, 2024 03:15
Show Gist options
  • Save wildthink/617c4471bef4bdb8c36f3ea82fec69a6 to your computer and use it in GitHub Desktop.
Save wildthink/617c4471bef4bdb8c36f3ea82fec69a6 to your computer and use it in GitHub Desktop.
Int64 Unique sequence generator as a fn(Date.now, Int16 tag)
//
// Unique64.swift
//
// Created by Jason Jobe on 11/17/24.
//
import Foundation
public protocol SystemEntity: Identifiable where ID == Int64 {}
public extension Identifiable where ID == Int64 {
@_disfavoredOverload
static func eid(tag: Int16 = 0) -> Int64 {
return Unique64.shared.next(tag: tag)
}
}
/* Example:
Extend SystemEntity to override the create id
logic and generator if desired.
```
protocol MyEntityRef: SystemEntity {}
extension SystemEntity where Self: MyEntityRef {
static func eid(tag: Int16 = 0) -> Int64 {
// Provide your own generator here
// NOTE: you can use Self.Type to get the tag
print ("SystemEntity", String(describing: Self.self))
return Unique64.shared.next(tag: tag)
}
}
struct It: Identifiable {
var id: Int64 = eid(tag: 23)
}
struct MyEntity: MyEntityRef {
var id: Int64 = eid()
}
```
*/
/**
• Date.timeIntervalSinceReferenceDate provides the time in seconds as a Double.
• Multiply the interval to scale it to the desired precision (e.g., microseconds).
• Convert it to an Int64, ensuring that the lower 16 bits are cleared by masking or shifting.
*/
public struct Unique64 {
private var last: Int64 = 0
private let lock = NSLock() // Ensure thread safety
/// Returns the current time as a 64-bit integer with lower 16 bits set to zero.
func now() -> Int64 {
// Get the current time in seconds since reference date
let interval = Date.timeIntervalSinceReferenceDate
// Convert to microseconds and clear lower 16 bits
let scaledInterval = Int64(interval * 1_000_000) & ~0xFFFF
return scaledInterval
}
/// Generates the next unique 64-bit value with a 16-bit tag.
public mutating func next(tag: Int16 = 0) -> Int64 {
lock.lock() // Begin critical section
defer { lock.unlock() } // Ensure lock is released
// Generate the base time value
var next = now()
// Ensure the sequence is monotonically increasing
while !(last < next) {
// Resolve collision by incrementing 17th bit
// next = next.increment(bit: 16)
next = next + (1 << 16)
}
// Update last value
last = next
// Add the tag to the lower 16 bits
return last | Int64(tag)
}
}
public extension Unique64 {
nonisolated(unsafe)
static var shared = Unique64()
}
/**
Explanation
1. date Property:
• Masks out the lower 16 bits using self & ~0xFFFF to isolate the timestamp.
• Converts the remaining timestamp (in microseconds) back to seconds by dividing by 1,000,000.
• Uses the timeIntervalSinceReferenceDate to create a Date object.
• Includes a sanity check to ensure the time interval is valid (non-negative).
2. tag16 Property:
• Extracts the lower 16 bits using self & 0xFFFF and converts the result to Int16.
*/
public extension Int64 {
/// Extracts the `Date` component from a `Unique64`-formatted value.
///
/// Assumes the value is encoded as microseconds since the reference date with the lower 16 bits reserved for the tag.
var date: Date? {
let timestamp = self & ~0xFFFF // Mask out the lower 16 bits to get the timestamp
let timeInterval = Double(timestamp) / 1_000_000 // Convert from microseconds to seconds
guard timeInterval >= 0 else { return nil } // Sanity check for a valid time interval
return Date(timeIntervalSinceReferenceDate: timeInterval)
}
/// Extracts the 16-bit tag from a `Unique64`-formatted value.
///
/// Assumes the lower 16 bits represent the tag.
var tag16: Int16 {
return Int16(self & 0xFFFF) // Extract the lower 16 bits
}
}
func unique64Check() {
var uniqueGenerator = Unique64()
let zeroTag = uniqueGenerator.next(tag: 0)
let first = uniqueGenerator.next(tag: 42)
let second = uniqueGenerator.next(tag: 1)
print("Now ", Date())
print("Tag 0 ", zeroTag.date!, "0\(zeroTag.tag16)", zeroTag)
print(String(zeroTag, radix: 16))
print("first ", first.date!, first.tag16, first)
print(String(first, radix: 16))
print("second", second.date!, "0\(second.tag16)", second)
print(String(second, radix: 16))
print("first < second", first < second)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment