~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain-ios

1 collaborator
0
1
main
49 lines · 2.1 KB Raw History
import Foundation

/// AT Protocol record key — a "timestamp identifier".
///
/// Grain assigns these client-side before a gallery is published rather than
/// letting the PDS pick them. That is what makes a publish safe to repeat: an
/// interrupted upload retried five seconds later writes to the *same* record
/// keys, so it overwrites its own half-finished work instead of leaving a
/// second gallery behind.
///
/// Format is 13 characters of sortable base32 packing a 64-bit value: a zero
/// high bit, 53 bits of microseconds since the UNIX epoch, then a 10-bit clock
/// identifier that keeps two devices writing in the same microsecond apart.
enum TID {
    private static let alphabet = Array("234567abcdefghijklmnopqrstuvwxyz")
    /// Characters that can appear first — anything higher would set the high bit.
    private static let leadingAlphabet = Set("234567abcdefghij")

    private static let lock = NSLock()
    private nonisolated(unsafe) static var lastMicros: UInt64 = 0
    private static let clockID = UInt64.random(in: 0 ..< 1024)

    /// A fresh TID that sorts after every TID this process has already issued,
    /// even if the clock hasn't ticked between two calls.
    static func next() -> String {
        lock.lock()
        let now = UInt64(Date().timeIntervalSince1970 * 1_000_000)
        let micros = max(now, lastMicros + 1)
        lastMicros = micros
        lock.unlock()
        return encode(micros: micros, clockID: clockID)
    }

    static func encode(micros: UInt64, clockID: UInt64) -> String {
        var value = ((micros & 0x1F_FFFF_FFFF_FFFF) << 10) | (clockID & 0x3FF)
        var chars = [Character](repeating: alphabet[0], count: 13)
        for index in stride(from: 12, through: 0, by: -1) {
            chars[index] = alphabet[Int(value & 0x1F)]
            value >>= 5
        }
        return String(chars)
    }

    /// Whether `string` is a syntactically valid record key we could have written.
    static func isValid(_ string: String) -> Bool {
        guard string.count == 13, let first = string.first else { return false }
        return leadingAlphabet.contains(first) && string.allSatisfy { alphabet.contains($0) }
    }
}