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

JuliaLang / julia / 1554

08 Jan 2026 06:52PM UTC coverage: 76.664% (+0.04%) from 76.623%
1554

push

buildkite

web-flow
[JuliaLowering] Fix-up always-defined check in `is_valid_body_ir_argument` (#60602)

This was slightly mis-translated from the flisp side:
```scheme
    (define (valid-body-ir-argument? aval)
      (or (valid-ir-argument? aval)
          (and (symbol? aval) ; Arguments are always defined slots
               (or (memq aval (lam:args lam))
                   (let ((vi (get vinfo-table aval #f)))
                     (and vi (vinfo:never-undef vi)))))))
```

Noticed in
https://github.com/JuliaLang/julia/pull/60567#discussion_r2672556146

62618 of 81679 relevant lines covered (76.66%)

23088815.22 hits per line

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

88.93
/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)
×
15
        @noinline
×
16
        throw(ArgumentError(LazyString("cannot reinterpret `", S, "` as `", T, "`, type `", U, "` is not a bits type")))
×
17
    end
18
    function throwsize0(S::Type, T::Type, msg)
15✔
19
        @noinline
15✔
20
        throw(ArgumentError(LazyString("cannot reinterpret a zero-dimensional `", S, "` array to `", T,
15✔
21
            "` which is of a ", msg, " size")))
22
    end
23
    function throwsingleton(S::Type, T::Type)
×
24
        @noinline
×
25
        throw(ArgumentError(LazyString("cannot reinterpret a `", S, "` array to `", T, "` which is a singleton type")))
×
26
    end
27

28
    global reinterpret
29

30
    @doc """
31
        reinterpret(T::DataType, A::AbstractArray)
32

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

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

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

44
    julia> reinterpret(Complex{Int}, 1:6)
45
    3-element reinterpret(Complex{$Int}, ::UnitRange{$Int}):
46
     1 + 2im
47
     3 + 4im
48
     5 + 6im
49
    ```
50

51
    If the location of padding bits does not line up between `T` and `eltype(A)`, the resulting array will be
52
    read-only or write-only, to prevent invalid bits from being written to or read from, respectively.
53

54
    ```jldoctest
55
    julia> a = reinterpret(Tuple{UInt8, UInt32}, UInt32[1, 2])
56
    1-element reinterpret(Tuple{UInt8, UInt32}, ::Vector{UInt32}):
57
     (0x01, 0x00000002)
58

59
    julia> a[1] = 3
60
    ERROR: Padding of type Tuple{UInt8, UInt32} is not compatible with type UInt32.
61

62
    julia> b = reinterpret(UInt32, Tuple{UInt8, UInt32}[(0x01, 0x00000002)]); # showing will error
63

64
    julia> b[1]
65
    ERROR: Padding of type UInt32 is not compatible with type Tuple{UInt8, UInt32}.
66
    ```
67
    """
68
    function reinterpret(::Type{T}, a::A) where {T,N,S,A<:AbstractArray{S, N}}
2,571✔
69
        function thrownonint(S::Type, T::Type, dim)
3,674✔
70
            @noinline
×
71
            throw(ArgumentError(LazyString(
×
72
                "cannot reinterpret an `", S, "` array to `", T, "` whose first dimension has size `", dim,
73
                "`. The resulting array would have a non-integral first dimension.")))
74
        end
75
        function throwaxes1(S::Type, T::Type, ax1)
3,680✔
76
            @noinline
6✔
77
            throw(ArgumentError(LazyString("cannot reinterpret a `", S, "` array to `", T,
6✔
78
                "` when the first axis is ", ax1, ". Try reshaping first.")))
79
        end
80
        isbitstype(T) || throwbits(S, T, T)
3,674✔
81
        isbitstype(S) || throwbits(S, T, S)
3,665✔
82
        (N != 0 || sizeof(T) == sizeof(S)) || throwsize0(S, T, "different")
3,656✔
83
        if N != 0 && sizeof(S) != sizeof(T)
3,644✔
84
            ax1 = axes(a)[1]
5,596✔
85
            dim = length(ax1)
2,287✔
86
            if issingletontype(T)
2,287✔
87
                issingletontype(S) || throwsingleton(S, T)
6✔
88
            else
89
                rem(dim*sizeof(S),sizeof(T)) == 0 || thrownonint(S, T, dim)
5,479✔
90
            end
91
            first(ax1) == 1 || throwaxes1(S, T, ax1)
2,281✔
92
        end
93
        readable = array_subpadding(T, S)
3,626✔
94
        writable = array_subpadding(S, T)
3,626✔
95
        new{T, N, S, A, false}(a, readable, writable)
14,363✔
96
    end
97
    reinterpret(::Type{T}, a::AbstractArray{T}) where {T} = a
473✔
98

99
    # With reshaping
100
    function reinterpret(::typeof(reshape), ::Type{T}, a::A) where {T,S,A<:AbstractArray{S}}
286✔
101
        function throwintmult(S::Type, T::Type)
2,851✔
102
            @noinline
103
            throw(ArgumentError(LazyString("`reinterpret(reshape, T, a)` requires that one of `sizeof(T)` (got ",
104
                sizeof(T), ") and `sizeof(eltype(a))` (got ", sizeof(S), ") be an integer multiple of the other")))
105
        end
106
        function throwsize1(a::AbstractArray, T::Type)
2,857✔
107
            @noinline
6✔
108
            throw(ArgumentError(LazyString("`reinterpret(reshape, ", T, ", a)` where `eltype(a)` is ", eltype(a),
6✔
109
                " requires that `axes(a, 1)` (got ", axes(a, 1), ") be equal to 1:",
110
                sizeof(T) ÷ sizeof(eltype(a)), " (from the ratio of element sizes)")))
111
        end
112
        function throwfromsingleton(S, T)
2,860✔
113
            @noinline
9✔
114
            throw(ArgumentError(LazyString("`reinterpret(reshape, ", T, ", a)` where `eltype(a)` is ", S,
9✔
115
                " requires that ", T, " be a singleton type, since ", S, " is one")))
116
        end
117
        isbitstype(T) || throwbits(S, T, T)
2,851✔
118
        isbitstype(S) || throwbits(S, T, S)
2,845✔
119
        if sizeof(S) == sizeof(T)
2,842✔
120
            N = ndims(a)
198✔
121
        elseif sizeof(S) > sizeof(T)
2,644✔
122
            issingletontype(T) && throwsingleton(S, T)
2,447✔
123
            rem(sizeof(S), sizeof(T)) == 0 || throwintmult(S, T)
2,438✔
124
            N = ndims(a) + 1
2,438✔
125
        else
126
            issingletontype(S) && throwfromsingleton(S, T)
197✔
127
            rem(sizeof(T), sizeof(S)) == 0 || throwintmult(S, T)
188✔
128
            N = ndims(a) - 1
185✔
129
            N > -1 || throwsize0(S, T, "larger")
185✔
130
            axes(a, 1) == OneTo(sizeof(T) ÷ sizeof(S)) || throwsize1(a, T)
188✔
131
        end
132
        readable = array_subpadding(T, S)
2,812✔
133
        writable = array_subpadding(S, T)
2,812✔
134
        new{T, N, S, A, true}(a, readable, writable)
2,812✔
135
    end
136
    reinterpret(::typeof(reshape), ::Type{T}, a::AbstractArray{T}) where {T} = a
6✔
137
end
138

139
ReshapedReinterpretArray{T,N,S,A<:AbstractArray{S}} = ReinterpretArray{T,N,S,A,true}
140
NonReshapedReinterpretArray{T,N,S,A<:AbstractArray{S, N}} = ReinterpretArray{T,N,S,A,false}
141

142
"""
143
    reinterpret(reshape, T, A::AbstractArray{S}) -> B
144

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

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

151
!!! compat "Julia 1.6"
152
    This method requires at least Julia 1.6.
153

154
# Examples
155

156
```jldoctest
157
julia> A = [1 2; 3 4]
158
2×2 Matrix{$Int}:
159
 1  2
160
 3  4
161

162
julia> reinterpret(reshape, Complex{Int}, A)    # the result is a vector
163
2-element reinterpret(reshape, Complex{$Int}, ::Matrix{$Int}) with eltype Complex{$Int}:
164
 1 + 3im
165
 2 + 4im
166

167
julia> a = [(1,2,3), (4,5,6)]
168
2-element Vector{Tuple{$Int, $Int, $Int}}:
169
 (1, 2, 3)
170
 (4, 5, 6)
171

172
julia> reinterpret(reshape, Int, a)             # the result is a matrix
173
3×2 reinterpret(reshape, $Int, ::Vector{Tuple{$Int, $Int, $Int}}) with eltype $Int:
174
 1  4
175
 2  5
176
 3  6
177
```
178
"""
179
reinterpret(::typeof(reshape), T::Type, a::AbstractArray)
180

181
reinterpret(::Type{T}, a::NonReshapedReinterpretArray) where {T} = reinterpret(T, a.parent)
464✔
182
reinterpret(::typeof(reshape), ::Type{T}, a::ReshapedReinterpretArray) where {T} = reinterpret(reshape, T, a.parent)
6✔
183

184
# Definition of StridedArray
185
StridedFastContiguousSubArray{T,N,A<:DenseArray} = FastContiguousSubArray{T,N,A}
186
StridedReinterpretArray{T,N,A<:Union{DenseArray,StridedFastContiguousSubArray},IsReshaped} = ReinterpretArray{T,N,S,A,IsReshaped} where S
187
StridedReshapedArray{T,N,A<:Union{DenseArray,StridedFastContiguousSubArray,StridedReinterpretArray}} = ReshapedArray{T,N,A}
188
StridedSubArray{T,N,A<:Union{DenseArray,StridedReshapedArray,StridedReinterpretArray},
189
    I<:Tuple{Vararg{Union{RangeIndex, ReshapedUnitRange, AbstractCartesianIndex}}}} = SubArray{T,N,A,I}
190
StridedArray{T,N} = Union{DenseArray{T,N}, StridedSubArray{T,N}, StridedReshapedArray{T,N}, StridedReinterpretArray{T,N}}
191
StridedVector{T} = StridedArray{T,1}
192
StridedMatrix{T} = StridedArray{T,2}
193
StridedVecOrMat{T} = Union{StridedVector{T}, StridedMatrix{T}}
194

195
strides(a::Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}) = size_to_strides(1, size(a)...)
73,023,117✔
196
stride(A::Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}, k::Integer) =
48,592,859✔
197
    k ≤ ndims(A) ? strides(A)[k] : length(A)
198

199
function strides(a::ReinterpretArray{T,<:Any,S,<:AbstractArray{S},IsReshaped}) where {T,S,IsReshaped}
303✔
200
    _checkcontiguous(Bool, a) && return size_to_strides(1, size(a)...)
9,381✔
201
    stp = strides(parent(a))
9,318✔
202
    els, elp = sizeof(T), sizeof(S)
9,318✔
203
    els == elp && return stp # 0dim parent is also handled here.
9,318✔
204
    IsReshaped && els < elp && return (1, _checked_strides(stp, els, elp)...)
5,928✔
205
    stp[1] == 1 || throw(ArgumentError("Parent must be contiguous in the 1st dimension!"))
2,010✔
206
    st′ = _checked_strides(tail(stp), els, elp)
1,935✔
207
    return IsReshaped ? st′ : (1, st′...)
1,821✔
208
end
209

210
@inline function _checked_strides(stp::Tuple, els::Integer, elp::Integer)
211
    if elp > els && rem(elp, els) == 0
5,862✔
212
        N = div(elp, els)
5,391✔
213
        return map(i -> N * i, stp)
12,630✔
214
    end
215
    drs = map(i -> divrem(elp * i, els), stp)
942✔
216
    all(i->iszero(i[2]), drs) ||
1,179✔
217
        throw(ArgumentError("Parent's strides could not be exactly divided!"))
218
    map(first, drs)
414✔
219
end
220

221
_checkcontiguous(::Type{Bool}, A::ReinterpretArray) = _checkcontiguous(Bool, parent(A))
14,364✔
222

223
similar(a::ReinterpretArray, T::Type, d::Dims) = similar(a.parent, T, d)
1,780✔
224
similar(::Type{TA}, dims::Dims) where {T,N,O,P,TA<:ReinterpretArray{T,N,O,P}} = similar(P, dims)
3✔
225

226
function check_readable(a::ReinterpretArray{T, N, S} where N) where {T,S}
6✔
227
    # See comment in check_writable
228
    if !a.readable && !array_subpadding(T, S)
1,326,796✔
229
        throw(PaddingError(T, S))
9✔
230
    end
231
end
232

233
function check_writable(a::ReinterpretArray{T, N, S} where N) where {T,S}
234
    # `array_subpadding` is relatively expensive (compared to a simple arrayref),
235
    # so it is cached in the array. However, it is computable at compile time if,
236
    # inference has the types available. By using this form of the check, we can
237
    # get the best of both worlds for the success case. If the types were not
238
    # available to inference, we simply need to check the field (relatively cheap)
239
    # and if they were we should be able to fold this check away entirely.
240
    if !a.writable && !array_subpadding(S, T)
131,513,025✔
241
        throw(PaddingError(T, S))
6✔
242
    end
243
end
244

245
## IndexStyle specializations
246

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

251
IndexStyle(::Type{ReinterpretArray{T,N,S,A,false}}) where {T,N,S,A<:AbstractArray{S,N}} = IndexStyle(A)
11,282✔
252
function IndexStyle(::Type{ReinterpretArray{T,N,S,A,true}}) where {T,N,S,A<:AbstractArray{S}}
253
    if sizeof(T) < sizeof(S)
12,787✔
254
        IndexStyle(A) === IndexLinear() && return IndexSCartesian2{sizeof(S) ÷ sizeof(T)}()
10,016✔
255
        return IndexCartesian()
3,590✔
256
    end
257
    return IndexStyle(A)
2,771✔
258
end
259
IndexStyle(::IndexSCartesian2{K}, ::IndexSCartesian2{K}) where {K} = IndexSCartesian2{K}()
9✔
260

261
struct SCartesianIndex2{K}   # can't make <:AbstractCartesianIndex without N, and 2 would be a bit misleading
262
    i::Int
24,909✔
263
    j::Int
264
end
265
to_index(i::SCartesianIndex2) = i
252✔
266

267
struct SCartesianIndices2{K,R<:AbstractUnitRange{Int}} <: AbstractMatrix{SCartesianIndex2{K}}
268
    indices2::R
123✔
269
end
270
SCartesianIndices2{K}(indices2::AbstractUnitRange{Int}) where {K} = (@assert K::Int > 1; SCartesianIndices2{K,typeof(indices2)}(indices2))
123✔
271

272
eachindex(::IndexSCartesian2{K}, A::ReshapedReinterpretArray) where {K} = SCartesianIndices2{K}(eachindex(IndexLinear(), parent(A)))
114✔
273
@inline function eachindex(style::IndexSCartesian2{K}, A::AbstractArray, B::AbstractArray...) where {K}
274
    iter = eachindex(style, A)
9✔
275
    itersBs = map(C->eachindex(style, C), B)
18✔
276
    all(==(iter), itersBs) || throw_eachindex_mismatch_indices("axes", axes(A), map(axes, B)...)
9✔
277
    return iter
9✔
278
end
279

280
size(iter::SCartesianIndices2{K}) where K = (K, length(iter.indices2))
12✔
281
axes(iter::SCartesianIndices2{K}) where K = (OneTo(K), iter.indices2)
27✔
282

283
first(iter::SCartesianIndices2{K}) where {K} = SCartesianIndex2{K}(1, first(iter.indices2))
15✔
284
last(iter::SCartesianIndices2{K}) where {K}  = SCartesianIndex2{K}(K, last(iter.indices2))
27✔
285

286
@inline function getindex(iter::SCartesianIndices2{K}, i::Int, j::Int) where {K}
×
287
    @boundscheck checkbounds(iter, i, j)
×
288
    return SCartesianIndex2{K}(i, iter.indices2[j])
×
289
end
290

291
function iterate(iter::SCartesianIndices2{K}) where {K}
292
    ret = iterate(iter.indices2)
108✔
293
    ret === nothing && return nothing
105✔
294
    item2, state2 = ret
105✔
295
    return SCartesianIndex2{K}(1, item2), (1, item2, state2)
105✔
296
end
297

298
function iterate(iter::SCartesianIndices2{K}, (state1, item2, state2)) where {K}
299
    if state1 < K
759✔
300
        item1 = state1 + 1
471✔
301
        return SCartesianIndex2{K}(item1, item2), (item1, item2, state2)
471✔
302
    end
303
    ret = iterate(iter.indices2, state2)
507✔
304
    ret === nothing && return nothing
288✔
305
    item2, state2 = ret
219✔
306
    return SCartesianIndex2{K}(1, item2), (1, item2, state2)
219✔
307
end
308

309
SimdLoop.simd_outer_range(iter::SCartesianIndices2) = iter.indices2
×
310
SimdLoop.simd_inner_length(::SCartesianIndices2{K}, ::Any) where K = K
×
311
@inline function SimdLoop.simd_index(::SCartesianIndices2{K}, Ilast::Int, I1::Int) where {K}
×
312
    SCartesianIndex2{K}(I1+1, Ilast)
×
313
end
314

315
_maybe_reshape(::IndexSCartesian2, A::AbstractArray, I...) = _maybe_reshape(IndexCartesian(), A, I...)
3✔
316
_maybe_reshape(::IndexSCartesian2, A::ReshapedReinterpretArray, I...) = A
12✔
317

318
# fallbacks
319
function _getindex(::IndexSCartesian2, A::AbstractArray, I::Vararg{Int, N}) where {N}
320
    @_propagate_inbounds_meta
198✔
321
    _getindex(IndexCartesian(), A, I...)
198✔
322
end
323
function _setindex!(::IndexSCartesian2, A::AbstractArray, v, I::Vararg{Int, N}) where {N}
×
324
    @_propagate_inbounds_meta
×
325
    _setindex!(IndexCartesian(), A, v, I...)
×
326
end
327
# fallbacks for array types that use "pass-through" indexing (e.g., `IndexStyle(A) = IndexStyle(parent(A))`)
328
# but which don't handle SCartesianIndex2
329
function _getindex(::IndexSCartesian2, A::AbstractArray{T,N}, ind::SCartesianIndex2) where {T,N}
×
330
    @_propagate_inbounds_meta
×
331
    J = _ind2sub(tail(axes(A)), ind.j)
×
332
    getindex(A, ind.i, J...)
×
333
end
334

335
function _getindex(::IndexSCartesian2{2}, A::AbstractArray{T,2}, ind::SCartesianIndex2) where {T}
336
    @_propagate_inbounds_meta
90✔
337
    J = first(axes(A, 2)) + ind.j - 1
90✔
338
    getindex(A, ind.i, J)
90✔
339
end
340

341
function _setindex!(::IndexSCartesian2, A::AbstractArray{T,N}, v, ind::SCartesianIndex2) where {T,N}
342
    @_propagate_inbounds_meta
72✔
343
    J = _ind2sub(tail(axes(A)), ind.j)
72✔
344
    setindex!(A, v, ind.i, J...)
72✔
345
end
346

347
function _setindex!(::IndexSCartesian2{2}, A::AbstractArray{T,2}, v, ind::SCartesianIndex2) where {T}
348
    @_propagate_inbounds_meta
90✔
349
    J = first(axes(A, 2)) + ind.j - 1
90✔
350
    setindex!(A, v, ind.i, J)
90✔
351
end
352

353
eachindex(style::IndexSCartesian2, A::AbstractArray) = eachindex(style, parent(A))
54✔
354

355
## AbstractArray interface
356

357
parent(a::ReinterpretArray) = a.parent
2,023,724✔
358
dataids(a::ReinterpretArray) = dataids(a.parent)
18,855✔
359
unaliascopy(a::NonReshapedReinterpretArray{T}) where {T} = reinterpret(T, unaliascopy(a.parent))
6✔
360
unaliascopy(a::ReshapedReinterpretArray{T}) where {T} = reinterpret(reshape, T, unaliascopy(a.parent))
3✔
361

362
function size(a::NonReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
18✔
363
    psize = size(a.parent)
40,905✔
364
    size1 = issingletontype(T) ? psize[1] : div(psize[1]*sizeof(S), sizeof(T))
40,818✔
365
    tuple(size1, tail(psize)...)
40,818✔
366
end
367
function size(a::ReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
21✔
368
    psize = size(a.parent)
12,987✔
369
    sizeof(S) > sizeof(T) && return (div(sizeof(S), sizeof(T)), psize...)
12,885✔
370
    sizeof(S) < sizeof(T) && return tail(psize)
1,524✔
371
    return psize
600✔
372
end
373
size(a::NonReshapedReinterpretArray{T,0}) where {T} = ()
159✔
374

375
function axes(a::NonReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
1,446✔
376
    paxs = axes(a.parent)
766,030✔
377
    f, l = first(paxs[1]), length(paxs[1])
602,026✔
378
    size1 = issingletontype(T) ? l : div(l*sizeof(S), sizeof(T))
757,594✔
379
    tuple(oftype(paxs[1], f:f+size1-1), tail(paxs)...)
757,594✔
380
end
381
function axes(a::ReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
4,230✔
382
    paxs = axes(a.parent)
67,304✔
383
    sizeof(S) > sizeof(T) && return (OneTo(div(sizeof(S), sizeof(T))), paxs...)
48,344✔
384
    sizeof(S) < sizeof(T) && return tail(paxs)
6,859✔
385
    return paxs
5,457✔
386
end
387
axes(a::NonReshapedReinterpretArray{T,0}) where {T} = ()
18✔
388

389
has_offset_axes(a::ReinterpretArray) = has_offset_axes(a.parent)
306✔
390

391
elsize(::Type{<:ReinterpretArray{T}}) where {T} = sizeof(T)
2,012,819✔
392
cconvert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = cconvert(Ptr{S}, a.parent)
480,418✔
393
unsafe_convert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = Ptr{T}(unsafe_convert(Ptr{S},a.parent))
6✔
394

395
@propagate_inbounds function getindex(a::NonReshapedReinterpretArray{T,0,S}) where {T,S}
3✔
396
    if isprimitivetype(T) && isprimitivetype(S)
45✔
397
        reinterpret(T, a.parent[])
18✔
398
    else
399
        a[firstindex(a)]
27✔
400
    end
401
end
402

403
check_ptr_indexable(a::ReinterpretArray, sz = elsize(a)) = check_ptr_indexable(parent(a), sz)
3,999,679✔
404
check_ptr_indexable(a::ReshapedArray, sz) = check_ptr_indexable(parent(a), sz)
1,476✔
405
check_ptr_indexable(a::FastContiguousSubArray, sz) = check_ptr_indexable(parent(a), sz)
924,577✔
406
check_ptr_indexable(a::Array, sz) = sizeof(eltype(a)) !== sz
1,028,711✔
407
check_ptr_indexable(a::Memory, sz) = true
×
408
check_ptr_indexable(a::AbstractArray, sz) = false
×
409

410
@propagate_inbounds getindex(a::ReshapedReinterpretArray{T,0}) where {T} = a[firstindex(a)]
120✔
411

412
@propagate_inbounds isassigned(a::ReinterpretArray, inds::Integer...) = checkbounds(Bool, a, inds...) && (check_ptr_indexable(a) || _isassigned_ra(a, inds...))
36✔
413
@propagate_inbounds isassigned(a::ReinterpretArray, inds::SCartesianIndex2) = isassigned(a.parent, inds.j)
×
414
@propagate_inbounds _isassigned_ra(a::ReinterpretArray, inds...) = true # that is not entirely true, but computing exactly which indexes will be accessed in the parent requires a lot of duplication from the _getindex_ra code
×
415

416
@propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, inds::Vararg{Int, N}) where {T,N,S}
276✔
417
    check_readable(a)
8,186,119✔
418
    check_ptr_indexable(a) && return _getindex_ptr(a, inds...)
8,341,681✔
419
    _getindex_ra(a, inds[1], tail(inds))
129,437,157✔
420
end
421

422
@propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, i::Int) where {T,N,S}
108✔
423
    check_readable(a)
25,776✔
424
    check_ptr_indexable(a) && return _getindex_ptr(a, i)
25,776✔
425
    if isa(IndexStyle(a), IndexLinear)
10,329✔
426
        return _getindex_ra(a, i, ())
2,019✔
427
    end
428
    # Convert to full indices here, to avoid needing multiple conversions in
429
    # the loop in _getindex_ra
430
    inds = _to_subscript_indices(a, i)
14,076✔
431
    isempty(inds) ? _getindex_ra(a, firstindex(a), ()) : _getindex_ra(a, inds[1], tail(inds))
8,652✔
432
end
433

434
@propagate_inbounds function getindex(a::ReshapedReinterpretArray{T,N,S}, ind::SCartesianIndex2) where {T,N,S}
435
    check_readable(a)
24,204✔
436
    s = Ref{S}(a.parent[ind.j])
24,204✔
437
    tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
24,204✔
438
    GC.@preserve s return unsafe_load(tptr, ind.i)
24,204✔
439
end
440

441
@inline function _getindex_ptr(a::ReinterpretArray{T}, inds...) where {T}
442
    @boundscheck checkbounds(a, inds...)
464,593✔
443
    li = _to_linear_index(a, inds...)
308,989✔
444
    ap = cconvert(Ptr{T}, a)
464,557✔
445
    p = unsafe_convert(Ptr{T}, ap) + sizeof(T) * (li - 1)
464,557✔
446
    GC.@preserve ap return unsafe_load(p)
464,557✔
447
end
448

449
@propagate_inbounds function _getindex_ra(a::NonReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT}
450
    # Make sure to match the scalar reinterpret if that is applicable
451
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
7,896,849✔
452
        if issingletontype(T) # singleton types
7,891,986✔
453
            @boundscheck checkbounds(a, i1, tailinds...)
279✔
454
            return T.instance
261✔
455
        end
456
        return reinterpret(T, a.parent[i1, tailinds...])
129,435,891✔
457
    else
458
        @boundscheck checkbounds(a, i1, tailinds...)
4,917✔
459
        ind_start, sidx = divrem((i1-1)*sizeof(T), sizeof(S))
4,809✔
460
        # Optimizations that avoid branches
461
        if sizeof(T) % sizeof(S) == 0
4,809✔
462
            # T is bigger than S and contains an integer number of them
463
            n = sizeof(T) ÷ sizeof(S)
249✔
464
            t = Ref{T}()
249✔
465
            GC.@preserve t begin
249✔
466
                sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
249✔
467
                for i = 1:n
249✔
468
                     s = a.parent[ind_start + i, tailinds...]
420✔
469
                     unsafe_store!(sptr, s, i)
396✔
470
                end
543✔
471
            end
472
            return t[]
249✔
473
        elseif sizeof(S) % sizeof(T) == 0
4,560✔
474
            # S is bigger than T and contains an integer number of them
475
            s = Ref{S}(a.parent[ind_start + 1, tailinds...])
4,524✔
476
            GC.@preserve s begin
4,452✔
477
                tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
4,452✔
478
                return unsafe_load(tptr + sidx)
4,452✔
479
            end
480
        else
481
            i = 1
108✔
482
            nbytes_copied = 0
108✔
483
            # This is a bit complicated to deal with partial elements
484
            # at both the start and the end. LLVM will fold as appropriate,
485
            # once it knows the data layout
486
            s = Ref{S}()
108✔
487
            t = Ref{T}()
108✔
488
            GC.@preserve s t begin
108✔
489
                sptr = Ptr{S}(unsafe_convert(Ref{S}, s))
108✔
490
                tptr = Ptr{T}(unsafe_convert(Ref{T}, t))
108✔
491
                while nbytes_copied < sizeof(T)
348✔
492
                    s[] = a.parent[ind_start + i, tailinds...]
252✔
493
                    nb = min(sizeof(S) - sidx, sizeof(T)-nbytes_copied)
240✔
494
                    memcpy(tptr + nbytes_copied, sptr + sidx, nb)
240✔
495
                    nbytes_copied += nb
240✔
496
                    sidx = 0
240✔
497
                    i += 1
240✔
498
                end
348✔
499
            end
500
            return t[]
108✔
501
        end
502
    end
503
end
504

505
@propagate_inbounds function _getindex_ra(a::ReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT}
506
    # Make sure to match the scalar reinterpret if that is applicable
507
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
6,033✔
508
        if issingletontype(T) # singleton types
2,067✔
509
            @boundscheck checkbounds(a, i1, tailinds...)
192✔
510
            return T.instance
186✔
511
        end
512
        return reinterpret(T, a.parent[i1, tailinds...])
1,878✔
513
    end
514
    @boundscheck checkbounds(a, i1, tailinds...)
3,966✔
515
    if sizeof(T) >= sizeof(S)
3,966✔
516
        t = Ref{T}()
360✔
517
        GC.@preserve t begin
360✔
518
            sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
360✔
519
            if sizeof(T) > sizeof(S)
360✔
520
                # Extra dimension in the parent array
521
                n = sizeof(T) ÷ sizeof(S)
336✔
522
                if isempty(tailinds) && IndexStyle(a.parent) === IndexLinear()
336✔
523
                    offset = n * (i1 - firstindex(a))
90✔
524
                    for i = 1:n
90✔
525
                        s = a.parent[i + offset]
420✔
526
                        unsafe_store!(sptr, s, i)
420✔
527
                    end
750✔
528
                else
529
                    for i = 1:n
246✔
530
                        s = a.parent[i, i1, tailinds...]
552✔
531
                        unsafe_store!(sptr, s, i)
492✔
532
                    end
738✔
533
                end
534
            else
535
                # No extra dimension
536
                s = a.parent[i1, tailinds...]
24✔
537
                unsafe_store!(sptr, s)
360✔
538
            end
539
        end
540
        return t[]
360✔
541
    end
542
    # S is bigger than T and contains an integer number of them
543
    # n = sizeof(S) ÷ sizeof(T)
544
    s = Ref{S}()
3,606✔
545
    GC.@preserve s begin
3,606✔
546
        tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
3,606✔
547
        s[] = a.parent[tailinds...]
3,642✔
548
        return unsafe_load(tptr, i1)
3,606✔
549
    end
550
end
551

552
@propagate_inbounds function setindex!(a::NonReshapedReinterpretArray{T,0,S}, v) where {T,S}
6✔
553
    if isprimitivetype(S) && isprimitivetype(T)
39✔
554
        a.parent[] = reinterpret(S, convert(T, v)::T)
15✔
555
        return a
9✔
556
    end
557
    setindex!(a, v, firstindex(a))
24✔
558
end
559

560
@propagate_inbounds setindex!(a::ReshapedReinterpretArray{T,0}, v) where {T} = setindex!(a, v, firstindex(a))
84✔
561

562
@propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, inds::Vararg{Int, N}) where {T,N,S}
171✔
563
    check_writable(a)
131,898,918✔
564
    check_ptr_indexable(a) && return _setindex_ptr!(a, v, inds...)
131,898,912✔
565
    _setindex_ra!(a, v, inds[1], tail(inds))
131,945,970✔
566
end
567

568
@propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, i::Int) where {T,N,S}
102✔
569
    check_writable(a)
540✔
570
    check_ptr_indexable(a) && return _setindex_ptr!(a, v, i)
540✔
571
    if isa(IndexStyle(a), IndexLinear)
411✔
572
        return _setindex_ra!(a, v, i, ())
321✔
573
    end
574
    inds = _to_subscript_indices(a, i)
156✔
575
    isempty(inds) ? _setindex_ra!(a, v, firstindex(a), ()) : _setindex_ra!(a, v, inds[1], tail(inds))
168✔
576
end
577

578
@propagate_inbounds function setindex!(a::ReshapedReinterpretArray{T,N,S}, v, ind::SCartesianIndex2) where {T,N,S}
579
    check_writable(a)
15✔
580
    v = convert(T, v)::T
15✔
581
    s = Ref{S}(a.parent[ind.j])
15✔
582
    GC.@preserve s begin
15✔
583
        tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
15✔
584
        unsafe_store!(tptr, v, ind.i)
15✔
585
    end
586
    a.parent[ind.j] = s[]
15✔
587
    return a
15✔
588
end
589

590
@inline function _setindex_ptr!(a::ReinterpretArray{T}, v, inds...) where {T}
591
    @boundscheck checkbounds(a, inds...)
2,478✔
592
    li = _to_linear_index(a, inds...)
2,442✔
593
    ap = cconvert(Ptr{T}, a)
2,442✔
594
    p = unsafe_convert(Ptr{T}, ap) + sizeof(T) * (li - 1)
2,442✔
595
    GC.@preserve ap unsafe_store!(p, v)
2,442✔
596
    return a
2,442✔
597
end
598

599
@propagate_inbounds function _setindex_ra!(a::NonReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT}
600
    v = convert(T, v)::T
131,896,767✔
601
    # Make sure to match the scalar reinterpret if that is applicable
602
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
131,896,665✔
603
        if issingletontype(T) # singleton types
131,896,293✔
604
            @boundscheck checkbounds(a, i1, tailinds...)
18✔
605
            # setindex! is a noop except for the index check
606
        else
607
            setindex!(a.parent, reinterpret(S, v), i1, tailinds...)
131,945,673✔
608
        end
609
    else
610
        @boundscheck checkbounds(a, i1, tailinds...)
426✔
611
        ind_start, sidx = divrem((i1-1)*sizeof(T), sizeof(S))
318✔
612
        # Optimizations that avoid branches
613
        if sizeof(T) % sizeof(S) == 0
318✔
614
            # T is bigger than S and contains an integer number of them
615
            t = Ref{T}(v)
57✔
616
            GC.@preserve t begin
57✔
617
                sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
57✔
618
                n = sizeof(T) ÷ sizeof(S)
57✔
619
                for i = 1:n
57✔
620
                    s = unsafe_load(sptr, i)
75✔
621
                    a.parent[ind_start + i, tailinds...] = s
75✔
622
                end
93✔
623
            end
624
        elseif sizeof(S) % sizeof(T) == 0
261✔
625
            # S is bigger than T and contains an integer number of them
626
            s = Ref{S}(a.parent[ind_start + 1, tailinds...])
237✔
627
            GC.@preserve s begin
237✔
628
                tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
237✔
629
                unsafe_store!(tptr + sidx, v)
237✔
630
                a.parent[ind_start + 1, tailinds...] = s[]
237✔
631
            end
632
        else
633
            t = Ref{T}(v)
24✔
634
            s = Ref{S}()
24✔
635
            GC.@preserve t s begin
24✔
636
                tptr = Ptr{UInt8}(unsafe_convert(Ref{T}, t))
24✔
637
                sptr = Ptr{UInt8}(unsafe_convert(Ref{S}, s))
24✔
638
                nbytes_copied = 0
24✔
639
                i = 1
24✔
640
                # Deal with any partial elements at the start. We'll have to copy in the
641
                # element from the original array and overwrite the relevant parts
642
                if sidx != 0
24✔
643
                    s[] = a.parent[ind_start + i, tailinds...]
15✔
644
                    nb = min((sizeof(S) - sidx) % UInt, sizeof(T) % UInt)
12✔
645
                    memcpy(sptr + sidx, tptr, nb)
12✔
646
                    nbytes_copied += nb
12✔
647
                    a.parent[ind_start + i, tailinds...] = s[]
12✔
648
                    i += 1
12✔
649
                    sidx = 0
12✔
650
                end
651
                # Deal with the main body of elements
652
                while nbytes_copied < sizeof(T) && (sizeof(T) - nbytes_copied) > sizeof(S)
36✔
653
                    nb = min(sizeof(S), sizeof(T) - nbytes_copied)
12✔
654
                    memcpy(sptr, tptr + nbytes_copied, nb)
12✔
655
                    nbytes_copied += nb
12✔
656
                    a.parent[ind_start + i, tailinds...] = s[]
12✔
657
                    i += 1
12✔
658
                end
12✔
659
                # Deal with trailing partial elements
660
                if nbytes_copied < sizeof(T)
24✔
661
                    s[] = a.parent[ind_start + i, tailinds...]
30✔
662
                    nb = min(sizeof(S), sizeof(T) - nbytes_copied)
24✔
663
                    memcpy(sptr, tptr + nbytes_copied, nb)
24✔
664
                    a.parent[ind_start + i, tailinds...] = s[]
24✔
665
                end
666
            end
667
        end
668
    end
669
    return a
131,896,635✔
670
end
671

672
@propagate_inbounds function _setindex_ra!(a::ReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT}
673
    v = convert(T, v)::T
267✔
674
    # Make sure to match the scalar reinterpret if that is applicable
675
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
264✔
676
        if issingletontype(T) # singleton types
48✔
677
            @boundscheck checkbounds(a, i1, tailinds...)
9✔
678
            # setindex! is a noop except for the index check
679
        else
680
            setindex!(a.parent, reinterpret(S, v), i1, tailinds...)
42✔
681
        end
682
    end
683
    @boundscheck checkbounds(a, i1, tailinds...)
261✔
684
    if sizeof(T) >= sizeof(S)
261✔
685
        t = Ref{T}(v)
141✔
686
        GC.@preserve t begin
141✔
687
            sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
141✔
688
            if sizeof(T) > sizeof(S)
141✔
689
                # Extra dimension in the parent array
690
                n = sizeof(T) ÷ sizeof(S)
81✔
691
                if isempty(tailinds) && IndexStyle(a.parent) === IndexLinear()
81✔
692
                    offset = n * (i1 - firstindex(a))
12✔
693
                    for i = 1:n
12✔
694
                        s = unsafe_load(sptr, i)
24✔
695
                        a.parent[i + offset] = s
24✔
696
                    end
36✔
697
                else
698
                    for i = 1:n
69✔
699
                        s = unsafe_load(sptr, i)
138✔
700
                        a.parent[i, i1, tailinds...] = s
138✔
701
                    end
207✔
702
                end
703
            else # sizeof(T) == sizeof(S)
704
                # No extra dimension
705
                s = unsafe_load(sptr)
60✔
706
                a.parent[i1, tailinds...] = s
141✔
707
            end
708
        end
709
    else
710
        # S is bigger than T and contains an integer number of them
711
        s = Ref{S}()
120✔
712
        GC.@preserve s begin
120✔
713
            tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
120✔
714
            s[] = a.parent[tailinds...]
144✔
715
            unsafe_store!(tptr, v, i1)
120✔
716
            a.parent[tailinds...] = s[]
120✔
717
        end
718
    end
719
    return a
261✔
720
end
721

722
# Padding
723
struct Padding
724
    offset::Int # 0-indexed offset of the next valid byte; sizeof(T) indicates trailing padding
3✔
725
    size::Int   # bytes of padding before a valid byte
726
end
727
function intersect(p1::Padding, p2::Padding)
×
728
    start = max(p1.offset, p2.offset)
×
729
    stop = min(p1.offset + p1.size, p2.offset + p2.size)
×
730
    Padding(start, max(0, stop-start))
×
731
end
732

733
struct PaddingError <: Exception
734
    S::Type
15✔
735
    T::Type
736
end
737

738
function showerror(io::IO, p::PaddingError)
×
739
    print(io, "Padding of type $(p.S) is not compatible with type $(p.T).")
×
740
end
741

742
"""
743
    CyclePadding(padding, total_size)
744

745
Cycles an iterator of `Padding` structs, restarting the padding at `total_size`.
746
E.g. if `padding` is all the padding in a struct and `total_size` is the total
747
aligned size of that array, `CyclePadding` will correspond to the padding in an
748
infinite vector of such structs.
749
"""
750
struct CyclePadding{P}
751
    padding::P
1,532✔
752
    total_size::Int
753
end
754
eltype(::Type{<:CyclePadding}) = Padding
×
755
IteratorSize(::Type{<:CyclePadding}) = IsInfinite()
×
756
isempty(cp::CyclePadding) = isempty(cp.padding)
×
757
function iterate(cp::CyclePadding)
758
    y = iterate(cp.padding)
766✔
759
    y === nothing && return nothing
766✔
760
    y[1], (0, y[2])
×
761
end
762
function iterate(cp::CyclePadding, state::Tuple)
×
763
    y = iterate(cp.padding, tail(state)...)
×
764
    y === nothing && return iterate(cp, (state[1]+cp.total_size,))
×
765
    Padding(y[1].offset+state[1], y[1].size), (state[1], tail(y)...)
×
766
end
767

768
"""
769
    Compute the location of padding in an isbits datatype. Recursive over the fields of that type.
770
"""
771
@assume_effects :foldable function padding(T::DataType, baseoffset::Int = 0)
4,359✔
772
    pads = Padding[]
7,343✔
773
    last_end::Int = baseoffset
4,576✔
774
    for i = 1:fieldcount(T)
4,906✔
775
        offset = baseoffset + Int(fieldoffset(T, i))
2,007✔
776
        fT = fieldtype(T, i)
2,007✔
777
        append!(pads, padding(fT, offset))
2,007✔
778
        if offset != last_end
2,007✔
779
            push!(pads, Padding(offset, offset-last_end))
×
780
        end
781
        last_end = offset + sizeof(fT)
2,007✔
782
    end
3,677✔
783
    if 0 < last_end - baseoffset < sizeof(T)
4,576✔
784
        push!(pads, Padding(baseoffset + sizeof(T), sizeof(T) - last_end + baseoffset))
3✔
785
    end
786
    return Core.svec(pads...)
4,807✔
787
end
788

789
function CyclePadding(T::DataType)
1,532✔
790
    a, s = datatype_alignment(T), sizeof(T)
1,532✔
791
    as = s + (a - (s % a)) % a
1,532✔
792
    pad = padding(T)
1,532✔
793
    if s != as
1,532✔
794
        pad = Core.svec(pad..., Padding(s, as - s))
×
795
    end
796
    CyclePadding(pad, as)
1,532✔
797
end
798

799
@assume_effects :total function array_subpadding(S, T)
766✔
800
    lcm_size = lcm(sizeof(S), sizeof(T))
766✔
801
    s, t = CyclePadding(S), CyclePadding(T)
766✔
802
    checked_size = 0
766✔
803
    # use of Stateful harms inference and makes this vulnerable to invalidation
804
    (pad, tstate) = let
805
        it = iterate(t)
766✔
806
        it === nothing && return true
766✔
807
        it
×
808
    end
809
    (ps, sstate) = let
810
        it = iterate(s)
×
811
        it === nothing && return false
×
812
        it
×
813
    end
814
    while checked_size < lcm_size
×
815
        while true
×
816
            # See if there's corresponding padding in S
817
            ps.offset > pad.offset && return false
×
818
            intersect(ps, pad) == pad && break
×
819
            ps, sstate = iterate(s, sstate)
×
820
        end
×
821
        checked_size = pad.offset + pad.size
×
822
        pad, tstate = iterate(t, tstate)
×
823
    end
×
824
    return true
×
825
end
826

827
@assume_effects :foldable function struct_subpadding(::Type{Out}, ::Type{In}) where {Out, In}
362✔
828
    padding(Out) == padding(In)
362✔
829
end
830

831
@assume_effects :foldable function packedsize(::Type{T}) where T
229✔
832
    pads = padding(T)
229✔
833
    return sizeof(T) - sum((p.size for p ∈ pads), init = 0)
229✔
834
end
835

836
@assume_effects :foldable ispacked(::Type{T}) where T = isempty(padding(T))
48✔
837

838
function _copytopacked!(ptr_out::Ptr{Out}, ptr_in::Ptr{In}) where {Out, In}
12✔
839
    writeoffset = 0
12✔
840
    for i ∈ 1:fieldcount(In)
12✔
841
        readoffset = fieldoffset(In, i)
39✔
842
        fT = fieldtype(In, i)
39✔
843
        if ispacked(fT)
45✔
844
            readsize = sizeof(fT)
42✔
845
            memcpy(ptr_out + writeoffset, ptr_in + readoffset, readsize)
36✔
846
            writeoffset += readsize
36✔
847
        else # nested padded type
848
            _copytopacked!(ptr_out + writeoffset, Ptr{fT}(ptr_in + readoffset))
3✔
849
            writeoffset += packedsize(fT)
3✔
850
        end
851
    end
39✔
852
end
853

854
function _copyfrompacked!(ptr_out::Ptr{Out}, ptr_in::Ptr{In}) where {Out, In}
12✔
855
    readoffset = 0
12✔
856
    for i ∈ 1:fieldcount(Out)
12✔
857
        writeoffset = fieldoffset(Out, i)
39✔
858
        fT = fieldtype(Out, i)
39✔
859
        if ispacked(fT)
45✔
860
            writesize = sizeof(fT)
42✔
861
            memcpy(ptr_out + writeoffset, ptr_in + readoffset, writesize)
36✔
862
            readoffset += writesize
36✔
863
        else # nested padded type
864
            _copyfrompacked!(Ptr{fT}(ptr_out + writeoffset), ptr_in + readoffset)
3✔
865
            readoffset += packedsize(fT)
3✔
866
        end
867
    end
39✔
868
end
869

870
@inline function _reinterpret(::Type{Out}, x::In) where {Out, In}
871
    # handle non-primitive types
872
    isbitstype(Out) || throw(ArgumentError("Target type for `reinterpret` must be isbits"))
11,161✔
873
    isbitstype(In) || throw(ArgumentError("Source type for `reinterpret` must be isbits"))
11,158✔
874
    inpackedsize = packedsize(In)
11,155✔
875
    outpackedsize = packedsize(Out)
11,155✔
876
    inpackedsize == outpackedsize ||
11,155✔
877
        throw(ArgumentError(LazyString("Packed sizes of types ", Out, " and ", In,
878
            " do not match; got ", outpackedsize, " and ", inpackedsize, ", respectively.")))
879
    in = Ref{In}(x)
11,490✔
880
    out = Ref{Out}()
11,490✔
881
    if struct_subpadding(Out, In)
11,143✔
882
        # if packed the same, just copy
883
        GC.@preserve in out begin
11,481✔
884
            ptr_in = unsafe_convert(Ptr{In}, in)
11,481✔
885
            ptr_out = unsafe_convert(Ptr{Out}, out)
11,481✔
886
            memcpy(ptr_out, ptr_in, sizeof(Out))
11,481✔
887
        end
888
        return out[]
11,481✔
889
    else
890
        # mismatched padding
891
        return _reinterpret_padding(Out, x)
9✔
892
    end
893
end
894

895
# If the code reaches this part, it needs to handle padding and is unlikely
896
# to compile to a noop. Therefore, we don't forcibly inline it.
897
function _reinterpret_padding(::Type{Out}, x::In) where {Out, In}
898
    inpackedsize = packedsize(In)
9✔
899
    in = Ref{In}(x)
9✔
900
    out = Ref{Out}()
9✔
901
    GC.@preserve in out begin
9✔
902
        ptr_in = unsafe_convert(Ptr{In}, in)
9✔
903
        ptr_out = unsafe_convert(Ptr{Out}, out)
9✔
904

905
        if fieldcount(In) > 0 && ispacked(Out)
9✔
906
            _copytopacked!(ptr_out, ptr_in)
×
907
        elseif fieldcount(Out) > 0 && ispacked(In)
9✔
908
            _copyfrompacked!(ptr_out, ptr_in)
×
909
        else
910
            packed = Ref{NTuple{inpackedsize, UInt8}}()
9✔
911
            GC.@preserve packed begin
9✔
912
                ptr_packed = unsafe_convert(Ptr{NTuple{inpackedsize, UInt8}}, packed)
9✔
913
                _copytopacked!(ptr_packed, ptr_in)
9✔
914
                _copyfrompacked!(ptr_out, ptr_packed)
9✔
915
            end
916
        end
917
    end
918
    return out[]
9✔
919
end
920

921
function String(v::ReinterpretArray{UInt8,1,S,<:Union{Vector{S},Memory{S}},IsReshaped}) where {S,IsReshaped}
12✔
922
    len = length(v)
18✔
923
    len == 0 && return ""
18✔
924
    check_readable(v) # stringifying empty arrays is always allowed
15✔
925
    return ccall(:jl_pchar_to_string, Ref{String}, (Ptr{UInt8}, Int), v, len)
12✔
926
end
927

928
# Reductions with IndexSCartesian2
929

930
function _mapreduce(f::F, op::OP, style::IndexSCartesian2{K}, A::AbstractArrayOrBroadcasted) where {F,OP,K}
931
    inds = eachindex(style, A)
12✔
932
    n = size(inds)[2]
12✔
933
    if n == 0
12✔
934
        return mapreduce_empty_iter(f, op, A, IteratorEltype(A))
×
935
    else
936
        return mapreduce_impl(f, op, A, first(inds), last(inds))
12✔
937
    end
938
end
939

940
@noinline function mapreduce_impl(f::F, op::OP, A::AbstractArrayOrBroadcasted,
24✔
941
                                  ifirst::SCI, ilast::SCI, blksize::Int) where {F,OP,SCI<:SCartesianIndex2{K}} where K
942
    if ilast.j - ifirst.j < blksize
24✔
943
        # sequential portion
944
        @inbounds a1 = A[ifirst]
18✔
945
        @inbounds a2 = A[SCI(2,ifirst.j)]
18✔
946
        v = op(f(a1), f(a2))
18✔
947
        @simd for i = ifirst.i + 2 : K
18✔
948
            @inbounds ai = A[SCI(i,ifirst.j)]
18✔
949
            v = op(v, f(ai))
18✔
950
        end
951
        # Remaining columns
952
        for j = ifirst.j+1 : ilast.j
18✔
953
            @simd for i = 1:K
7,998✔
954
                @inbounds ai = A[SCI(i,j)]
23,994✔
955
                v = op(v, f(ai))
23,994✔
956
            end
957
        end
15,978✔
958
        return v
18✔
959
    else
960
        # pairwise portion
961
        jmid = ifirst.j + (ilast.j - ifirst.j) >> 1
6✔
962
        v1 = mapreduce_impl(f, op, A, ifirst, SCI(K,jmid), blksize)
6✔
963
        v2 = mapreduce_impl(f, op, A, SCI(1,jmid+1), ilast, blksize)
6✔
964
        return op(v1, v2)
6✔
965
    end
966
end
967

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

© 2026 Coveralls, Inc