First version
This commit is contained in:
commit
3951521a16
7 changed files with 231 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
**/uuids
|
||||
|
||||
.vscode/.browse.*
|
||||
nimsuggest.log
|
||||
|
||||
**/nimcache/**
|
||||
|
||||
.DS_Store
|
||||
15
.vscode/settings.json
vendored
Normal file
15
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"files.exclude": {
|
||||
"**/nimcache/**": true
|
||||
},
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true,
|
||||
"editor.rulers": [80],
|
||||
"editor.renderIndentGuides": true,
|
||||
"files.autoSave": "afterDelay",
|
||||
"nim.licenseString": "# Copyright 2016 Xored Software, Inc.\n\n",
|
||||
"nim.buildOnSave": false,
|
||||
"nim.lintOnSave": true,
|
||||
"nim.project": ["uuids.nim"]
|
||||
}
|
||||
20
LICENSE
Normal file
20
LICENSE
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
uuids - UUID library for Nim
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2016 Xored Software Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
33
README.md
Normal file
33
README.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# uuids
|
||||
|
||||
UUID library for Nim.
|
||||
|
||||
API:
|
||||
```nim
|
||||
type UUID* = object
|
||||
## 128-bit UUID compliant with RFC-4122
|
||||
|
||||
proc `$`*(uuid: UUID): string
|
||||
## Returns a string representation of the UUID in canonical form.
|
||||
|
||||
proc hash*(uuid: UUID): Hash
|
||||
## Computes hash of the specified UUID.
|
||||
|
||||
proc `==`*(x, y: UUID): bool
|
||||
## Returns true when the specified UUIDs are equal, false otherwise.
|
||||
|
||||
proc genUUID*(): UUID =
|
||||
## Returns a random (v4) UUID.
|
||||
## Uses random values obtained from system source (e.g. urandom).
|
||||
## In the future this will use a cryptographically secure PRNG for efficiency.
|
||||
|
||||
proc toUUID*(s: string): UUID {.raises: [ValueError].}
|
||||
## Converts string representation of an UUID to UUID object.
|
||||
## Raises ValueError if invalid format is provided.
|
||||
```
|
||||
|
||||
## License
|
||||
This library is licensed under the MIT license.
|
||||
Read [LICENSE](https://github.com/pragmagic/uuids/blob/master/LICENSE) file for details.
|
||||
|
||||
Copyright (c) 2016 Xored Software, Inc.
|
||||
65
src/urandom.nim
Normal file
65
src/urandom.nim
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Source: https://github.com/BlaXpirit/nim-random/blob/master/src/random/urandom.nim
|
||||
|
||||
when defined(windows):
|
||||
import winlean
|
||||
|
||||
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
|
||||
|
||||
{.push, stdcall, dynlib: "Advapi32.dll".}
|
||||
|
||||
when useWinUnicode:
|
||||
proc CryptAcquireContext(
|
||||
phProv: ptr HCRYPTPROV, pszContainer: WideCString,
|
||||
pszProvider: WideCString, dwProvType: DWORD, dwFlags: DWORD
|
||||
): WinBool {.importc: "CryptAcquireContextW".}
|
||||
else:
|
||||
proc CryptAcquireContext(
|
||||
phProv: ptr HCRYPTPROV, pszContainer: cstring, pszProvider: cstring,
|
||||
dwProvType: DWORD, dwFlags: DWORD
|
||||
): WinBool {.importc: "CryptAcquireContextA".}
|
||||
|
||||
proc CryptGenRandom(
|
||||
hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: pointer
|
||||
): WinBool {.importc: "CryptGenRandom".}
|
||||
|
||||
{.pop.}
|
||||
|
||||
var cryptProv: HCRYPTPROV = 0
|
||||
|
||||
proc urandomInit() =
|
||||
let success = CryptAcquireContext(
|
||||
addr cryptProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT
|
||||
)
|
||||
if success == 0:
|
||||
raise newException(OSError, "Call to CryptAcquireContext failed")
|
||||
|
||||
template urandomImpl() =
|
||||
when defined(windows):
|
||||
if cryptProv == 0:
|
||||
urandomInit()
|
||||
|
||||
let success = CryptGenRandom(cryptProv, DWORD(size), addr result[0])
|
||||
if success == 0:
|
||||
raise newException(OSError, "Call to CryptGenRandom failed")
|
||||
|
||||
else:
|
||||
var file: File
|
||||
if not file.open("/dev/urandom"):
|
||||
raise newException(OSError, "/dev/urandom is not available")
|
||||
try:
|
||||
var index = 0
|
||||
while index < size:
|
||||
let bytesRead = file.readBuffer(addr result[index], size-index)
|
||||
if bytesRead <= 0:
|
||||
raise newException(OSError, "Can't read enough bytes from /dev/urandom")
|
||||
index += bytesRead
|
||||
finally:
|
||||
file.close()
|
||||
|
||||
proc urandom*(size: static[Natural]): array[size, uint8] =
|
||||
## Returns an ``array`` of random integers ``0 <= n < 256`` provided by
|
||||
## the operating system's cryptographic source.
|
||||
urandomImpl()
|
||||
83
src/uuids.nim
Normal file
83
src/uuids.nim
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import strutils, hashes
|
||||
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
|
||||
|
||||
proc genUUID*(): UUID =
|
||||
## Returns a random (v4) UUID.
|
||||
## Uses random values obtained from system source (e.g. urandom).
|
||||
## In the future this will use a cryptographically secure PRNG for efficiency.
|
||||
var randBytes: array[16, uint8] = urandom(16)
|
||||
copyMem(addr result.mostSigBits, addr randBytes[0], 8)
|
||||
copyMem(addr result.leastSigBits, addr randBytes[8], 8)
|
||||
|
||||
# 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 toUUID*(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.toUUID() == uuid)
|
||||
assert(uuidStr.toUUID().hash() == uuid.hash())
|
||||
7
uuids.nimble
Normal file
7
uuids.nimble
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[Package]
|
||||
name: "uuids"
|
||||
version: "0.1.0"
|
||||
author: "Xored Software, Inc."
|
||||
description: "UUID library"
|
||||
license: "MIT"
|
||||
srcDir: "src"
|
||||
Loading…
Add table
Add a link
Reference in a new issue