• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37433

pending completion
#37433

push

local

web-flow
Merge pull request #48513 from JuliaLang/jn/extend-once

ensure extension triggers are only run by the package that satified them

60 of 60 new or added lines in 1 file covered. (100.0%)

72324 of 82360 relevant lines covered (87.81%)

31376331.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.83
/base/reinterpretarray.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
"""
4
Gives a reinterpreted view (of element type T) of the underlying array (of element type S).
5
If the size of `T` differs from the size of `S`, the array will be compressed/expanded in
6
the first dimension. The variant `reinterpret(reshape, T, a)` instead adds or consumes the first dimension
7
depending on the ratio of element sizes.
8
"""
9
struct ReinterpretArray{T,N,S,A<:AbstractArray{S},IsReshaped} <: AbstractArray{T, N}
10
    parent::A
11
    readable::Bool
12
    writable::Bool
13

14
    function throwbits(S::Type, T::Type, U::Type)
9✔
15
        @noinline
9✔
16
        throw(ArgumentError("cannot reinterpret `$(S)` as `$(T)`, type `$(U)` is not a bits type"))
9✔
17
    end
18
    function throwsize0(S::Type, T::Type, msg)
5✔
19
        @noinline
5✔
20
        throw(ArgumentError("cannot reinterpret a zero-dimensional `$(S)` array to `$(T)` which is of a $msg size"))
5✔
21
    end
22
    function throwsingleton(S::Type, T::Type)
5✔
23
        @noinline
5✔
24
        throw(ArgumentError("cannot reinterpret a `$(S)` array to `$(T)` which is a singleton type"))
5✔
25
    end
26

27
    global reinterpret
28

29
    """
30
        reinterpret(T::DataType, A::AbstractArray)
31

32
    Construct a view of the array with the same binary data as the given
33
    array, but with `T` as element type.
34

35
    This function also works on "lazy" array whose elements are not computed until they are explicitly retrieved.
36
    For instance, `reinterpret` on the range `1:6` works similarly as on the dense vector `collect(1:6)`:
37

38
    ```jldoctest
39
    julia> reinterpret(Float32, UInt32[1 2 3 4 5])
40
    1×5 reinterpret(Float32, ::Matrix{UInt32}):
41
    1.0f-45  3.0f-45  4.0f-45  6.0f-45  7.0f-45
42

43
    julia> reinterpret(Complex{Int}, 1:6)
44
    3-element reinterpret(Complex{$Int}, ::UnitRange{$Int}):
45
    1 + 2im
46
    3 + 4im
47
    5 + 6im
48
    ```
49
    """
50
    function reinterpret(::Type{T}, a::A) where {T,N,S,A<:AbstractArray{S, N}}
24,933✔
51
        function thrownonint(S::Type, T::Type, dim)
35,517✔
52
            @noinline
2✔
53
            throw(ArgumentError("""
2✔
54
                cannot reinterpret an `$(S)` array to `$(T)` whose first dimension has size `$(dim)`.
55
                The resulting array would have non-integral first dimension.
56
                """))
57
        end
58
        function throwaxes1(S::Type, T::Type, ax1)
5,400✔
59
            @noinline
2✔
60
            throw(ArgumentError("cannot reinterpret a `$(S)` array to `$(T)` when the first axis is $ax1. Try reshaping first."))
2✔
61
        end
62
        isbitstype(T) || throwbits(S, T, T)
5,398✔
63
        isbitstype(S) || throwbits(S, T, S)
5,395✔
64
        (N != 0 || sizeof(T) == sizeof(S)) || throwsize0(S, T, "different")
5,392✔
65
        if N != 0 && sizeof(S) != sizeof(T)
37,471✔
66
            ax1 = axes(a)[1]
35,087✔
67
            dim = length(ax1)
4,970✔
68
            if issingletontype(T)
4,970✔
69
                issingletontype(S) || throwsingleton(S, T)
2✔
70
            else
71
                rem(dim*sizeof(S),sizeof(T)) == 0 || thrownonint(S, T, dim)
35,087✔
72
            end
73
            first(ax1) == 1 || throwaxes1(S, T, ax1)
35,085✔
74
        end
75
        readable = array_subpadding(T, S)
5,382✔
76
        writable = array_subpadding(S, T)
5,382✔
77
        new{T, N, S, A, false}(a, readable, writable)
37,465✔
78
    end
79
    reinterpret(::Type{T}, a::AbstractArray{T}) where {T} = a
128✔
80

81
    # With reshaping
82
    function reinterpret(::typeof(reshape), ::Type{T}, a::A) where {T,S,A<:AbstractArray{S}}
774✔
83
        function throwintmult(S::Type, T::Type)
775✔
84
            @noinline
1✔
85
            throw(ArgumentError("`reinterpret(reshape, T, a)` requires that one of `sizeof(T)` (got $(sizeof(T))) and `sizeof(eltype(a))` (got $(sizeof(S))) be an integer multiple of the other"))
1✔
86
        end
87
        function throwsize1(a::AbstractArray, T::Type)
776✔
88
            @noinline
2✔
89
            throw(ArgumentError("`reinterpret(reshape, $T, a)` where `eltype(a)` is $(eltype(a)) requires that `axes(a, 1)` (got $(axes(a, 1))) be equal to 1:$(sizeof(T) ÷ sizeof(eltype(a))) (from the ratio of element sizes)"))
2✔
90
        end
