Compare commits
11 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cb8720b56 | ||
|
|
c5039c1cc6 | ||
|
|
2ff415fb2b | ||
|
|
57396065ce | ||
|
|
78fadcb469 | ||
|
|
c58d9474ee | ||
|
|
654e32c43e | ||
|
|
ff5d895688 | ||
|
|
cc0f61aa39 | ||
|
|
eca7e21194 | ||
|
|
f6d0227369 |
5 changed files with 80 additions and 32 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,5 +1,3 @@
|
||||||
**/uuids
|
|
||||||
|
|
||||||
.vscode/.browse.*
|
.vscode/.browse.*
|
||||||
nimsuggest.log
|
nimsuggest.log
|
||||||
|
|
||||||
|
|
|
||||||
17
README.md
17
README.md
|
|
@ -7,19 +7,28 @@ API:
|
||||||
type UUID* = object
|
type UUID* = object
|
||||||
## 128-bit UUID compliant with RFC-4122
|
## 128-bit UUID compliant with RFC-4122
|
||||||
|
|
||||||
|
proc initUUID*(mostSigBits, leastSigBits: int64): UUID =
|
||||||
|
## Initializes UUID with the specified most and least significant bits
|
||||||
|
|
||||||
|
proc leastSigBits*(uuid: UUID): int64 {.inline.}
|
||||||
|
## Returns 64 least significant bits of the ``uuid``
|
||||||
|
|
||||||
|
proc mostSigBits*(uuid: UUID): int64 {.inline.}
|
||||||
|
## Returns 64 most significant bits of the ``uuid``
|
||||||
|
|
||||||
proc `$`*(uuid: UUID): string
|
proc `$`*(uuid: UUID): string
|
||||||
## Returns a string representation of the UUID in canonical form.
|
## Returns a string representation of the ``uuid`` in canonical form.
|
||||||
|
|
||||||
proc hash*(uuid: UUID): Hash
|
proc hash*(uuid: UUID): Hash
|
||||||
## Computes hash of the specified UUID.
|
## Computes hash of the specified ``uuid``.
|
||||||
|
|
||||||
proc `==`*(x, y: UUID): bool
|
proc `==`*(x, y: UUID): bool
|
||||||
## Returns true when the specified UUIDs are equal, false otherwise.
|
## Returns true when the specified UUIDs are equal, false otherwise.
|
||||||
|
|
||||||
proc isZero*(uuid: UUID): bool
|
proc isZero*(uuid: UUID): bool
|
||||||
## Returns ``true`` when the UUID is zero (not set), ``false`` otherwise.
|
## Returns ``true`` when the ``uuid`` is zero (not set), ``false`` otherwise.
|
||||||
|
|
||||||
proc genUUID*(): UUID =
|
proc genUUID*(): UUID
|
||||||
## Returns a random (v4) UUID.
|
## Returns a random (v4) UUID.
|
||||||
## Uses a thread-local cryptographically secure PRNG (ISAAC) seeded with
|
## Uses a thread-local cryptographically secure PRNG (ISAAC) seeded with
|
||||||
## true random values obtained from OS.
|
## true random values obtained from OS.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import strutils, hashes
|
import strutils, hashes
|
||||||
import isaac
|
import isaac
|
||||||
import urandom
|
import uuids/urandom
|
||||||
|
|
||||||
type
|
type
|
||||||
UUID* = object
|
UUID* = object
|
||||||
|
|
@ -10,16 +10,45 @@ type
|
||||||
|
|
||||||
template toHex(s: string, start: Natural,
|
template toHex(s: string, start: Natural,
|
||||||
x: BiggestInt, len: Positive) =
|
x: BiggestInt, len: Positive) =
|
||||||
const HexChars = "0123456789abcdef"
|
const HexChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
'9', 'a', 'b', 'c', 'd', 'e', 'f']
|
||||||
var n = x
|
var n = x
|
||||||
for j in countdown(len - 1, 0):
|
for j in countdown(len - 1, 0):
|
||||||
s[start + j] = HexChars[n and 0xF]
|
s[start + j] = HexChars[int(n and 0xF)]
|
||||||
n = n shr 4
|
n = n shr 4
|
||||||
# handle negative overflow
|
# handle negative overflow
|
||||||
if n == 0 and x < 0: n = -1
|
if n == 0 and x < 0: n = -1
|
||||||
|
|
||||||
|
proc uuidsParseHexInt(s: string, maxLen: int): int64 =
|
||||||
|
if s.len == 0:
|
||||||
|
raise newException(ValueError, "UUID part is empty")
|
||||||
|
if s.len > maxLen or s.len > sizeof(result) * 2:
|
||||||
|
raise newException(ValueError, "UUID part is longer than expected")
|
||||||
|
for c in s:
|
||||||
|
case c
|
||||||
|
of '0'..'9':
|
||||||
|
result = result shl 4 or (ord(c) - ord('0'))
|
||||||
|
of 'a'..'f':
|
||||||
|
result = result shl 4 or (ord(c) - ord('a') + 10)
|
||||||
|
of 'A'..'F':
|
||||||
|
result = result shl 4 or (ord(c) - ord('A') + 10)
|
||||||
|
else: raise newException(ValueError, "Invalid hex string: " & s)
|
||||||
|
|
||||||
|
proc initUUID*(mostSigBits, leastSigBits: int64): UUID =
|
||||||
|
## Initializes UUID with the specified most and least significant bits
|
||||||
|
result.mostSigBits = mostSigBits
|
||||||
|
result.leastSigBits = leastSigBits
|
||||||
|
|
||||||
|
proc leastSigBits*(uuid: UUID): int64 {.inline.} =
|
||||||
|
## Returns 64 least significant bits of the ``uuid``
|
||||||
|
uuid.leastSigBits
|
||||||
|
|
||||||
|
proc mostSigBits*(uuid: UUID): int64 {.inline.} =
|
||||||
|
## Returns 64 most significant bits of the ``uuid``
|
||||||
|
uuid.mostSigBits
|
||||||
|
|
||||||
proc `$`*(uuid: UUID): string =
|
proc `$`*(uuid: UUID): string =
|
||||||
## Returns a string representation of the UUID in canonical form.
|
## Returns a string representation of the ``uuid`` in canonical form.
|
||||||
result = newString(36)
|
result = newString(36)
|
||||||
toHex(result, 0, uuid.mostSigBits shr 32, 8)
|
toHex(result, 0, uuid.mostSigBits shr 32, 8)
|
||||||
result[8] = '-'
|
result[8] = '-'
|
||||||
|
|
@ -32,7 +61,7 @@ proc `$`*(uuid: UUID): string =
|
||||||
toHex(result, 24, uuid.leastSigBits, 12)
|
toHex(result, 24, uuid.leastSigBits, 12)
|
||||||
|
|
||||||
proc hash*(uuid: UUID): Hash =
|
proc hash*(uuid: UUID): Hash =
|
||||||
## Computes hash of the specified UUID.
|
## Computes hash of the specified ``uuid``.
|
||||||
result = uuid.mostSigBits.hash() !& uuid.leastSigBits.hash()
|
result = uuid.mostSigBits.hash() !& uuid.leastSigBits.hash()
|
||||||
result = !$result
|
result = !$result
|
||||||
|
|
||||||
|
|
@ -41,7 +70,7 @@ proc `==`*(x, y: UUID): bool =
|
||||||
x.mostSigBits == y.mostSigBits and x.leastSigBits == y.leastSigBits
|
x.mostSigBits == y.mostSigBits and x.leastSigBits == y.leastSigBits
|
||||||
|
|
||||||
proc isZero*(uuid: UUID): bool =
|
proc isZero*(uuid: UUID): bool =
|
||||||
## Returns ``true`` when the UUID is zero (not set), ``false`` otherwise.
|
## Returns ``true`` when the ``uuid`` is zero (not set), ``false`` otherwise.
|
||||||
uuid.mostSigBits == 0'i64 and uuid.leastSigBits == 0'i64
|
uuid.mostSigBits == 0'i64 and uuid.leastSigBits == 0'i64
|
||||||
|
|
||||||
var rand {.threadvar.}: IsaacGenerator
|
var rand {.threadvar.}: IsaacGenerator
|
||||||
|
|
@ -69,25 +98,36 @@ proc parseUUID*(s: string): UUID {.raises: [ValueError].} =
|
||||||
if parts.len != 5:
|
if parts.len != 5:
|
||||||
raise newException(ValueError,
|
raise newException(ValueError,
|
||||||
"UUID must consist of 5 parts separated with `-`")
|
"UUID must consist of 5 parts separated with `-`")
|
||||||
var mostSigBits: int64 = parseHexInt(parts[0])
|
var mostSigBits: int64 = uuidsParseHexInt(parts[0], 8)
|
||||||
mostSigBits = mostSigBits shl 16
|
mostSigBits = mostSigBits shl 16
|
||||||
mostSigBits = mostSigBits or parseHexInt(parts[1])
|
mostSigBits = mostSigBits or uuidsParseHexInt(parts[1], 4)
|
||||||
mostSigBits = mostSigBits shl 16
|
mostSigBits = mostSigBits shl 16
|
||||||
mostSigBits = mostSigBits or parseHexInt(parts[2])
|
mostSigBits = mostSigBits or uuidsParseHexInt(parts[2], 4)
|
||||||
|
|
||||||
var leastSigBits: int64 = parseHexInt(parts[3])
|
var leastSigBits: int64 = uuidsParseHexInt(parts[3], 4)
|
||||||
leastSigBits = leastSigBits shl 48
|
leastSigBits = leastSigBits shl 48
|
||||||
leastSigBits = leastSigBits or parseHexInt(parts[4])
|
leastSigBits = leastSigBits or uuidsParseHexInt(parts[4], 12)
|
||||||
|
|
||||||
result = UUID(mostSigBits: mostSigBits, leastSigBits: leastSigBits)
|
result = UUID(mostSigBits: mostSigBits, leastSigBits: leastSigBits)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var uuid: UUID
|
var uuid: UUID
|
||||||
assert(uuid.isZero())
|
assert(uuid.isZero())
|
||||||
uuid = genUUID()
|
for i in 1..100:
|
||||||
let uuidStr = $uuid
|
uuid = genUUID()
|
||||||
assert(uuidStr.len == 36)
|
let uuidStr = $uuid
|
||||||
assert(uuidStr[14] == '4') # version
|
assert(uuidStr.len == 36)
|
||||||
assert(uuidStr[19] in {'8', '9', 'a', 'b'}) # variant (2 bits)
|
assert(uuidStr[14] == '4') # version
|
||||||
assert(uuidStr.parseUUID() == uuid)
|
assert(uuidStr[19] in {'8', '9', 'a', 'b'}) # variant (2 bits)
|
||||||
assert(uuidStr.parseUUID().hash() == uuid.hash())
|
|
||||||
|
let parsedUUID = uuidStr.parseUUID()
|
||||||
|
assert(parsedUUID == uuid)
|
||||||
|
assert(parsedUUID.hash() == uuid.hash())
|
||||||
|
assert(mostSigBits(parsedUUID) == mostSigBits(uuid))
|
||||||
|
assert(leastSigBits(parsedUUID) == leastSigBits(uuid))
|
||||||
|
|
||||||
|
let newUUID = initUUID(mostSigBits(uuid), leastSigBits(uuid))
|
||||||
|
assert(newUUID == uuid)
|
||||||
|
assert(newUUID.hash() == uuid.hash())
|
||||||
|
assert(mostSigBits(newUUID) == mostSigBits(uuid))
|
||||||
|
assert(leastSigBits(newUUID) == leastSigBits(uuid))
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
[Package]
|
[Package]
|
||||||
name: "uuids"
|
name: "uuids"
|
||||||
version: "0.1.2"
|
version: "0.1.11"
|
||||||
author: "Xored Software, Inc."
|
author: "Xored Software, Inc."
|
||||||
description: "UUID library"
|
description: "UUID library"
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
srcDir: "src"
|
|
||||||
|
|
||||||
[Deps]
|
[Deps]
|
||||||
requires: "isaac >= 0.1.0"
|
requires: "isaac >= 0.1.3"
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,10 @@ when defined(windows):
|
||||||
|
|
||||||
type ULONG_PTR = int
|
type ULONG_PTR = int
|
||||||
type HCRYPTPROV = ULONG_PTR
|
type HCRYPTPROV = ULONG_PTR
|
||||||
var PROV_RSA_FULL {.importc, header: "<windows.h>".}: DWORD
|
var PROV_RSA_FULL {.importc, header: """#include <windows.h>
|
||||||
var CRYPT_VERIFYCONTEXT {.importc, header: "<windows.h>".}: DWORD
|
#include <wincrypt.h>""".}: DWORD
|
||||||
|
var CRYPT_VERIFYCONTEXT {.importc, header: """#include <windows.h>
|
||||||
|
#include <wincrypt.h>""".}: DWORD
|
||||||
|
|
||||||
{.push, stdcall, dynlib: "Advapi32.dll".}
|
{.push, stdcall, dynlib: "Advapi32.dll".}
|
||||||
|
|
||||||
|
|
@ -14,16 +16,16 @@ when defined(windows):
|
||||||
proc CryptAcquireContext(
|
proc CryptAcquireContext(
|
||||||
phProv: ptr HCRYPTPROV, pszContainer: WideCString,
|
phProv: ptr HCRYPTPROV, pszContainer: WideCString,
|
||||||
pszProvider: WideCString, dwProvType: DWORD, dwFlags: DWORD
|
pszProvider: WideCString, dwProvType: DWORD, dwFlags: DWORD
|
||||||
): WinBool {.importc: "CryptAcquireContextW".}
|
): WINBOOL {.importc: "CryptAcquireContextW".}
|
||||||
else:
|
else:
|
||||||
proc CryptAcquireContext(
|
proc CryptAcquireContext(
|
||||||
phProv: ptr HCRYPTPROV, pszContainer: cstring, pszProvider: cstring,
|
phProv: ptr HCRYPTPROV, pszContainer: cstring, pszProvider: cstring,
|
||||||
dwProvType: DWORD, dwFlags: DWORD
|
dwProvType: DWORD, dwFlags: DWORD
|
||||||
): WinBool {.importc: "CryptAcquireContextA".}
|
): WINBOOL {.importc: "CryptAcquireContextA".}
|
||||||
|
|
||||||
proc CryptGenRandom(
|
proc CryptGenRandom(
|
||||||
hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: pointer
|
hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: pointer
|
||||||
): WinBool {.importc: "CryptGenRandom".}
|
): WINBOOL {.importc: "CryptGenRandom".}
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue