nimtrest
nimtrest copied to clipboard
alignedAseq
Thanks for the alignedSeq.
Off course it didn't work for windows, so I added that. Then there where still a few things as noted in the file. Then I asked one of 'the things' to add some test. So, pick what you like.
import std/math
type
Payload[T] = ptr UncheckedArray[T]
AlignedSeq*[T] = object
len: int
cap: int
alignment: int
data: Payload[T]
when defined(windows):
proc alignedAlloc(size, align: uint): pointer {.importc: "_aligned_malloc", cdecl, header: "<malloc.h>".}
proc alignedFree(p: pointer) {.importc: "_aligned_free", header: "<malloc.h>", cdecl.}
else:
proc alignedAlloc(align, size: uint): pointer {.importc: "aligned_alloc", cdecl, header: "<stdlib.h>".}
proc alignedFree(p: pointer) {.importc: "free", header: "<stdlib.h>", cdecl.}
proc allocAligned*(size, alignment: uint): pointer =
when defined(windows):
return alignedAlloc(size, alignment)
else:
return alignedAlloc(alignment, size)
proc `=destroy`[T](aseq: var AlignedSeq[T]) =
for i in 0..<aseq.len:
reset aseq.data[i]
if aseq.data != nil:
alignedFree(aseq.data)
reset aseq.data
aseq.len = 0
proc `=copy`[T](a: var AlignedSeq[T], b: AlignedSeq[T]) {.error.}
proc `=dup`[T](a: AlignedSeq[T]): AlignedSeq[T] {.error.}
proc payloadSize[T](alignment, len: int): int =
lcm(len * sizeof(T), alignment)
proc allocPayload[T](alignment, len: int): Payload[T] =
when defined(debugAseq):
echo "Allocating: ", (Alignment: alignment, Len: len, Payload: payloadSize[T](alignment, len))
result = cast[Payload[T]](allocAligned(uint payloadSize[T](alignment, len), uint alignment))
assert result != nil
proc init*[T](t: typedesc[AlignedSeq[T]], alignment: int, len: int, zeroMemory: bool = true): t =
result = t(
len: len,
alignment: alignment,
data: allocPayload[T](alignment, len)
)
if zeroMemory:
result.data.zeroMem(uint payloadSize[T](alignment, len))
result.cap = len
proc init*[T](t: typedesc[AlignedSeq[T]], alignment: int, cap: int = 64, zeroMemory: bool = true): t =
result = t(
len: 0,
alignment: alignment,
data: allocPayload[T](alignment, cap)
)
if zeroMemory:
result.data.zeroMem(uint payloadSize[T](alignment, cap))
result.cap = cap
proc setLen*[T](aseq: var AlignedSeq[T], len: int) =
when defined(debugAseq):
echo "Set len: ", len, " from ", aseq.len
if len == aseq.len:
return
elif len <= aseq.cap:
for i in len..<aseq.len:
reset aseq.data[i]
aseq.len = len
else:
let
newSize =
if aseq.data == nil:
len
else:
aseq.cap div 2 * 3
startLen = aseq.len
oldData = aseq.data
aseq.data = allocPayload[T](aseq.alignment, newSize)
if startLen > 0:
copyMem(aseq.data, oldData, payloadSize[T](aseq.alignment, startLen))
zeroMem(aseq.data[startLen].addr, (newSize - startLen) * sizeof(T))
else:
zeroMem(aseq.data[startLen].addr, newSize * sizeof(T))
if oldData != nil:
alignedFree(oldData)
aseq.cap = newSize
aseq.len = len
assert aseq.len == len
proc add*[T](aseq: var AlignedSeq[T], val: sink T) =
if aseq.len == aseq.cap:
let
newSize = aseq.cap div 2 * 3
oldData = aseq.data
oldCap = aseq.cap
aseq.data = allocPayload[T](aseq.alignment, newSize)
copyMem(aseq.data, oldData, payloadSize[T](aseq.alignment, oldCap))
alignedFree(oldData)
aseq.cap = newSize
aseq.data[aseq.len] = val # Fixed: was aseq.data.data[aseq.len - 1]
inc aseq.len
template indexCheck[T](aseq: AlignedSeq[T], ind: int) = # Fixed: was s instead of aseq
when compileOption("boundChecks"):
if ind notin 0..<s.len:
{.line: instantiationInfo(-1).}:
raise (ref IndexDefect)(msg: "Index " & $ind & " out of range 0..<" & $s.len & ".")
proc `[]`*[T](aseq: AlignedSeq[T], ind: int): lent T =
aseq.indexCheck(ind)
aseq.data[ind]
proc `[]`*[T](aseq: var AlignedSeq[T], ind: int): var T =
aseq.indexCheck(ind)
aseq.data[ind]
proc `[]=`*[T](aseq: var AlignedSeq[T], ind: int, val: sink T) =
aseq.indexCheck(ind)
aseq.data[ind] = val
proc len*[T](aseq: AlignedSeq[T]): int = aseq.len
proc high*[T](aseq: AlignedSeq[T]): int = aseq.len - 1
proc alignment*[T](aseq: AlignedSeq[T]): int = aseq.alignment
proc delete*[T](aseq: var AlignedSeq[T], ind: int) =
aseq.indexCheck(ind)
# Shift elements left to fill the gap
for i in ind..<(aseq.len - 1):
aseq[i] = aseq[i + 1]
dec aseq.len
proc delete*[T](aseq: var AlignedSeq[T], rng: Slice[int]) =
when defined(debugAseq):
echo "Deleting ", $rng , " from seq with length: " , aseq.len
for i in rng:
aseq.indexCheck(i)
# Move elements to fill the gap
let rangeLen = rng.len
for i in rng.b + 1..<aseq.len:
aseq[i - rangeLen] = aseq[i]
dec aseq.len, rangeLen
when defined(debugAseq):
echo "Len is now: ", aseq.len
#Fixed: removed double data.data for all iterators
iterator items*[T](aseq: AlignedSeq[T]): lent T =
for i in 0..<aseq.len:
yield aseq.data[i]
iterator mitems*[T](aseq: var AlignedSeq[T]): var T =
for i in 0..<aseq.len:
yield aseq.data[i]
iterator pairs*[T](aseq: AlignedSeq[T]): (int, lent T) =
for i in 0..<aseq.len:
yield (i, aseq.data[i])
iterator mpairs*[T](aseq: var AlignedSeq[T]): (int, var T) =
for i in 0..<aseq.len:
yield (i, aseq.data[i])
proc `$`*[T](aseq: AlignedSeq[T]): string =
result = "@["
for i in 0..<aseq.len:
if i > 0: result.add(", ")
result.add($aseq.data[i])
result.add("]")
result.add(" (alignment: " & $aseq.alignment & ", capacity: " & $aseq.cap & ")")
when isMainModule:
# 1: Basic initialization
echo "\n1. Basic initialization:"
var s = init(AlignedSeq[float32], 32, cap = 64)
echo "Created empty AlignedSeq: ", s
echo "Length: ", s.len, ", Capacity: ", s.cap, ", Alignment: ", s.alignment
# 2: Adding elements
echo "\n2. Adding elements:"
s.add(1.5)
s.add(2.7)
s.add(3.14)
s.add(42.0)
echo "After adding 4 elements: ", s
echo "Length: ", s.len
#3: Access elements
echo "\n3. Accessing elements:"
echo "s[0] = ", s[0]
echo "s[2] = ", s[2]
echo "s.high = ", s.high
#4: Modify elements
echo "\n4. Modifying elements:"
s[1] = 99.9
echo "After s[1] = 99.9: ", s
#5: Iterator tests
echo "\n5. Iterator tests:"
echo "Items: "
for item in s.items:
echo " ", item
echo "Pairs: "
for i, item in s.pairs:
echo " [", i, "] = ", item
#6: setLen tests
echo "\n6. setLen tests:"
s.setLen(6)
echo "After setLen(6): ", s
echo "Length: ", s.len
s.setLen(2)
echo "After setLen(2): ", s
echo "Length: ", s.len
#7: Delete single element
echo "\n7. Delete single element:"
s.add(10.1)
s.add(20.2)
s.add(30.3)
echo "Before delete: ", s
s.delete(1) # Delete element at index 1
echo "After delete(1): ", s
#8: Delete range
echo "\n8. Delete range:"
s.add(100.0)
s.add(200.0)
s.add(300.0)
echo "Before range delete: ", s
s.delete(1..2) # Delete elements at indices 1 and 2
echo "After delete(1..2): ", s
#9: Capacity growth test
echo "\n9. Capacity growth test:"
var s2 = init(AlignedSeq[int], 16, cap = 4)
echo "Initial small capacity seq: ", s2
echo "Capacity: ", s2.cap
for i in 1..8:
s2.add(i * 10)
echo "After adding 8 elements: ", s2
echo "New capacity: ", s2.cap
#10: Initialize with specific length
echo "\n10. Initialize with specific length:"
var s3 = init(AlignedSeq[float32], 64, len = 5, zeroMemory = true)
echo "Initialized with len=5: ", s3
s3[0] = 1.1
s3[4] = 5.5
echo "After setting s3[0] and s3[4]: ", s3
#11: Mutable iterator test
echo "\n11. Mutable iterator test:"
echo "Before modification: ", s3
for item in s3.mitems:
item += 10.0
echo "After adding 10 to each element: ", s3
#12: Edge case - empty sequence operations
echo "\n12. Edge case tests:"
var empty = init(AlignedSeq[int], 32, cap = 4)
echo "Empty sequence: ", empty
echo "Length of empty: ", empty.len
echo "High of empty: ", empty.high