91
        function throwfromsingleton(S, T)
777✔
92
            @noinline
3✔
93
            throw(ArgumentError("`reinterpret(reshape, $T, a)` where `eltype(a)` is $S requires that $T be a singleton type, since $S is one"))
3✔
94
        end
95
        isbitstype(T) || throwbits(S, T, T)
774✔
96
        isbitstype(S) || throwbits(S, T, S)
772✔
97
        if sizeof(S) == sizeof(T)
771✔
98
            N = ndims(a)
44✔
99
        elseif sizeof(S) > sizeof(T)
727✔
100
            issingletontype(T) && throwsingleton(S, T)
684✔
101
            rem(sizeof(S), sizeof(T)) == 0 || throwintmult(S, T)
681✔
102
            N = ndims(a) + 1
681✔
103
        else
104
            issingletontype(S) && throwfromsingleton(S, T)
43✔
105
            rem(sizeof(T), sizeof(S)) == 0 || throwintmult(S, T)
40✔
106
            N = ndims(a) - 1
39✔
107
            N > -1 || throwsize0(S, T, "larger")
39✔
108
            axes(a, 1) == OneTo(sizeof(T) ÷ sizeof(S)) || throwsize1(a, T)
40✔
109
        end
110
        readable = array_subpadding(T, S)
761✔
111
        writable = array_subpadding(S, T)
761✔
112
        new{T, N, S, A, true}(a, readable, writable)
761✔
113
    end
114
    reinterpret(::typeof(reshape), ::Type{T}, a::AbstractArray{T}) where {T} = a
1✔
115
end
116

117
ReshapedReinterpretArray{T,N,S,A<:AbstractArray{S}} = ReinterpretArray{T,N,S,A,true}
118
NonReshapedReinterpretArray{T,N,S,A<:AbstractArray{S, N}} = ReinterpretArray{T,N,S,A,false}
119

120
"""
121
    reinterpret(reshape, T, A::AbstractArray{S}) -> B
122

123
Change the type-interpretation of `A` while consuming or adding a "channel dimension."
124

125
If `sizeof(T) = n*sizeof(S)` for `n>1`, `A`'s first dimension must be
126
of size `n` and `B` lacks `A`'s first dimension. Conversely, if `sizeof(S) = n*sizeof(T)` for `n>1`,
127
`B` gets a new first dimension of size `n`. The dimensionality is unchanged if `sizeof(T) == sizeof(S)`.
128

129
!!! compat "Julia 1.6"
130
    This method requires at least Julia 1.6.
131

132
# Examples
133

134
```jldoctest
135
julia> A = [1 2; 3 4]
136
2×2 Matrix{$Int}:
137
 1  2
138
 3  4
139

140
julia> reinterpret(reshape, Complex{Int}, A)    # the result is a vector
141
2-element reinterpret(reshape, Complex{$Int}, ::Matrix{$Int}) with eltype Complex{$Int}:
142
 1 + 3im
143
 2 + 4im
144

145
julia> a = [(1,2,3), (4,5,6)]
146
2-element Vector{Tuple{$Int, $Int, $Int}}:
147
 (1, 2, 3)
148
 (4, 5, 6)
149

150
julia> reinterpret(reshape, Int, a)             # the result is a matrix
151
3×2 reinterpret(reshape, $Int, ::Vector{Tuple{$Int, $Int, $Int}}) with eltype $Int:
152
 1  4
153
 2  5
154
 3  6
155
```
156
"""
157
reinterpret(::typeof(reshape), T::Type, a::AbstractArray)
158

159
reinterpret(::Type{T}, a::NonReshapedReinterpretArray) where {T} = reinterpret(T, a.parent)
127✔
160
reinterpret(::typeof(reshape), ::Type{T}, a::ReshapedReinterpretArray) where {T} = reinterpret(reshape, T, a.parent)
1✔
161

162
# Definition of StridedArray
163
StridedFastContiguousSubArray{T,N,A<:DenseArray} = FastContiguousSubArray{T,N,A}
164
StridedReinterpretArray{T,N,A<:Union{DenseArray,StridedFastContiguousSubArray},IsReshaped} = ReinterpretArray{T,N,S,A,IsReshaped} where S
165
StridedReshapedArray{T,N,A<:Union{DenseArray,StridedFastContiguousSubArray,StridedReinterpretArray}} = ReshapedArray{T,N,A}
166
StridedSubArray{T,N,A<:Union{DenseArray,StridedReshapedArray,StridedReinterpretArray},
167
    I<:Tuple{Vararg{Union{RangeIndex, ReshapedUnitRange, AbstractCartesianIndex}}}} = SubArray{T,N,A,I}
168
StridedArray{T,N} = Union{DenseArray{T,N}, StridedSubArray{T,N}, StridedReshapedArray{T,N}, StridedReinterpretArray{T,N}}
169
StridedVector{T} = StridedArray{T,1}
170
StridedMatrix{T} = StridedArray{T,2}
171
StridedVecOrMat{T} = Union{StridedVector{T}, StridedMatrix{T}}
172

173
strides(a::Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}) = size_to_strides(1, size(a)...)
1,002,909✔
174
stride(A::Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}, k::Integer) =
1,355,528✔
175
    k ≤ ndims(A) ? strides(A)[k] : length(A)
176

