First version

This commit is contained in:
Ruslan Mustakov 2016-09-15 20:35:01 +07:00
commit 3951521a16
7 changed files with 231 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
**/uuids
.vscode/.browse.*
nimsuggest.log
**/nimcache/**
.DS_Store

15
.vscode/settings.json vendored Normal file
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,7 @@
[Package]
name: "uuids"
version: "0.1.0"
author: "Xored Software, Inc."
description: "UUID library"
license: "MIT"
srcDir: "src"