add ranges
This commit is contained in:
parent
c95cee45bc
commit
904f84c8e0
23
README.md
23
README.md
|
@ -22,7 +22,7 @@ broken out into separate repositories.
|
|||
follow the following layout - if you've used C++'s `boost`, you'll feel right at
|
||||
home:
|
||||
|
||||
```
|
||||
```bash
|
||||
# Single-module libraries
|
||||
stew/small.nim # small libraries that fits in one module
|
||||
|
||||
|
@ -34,7 +34,7 @@ stew/libname/stuff.nim # Detail import file
|
|||
# support for multiple nim versions - code in here typically has been taken
|
||||
# from nim `devel` branch and `name` will reexport the corresponding std lib
|
||||
# module
|
||||
# stew/shims/macros.nim - module that reexports `macros.nim` adding code from newer nim versions
|
||||
stew/shims/macros.nim # module that reexports `macros.nim` adding code from newer nim versions
|
||||
|
||||
# Tests are in the tests folder (duh!)
|
||||
# To execute, run either `all_tests.nim` or specific `test_xxx.nim` files:
|
||||
|
@ -48,6 +48,17 @@ for different Nim versions, such that code using `stew` works well with multiple
|
|||
versions of Nim. If `stew` is not working with the Nim version you're using, we
|
||||
welcome patches.
|
||||
|
||||
You can create multiple versions of your code using the following pattern:
|
||||
|
||||
```nim
|
||||
when (NimMajor,NimMinor,NimPatch) >= (0,19,9):
|
||||
discard
|
||||
elif (NimMajor,NimMinor,NimPatch) >= (0,19,0):
|
||||
discard
|
||||
else
|
||||
{.fatal: "unsupported nim version"}
|
||||
```
|
||||
|
||||
## Notable libraries
|
||||
|
||||
Libraries are documented either in-module or on a separate README in their
|
||||
|
@ -55,6 +66,7 @@ respective folders
|
|||
|
||||
- `bitops2` - an updated version of `bitops.nim`, filling in gaps in original code
|
||||
- `byteutils` - utilities that make working with the Nim `byte` type convenient
|
||||
- `ranges` - utility functions for working with parts and blobs of memory
|
||||
- `shims` - backports of nim `devel` code to the stable version that Status is using
|
||||
|
||||
## Using stew in your project
|
||||
|
@ -64,6 +76,13 @@ are no versioned releases and we will not maintain API/ABI stability. Instead,
|
|||
make sure you pin your dependency to a specific git hash (for example using a
|
||||
submodule) or copy the file to your project instead.
|
||||
|
||||
Typically, you will import either a top-level library or drill down into its
|
||||
submodules:
|
||||
```nim
|
||||
import stew/bitops2
|
||||
import stew/ranges/ptr_arith
|
||||
```
|
||||
|
||||
:warning: No API/ABI stability - pick a commit and stick with it :warning:
|
||||
|
||||
## Contributing to stew
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import
|
||||
ranges/memranges,
|
||||
ranges/typedranges
|
||||
|
||||
export
|
||||
memranges, typedranges
|
|
@ -0,0 +1,230 @@
|
|||
import
|
||||
typedranges, ptr_arith
|
||||
|
||||
type
|
||||
BitRange* = object
|
||||
data: MutByteRange
|
||||
start: int
|
||||
mLen: int
|
||||
|
||||
BitIndexable = SomeUnsignedInt
|
||||
|
||||
template `@`(s, idx: untyped): untyped =
|
||||
(when idx is BackwardsIndex: s.len - int(idx) else: int(idx))
|
||||
|
||||
proc bits*(a: MutByteRange, start, len: int): BitRange =
|
||||
doAssert start <= len
|
||||
doAssert len <= 8 * a.len
|
||||
result.data = a
|
||||
result.start = start
|
||||
result.mLen = len
|
||||
|
||||
template bits*(a: var seq[byte], start, len: int): BitRange =
|
||||
bits(a.toRange, start, len)
|
||||
|
||||
template bits*(a: MutByteRange): BitRange =
|
||||
bits(a, 0, a.len * 8)
|
||||
|
||||
template bits*(a: var seq[byte]): BitRange =
|
||||
bits(a.toRange, 0, a.len * 8)
|
||||
|
||||
template bits*(a: MutByteRange, len: int): BitRange =
|
||||
bits(a, 0, len)
|
||||
|
||||
template bits*(a: var seq[byte], len: int): BitRange =
|
||||
bits(a.toRange, 0, len)
|
||||
|
||||
template bits*(bytes: MutByteRange, slice: HSlice): BitRange =
|
||||
bits(bytes, bytes @ slice.a, bytes @ slice.b)
|
||||
|
||||
template bits*(x: BitRange): BitRange = x
|
||||
|
||||
template mostSignificantBit(T: typedesc): auto =
|
||||
const res = 1 shl (sizeof(T) * 8 - 1)
|
||||
T(res)
|
||||
|
||||
template getBit*(x: BitIndexable, bit: Natural): bool =
|
||||
## reads a bit from `x`, assuming 0 to be the position of the
|
||||
## most significant bit
|
||||
(x and mostSignificantBit(x.type) shr bit) != 0
|
||||
|
||||
template getBitLE*(x: BitIndexable, bit: Natural): bool =
|
||||
## reads a bit from `x`, assuming 0 to be the position of the
|
||||
## least significant bit
|
||||
type T = type(x)
|
||||
(x and T(0b1 shl bit)) != 0
|
||||
|
||||
proc setBit*(x: var BitIndexable, bit: Natural, val: bool) =
|
||||
## writes a bit in `x`, assuming 0 to be the position of the
|
||||
## most significant bit
|
||||
let mask = mostSignificantBit(x.type) shr bit
|
||||
if val:
|
||||
x = x or mask
|
||||
else:
|
||||
x = x and not mask
|
||||
|
||||
proc setBitLE*(x: var BitIndexable, bit: Natural, val: bool) =
|
||||
## writes a bit in `x`, assuming 0 to be the position of the
|
||||
## least significant bit
|
||||
type T = type(x)
|
||||
let mask = 0b1 shl bit
|
||||
if val:
|
||||
x = x or mask
|
||||
else:
|
||||
x = x and not mask
|
||||
|
||||
proc raiseBit*(x: var BitIndexable, bit: Natural) =
|
||||
## raises a bit in `x`, assuming 0 to be the position of the
|
||||
## most significant bit
|
||||
type T = type(x)
|
||||
let mask = mostSignificantBit(x.type) shr bit
|
||||
x = x or mask
|
||||
|
||||
proc lowerBit*(x: var BitIndexable, bit: Natural) =
|
||||
## raises a bit in `x`, assuming 0 to be the position of the
|
||||
## most significant bit
|
||||
type T = type(x)
|
||||
let mask = mostSignificantBit(x.type) shr bit
|
||||
x = x and not mask
|
||||
|
||||
proc raiseBitLE*(x: var BitIndexable, bit: Natural) =
|
||||
## raises bit in `x`, assuming 0 to be the position of the
|
||||
## least significant bit
|
||||
type T = type(x)
|
||||
let mask = 0b1 shl bit
|
||||
x = x or mask
|
||||
|
||||
proc lowerBitLE*(x: var BitIndexable, bit: Natural) =
|
||||
## raises bit in a byte, assuming 0 to be the position of the
|
||||
## least significant bit
|
||||
type T = type(x)
|
||||
let mask = 0b1 shl bit
|
||||
x = x and not mask
|
||||
|
||||
proc len*(r: BitRange): int {.inline.} = r.mLen
|
||||
|
||||
template getAbsoluteBit(bytes, absIdx: untyped): bool =
|
||||
## Returns a bit with a position relative to the start of
|
||||
## the underlying range. Not to be confused with a position
|
||||
## relative to the start of the BitRange (i.e. the two would
|
||||
## match only when range.start == 0).
|
||||
let
|
||||
byteToCheck = absIdx shr 3 # the same as absIdx / 8
|
||||
bitToCheck = (absIdx and 0b111)
|
||||
|
||||
getBit(bytes[byteToCheck], bitToCheck)
|
||||
|
||||
template setAbsoluteBit(bytes, absIdx, value) =
|
||||
let
|
||||
byteToWrite = absIdx shr 3 # the same as absIdx / 8
|
||||
bitToWrite = (absIdx and 0b111)
|
||||
|
||||
setBit(bytes[byteToWrite], bitToWrite, value)
|
||||
|
||||
iterator enumerateBits(x: BitRange): (int, bool) =
|
||||
var p = x.start
|
||||
var i = 0
|
||||
let e = x.len
|
||||
while i != e:
|
||||
yield (i, getAbsoluteBit(x.data, p))
|
||||
inc p
|
||||
inc i
|
||||
|
||||
proc getBit*(bytes: openarray[byte], pos: Natural): bool =
|
||||
getAbsoluteBit(bytes, pos)
|
||||
|
||||
proc setBit*(bytes: var openarray[byte], pos: Natural, value: bool) =
|
||||
setAbsoluteBit(bytes, pos, value)
|
||||
|
||||
iterator items*(x: BitRange): bool =
|
||||
for _, v in enumerateBits(x): yield v
|
||||
|
||||
iterator pairs*(x: BitRange): (int, bool) =
|
||||
for i, v in enumerateBits(x): yield (i, v)
|
||||
|
||||
proc `[]`*(x: BitRange, idx: int): bool {.inline.} =
|
||||
doAssert idx < x.len
|
||||
let p = x.start + idx
|
||||
result = getAbsoluteBit(x.data, p)
|
||||
|
||||
proc sliceNormalized(x: BitRange, ibegin, iend: int): BitRange =
|
||||
doAssert ibegin >= 0 and
|
||||
ibegin < x.len and
|
||||
iend < x.len and
|
||||
iend + 1 >= ibegin # the +1 here allows the result to be
|
||||
# an empty range
|
||||
|
||||
result.data = x.data
|
||||
result.start = x.start + ibegin
|
||||
result.mLen = iend - ibegin + 1
|
||||
|
||||
proc `[]`*(r: BitRange, s: HSlice): BitRange {.inline.} =
|
||||
sliceNormalized(r, r @ s.a, r @ s.b)
|
||||
|
||||
proc `==`*(a, b: BitRange): bool =
|
||||
if a.len != b.len: return false
|
||||
for i in 0 ..< a.len:
|
||||
if a[i] != b[i]: return false
|
||||
true
|
||||
|
||||
proc `[]=`*(r: var BitRange, idx: Natural, val: bool) {.inline.} =
|
||||
doAssert idx < r.len
|
||||
let absIdx = r.start + idx
|
||||
setAbsoluteBit(r.data, absIdx, val)
|
||||
|
||||
proc setAbsoluteBit(x: BitRange, absIdx: int, val: bool) {.inline.} =
|
||||
## Assumes the destination bit is already zeroed.
|
||||
## Works with absolute positions similar to `getAbsoluteBit`
|
||||
doAssert absIdx < x.len
|
||||
let
|
||||
byteToWrite = absIdx shr 3 # the same as absIdx / 8
|
||||
bitToWrite = (absIdx and 0b111)
|
||||
|
||||
if val:
|
||||
raiseBit x.data[byteToWrite], bitToWrite
|
||||
|
||||
proc pushFront*(x: var BitRange, val: bool) =
|
||||
doAssert x.start > 0
|
||||
dec x.start
|
||||
x[0] = val
|
||||
inc x.mLen
|
||||
|
||||
template neededBytes(nBits: int): int =
|
||||
(nBits shr 3) + ord((nBits and 0b111) != 0)
|
||||
|
||||
static:
|
||||
doAssert neededBytes(2) == 1
|
||||
doAssert neededBytes(8) == 1
|
||||
doAssert neededBytes(9) == 2
|
||||
|
||||
proc `&`*(a, b: BitRange): BitRange =
|
||||
let totalLen = a.len + b.len
|
||||
|
||||
var bytes = newSeq[byte](totalLen.neededBytes)
|
||||
result = bits(bytes, 0, totalLen)
|
||||
|
||||
for i in 0 ..< a.len: result.setAbsoluteBit(i, a[i])
|
||||
for i in 0 ..< b.len: result.setAbsoluteBit(i + a.len, b[i])
|
||||
|
||||
proc `$`*(r: BitRange): string =
|
||||
result = newStringOfCap(r.len)
|
||||
for b in r:
|
||||
result.add(if b: '1' else: '0')
|
||||
|
||||
proc fromBits*(T: typedesc, r: BitRange, offset, num: Natural): T =
|
||||
doAssert(num <= sizeof(T) * 8)
|
||||
# XXX: Nim has a bug that a typedesc parameter cannot be used
|
||||
# in a type coercion, so we must define an alias here:
|
||||
type TT = T
|
||||
for i in 0 ..< num:
|
||||
result = (result shl 1) or TT(r[offset + i])
|
||||
|
||||
proc parse*(T: typedesc[BitRange], s: string): BitRange =
|
||||
var bytes = newSeq[byte](s.len.neededBytes)
|
||||
for i, c in s:
|
||||
case c
|
||||
of '0': discard
|
||||
of '1': raiseBit(bytes[i shr 3], i and 0b111)
|
||||
else: doAssert false
|
||||
result = bits(bytes, 0, s.len)
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import
|
||||
ptr_arith
|
||||
|
||||
type
|
||||
MemRange* = object
|
||||
start: pointer
|
||||
size: csize
|
||||
|
||||
template len*(mr: MemRange): int = mr.size
|
||||
template `[]`*(mr: MemRange, idx: int): byte = (cast[ptr byte](shift(mr.start, idx)))[]
|
||||
proc baseAddr*(mr: MemRange): pointer = mr.start
|
||||
|
||||
proc makeMemRange*(start: pointer, size: csize): MemRange =
|
||||
result.start = start
|
||||
result.size = size
|
||||
|
||||
proc toMemRange*(x: string): MemRange =
|
||||
result.start = x.cstring.pointer
|
||||
result.size = x.len
|
||||
|
||||
proc toMemRange*[T](x: openarray[T], fromIdx, toIdx: int): MemRange =
|
||||
doAssert(fromIdx >= 0 and toIdx >= fromIdx and fromIdx < x.len and toIdx < x.len)
|
||||
result.start = unsafeAddr x[fromIdx]
|
||||
result.size = (toIdx - fromIdx + 1) * T.sizeof
|
||||
|
||||
proc toMemRange*[T](x: openarray[T], fromIdx: int): MemRange {.inline.} =
|
||||
toMemRange(x, fromIdx, x.high)
|
||||
|
||||
proc toMemRange*[T](x: openarray[T]): MemRange {.inline.} =
|
||||
toMemRange(x, 0, x.high)
|
||||
|
||||
template toMemRange*(mr: MemRange): MemRange = mr
|
|
@ -0,0 +1,27 @@
|
|||
proc baseAddr*[T](x: openarray[T]): pointer = cast[pointer](x)
|
||||
|
||||
proc shift*(p: pointer, delta: int): pointer {.inline.} =
|
||||
cast[pointer](cast[int](p) + delta)
|
||||
|
||||
proc distance*(a, b: pointer): int {.inline.} =
|
||||
cast[int](b) - cast[int](a)
|
||||
|
||||
proc shift*[T](p: ptr T, delta: int): ptr T {.inline.} =
|
||||
cast[ptr T](shift(cast[pointer](p), delta * sizeof(T)))
|
||||
|
||||
when (NimMajor,NimMinor,NimPatch) >= (0,19,9):
|
||||
template makeOpenArray*[T](p: ptr T, len: int): auto =
|
||||
toOpenArray(cast[ptr UncheckedArray[T]](p), 0, len - 1)
|
||||
|
||||
template makeOpenArray*(p: pointer, T: type, len: int): auto =
|
||||
toOpenArray(cast[ptr UncheckedArray[T]](p), 0, len - 1)
|
||||
|
||||
elif (NimMajor,NimMinor,NimPatch) >= (0,19,0):
|
||||
# TODO: These are fallbacks until we upgrade to 0.19.9
|
||||
template makeOpenArray*(p: pointer, T: type, len: int): auto =
|
||||
toOpenArray(cast[ptr array[0, T]](p)[], 0, len - 1)
|
||||
|
||||
template makeOpenArray*[T](p: ptr T, len: int): auto =
|
||||
toOpenArray(cast[ptr array[0, T]](p)[], 0, len - 1)
|
||||
else:
|
||||
{.fatal: "Unsupported nim version".}
|
|
@ -0,0 +1,133 @@
|
|||
## Stack-allocated arrays should be used with great care.
|
||||
##
|
||||
## They pose several major risks:
|
||||
##
|
||||
## 1. They should not be used inside resumable procs
|
||||
## (i.e. closure iterators and async procs)
|
||||
##
|
||||
## Future versions of the library may automatically
|
||||
## detect such usages and flag them as errors
|
||||
##
|
||||
## 2. The user code should be certain that enough stack space
|
||||
## is available for the allocation and there will be enough
|
||||
## room for additional calls after the allocation.
|
||||
##
|
||||
## Future versions of this library may provide checks
|
||||
##
|
||||
## Please note that the stack size on certain platforms
|
||||
## may be very small (e.g. 8 to 32 kb on some Android versions)
|
||||
##
|
||||
## Before using alloca-backed arrays, consider using:
|
||||
##
|
||||
## 1. A regular stack array with a reasonable size
|
||||
##
|
||||
## 2. A global {.threadvar.} sequence that can be resized when
|
||||
## needed (only in non-reentrant procs)
|
||||
##
|
||||
## Other possible future directions:
|
||||
##
|
||||
## Instead of `alloca`, we may start using a shadow stack that will be much
|
||||
## harder to overflow. This will work by allocating a very large chunk of the
|
||||
## address space at program init (e.g. 1TB on a 64-bit system) and then by
|
||||
## gradually committing the individual pages to memory as they are requested.
|
||||
##
|
||||
## Such a scheme will even allow us to resize the stack array on demand
|
||||
## in situations where the final size is not known upfront. With a resizing
|
||||
## factor of 2, we'll never waste more than 50% of the memory which should
|
||||
## be reasonable for short-lived allocations.
|
||||
##
|
||||
|
||||
type
|
||||
StackArray*[T] = object
|
||||
bufferLen: int32
|
||||
buffer: ptr UncheckedArray[T]
|
||||
|
||||
when defined(windows):
|
||||
proc alloca(n: int): pointer {.importc, header: "<malloc.h>".}
|
||||
else:
|
||||
proc alloca(n: int): pointer {.importc, header: "<alloca.h>".}
|
||||
|
||||
proc raiseRangeError(s: string) =
|
||||
raise newException(RangeError, s)
|
||||
|
||||
proc raiseOutOfRange =
|
||||
raiseRangeError "index out of range"
|
||||
|
||||
template len*(a: StackArray): int =
|
||||
int(a.bufferLen)
|
||||
|
||||
template high*(a: StackArray): int =
|
||||
int(a.bufferLen) - 1
|
||||
|
||||
template low*(a: StackArray): int =
|
||||
0
|
||||
|
||||
template `[]`*(a: StackArray, i: int): auto =
|
||||
if i < 0 or i >= a.len: raiseOutOfRange()
|
||||
a.buffer[i]
|
||||
|
||||
proc `[]=`*(a: StackArray, i: int, val: a.T) {.inline.} =
|
||||
if i < 0 or i >= a.len: raiseOutOfRange()
|
||||
a.buffer[i] = val
|
||||
|
||||
template `[]`*(a: StackArray, i: BackwardsIndex): auto =
|
||||
if int(i) < 1 or int(i) > a.len: raiseOutOfRange()
|
||||
a.buffer[a.len - int(i)]
|
||||
|
||||
proc `[]=`*(a: StackArray, i: BackwardsIndex, val: a.T) =
|
||||
if int(i) < 1 or int(i) > a.len: raiseOutOfRange()
|
||||
a.buffer[a.len - int(i)] = val
|
||||
|
||||
iterator items*(a: StackArray): a.T =
|
||||
for i in 0 .. a.high:
|
||||
yield a.buffer[i]
|
||||
|
||||
iterator mitems*(a: var StackArray): var a.T =
|
||||
for i in 0 .. a.high:
|
||||
yield a.buffer[i]
|
||||
|
||||
iterator pairs*(a: StackArray): a.T =
|
||||
for i in 0 .. a.high:
|
||||
yield (i, a.buffer[i])
|
||||
|
||||
iterator mpairs*(a: var StackArray): (int, var a.T) =
|
||||
for i in 0 .. a.high:
|
||||
yield (i, a.buffer[i])
|
||||
|
||||
template allocaAux(sz: int, init: static[bool]): pointer =
|
||||
let s = sz
|
||||
let b = alloca(s)
|
||||
when init: zeroMem(b, s)
|
||||
b
|
||||
|
||||
template allocStackArrayAux(T: typedesc, size: int, init: static[bool]): StackArray[T] =
|
||||
let sz = int(size) # Evaluate size only once
|
||||
if sz < 0: raiseRangeError "allocation with a negative size"
|
||||
# XXX: is it possible to perform a stack size check before calling `alloca`?
|
||||
# On thread init, Nim may record the base address and the capacity of the stack,
|
||||
# so in theory we can verify that we still have enough room for the allocation.
|
||||
# Research this.
|
||||
StackArray[T](bufferLen: int32(sz), buffer: cast[ptr UncheckedArray[T]](allocaAux(sz * sizeof(T), init)))
|
||||
|
||||
template allocStackArray*(T: typedesc, size: int): StackArray[T] =
|
||||
allocStackArrayAux(T, size, true)
|
||||
|
||||
template allocStackArrayNoInit*(T: typedesc, size: int): StackArray[T] =
|
||||
allocStackArrayAux(T, size, false)
|
||||
|
||||
template getBuffer*(a: StackArray): untyped =
|
||||
when (NimMajor,NimMinor,NimPatch)>=(0,19,9):
|
||||
a.buffer
|
||||
else:
|
||||
a.buffer[]
|
||||
|
||||
template toOpenArray*(a: StackArray): auto =
|
||||
toOpenArray(a.getBuffer, 0, a.high)
|
||||
|
||||
template toOpenArray*(a: StackArray, first: int): auto =
|
||||
if first < 0 or first >= a.len: raiseOutOfRange()
|
||||
toOpenArray(a.getBuffer, first, a.high)
|
||||
|
||||
template toOpenArray*(a: StackArray, first, last: int): auto =
|
||||
if first < 0 or first >= last or last <= a.len: raiseOutOfRange()
|
||||
toOpenArray(a.getBuffer, first, last)
|
|
@ -0,0 +1,251 @@
|
|||
import ./ptr_arith, typetraits, hashes
|
||||
|
||||
const rangesGCHoldEnabled = not defined(rangesDisableGCHold)
|
||||
const unsafeAPIEnabled* = defined(rangesEnableUnsafeAPI)
|
||||
|
||||
type
|
||||
# A view into immutable array
|
||||
Range* {.shallow.} [T] = object
|
||||
when rangesGCHoldEnabled:
|
||||
gcHold: seq[T]
|
||||
start: ptr T
|
||||
mLen: int
|
||||
|
||||
# A view into mutable array
|
||||
MutRange* {.shallow.} [T] = distinct Range[T]
|
||||
|
||||
ByteRange* = Range[byte]
|
||||
MutByteRange* = MutRange[byte]
|
||||
|
||||
proc isLiteral[T](s: seq[T]): bool {.inline.} =
|
||||
type
|
||||
SeqHeader = object
|
||||
length, reserved: int
|
||||
(cast[ptr SeqHeader](s).reserved and (1 shl (sizeof(int) * 8 - 2))) != 0
|
||||
|
||||
proc toImmutableRange[T](a: seq[T]): Range[T] =
|
||||
if a.len != 0:
|
||||
when rangesGCHoldEnabled:
|
||||
if not isLiteral(a):
|
||||
shallowCopy(result.gcHold, a)
|
||||
else:
|
||||
result.gcHold = a
|
||||
result.start = addr result.gcHold[0]
|
||||
result.mLen = a.len
|
||||
|
||||
when unsafeAPIEnabled:
|
||||
proc toImmutableRangeNoGCHold[T](a: openarray[T]): Range[T] =
|
||||
if a.len != 0:
|
||||
result.start = unsafeAddr a[0]
|
||||
result.mLen = a.len
|
||||
|
||||
proc toImmutableRange[T](a: openarray[T]): Range[T] {.inline.} =
|
||||
toImmutableRangeNoGCHold(a)
|
||||
|
||||
proc toRange*[T](a: var seq[T]): MutRange[T] {.inline.} =
|
||||
MutRange[T](toImmutableRange(a))
|
||||
|
||||
when unsafeAPIEnabled:
|
||||
proc toRange*[T](a: var openarray[T]): MutRange[T] {.inline.} =
|
||||
MutRange[T](toImmutableRange(a))
|
||||
|
||||
template initStackRange*[T](sz: static[int]): MutRange[T] =
|
||||
var data: array[sz, T]
|
||||
data.toRange()
|
||||
|
||||
proc toRange*[T](a: openarray[T]): Range[T] {.inline.} = toImmutableRange(a)
|
||||
|
||||
proc unsafeRangeConstruction*[T](a: var openarray[T]): MutRange[T] {.inline.} =
|
||||
MutRange[T](toImmutableRange(a))
|
||||
|
||||
proc unsafeRangeConstruction*[T](a: openarray[T]): Range[T] {.inline.} =
|
||||
toImmutableRange(a)
|
||||
|
||||
proc newRange*[T](sz: int): MutRange[T] {.inline.} =
|
||||
MutRange[T](toImmutableRange(newSeq[T](sz)))
|
||||
|
||||
proc toRange*[T](a: seq[T]): Range[T] {.inline.} = toImmutableRange(a)
|
||||
|
||||
converter toImmutableRange*[T](a: MutRange[T]): Range[T] {.inline.} = Range[T](a)
|
||||
|
||||
proc len*(r: Range): int {.inline.} = int(r.mLen)
|
||||
|
||||
proc high*(r: Range): int {.inline.} = r.len - 1
|
||||
proc low*(r: Range): int {.inline.} = 0
|
||||
|
||||
proc elemAt[T](r: MutRange[T], idx: int): var T {.inline.} =
|
||||
doAssert(idx < r.len)
|
||||
Range[T](r).start.shift(idx)[]
|
||||
|
||||
proc `[]=`*[T](r: MutRange[T], idx: int, v: T) {.inline.} = r.elemAt(idx) = v
|
||||
proc `[]`*[T](r: MutRange[T], i: int): var T = r.elemAt(i)
|
||||
|
||||
proc `[]`*[T](r: Range[T], idx: int): T {.inline.} =
|
||||
doAssert(idx < r.len)
|
||||
r.start.shift(idx)[]
|
||||
|
||||
proc `==`*[T](a, b: Range[T]): bool =
|
||||
if a.len != b.len: return false
|
||||
equalMem(a.start, b.start, sizeof(T) * a.len)
|
||||
|
||||
iterator ptrs[T](r: Range[T]): (int, ptr T) =
|
||||
var p = r.start
|
||||
var i = 0
|
||||
let e = r.len
|
||||
while i != e:
|
||||
yield (i, p)
|
||||
p = p.shift(1)
|
||||
inc i
|
||||
|
||||
iterator items*[T](r: Range[T]): T =
|
||||
for _, v in ptrs(r): yield v[]
|
||||
|
||||
iterator pairs*[T](r: Range[T]): (int, T) =
|
||||
for i, v in ptrs(r): yield (i, v[])
|
||||
|
||||
iterator mitems*[T](r: MutRange[T]): var T =
|
||||
for _, v in ptrs(r): yield v[]
|
||||
|
||||
iterator mpairs*[T](r: MutRange[T]): (int, var T) =
|
||||
for i, v in ptrs(r): yield (i, v[])
|
||||
|
||||
proc toSeq*[T](r: Range[T]): seq[T] =
|
||||
result = newSeqOfCap[T](r.len)
|
||||
for i in r: result.add(i)
|
||||
|
||||
proc `$`*(r: Range): string =
|
||||
result = "R["
|
||||
for i, v in r:
|
||||
if i != 0:
|
||||
result &= ", "
|
||||
result &= $v
|
||||
result &= "]"
|
||||
|
||||
proc sliceNormalized[T](r: Range[T], ibegin, iend: int): Range[T] =
|
||||
doAssert ibegin >= 0 and
|
||||
ibegin < r.len and
|
||||
iend < r.len and
|
||||
iend + 1 >= ibegin # the +1 here allows the result to be
|
||||
# an empty range
|
||||
|
||||
when rangesGCHoldEnabled:
|
||||
shallowCopy(result.gcHold, r.gcHold)
|
||||
result.start = r.start.shift(ibegin)
|
||||
result.mLen = iend - ibegin + 1
|
||||
|
||||
proc slice*[T](r: Range[T], ibegin = 0, iend = -1): Range[T] =
|
||||
let e = if iend < 0: r.len + iend
|
||||
else: iend
|
||||
sliceNormalized(r, ibegin, e)
|
||||
|
||||
proc slice*[T](r: MutRange[T], ibegin = 0, iend = -1): MutRange[T] {.inline.} =
|
||||
MutRange[T](Range[T](r).slice(ibegin, iend))
|
||||
|
||||
template `^^`(s, i: untyped): untyped =
|
||||
(when i is BackwardsIndex: s.len - int(i) else: int(i))
|
||||
|
||||
proc `[]`*[T, U, V](r: Range[T], s: HSlice[U, V]): Range[T] {.inline.} =
|
||||
sliceNormalized(r, r ^^ s.a, r ^^ s.b)
|
||||
|
||||
proc `[]`*[T, U, V](r: MutRange[T], s: HSlice[U, V]): MutRange[T] {.inline.} =
|
||||
MutRange[T](sliceNormalized(r, r ^^ s.a, r ^^ s.b))
|
||||
|
||||
proc `[]=`*[T, U, V](r: MutRange[T], s: HSlice[U, V], v: openarray[T]) =
|
||||
let a = r ^^ s.a
|
||||
let b = r ^^ s.b
|
||||
let L = b - a + 1
|
||||
if L == v.len:
|
||||
for i in 0..<L: r[i + a] = v[i]
|
||||
else:
|
||||
raise newException(RangeError, "different lengths for slice assignment")
|
||||
|
||||
template toOpenArray*[T](r: Range[T]): auto =
|
||||
when false:
|
||||
# when (NimMajor,NimMinor,NimPatch)>=(0,19,9):
|
||||
# error message in Nim HEAD 2019-01-02:
|
||||
# "for a 'var' type a variable needs to be passed, but 'toOpenArray(cast[ptr UncheckedArray[T]](curHash.start), 0, high(curHash))' is immutable"
|
||||
toOpenArray(cast[ptr UncheckedArray[T]](r.start), 0, r.high)
|
||||
else:
|
||||
# NOTE: `0` in `array[0, T]` is irrelevant
|
||||
toOpenArray(cast[ptr array[0, T]](r.start)[], 0, r.high)
|
||||
|
||||
proc `[]=`*[T, U, V](r: MutRange[T], s: HSlice[U, V], v: Range[T]) {.inline.} =
|
||||
r[s] = toOpenArray(v)
|
||||
|
||||
proc baseAddr*[T](r: Range[T]): ptr T {.inline.} = r.start
|
||||
proc gcHolder*[T](r: Range[T]): ptr T {.inline.} =
|
||||
## This procedure is used only for shallow test, do not use it
|
||||
## in production.
|
||||
when rangesGCHoldEnabled:
|
||||
if r.len > 0:
|
||||
result = unsafeAddr r.gcHold[0]
|
||||
template toRange*[T](a: Range[T]): Range[T] = a
|
||||
|
||||
# this preferred syntax doesn't work
|
||||
# see https://github.com/nim-lang/Nim/issues/7995
|
||||
#template copyRange[T](dest: seq[T], destOffset: int, src: Range[T]) =
|
||||
# when supportsCopyMem(T):
|
||||
|
||||
template copyRange[T](E: typedesc, dest: seq[T], destOffset: int, src: Range[T]) =
|
||||
when supportsCopyMem(E):
|
||||
if dest.len != 0 and src.len != 0:
|
||||
copyMem(dest[destOffset].unsafeAddr, src.start, sizeof(T) * src.len)
|
||||
else:
|
||||
for i in 0..<src.len:
|
||||
dest[i + destOffset] = src[i]
|
||||
|
||||
proc concat*[T](v: varargs[Range[T], toRange]): seq[T] =
|
||||
var len = 0
|
||||
for c in v: inc(len, c.len)
|
||||
result = newSeq[T](len)
|
||||
len = 0
|
||||
for c in v:
|
||||
copyRange(T, result, len, c)
|
||||
inc(len, c.len)
|
||||
|
||||
proc `&`*[T](a, b: Range[T]): seq[T] =
|
||||
result = newSeq[T](a.len + b.len)
|
||||
copyRange(T, result, 0, a)
|
||||
copyRange(T, result, a.len, b)
|
||||
|
||||
proc hash*(x: Range): Hash =
|
||||
result = hash(toOpenArray(x))
|
||||
|
||||
template advanceImpl(a, b: untyped): bool =
|
||||
var res = false
|
||||
if b == 0:
|
||||
res = true
|
||||
elif b > 0:
|
||||
if isNil(a.start) or a.mLen <= 0:
|
||||
res = false
|
||||
else:
|
||||
if a.mLen - b < 0:
|
||||
res = false
|
||||
else:
|
||||
a.start = a.start.shift(b)
|
||||
a.mLen -= b
|
||||
res = true
|
||||
res
|
||||
|
||||
proc tryAdvance*[T](x: var Range[T], idx: int): bool =
|
||||
## Move internal start offset of range ``x`` by ``idx`` elements forward.
|
||||
##
|
||||
## Returns ``true`` if operation got completed successfully, or
|
||||
## ``false`` if you are trying to overrun range ``x``.
|
||||
result = x.advanceImpl(idx)
|
||||
|
||||
proc tryAdvance*[T](x: var MutRange[T], idx: int): bool {.inline.} =
|
||||
## Move internal start offset of range ``x`` by ``idx`` elements forward.
|
||||
##
|
||||
## Returns ``true`` if operation got completed successfully, or
|
||||
## ``false`` if you are trying to overrun range ``x``.
|
||||
result = tryAdvance(Range[T](x), idx)
|
||||
|
||||
proc advance*[T](x: var Range[T], idx: int) =
|
||||
## Move internal start offset of range ``x`` by ``idx`` elements forward.
|
||||
let res = x.advanceImpl(idx)
|
||||
if not res: raise newException(IndexError, "Advance Error")
|
||||
|
||||
proc advance*[T](x: var MutRange[T], idx: int) {.inline.} =
|
||||
## Move internal start offset of range ``x`` by ``idx`` elements forward.
|
||||
advance(Range[T](x), idx)
|
|
@ -8,4 +8,6 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
ranges/all,
|
||||
|
||||
test_byteutils
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
import
|
||||
ttypedranges, tstackarrays, tbitranges
|
|
@ -0,0 +1,83 @@
|
|||
import
|
||||
random, unittest,
|
||||
../../stew/ranges/bitranges
|
||||
|
||||
proc randomBytes(n: int): seq[byte] =
|
||||
result = newSeq[byte](n)
|
||||
for i in 0 ..< result.len:
|
||||
result[i] = byte(rand(256))
|
||||
|
||||
suite "bit ranges":
|
||||
|
||||
test "basic":
|
||||
var a = @[byte 0b10101010, 0b11110000, 0b00001111, 0b01010101]
|
||||
|
||||
var bSeq = @[byte 0b10101010, 0b00000000, 0b00000000, 0b11111111]
|
||||
var b = bits(bSeq, 8)
|
||||
|
||||
var cSeq = @[byte 0b11110000, 0b00001111, 0b00000000, 0b00000000]
|
||||
var c = bits(cSeq, 16)
|
||||
|
||||
var dSeq = @[byte 0b00001111, 0b00000000, 0b00000000, 0b00000000]
|
||||
var d = bits(dSeq, 8)
|
||||
|
||||
var eSeq = @[byte 0b01010101, 0b00000000, 0b00000000, 0b00000000]
|
||||
var e = bits(eSeq, 8)
|
||||
|
||||
var m = a.bits
|
||||
var n = m[0..7]
|
||||
check n == b
|
||||
check n.len == 8
|
||||
check b.len == 8
|
||||
check c == m[8..23]
|
||||
check $(d) == "00001111"
|
||||
check $(e) == "01010101"
|
||||
|
||||
var f = int.fromBits(e, 0, 4)
|
||||
check f == 0b0101
|
||||
|
||||
let k = n & d
|
||||
check(k.len == n.len + d.len)
|
||||
check($k == $n & $d)
|
||||
|
||||
var asciiSeq = @[byte('A'),byte('S'),byte('C'),byte('I'),byte('I')]
|
||||
let asciiBits = bits(asciiSeq)
|
||||
check $asciiBits == "0100000101010011010000110100100101001001"
|
||||
|
||||
test "concat operator":
|
||||
randomize(5000)
|
||||
|
||||
for i in 0..<256:
|
||||
var xSeq = randomBytes(rand(i))
|
||||
var ySeq = randomBytes(rand(i))
|
||||
let x = xSeq.bits
|
||||
let y = ySeq.bits
|
||||
var z = x & y
|
||||
check z.len == x.len + y.len
|
||||
check($z == $x & $y)
|
||||
|
||||
test "get set bits":
|
||||
randomize(1000)
|
||||
|
||||
for i in 0..<256:
|
||||
# produce random vector
|
||||
var xSeq = randomBytes(i)
|
||||
var ySeq = randomBytes(i)
|
||||
var x = xSeq.bits
|
||||
var y = ySeq.bits
|
||||
for idx, bit in x:
|
||||
y[idx] = bit
|
||||
check x == y
|
||||
|
||||
test "constructor with start":
|
||||
var a = @[byte 0b10101010, 0b11110000, 0b00001111, 0b01010101]
|
||||
var b = a.bits(1, 8)
|
||||
check b.len == 8
|
||||
check b[0] == false
|
||||
check $b == "01010101"
|
||||
b[0] = true
|
||||
check $b == "11010101"
|
||||
check b[0] == true
|
||||
b.pushFront(false)
|
||||
check b[0] == false
|
||||
check $b == "011010101"
|
|
@ -0,0 +1,47 @@
|
|||
import
|
||||
unittest, math,
|
||||
../../stew/ranges/[stackarrays, ptr_arith]
|
||||
|
||||
suite "Stack arrays":
|
||||
test "Basic operations work as expected":
|
||||
var arr = allocStackArray(int, 10)
|
||||
check:
|
||||
type(arr[0]) is int
|
||||
arr.len == 10
|
||||
|
||||
# all items should be initially zero
|
||||
for i in arr: check i == 0
|
||||
for i in 0 .. arr.high: check arr[i] == 0
|
||||
|
||||
arr[0] = 3
|
||||
arr[5] = 10
|
||||
arr[9] = 6
|
||||
|
||||
check:
|
||||
sum(arr.toOpenArray) == 19
|
||||
arr[5] == 10
|
||||
arr[^1] == 6
|
||||
cast[ptr int](shift(addr arr[0], 5))[] == 10
|
||||
|
||||
test "Allocating with a negative size throws a RangeError":
|
||||
expect RangeError:
|
||||
var arr = allocStackArray(string, -1)
|
||||
|
||||
test "The array access is bounds-checked":
|
||||
var arr = allocStackArray(string, 3)
|
||||
arr[2] = "test"
|
||||
check arr[2] == "test"
|
||||
expect RangeError:
|
||||
arr[3] = "another test"
|
||||
|
||||
test "proof of stack allocation":
|
||||
proc fun() =
|
||||
# NOTE: has to be inside a proc otherwise x1 not allocated on stack.
|
||||
var x1 = 0
|
||||
var arr = allocStackArray(int, 3)
|
||||
|
||||
check:
|
||||
# stack can go either up or down, hence `abs`.
|
||||
# 1024 should be large enough (was 312 on OSX).
|
||||
abs(cast[int](x1.addr) - cast[int](addr(arr[0]))) < 1024
|
||||
fun()
|
|
@ -0,0 +1,254 @@
|
|||
import
|
||||
unittest, sets,
|
||||
../../stew/ranges/[typedranges, ptr_arith]
|
||||
|
||||
suite "Typed ranges":
|
||||
test "basic stuff":
|
||||
var r = newRange[int](5)
|
||||
r[0] = 1
|
||||
r[1 .. ^1] = [2, 3, 4, 5]
|
||||
|
||||
check $r == "R[1, 2, 3, 4, 5]"
|
||||
|
||||
var s = newSeq[int]()
|
||||
for a in r: s.add(a)
|
||||
check s == @[1, 2, 3, 4, 5]
|
||||
|
||||
test "subrange":
|
||||
var a = newRange[int](5)
|
||||
let b = toRange(@[1, 2, 3])
|
||||
a[1 .. 3] = b
|
||||
check a.toSeq == @[0, 1, 2, 3, 0]
|
||||
check:
|
||||
a[2 .. 2].len == 1
|
||||
a[1 ..< 1].len == 0
|
||||
|
||||
test "equality operator":
|
||||
var x = toRange(@[0, 1, 2, 3, 4, 5])
|
||||
var y = x[1 .. ^2]
|
||||
var z = toRange(@[1, 2, 3, 4])
|
||||
check y == z
|
||||
check x != z
|
||||
|
||||
test "concat operation":
|
||||
var a = toRange(@[1,2,3])
|
||||
var b = toRange(@[4,5,6])
|
||||
var c = toRange(@[7,8,9])
|
||||
var d = @[1,2,3,4,5,6,7,8,9]
|
||||
var e = @[1,2,3,4,5,6]
|
||||
var f = @[4,5,6,7,8,9]
|
||||
var x = concat(a, b, c)
|
||||
var y = a & b
|
||||
check x == d
|
||||
check y == e
|
||||
var z = concat(b, @[7,8,9])
|
||||
check z == f
|
||||
|
||||
let u = toRange(newSeq[int](0))
|
||||
let v = toRange(@[3])
|
||||
check concat(u, v) == @[3]
|
||||
check (v & u) == @[3]
|
||||
|
||||
test "complex types concat operation":
|
||||
type
|
||||
Jaeger = object
|
||||
name: string
|
||||
weight: int
|
||||
|
||||
var A = Jaeger(name: "Gipsy Avenger", weight: 2004)
|
||||
var B = Jaeger(name: "Striker Eureka", weight: 1850)
|
||||
var C = Jaeger(name: "Saber Athena", weight: 1628)
|
||||
var D = Jaeger(name: "Cherno Alpha", weight: 2412)
|
||||
|
||||
var k = toRange(@[A, B])
|
||||
var m = toRange(@[C, D])
|
||||
var n = concat(k, m)
|
||||
check n == @[A, B, C ,D]
|
||||
check n != @[A, B, C ,C]
|
||||
|
||||
test "shallowness":
|
||||
var s = @[1, 2, 3]
|
||||
var r = s.toRange()
|
||||
var r2 = r
|
||||
s[0] = 5
|
||||
check(r[0] == 5)
|
||||
s[1] = 10
|
||||
check(r2[1] == 10)
|
||||
var r3 = r[2..2]
|
||||
s[2] = 15
|
||||
check(r3[0] == 15)
|
||||
|
||||
test "hash function":
|
||||
var a = toRange(@[1,2,3])
|
||||
var b = toRange(@[4,5,6])
|
||||
var c = toRange(@[7,8,9])
|
||||
var d = toRange(@[1,2,3,4,5,6,7,8,9])
|
||||
var e = toRange(@[1,2,3,4,5,6,7,8,9])
|
||||
var x = toSet([a, b, c, a, b])
|
||||
check x.len == 3
|
||||
check a in x
|
||||
|
||||
var z = toRange(@[7,8,9])
|
||||
var y = toSet([z, b, c])
|
||||
check z in y
|
||||
check z in x
|
||||
|
||||
var u = d[0..2]
|
||||
var v = d[3..5]
|
||||
var uu = e[0..2]
|
||||
var vv = e[3..5]
|
||||
check hash(u) != hash(v)
|
||||
check hash(uu) == hash(u)
|
||||
check hash(v) == hash(vv)
|
||||
check hash(uu) != hash(vv)
|
||||
|
||||
test "toOpenArray":
|
||||
var a = toRange(@[1,2,3])
|
||||
check $a.toOpenArray == "[1, 2, 3]"
|
||||
|
||||
test "MutRange[T] shallow test":
|
||||
var b = @[1, 2, 3, 4, 5, 6]
|
||||
var r1 = b.toRange()
|
||||
var r2 = r1
|
||||
b[0] = 5
|
||||
b[1] = 10
|
||||
b[2] = 15
|
||||
var r3 = r1[1..1]
|
||||
var a0 = cast[uint](addr b[0])
|
||||
var a1 = cast[uint](r1.gcHolder)
|
||||
var a2 = cast[uint](r2.gcHolder)
|
||||
var a3 = cast[uint](r3.gcHolder)
|
||||
check:
|
||||
a1 == a0
|
||||
a2 == a0
|
||||
a3 == a0
|
||||
|
||||
test "Range[T] shallow test":
|
||||
var r1 = toRange(@[1, 2, 3, 4, 5, 6])
|
||||
var r2 = r1
|
||||
var r3 = r1[1..1]
|
||||
var a1 = cast[uint](r1.gcHolder)
|
||||
var a2 = cast[uint](r2.gcHolder)
|
||||
var a3 = cast[uint](r3.gcHolder)
|
||||
check:
|
||||
a2 == a1
|
||||
a3 == a1
|
||||
|
||||
test "tryAdvance(Range)":
|
||||
var a: Range[int]
|
||||
check:
|
||||
a.tryAdvance(1) == false
|
||||
a.tryAdvance(-1) == false
|
||||
a.tryAdvance(0) == true
|
||||
var b = toRange(@[1, 2, 3])
|
||||
check:
|
||||
b.tryAdvance(-1) == false
|
||||
$b.toOpenArray == "[1, 2, 3]"
|
||||
b.tryAdvance(0) == true
|
||||
$b.toOpenArray == "[1, 2, 3]"
|
||||
b.tryAdvance(1) == true
|
||||
$b.toOpenArray == "[2, 3]"
|
||||
b.tryAdvance(1) == true
|
||||
$b.toOpenArray == "[3]"
|
||||
b.tryAdvance(1) == true
|
||||
$b.toOpenArray == "[]"
|
||||
b.tryAdvance(1) == false
|
||||
$b.toOpenArray == "[]"
|
||||
|
||||
test "advance(Range)":
|
||||
template aecheck(a, b): int =
|
||||
var res = 0
|
||||
try:
|
||||
a.advance(b)
|
||||
res = 1
|
||||
except IndexError:
|
||||
res = 2
|
||||
res
|
||||
|
||||
var a: Range[int]
|
||||
check:
|
||||
a.aecheck(1) == 2
|
||||
a.aecheck(-1) == 2
|
||||
a.aecheck(0) == 1
|
||||
var b = toRange(@[1, 2, 3])
|
||||
check:
|
||||
b.aecheck(-1) == 2
|
||||
$b.toOpenArray == "[1, 2, 3]"
|
||||
b.aecheck(0) == 1
|
||||
$b.toOpenArray == "[1, 2, 3]"
|
||||
b.aecheck(1) == 1
|
||||
$b.toOpenArray == "[2, 3]"
|
||||
b.aecheck(1) == 1
|
||||
$b.toOpenArray == "[3]"
|
||||
b.aecheck(1) == 1
|
||||
$b.toOpenArray == "[]"
|
||||
b.aecheck(1) == 2
|
||||
$b.toOpenArray == "[]"
|
||||
|
||||
test "tryAdvance(MutRange)":
|
||||
var a: MutRange[int]
|
||||
check:
|
||||
a.tryAdvance(1) == false
|
||||
a.tryAdvance(-1) == false
|
||||
a.tryAdvance(0) == true
|
||||
var buf = @[1, 2, 3]
|
||||
var b = toRange(buf)
|
||||
check:
|
||||
b.tryAdvance(-1) == false
|
||||
$b.toOpenArray == "[1, 2, 3]"
|
||||
b.tryAdvance(0) == true
|
||||
$b.toOpenArray == "[1, 2, 3]"
|
||||
b.tryAdvance(1) == true
|
||||
$b.toOpenArray == "[2, 3]"
|
||||
b.tryAdvance(1) == true
|
||||
$b.toOpenArray == "[3]"
|
||||
b.tryAdvance(1) == true
|
||||
$b.toOpenArray == "[]"
|
||||
b.tryAdvance(1) == false
|
||||
$b.toOpenArray == "[]"
|
||||
|
||||
test "advance(MutRange)":
|
||||
template aecheck(a, b): int =
|
||||
var res = 0
|
||||
try:
|
||||
a.advance(b)
|
||||
res = 1
|
||||
except IndexError:
|
||||
res = 2
|
||||
res
|
||||
|
||||
var a: MutRange[int]
|
||||
check:
|
||||
a.aecheck(1) == 2
|
||||
a.aecheck(-1) == 2
|
||||
a.aecheck(0) == 1
|
||||
var buf = @[1, 2, 3]
|
||||
var b = toRange(buf)
|
||||
check:
|
||||
b.aecheck(-1) == 2
|
||||
$b.toOpenArray == "[1, 2, 3]"
|
||||
b.aecheck(0) == 1
|
||||
$b.toOpenArray == "[1, 2, 3]"
|
||||
b.aecheck(1) == 1
|
||||
$b.toOpenArray == "[2, 3]"
|
||||
b.aecheck(1) == 1
|
||||
$b.toOpenArray == "[3]"
|
||||
b.aecheck(1) == 1
|
||||
$b.toOpenArray == "[]"
|
||||
b.aecheck(1) == 2
|
||||
$b.toOpenArray == "[]"
|
||||
|
||||
test "make openarrays from pointers":
|
||||
var str = "test 1,2,3"
|
||||
var charPtr: ptr char = addr str[7]
|
||||
var regularPtr: pointer = addr str[5]
|
||||
|
||||
check:
|
||||
# (regularPtr.makeOpenArray(char, 4).len == 4)
|
||||
(regularPtr.makeOpenArray(char, 5) == "1,2,3")
|
||||
(regularPtr.makeOpenArray(char, 5) == str[5..9])
|
||||
|
||||
# (charPtr.makeOpenArray(3).len == 3)
|
||||
(charPtr.makeOpenArray(3) == "2,3")
|
||||
(charPtr.makeOpenArray(1) == str[7..7])
|
||||
|
Loading…
Reference in New Issue