177
function strides(a::ReinterpretArray{T,<:Any,S,<:AbstractArray{S},IsReshaped}) where {T,S,IsReshaped}
3,031✔
178
    _checkcontiguous(Bool, a) && return size_to_strides(1, size(a)...)
3,031✔
179
    stp = strides(parent(a))
3,010✔
180
    els, elp = sizeof(T), sizeof(S)
3,010✔
181
    els == elp && return stp # 0dim parent is also handled here.
3,010✔
182
    IsReshaped && els < elp && return (1, _checked_strides(stp, els, elp)...)
1,880✔
183
    stp[1] == 1 || throw(ArgumentError("Parent must be contiguous in the 1st dimension!"))
670✔
184
    st′ = _checked_strides(tail(stp), els, elp)
645✔
185
    return IsReshaped ? st′ : (1, st′...)
607✔
186
end
187

188
@inline function _checked_strides(stp::Tuple, els::Integer, elp::Integer)
1,858✔
189
    if elp > els && rem(elp, els) == 0
1,858✔
190
        N = div(elp, els)
1,701✔
191
        return map(i -> N * i, stp)
5,866✔
192
    end
193
    drs = map(i -> divrem(elp * i, els), stp)
378✔
194
    all(i->iszero(i[2]), drs) ||
393✔
195
        throw(ArgumentError("Parent's strides could not be exactly divided!"))
196
    map(first, drs)
138✔
197
end
198

199
_checkcontiguous(::Type{Bool}, A::ReinterpretArray) = _checkcontiguous(Bool, parent(A))
3,117✔
200

201
similar(a::ReinterpretArray, T::Type, d::Dims) = similar(a.parent, T, d)
10,598✔
202

203
function check_readable(a::ReinterpretArray{T, N, S} where N) where {T,S}
1✔
204
    # See comment in check_writable
205
    if !a.readable && !array_subpadding(T, S)
1✔
206
        throw(PaddingError(T, S))
1✔
207
    end
208
end
209

210
function check_writable(a::ReinterpretArray{T, N, S} where N) where {T,S}
1✔
211
    # `array_subpadding` is relatively expensive (compared to a simple arrayref),
212
    # so it is cached in the array. However, it is computable at compile time if,
213
    # inference has the types available. By using this form of the check, we can
214
    # get the best of both worlds for the success case. If the types were not
215
    # available to inference, we simply need to check the field (relatively cheap)
216
    # and if they were we should be able to fold this check away entirely.
217
    if !a.writable && !array_subpadding(S, T)
1✔
218
        throw(PaddingError(T, S))
1✔
219
    end
220
end
221

222
## IndexStyle specializations
223

224
# For `reinterpret(reshape, T, a)` where we're adding a channel dimension and with
225
# `IndexStyle(a) == IndexLinear()`, it's advantageous to retain pseudo-linear indexing.
226
struct IndexSCartesian2{K} <: IndexStyle end   # K = sizeof(S) ÷ sizeof(T), a static-sized 2d cartesian iterator
1,358✔
227

228
IndexStyle(::Type{ReinterpretArray{T,N,S,A,false}}) where {T,N,S,A<:AbstractArray{S,N}} = IndexStyle(A)
341✔
229
function IndexStyle(::Type{ReinterpretArray{T,N,S,A,true}}) where {T,N,S,A<:AbstractArray{S}}
1,379✔
230
    if sizeof(T) < sizeof(S)
1,379✔
231
        IndexStyle(A) === IndexLinear() && return IndexSCartesian2{sizeof(S) ÷ sizeof(T)}()
1,364✔
232
        return IndexCartesian()
9✔
233
    end
234
    return IndexStyle(A)
15✔
235
end
236
IndexStyle(::IndexSCartesian2{K}, ::IndexSCartesian2{K}) where {K} = IndexSCartesian2{K}()
2✔
237

238
struct SCartesianIndex2{K}   # can't make <:AbstractCartesianIndex without N, and 2 would be a bit misleading
239
    i::Int
4,113✔
240
    j::Int
241
end
242
to_index(i::SCartesianIndex2) = i
24✔
243

244
struct SCartesianIndices2{K,R<:AbstractUnitRange{Int}} <: AbstractMatrix{SCartesianIndex2{K}}
245
    indices2::R
15✔
246
end
247
SCartesianIndices2{K}(indices2::AbstractUnitRange{Int}) where {K} = (@assert K::Int > 1; SCartesianIndices2{K,typeof(indices2)}(indices2))
30✔
248

249
eachindex(::IndexSCartesian2{K}, A::ReshapedReinterpretArray) where {K} = SCartesianIndices2{K}(eachindex(IndexLinear(), parent(A)))
13✔
250
@inline function eachindex(style::IndexSCartesian2{K}, A::AbstractArray, B::AbstractArray...) where {K}
2✔
251
    iter = eachindex(style, A)
2✔
252
    _all_match_first(C->eachindex(style, C), iter, B...) || throw_eachindex_mismatch_indices(IndexSCartesian2{K}(), axes(A), axes.(B)...)
4✔
253
    return iter
2✔
254
end
255

256
size(iter::SCartesianIndices2{K}) where K = (K, length(iter.indices2))
2✔
257
axes(iter::SCartesianIndices2{K}) where K = (OneTo(K), iter.indices2)
11✔
258

259
first(iter::SCartesianIndices2{K}) where {K} = SCartesianIndex2{K}(1, first(iter.indices2))
2✔
260
last(iter::SCartesianIndices2{K}) where {K}  = SCartesianIndex2{K}(K, last(iter.indices2))
3✔
261

