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

JuliaLang / julia / #37592

pending completion
#37592

push

local

web-flow
Print out module in more places when we abort (#50723)

70869 of 83602 relevant lines covered (84.77%)

32000283.58 hits per line

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

94.94
/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
    @doc """
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}}
23,421✔
51
        function thrownonint(S::Type, T::Type, dim)
89,071✔
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,465✔
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,463✔
63
        isbitstype(S) || throwbits(S, T, S)
5,460✔
64
        (N != 0 || sizeof(T) == sizeof(S)) || throwsize0(S, T, "different")
5,457✔
65
        if N != 0 && sizeof(S) != sizeof(T)
90,776✔
66
            ax1 = axes(a)[1]
88,342✔
67
            dim = length(ax1)
4,736✔
68
            if issingletontype(T)
4,736✔
69
                issingletontype(S) || throwsingleton(S, T)
2✔
70
            else
71
                rem(dim*sizeof(S),sizeof(T)) == 0 || thrownonint(S, T, dim)
88,342✔
72
            end
73
            first(ax1) == 1 || throwaxes1(S, T, ax1)
88,340✔
74
        end
75
        readable = array_subpadding(T, S)
5,447✔
76
        writable = array_subpadding(S, T)
5,447✔
77
        new{T, N, S, A, false}(a, readable, writable)
90,770✔
78
    end
79
    reinterpret(::Type{T}, a::AbstractArray{T}) where {T} = a
132✔
80

81
    # With reshaping
82
    function reinterpret(::typeof(reshape), ::Type{T}, a::A) where {T,S,A<:AbstractArray{S}}
982✔
83
        function throwintmult(S::Type, T::Type)
983✔
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)
984✔
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)
985✔
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)
982✔
96
        isbitstype(S) || throwbits(S, T, S)
980✔
97
        if sizeof(S) == sizeof(T)
979✔
98
            N = ndims(a)
44✔
99
        elseif sizeof(S) > sizeof(T)
935✔
100
            issingletontype(T) && throwsingleton(S, T)
892✔
101
            rem(sizeof(S), sizeof(T)) == 0 || throwintmult(S, T)
889✔
102
            N = ndims(a) + 1
889✔
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)
969✔
111
        writable = array_subpadding(S, T)
969✔
112
        new{T, N, S, A, true}(a, readable, writable)
969✔
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)
133✔
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)...)
349,300✔
174
stride(A::Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}, k::Integer) =
225,169✔
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,127✔
178
    _checkcontiguous(Bool, a) && return size_to_strides(1, size(a)...)
3,127✔
179
    stp = strides(parent(a))
3,106✔
180
    els, elp = sizeof(T), sizeof(S)
3,106✔
181
    els == elp && return stp # 0dim parent is also handled here.
3,106✔
182
    IsReshaped && els < elp && return (1, _checked_strides(stp, els, elp)...)
1,976✔
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,954✔
189
    if elp > els && rem(elp, els) == 0
1,954✔
190
        N = div(elp, els)
1,797✔
191
        return map(i -> N * i, stp)
6,058✔
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,207✔
200

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

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

210
function check_writable(a::ReinterpretArray{T, N, S} where N) where {T,S}
40,534,167✔
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)
40,534,167✔
218
        throw(PaddingError(T, S))
40,534,167✔
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,898✔
227

228
IndexStyle(::Type{ReinterpretArray{T,N,S,A,false}}) where {T,N,S,A<:AbstractArray{S,N}} = IndexStyle(A)
8,071✔
229
function IndexStyle(::Type{ReinterpretArray{T,N,S,A,true}}) where {T,N,S,A<:AbstractArray{S}}
3,855✔
230
    if sizeof(T) < sizeof(S)
3,855✔
231
        IndexStyle(A) === IndexLinear() && return IndexSCartesian2{sizeof(S) ÷ sizeof(T)}()
3,063✔
232
        return IndexCartesian()
1,168✔
233
    end
234
    return IndexStyle(A)
792✔
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,114✔
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,328✔
319
dataids(a::ReinterpretArray) = dataids(a.parent)
12,631✔
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}
41,140✔
324
    psize = size(a.parent)
41,898✔
325
    size1 = issingletontype(T) ? psize[1] : div(psize[1]*sizeof(S), sizeof(T))
41,898✔
326
    tuple(size1, tail(psize)...)
41,140✔
327
end
328
function size(a::ReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
4,416✔
329
    psize = size(a.parent)
4,416✔
330
    sizeof(S) > sizeof(T) && return (div(sizeof(S), sizeof(T)), psize...)
4,416✔
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,631✔
337
    paxs = axes(a.parent)
78,153,319✔
338
    f, l = first(paxs[1]), length(paxs[1])
77,861,036✔
339
    size1 = issingletontype(T) ? l : div(l*sizeof(S), sizeof(T))
78,153,319✔
340
    tuple(oftype(paxs[1], f:f+size1-1), tail(paxs)...)
78,153,608✔
341
end
342
function axes(a::ReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
13,357✔
343
    paxs = axes(a.parent)
13,357✔
344
    sizeof(S) > sizeof(T) && return (OneTo(div(sizeof(S), sizeof(T))), paxs...)
13,357✔
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} = ()
4✔
349

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

352
elsize(::Type{<:ReinterpretArray{T}}) where {T} = sizeof(T)
4,544✔
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,786✔
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,591,509✔
366
    check_readable(a)
39,567,657✔
367
    _getindex_ra(a, inds[1], tail(inds))
85,375,628✔
368
end
369

370
@inline @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, i::Int) where {T,N,S}
9,374✔
371
    check_readable(a)
9,374✔
372
    if isa(IndexStyle(a), IndexLinear)
9,374✔
373
        return _getindex_ra(a, i, ())
11,152✔
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 @propagate_inbounds function _getindex_ra(a::NonReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT}
39,574,021✔
391
    # Make sure to match the scalar reinterpret if that is applicable
392
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
39,574,021✔
393
        if issingletontype(T) # singleton types
596,646✔
394
            @boundscheck checkbounds(a, i1, tailinds...)
85✔
395
            return T.instance
79✔
396
        end
397
        return reinterpret(T, a.parent[i1, tailinds...])
5,644,585✔
398
    else
399
        @boundscheck checkbounds(a, i1, tailinds...)
40,544,191✔
400
        ind_start, sidx = divrem((i1-1)*sizeof(T), sizeof(S))
40,544,167✔
401
        # Optimizations that avoid branches
402
        if sizeof(T) % sizeof(S) == 0
38,977,363✔
403
            # T is bigger than S and contains an integer number of them
404
            n = sizeof(T) ÷ sizeof(S)
38,947,376✔
405
            t = Ref{T}()
38,983,100✔
406
            GC.@preserve t begin
38,983,100✔
407
                sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
38,983,100✔
408
                for i = 1:n
38,983,100✔
409
                     s = a.parent[ind_start + i, tailinds...]
78,180,668✔
410
                     unsafe_store!(sptr, s, i)
78,180,660✔
411
                end
117,378,220✔
412
            end
413
            return t[]
38,983,100✔
414
        elseif sizeof(S) % sizeof(T) == 0
29,987✔
415
            # S is bigger than T and contains an integer number of them
416
            s = Ref{S}(a.parent[ind_start + 1, tailinds...])
1,561,095✔
417
            GC.@preserve s begin
1,561,035✔
418
                tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
1,561,035✔
419
                return unsafe_load(tptr + sidx)
1,561,035✔
420
            end
421
        else
422
            i = 1
32✔
423
            nbytes_copied = 0
32✔
424
            # This is a bit complicated to deal with partial elements
425
            # at both the start and the end. LLVM will fold as appropriate,
426
            # once it knows the data layout
427
            s = Ref{S}()
32✔
428
            t = Ref{T}()
32✔
429
            GC.@preserve s t begin
32✔
430
                sptr = Ptr{S}(unsafe_convert(Ref{S}, s))
32✔
431
                tptr = Ptr{T}(unsafe_convert(Ref{T}, t))
32✔
432
                while nbytes_copied < sizeof(T)
104✔
433
                    s[] = a.parent[ind_start + i, tailinds...]
76✔
434
                    nb = min(sizeof(S) - sidx, sizeof(T)-nbytes_copied)
72✔
435
                    memcpy(tptr + nbytes_copied, sptr + sidx, nb)
72✔
436
                    nbytes_copied += nb
72✔
437
                    sidx = 0
72✔
438
                    i += 1
72✔
439
                end
102✔
440
            end
441
            return t[]
32✔
442
        end
443
    end
444
end
445

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

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

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

503
@inline @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, inds::Vararg{Int, N}) where {T,N,S}
40,655,489✔
504
    check_writable(a)
40,655,455✔
505
    _setindex_ra!(a, v, inds[1], tail(inds))
79,887,889✔
506
end
507

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

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

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

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

652
# Padding
653
struct Padding
654
    offset::Int # 0-indexed offset of the next valid byte; sizeof(T) indicates trailing padding
82✔
655
    size::Int   # bytes of padding before a valid byte
656
end
657
function intersect(p1::Padding, p2::Padding)
16✔
658
    start = max(p1.offset, p2.offset)
16✔
659
    stop = min(p1.offset + p1.size, p2.offset + p2.size)
16✔
660
    Padding(start, max(0, stop-start))
16✔
661
end
662

663
struct PaddingError <: Exception
664
    S::Type
2✔
665
    T::Type
666
end
667

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

672
"""
673
    CyclePadding(padding, total_size)
674

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

698
"""
699
    Compute the location of padding in an isbits datatype. Recursive over the fields of that type.
700
"""
701
@assume_effects :foldable function padding(T::DataType, baseoffset::Int = 0)
1,641✔
702
    pads = Padding[]
1,641✔
703
    last_end::Int = baseoffset
×
704
    for i = 1:fieldcount(T)
1,097✔
705
        offset = baseoffset + Int(fieldoffset(T, i))
246✔
706
        fT = fieldtype(T, i)
246✔
707
        append!(pads, padding(fT, offset))
246✔
708
        if offset != last_end
246✔
709
            push!(pads, Padding(offset, offset-last_end))
42✔
710
        end
711
        last_end = offset + sizeof(fT)
246✔
712
    end
392✔
713
    if 0 < last_end - baseoffset < sizeof(T)
997✔
714
        push!(pads, Padding(baseoffset + sizeof(T), sizeof(T) - last_end + baseoffset))
4✔
715
    end
716
    return Core.svec(pads...)
997✔
717
end
718

719
function CyclePadding(T::DataType)
624✔
720
    a, s = datatype_alignment(T), sizeof(T)
624✔
721
    as = s + (a - (s % a)) % a
624✔
722
    pad = padding(T)
624✔
723
    if s != as
624✔
724
        pad = Core.svec(pad..., Padding(s, as - s))
×
725
    end
726
    CyclePadding(pad, as)
624✔
727
end
728

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

757
@assume_effects :foldable function struct_subpadding(::Type{Out}, ::Type{In}) where {Out, In}
9✔
758
    padding(Out) == padding(In)
9✔
759
end
760

761
@assume_effects :foldable function packedsize(::Type{T}) where T
2✔
762
    pads = padding(T)
2✔
763
    return sizeof(T) - sum((p.size for p ∈ pads), init = 0)
2✔
764
end
765

766
@assume_effects :foldable ispacked(::Type{T}) where T = isempty(padding(T))
×
767

768
function _copytopacked!(ptr_out::Ptr{Out}, ptr_in::Ptr{In}) where {Out, In}
4✔
769
    writeoffset = 0
4✔
770
    for i ∈ 1:fieldcount(In)
4✔
771
        readoffset = fieldoffset(In, i)
13✔
772
        fT = fieldtype(In, i)
13✔
773
        if ispacked(fT)
15✔
774
            readsize = sizeof(fT)
14✔
775
            memcpy(ptr_out + writeoffset, ptr_in + readoffset, readsize)
12✔
776
            writeoffset += readsize
12✔
777
        else # nested padded type
778
            _copytopacked!(ptr_out + writeoffset, Ptr{fT}(ptr_in + readoffset))
2✔
779
            writeoffset += packedsize(fT)
1✔
780
        end
781
    end
14✔
782
end
783

784
function _copyfrompacked!(ptr_out::Ptr{Out}, ptr_in::Ptr{In}) where {Out, In}
4✔
785
    readoffset = 0
4✔
786
    for i ∈ 1:fieldcount(Out)
4✔
787
        writeoffset = fieldoffset(Out, i)
13✔
788
        fT = fieldtype(Out, i)
13✔
789
        if ispacked(fT)
15✔
790
            writesize = sizeof(fT)
14✔
791
            memcpy(ptr_out + writeoffset, ptr_in + readoffset, writesize)
12✔
792
            readoffset += writesize
12✔
793
        else # nested padded type
794
            _copyfrompacked!(Ptr{fT}(ptr_out + writeoffset), ptr_in + readoffset)
2✔
795
            readoffset += packedsize(fT)
1✔
796
        end
797
    end
14✔
798
end
799

800
@inline function _reinterpret(::Type{Out}, x::In) where {Out, In}
15✔
801
    # handle non-primitive types
802
    isbitstype(Out) || throw(ArgumentError("Target type for `reinterpret` must be isbits"))
15✔
803
    isbitstype(In) || throw(ArgumentError("Source type for `reinterpret` must be isbits"))
14✔
804
    inpackedsize = packedsize(In)
13✔
805
    outpackedsize = packedsize(Out)
13✔
806
    inpackedsize == outpackedsize ||
13✔
807
        throw(ArgumentError("Packed sizes of types $Out and $In do not match; got $outpackedsize \
808
            and $inpackedsize, respectively."))
809
    in = Ref{In}(x)
9✔
810
    out = Ref{Out}()
9✔
811
    if struct_subpadding(Out, In)
9✔
812
        # if packed the same, just copy
813
        GC.@preserve in out begin
6✔
814
            ptr_in = unsafe_convert(Ptr{In}, in)
6✔
815
            ptr_out = unsafe_convert(Ptr{Out}, out)
6✔
816
            memcpy(ptr_out, ptr_in, sizeof(Out))
6✔
817
        end
818
        return out[]
6✔
819
    else
820
        # mismatched padding
821
        GC.@preserve in out begin
3✔
822
            ptr_in = unsafe_convert(Ptr{In}, in)
3✔
823
            ptr_out = unsafe_convert(Ptr{Out}, out)
3✔
824

825
            if fieldcount(In) > 0 && ispacked(Out)
3✔
826
                _copytopacked!(ptr_out, ptr_in)
×
827
            elseif fieldcount(Out) > 0 && ispacked(In)
3✔
828
                _copyfrompacked!(ptr_out, ptr_in)
×
829
            else
830
                packed = Ref{NTuple{inpackedsize, UInt8}}()
3✔
831
                GC.@preserve packed begin
3✔
832
                    ptr_packed = unsafe_convert(Ptr{NTuple{inpackedsize, UInt8}}, packed)
3✔
833
                    _copytopacked!(ptr_packed, ptr_in)
3✔
834
                    _copyfrompacked!(ptr_out, ptr_packed)
3✔
835
                end
836
            end
837
        end
838
        return out[]
3✔
839
    end
840
end
841

842

843
# Reductions with IndexSCartesian2
844

845
function _mapreduce(f::F, op::OP, style::IndexSCartesian2{K}, A::AbstractArrayOrBroadcasted) where {F,OP,K}
2✔
846
    inds = eachindex(style, A)
2✔
847
    n = size(inds)[2]
2✔
848
    if n == 0
2✔
849
        return mapreduce_empty_iter(f, op, A, IteratorEltype(A))
×
850
    else
851
        return mapreduce_impl(f, op, A, first(inds), last(inds))
2✔
852
    end
853
end
854

855
@noinline function mapreduce_impl(f::F, op::OP, A::AbstractArrayOrBroadcasted,
4✔
856
                                  ifirst::SCI, ilast::SCI, blksize::Int) where {F,OP,SCI<:SCartesianIndex2{K}} where K
857
    if ilast.j - ifirst.j < blksize
4✔
858
        # sequential portion
859
        @inbounds a1 = A[ifirst]
3✔
860
        @inbounds a2 = A[SCI(2,ifirst.j)]
3✔
861
        v = op(f(a1), f(a2))
3✔
862
        @simd for i = ifirst.i + 2 : K
3✔
863
            @inbounds ai = A[SCI(i,ifirst.j)]
3✔
864
            v = op(v, f(ai))
3✔
865
        end
×
866
        # Remaining columns
867
        for j = ifirst.j+1 : ilast.j
6✔
868
            @simd for i = 1:K
1,333✔
869
                @inbounds ai = A[SCI(i,j)]
3,999✔
870
                v = op(v, f(ai))
3,999✔
871
            end
×
872
        end
2,663✔
873
        return v
3✔
874
    else
875
        # pairwise portion
876
        jmid = ifirst.j + (ilast.j - ifirst.j) >> 1
1✔
877
        v1 = mapreduce_impl(f, op, A, ifirst, SCI(K,jmid), blksize)
1✔
878
        v2 = mapreduce_impl(f, op, A, SCI(1,jmid+1), ilast, blksize)
1✔
879
        return op(v1, v2)
1✔
880
    end
881
end
882

883
mapreduce_impl(f::F, op::OP, A::AbstractArrayOrBroadcasted, ifirst::SCartesianIndex2, ilast::SCartesianIndex2) where {F,OP} =
2✔
884
    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