@@ -24,6 +24,14 @@ function Blob{T}(blob::Blob) where T
2424 Blob{T}(getfield(blob, :base), getfield(blob, :offset), getfield(blob, :limit))
2525end
2626
27+ function Blob{T}(blob:: Blob , rel_offset:: Int64 ) where {T}
28+ Blob{T}(
29+ getfield(blob, :base),
30+ getfield(blob, :offset) + rel_offset,
31+ getfield(blob, :limit)
32+ )
33+ end
34+
2735function assert_same_allocation(blob1:: Blob , blob2:: Blob )
2836 @noinline _throw(blob1, blob2) =
2937 throw(AssertionError(" These blobs do not share the same allocation: $blob1 - $blob2 " ))
@@ -45,12 +53,20 @@ function Base.:-(blob1::Blob, blob2::Blob)
4553 getfield(blob1, :offset) - getfield(blob2, :offset)
4654end
4755
48- function boundscheck(blob:: Blob{T} ) where T
49- begin
50- if (getfield(blob, :offset) < 0 ) || (getfield(blob, :offset) + self_size(T) > getfield(blob, :limit))
51- throw(BoundsError(blob))
52- end
53- @assert (getfield(blob, :base) != Ptr{Nothing}(0 )) " Null pointer dereference in $(typeof(blob)) "
56+ @noinline function _throw_assert_not_null_error(typename:: Symbol )
57+ throw(AssertionError(" Null pointer dereference in Blob{$(typename) }" ))
58+ end
59+
60+ @noinline function boundscheck(blob:: Blob{T} ) where T
61+ base = getfield(blob, :base)
62+ offset = getfield(blob, :offset)
63+ limit = getfield(blob, :limit)
64+ element_size = self_size(T)
65+ if (offset < 0 ) || (offset + element_size > limit)
66+ throw(BoundsError())
67+ end
68+ if base == Ptr{Nothing}(0 )
69+ _throw_assert_not_null_error(T. name. name)
5470 end
5571end
5672
@@ -124,9 +140,12 @@ _fieldidx_lookup(::Type{T}, ::Val{field}, ::Val{i}) where {T,i,field} =
124140 Blob{FT}(blob + blob_offset(T, i))
125141end
126142
143+ @noinline function _throw_getindex_boundserror(blob:: Blob , i:: Int )
144+ throw(BoundsError(blob, i))
145+ end
127146@inline function Base. getindex(blob:: Blob{T} , i:: Int ) where {T}
128147 @boundscheck if i < 1 || i > fieldcount(T)
129- throw(BoundsError( blob, i) )
148+ _throw_getindex_boundserror( blob, i)
130149 end
131150 return Blob{fieldtype(T, i)}(blob + Blobs. blob_offset(T, i))
132151end
@@ -169,33 +188,21 @@ end
169188 unsafe_store!(pointer(blob), value)
170189 value
171190 else
172- _unsafe_store_struct !(blob, value, Val(fieldcount(T)))
191+ _unsafe_store !(blob, value, Val(fieldcount(T)))
173192 value
174193 end
175194end
176195# On julia 1.11, this is equivalantly fast to the `@generated` version.
177196# On julia 1.10, this is about 2x slower than generated for medium structs: ~10 ns vs ~5 ns.
178197# We will go with the recursive version, to avoid the compilation cost.
179- @inline _unsafe_store_struct !(:: Blob{T} , :: T , :: Val{0} ) where {T} = nothing
180- function _unsafe_store_struct !(blob:: Blob{T} , value:: T , :: Val{I} ) where {T, I}
198+ @inline _unsafe_store !(:: Blob{T} , :: T , :: Val{0} ) where {T} = nothing
199+ function _unsafe_store !(blob:: Blob{T} , value:: T , :: Val{I} ) where {T, I}
181200 @inline
182201 types = fieldnames(T)
183- _unsafe_store_struct !(blob, value, Val(I- 1 ))
202+ _unsafe_store !(blob, value, Val(I- 1 ))
184203 unsafe_store!(getindex(blob, types[I]), getproperty(value, types[I]))
185204 nothing
186205end
187- # Recursive function for tuples is equivalent to unrolled via `@generated`.
188- function Base. unsafe_store!(blob:: Blob{T} , value:: T ) where {T <: Tuple }
189- _unsafe_store_tuple!(blob, value, Val(fieldcount(T)))
190- value
191- end
192- @inline _unsafe_store_tuple!(:: Blob{T} , :: T , :: Val{0} ) where {T<: Tuple } = nothing
193- function _unsafe_store_tuple!(blob:: Blob{T} , value:: T , :: Val{I} ) where {T<: Tuple , I}
194- @inline
195- _unsafe_store_struct!(blob, value, Val(I- 1 ))
196- unsafe_store!(getindex(blob, I), value[I])
197- nothing
198- end
199206
200207# if the value is the wrong type, try to convert it (just like setting a field normally)
201208function Base. unsafe_store!(blob:: Blob{T} , value) where {T}
0 commit comments