mirror of
https://github.com/status-im/nim-stew.git
synced 2025-01-09 11:45:42 +00:00
Merge branch 'intops' of github.com:status-im/nim-stew into intops
This commit is contained in:
commit
422b1540de
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
|||||||
cpu: amd64
|
cpu: amd64
|
||||||
#- os: windows
|
#- os: windows
|
||||||
#cpu: i386
|
#cpu: i386
|
||||||
branch: [version-1-2, version-1-4, version-1-6, devel]
|
branch: [version-1-2, version-1-4, version-1-6, version-2-0, devel]
|
||||||
include:
|
include:
|
||||||
- target:
|
- target:
|
||||||
os: linux
|
os: linux
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
# stew - status e-something w-something
|
# stew - status e-something w-something
|
||||||
|
|
||||||
[![Build Status (Travis)](https://img.shields.io/travis/status-im/nim-stew/master.svg?label=Linux%20/%20macOS "Linux/macOS build status (Travis)")](https://travis-ci.org/status-im/nim-stew)
|
|
||||||
[![Windows build status (Appveyor)](https://img.shields.io/appveyor/ci/nimbus/nim-stew/master.svg?label=Windows "Windows build status (Appveyor)")](https://ci.appveyor.com/project/nimbus/nim-stew)
|
|
||||||
[![License: Apache](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
|
[![License: Apache](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
|
||||||
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
|
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
|
||||||
![Stability: experimental](https://img.shields.io/badge/stability-experimental-orange.svg)
|
![Stability: experimental](https://img.shields.io/badge/stability-experimental-orange.svg)
|
||||||
![Github action](https://github.com/status-im/nim-stew/workflows/nim-stew%20CI/badge.svg)
|
![Github action](https://github.com/status-im/nim-stew/workflows/CI/badge.svg)
|
||||||
|
|
||||||
`stew` is collection of utilities, std library extensions and budding libraries
|
`stew` is collection of utilities, std library extensions and budding libraries
|
||||||
that are frequently used at Status, but are too small to deserve their own
|
that are frequently used at Status, but are too small to deserve their own
|
||||||
|
@ -32,5 +32,7 @@ task test, "Run all tests":
|
|||||||
for args in [
|
for args in [
|
||||||
"--threads:off",
|
"--threads:off",
|
||||||
"--threads:on -d:nimTypeNames",
|
"--threads:on -d:nimTypeNames",
|
||||||
"--threads:on -d:noIntrinsicsBitOpts -d:noIntrinsicsEndians"
|
"--threads:on -d:noIntrinsicsBitOpts -d:noIntrinsicsEndians"]:
|
||||||
]: run args, "tests/all_tests"
|
run args, "tests/all_tests"
|
||||||
|
if (NimMajor, NimMinor) > (1, 6):
|
||||||
|
run args & " --mm:refc", "tests/all_tests"
|
||||||
|
@ -15,7 +15,7 @@ func assignImpl[T](tgt: var openArray[T], src: openArray[T]) =
|
|||||||
mixin assign
|
mixin assign
|
||||||
when supportsCopyMem(T):
|
when supportsCopyMem(T):
|
||||||
if tgt.len > 0:
|
if tgt.len > 0:
|
||||||
copyMem(addr tgt[0], unsafeAddr src[0], sizeof(tgt[0]) * tgt.len)
|
moveMem(addr tgt[0], unsafeAddr src[0], sizeof(tgt[0]) * tgt.len)
|
||||||
else:
|
else:
|
||||||
for i in 0..<tgt.len:
|
for i in 0..<tgt.len:
|
||||||
assign(tgt[i], src[i])
|
assign(tgt[i], src[i])
|
||||||
@ -52,7 +52,7 @@ func assign*[T](tgt: var T, src: T) =
|
|||||||
when sizeof(src) <= sizeof(int):
|
when sizeof(src) <= sizeof(int):
|
||||||
tgt = src
|
tgt = src
|
||||||
else:
|
else:
|
||||||
copyMem(addr tgt, unsafeAddr src, sizeof(tgt))
|
moveMem(addr tgt, unsafeAddr src, sizeof(tgt))
|
||||||
elif T is object|tuple:
|
elif T is object|tuple:
|
||||||
for t, s in fields(tgt, src):
|
for t, s in fields(tgt, src):
|
||||||
when supportsCopyMem(type s) and sizeof(s) <= sizeof(int) * 2:
|
when supportsCopyMem(type s) and sizeof(s) <= sizeof(int) * 2:
|
||||||
|
@ -170,13 +170,13 @@ proc decode*[T: byte|char](btype: typedesc[Base58C], instr: openArray[T],
|
|||||||
result = Base58Status.Incorrect
|
result = Base58Status.Incorrect
|
||||||
return
|
return
|
||||||
let ch = alphabet.decode[int8(instr[i])]
|
let ch = alphabet.decode[int8(instr[i])]
|
||||||
if ch == -1:
|
if ch < 0:
|
||||||
outlen = 0
|
outlen = 0
|
||||||
result = Base58Status.Incorrect
|
result = Base58Status.Incorrect
|
||||||
return
|
return
|
||||||
var c = cast[uint32](ch)
|
var c = uint32(ch)
|
||||||
for j in countdown(size - 1, 0):
|
for j in countdown(size - 1, 0):
|
||||||
let t = cast[uint64](buffer[j]) * 58 + c
|
let t = uint64(buffer[j]) * 58 + c
|
||||||
c = cast[uint32]((t and 0x3F_0000_0000'u64) shr 32)
|
c = cast[uint32]((t and 0x3F_0000_0000'u64) shr 32)
|
||||||
buffer[j] = cast[uint32](t and 0xFFFF_FFFF'u32)
|
buffer[j] = cast[uint32](t and 0xFFFF_FFFF'u32)
|
||||||
if c != 0:
|
if c != 0:
|
||||||
|
@ -82,7 +82,7 @@ func log2truncNim(x: uint8|uint16|uint32): int =
|
|||||||
v = v or v shr 4
|
v = v or v shr 4
|
||||||
v = v or v shr 8
|
v = v or v shr 8
|
||||||
v = v or v shr 16
|
v = v or v shr 16
|
||||||
cast[int](lookup[uint32(v * 0x07C4ACDD'u32) shr 27])
|
int(lookup[uint32(v * 0x07C4ACDD'u32) shr 27])
|
||||||
|
|
||||||
func log2truncNim(x: uint64): int =
|
func log2truncNim(x: uint64): int =
|
||||||
## Quickly find the log base 2 of a 64-bit integer.
|
## Quickly find the log base 2 of a 64-bit integer.
|
||||||
@ -169,9 +169,9 @@ when (defined(gcc) or defined(llvm_gcc) or defined(clang)) and useBuiltins:
|
|||||||
cast[int](builtin_ffs(cast[cint](x.cuint)))
|
cast[int](builtin_ffs(cast[cint](x.cuint)))
|
||||||
|
|
||||||
func log2truncBuiltin(v: uint8|uint16|uint32): int =
|
func log2truncBuiltin(v: uint8|uint16|uint32): int =
|
||||||
cast[int](31 - cast[cuint](builtin_clz(v.uint32)))
|
int(31 - cast[cuint](builtin_clz(v.uint32)))
|
||||||
func log2truncBuiltin(v: uint64): int =
|
func log2truncBuiltin(v: uint64): int =
|
||||||
cast[int](63 - cast[cuint](builtin_clzll(v)))
|
int(63 - cast[cuint](builtin_clzll(v)))
|
||||||
|
|
||||||
elif defined(vcc) and useBuiltins:
|
elif defined(vcc) and useBuiltins:
|
||||||
const arch64 = sizeof(int) == 8
|
const arch64 = sizeof(int) == 8
|
||||||
|
@ -18,14 +18,16 @@ export arrayops.`&`, arrayops.initArrayWith, arrayops.`[]=`
|
|||||||
|
|
||||||
when (NimMajor, NimMinor) < (1, 4):
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
{.push raises: [Defect].}
|
{.push raises: [Defect].}
|
||||||
|
{.pragma: hexRaises, raises: [Defect, ValueError].}
|
||||||
else:
|
else:
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
{.pragma: hexRaises, raises: [ValueError].}
|
||||||
|
|
||||||
########################################################################################################
|
########################################################################################################
|
||||||
##################################### Hex utilities ################################################
|
##################################### Hex utilities ################################################
|
||||||
|
|
||||||
proc readHexChar*(c: char): byte
|
proc readHexChar*(c: char): byte
|
||||||
{.raises: [ValueError, Defect], noSideEffect, inline.} =
|
{.hexRaises, noSideEffect, inline.} =
|
||||||
## Converts an hex char to a byte
|
## Converts an hex char to a byte
|
||||||
case c
|
case c
|
||||||
of '0'..'9': result = byte(ord(c) - ord('0'))
|
of '0'..'9': result = byte(ord(c) - ord('0'))
|
||||||
@ -42,7 +44,7 @@ template skip0xPrefix(hexStr: openArray[char]): int =
|
|||||||
|
|
||||||
func hexToByteArrayImpl(
|
func hexToByteArrayImpl(
|
||||||
hexStr: openArray[char], output: var openArray[byte], fromIdx, toIdx: int):
|
hexStr: openArray[char], output: var openArray[byte], fromIdx, toIdx: int):
|
||||||
int {.raises: [ValueError, Defect].} =
|
int {.hexRaises.} =
|
||||||
var sIdx = skip0xPrefix(hexStr)
|
var sIdx = skip0xPrefix(hexStr)
|
||||||
# Fun with closed intervals
|
# Fun with closed intervals
|
||||||
doAssert fromIdx >= 0 and
|
doAssert fromIdx >= 0 and
|
||||||
@ -65,7 +67,7 @@ func hexToByteArrayImpl(
|
|||||||
|
|
||||||
func hexToByteArray*(
|
func hexToByteArray*(
|
||||||
hexStr: openArray[char], output: var openArray[byte], fromIdx, toIdx: int)
|
hexStr: openArray[char], output: var openArray[byte], fromIdx, toIdx: int)
|
||||||
{.raises: [ValueError, Defect].} =
|
{.hexRaises.} =
|
||||||
## Read hex-encoded data from `hexStr[mapHex(fromIdx..toIdx)]` and store
|
## Read hex-encoded data from `hexStr[mapHex(fromIdx..toIdx)]` and store
|
||||||
## corresponding bytes in `output[fromIdx..toIdx]` where `mapHex` takes into
|
## corresponding bytes in `output[fromIdx..toIdx]` where `mapHex` takes into
|
||||||
## account stripped characters.
|
## account stripped characters.
|
||||||
@ -80,7 +82,7 @@ func hexToByteArray*(
|
|||||||
discard hexToByteArrayImpl(hexStr, output, fromIdx, toIdx)
|
discard hexToByteArrayImpl(hexStr, output, fromIdx, toIdx)
|
||||||
|
|
||||||
func hexToByteArray*(hexStr: openArray[char], output: var openArray[byte])
|
func hexToByteArray*(hexStr: openArray[char], output: var openArray[byte])
|
||||||
{.raises: [ValueError, Defect].} =
|
{.hexRaises.} =
|
||||||
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
||||||
## `output`.
|
## `output`.
|
||||||
##
|
##
|
||||||
@ -92,7 +94,7 @@ func hexToByteArray*(hexStr: openArray[char], output: var openArray[byte])
|
|||||||
hexToByteArray(hexStr, output, 0, output.high)
|
hexToByteArray(hexStr, output, 0, output.high)
|
||||||
|
|
||||||
func hexToByteArray*[N: static[int]](hexStr: openArray[char]): array[N, byte]
|
func hexToByteArray*[N: static[int]](hexStr: openArray[char]): array[N, byte]
|
||||||
{.raises: [ValueError, Defect], noinit.}=
|
{.hexRaises, noinit.}=
|
||||||
## Read hex-encoded data from `hexStr` returning an array of N bytes.
|
## Read hex-encoded data from `hexStr` returning an array of N bytes.
|
||||||
##
|
##
|
||||||
## * `0x`/`0X` is stripped if present
|
## * `0x`/`0X` is stripped if present
|
||||||
@ -103,7 +105,7 @@ func hexToByteArray*[N: static[int]](hexStr: openArray[char]): array[N, byte]
|
|||||||
hexToByteArray(hexStr, result)
|
hexToByteArray(hexStr, result)
|
||||||
|
|
||||||
func hexToByteArray*(hexStr: openArray[char], N: static int): array[N, byte]
|
func hexToByteArray*(hexStr: openArray[char], N: static int): array[N, byte]
|
||||||
{.raises: [ValueError, Defect], noinit.}=
|
{.hexRaises, noinit.}=
|
||||||
## Read hex-encoded data from `hexStr` returning an array of N bytes.
|
## Read hex-encoded data from `hexStr` returning an array of N bytes.
|
||||||
##
|
##
|
||||||
## * `0x`/`0X` is stripped if present
|
## * `0x`/`0X` is stripped if present
|
||||||
@ -114,7 +116,7 @@ func hexToByteArray*(hexStr: openArray[char], N: static int): array[N, byte]
|
|||||||
hexToByteArray(hexStr, result)
|
hexToByteArray(hexStr, result)
|
||||||
|
|
||||||
func hexToByteArrayStrict*(hexStr: openArray[char], output: var openArray[byte])
|
func hexToByteArrayStrict*(hexStr: openArray[char], output: var openArray[byte])
|
||||||
{.raises: [ValueError, Defect].} =
|
{.hexRaises.} =
|
||||||
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
||||||
## `output`.
|
## `output`.
|
||||||
##
|
##
|
||||||
@ -126,7 +128,7 @@ func hexToByteArrayStrict*(hexStr: openArray[char], output: var openArray[byte])
|
|||||||
raise (ref ValueError)(msg: "hex string too long")
|
raise (ref ValueError)(msg: "hex string too long")
|
||||||
|
|
||||||
func hexToByteArrayStrict*[N: static[int]](hexStr: openArray[char]): array[N, byte]
|
func hexToByteArrayStrict*[N: static[int]](hexStr: openArray[char]): array[N, byte]
|
||||||
{.raises: [ValueError, Defect], noinit, inline.}=
|
{.hexRaises, noinit, inline.}=
|
||||||
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
||||||
## `output`.
|
## `output`.
|
||||||
##
|
##
|
||||||
@ -137,7 +139,7 @@ func hexToByteArrayStrict*[N: static[int]](hexStr: openArray[char]): array[N, by
|
|||||||
hexToByteArrayStrict(hexStr, result)
|
hexToByteArrayStrict(hexStr, result)
|
||||||
|
|
||||||
func hexToByteArrayStrict*(hexStr: openArray[char], N: static int): array[N, byte]
|
func hexToByteArrayStrict*(hexStr: openArray[char], N: static int): array[N, byte]
|
||||||
{.raises: [ValueError, Defect], noinit, inline.}=
|
{.hexRaises, noinit, inline.}=
|
||||||
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
||||||
## `output`.
|
## `output`.
|
||||||
##
|
##
|
||||||
@ -148,7 +150,7 @@ func hexToByteArrayStrict*(hexStr: openArray[char], N: static int): array[N, byt
|
|||||||
hexToByteArrayStrict(hexStr, result)
|
hexToByteArrayStrict(hexStr, result)
|
||||||
|
|
||||||
func fromHex*[N](A: type array[N, byte], hexStr: string): A
|
func fromHex*[N](A: type array[N, byte], hexStr: string): A
|
||||||
{.raises: [ValueError, Defect], noinit, inline.}=
|
{.hexRaises, noinit, inline.}=
|
||||||
## Read hex-encoded data from `hexStr` returning an array of N bytes.
|
## Read hex-encoded data from `hexStr` returning an array of N bytes.
|
||||||
##
|
##
|
||||||
## * `0x`/`0X` is stripped if present
|
## * `0x`/`0X` is stripped if present
|
||||||
@ -159,7 +161,7 @@ func fromHex*[N](A: type array[N, byte], hexStr: string): A
|
|||||||
hexToByteArray(hexStr, result)
|
hexToByteArray(hexStr, result)
|
||||||
|
|
||||||
func hexToPaddedByteArray*[N: static[int]](hexStr: string): array[N, byte]
|
func hexToPaddedByteArray*[N: static[int]](hexStr: string): array[N, byte]
|
||||||
{.raises: [ValueError, Defect].} =
|
{.hexRaises.} =
|
||||||
## Read a hex string and store it in a byte array `output`.
|
## Read a hex string and store it in a byte array `output`.
|
||||||
## The string may be shorter than the byte array.
|
## The string may be shorter than the byte array.
|
||||||
## No "endianness" reordering is done.
|
## No "endianness" reordering is done.
|
||||||
@ -188,7 +190,7 @@ func hexToPaddedByteArray*[N: static[int]](hexStr: string): array[N, byte]
|
|||||||
bIdx += shift shr 2
|
bIdx += shift shr 2
|
||||||
|
|
||||||
func hexToSeqByte*(hexStr: string): seq[byte]
|
func hexToSeqByte*(hexStr: string): seq[byte]
|
||||||
{.raises: [ValueError, Defect].} =
|
{.hexRaises.} =
|
||||||
## Read an hex string and store it in a sequence of bytes. No "endianness" reordering is done.
|
## Read an hex string and store it in a sequence of bytes. No "endianness" reordering is done.
|
||||||
if (hexStr.len and 1) == 1:
|
if (hexStr.len and 1) == 1:
|
||||||
raise (ref ValueError)(msg: "hex string must have even length")
|
raise (ref ValueError)(msg: "hex string must have even length")
|
||||||
|
@ -19,9 +19,13 @@ elif sizeof(int) == 4:
|
|||||||
type
|
type
|
||||||
AnyItem* = byte|char|int8|uint16|int16|uint32|int32|uint|int
|
AnyItem* = byte|char|int8|uint16|int16|uint32|int32|uint|int
|
||||||
|
|
||||||
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
else:
|
||||||
|
{.push raises: [].}
|
||||||
|
|
||||||
proc isEqual*[A: AnyItem, B: AnyItem](c: typedesc[CT], a: openArray[A],
|
proc isEqual*[A: AnyItem, B: AnyItem](c: typedesc[CT], a: openArray[A],
|
||||||
b: openArray[B]): bool {.
|
b: openArray[B]): bool =
|
||||||
raises: [Defect] .} =
|
|
||||||
## Perform constant time comparison of two arrays ``a`` and ``b``.
|
## Perform constant time comparison of two arrays ``a`` and ``b``.
|
||||||
##
|
##
|
||||||
## Please note that it only makes sense to compare arrays of the same length.
|
## Please note that it only makes sense to compare arrays of the same length.
|
||||||
|
@ -112,7 +112,7 @@ import
|
|||||||
"."/[results, sorted_set]
|
"."/[results, sorted_set]
|
||||||
|
|
||||||
when (NimMajor, NimMinor) < (1, 4):
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
{.push raises: [Defect].}
|
{.push raises: [Defect, CatchableError].}
|
||||||
else:
|
else:
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
|
@ -325,13 +325,17 @@ proc shift*[K,V](rq: var KeyedQueue[K,V]): Result[KeyedQueuePair[K,V],void] =
|
|||||||
##
|
##
|
||||||
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
||||||
## item returned and deleted is the *left-most* item.
|
## item returned and deleted is the *left-most* item.
|
||||||
|
type T = KeyedQueuePair[K,V]
|
||||||
if 0 < rq.tab.len:
|
if 0 < rq.tab.len:
|
||||||
noKeyError("shift"):
|
noKeyError("shift"):
|
||||||
let kvp = KeyedQueuePair[K,V](
|
let kvp = KeyedQueuePair[K,V](
|
||||||
key: rq.kFirst,
|
key: rq.kFirst,
|
||||||
data: rq.tab[rq.kFirst].data)
|
data: rq.tab[rq.kFirst].data)
|
||||||
rq.shiftImpl
|
rq.shiftImpl
|
||||||
return ok(KeyedQueuePair[K,V](kvp))
|
when kvp is T:
|
||||||
|
return ok(kvp)
|
||||||
|
else:
|
||||||
|
return ok(T(kvp))
|
||||||
err()
|
err()
|
||||||
|
|
||||||
proc shiftKey*[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc shiftKey*[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
||||||
@ -355,13 +359,17 @@ proc pop*[K,V](rq: var KeyedQueue[K,V]): Result[KeyedQueuePair[K,V],void] =
|
|||||||
##
|
##
|
||||||
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
## Using the notation introduced with `rq.append` and `rq.prepend`, the
|
||||||
## item returned and deleted is the *right-most* item.
|
## item returned and deleted is the *right-most* item.
|
||||||
|
type T = KeyedQueuePair[K,V]
|
||||||
if 0 < rq.tab.len:
|
if 0 < rq.tab.len:
|
||||||
noKeyError("pop"):
|
noKeyError("pop"):
|
||||||
let kvp = KeyedQueuePair[K,V](
|
let kvp = KeyedQueuePair[K,V](
|
||||||
key: rq.kLast,
|
key: rq.kLast,
|
||||||
data: rq.tab[rq.kLast].data)
|
data: rq.tab[rq.kLast].data)
|
||||||
rq.popImpl
|
rq.popImpl
|
||||||
return ok(KeyedQueuePair[K,V](kvp))
|
when kvp is T:
|
||||||
|
return ok(kvp)
|
||||||
|
else:
|
||||||
|
return ok(T(kvp))
|
||||||
err()
|
err()
|
||||||
|
|
||||||
proc popKey*[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
proc popKey*[K,V](rq: var KeyedQueue[K,V]): Result[K,void] =
|
||||||
@ -450,7 +458,7 @@ proc eq*[K,V](rq: var KeyedQueue[K,V]; key: K): Result[V,void] =
|
|||||||
return ok(rq.tab[key].data)
|
return ok(rq.tab[key].data)
|
||||||
|
|
||||||
proc `[]`*[K,V](rq: var KeyedQueue[K,V]; key: K): V
|
proc `[]`*[K,V](rq: var KeyedQueue[K,V]; key: K): V
|
||||||
{.gcsafe,raises: [Defect,KeyError].} =
|
{.gcsafe,raises: [KeyError].} =
|
||||||
## This function provides a simplified version of the `eq()` function with
|
## This function provides a simplified version of the `eq()` function with
|
||||||
## table semantics. Note that this finction throws a `KeyError` exception
|
## table semantics. Note that this finction throws a `KeyError` exception
|
||||||
## unless the argument `key` exists in the queue.
|
## unless the argument `key` exists in the queue.
|
||||||
|
@ -31,6 +31,11 @@ type
|
|||||||
kQVfyPrvNxtExpected
|
kQVfyPrvNxtExpected
|
||||||
kQVfyFirstExpected
|
kQVfyFirstExpected
|
||||||
|
|
||||||
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
else:
|
||||||
|
{.push raises: [].}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public functions, debugging
|
# Public functions, debugging
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@ -50,7 +55,7 @@ proc `$`*[K,V](item: KeyedQueueItem[K,V]): string =
|
|||||||
"(" & $item.value & ", link[" & $item.prv & "," & $item.kNxt & "])"
|
"(" & $item.value & ", link[" & $item.prv & "," & $item.kNxt & "])"
|
||||||
|
|
||||||
proc verify*[K,V](rq: var KeyedQueue[K,V]): Result[void,(K,V,KeyedQueueInfo)]
|
proc verify*[K,V](rq: var KeyedQueue[K,V]): Result[void,(K,V,KeyedQueueInfo)]
|
||||||
{.gcsafe,raises: [Defect,KeyError].} =
|
{.gcsafe,raises: [KeyError].} =
|
||||||
## Check for consistency. Returns an error unless the argument
|
## Check for consistency. Returns an error unless the argument
|
||||||
## queue `rq` is consistent.
|
## queue `rq` is consistent.
|
||||||
let tabLen = rq.tab.len
|
let tabLen = rq.tab.len
|
||||||
|
@ -104,7 +104,7 @@ func checkedEnumAssign*[E: enum, I: SomeInteger](res: var E, value: I): bool =
|
|||||||
if value notin E:
|
if value notin E:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
res = E value
|
res = cast[E](value)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
func isZeroMemory*[T](x: T): bool =
|
func isZeroMemory*[T](x: T): bool =
|
||||||
|
@ -445,7 +445,7 @@ template isOk*(self: Result): bool = self.oResultPrivate
|
|||||||
template isErr*(self: Result): bool = not self.oResultPrivate
|
template isErr*(self: Result): bool = not self.oResultPrivate
|
||||||
|
|
||||||
when not defined(nimHasEffectsOfs):
|
when not defined(nimHasEffectsOfs):
|
||||||
template effectsOf(f: untyped) {.pragma.}
|
template effectsOf(f: untyped) {.pragma, used.}
|
||||||
|
|
||||||
func map*[T0, E, T1](
|
func map*[T0, E, T1](
|
||||||
self: Result[T0, E], f: proc(x: T0): T1):
|
self: Result[T0, E], f: proc(x: T0): T1):
|
||||||
@ -845,15 +845,85 @@ template unsafeError*[T](self: Result[T, void]) =
|
|||||||
## See also: `unsafeGet`
|
## See also: `unsafeGet`
|
||||||
assert not self.oResultPrivate # Emulate field access defect in debug builds
|
assert not self.oResultPrivate # Emulate field access defect in debug builds
|
||||||
|
|
||||||
|
func optValue*[T, E](self: Result[T, E]): Opt[T] =
|
||||||
|
## Return the value of a Result as an Opt, or none if Result is an error
|
||||||
|
if self.oResultPrivate:
|
||||||
|
Opt.some(self.vResultPrivate)
|
||||||
|
else:
|
||||||
|
Opt.none(T)
|
||||||
|
|
||||||
|
func optError*[T, E](self: Result[T, E]): Opt[E] =
|
||||||
|
## Return the error of a Result as an Opt, or none if Result is a value
|
||||||
|
if self.oResultPrivate:
|
||||||
|
Opt.none(E)
|
||||||
|
else:
|
||||||
|
Opt.some(self.eResultPrivate)
|
||||||
|
|
||||||
# Alternative spellings for get
|
# Alternative spellings for get
|
||||||
template value*[T, E](self: Result[T, E]): T = self.get()
|
template value*[T, E](self: Result[T, E]): T = self.get()
|
||||||
template value*[T: not void, E](self: var Result[T, E]): var T = self.get()
|
template value*[T: not void, E](self: var Result[T, E]): var T = self.get()
|
||||||
|
|
||||||
|
template isOkOr*[T, E](self: Result[T, E], body: untyped) =
|
||||||
|
## Evaluate `body` iff result has been assigned an error
|
||||||
|
## `body` is evaluated lazily.
|
||||||
|
##
|
||||||
|
## Example:
|
||||||
|
## ```
|
||||||
|
## let
|
||||||
|
## v = Result[int, string].err("hello")
|
||||||
|
## x = v.isOkOr: echo "not ok"
|
||||||
|
## # experimental: direct error access using an unqualified `error` symbol
|
||||||
|
## z = v.isOkOr: echo error
|
||||||
|
## ```
|
||||||
|
##
|
||||||
|
## `error` access:
|
||||||
|
##
|
||||||
|
## TODO experimental, might change in the future
|
||||||
|
##
|
||||||
|
## The template contains a shortcut for accessing the error of the result,
|
||||||
|
## it can only be used outside of generic code,
|
||||||
|
## see https://github.com/status-im/nim-stew/issues/161#issuecomment-1397121386
|
||||||
|
|
||||||
|
let s = (self) # TODO avoid copy
|
||||||
|
if not s.oResultPrivate:
|
||||||
|
when E isnot void:
|
||||||
|
template error: E {.used, inject.} = s.eResultPrivate
|
||||||
|
body
|
||||||
|
|
||||||
|
template isErrOr*[T, E](self: Result[T, E], body: untyped) =
|
||||||
|
## Evaluate `body` iff result has been assigned a value
|
||||||
|
## `body` is evaluated lazily.
|
||||||
|
##
|
||||||
|
## Example:
|
||||||
|
## ```
|
||||||
|
## let
|
||||||
|
## v = Result[int, string].err("hello")
|
||||||
|
## x = v.isOkOr: echo "not ok"
|
||||||
|
## # experimental: direct error access using an unqualified `error` symbol
|
||||||
|
## z = v.isOkOr: echo error
|
||||||
|
## ```
|
||||||
|
##
|
||||||
|
## `value` access:
|
||||||
|
##
|
||||||
|
## TODO experimental, might change in the future
|
||||||
|
##
|
||||||
|
## The template contains a shortcut for accessing the value of the result,
|
||||||
|
## it can only be used outside of generic code,
|
||||||
|
## see https://github.com/status-im/nim-stew/issues/161#issuecomment-1397121386
|
||||||
|
|
||||||
|
let s = (self) # TODO avoid copy
|
||||||
|
if s.oResultPrivate:
|
||||||
|
when T isnot void:
|
||||||
|
template value: T {.used, inject.} = s.vResultPrivate
|
||||||
|
body
|
||||||
|
|
||||||
template valueOr*[T: not void, E](self: Result[T, E], def: untyped): T =
|
template valueOr*[T: not void, E](self: Result[T, E], def: untyped): T =
|
||||||
## Fetch value of result if set, or evaluate `def`
|
## Fetch value of result if set, or evaluate `def`
|
||||||
## `def` is evaluated lazily, and must be an expression of `T` or exit
|
## `def` is evaluated lazily, and must be an expression of `T` or exit
|
||||||
## the scope (for example using `return` / `raise`)
|
## the scope (for example using `return` / `raise`)
|
||||||
##
|
##
|
||||||
|
## See `isOkOr` for a version that works with `Result[void, E]`.
|
||||||
|
##
|
||||||
## Example:
|
## Example:
|
||||||
## ```
|
## ```
|
||||||
## let
|
## let
|
||||||
@ -869,11 +939,12 @@ template valueOr*[T: not void, E](self: Result[T, E], def: untyped): T =
|
|||||||
## TODO experimental, might change in the future
|
## TODO experimental, might change in the future
|
||||||
##
|
##
|
||||||
## The template contains a shortcut for accessing the error of the result,
|
## The template contains a shortcut for accessing the error of the result,
|
||||||
## without specifying the error - it can only be used outside of generic code,
|
## it can only be used outside of generic code,
|
||||||
## see https://github.com/status-im/nim-stew/issues/161#issuecomment-1397121386
|
## see https://github.com/status-im/nim-stew/issues/161#issuecomment-1397121386
|
||||||
##
|
##
|
||||||
let s = (self) # TODO avoid copy
|
let s = (self) # TODO avoid copy
|
||||||
if s.oResultPrivate: s.vResultPrivate
|
if s.oResultPrivate:
|
||||||
|
s.vResultPrivate
|
||||||
else:
|
else:
|
||||||
when E isnot void:
|
when E isnot void:
|
||||||
template error: E {.used, inject.} = s.eResultPrivate
|
template error: E {.used, inject.} = s.eResultPrivate
|
||||||
@ -883,8 +954,11 @@ template errorOr*[T, E: not void](self: Result[T, E], def: untyped): E =
|
|||||||
## Fetch error of result if not set, or evaluate `def`
|
## Fetch error of result if not set, or evaluate `def`
|
||||||
## `def` is evaluated lazily, and must be an expression of `T` or exit
|
## `def` is evaluated lazily, and must be an expression of `T` or exit
|
||||||
## the scope (for example using `return` / `raise`)
|
## the scope (for example using `return` / `raise`)
|
||||||
|
##
|
||||||
|
## See `isErrOr` for a version that works with `Result[T, void]`.
|
||||||
let s = (self) # TODO avoid copy
|
let s = (self) # TODO avoid copy
|
||||||
if not s.oResultPrivate: s.eResultPrivate
|
if not s.oResultPrivate:
|
||||||
|
s.eResultPrivate
|
||||||
else:
|
else:
|
||||||
when T isnot void:
|
when T isnot void:
|
||||||
template value: T {.used, inject.} = s.vResultPrivate
|
template value: T {.used, inject.} = s.vResultPrivate
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
# From: https://github.com/nim-lang/Nim/pull/11067/
|
|
||||||
proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int
|
|
||||||
{.inline, noSideEffect.} =
|
|
||||||
## Parses a hexadecimal number and stores its value in ``number``.
|
|
||||||
##
|
|
||||||
## Returns the number of the parsed characters or 0 in case of an error.
|
|
||||||
## If error, the value of ``number`` is not changed.
|
|
||||||
##
|
|
||||||
## If ``maxLen == 0``, the parsing continues until the first non-hex character
|
|
||||||
## or to the end of the string. Otherwise, no more than ``maxLen`` characters
|
|
||||||
## are parsed starting from the ``start`` position.
|
|
||||||
##
|
|
||||||
## It does not check for overflow. If the value represented by the string is
|
|
||||||
## too big to fit into ``number``, only the value of last fitting characters
|
|
||||||
## will be stored in ``number`` without producing an error.
|
|
||||||
runnableExamples:
|
|
||||||
var num: int
|
|
||||||
doAssert parseHex("4E_69_ED", num) == 8
|
|
||||||
doAssert num == 5138925
|
|
||||||
doAssert parseHex("X", num) == 0
|
|
||||||
doAssert parseHex("#ABC", num) == 4
|
|
||||||
var num8: int8
|
|
||||||
doAssert parseHex("0x_4E_69_ED", num8) == 11
|
|
||||||
doAssert num8 == 0xED'i8
|
|
||||||
doAssert parseHex("0x_4E_69_ED", num8, 3, 2) == 2
|
|
||||||
doAssert num8 == 0x4E'i8
|
|
||||||
var num8u: uint8
|
|
||||||
doAssert parseHex("0x_4E_69_ED", num8u) == 11
|
|
||||||
doAssert num8u == 237
|
|
||||||
var num64: int64
|
|
||||||
doAssert parseHex("4E69ED4E69ED", num64) == 12
|
|
||||||
doAssert num64 == 86216859871725
|
|
||||||
var i = start
|
|
||||||
var output = T(0)
|
|
||||||
var foundDigit = false
|
|
||||||
let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
|
|
||||||
if i + 1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
|
|
||||||
elif i < last and s[i] == '#': inc(i)
|
|
||||||
while i < last:
|
|
||||||
case s[i]
|
|
||||||
of '_': discard
|
|
||||||
of '0'..'9':
|
|
||||||
output = output shl 4 or T(ord(s[i]) - ord('0'))
|
|
||||||
foundDigit = true
|
|
||||||
of 'a'..'f':
|
|
||||||
output = output shl 4 or T(ord(s[i]) - ord('a') + 10)
|
|
||||||
foundDigit = true
|
|
||||||
of 'A'..'F':
|
|
||||||
output = output shl 4 or T(ord(s[i]) - ord('A') + 10)
|
|
||||||
foundDigit = true
|
|
||||||
else: break
|
|
||||||
inc(i)
|
|
||||||
if foundDigit:
|
|
||||||
number = output
|
|
||||||
result = i - start
|
|
127
stew/shims/parseutils.nim
Normal file
127
stew/shims/parseutils.nim
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
# From: https://github.com/nim-lang/Nim/pull/11067/
|
||||||
|
proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int
|
||||||
|
{.inline, noSideEffect.} =
|
||||||
|
## Parses a hexadecimal number and stores its value in ``number``.
|
||||||
|
##
|
||||||
|
## Returns the number of the parsed characters or 0 in case of an error.
|
||||||
|
## If error, the value of ``number`` is not changed.
|
||||||
|
##
|
||||||
|
## If ``maxLen == 0``, the parsing continues until the first non-hex character
|
||||||
|
## or to the end of the string. Otherwise, no more than ``maxLen`` characters
|
||||||
|
## are parsed starting from the ``start`` position.
|
||||||
|
##
|
||||||
|
## It does not check for overflow. If the value represented by the string is
|
||||||
|
## too big to fit into ``number``, only the value of last fitting characters
|
||||||
|
## will be stored in ``number`` without producing an error.
|
||||||
|
runnableExamples:
|
||||||
|
var num: int
|
||||||
|
doAssert parseHex("4E_69_ED", num) == 8
|
||||||
|
doAssert num == 5138925
|
||||||
|
doAssert parseHex("X", num) == 0
|
||||||
|
doAssert parseHex("#ABC", num) == 4
|
||||||
|
var num8: int8
|
||||||
|
doAssert parseHex("0x_4E_69_ED", num8) == 11
|
||||||
|
doAssert num8 == 0xED'i8
|
||||||
|
doAssert parseHex("0x_4E_69_ED", num8, 3, 2) == 2
|
||||||
|
doAssert num8 == 0x4E'i8
|
||||||
|
var num8u: uint8
|
||||||
|
doAssert parseHex("0x_4E_69_ED", num8u) == 11
|
||||||
|
doAssert num8u == 237
|
||||||
|
var num64: int64
|
||||||
|
doAssert parseHex("4E69ED4E69ED", num64) == 12
|
||||||
|
doAssert num64 == 86216859871725
|
||||||
|
var i = start
|
||||||
|
var output = T(0)
|
||||||
|
var foundDigit = false
|
||||||
|
let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
|
||||||
|
if i + 1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
|
||||||
|
elif i < last and s[i] == '#': inc(i)
|
||||||
|
while i < last:
|
||||||
|
case s[i]
|
||||||
|
of '_': discard
|
||||||
|
of '0'..'9':
|
||||||
|
output = output shl 4 or T(ord(s[i]) - ord('0'))
|
||||||
|
foundDigit = true
|
||||||
|
of 'a'..'f':
|
||||||
|
output = output shl 4 or T(ord(s[i]) - ord('a') + 10)
|
||||||
|
foundDigit = true
|
||||||
|
of 'A'..'F':
|
||||||
|
output = output shl 4 or T(ord(s[i]) - ord('A') + 10)
|
||||||
|
foundDigit = true
|
||||||
|
else: break
|
||||||
|
inc(i)
|
||||||
|
if foundDigit:
|
||||||
|
number = output
|
||||||
|
result = i - start
|
||||||
|
|
||||||
|
import std/parseutils; export parseutils
|
||||||
|
#From https://github.com/nim-lang/Nim/pull/21349 in -devel and in version-1-6:
|
||||||
|
# https://github.com/nim-lang/Nim/commit/c546ba5d23bb2e7bc562a071c88efd94cca7b89e
|
||||||
|
# https://github.com/nim-lang/Nim/commit/fca6a0bd6a6d3b9a25d1272e29bc39e88853188e
|
||||||
|
when not declared(parseSize): # Odd code formatting to minimize diff v. mainLine
|
||||||
|
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||||
|
|
||||||
|
func toLowerAscii(c: char): char =
|
||||||
|
if c in {'A'..'Z'}: char(uint8(c) xor 0b0010_0000'u8) else: c
|
||||||
|
|
||||||
|
func parseSize*(s: string, size: var int64, alwaysBin=false): int =
|
||||||
|
## Parse a size qualified by binary or metric units into `size`. This format
|
||||||
|
## is often called "human readable". Result is the number of processed chars
|
||||||
|
## or 0 on parse errors and size is rounded to the nearest integer. Trailing
|
||||||
|
## garbage like "/s" in "1k/s" is allowed and detected by `result < s.len`.
|
||||||
|
##
|
||||||
|
## To simplify use, following non-rare wild conventions, and since fractional
|
||||||
|
## data like milli-bytes is so rare, unit matching is case-insensitive but for
|
||||||
|
## the 'i' distinguishing binary-metric from metric (which cannot be 'I').
|
||||||
|
##
|
||||||
|
## An optional trailing 'B|b' is ignored but processed. I.e., you must still
|
||||||
|
## know if units are bytes | bits or infer this fact via the case of s[^1] (if
|
||||||
|
## users can even be relied upon to use 'B' for byte and 'b' for bit or have
|
||||||
|
## that be s[^1]).
|
||||||
|
##
|
||||||
|
## If `alwaysBin==true` then scales are always binary-metric, but e.g. "KiB"
|
||||||
|
## is still accepted for clarity. If the value would exceed the range of
|
||||||
|
## `int64`, `size` saturates to `int64.high`. Supported metric prefix chars
|
||||||
|
## include k, m, g, t, p, e, z, y (but z & y saturate unless the number is a
|
||||||
|
## small fraction).
|
||||||
|
##
|
||||||
|
## **See also:**
|
||||||
|
## * https://en.wikipedia.org/wiki/Binary_prefix
|
||||||
|
## * `formatSize module<strutils.html>`_ for formatting
|
||||||
|
runnableExamples:
|
||||||
|
var res: int64 # caller must still know if 'b' refers to bytes|bits
|
||||||
|
doAssert parseSize("10.5 MB", res) == 7
|
||||||
|
doAssert res == 10_500_000 # decimal metric Mega prefix
|
||||||
|
doAssert parseSize("64 mib", res) == 6
|
||||||
|
doAssert res == 67108864 # 64 shl 20
|
||||||
|
doAssert parseSize("1G/h", res, true) == 2 # '/' stops parse
|
||||||
|
doAssert res == 1073741824 # 1 shl 30, forced binary metric
|
||||||
|
const prefix = "b" & "kmgtpezy" # byte|bit & lowCase metric-ish prefixes
|
||||||
|
const scaleM = [1.0, 1e3, 1e6, 1e9, 1e12, 1e15, 1e18, 1e21, 1e24] # 10^(3*idx)
|
||||||
|
const scaleB = [1.0, 1024, 1048576, 1073741824, 1099511627776.0, # 2^(10*idx)
|
||||||
|
1125899906842624.0, 1152921504606846976.0, # ldexp?
|
||||||
|
1.180591620717411303424e21, 1.208925819614629174706176e24]
|
||||||
|
var number: float
|
||||||
|
var scale = 1.0
|
||||||
|
result = parseFloat(s, number)
|
||||||
|
if number < 0: # While parseFloat accepts negatives ..
|
||||||
|
result = 0 #.. we do not since sizes cannot be < 0
|
||||||
|
if result > 0:
|
||||||
|
let start = result # Save spot to maybe unwind white to EOS
|
||||||
|
while result < s.len and s[result] in Whitespace:
|
||||||
|
inc result
|
||||||
|
if result < s.len: # Illegal starting char => unity
|
||||||
|
if (let si = prefix.find(s[result].toLowerAscii); si >= 0):
|
||||||
|
inc result # Now parse the scale
|
||||||
|
scale = if alwaysBin: scaleB[si] else: scaleM[si]
|
||||||
|
if result < s.len and s[result] == 'i':
|
||||||
|
scale = scaleB[si] # Switch from default to binary-metric
|
||||||
|
inc result
|
||||||
|
if result < s.len and s[result].toLowerAscii == 'b':
|
||||||
|
inc result # Skip optional '[bB]'
|
||||||
|
else: # Unwind result advancement when there..
|
||||||
|
result = start #..is no unit to the end of `s`.
|
||||||
|
var sizeF = number * scale + 0.5 # Saturate to int64.high when too big
|
||||||
|
size = if sizeF > 9223372036854774784.0: int64.high else: sizeF.int64
|
||||||
|
# Above constant=2^63-1024 avoids C UB; github.com/nim-lang/Nim/issues/20102 or
|
||||||
|
# stackoverflow.com/questions/20923556/math-pow2-63-1-math-pow2-63-512-is-true
|
@ -21,3 +21,5 @@ when (NimMajor, NimMinor) < (1, 4):
|
|||||||
RangeDefect* = RangeError
|
RangeDefect* = RangeError
|
||||||
ReraiseDefect* = ReraiseError
|
ReraiseDefect* = ReraiseError
|
||||||
StackOverflowDefect* = StackOverflowError
|
StackOverflowDefect* = StackOverflowError
|
||||||
|
else:
|
||||||
|
{.used.}
|
||||||
|
@ -88,7 +88,7 @@ type
|
|||||||
RbResult[SortedSetItemRef[K,V]]
|
RbResult[SortedSetItemRef[K,V]]
|
||||||
|
|
||||||
when (NimMajor, NimMinor) < (1, 4):
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
{.push raises: [Defect].}
|
{.push raises: [Defect,CatchableError].}
|
||||||
else:
|
else:
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
@ -301,7 +301,7 @@ proc `$`*[K,V](rc: SortedSetResult[K,V]): string =
|
|||||||
|
|
||||||
proc verify*[K,V](sl: var SortedSet[K,V]):
|
proc verify*[K,V](sl: var SortedSet[K,V]):
|
||||||
Result[void,(SortedSetItemRef[K,V],RbInfo)]
|
Result[void,(SortedSetItemRef[K,V],RbInfo)]
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
{.gcsafe.} =
|
||||||
## Checks for consistency, may print an error message. Returns `rbOk` if
|
## Checks for consistency, may print an error message. Returns `rbOk` if
|
||||||
## the argument list `sl` is consistent. This function traverses all the
|
## the argument list `sl` is consistent. This function traverses all the
|
||||||
## internal data nodes which might be time consuming. So it would not be
|
## internal data nodes which might be time consuming. So it would not be
|
||||||
|
@ -18,11 +18,20 @@ type
|
|||||||
## for the equivalent of `a < b`
|
## for the equivalent of `a < b`
|
||||||
proc(a, b: C): bool {.gcsafe.}
|
proc(a, b: C): bool {.gcsafe.}
|
||||||
|
|
||||||
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
|
type
|
||||||
RbPrnFn* = ##\
|
RbPrnFn* = ##\
|
||||||
## Handle error message
|
## Handle error message
|
||||||
proc(code: RbInfo; ctxInfo: string)
|
proc(code: RbInfo; ctxInfo: string)
|
||||||
{.gcsafe, raises: [Defect,CatchableError].}
|
{.gcsafe, raises: [Defect,CatchableError].}
|
||||||
|
else:
|
||||||
|
type
|
||||||
|
RbPrnFn* = ##\
|
||||||
|
## Handle error message
|
||||||
|
proc(code: RbInfo; ctxInfo: string)
|
||||||
|
{.gcsafe, raises: [].}
|
||||||
|
|
||||||
|
type
|
||||||
RbDdebug[C,K] = object
|
RbDdebug[C,K] = object
|
||||||
tree: RbTreeRef[C,K] ## tree, not-Nil
|
tree: RbTreeRef[C,K] ## tree, not-Nil
|
||||||
node: RbNodeRef[C] ## current node
|
node: RbNodeRef[C] ## current node
|
||||||
@ -34,7 +43,7 @@ type
|
|||||||
msg: string ## collect data
|
msg: string ## collect data
|
||||||
|
|
||||||
when (NimMajor, NimMinor) < (1, 4):
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
{.push raises: [Defect].}
|
{.push raises: [Defect, CatchableError].}
|
||||||
else:
|
else:
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
@ -52,8 +61,7 @@ proc pp[C](n: RbNodeRef[C]): string =
|
|||||||
result &= "~black"
|
result &= "~black"
|
||||||
|
|
||||||
proc doError[C,K](d: var RbDdebug[C,K]; t: RbInfo; s: string):
|
proc doError[C,K](d: var RbDdebug[C,K]; t: RbInfo; s: string):
|
||||||
Result[void,(C,RbInfo)]
|
Result[void,(C,RbInfo)] {.gcsafe.} =
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
|
||||||
if not d.pr.isNil:
|
if not d.pr.isNil:
|
||||||
var msg = s &
|
var msg = s &
|
||||||
": <" & d.node.pp &
|
": <" & d.node.pp &
|
||||||
@ -63,44 +71,44 @@ proc doError[C,K](d: var RbDdebug[C,K]; t: RbInfo; s: string):
|
|||||||
err((d.node.casket,t))
|
err((d.node.casket,t))
|
||||||
|
|
||||||
proc rootIsRed[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
proc rootIsRed[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
{.gcsafe.} =
|
||||||
d.doError(rbVfyRootIsRed, "Root node is red")
|
d.doError(rbVfyRootIsRed, "Root node is red")
|
||||||
|
|
||||||
|
|
||||||
proc redNodeRedLinkLeft[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
proc redNodeRedLinkLeft[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
{.gcsafe.} =
|
||||||
d.doError(rbVfyRedParentRedLeftLink, "Parent node and left link red")
|
d.doError(rbVfyRedParentRedLeftLink, "Parent node and left link red")
|
||||||
|
|
||||||
proc redNodeRedLinkRight[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
proc redNodeRedLinkRight[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
{.gcsafe.} =
|
||||||
d.doError(rbVfyRedParentRedRightLink, "Parent node and right link red")
|
d.doError(rbVfyRedParentRedRightLink, "Parent node and right link red")
|
||||||
|
|
||||||
proc redNodeRedLinkBoth[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
proc redNodeRedLinkBoth[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
{.gcsafe.} =
|
||||||
d.doError(rbVfyRedParentRedBothLinks, "Parent node and both links red")
|
d.doError(rbVfyRedParentRedBothLinks, "Parent node and both links red")
|
||||||
|
|
||||||
|
|
||||||
proc linkLeftCompError[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
proc linkLeftCompError[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
{.gcsafe.} =
|
||||||
d.doError(rbVfyLeftLinkGtParent, "Left node greater than parent")
|
d.doError(rbVfyLeftLinkGtParent, "Left node greater than parent")
|
||||||
|
|
||||||
proc linkRightCompError[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
proc linkRightCompError[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
{.gcsafe.} =
|
||||||
d.doError(rbVfyRightLinkLtParent, "Right node greater than parent")
|
d.doError(rbVfyRightLinkLtParent, "Right node greater than parent")
|
||||||
|
|
||||||
proc linkBothCompError[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
proc linkBothCompError[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
{.gcsafe.} =
|
||||||
d.doError(rbVfyBothLinkCmpParentReversed,
|
d.doError(rbVfyBothLinkCmpParentReversed,
|
||||||
"Left node greater than parent greater than right node")
|
"Left node greater than parent greater than right node")
|
||||||
|
|
||||||
proc blackChainLevelError[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
proc blackChainLevelError[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
{.gcsafe.} =
|
||||||
d.doError(rbVfyBlackChainLevelMismatch,
|
d.doError(rbVfyBlackChainLevelMismatch,
|
||||||
"Inconsistent length of black node chains")
|
"Inconsistent length of black node chains")
|
||||||
|
|
||||||
|
|
||||||
proc subTreeVerify[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
proc subTreeVerify[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
{.gcsafe.} =
|
||||||
let node = d.node
|
let node = d.node
|
||||||
doAssert not node.isNil
|
doAssert not node.isNil
|
||||||
|
|
||||||
@ -168,7 +176,7 @@ proc subTreeVerify[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
|||||||
proc rbTreeVerify*[C,K](rbt: RbTreeRef[C,K];
|
proc rbTreeVerify*[C,K](rbt: RbTreeRef[C,K];
|
||||||
lt: RbLtFn[C] = nil; pr: RbPrnFn = nil):
|
lt: RbLtFn[C] = nil; pr: RbPrnFn = nil):
|
||||||
Result[void,(C,RbInfo)]
|
Result[void,(C,RbInfo)]
|
||||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
{.gcsafe.} =
|
||||||
## Verifies the argument tree `rbt` for
|
## Verifies the argument tree `rbt` for
|
||||||
## * No consecutively linked red nodes down the tree
|
## * No consecutively linked red nodes down the tree
|
||||||
## * Link consisteny: value(leftLink) < value(node) < value(rightLink). This
|
## * Link consisteny: value(leftLink) < value(node) < value(rightLink). This
|
||||||
|
@ -9,6 +9,11 @@
|
|||||||
import unittest2
|
import unittest2
|
||||||
import ../stew/ctops
|
import ../stew/ctops
|
||||||
|
|
||||||
|
when (NimMajor, NimMinor) < (1, 6):
|
||||||
|
type DefectEx = AssertionError
|
||||||
|
else:
|
||||||
|
type DefectEx = AssertionDefect
|
||||||
|
|
||||||
suite "Constant-time operations test suite":
|
suite "Constant-time operations test suite":
|
||||||
test "isEqual() test":
|
test "isEqual() test":
|
||||||
let
|
let
|
||||||
@ -97,17 +102,17 @@ suite "Constant-time operations test suite":
|
|||||||
CT.isEqual(int64Arr, uint64Arr) == true
|
CT.isEqual(int64Arr, uint64Arr) == true
|
||||||
|
|
||||||
# Empty arrays
|
# Empty arrays
|
||||||
expect(AssertionError):
|
expect(DefectEx):
|
||||||
discard CT.isEqual(emptyArray, emptyArray)
|
discard CT.isEqual(emptyArray, emptyArray)
|
||||||
expect(AssertionError):
|
expect(DefectEx):
|
||||||
discard CT.isEqual(emptyArray, emptyString)
|
discard CT.isEqual(emptyArray, emptyString)
|
||||||
expect(AssertionError):
|
expect(DefectEx):
|
||||||
discard CT.isEqual(emptyArray, emptySeq)
|
discard CT.isEqual(emptyArray, emptySeq)
|
||||||
|
|
||||||
# Arrays, where T is different type size
|
# Arrays, where T is different type size
|
||||||
expect(AssertionError):
|
expect(DefectEx):
|
||||||
discard CT.isEqual(int8Arr, int16Arr)
|
discard CT.isEqual(int8Arr, int16Arr)
|
||||||
expect(AssertionError):
|
expect(DefectEx):
|
||||||
discard CT.isEqual(int16Arr, int32Arr)
|
discard CT.isEqual(int16Arr, int32Arr)
|
||||||
expect(AssertionError):
|
expect(DefectEx):
|
||||||
discard CT.isEqual(int8Arr, intArr)
|
discard CT.isEqual(int8Arr, intArr)
|
||||||
|
@ -17,8 +17,6 @@ import
|
|||||||
../stew/keyed_queue/kq_debug
|
../stew/keyed_queue/kq_debug
|
||||||
|
|
||||||
const
|
const
|
||||||
usedStrutils = newSeq[string]().join(" ")
|
|
||||||
|
|
||||||
lruCacheLimit = 10
|
lruCacheLimit = 10
|
||||||
lruCacheModulo = 13
|
lruCacheModulo = 13
|
||||||
|
|
||||||
@ -50,7 +48,7 @@ let
|
|||||||
proc `$`(rc: KeyedQueuePair[uint,uint]): string =
|
proc `$`(rc: KeyedQueuePair[uint,uint]): string =
|
||||||
"(" & $rc.key & "," & $rc.data & ")"
|
"(" & $rc.key & "," & $rc.data & ")"
|
||||||
|
|
||||||
proc `$`(rc: Result[KeyedQueuePair[uint,uint],void]): string =
|
proc `$`(rc: Result[KeyedQueuePair[uint,uint],void]): string {.used.} =
|
||||||
result = "<"
|
result = "<"
|
||||||
if rc.isOk:
|
if rc.isOk:
|
||||||
result &= $rc.value.key & "," & $rc.value.data
|
result &= $rc.value.key & "," & $rc.value.data
|
||||||
@ -136,7 +134,7 @@ proc compileGenericFunctions(rq: var KUQueue) =
|
|||||||
rq[0] = 0 # so `rq[0]` works
|
rq[0] = 0 # so `rq[0]` works
|
||||||
discard rq[0]
|
discard rq[0]
|
||||||
|
|
||||||
let ignoreValues = (
|
let ignoreValues {.used.} = (
|
||||||
(rq.append(0,0), rq.push(0,0),
|
(rq.append(0,0), rq.push(0,0),
|
||||||
rq.replace(0,0),
|
rq.replace(0,0),
|
||||||
rq.prepend(0,0), rq.unshift(0,0),
|
rq.prepend(0,0), rq.unshift(0,0),
|
||||||
@ -533,10 +531,13 @@ suite "KeyedQueue: Data queue as LRU cache":
|
|||||||
c1 = keyList.toLruCache
|
c1 = keyList.toLruCache
|
||||||
sq = toSeq(c1.q.nextPairs).mapIt(it.key.fromKey)
|
sq = toSeq(c1.q.nextPairs).mapIt(it.key.fromKey)
|
||||||
s0 = sq
|
s0 = sq
|
||||||
inx = 5
|
|
||||||
key = sq[5].toKey
|
key = sq[5].toKey
|
||||||
|
|
||||||
sq.delete(5,5) # delete index 5 in sequence
|
when (NimMajor, NimMinor) >= (1, 6):
|
||||||
|
sq.delete(5..5) # delete index 5 in sequence
|
||||||
|
else:
|
||||||
|
sq.delete(5, 5) # delete index 5 in sequence
|
||||||
|
|
||||||
noisy.say &"sq: {s0} <off sq[5]({key})> {sq}"
|
noisy.say &"sq: {s0} <off sq[5]({key})> {sq}"
|
||||||
|
|
||||||
check c1.q.delete(key).value.key == key
|
check c1.q.delete(key).value.key == key
|
||||||
|
@ -70,6 +70,15 @@ block:
|
|||||||
|
|
||||||
doAssert rOk.get() == rOk.unsafeGet()
|
doAssert rOk.get() == rOk.unsafeGet()
|
||||||
|
|
||||||
|
rOk.isOkOr: raiseAssert "should not end up in here"
|
||||||
|
rErr.isErrOr: raiseAssert "should not end up in here"
|
||||||
|
|
||||||
|
rErr.isOkOr:
|
||||||
|
doAssert error == rErr.error()
|
||||||
|
|
||||||
|
rOk.isErrOr:
|
||||||
|
doAssert value == rOk.value()
|
||||||
|
|
||||||
doAssert rOk.valueOr(failFast()) == rOk.value()
|
doAssert rOk.valueOr(failFast()) == rOk.value()
|
||||||
let rErrV = rErr.valueOr:
|
let rErrV = rErr.valueOr:
|
||||||
error.len
|
error.len
|
||||||
@ -88,12 +97,18 @@ block:
|
|||||||
doAssert c.isErr
|
doAssert c.isErr
|
||||||
|
|
||||||
# De-reference
|
# De-reference
|
||||||
|
when (NimMajor, NimMinor) >= (1, 6):
|
||||||
|
{.warning[BareExcept]:off.}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
echo rErr[]
|
echo rErr[]
|
||||||
doAssert false
|
doAssert false
|
||||||
except:
|
except:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
when (NimMajor, NimMinor) >= (1, 6):
|
||||||
|
{.warning[BareExcept]:on.}
|
||||||
|
|
||||||
# Comparisons
|
# Comparisons
|
||||||
doAssert (rOk == rOk)
|
doAssert (rOk == rOk)
|
||||||
doAssert (rErr == rErr)
|
doAssert (rErr == rErr)
|
||||||
@ -144,6 +159,12 @@ block:
|
|||||||
# Expectations
|
# Expectations
|
||||||
doAssert rOk.expect("testOk never fails") == 42
|
doAssert rOk.expect("testOk never fails") == 42
|
||||||
|
|
||||||
|
# Conversions to Opt
|
||||||
|
doAssert rOk.optValue() == Opt.some(rOk.get())
|
||||||
|
doAssert rOk.optError().isNone()
|
||||||
|
doAssert rErr.optValue().isNone()
|
||||||
|
doAssert rErr.optError() == Opt.some(rErr.error())
|
||||||
|
|
||||||
# Question mark operator
|
# Question mark operator
|
||||||
func testQn(): Result[int, string] =
|
func testQn(): Result[int, string] =
|
||||||
let x = ?works() - ?works()
|
let x = ?works() - ?works()
|
||||||
@ -356,6 +377,10 @@ block: # Result[T, void] aka `Opt`
|
|||||||
doAssert Opt.some(42).get() == 42
|
doAssert Opt.some(42).get() == 42
|
||||||
doAssert Opt.none(int).isNone()
|
doAssert Opt.none(int).isNone()
|
||||||
|
|
||||||
|
# Construct Result from Opt
|
||||||
|
doAssert oOk.orErr("error").value() == oOk.get()
|
||||||
|
doAssert oErr.orErr("error").error() == "error"
|
||||||
|
|
||||||
block: # `cstring` dangling reference protection
|
block: # `cstring` dangling reference protection
|
||||||
type CSRes = Result[void, cstring]
|
type CSRes = Result[void, cstring]
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user