Add seek() operation with tests. (#25)
* Add seek() operation with tests. * Workaround for Nim issue with `|`. * Range length from int32 to int. seek() got renamed to advance(). Zero advance is allowed. tryAdvance() is now non-throw version of advance(). * Allow advance() to make zero length ranges.
This commit is contained in:
parent
4e6835d041
commit
49f32c604b
|
@ -9,7 +9,7 @@ type
|
||||||
when rangesGCHoldEnabled:
|
when rangesGCHoldEnabled:
|
||||||
gcHold: seq[T]
|
gcHold: seq[T]
|
||||||
start: ptr T
|
start: ptr T
|
||||||
mLen: int32
|
mLen: int
|
||||||
|
|
||||||
# A view into mutable array
|
# A view into mutable array
|
||||||
MutRange*[T] = distinct Range[T]
|
MutRange*[T] = distinct Range[T]
|
||||||
|
@ -22,13 +22,13 @@ proc toImmutableRange[T](a: seq[T]): Range[T] =
|
||||||
when rangesGCHoldEnabled:
|
when rangesGCHoldEnabled:
|
||||||
result.gcHold = a
|
result.gcHold = a
|
||||||
result.start = addr result.gcHold[0]
|
result.start = addr result.gcHold[0]
|
||||||
result.mLen = int32(a.len)
|
result.mLen = a.len
|
||||||
|
|
||||||
when unsafeAPIEnabled:
|
when unsafeAPIEnabled:
|
||||||
proc toImmutableRangeNoGCHold[T](a: openarray[T]): Range[T] =
|
proc toImmutableRangeNoGCHold[T](a: openarray[T]): Range[T] =
|
||||||
if a.len != 0:
|
if a.len != 0:
|
||||||
result.start = unsafeAddr a[0]
|
result.start = unsafeAddr a[0]
|
||||||
result.mLen = int32(a.len)
|
result.mLen = a.len
|
||||||
|
|
||||||
proc toImmutableRange[T](a: openarray[T]): Range[T] {.inline.} =
|
proc toImmutableRange[T](a: openarray[T]): Range[T] {.inline.} =
|
||||||
toImmutableRangeNoGCHold(a)
|
toImmutableRangeNoGCHold(a)
|
||||||
|
@ -122,7 +122,7 @@ proc sliceNormalized[T](r: Range[T], ibegin, iend: int): Range[T] =
|
||||||
when rangesGCHoldEnabled:
|
when rangesGCHoldEnabled:
|
||||||
result.gcHold = r.gcHold
|
result.gcHold = r.gcHold
|
||||||
result.start = r.start.shift(ibegin)
|
result.start = r.start.shift(ibegin)
|
||||||
result.mLen = int32(iend - ibegin + 1)
|
result.mLen = iend - ibegin + 1
|
||||||
|
|
||||||
proc slice*[T](r: Range[T], ibegin = 0, iend = -1): Range[T] =
|
proc slice*[T](r: Range[T], ibegin = 0, iend = -1): Range[T] =
|
||||||
let e = if iend < 0: r.len + iend
|
let e = if iend < 0: r.len + iend
|
||||||
|
@ -192,3 +192,43 @@ proc `&`*[T](a, b: Range[T]): seq[T] =
|
||||||
|
|
||||||
proc hash*(x: Range): Hash =
|
proc hash*(x: Range): Hash =
|
||||||
result = hash(toOpenArray(x))
|
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 =
|
||||||
|
## 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 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) =
|
||||||
|
## Move internal start offset of range ``x`` by ``idx`` elements forward.
|
||||||
|
let res = x.advanceImpl(idx)
|
||||||
|
if not res: raise newException(IndexError, "Advance Error")
|
||||||
|
|
|
@ -102,3 +102,54 @@ suite "Typed ranges":
|
||||||
test "toOpenArray":
|
test "toOpenArray":
|
||||||
var a = toRange(@[1,2,3])
|
var a = toRange(@[1,2,3])
|
||||||
check $a.toOpenArray == "[1, 2, 3]"
|
check $a.toOpenArray == "[1, 2, 3]"
|
||||||
|
|
||||||
|
test "tryAdvance":
|
||||||
|
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":
|
||||||
|
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 == "[]"
|
||||||
|
|
Loading…
Reference in New Issue