262
@inline function getindex(iter::SCartesianIndices2{K}, i::Int, j::Int) where {K}
×
263
    @boundscheck checkbounds(iter, i, j)
×
264
    return SCartesianIndex2{K}(i, iter.indices2[j])
×
265
end
266

267
function iterate(iter::SCartesianIndices2{K}) where {K}
14✔
268
    ret = iterate(iter.indices2)
28✔
269
    ret === nothing && return nothing
14✔
270
    item2, state2 = ret
14✔
271
    return SCartesianIndex2{K}(1, item2), (1, item2, state2)
14✔
272
end
273

274
function iterate(iter::SCartesianIndices2{K}, (state1, item2, state2)) where {K}
96✔
275
    if state1 < K
96✔
276
        item1 = state1 + 1
61✔
277
        return SCartesianIndex2{K}(item1, item2), (item1, item2, state2)
61✔
278
    end
279
    ret = iterate(iter.indices2, state2)
62✔
280
    ret === nothing && return nothing
35✔
281
    item2, state2 = ret
27✔
282
    return SCartesianIndex2{K}(1, item2), (1, item2, state2)
27✔
283
end
284

285
SimdLoop.simd_outer_range(iter::SCartesianIndices2) = iter.indices2
×
286
SimdLoop.simd_inner_length(::SCartesianIndices2{K}, ::Any) where K = K
×
287
@inline function SimdLoop.simd_index(::SCartesianIndices2{K}, Ilast::Int, I1::Int) where {K}
×
288
    SCartesianIndex2{K}(I1+1, Ilast)
×
289
end
290

291
_maybe_reshape(::IndexSCartesian2, A::ReshapedReinterpretArray, I...) = A
×
292

293
# fallbacks
294
function _getindex(::IndexSCartesian2, A::AbstractArray{T,N}, I::Vararg{Int, N}) where {T,N}
18✔
295
    @_propagate_inbounds_meta
18✔
296
    getindex(A, I...)
18✔
297
end
298
function _setindex!(::IndexSCartesian2, A::AbstractArray{T,N}, v, I::Vararg{Int, N}) where {T,N}
×
299
    @_propagate_inbounds_meta
×
300
    setindex!(A, v, I...)
×
301
end
302
# fallbacks for array types that use "pass-through" indexing (e.g., `IndexStyle(A) = IndexStyle(parent(A))`)
303
# but which don't handle SCartesianIndex2
304
function _getindex(::IndexSCartesian2, A::AbstractArray{T,N}, ind::SCartesianIndex2) where {T,N}
6✔
305
    @_propagate_inbounds_meta
6✔
306
    J = _ind2sub(tail(axes(A)), ind.j)
6✔
307
    getindex(A, ind.i, J...)
6✔
308
end
309
function _setindex!(::IndexSCartesian2, A::AbstractArray{T,N}, v, ind::SCartesianIndex2) where {T,N}
18✔
310
    @_propagate_inbounds_meta
18✔
311
    J = _ind2sub(tail(axes(A)), ind.j)
18✔
312
    setindex!(A, v, ind.i, J...)
18✔
313
end
314
eachindex(style::IndexSCartesian2, A::AbstractArray) = eachindex(style, parent(A))
6✔
315

316
## AbstractArray interface
317

318
parent(a::ReinterpretArray) = a.parent
6,142✔
319
dataids(a::ReinterpretArray) = dataids(a.parent)
13,655✔
320
unaliascopy(a::NonReshapedReinterpretArray{T}) where {T} = reinterpret(T, unaliascopy(a.parent))
2✔
321
unaliascopy(a::ReshapedReinterpretArray{T}) where {T} = reinterpret(reshape, T, unaliascopy(a.parent))
1✔
322

