Compare commits

...

13 commits

Author SHA1 Message Date
ee7
8cb8720b56 Fix error with styleCheck
Before this commit, running `nim c --styleCheck:error` on a file
that imports the `uuids` package would produce an error on Windows.

`WINBOOL` is defined here:
52cf728001/lib/windows/winlean.nim (L33)

Fixes: #5
2021-01-15 17:07:13 +07:00
Giovanni
c5039c1cc6 fixed nimble check and compatibility with new nim compiler (strings can't be nil) 2018-09-05 15:00:33 +07:00
Ruslan Mustakov
2ff415fb2b Update isaac dependency 2017-11-22 16:53:53 +07:00
Ruslan Mustakov
57396065ce Fix for latest Nim 2017-11-01 19:53:09 +01:00
Ruslan Mustakov
78fadcb469 Expose mostSigBits and leastSigBits 2017-06-18 22:37:13 +07:00
Ruslan Mustakov
c58d9474ee Implement own parseHexInt
stdlib version allows some non-hex characters and doesn't work
with long numbers on 32-bit platforms
2017-06-18 22:24:57 +07:00
Ruslan Mustakov
654e32c43e Up version to 0.1.5 2017-03-13 13:52:15 +07:00
Anatoly Galiulin
ff5d895688 Fix `isaac` dependency 2017-03-13 13:49:25 +07:00
Ruslan Mustakov
cc0f61aa39 Bumped version to v0.1.4 2016-11-30 18:33:33 +07:00
Ruslan Mustakov
eca7e21194 Fixed urandom when windows.h is not included 2016-11-30 17:09:29 +07:00
Ruslan Mustakov
f6d0227369 Fixed headers for urandom on Windows 2016-11-30 15:50:54 +07:00
Ruslan Mustakov
c6783039ac Added isZero proc 2016-10-23 20:51:22 +07:00
Ruslan Mustakov
360ad9b082 Fixed setting unnecessary bit 2016-09-16 12:41:51 +07:00
6 changed files with 157 additions and 100 deletions

2
.gitignore vendored
View file

@ -1,5 +1,3 @@
**/uuids
.vscode/.browse.*
nimsuggest.log

View file

@ -7,16 +7,28 @@ API:
type UUID* = object
## 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
## 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
## Computes hash of the specified UUID.
## Computes hash of the specified ``uuid``.
proc `==`*(x, y: UUID): bool
## Returns true when the specified UUIDs are equal, false otherwise.
proc genUUID*(): UUID =
proc isZero*(uuid: UUID): bool
## Returns ``true`` when the ``uuid`` is zero (not set), ``false`` otherwise.
proc genUUID*(): UUID
## Returns a random (v4) UUID.
## Uses a thread-local cryptographically secure PRNG (ISAAC) seeded with
## true random values obtained from OS.

View file

@ -1,87 +0,0 @@
import strutils, hashes
import isaac
import urandom
type
UUID* = object
## 128-bit UUID compliant with RFC-4122
mostSigBits: int64
leastSigBits: int64
template toHex(s: string, start: Natural,
x: BiggestInt, len: Positive) =
const HexChars = "0123456789abcdef"
var n = x
for j in countdown(len - 1, 0):
s[start + j] = HexChars[n and 0xF]
n = n shr 4
# handle negative overflow
if n == 0 and x < 0: n = -1
proc `$`*(uuid: UUID): string =
## Returns a string representation of the UUID in canonical form.
result = newString(36)
toHex(result, 0, uuid.mostSigBits shr 32, 8)
result[8] = '-'
toHex(result, 9, uuid.mostSigBits shr 16, 4)
result[13] = '-'
toHex(result, 14, uuid.mostSigBits, 4)
result[18] = '-'
toHex(result, 19, uuid.leastSigBits shr 48, 4)
result[23] = '-'
toHex(result, 24, uuid.leastSigBits, 12)
proc hash*(uuid: UUID): Hash =
## Computes hash of the specified UUID.
result = uuid.mostSigBits.hash() !& uuid.leastSigBits.hash()
result = !$result
proc `==`*(x, y: UUID): bool =
## Returns ``true`` when the specified UUIDs are equal, ``false`` otherwise.
x.mostSigBits == y.mostSigBits and x.leastSigBits == y.leastSigBits
var rand {.threadvar.}: IsaacGenerator
proc genUUID*(): UUID =
## Returns a random (v4) UUID.
## Uses a thread-local cryptographically secure PRNG (ISAAC) seeded with
## true random values obtained from OS.
if rand == nil:
var seed = cast[array[256, uint32]](urandom(1024))
rand = newIsaacGenerator(seed)
result.mostSigBits = cast[int64]((rand.nextU32().uint64 shl 32) or rand.nextU32())
result.leastSigBits = cast[int64]((rand.nextU32().uint64 shl 32) or rand.nextU32())
# set version to 4
result.mostSigBits = (result.mostSigBits and 0xFFFFFFFFFFFF0FFF'i64) or
0x0000000000004000'i64
# set IETF variant
result.leastSigBits = (result.leastSigBits and 0x3FFFFFFFFFFFFFFF'i64) or
0x8000000000004000'i64
proc parseUUID*(s: string): UUID {.raises: [ValueError].} =
## Converts string representation of an UUID to UUID object.
## Raises ``ValueError`` if invalid format is provided.
let parts = s.split('-')
if parts.len != 5:
raise newException(ValueError,
"UUID must consist of 5 parts separated with `-`")
var mostSigBits: int64 = parseHexInt(parts[0])
mostSigBits = mostSigBits shl 16
mostSigBits = mostSigBits or parseHexInt(parts[1])
mostSigBits = mostSigBits shl 16
mostSigBits = mostSigBits or parseHexInt(parts[2])
var leastSigBits: int64 = parseHexInt(parts[3])
leastSigBits = leastSigBits shl 48
leastSigBits = leastSigBits or parseHexInt(parts[4])
result = UUID(mostSigBits: mostSigBits, leastSigBits: leastSigBits)
when isMainModule:
let uuid = genUUID()
let uuidStr = $uuid
assert(uuidStr.len == 36)
assert(uuidStr[14] == '4') # version
assert(uuidStr[19] in {'8', '9', 'a', 'b'}) # variant (2 bits)
assert(uuidStr.parseUUID() == uuid)
assert(uuidStr.parseUUID().hash() == uuid.hash())

133
uuids.nim Normal file
View file

@ -0,0 +1,133 @@
import strutils, hashes
import isaac
import uuids/urandom
type
UUID* = object
## 128-bit UUID compliant with RFC-4122
mostSigBits: int64
leastSigBits: int64
template toHex(s: string, start: Natural,
x: BiggestInt, len: Positive) =
const HexChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f']
var n = x
for j in countdown(len - 1, 0):
s[start + j] = HexChars[int(n and 0xF)]
n = n shr 4
# handle negative overflow
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 =
## Returns a string representation of the ``uuid`` in canonical form.
result = newString(36)
toHex(result, 0, uuid.mostSigBits shr 32, 8)
result[8] = '-'
toHex(result, 9, uuid.mostSigBits shr 16, 4)
result[13] = '-'
toHex(result, 14, uuid.mostSigBits, 4)
result[18] = '-'
toHex(result, 19, uuid.leastSigBits shr 48, 4)
result[23] = '-'
toHex(result, 24, uuid.leastSigBits, 12)
proc hash*(uuid: UUID): Hash =
## Computes hash of the specified ``uuid``.
result = uuid.mostSigBits.hash() !& uuid.leastSigBits.hash()
result = !$result
proc `==`*(x, y: UUID): bool =
## Returns ``true`` when the specified UUIDs are equal, ``false`` otherwise.
x.mostSigBits == y.mostSigBits and x.leastSigBits == y.leastSigBits
proc isZero*(uuid: UUID): bool =
## Returns ``true`` when the ``uuid`` is zero (not set), ``false`` otherwise.
uuid.mostSigBits == 0'i64 and uuid.leastSigBits == 0'i64
var rand {.threadvar.}: IsaacGenerator
proc genUUID*(): UUID =
## Returns a random (v4) UUID.
## Uses a thread-local cryptographically secure PRNG (ISAAC) seeded with
## true random values obtained from OS.
if rand == nil:
var seed = cast[array[256, uint32]](urandom(1024))
rand = newIsaacGenerator(seed)
result.mostSigBits = cast[int64]((rand.nextU32().uint64 shl 32) or rand.nextU32())
result.leastSigBits = cast[int64]((rand.nextU32().uint64 shl 32) or rand.nextU32())
# set version to 4
result.mostSigBits = (result.mostSigBits and 0xFFFFFFFFFFFF0FFF'i64) or
0x0000000000004000'i64
# set IETF variant
result.leastSigBits = (result.leastSigBits and 0x3FFFFFFFFFFFFFFF'i64) or
0x8000000000000000'i64
proc parseUUID*(s: string): UUID {.raises: [ValueError].} =
## Converts string representation of an UUID to UUID object.
## Raises ``ValueError`` if invalid format is provided.
let parts = s.split('-')
if parts.len != 5:
raise newException(ValueError,
"UUID must consist of 5 parts separated with `-`")
var mostSigBits: int64 = uuidsParseHexInt(parts[0], 8)
mostSigBits = mostSigBits shl 16
mostSigBits = mostSigBits or uuidsParseHexInt(parts[1], 4)
mostSigBits = mostSigBits shl 16
mostSigBits = mostSigBits or uuidsParseHexInt(parts[2], 4)
var leastSigBits: int64 = uuidsParseHexInt(parts[3], 4)
leastSigBits = leastSigBits shl 48
leastSigBits = leastSigBits or uuidsParseHexInt(parts[4], 12)
result = UUID(mostSigBits: mostSigBits, leastSigBits: leastSigBits)
when isMainModule:
var uuid: UUID
assert(uuid.isZero())
for i in 1..100:
uuid = genUUID()
let uuidStr = $uuid
assert(uuidStr.len == 36)
assert(uuidStr[14] == '4') # version
assert(uuidStr[19] in {'8', '9', 'a', 'b'}) # variant (2 bits)
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))

View file

@ -1,10 +1,9 @@
[Package]
name: "uuids"
version: "0.1.1"
version: "0.1.11"
author: "Xored Software, Inc."
description: "UUID library"
license: "MIT"
srcDir: "src"
[Deps]
requires: "isaac >= 0.1.0"
requires: "isaac >= 0.1.3"

View file

@ -5,8 +5,10 @@ when defined(windows):
type ULONG_PTR = int
type HCRYPTPROV = ULONG_PTR
var PROV_RSA_FULL {.importc, header: "<windows.h>".}: DWORD
var CRYPT_VERIFYCONTEXT {.importc, header: "<windows.h>".}: DWORD
var PROV_RSA_FULL {.importc, header: """#include <windows.h>
#include <wincrypt.h>""".}: DWORD
var CRYPT_VERIFYCONTEXT {.importc, header: """#include <windows.h>
#include <wincrypt.h>""".}: DWORD
{.push, stdcall, dynlib: "Advapi32.dll".}
@ -14,16 +16,16 @@ when defined(windows):
proc CryptAcquireContext(
phProv: ptr HCRYPTPROV, pszContainer: WideCString,
pszProvider: WideCString, dwProvType: DWORD, dwFlags: DWORD
): WinBool {.importc: "CryptAcquireContextW".}
): WINBOOL {.importc: "CryptAcquireContextW".}
else:
proc CryptAcquireContext(
phProv: ptr HCRYPTPROV, pszContainer: cstring, pszProvider: cstring,
dwProvType: DWORD, dwFlags: DWORD
): WinBool {.importc: "CryptAcquireContextA".}
): WINBOOL {.importc: "CryptAcquireContextA".}
proc CryptGenRandom(
hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: pointer
): WinBool {.importc: "CryptGenRandom".}
): WINBOOL {.importc: "CryptGenRandom".}
{.pop.}