ArrRef: Have SliceMem copy container contents instead of trying to store a reference.

This commit is contained in:
DiThi 2025-03-18 14:25:00 +01:00 committed by Alberto Torres
parent aaf89d2589
commit adc21f8e29
2 changed files with 92 additions and 53 deletions

View file

@ -1,12 +1,5 @@
import std/strformat import std/strformat
import std/hashes import std/hashes
import ./slice_mem
export slice_mem
const header_size = when not defined(release):
sizeof(pointer)*2 + sizeof(Natural)
else:
sizeof(pointer)
type ArrRef*[T] = ref object type ArrRef*[T] = ref object
byte_len: int byte_len: int
@ -15,6 +8,8 @@ type ArrRef*[T] = ref object
arr_ptr: ptr T arr_ptr: ptr T
arr: UncheckedArray[T] arr: UncheckedArray[T]
const header_size = offsetof(ArrRef[byte], arr)
proc newArrRef*[T](size: Natural): ArrRef[T] = proc newArrRef*[T](size: Natural): ArrRef[T] =
when defined(TCC): when defined(TCC):
var r: ref pointer # workaround for tcc bug var r: ref pointer # workaround for tcc bug
@ -130,9 +125,6 @@ proc hash*[T](arr: ArrRef[T]): Hash =
hash(cast[ptr UncheckedArray[byte]](arr.arr.addr).toOpenArray(0, arr.len * sizeof(T) - 1)) hash(cast[ptr UncheckedArray[byte]](arr.arr.addr).toOpenArray(0, arr.len * sizeof(T) - 1))
# TODO: for bigger elements, would a different algorithm be faster? # TODO: for bigger elements, would a different algorithm be faster?
template `[]`*[T](a: ArrRef[T], s: Slice[int]): var SliceMem[T] =
toSliceMem(a, s)
template concatImpl(arrs: untyped) = template concatImpl(arrs: untyped) =
var total = 0 var total = 0
for a in arrs: for a in arrs:
@ -152,3 +144,9 @@ proc concat*[T](arrs: seq[ArrRef[T]]): ArrRef[T] =
template `&`*[T,U](a: ArrRef[T], b: ArrRef[U]): ArrRef[T] = template `&`*[T,U](a: ArrRef[T], b: ArrRef[U]): ArrRef[T] =
concat(a, b.to(T)) concat(a, b.to(T))
import ./slice_mem
export slice_mem
template `[]`*[T](a: ArrRef[T], s: Slice[int]): var SliceMem[T] =
toSliceMem(a, s)

View file

@ -7,11 +7,20 @@
## ##
## It's called "SliceMem" because "MemSlice" is taken by std/memfiles. ## It's called "SliceMem" because "MemSlice" is taken by std/memfiles.
const arrRefCopyContainers {.booldefine.} = true
import std/strformat import std/strformat
import std/hashes import std/hashes
import std/macros # for noMove() import std/macros # for noMove()
import ./arr_ref
type when arrRefCopyContainers:
type SliceMem*[T] = object
data*: ptr UncheckedArray[T]
byte_len*: int
data_ref: ArrRef[T]
else:
type
SliceMem*[T] = object SliceMem*[T] = object
data*: ptr UncheckedArray[T] data*: ptr UncheckedArray[T]
byte_len*: int byte_len*: int
@ -20,7 +29,7 @@ type
CustomDestructor = object CustomDestructor = object
destroy: proc() {.closure, raises: [].} destroy: proc() {.closure, raises: [].}
proc `=destroy`(s: var CustomDestructor) = proc `=destroy`(s: var CustomDestructor) =
if s.destroy != nil: if s.destroy != nil:
s.destroy() s.destroy()
s.destroy = nil s.destroy = nil
@ -31,13 +40,21 @@ template toInt(p: pointer): int = cast[int](p)
template toPointer*(s: SliceMem): pointer = template toPointer*(s: SliceMem): pointer =
s.data.pointer s.data.pointer
proc newSliceMem*[T, U](container: sink U; p: pointer, byte_len: int): SliceMem[T] = proc newSliceMem*[T, U](container: sink U; p: pointer, byte_len: Natural): SliceMem[T] =
## Create a SliceMem from a container, a pointer, and a length in bytes. ## Create a SliceMem from a container, a pointer, and a length in bytes.
runnableExamples: runnableExamples:
let x = @[1,2,3,4,5] let x = @[1,2,3,4,5]
# You can omit the [int] here because the container is iterable # You can omit the [int] here because the container is iterable
let s = newSliceMem[int](x, x[0].addr, x.len * sizeof(x[0])) let s = newSliceMem[int](x, x[0].addr, x.len * sizeof(x[0]))
when arrRefCopyContainers:
result = SliceMem[T](
byte_len: byte_len,
data_ref: newArrRef[T](byte_len div sizeof(T)),
)
copyMem(result.data_ref.toPointer, p, byte_len)
result.data = cast[ptr UncheckedArray[T]](result.data_ref.toPointer)
else:
result = SliceMem[T]( result = SliceMem[T](
data: cast[ptr UncheckedArray[T]](p), data: cast[ptr UncheckedArray[T]](p),
byte_len: byte_len, byte_len: byte_len,
@ -57,6 +74,15 @@ proc newSliceMem*[T](p: ptr T, len: int, destructor: proc() {.closure, raises: [
proc destroy() = deallocShared(x) proc destroy() = deallocShared(x)
let s = newSliceMem(x, 5, destroy) let s = newSliceMem(x, 5, destroy)
when arrRefCopyContainers:
result = SliceMem[T](
byte_len: len * sizeof(T),
data_ref: newArrRef[T](len),
)
copyMem(result.data_ref.toPointer, p, len * sizeof(T))
result.data = cast[ptr UncheckedArray[T]](result.data_ref.toPointer)
destructor()
else:
result = SliceMem[T]( result = SliceMem[T](
data: cast[ptr UncheckedArray[T]](p), data: cast[ptr UncheckedArray[T]](p),
byte_len: len * sizeof(T), byte_len: len * sizeof(T),
@ -76,9 +102,18 @@ proc newSliceMem*[T, U](container: sink U; first, last: pointer): SliceMem[T] =
## Create a SliceMem from a container and the two pointers of the first and ## Create a SliceMem from a container and the two pointers of the first and
## last elements in the container. Usually you will want to use `toSliceMem` ## last elements in the container. Usually you will want to use `toSliceMem`
## instead. ## instead.
let byte_len = (last.toInt -% first.toInt) + sizeof(T)
when arrRefCopyContainers:
result = SliceMem[T](
byte_len: byte_len,
data_ref: newArrRef[T](byte_len div sizeof(T)),
)
copyMem(result.data_ref.toPointer, first, byte_len)
result.data = cast[ptr UncheckedArray[T]](result.data_ref.toPointer)
else:
result = SliceMem[T]( result = SliceMem[T](
data: cast[ptr UncheckedArray[T]](first), data: cast[ptr UncheckedArray[T]](first),
byte_len: (last.toInt -% first.toInt) + sizeof(T), byte_len: byte_len,
destroy_ref: (ref CustomDestructor)(destroy: proc()= destroy_ref: (ref CustomDestructor)(destroy: proc()=
discard container discard container
), ),
@ -91,13 +126,19 @@ proc newSliceMem*[T](size: int): SliceMem[T] =
for i,n in s.mpairs: for i,n in s.mpairs:
n = (i+1)*11 n = (i+1)*11
doAssert $s == "SliceMem([11, 22, 33, 44, 55, 66, 77, 88, 99, 110])" doAssert $s == "SliceMem([11, 22, 33, 44, 55, 66, 77, 88, 99, 110])"
var r: ref byte when arrRefCopyContainers:
unsafeNew(r, size * sizeof(T)) result = SliceMem[T](
byte_len: size * sizeof(T),
data_ref: newArrRef[T](size),
)
result.data = cast[ptr UncheckedArray[T]](result.data_ref.toPointer)
else:
var r = allocShared0 size * sizeof(T)
result = SliceMem[T]( result = SliceMem[T](
data: cast[ptr UncheckedArray[T]](r), data: cast[ptr UncheckedArray[T]](r),
byte_len: size * sizeof(T), byte_len: size * sizeof(T),
destroy_ref: (ref CustomDestructor)(destroy: proc()= destroy_ref: (ref CustomDestructor)(destroy: proc()=
discard r deallocShared r
), ),
) )