From 534ba1c2f5ee81744d1f18fea65c937f08dc39fe Mon Sep 17 00:00:00 2001 From: Yu-Vitaqua-fer-Chronos Date: Thu, 26 Oct 2023 14:13:01 +0100 Subject: [PATCH] New library version, rename NULID* types to ULID, remove async code (useless), add a lock and make threadsafe --- nulid.nimble | 2 +- src/nulid.nim | 155 +++++++++++++++++--------------- src/nulid/private/constants.nim | 7 +- tests/test1.nim | 2 +- 4 files changed, 91 insertions(+), 75 deletions(-) diff --git a/nulid.nimble b/nulid.nimble index b27b59d..9f84bfb 100644 --- a/nulid.nimble +++ b/nulid.nimble @@ -1,6 +1,6 @@ # Package -version = "0.2.5" +version = "1.0.0" author = "Yu Vitaqua fer Chronos" description = "An implementation of ULID!" license = "CC0" diff --git a/src/nulid.nim b/src/nulid.nim index 1b08e08..22b06da 100644 --- a/src/nulid.nim +++ b/src/nulid.nim @@ -1,12 +1,11 @@ import std/[ - asyncdispatch, - times + rlocks, + times, + os ] -import pkg/[ - crockfordb32, - nint128 -] +import crockfordb32 +import nint128 import ./nulid/private/constants @@ -21,35 +20,39 @@ else: ##[ Note: `--define:nulidInsecureRandom` can be passed to the compiler to make it so that `std/random` is used instead of `std/sysrand`. -The JS backend and Nimscript use this by default (whether either work with NULID -is untested). + +The JS backend and Nimscript use this by default (whether either work with NULID, +is assumed to be no, due to locks being used). ]## type - NULID* = object + ULID* = object ## An object representing a ULID. timestamp*: int64 randomness*: UInt128 - NULIDGenerator* = ref object - ## A NULID generator object, contains details needed to follow the spec. - ## A generator was made to be compliant with the NULID spec and also to be + ULIDGenerator* = ref object + ## A `ULID` generator object, contains details needed to follow the spec. + ## A generator was made to be compliant with the ULID spec and also to be ## threadsafe not use globals that could change. - lastTime: int64 # Timestamp of last ULID - random: UInt128 # A random number + lock*: RLock + lastTime {.guard: lock.}: int64 # Timestamp of last ULID, 48 bits + random {.guard: lock.}: UInt128 # A random number, 80 bits when InsecureRandom: - rand: Rand # Random generator when using insecure random + rand {.guard: lock.}: Rand # Random generator when using insecure random -proc initNulidGenerator*(): NULIDGenerator = - ## Initialises a `NULIDGenerator` for use. - result = NULIDGenerator(lastTime: 0, random: 0.u128) +proc initUlidGenerator*(): ULIDGenerator = + ## Initialises a `ULIDGenerator` for use. + result = ULIDGenerator(lock: RLock(), lastTime: 0, random: 0.u128) + initRLock(result.lock) when InsecureRandom: - result.rand = initRand() + {.cast(gcsafe).}: + withRLock(result.lock): + result.rand = initRand() -# Discouraged to use it but it's fine for single-threaded apps really -let globalGen = initNulidGenerator() +let globalGen = initUlidGenerator() func swapBytes(x: Int128): Int128 = result.lo = swapBytes(cast[uint64](x.hi)) @@ -58,14 +61,16 @@ func swapBytes(x: Int128): Int128 = func toArray[T](oa: openArray[T], size: static Slice[int]): array[size.len, T] = result[0..`_ runnableExamples: - echo nulidSync() + echo ulid() - result = waitFor nulid(timestamp) + result = ulid(globalGen, timestamp) -func toInt128*(ulid: NULID): Int128 = - ## Allows for a ULID to be converted to an Int128. +func toInt128*(ulid: ULID): Int128 = + ## Allows for a `ULID` to be converted to an Int128. runnableExamples: - echo nulidSync().toInt128() + echo ulid().toInt128() result = i128(ulid.timestamp) shl 80 result.hi += cast[int64](ulid.randomness.hi) result.lo += ulid.randomness.lo -func fromInt128*(_: typedesc[NULID], val: Int128): NULID = - ## Parses an Int128 to a NULID. +func fromInt128*(_: typedesc[ULID], val: Int128): ULID = + ## Parses an Int128 to a ULID. result.timestamp = (val shr 16).hi result.randomness = UInt128( hi: cast[uint64]((val.hi shl 48) shr 48), lo: val.lo ) -func toBytes*(ulid: NULID): array[16, byte] = - ## Allows for a NULID to be converted to a byte array. +func toBytes*(ulid: ULID): array[16, byte] = + ## Allows for a `ULID` to be converted to a byte array for the binary format. runnableExamples: let - ulid = parseNulid("01H999MBGTEA8BDS0M5AWEBB1A") + ulid = ULID.parse("01H999MBGTEA8BDS0M5AWEBB1A") ulidBytes = [1.byte, 138, 82, 154, 46, 26, 114, 144, 182, 228, 20, 42, 184, 229, 172, 42] - echo ulid == NULID.fromBytes(ulidBytes) + assert ulid == ULID.fromBytes(ulidBytes) when cpuEndian == littleEndian: return cast[array[16, byte]](ulid.toInt128().swapBytes()) @@ -160,21 +173,21 @@ func toBytes*(ulid: NULID): array[16, byte] = else: return cast[array[16, byte]](ulid.toInt128()) -func fromBytes*(_: typedesc[NULID], ulidBytes: openArray[byte]): NULID = - ## Parses a byte array to a NULID. +func fromBytes*(_: typedesc[ULID], ulidBytes: openArray[byte]): ULID = + ## Parses a byte array to a `ULID.`. 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()) + return ULID.fromInt128(cast[Int128](ulidBytes.toArray(0..15)).swapBytes()) else: - return NULID.fromInt128(cast[Int128](ulidBytes.toArray(0..15))) + return ULID.fromInt128(cast[Int128](ulidBytes.toArray(0..15))) -func parseNulid*(ulidStr: string): NULID = - ## Parses a string to a NULID. +func parse*(_: typedesc[ULID], ulidStr: string): ULID = + ## Parses a `ULID` from a string. runnableExamples: - echo parseNulid("01H999MBGTEA8BDS0M5AWEBB1A") + echo ULID.parse("01H999MBGTEA8BDS0M5AWEBB1A") if ulidStr.len != 26: raise newException(RangeDefect, "Invalid ULID! Must be 26 characters long!") @@ -182,9 +195,9 @@ func parseNulid*(ulidStr: string): NULID = result.timestamp = int64.decode(ulidStr[0..9]) result.randomness = UInt128.decode(ulidStr[10..25]) -func `$`*(ulid: NULID): string = +func `$`*(ulid: ULID): string = ## Returns the string representation of a ULID. runnableExamples: - echo nulidSync() + echo $ulid() result = Int128.encode(ulid.toInt128(), 26) diff --git a/src/nulid/private/constants.nim b/src/nulid/private/constants.nim index 5a64459..48f57ee 100644 --- a/src/nulid/private/constants.nim +++ b/src/nulid/private/constants.nim @@ -1,5 +1,8 @@ import nint128 -const HighUint80* = u128("1208925819614629174706176") -# No sysrand on the JS backend nor VM (does this even work on either?) +const + HighInt48* = 281474976710655'i64 + HighUint80* = u128("1208925819614629174706176") + +# No sysrand on the JS backend nor VM (though neither is expected to work). const InsecureRandom* = defined(nulidInsecureRandom) or defined(js) or defined(nimvm) diff --git a/tests/test1.nim b/tests/test1.nim index 1333c58..726aa85 100644 --- a/tests/test1.nim +++ b/tests/test1.nim @@ -13,7 +13,7 @@ import nulid test "NULID Generation": for _ in 0..5: - let nulid = nulidSync() + let nulid = nulid() echo nulid test "NULID Parsing":