323
function size(a::NonReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
42,108✔
324
    psize = size(a.parent)
43,590✔
325
    size1 = issingletontype(T) ? psize[1] : div(psize[1]*sizeof(S), sizeof(T))
43,590✔
326
    tuple(size1, tail(psize)...)
42,108✔
327
end
328
function size(a::ReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
2,904✔
329
    psize = size(a.parent)
2,904✔
330
    sizeof(S) > sizeof(T) && return (div(sizeof(S), sizeof(T)), psize...)
2,904✔
331
    sizeof(S) < sizeof(T) && return tail(psize)
263✔
332
    return psize
98✔
333
end
334
size(a::NonReshapedReinterpretArray{T,0}) where {T} = ()
18✔
335

336
function axes(a::NonReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
77,869,986✔
337
    paxs = axes(a.parent)
78,059,379✔
338
    f, l = first(paxs[1]), length(paxs[1])
77,860,762✔
339
    size1 = issingletontype(T) ? l : div(l*sizeof(S), sizeof(T))
78,059,379✔
340
    tuple(oftype(paxs[1], f:f+size1-1), tail(paxs)...)
78,059,668✔
341
end
342
function axes(a::ReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
10,649✔
343
    paxs = axes(a.parent)
10,649✔
344
    sizeof(S) > sizeof(T) && return (OneTo(div(sizeof(S), sizeof(T))), paxs...)
10,649✔
345
    sizeof(S) < sizeof(T) && return tail(paxs)
2,047✔
346
    return paxs
1,760✔
347
end
348
axes(a::NonReshapedReinterpretArray{T,0}) where {T} = ()
2✔
349

350
has_offset_axes(a::ReinterpretArray) = has_offset_axes(a.parent)
4,384✔
351

352
elsize(::Type{<:ReinterpretArray{T}}) where {T} = sizeof(T)
4,064✔
353
unsafe_convert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = Ptr{T}(unsafe_convert(Ptr{S},a.parent))
8,464✔
354

355
@inline @propagate_inbounds function getindex(a::NonReshapedReinterpretArray{T,0,S}) where {T,S}
3✔
356
    if isprimitivetype(T) && isprimitivetype(S)
3✔
357
        reinterpret(T, a.parent[])
1✔
358
    else
359
        a[firstindex(a)]
2✔
360
    end
361
end
362

363
@inline @propagate_inbounds getindex(a::ReinterpretArray) = a[firstindex(a)]
5✔
364

365
@inline @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, inds::Vararg{Int, N}) where {T,N,S}
41,540,643✔
366
    check_readable(a)
39,564,043✔
367
    _getindex_ra(a, inds[1], tail(inds))
84,268,211✔
368
end
369

370
@inline @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, i::Int) where {T,N,S}
8,328✔
371
    check_readable(a)
8,328✔
372
    if isa(IndexStyle(a), IndexLinear)
8,328✔
373
        return _getindex_ra(a, i, ())
9,420✔
374
    end
375
    # Convert to full indices here, to avoid needing multiple conversions in
376
    # the loop in _getindex_ra
377
    inds = _to_subscript_indices(a, i)
2,742✔
378
    isempty(inds) ? _getindex_ra(a, 1, ()) : _getindex_ra(a, inds[1], tail(inds))
2,822✔
379
end
380

381
@inline @propagate_inbounds function getindex(a::ReshapedReinterpretArray{T,N,S}, ind::SCartesianIndex2) where {T,N,S}
4,014✔
382
    check_readable(a)
4,014✔
383
    s = Ref{S}(a.parent[ind.j])
4,014✔
384
    GC.@preserve s begin
4,014✔
385
        tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
4,014✔
386
        return unsafe_load(tptr, ind.i)
4,014✔
387
    end
388
end
389

390
@inline _memcpy!(dst, src, n) = ccall(:memcpy, Cvoid, (Ptr{UInt8}, Ptr{UInt8}, Csize_t), dst, src, n)
80✔
391

392
@inline @propagate_inbounds function _getindex_ra(a::NonReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT}
39,570,423✔
393
    # Make sure to match the scalar reinterpret if that is applicable
394
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
39,570,423✔
395
        if issingletontype(T) # singleton types
594,387✔
396
            @boundscheck checkbounds(a, i1, tailinds...)
85✔
397
            return T.instance
79✔
398
        end
399
        return reinterpret(T, a.parent[i1, tailinds...])
5,580,952✔
400
    else
401
        @boundscheck checkbounds(a, i1, tailinds...)
39,471,660✔
402
        ind_start, sidx = divrem((i1-1)*sizeof(T), sizeof(S))
39,471,636✔
403
        # Optimizations that avoid branches
404
        if sizeof(T) % sizeof(S) == 0
38,976,024✔
405
            # T is bigger than S and contains an integer number of them
406
            n = sizeof(T) ÷ sizeof(S)
38,946,831✔
407
            t = Ref{T}()
38,986,523✔
408
            GC.@preserve t begin
38,986,523✔
409
                sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
38,986,523✔
410
                for i = 1:n
38,986,523✔
411
                     s = a.parent[ind_start + i, tailinds...]
78,212,168✔
412
                     unsafe_store!(sptr, s, i)
78,212,160✔
413
                end
117,437,797✔
414
            end
415
            return t[]
38,986,523✔
416
        elseif sizeof(S) % sizeof(T) == 0
29,193✔
417
            # S is bigger than T and contains an integer number of them
418
            s = Ref{S}(a.parent[ind_start + 1, tailinds...])
485,141✔
419
            GC.@preserve s begin
485,081✔
420
                tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
485,081✔
421
                return unsafe_load(tptr + sidx)
485,081✔
422
            end
423
        else
424
            i = 1
32✔
425
            nbytes_copied = 0
32✔
426
            # This is a bit complicated to deal with partial elements
427
            # at both the start and the end. LLVM will fold as appropriate,
428
            # once it knows the data layout
429
            s = Ref{S}()
32✔
430
            t = Ref{T}()
32✔
431
            GC.@preserve s t begin
32✔
432
                sptr = Ptr{S}(unsafe_convert(Ref{S}, s))
32✔
433
                tptr = Ptr{T}(unsafe_convert(Ref{T}, t))
32✔
434
                while nbytes_copied < sizeof(T)
104✔
435
                    s[] = a.parent[ind_start + i, tailinds...]
76✔
436
                    nb = min(sizeof(S) - sidx, sizeof(T)-nbytes_copied)
72✔
437
                    _memcpy!(tptr + nbytes_copied, sptr + sidx, nb)
72✔
438
                    nbytes_copied += nb
72✔
439
                    sidx = 0
72✔
440
                    i += 1
72✔
441
                end
102✔
442
            end
443
            return t[]
32✔
444
        end
445
    end
446
end
447

448
@inline @propagate_inbounds function _getindex_ra(a::ReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT}
1,947✔
449
    # Make sure to match the scalar reinterpret if that is applicable
450
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
1,947✔
451
        if issingletontype(T) # singleton types
656✔
452
            @boundscheck checkbounds(a, i1, tailinds...)
64✔
453
            return T.instance
62✔
454
        end
455
        return reinterpret(T, a.parent[i1, tailinds...])
601✔
456
    end
457
    @boundscheck checkbounds(a, i1, tailinds...)
1,291✔
458
    if sizeof(T) >= sizeof(S)
1,291✔
459
        t = Ref{T}()
107✔
460
        GC.@preserve t begin
107✔
461
            sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
107✔
462
            if sizeof(T) > sizeof(S)
107✔
463
                # Extra dimension in the parent array
464
                n = sizeof(T) ÷ sizeof(S)
104✔
465
                if isempty(tailinds) && IndexStyle(a.parent) === IndexLinear()
104✔
466
                    offset = n * (i1 - firstindex(a))
51✔
467
                    for i = 1:n
51✔
468
                        s = a.parent[i + offset]
122✔
469
                        unsafe_store!(sptr, s, i)
122✔
470
                    end
193✔
471
                else
472
                    for i = 1:n
53✔
473
                        s = a.parent[i, i1, tailinds...]
112✔
474
                        unsafe_store!(sptr, s, i)
106✔
475
                    end
159✔
476
                end
477
            else
478
                # No extra dimension
479
                s = a.parent[i1, tailinds...]
3✔
480
                unsafe_store!(sptr, s)
107✔
481
            end
482
        end
483
        return t[]
107✔
484
    end
485
    # S is bigger than T and contains an integer number of them
486
    # n = sizeof(S) ÷ sizeof(T)
487
    s = Ref{S}()
1,184✔
488
    GC.@preserve s begin
1,184✔
489
        tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
1,184✔
490
        s[] = a.parent[tailinds...]
1,190✔
491
        return unsafe_load(tptr, i1)
1,184✔
492
    end
493
end
494

495
@inline @propagate_inbounds function setindex!(a::NonReshapedReinterpretArray{T,0,S}, v) where {T,S}
4✔
496
    if isprimitivetype(S) && isprimitivetype(T)
4✔
497
        a.parent[] = reinterpret(S, v)
1✔
498
        return a
1✔
499
    end
500
    setindex!(a, v, firstindex(a))
3✔
501
end
502

503
@inline @propagate_inbounds setindex!(a::ReinterpretArray, v) = setindex!(a, v, firstindex(a))
4✔
504

505
@inline @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, inds::Vararg{Int, N}) where {T,N,S}
40,614,294✔
506
    check_writable(a)
40,614,260✔
507
    _setindex_ra!(a, v, inds[1], tail(inds))
79,841,554✔
508
end
509

510
@inline @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, i::Int) where {T,N,S}
72✔
511
    check_writable(a)
