import ../pointerutils # configure stacks here 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) {.inline.} = ## 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]) {.inline.} = 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) {.inline.} = 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 {.inline.} = 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 {.inline.} = 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) {.inline.} = 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 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): 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)