nondescript/src/ndspkg/types/stack.nim

120 lines
3.9 KiB
Nim

import ../pointerutils
# configure stacks here
import ../config
#const boundsChecks = defined(debug)
# boundsChecks default: false, true has a large performance impact, and emitting correct code is on the compiler's job
# boundsChecking is only meant for debugging
const growthFactor = 2
# should be a natural number larger than 1
type
Stack*[T] = object
top: ptr T
start: ptr T
cap*: int
proc newStack*[T](startingCap: int): Stack[T] =
result.start = cast[ptr T](alloc(startingCap * sizeof(T)))
result.top = result.start.psub(sizeof(T))
result.cap = startingCap
proc free*[T](stack: var Stack[T]) =
## dealloc's the stack object
## if the stack contains pointers, those should be freed before destroying the stack
stack.cap = 0
stack.start.dealloc()
stack.start = nil
stack.top = nil
proc grow[T](stack: var Stack[T], len: int) =
## growth the stack's capacity and increments the top's index by one
stack.cap *= growthFactor
stack.start = cast[ptr T](realloc(stack.start, stack.cap * sizeof(T)))
stack.top = stack.start.padd(len * sizeof(T))
proc shrink[T](stack: var Stack[T]) =
discard
template high*[T](stack: Stack[T]): int =
stack.top.pdiff(stack.start) div sizeof(T)
template len*[T](stack: Stack[T]): int =
stack.high() + 1
proc push*[T](stack: var Stack[T], item: T) =
let len = stack.len()
if len == stack.cap:
stack.grow(len)
else:
stack.top = stack.top.padd(sizeof(T))
stack.top[]= item
template add*[T](stack: var Stack[T], item: T) =
stack.push(item)
proc pop*[T](stack: var Stack[T]): T =
when boundsChecks:
if stack.top == nil or stack.top.pless(stack.start):
raise newException(Defect, "Stacktop is nil or smaller than start.")
result = stack.top[]
stack.top = stack.top.psub(sizeof(T))
proc peek*[T](stack: Stack[T]): var T =
when boundsChecks:
if stack.top == nil or stack.top.pless(stack.start):
raise newException(Defect, "Stacktop is nil or smaller than start.")
stack.top[]
proc settip*[T](stack: var Stack[T], newtip: T) =
when boundsChecks:
if stack.top == nil or stack.top.pless(stack.start):
raise newException(Defect, "Stacktop is nil or smaller than start")
stack.top[]= newtip
proc swap*[T](stack: var Stack[T]) =
when boundsChecks:
if stack.top == nil or stack.len() < 2:
raise newException(Defect, "Stacktop is nil, or not high enough for swap.")
let temp = stack.top[]
var below = stack.top.psub(sizeof(T))
stack.top[] = below[]
below[] = temp
proc deleteTopN*[T](stack: var Stack[T], n: Natural) =
stack.top = stack.top.psub(sizeof(T) * n)
when boundsChecks:
if stack.top.pless(stack.start):
raise newException(Defect, "Stacktop sunk below the start after a deleteTopN.")
proc getIndex*[T](stack: Stack[T], index: int): var T =
when boundsChecks:
if index < 0 or index >= stack.len():
raise newException(Defect, &"Attempt to getIndex with an index {index} which is out of bounds (len: {stack.len()}).")
stack.start.padd(index * sizeof(T))[]
proc getIndexNeg*[T](stack: Stack[T], index: int): var T =
when boundsChecks:
if index < 0 or index >= stack.len():
raise newException(Defect, "Attempt to getIndexNeg with an index out of bounds.")
stack.top.psub(index * sizeof(T))[]
template `[]`*[T](stack: Stack[T], index: int): var T =
stack.getIndex(index)
proc setIndex*[T](stack: var Stack[T], index: int, item: T) =
when boundsChecks:
if index < 0 or index >= stack.len():
raise newException(Defect, "Attempt to getIndex with an index out of bounds.")
stack.start.padd(index * sizeof(T))[]= item
proc setIndexNeg*[T](stack: Stack[T], index: int, item: T) =
when boundsChecks:
if index < 0 or index >= stack.len():
raise newException(Defect, "Attempt to setIndexNeg with an index out of bounds.")
stack.top.psub(index * sizeof(T))[] = item
template `[]=`*[T](stack: var Stack[T], index: int, item: T) =
stack.setIndex(index, item)