72✔
512
    if isa(IndexStyle(a), IndexLinear)
72✔
513
        return _setindex_ra!(a, v, i, ())
64✔
514
    end
515
    inds = _to_subscript_indices(a, i)
9✔
516
    _setindex_ra!(a, v, inds[1], tail(inds))
9✔
517
end
518

519
@inline @propagate_inbounds function setindex!(a::ReshapedReinterpretArray{T,N,S}, v, ind::SCartesianIndex2) where {T,N,S}
1✔
520
    check_writable(a)
1✔
521
    v = convert(T, v)::T
1✔
522
    s = Ref{S}(a.parent[ind.j])
1✔
523
    GC.@preserve s begin
1✔
524
        tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
1✔
525
        unsafe_store!(tptr, v, ind.i)
1✔
526
    end
527
    a.parent[ind.j] = s[]
1✔
528
    return a
1✔
529
end
530

531
@inline @propagate_inbounds function _setindex_ra!(a::NonReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT}
40,614,297✔
532
    v = convert(T, v)::T
40,614,297✔
533
    # Make sure to match the scalar reinterpret if that is applicable
534
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
40,614,295✔
535
        if issingletontype(T) # singleton types
1,837,158✔
536
            @boundscheck checkbounds(a, i1, tailinds...)
6✔
537
            # setindex! is a noop except for the index check
538
        else
539
            setindex!(a.parent, reinterpret(S, v), i1, tailinds...)
2,288,114✔
540
        end
541
    else
542
        @boundscheck checkbounds(a, i1, tailinds...)
38,777,149✔
543
        ind_start, sidx = divrem((i1-1)*sizeof(T), sizeof(S))
38,777,125✔
544
        # Optimizations that avoid branches
545
        if sizeof(T) % sizeof(S) == 0
38,777,125✔
546
            # T is bigger than S and contains an integer number of them
547
            t = Ref{T}(v)
38,776,338✔
548
            GC.@preserve t begin
38,776,338✔
549
                sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
38,776,338✔
550
                n = sizeof(T) ÷ sizeof(S)
38,776,338✔
551
                for i = 1:n
38,776,338✔
552
                    s = unsafe_load(sptr, i)
77,552,679✔
553
                    a.parent[ind_start + i, tailinds...] = s
77,552,679✔
554
                end
116,329,020✔
555
            end
556
        elseif sizeof(S) % sizeof(T) == 0
787✔
557
            # S is bigger than T and contains an integer number of them
558
            s = Ref{S}(a.parent[ind_start + 1, tailinds...])
783✔
559
            GC.@preserve s begin
783✔
560
                tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
783✔
561
                unsafe_store!(tptr + sidx, v)
