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/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
byte_len: int
@ -15,6 +8,8 @@ type ArrRef*[T] = ref object
arr_ptr: ptr T
arr: UncheckedArray[T]
const header_size = offsetof(ArrRef[byte], arr)
proc newArrRef*[T](size: Natural): ArrRef[T] =
when defined(TCC):
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))
# 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) =
var total = 0
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] =
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,23 +7,32 @@
##
## It's called "SliceMem" because "MemSlice" is taken by std/memfiles.
const arrRefCopyContainers {.booldefine.} = true
import std/strformat
import std/hashes
import std/macros # for noMove()
import ./arr_ref
type
SliceMem*[T] = object
when arrRefCopyContainers:
type SliceMem*[T] = object
data*: ptr UncheckedArray[T]
byte_len*: int
destroy_ref: ref CustomDestructor
CustomDestructor = object
destroy: proc() {.closure, raises: [].}
data_ref: ArrRef[T]
else:
type
SliceMem*[T] = object
data*: ptr UncheckedArray[T]
byte_len*: int
destroy_ref: ref CustomDestructor
CustomDestructor = object
destroy: proc() {.closure, raises: [].}
proc `=destroy`(s: var CustomDestructor) =
if s.destroy != nil:
s.destroy()
s.destroy = nil
proc `=destroy`(s: var CustomDestructor) =
if s.destroy != nil:
s.destroy()
s.destroy = nil
template toInt(p: pointer): int = cast[int](p)
# template toPointer(i: int): pointer = cast[pointer](p)
@ -31,23 +40,31 @@ template toInt(p: pointer): int = cast[int](p)
template toPointer*(s: SliceMem): 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.
runnableExamples:
let x = @[1,2,3,4,5]
# You can omit the [int] here because the container is iterable
let s = newSliceMem[int](x, x[0].addr, x.len * sizeof(x[0]))
result = SliceMem[T](
data: cast[ptr UncheckedArray[T]](p),
byte_len: byte_len,
destroy_ref: (ref CustomDestructor)(destroy: proc()=
# we don't actually need to destroy the container here, destroying
# the closure will do it for us. Calling it allows us to use custom
# destructors though.
discard container
),
)
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](
data: cast[ptr UncheckedArray[T]](p),
byte_len: byte_len,
destroy_ref: (ref CustomDestructor)(destroy: proc()=
# we don't actually need to destroy the container here, destroying
# the closure will do it for us. Calling it allows us to use custom
# destructors though.
discard container
),
)
proc newSliceMem*[T](p: ptr T, len: int, destructor: proc() {.closure, raises: [].}): SliceMem[T] =
# Create a SliceMem from a pointer to a type, a length, and a destructor
@ -57,11 +74,20 @@ proc newSliceMem*[T](p: ptr T, len: int, destructor: proc() {.closure, raises: [
proc destroy() = deallocShared(x)
let s = newSliceMem(x, 5, destroy)
result = SliceMem[T](
data: cast[ptr UncheckedArray[T]](p),
byte_len: len * sizeof(T),
destroy_ref: (ref CustomDestructor)(destroy: destructor),
)
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](
data: cast[ptr UncheckedArray[T]](p),
byte_len: len * sizeof(T),
destroy_ref: (ref CustomDestructor)(destroy: destructor),
)
proc newSliceMem*(p: pointer, byte_len: int, destructor: proc() {.closure, raises: [].}): SliceMem[byte] {.inline.} =
## Create a SliceMem from a pointer without type, a length in bytes, and a
@ -76,13 +102,22 @@ 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
## last elements in the container. Usually you will want to use `toSliceMem`
## instead.
result = SliceMem[T](
data: cast[ptr UncheckedArray[T]](first),
byte_len: (last.toInt -% first.toInt) + sizeof(T),
destroy_ref: (ref CustomDestructor)(destroy: proc()=
discard container
),
)
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](
data: cast[ptr UncheckedArray[T]](first),
byte_len: byte_len,
destroy_ref: (ref CustomDestructor)(destroy: proc()=
discard container
),
)
proc newSliceMem*[T](size: int): SliceMem[T] =
## Create a SliceMem from new memory, similar to ArrRef[T].
@ -91,15 +126,21 @@ proc newSliceMem*[T](size: int): SliceMem[T] =
for i,n in s.mpairs:
n = (i+1)*11
doAssert $s == "SliceMem([11, 22, 33, 44, 55, 66, 77, 88, 99, 110])"
var r: ref byte
unsafeNew(r, size * sizeof(T))
result = SliceMem[T](
data: cast[ptr UncheckedArray[T]](r),
byte_len: size * sizeof(T),
destroy_ref: (ref CustomDestructor)(destroy: proc()=
discard r
),
)
when arrRefCopyContainers:
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](
data: cast[ptr UncheckedArray[T]](r),
byte_len: size * sizeof(T),
destroy_ref: (ref CustomDestructor)(destroy: proc()=
deallocShared r
),
)
macro noMove(e: untyped): untyped =
# remove the "move" from an expression