Merge branch 'master' into intops

This commit is contained in:
Jacek Sieka 2023-05-24 17:30:01 +02:00 committed by GitHub
commit 72d5338056
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 332 additions and 126 deletions

View File

@ -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

View File

@ -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

View File

@ -30,7 +30,9 @@ proc run(args, path: string) =
task test, "Run all tests": task test, "Run all tests":
build "", "tests/test_helper" build "", "tests/test_helper"
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"

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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")

View File

@ -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.

View File

@ -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: [].}

View File

@ -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.

View File

@ -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

View File

@ -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 =

View File

@ -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

View File

@ -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
View 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

View File

@ -21,3 +21,5 @@ when (NimMajor, NimMinor) < (1, 4):
RangeDefect* = RangeError RangeDefect* = RangeError
ReraiseDefect* = ReraiseError ReraiseDefect* = ReraiseError
StackOverflowDefect* = StackOverflowError StackOverflowDefect* = StackOverflowError
else:
{.used.}

View File

@ -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

View File

@ -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.}
RbPrnFn* = ##\ when (NimMajor, NimMinor) < (1, 4):
## Handle error message type
proc(code: RbInfo; ctxInfo: string) RbPrnFn* = ##\
{.gcsafe, raises: [Defect,CatchableError].} ## 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 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

View File

@ -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)

View File

@ -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

View File

@ -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]