783✔
562
                a.parent[ind_start + 1, tailinds...] = s[]
783✔
563
            end
564
        else
565
            t = Ref{T}(v)
4✔
566
            s = Ref{S}()
4✔
567
            GC.@preserve t s begin
4✔
568
                tptr = Ptr{UInt8}(unsafe_convert(Ref{T}, t))
4✔
569
                sptr = Ptr{UInt8}(unsafe_convert(Ref{S}, s))
4✔
570
                nbytes_copied = 0
4✔
571
                i = 1
4✔
572
                # Deal with any partial elements at the start. We'll have to copy in the
573
                # element from the original array and overwrite the relevant parts
574
                if sidx != 0
4✔
575
                    s[] = a.parent[ind_start + i, tailinds...]
3✔
576
                    nb = min((sizeof(S) - sidx) % UInt, sizeof(T) % UInt)
2✔
577
                    _memcpy!(sptr + sidx, tptr, nb)
2✔
578
                    nbytes_copied += nb
2✔
579
                    a.parent[ind_start + i, tailinds...] = s[]
2✔
580
                    i += 1
2✔
581
                    sidx = 0
2✔
582
                end
583
                # Deal with the main body of elements
584
                while nbytes_copied < sizeof(T) && (sizeof(T) - nbytes_copied) > sizeof(S)
8✔
585
                    nb = min(sizeof(S), sizeof(T) - nbytes_copied)
2✔
586
                    _memcpy!(sptr, tptr + nbytes_copied, nb)
2✔
587
                    nbytes_copied += nb
2✔
588
                    a.parent[ind_start + i, tailinds...] = s[]
2✔
589
                    i += 1
2✔
590
                end
2✔
591
                # Deal with trailing partial elements
592
                if nbytes_copied < sizeof(T)
6✔
593
                    s[] = a.parent[ind_start + i, tailinds...]
6✔
594
                    nb = min(sizeof(S), sizeof(T) - nbytes_copied)
6✔
595
                    _memcpy!(sptr, tptr + nbytes_copied, nb)
6✔
596
                    a.parent[ind_start + i, tailinds...] = s[]
4✔
597
                end
598
            end
599
        end
600
    end
601
    return a
40,614,269✔
602
end
603

604
@inline @propagate_inbounds function _setindex_ra!(a::ReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT}
34✔
605
    v = convert(T, v)::T
34✔
606
    # Make sure to match the scalar reinterpret if that is applicable
607
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
33✔
608
        if issingletontype(T) # singleton types
7✔
609
            @boundscheck checkbounds(a, i1, tailinds...)
3✔
610
            # setindex! is a noop except for the index check
611
        else
612
            setindex!(a.parent, reinterpret(S, v), i1, tailinds...)
5✔
613
        end
614
    end
615
    @boundscheck checkbounds(a, i1, tailinds...)
32✔
616
    if sizeof(T) >= sizeof(S)
32✔
617
        t = Ref{T}(v)
11✔
618
        GC.@preserve t begin
11✔
619
            sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
11✔
620
            if sizeof(T) > sizeof(S)
11✔
621
                # Extra dimension in the parent array
622
                n = sizeof(T) ÷ sizeof(S)
3✔
623
                if isempty(tailinds) && IndexStyle(a.parent) === IndexLinear()
3✔
624
                    offset = n * (i1 - firstindex(a))
2✔
625
                    for i = 1:n
2✔
626
                        s = unsafe_load(sptr, i)
4✔
627
                        a.parent[i + offset] = s
4✔
628
                    end
6✔
629
                else
630
                    for i = 1:n
1✔
631
                        s = unsafe_load(sptr, i)
2✔
632
                        a.parent[i, i1, tailinds...] = s
2✔
633
                    end
3✔
634
                end
635
            else # sizeof(T) == sizeof(S)
636
                # No extra dimension
637
                s = unsafe_load(sptr)
8✔
638
                a.parent[i1, tailinds...] = s
11✔
639
            end
640
        end
641
    else
642
        # S is bigger than T and contains an integer number of them
643
        s = Ref{S}()
21✔
644
        GC.@preserve s begin
21✔
645
            tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
21✔
646
            s[] = a.parent[tailinds...]
23✔
647
            unsafe_store!(tptr, v, i1)
21✔
648
            a.parent[tailinds...] = s[]
21✔
649
        end
650
    end
651
    return a
32✔
652
end
653

654
# Padding
655
struct Padding
656
    offset::Int
48✔
657
    size::Int
658
end
659
function intersect(p1::Padding, p2::Padding)
×
660
    start = max(p1.offset, p2.offset)
16✔
661
    stop = min(p1.offset + p1.size, p2.offset + p2.size)
16✔
662
    Padding(start, max(0, stop-start))
16✔
663
end
664

665
struct PaddingError <: Exception
666
    S::Type
2✔
667
    T::Type
668
end
669

670
function showerror(io::IO, p::PaddingError)
×
671
    print(io, "Padding of type $(p.S) is not compatible with type $(p.T).")
×
672
end
673

674
"""
675
    CyclePadding(padding, total_size)
676

677
Cylces an iterator of `Padding` structs, restarting the padding at `total_size`.
678
E.g. if `padding` is all the padding in a struct and `total_size` is the total
679
aligned size of that array, `CyclePadding` will correspond to the padding in an
680
infinite vector of such structs.
681
"""
682
struct CyclePadding{P}
683
    padding::P
