Merge branch 'intops' of github.com:status-im/nim-stew into intops
This commit is contained in:
commit
422b1540de
|
@ -26,7 +26,7 @@ jobs:
|
|||
cpu: amd64
|
||||
#- os: windows
|
||||
#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:
|
||||
- target:
|
||||
os: linux
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
# 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: 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)
|
||||
![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
|
||||
that are frequently used at Status, but are too small to deserve their own
|
||||
|
|
10
stew.nimble
10
stew.nimble
|
@ -30,7 +30,9 @@ proc run(args, path: string) =
|
|||
task test, "Run all tests":
|
||||
build "", "tests/test_helper"
|
||||
for args in [
|
||||
"--threads:off",
|
||||
"--threads:on -d:nimTypeNames",
|
||||
"--threads:on -d:noIntrinsicsBitOpts -d:noIntrinsicsEndians"
|
||||
]: run args, "tests/all_tests"
|
||||
"--threads:off",
|
||||
"--threads:on -d:nimTypeNames",
|
||||
"--threads:on -d:noIntrinsicsBitOpts -d:noIntrinsicsEndians"]:
|
||||
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
|
||||
when supportsCopyMem(T):
|
||||
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:
|
||||
for i in 0..<tgt.len:
|
||||
assign(tgt[i], src[i])
|
||||
|
@ -52,7 +52,7 @@ func assign*[T](tgt: var T, src: T) =
|
|||
when sizeof(src) <= sizeof(int):
|
||||
tgt = src
|
||||
else:
|
||||
copyMem(addr tgt, unsafeAddr src, sizeof(tgt))
|
||||
moveMem(addr tgt, unsafeAddr src, sizeof(tgt))
|
||||
elif T is object|tuple:
|
||||
for t, s in fields(tgt, src):
|
||||
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
|
||||
return
|
||||
let ch = alphabet.decode[int8(instr[i])]
|
||||
if ch == -1:
|
||||
if ch < 0:
|
||||
outlen = 0
|
||||
result = Base58Status.Incorrect
|
||||
return
|
||||
var c = cast[uint32](ch)
|
||||
var c = uint32(ch)
|
||||
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)
|
||||
buffer[j] = cast[uint32](t and 0xFFFF_FFFF'u32)
|
||||
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 8
|
||||
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 =
|
||||
## 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)))
|
||||
|
||||
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 =
|
||||
cast[int](63 - cast[cuint](builtin_clzll(v)))
|
||||
int(63 - cast[cuint](builtin_clzll(v)))
|
||||
|
||||
elif defined(vcc) and useBuiltins:
|
||||
const arch64 = sizeof(int) == 8
|
||||
|
|
|
@ -18,14 +18,16 @@ export arrayops.`&`, arrayops.initArrayWith, arrayops.`[]=`
|
|||
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
{.push raises: [Defect].}
|
||||
{.pragma: hexRaises, raises: [Defect, ValueError].}
|
||||
else:
|
||||
{.push raises: [].}
|
||||
{.pragma: hexRaises, raises: [ValueError].}
|
||||
|
||||
########################################################################################################
|
||||
##################################### Hex utilities ################################################
|
||||
|
||||
proc readHexChar*(c: char): byte
|
||||
{.raises: [ValueError, Defect], noSideEffect, inline.} =
|
||||
{.hexRaises, noSideEffect, inline.} =
|
||||
## Converts an hex char to a byte
|
||||
case c
|
||||
of '0'..'9': result = byte(ord(c) - ord('0'))
|
||||
|
@ -42,7 +44,7 @@ template skip0xPrefix(hexStr: openArray[char]): int =
|
|||
|
||||
func hexToByteArrayImpl(
|
||||
hexStr: openArray[char], output: var openArray[byte], fromIdx, toIdx: int):
|
||||
int {.raises: [ValueError, Defect].} =
|
||||
int {.hexRaises.} =
|
||||
var sIdx = skip0xPrefix(hexStr)
|
||||
# Fun with closed intervals
|
||||
doAssert fromIdx >= 0 and
|
||||
|
@ -65,7 +67,7 @@ func hexToByteArrayImpl(
|
|||
|
||||
func hexToByteArray*(
|
||||
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
|
||||
## corresponding bytes in `output[fromIdx..toIdx]` where `mapHex` takes into
|
||||
## account stripped characters.
|
||||
|
@ -80,7 +82,7 @@ func hexToByteArray*(
|
|||
discard hexToByteArrayImpl(hexStr, output, fromIdx, toIdx)
|
||||
|
||||
func hexToByteArray*(hexStr: openArray[char], output: var openArray[byte])
|
||||
{.raises: [ValueError, Defect].} =
|
||||
{.hexRaises.} =
|
||||
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
||||
## `output`.
|
||||
##
|
||||
|
@ -92,7 +94,7 @@ func hexToByteArray*(hexStr: openArray[char], output: var openArray[byte])
|
|||
hexToByteArray(hexStr, output, 0, output.high)
|
||||
|
||||
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.
|
||||
##
|
||||
## * `0x`/`0X` is stripped if present
|
||||
|
@ -103,7 +105,7 @@ func hexToByteArray*[N: static[int]](hexStr: openArray[char]): array[N, byte]
|
|||
hexToByteArray(hexStr, result)
|
||||
|
||||
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.
|
||||
##
|
||||
## * `0x`/`0X` is stripped if present
|
||||
|
@ -114,7 +116,7 @@ func hexToByteArray*(hexStr: openArray[char], N: static int): array[N, byte]
|
|||
hexToByteArray(hexStr, result)
|
||||
|
||||
func hexToByteArrayStrict*(hexStr: openArray[char], output: var openArray[byte])
|
||||
{.raises: [ValueError, Defect].} =
|
||||
{.hexRaises.} =
|
||||
## Read hex-encoded data from `hexStr` and store corresponding bytes in
|
||||
## `output`.
|
||||
##
|
||||
|
@ -126,7 +128,7 @@ func hexToByteArrayStrict*(hexStr: openArray[char], output: var openArray[byte])
|
|||
raise (ref ValueError)(msg: "hex string too long")
|
||||
|
||||
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
|
||||
## `output`.
|
||||
##
|
||||
|
@ -137,7 +139,7 @@ func hexToByteArrayStrict*[N: static[int]](hexStr: openArray[char]): array[N, by
|
|||
hexToByteArrayStrict(hexStr, result)
|
||||
|
||||
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
|
||||
## `output`.
|
||||
##
|
||||
|
@ -148,7 +150,7 @@ func hexToByteArrayStrict*(hexStr: openArray[char], N: static int): array[N, byt
|
|||
hexToByteArrayStrict(hexStr, result)
|
||||
|
||||
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.
|
||||
##
|
||||
## * `0x`/`0X` is stripped if present
|
||||
|
@ -159,7 +161,7 @@ func fromHex*[N](A: type array[N, byte], hexStr: string): A
|
|||
hexToByteArray(hexStr, result)
|
||||
|
||||
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`.
|
||||
## The string may be shorter than the byte array.
|
||||
## No "endianness" reordering is done.
|
||||
|
@ -188,7 +190,7 @@ func hexToPaddedByteArray*[N: static[int]](hexStr: string): array[N, byte]
|
|||
bIdx += shift shr 2
|
||||
|
||||
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.
|
||||
if (hexStr.len and 1) == 1:
|
||||
raise (ref ValueError)(msg: "hex string must have even length")
|
||||
|
|
|
@ -19,9 +19,13 @@ elif sizeof(int) == 4:
|
|||
type
|
||||
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],
|
||||
b: openArray[B]): bool {.
|
||||
raises: [Defect] .} =
|
||||
b: openArray[B]): bool =
|
||||
## Perform constant time comparison of two arrays ``a`` and ``b``.
|
||||
##
|
||||
## Please note that it only makes sense to compare arrays of the same length.
|
||||
|
|
|
@ -112,7 +112,7 @@ import
|
|||
"."/[results, sorted_set]
|
||||
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
{.push raises: [Defect].}
|
||||
{.push raises: [Defect, CatchableError].}
|
||||
else:
|
||||
{.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
|
||||
## item returned and deleted is the *left-most* item.
|
||||
type T = KeyedQueuePair[K,V]
|
||||
if 0 < rq.tab.len:
|
||||
noKeyError("shift"):
|
||||
let kvp = KeyedQueuePair[K,V](
|
||||
key: rq.kFirst,
|
||||
data: rq.tab[rq.kFirst].data)
|
||||
rq.shiftImpl
|
||||
return ok(KeyedQueuePair[K,V](kvp))
|
||||
when kvp is T:
|
||||
return ok(kvp)
|
||||
else:
|
||||
return ok(T(kvp))
|
||||
err()
|
||||
|
||||
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
|
||||
## item returned and deleted is the *right-most* item.
|
||||
type T = KeyedQueuePair[K,V]
|
||||
if 0 < rq.tab.len:
|
||||
noKeyError("pop"):
|
||||
let kvp = KeyedQueuePair[K,V](
|
||||
key: rq.kLast,
|
||||
data: rq.tab[rq.kLast].data)
|
||||
rq.popImpl
|
||||
return ok(KeyedQueuePair[K,V](kvp))
|
||||
when kvp is T:
|
||||
return ok(kvp)
|
||||
else:
|
||||
return ok(T(kvp))
|
||||
err()
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
## table semantics. Note that this finction throws a `KeyError` exception
|
||||
## unless the argument `key` exists in the queue.
|
||||
|
|
|
@ -31,6 +31,11 @@ type
|
|||
kQVfyPrvNxtExpected
|
||||
kQVfyFirstExpected
|
||||
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
{.push raises: [Defect].}
|
||||
else:
|
||||
{.push raises: [].}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions, debugging
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -50,7 +55,7 @@ proc `$`*[K,V](item: KeyedQueueItem[K,V]): string =
|
|||
"(" & $item.value & ", link[" & $item.prv & "," & $item.kNxt & "])"
|
||||
|
||||
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
|
||||
## queue `rq` is consistent.
|
||||
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:
|
||||
return false
|
||||
|
||||
res = E value
|
||||
res = cast[E](value)
|
||||
return true
|
||||
|
||||
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
|
||||
|
||||
when not defined(nimHasEffectsOfs):
|
||||
template effectsOf(f: untyped) {.pragma.}
|
||||
template effectsOf(f: untyped) {.pragma, used.}
|
||||
|
||||
func map*[T0, E, T1](
|
||||
self: Result[T0, E], f: proc(x: T0): T1):
|
||||
|
@ -845,15 +845,85 @@ template unsafeError*[T](self: Result[T, void]) =
|
|||
## See also: `unsafeGet`
|
||||
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
|
||||
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 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 =
|
||||
## Fetch value of result if set, or evaluate `def`
|
||||
## `def` is evaluated lazily, and must be an expression of `T` or exit
|
||||
## the scope (for example using `return` / `raise`)
|
||||
##
|
||||
## See `isOkOr` for a version that works with `Result[void, E]`.
|
||||
##
|
||||
## Example:
|
||||
## ```
|
||||
## 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
|
||||
##
|
||||
## 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
|
||||
##
|
||||
let s = (self) # TODO avoid copy
|
||||
if s.oResultPrivate: s.vResultPrivate
|
||||
if s.oResultPrivate:
|
||||
s.vResultPrivate
|
||||
else:
|
||||
when E isnot void:
|
||||
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`
|
||||
## `def` is evaluated lazily, and must be an expression of `T` or exit
|
||||
## the scope (for example using `return` / `raise`)
|
||||
##
|
||||
## See `isErrOr` for a version that works with `Result[T, void]`.
|
||||
let s = (self) # TODO avoid copy
|
||||
if not s.oResultPrivate: s.eResultPrivate
|
||||
if not s.oResultPrivate:
|
||||
s.eResultPrivate
|
||||
else:
|
||||
when T isnot void:
|
||||
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
|
|
@ -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
|
||||
ReraiseDefect* = ReraiseError
|
||||
StackOverflowDefect* = StackOverflowError
|
||||
else:
|
||||
{.used.}
|
||||
|
|
|
@ -88,7 +88,7 @@ type
|
|||
RbResult[SortedSetItemRef[K,V]]
|
||||
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
{.push raises: [Defect].}
|
||||
{.push raises: [Defect,CatchableError].}
|
||||
else:
|
||||
{.push raises: [].}
|
||||
|
||||
|
@ -301,7 +301,7 @@ proc `$`*[K,V](rc: SortedSetResult[K,V]): string =
|
|||
|
||||
proc verify*[K,V](sl: var SortedSet[K,V]):
|
||||
Result[void,(SortedSetItemRef[K,V],RbInfo)]
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
{.gcsafe.} =
|
||||
## Checks for consistency, may print an error message. Returns `rbOk` if
|
||||
## the argument list `sl` is consistent. This function traverses all the
|
||||
## internal data nodes which might be time consuming. So it would not be
|
||||
|
|
|
@ -18,11 +18,20 @@ type
|
|||
## for the equivalent of `a < b`
|
||||
proc(a, b: C): bool {.gcsafe.}
|
||||
|
||||
RbPrnFn* = ##\
|
||||
## Handle error message
|
||||
proc(code: RbInfo; ctxInfo: string)
|
||||
{.gcsafe, raises: [Defect,CatchableError].}
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
type
|
||||
RbPrnFn* = ##\
|
||||
## Handle error message
|
||||
proc(code: RbInfo; ctxInfo: string)
|
||||
{.gcsafe, raises: [Defect,CatchableError].}
|
||||
else:
|
||||
type
|
||||
RbPrnFn* = ##\
|
||||
## Handle error message
|
||||
proc(code: RbInfo; ctxInfo: string)
|
||||
{.gcsafe, raises: [].}
|
||||
|
||||
type
|
||||
RbDdebug[C,K] = object
|
||||
tree: RbTreeRef[C,K] ## tree, not-Nil
|
||||
node: RbNodeRef[C] ## current node
|
||||
|
@ -34,7 +43,7 @@ type
|
|||
msg: string ## collect data
|
||||
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
{.push raises: [Defect].}
|
||||
{.push raises: [Defect, CatchableError].}
|
||||
else:
|
||||
{.push raises: [].}
|
||||
|
||||
|
@ -52,8 +61,7 @@ proc pp[C](n: RbNodeRef[C]): string =
|
|||
result &= "~black"
|
||||
|
||||
proc doError[C,K](d: var RbDdebug[C,K]; t: RbInfo; s: string):
|
||||
Result[void,(C,RbInfo)]
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
Result[void,(C,RbInfo)] {.gcsafe.} =
|
||||
if not d.pr.isNil:
|
||||
var msg = s &
|
||||
": <" & 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))
|
||||
|
||||
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")
|
||||
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
proc linkBothCompError[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
{.gcsafe.} =
|
||||
d.doError(rbVfyBothLinkCmpParentReversed,
|
||||
"Left node greater than parent greater than right node")
|
||||
|
||||
proc blackChainLevelError[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
{.gcsafe.} =
|
||||
d.doError(rbVfyBlackChainLevelMismatch,
|
||||
"Inconsistent length of black node chains")
|
||||
|
||||
|
||||
proc subTreeVerify[C,K](d: var RbDdebug[C,K]): Result[void,(C,RbInfo)]
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
{.gcsafe.} =
|
||||
let node = d.node
|
||||
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];
|
||||
lt: RbLtFn[C] = nil; pr: RbPrnFn = nil):
|
||||
Result[void,(C,RbInfo)]
|
||||
{.gcsafe, raises: [Defect,CatchableError].} =
|
||||
{.gcsafe.} =
|
||||
## Verifies the argument tree `rbt` for
|
||||
## * No consecutively linked red nodes down the tree
|
||||
## * Link consisteny: value(leftLink) < value(node) < value(rightLink). This
|
||||
|
|
|
@ -9,6 +9,11 @@
|
|||
import unittest2
|
||||
import ../stew/ctops
|
||||
|
||||
when (NimMajor, NimMinor) < (1, 6):
|
||||
type DefectEx = AssertionError
|
||||
else:
|
||||
type DefectEx = AssertionDefect
|
||||
|
||||
suite "Constant-time operations test suite":
|
||||
test "isEqual() test":
|
||||
let
|
||||
|
@ -97,17 +102,17 @@ suite "Constant-time operations test suite":
|
|||
CT.isEqual(int64Arr, uint64Arr) == true
|
||||
|
||||
# Empty arrays
|
||||
expect(AssertionError):
|
||||
expect(DefectEx):
|
||||
discard CT.isEqual(emptyArray, emptyArray)
|
||||
expect(AssertionError):
|
||||
expect(DefectEx):
|
||||
discard CT.isEqual(emptyArray, emptyString)
|
||||
expect(AssertionError):
|
||||
expect(DefectEx):
|
||||
discard CT.isEqual(emptyArray, emptySeq)
|
||||
|
||||
# Arrays, where T is different type size
|
||||
expect(AssertionError):
|
||||
expect(DefectEx):
|
||||
discard CT.isEqual(int8Arr, int16Arr)
|
||||
expect(AssertionError):
|
||||
expect(DefectEx):
|
||||
discard CT.isEqual(int16Arr, int32Arr)
|
||||
expect(AssertionError):
|
||||
expect(DefectEx):
|
||||
discard CT.isEqual(int8Arr, intArr)
|
||||
|
|
|
@ -17,8 +17,6 @@ import
|
|||
../stew/keyed_queue/kq_debug
|
||||
|
||||
const
|
||||
usedStrutils = newSeq[string]().join(" ")
|
||||
|
||||
lruCacheLimit = 10
|
||||
lruCacheModulo = 13
|
||||
|
||||
|
@ -50,7 +48,7 @@ let
|
|||
proc `$`(rc: KeyedQueuePair[uint,uint]): string =
|
||||
"(" & $rc.key & "," & $rc.data & ")"
|
||||
|
||||
proc `$`(rc: Result[KeyedQueuePair[uint,uint],void]): string =
|
||||
proc `$`(rc: Result[KeyedQueuePair[uint,uint],void]): string {.used.} =
|
||||
result = "<"
|
||||
if rc.isOk:
|
||||
result &= $rc.value.key & "," & $rc.value.data
|
||||
|
@ -136,7 +134,7 @@ proc compileGenericFunctions(rq: var KUQueue) =
|
|||
rq[0] = 0 # so `rq[0]` works
|
||||
discard rq[0]
|
||||
|
||||
let ignoreValues = (
|
||||
let ignoreValues {.used.} = (
|
||||
(rq.append(0,0), rq.push(0,0),
|
||||
rq.replace(0,0),
|
||||
rq.prepend(0,0), rq.unshift(0,0),
|
||||
|
@ -533,10 +531,13 @@ suite "KeyedQueue: Data queue as LRU cache":
|
|||
c1 = keyList.toLruCache
|
||||
sq = toSeq(c1.q.nextPairs).mapIt(it.key.fromKey)
|
||||
s0 = sq
|
||||
inx = 5
|
||||
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}"
|
||||
|
||||
check c1.q.delete(key).value.key == key
|
||||
|
|
|
@ -70,6 +70,15 @@ block:
|
|||
|
||||
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()
|
||||
let rErrV = rErr.valueOr:
|
||||
error.len
|
||||
|
@ -88,12 +97,18 @@ block:
|
|||
doAssert c.isErr
|
||||
|
||||
# De-reference
|
||||
when (NimMajor, NimMinor) >= (1, 6):
|
||||
{.warning[BareExcept]:off.}
|
||||
|
||||
try:
|
||||
echo rErr[]
|
||||
doAssert false
|
||||
except:
|
||||
discard
|
||||
|
||||
when (NimMajor, NimMinor) >= (1, 6):
|
||||
{.warning[BareExcept]:on.}
|
||||
|
||||
# Comparisons
|
||||
doAssert (rOk == rOk)
|
||||
doAssert (rErr == rErr)
|
||||
|
@ -144,6 +159,12 @@ block:
|
|||
# Expectations
|
||||
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
|
||||
func testQn(): Result[int, string] =
|
||||
let x = ?works() - ?works()
|
||||
|
@ -356,6 +377,10 @@ block: # Result[T, void] aka `Opt`
|
|||
doAssert Opt.some(42).get() == 42
|
||||
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
|
||||
type CSRes = Result[void, cstring]
|
||||
|
||||
|
|
Loading…
Reference in New Issue