From 0fb54a8a9df7f54f82f5d63d50c9a5cdff77278f Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 18 Apr 2018 12:48:03 +0300 Subject: [PATCH] Implement helpers for using alloca-based stack arrays --- ranges/stackarray.nim | 56 ++++++++++++++++++++++++++++++++++++++++++ tests/all.nim | 3 ++- tests/tstackarrays.nim | 34 +++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 ranges/stackarray.nim create mode 100644 tests/tstackarrays.nim diff --git a/ranges/stackarray.nim b/ranges/stackarray.nim new file mode 100644 index 0000000..350a411 --- /dev/null +++ b/ranges/stackarray.nim @@ -0,0 +1,56 @@ +proc alloca(n: int): pointer {.importc, header: "".} + +type + StackArray*[T] = ptr object + bufferLen: int + buffer: UncheckedArray[T] + +template `[]`*(a: StackArray, i: int): auto = + if i < 0 or i >= a.len: raise newException(RangeError, "index out of range") + a.buffer[i] + +proc `[]=`*(a: StackArray, i: int, val: a.T) = + if i < 0 or i >= a.len: raise newException(RangeError, "index out of range") + a.buffer[i] = val + +proc len*(a: StackArray): int {.inline.} = + a.bufferLen + +template high*(a: StackArray): int = + a.bufferLen - 1 + +template low*(a: StackArray): int = + 0 + +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 allocStackArray*(T: typedesc, size: int): auto = + if size < 0: raise newException(RangeError, "allocation with a negative size") + # XXX: is it possible to perform a stack size check before + # calling `alloca`? Nim has a stackBottom pointer in the + # system module. + var + bufferSize = size * sizeof(T) + totalSize = sizeof(int) + bufferSize + arr = cast[StackArray[T]](alloca(totalSize)) + zeroMem(addr arr.buffer[0], bufferSize) + arr.bufferLen = size + arr + +template toOpenArray*(a: StackArray): auto = + toOpenArray(a.buffer, 0, a.high) + diff --git a/tests/all.nim b/tests/all.nim index 65719f6..fcd4fc0 100644 --- a/tests/all.nim +++ b/tests/all.nim @@ -1 +1,2 @@ -import ttypedranges +import + ttypedranges, tstackarrays diff --git a/tests/tstackarrays.nim b/tests/tstackarrays.nim new file mode 100644 index 0000000..a9d7abf --- /dev/null +++ b/tests/tstackarrays.nim @@ -0,0 +1,34 @@ +import + unittest, math, + ../ranges/stackarray + +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 + + 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" +