mirror of
https://github.com/Yu-Vitaqua-fer-Chronos/NULID.git
synced 2024-11-21 22:12:46 +00:00
Implement binary format, allow for conversion to int128, and allow for custom timestamps to be set
This commit is contained in:
parent
607411d40a
commit
37420ad406
|
@ -1,7 +1,7 @@
|
|||
# Package
|
||||
|
||||
version = "0.1.1"
|
||||
author = "Mythical Forest Collective"
|
||||
version = "0.2.0"
|
||||
author = "Yu Vitaqua fer Chronos"
|
||||
description = "An implementation of ULID!"
|
||||
license = "CC0"
|
||||
srcDir = "src"
|
||||
|
|
|
@ -9,6 +9,8 @@ import pkg/[
|
|||
nint128
|
||||
]
|
||||
|
||||
import pkg/nint128/vendor/stew/endians2
|
||||
|
||||
const HighUint80 = u128("1208925819614629174706176")
|
||||
|
||||
type
|
||||
|
@ -20,13 +22,17 @@ type
|
|||
lastTime: int64 = 0
|
||||
random: UInt128 = u128(0)
|
||||
|
||||
func swapBytes(x: Int128): Int128 =
|
||||
result.lo = swapBytes(cast[uint64](x.hi))
|
||||
result.hi = cast[int64](swapBytes(x.lo))
|
||||
|
||||
proc toArray*[T](oa: openArray[T], size: static Slice[int]): array[size.len, T] =
|
||||
result[0..<size.len] = oa[size]
|
||||
|
||||
proc randomBits(): UInt128 =
|
||||
let rnd = urandom(10)
|
||||
|
||||
result = UInt128.fromBytesBE(
|
||||
[0.byte, 0, 0, 0, 0, 0, rnd[0], rnd[1], rnd[2], rnd[3],
|
||||
rnd[4], rnd[5], rnd[6], rnd[7], rnd[8], rnd[9]]
|
||||
)
|
||||
result = UInt128.fromBytesBE(@[0.byte, 0, 0, 0, 0, 0] & rnd)
|
||||
|
||||
template getTime: int64 = (epochTime() * 1000).int64
|
||||
|
||||
|
@ -37,37 +43,67 @@ proc wait(gen: NULIDGenerator): Future[int64] {.async.} =
|
|||
await sleepAsync(1)
|
||||
result = getTime()
|
||||
|
||||
proc nulid*(gen: NULIDGenerator): Future[NULID] {.async.} =
|
||||
var now = getTime()
|
||||
proc nulid*(gen: NULIDGenerator, timestamp: int64 = 0): Future[NULID] {.async.} =
|
||||
if timestamp == 0:
|
||||
var now = getTime()
|
||||
|
||||
if now < gen.lastTime:
|
||||
raise newException(OSError, "Time went backwards!")
|
||||
if now < gen.lastTime:
|
||||
raise newException(OSError, "Time went backwards!")
|
||||
|
||||
if gen.lastTime == now:
|
||||
inc gen.random
|
||||
if gen.lastTime == now:
|
||||
inc gen.random
|
||||
|
||||
if gen.random == HighUInt80:
|
||||
now = await gen.wait()
|
||||
if gen.random == HighUInt80:
|
||||
now = await gen.wait()
|
||||
|
||||
else:
|
||||
gen.random = randomBits()
|
||||
|
||||
result.timestamp = now
|
||||
|
||||
else:
|
||||
gen.random = randomBits()
|
||||
|
||||
result.timestamp = now
|
||||
result.timestamp = timestamp
|
||||
result.randomness = gen.random
|
||||
|
||||
proc nulidSync*(gen: NULIDGenerator): NULID = waitFor gen.nulid()
|
||||
proc nulidSync*(gen: NULIDGenerator, timestamp: int64 = 0): NULID =
|
||||
result = waitFor gen.nulid(timestamp)
|
||||
|
||||
proc toInt128*(ulid: NULID): Int128 =
|
||||
result = i128(ulid.timestamp) shl 80
|
||||
|
||||
result.hi += cast[int64](ulid.randomness.hi)
|
||||
result.lo += ulid.randomness.lo
|
||||
|
||||
proc fromInt128*(_: typedesc[NULID], val: Int128): NULID =
|
||||
result.timestamp = (val shr 16).hi
|
||||
result.randomness = UInt128(
|
||||
hi: cast[uint64]((val.hi shl 48) shr 48),
|
||||
lo: val.lo
|
||||
)
|
||||
|
||||
proc toBytes*(ulid: NULID): array[16, byte] =
|
||||
when cpuEndian == littleEndian:
|
||||
return cast[array[16, byte]](ulid.toInt128().swapBytes())
|
||||
|
||||
else:
|
||||
return cast[array[16, byte]](ulid.toInt128())
|
||||
|
||||
proc fromBytes*(_: typedesc[NULID], ulidBytes: openArray[byte]): NULID =
|
||||
if ulidBytes.len != 16:
|
||||
raise newException(RangeDefect, "Given byte array must be 16 bytes long!")
|
||||
|
||||
when cpuEndian == littleEndian:
|
||||
return NULID.fromInt128(cast[Int128](ulidBytes.toArray(0..15)).swapBytes())
|
||||
|
||||
else:
|
||||
return NULID.fromInt128(cast[Int128](ulidBytes.toArray(0..15)))
|
||||
|
||||
proc parseNulid*(ulidStr: string): NULID =
|
||||
if ulidStr.len != 26:
|
||||
raise newException(RangeDefect, "Invalid ULID! Must be 26 characters long!")
|
||||
|
||||
result.timestamp = int64.decode(ulidStr[0..9])
|
||||
result.randomness = UInt128.decode(ulidStr[10..25])
|
||||
|
||||
proc `$`*(ulid: NULID): string =
|
||||
var res = i128(ulid.timestamp)
|
||||
|
||||
res = res shl 80
|
||||
res.hi += cast[int64](ulid.randomness.hi)
|
||||
res.lo += ulid.randomness.lo
|
||||
|
||||
result = '0' & Int128.encode(res, 26)
|
||||
|
||||
proc `==`*(a: NULID, b: string): bool = a == parseNulid(b)
|
||||
result = '0' & Int128.encode(ulid.toInt128(), 26)
|
||||
|
|
|
@ -24,3 +24,16 @@ test "NULID Parsing":
|
|||
randomness: u128("541019288874337045949482"))
|
||||
|
||||
check parseNulid(nulidStr) == nulid
|
||||
|
||||
test "NULID Int128 Conversion":
|
||||
let nulid = parseNulid("01H999MBGTEA8BDS0M5AWEBB1A")
|
||||
|
||||
check NULID.fromInt128(nulid.toInt128()) == nulid
|
||||
|
||||
test "NULID Binary Format":
|
||||
let
|
||||
nulid = parseNulid("01H999MBGTEA8BDS0M5AWEBB1A")
|
||||
nulidBytes = [1.byte, 138, 82, 154, 46, 26, 114, 144, 182, 228, 20, 42, 184, 229, 172, 42]
|
||||
|
||||
check nulid == NULID.fromBytes(nulidBytes)
|
||||
check nulid.toBytes == nulidBytes
|
||||
|
|
Loading…
Reference in a new issue