nimtrest icon indicating copy to clipboard operation
nimtrest copied to clipboard

alignedAseq

Open ingoogni opened this issue 7 months ago • 0 comments

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

ingoogni avatar May 29 '25 13:05 ingoogni