Yes, something like that. I’d combine the constructors, to ensure your invariants hold and to make using it a little easier - no need for the explicit Nothing dispatch:
struct Workspace{T}
data::Vector{UInt8}
wrapper::Vector{T}
function Workspace{T}(len, data=Vector{UInt8}(undef, len*Base.aligned_sizeof(T))) where T
div(length(data), len) != Base.aligned_sizeof(T) && throw(ArgumentError("Backing buffer too small")) # or resize!
new{T}(data, unsafe_wrap(Vector{T}, Ptr{T}(pointer(data)), len))
end
end
function Workspace{T}(len, w::Workspace) where T
length(w.data) < len*Base.aligned_sizeof(T) && resize!(w.data, len*Base.aligned_sizeof(T))
Workspace{T}(len, w.data)
end
# further array wrappers...
To me at least it feels a bit cleaner to just specify the type parameter, like for Vector. The outer constructor may need some form of explicit ownership transfer off of the original Workspace data - wouldn’t want to have two live Workspace objects actually referencing the same exact memory ![]()
Writing the above also felt a lot like a potential Buffer{T} type that e.g. @tkf and I think @jameson are thinking about. There are some notes in the multithreading BoF from May 4th, but nothing concrete yet I don’t think. At least at its core this feels very much like the sort of problems that type could encounter as well.
Either way, here’s an example of the kinds of problems I was thinking of with my comments about explicitly having to model the Ptr{T} and doing the (un)marshaling yourself:
julia> struct A
s::String # to force this struct to not be allocated inline
end
# works?
julia> w = Workspace{A}(5)
Workspace{A}(UInt8[0x20, 0x2b, 0xe2, 0xeb, 0x90, 0x7f, 0x00, 0x00, 0x50, 0x43 … 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], A[A(Nothing), A(Any), #undef, #undef, #undef])
# horribly dies, due to the memory layout not being valid for `A` when accessing that in any way, i.e. no printing of this data allowed, at all.
julia> w = Workspace{A}(5)
Workspace{A}(UInt8[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x77 … 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], A[#undef, A(Core.Compiler.NewNodeInfo[]), A(
signal (11): Segmentation fault
in expression starting at none:0
sig_match_fast at /home/sukera/julia/src/gf.c:2411 [inlined]
sig_match_fast at /home/sukera/julia/src/gf.c:2402 [inlined]
jl_lookup_generic_ at /home/sukera/julia/src/gf.c:2491 [inlined]
ijl_apply_generic at /home/sukera/julia/src/gf.c:2570
_show_default at ./show.jl:465
show_default at ./show.jl:448 [inlined]
show at ./show.jl:443 [inlined]
show_delim_array at ./show.jl:1276
...
That particular segfault may be mitigated by using zeros(UInt8, len*Base.aligned_sizeof(T)) and making sure any resized vectors are zeroed as well, but there are bound to be more like this.