3,416✔
684
    total_size::Int
685
end
686
eltype(::Type{<:CyclePadding}) = Padding
×
687
IteratorSize(::Type{<:CyclePadding}) = IsInfinite()
×
688
isempty(cp::CyclePadding) = isempty(cp.padding)
×
689
function iterate(cp::CyclePadding)
×
690
    y = iterate(cp.padding)
1,708✔
691
    y === nothing && return nothing
862✔
692
    y[1], (0, y[2])
16✔
693
end
694
function iterate(cp::CyclePadding, state::Tuple)
×
695
    y = iterate(cp.padding, tail(state)...)
48✔
696
    y === nothing && return iterate(cp, (state[1]+cp.total_size,))
48✔
697
    Padding(y[1].offset+state[1], y[1].size), (state[1], tail(y)...)
16✔
698
end
699

700
"""
701
    Compute the location of padding in a type.
702
"""
703
function padding(T)
1,708✔
704
    padding = Padding[]
1,708✔
705
    last_end::Int = 0
×
706
    for i = 1:fieldcount(T)
2,068✔
707
        offset = fieldoffset(T, i)
794✔
708
        fT = fieldtype(T, i)
794✔
709
        if offset != last_end
794✔
710
            push!(padding, Padding(offset, offset-last_end))
16✔
711
        end
712
        last_end = offset + sizeof(fT)
794✔
713
    end
1,228✔
714
    padding
1,708✔
715
end
716

717
function CyclePadding(T::DataType)
1,708✔
718
    a, s = datatype_alignment(T), sizeof(T)
1,708✔
719
    as = s + (a - (s % a)) % a
1,708✔
720
    pad = padding(T)
1,708✔
721
    s != as && push!(pad, Padding(s, as - s))
1,708✔
722
    CyclePadding(pad, as)
1,708✔
723
end
724

725
@assume_effects :total function array_subpadding(S, T)
854✔
726
    lcm_size = lcm(sizeof(S), sizeof(T))
854✔
727
    s, t = CyclePadding(S), CyclePadding(T)
854✔
728
    checked_size = 0
×
729
    # use of Stateful harms inference and makes this vulnerable to invalidation
730
    (pad, tstate) = let
×
731
        it = iterate(t)
862✔
732
        it === nothing && return true
854✔
733
        it
×
734
    end
735
    (ps, sstate) = let
×
736
        it = iterate(s)
16✔
737
        it === nothing && return false
8✔
738
        it
×
739
    end
740
    while checked_size < lcm_size
16✔
741
        while true
20✔
742
            # See if there's corresponding padding in S
743
            ps.offset > pad.offset && return false
20✔
744
            intersect(ps, pad) == pad && break
16✔
745
            ps, sstate = iterate(s, sstate)
16✔
746
        end
8✔
747
        checked_size = pad.offset + pad.size
8✔
748
        pad, tstate = iterate(t, tstate)
16✔
749
    end
8✔
750
    return true
4✔
751
end
752

753
# Reductions with IndexSCartesian2
754

755
function _mapreduce(f::F, op::OP, style::IndexSCartesian2{K}, A::AbstractArrayOrBroadcasted) where {F,OP,K}
2✔
756
    inds = eachindex(style, A)
2✔
757
    n = size(inds)[2]
2✔
758
    if n == 0
2✔
759
        return mapreduce_empty_iter(f, op, A, IteratorEltype(A))
×
760
    else
761
        return mapreduce_impl(f, op, A, first(inds), last(inds))
2✔
762
    end
763
end
764

765
@noinline function mapreduce_impl(f::F, op::OP, A::AbstractArrayOrBroadcasted,
4✔
766
                                  ifirst::SCI, ilast::SCI, blksize::Int) where {F,OP,SCI<:SCartesianIndex2{K}} where K
767
    if ilast.j - ifirst.j < blksize
4✔
768
        # sequential portion
769
        @inbounds a1 = A[ifirst]
3✔
770
        @inbounds a2 = A[SCI(2,ifirst.j)]
3✔
771
        v = op(f(a1), f(a2))
3✔
772
        @simd for i = ifirst.i + 2 : K
3✔
773
            @inbounds ai = A[SCI(i,ifirst.j)]
3✔
774
            v = op(v, f(ai))
3✔
775
        end
×
776
        # Remaining columns
777
        for j = ifirst.j+1 : ilast.j
6✔
778
            @simd for i = 1:K
1,333✔
779
                @inbounds ai = A[SCI(i,j)]
3,999✔
780
                v = op(v, f(ai))
3,999✔
781
            end
×
782
        end
2,663✔
783
        return v
3✔
784
    else
785
        # pairwise portion
786
        jmid = ifirst.j + (ilast.j - ifirst.j) >> 1
1✔
787
        v1 = mapreduce_impl(f, op, A, ifirst, SCI(K,jmid), blksize)
1✔
788
        v2 = mapreduce_impl(f, op, A, SCI(1,jmid+1), ilast, blksize)
1✔
789
        return op(v1, v2)
1✔
790
    end
791
end
792

793
mapreduce_impl(f::F, op::OP, A::AbstractArrayOrBroadcasted, ifirst::SCartesianIndex2, ilast::SCartesianIndex2) where {F,OP} =
2✔
794
    mapreduce_impl(f, op, A, ifirst, ilast, pairwise_blocksize(f, op))
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc