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

JuliaLang / julia / #37865

08 Aug 2024 11:44PM UTC coverage: 86.076% (-1.5%) from 87.613%
#37865

push

local

web-flow
Vendor the terminfo database for use with base/terminfo.jl (#55411)

This adds the `terminfo` database to `deps/`, providing a better user
experience on systems that don't have `terminfo` on the system by
default. The database is built using BinaryBuilder but is not actually
platform-specific (it's built for `AnyPlatform`) and as such, this
fetches the artifact directly rather than adding a new JLL to stdlib,
and it requires no compilation.

A build flag, `WITH_TERMINFO`, is added here and assumed true by
default, allowing users to set `WITH_TERMINFO=0` in Make.user to avoid
bundling `terminfo` should they want to do so.

The lookup policy for `terminfo` entries is still compliant with what's
described in `terminfo(5)`; the bundled directory is taken to be the
first "compiled in" location, i.e. prepended to `@TERMINFO_DIRS@`. This
allows any user settings that exist locally, such as custom entries or
locations, to take precedence.

Fixes #55274

Co-authored-by: Mosè Giordano <giordano@users.noreply.github.com>

1 of 4 new or added lines in 1 file covered. (25.0%)

2056 existing lines in 57 files now uncovered.

76330 of 88677 relevant lines covered (86.08%)

15164230.56 hits per line

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

94.77
/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(LazyString("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(LazyString("cannot reinterpret a zero-dimensional `", S, "` array to `", T,
5✔
21
            "` which is of a ", msg, " size")))
22
    end
23
    function throwsingleton(S::Type, T::Type)
5✔
24
        @noinline
5✔
25
        throw(ArgumentError(LazyString("cannot reinterpret a `", S, "` array to `", T, "` which is a singleton type")))
5✔
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}}
98✔
69
        function thrownonint(S::Type, T::Type, dim)
6,344✔
70
            @noinline
2✔
71
            throw(ArgumentError(LazyString(
2✔
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)
6,344✔
76
            @noinline
2✔
77
            throw(ArgumentError(LazyString("cannot reinterpret a `", S, "` array to `", T,
2✔
78
                "` when the first axis is ", ax1, ". Try reshaping first.")))
79
        end
80
        isbitstype(T) || throwbits(S, T, T)
6,342✔
81
        isbitstype(S) || throwbits(S, T, S)
6,339✔
82
        (N != 0 || sizeof(T) == sizeof(S)) || throwsize0(S, T, "different")
6,336✔
83
        if N != 0 && sizeof(S) != sizeof(T)
6,332✔
84
            ax1 = axes(a)[1]
135,484✔
85
            dim = length(ax1)
6,168✔
86
            if issingletontype(T)
6,168✔
87
                issingletontype(S) || throwsingleton(S, T)
2✔
88
            else
89
                rem(dim*sizeof(S),sizeof(T)) == 0 || thrownonint(S, T, dim)
135,484✔
90
            end
91
            first(ax1) == 1 || throwaxes1(S, T, ax1)
6,166✔
92
        end
93
        readable = array_subpadding(T, S)
6,326✔
94
        writable = array_subpadding(S, T)
6,326✔
95
        new{T, N, S, A, false}(a, readable, writable)
135,827✔
96
    end
97
    reinterpret(::Type{T}, a::AbstractArray{T}) where {T} = a
2✔
98

99
    # With reshaping
100
    function reinterpret(::typeof(reshape), ::Type{T}, a::A) where {T,S,A<:AbstractArray{S}}
91✔
101
        function throwintmult(S::Type, T::Type)
1,038✔
102
            @noinline
1✔
103
            throw(ArgumentError(LazyString("`reinterpret(reshape, T, a)` requires that one of `sizeof(T)` (got ",
1✔
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)
1,039✔
107
            @noinline
2✔
108
            throw(ArgumentError(LazyString("`reinterpret(reshape, ", T, ", a)` where `eltype(a)` is ", eltype(a),
2✔
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)
1,040✔
113
            @noinline
3✔
114
            throw(ArgumentError(LazyString("`reinterpret(reshape, ", T, ", a)` where `eltype(a)` is ", S,
3✔
115
                " requires that ", T, " be a singleton type, since ", S, " is one")))
116
        end
117
        isbitstype(T) || throwbits(S, T, T)
1,037✔
118
        isbitstype(S) || throwbits(S, T, S)
1,035✔
119
        if sizeof(S) == sizeof(T)
1,034✔
120
            N = ndims(a)
58✔
121
        elseif sizeof(S) > sizeof(T)
976✔
122
            issingletontype(T) && throwsingleton(S, T)
923✔
123
            rem(sizeof(S), sizeof(T)) == 0 || throwintmult(S, T)
920✔
124
            N = ndims(a) + 1
920✔
125
        else
126
            issingletontype(S) && throwfromsingleton(S, T)
53✔
127
            rem(sizeof(T), sizeof(S)) == 0 || throwintmult(S, T)
50✔
128
            N = ndims(a) - 1
49✔
129
            N > -1 || throwsize0(S, T, "larger")
49✔
130
            axes(a, 1) == OneTo(sizeof(T) ÷ sizeof(S)) || throwsize1(a, T)
50✔
131
        end
132
        readable = array_subpadding(T, S)
1,024✔
133
        writable = array_subpadding(S, T)
1,024✔
134
        new{T, N, S, A, true}(a, readable, writable)
1,024✔
135
    end
136
    reinterpret(::typeof(reshape), ::Type{T}, a::AbstractArray{T}) where {T} = a
2✔
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)
1✔
182
reinterpret(::typeof(reshape), ::Type{T}, a::ReshapedReinterpretArray) where {T} = reinterpret(reshape, T, a.parent)
2✔
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)...)
24,393,041✔
196
stride(A::Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}, k::Integer) =
16,231,914✔
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}
86✔
200
    _checkcontiguous(Bool, a) && return size_to_strides(1, size(a)...)
3,127✔
201
    stp = strides(parent(a))
3,106✔
202
    els, elp = sizeof(T), sizeof(S)
3,106✔
203
    els == elp && return stp # 0dim parent is also handled here.
3,106✔
204
    IsReshaped && els < elp && return (1, _checked_strides(stp, els, elp)...)
1,976✔
205
    stp[1] == 1 || throw(ArgumentError("Parent must be contiguous in the 1st dimension!"))
670✔
206
    st′ = _checked_strides(tail(stp), els, elp)
645✔
207
    return IsReshaped ? st′ : (1, st′...)
607✔
208
end
209

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

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

223
similar(a::ReinterpretArray, T::Type, d::Dims) = similar(a.parent, T, d)
259,560✔
224

225
function check_readable(a::ReinterpretArray{T, N, S} where N) where {T,S}
2✔
226
    # See comment in check_writable
227
    if !a.readable && !array_subpadding(T, S)
38,993,627✔
228
        throw(PaddingError(T, S))
2✔
229
    end
230
end
231

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

244
## IndexStyle specializations
245

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

250
IndexStyle(::Type{ReinterpretArray{T,N,S,A,false}}) where {T,N,S,A<:AbstractArray{S,N}} = IndexStyle(A)
21,685✔
251
function IndexStyle(::Type{ReinterpretArray{T,N,S,A,true}}) where {T,N,S,A<:AbstractArray{S}}
252
    if sizeof(T) < sizeof(S)
4,044✔
253
        IndexStyle(A) === IndexLinear() && return IndexSCartesian2{sizeof(S) ÷ sizeof(T)}()
3,258✔
254
        return IndexCartesian()
1,195✔
255
    end
256
    return IndexStyle(A)
786✔
257
end
258
IndexStyle(::IndexSCartesian2{K}, ::IndexSCartesian2{K}) where {K} = IndexSCartesian2{K}()
3✔
259

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

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

271
eachindex(::IndexSCartesian2{K}, A::ReshapedReinterpretArray) where {K} = SCartesianIndices2{K}(eachindex(IndexLinear(), parent(A)))
28✔
272
@inline function eachindex(style::IndexSCartesian2{K}, A::AbstractArray, B::AbstractArray...) where {K}
273
    iter = eachindex(style, A)
3✔
274
    _all_match_first(C->eachindex(style, C), iter, B...) || throw_eachindex_mismatch_indices(IndexSCartesian2{K}(), axes(A), axes.(B)...)
6✔
275
    return iter
3✔
276
end
277

278
size(iter::SCartesianIndices2{K}) where K = (K, length(iter.indices2))
4✔
279
axes(iter::SCartesianIndices2{K}) where K = (OneTo(K), iter.indices2)
9✔
280

281
first(iter::SCartesianIndices2{K}) where {K} = SCartesianIndex2{K}(1, first(iter.indices2))
5✔
282
last(iter::SCartesianIndices2{K}) where {K}  = SCartesianIndex2{K}(K, last(iter.indices2))
7✔
283

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

289
function iterate(iter::SCartesianIndices2{K}) where {K}
290
    ret = iterate(iter.indices2)
28✔
291
    ret === nothing && return nothing
27✔
292
    item2, state2 = ret
27✔
293
    return SCartesianIndex2{K}(1, item2), (1, item2, state2)
27✔
294
end
295

296
function iterate(iter::SCartesianIndices2{K}, (state1, item2, state2)) where {K}
297
    if state1 < K
201✔
298
        item1 = state1 + 1
127✔
299
        return SCartesianIndex2{K}(item1, item2), (item1, item2, state2)
127✔
300
    end
301
    ret = iterate(iter.indices2, state2)
131✔
302
    ret === nothing && return nothing
74✔
303
    item2, state2 = ret
57✔
304
    return SCartesianIndex2{K}(1, item2), (1, item2, state2)
57✔
305
end
306

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

313
_maybe_reshape(::IndexSCartesian2, A::ReshapedReinterpretArray, I...) = A
×
314

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

338
## AbstractArray interface
339

340
parent(a::ReinterpretArray) = a.parent
77,781,141✔
341
dataids(a::ReinterpretArray) = dataids(a.parent)
315✔
342
unaliascopy(a::NonReshapedReinterpretArray{T}) where {T} = reinterpret(T, unaliascopy(a.parent))
2✔
343
unaliascopy(a::ReshapedReinterpretArray{T}) where {T} = reinterpret(reshape, T, unaliascopy(a.parent))
1✔
344

345
function size(a::NonReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
346
    psize = size(a.parent)
25,845✔
347
    size1 = issingletontype(T) ? psize[1] : div(psize[1]*sizeof(S), sizeof(T))
25,845✔
348
    tuple(size1, tail(psize)...)
25,845✔
349
end
350
function size(a::ReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
1✔
351
    psize = size(a.parent)
4,481✔
352
    sizeof(S) > sizeof(T) && return (div(sizeof(S), sizeof(T)), psize...)
4,481✔
353
    sizeof(S) < sizeof(T) && return tail(psize)
289✔
354
    return psize
109✔
355
end
356
size(a::NonReshapedReinterpretArray{T,0}) where {T} = ()
24✔
357

358
function axes(a::NonReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
2✔
359
    paxs = axes(a.parent)
79,519,827✔
360
    f, l = first(paxs[1]), length(paxs[1])
77,874,184✔
361
    size1 = issingletontype(T) ? l : div(l*sizeof(S), sizeof(T))
79,519,827✔
362
    tuple(oftype(paxs[1], f:f+size1-1), tail(paxs)...)
79,519,827✔
363
end
364
function axes(a::ReshapedReinterpretArray{T,N,S} where {N}) where {T,S}
2✔
365
    paxs = axes(a.parent)
16,579✔
366
    sizeof(S) > sizeof(T) && return (OneTo(div(sizeof(S), sizeof(T))), paxs...)
16,579✔
367
    sizeof(S) < sizeof(T) && return tail(paxs)
2,138✔
368
    return paxs
1,780✔
369
end
370
axes(a::NonReshapedReinterpretArray{T,0}) where {T} = ()
6✔
371

372
has_offset_axes(a::ReinterpretArray) = has_offset_axes(a.parent)
3,994✔
373

374
elsize(::Type{<:ReinterpretArray{T}}) where {T} = sizeof(T)
77,777,511✔
375
cconvert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = cconvert(Ptr{S}, a.parent)
80,508,315✔
UNCOV
376
unsafe_convert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = Ptr{T}(unsafe_convert(Ptr{S},a.parent))
×
377

378
@propagate_inbounds function getindex(a::NonReshapedReinterpretArray{T,0,S}) where {T,S}
1✔
379
    if isprimitivetype(T) && isprimitivetype(S)
5✔
380
        reinterpret(T, a.parent[])
2✔
381
    else
382
        a[firstindex(a)]
3✔
383
    end
384
end
385

386
check_ptr_indexable(a::ReinterpretArray, sz = elsize(a)) = check_ptr_indexable(parent(a), sz)
155,545,905✔
387
check_ptr_indexable(a::ReshapedArray, sz) = check_ptr_indexable(parent(a), sz)
488✔
388
check_ptr_indexable(a::FastContiguousSubArray, sz) = check_ptr_indexable(parent(a), sz)
11,441✔
389
check_ptr_indexable(a::Array, sz) = sizeof(eltype(a)) !== sz
77,687,037✔
390
check_ptr_indexable(a::Memory, sz) = true
×
UNCOV
391
check_ptr_indexable(a::AbstractArray, sz) = false
×
392

393
@propagate_inbounds getindex(a::ReinterpretArray) = a[firstindex(a)]
7✔
394

395
@propagate_inbounds isassigned(a::ReinterpretArray, inds::Integer...) = checkbounds(Bool, a, inds...) && (check_ptr_indexable(a) || _isassigned_ra(a, inds...))
12✔
396
@propagate_inbounds isassigned(a::ReinterpretArray, inds::SCartesianIndex2) = isassigned(a.parent, inds.j)
×
UNCOV
397
@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
×
398

399
@propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, inds::Vararg{Int, N}) where {T,N,S}
84✔
400
    check_readable(a)
38,975,909✔
401
    check_ptr_indexable(a) && return _getindex_ptr(a, inds...)
41,730,303✔
402
    _getindex_ra(a, inds[1], tail(inds))
2,245,254✔
403
end
404

405
@propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, i::Int) where {T,N,S}
35✔
406
    check_readable(a)
9,680✔
407
    check_ptr_indexable(a) && return _getindex_ptr(a, i)
9,680✔
408
    if isa(IndexStyle(a), IndexLinear)
3,392✔
409
        return _getindex_ra(a, i, ())
643✔
410
    end
411
    # Convert to full indices here, to avoid needing multiple conversions in
412
    # the loop in _getindex_ra
413
    inds = _to_subscript_indices(a, i)
2,752✔
414
    isempty(inds) ? _getindex_ra(a, 1, ()) : _getindex_ra(a, inds[1], tail(inds))
2,836✔
415
end
416

417
@propagate_inbounds function getindex(a::ReshapedReinterpretArray{T,N,S}, ind::SCartesianIndex2) where {T,N,S}
418
    check_readable(a)
8,038✔
419
    s = Ref{S}(a.parent[ind.j])
8,038✔
420
    tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
8,038✔
421
    GC.@preserve s return unsafe_load(tptr, ind.i)
8,038✔
422
end
423

424
@inline function _getindex_ptr(a::ReinterpretArray{T}, inds...) where {T}
425
    @boundscheck checkbounds(a, inds...)
41,722,878✔
426
    li = _to_linear_index(a, inds...)
38,968,470✔
427
    ap = cconvert(Ptr{T}, a)
41,722,866✔
428
    p = unsafe_convert(Ptr{T}, ap) + sizeof(T) * (li - 1)
41,722,866✔
429
    GC.@preserve ap return unsafe_load(p)
41,722,866✔
430
end
431

432
@propagate_inbounds function _getindex_ra(a::NonReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT}
433
    # Make sure to match the scalar reinterpret if that is applicable
434
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
15,162✔
435
        if issingletontype(T) # singleton types
13,693✔
436
            @boundscheck checkbounds(a, i1, tailinds...)
85✔
437
            return T.instance
79✔
438
        end
439
        return reinterpret(T, a.parent[i1, tailinds...])
2,245,062✔
440
    else
441
        @boundscheck checkbounds(a, i1, tailinds...)
1,487✔
442
        ind_start, sidx = divrem((i1-1)*sizeof(T), sizeof(S))
1,451✔
443
        # Optimizations that avoid branches
444
        if sizeof(T) % sizeof(S) == 0
1,451✔
445
            # T is bigger than S and contains an integer number of them
446
            n = sizeof(T) ÷ sizeof(S)
63✔
447
            t = Ref{T}()
63✔
448
            GC.@preserve t begin
63✔
449
                sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
63✔
450
                for i = 1:n
63✔
451
                     s = a.parent[ind_start + i, tailinds...]
116✔
452
                     unsafe_store!(sptr, s, i)
108✔
453
                end
153✔
454
            end
455
            return t[]
63✔
456
        elseif sizeof(S) % sizeof(T) == 0
1,388✔
457
            # S is bigger than T and contains an integer number of them
458
            s = Ref{S}(a.parent[ind_start + 1, tailinds...])
1,380✔
459
            GC.@preserve s begin
1,356✔
460
                tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
1,356✔
461
                return unsafe_load(tptr + sidx)
1,356✔
462
            end
463
        else
464
            i = 1
32✔
465
            nbytes_copied = 0
32✔
466
            # This is a bit complicated to deal with partial elements
467
            # at both the start and the end. LLVM will fold as appropriate,
468
            # once it knows the data layout
469
            s = Ref{S}()
32✔
470
            t = Ref{T}()
32✔
471
            GC.@preserve s t begin
32✔
472
                sptr = Ptr{S}(unsafe_convert(Ref{S}, s))
32✔
473
                tptr = Ptr{T}(unsafe_convert(Ref{T}, t))
32✔
474
                while nbytes_copied < sizeof(T)
104✔
475
                    s[] = a.parent[ind_start + i, tailinds...]
76✔
476
                    nb = min(sizeof(S) - sidx, sizeof(T)-nbytes_copied)
72✔
477
                    memcpy(tptr + nbytes_copied, sptr + sidx, nb)
72✔
478
                    nbytes_copied += nb
72✔
479
                    sidx = 0
72✔
480
                    i += 1
72✔
481
                end
104✔
482
            end
483
            return t[]
32✔
484
        end
485
    end
486
end
487

488
@propagate_inbounds function _getindex_ra(a::ReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT}
489
    # Make sure to match the scalar reinterpret if that is applicable
490
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
1,949✔
491
        if issingletontype(T) # singleton types
673✔
492
            @boundscheck checkbounds(a, i1, tailinds...)
64✔
493
            return T.instance
62✔
494
        end
495
        return reinterpret(T, a.parent[i1, tailinds...])
614✔
496
    end
497
    @boundscheck checkbounds(a, i1, tailinds...)
1,276✔
498
    if sizeof(T) >= sizeof(S)
1,276✔
499
        t = Ref{T}()
86✔
500
        GC.@preserve t begin
86✔
501
            sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
86✔
502
            if sizeof(T) > sizeof(S)
86✔
503
                # Extra dimension in the parent array
504
                n = sizeof(T) ÷ sizeof(S)
78✔
505
                if isempty(tailinds) && IndexStyle(a.parent) === IndexLinear()
78✔
506
                    offset = n * (i1 - firstindex(a))
21✔
507
                    for i = 1:n
21✔
508
                        s = a.parent[i + offset]
98✔
509
                        unsafe_store!(sptr, s, i)
98✔
510
                    end
175✔
511
                else
512
                    for i = 1:n
57✔
513
                        s = a.parent[i, i1, tailinds...]
126✔
514
                        unsafe_store!(sptr, s, i)
114✔
515
                    end
171✔
516
                end
517
            else
518
                # No extra dimension
519
                s = a.parent[i1, tailinds...]
8✔
520
                unsafe_store!(sptr, s)
86✔
521
            end
522
        end
523
        return t[]
86✔
524
    end
525
    # S is bigger than T and contains an integer number of them
526
    # n = sizeof(S) ÷ sizeof(T)
527
    s = Ref{S}()
1,190✔
528
    GC.@preserve s begin
1,190✔
529
        tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
1,190✔
530
        s[] = a.parent[tailinds...]
1,202✔
531
        return unsafe_load(tptr, i1)
1,190✔
532
    end
533
end
534

535
@propagate_inbounds function setindex!(a::NonReshapedReinterpretArray{T,0,S}, v) where {T,S}
2✔
536
    if isprimitivetype(S) && isprimitivetype(T)
6✔
537
        a.parent[] = reinterpret(S, v)
2✔
538
        return a
2✔
539
    end
540
    setindex!(a, v, firstindex(a))
4✔
541
end
542

543
@propagate_inbounds setindex!(a::ReinterpretArray, v) = setindex!(a, v, firstindex(a))
10✔
544

545
@propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, inds::Vararg{Int, N}) where {T,N,S}
57✔
546
    check_writable(a)
38,798,437✔
547
    check_ptr_indexable(a) && return _setindex_ptr!(a, v, inds...)
38,798,435✔
548
    _setindex_ra!(a, v, inds[1], tail(inds))
173,528✔
549
end
550

551
@propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, i::Int) where {T,N,S}
34✔
552
    check_writable(a)
143✔
553
    check_ptr_indexable(a) && return _setindex_ptr!(a, v, i)
143✔
554
    if isa(IndexStyle(a), IndexLinear)
103✔
555
        return _setindex_ra!(a, v, i, ())
88✔
556
    end
557
    inds = _to_subscript_indices(a, i)
18✔
558
    _setindex_ra!(a, v, inds[1], tail(inds))
20✔
559
end
560

561
@propagate_inbounds function setindex!(a::ReshapedReinterpretArray{T,N,S}, v, ind::SCartesianIndex2) where {T,N,S}
562
    check_writable(a)
3✔
563
    v = convert(T, v)::T
3✔
564
    s = Ref{S}(a.parent[ind.j])
3✔
565
    GC.@preserve s begin
3✔
566
        tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
3✔
567
        unsafe_store!(tptr, v, ind.i)
3✔
568
    end
569
    a.parent[ind.j] = s[]
3✔
570
    return a
3✔
571
end
572

573
@inline function _setindex_ptr!(a::ReinterpretArray{T}, v, inds...) where {T}
574
    @boundscheck checkbounds(a, inds...)
38,776,426✔
575
    li = _to_linear_index(a, inds...)
38,776,414✔
576
    ap = cconvert(Ptr{T}, a)
38,776,414✔
577
    p = unsafe_convert(Ptr{T}, ap) + sizeof(T) * (li - 1)
38,776,414✔
578
    GC.@preserve ap unsafe_store!(p, v)
38,776,414✔
579
    return a
38,776,414✔
580
end
581

582
@propagate_inbounds function _setindex_ra!(a::NonReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT}
583
    v = convert(T, v)::T
22,108✔
584
    # Make sure to match the scalar reinterpret if that is applicable
585
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
22,106✔
586
        if issingletontype(T) # singleton types
21,994✔
587
            @boundscheck checkbounds(a, i1, tailinds...)
6✔
588
            # setindex! is a noop except for the index check
589
        else
590
            setindex!(a.parent, reinterpret(S, v), i1, tailinds...)
173,466✔
591
        end
592
    else
593
        @boundscheck checkbounds(a, i1, tailinds...)
130✔
594
        ind_start, sidx = divrem((i1-1)*sizeof(T), sizeof(S))
94✔
595
        # Optimizations that avoid branches
596
        if sizeof(T) % sizeof(S) == 0
94✔
597
            # T is bigger than S and contains an integer number of them
598
            t = Ref{T}(v)
11✔
599
            GC.@preserve t begin
11✔
600
                sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
11✔
601
                n = sizeof(T) ÷ sizeof(S)
11✔
602
                for i = 1:n
11✔
603
                    s = unsafe_load(sptr, i)
15✔
604
                    a.parent[ind_start + i, tailinds...] = s
15✔
605
                end
19✔
606
            end
607
        elseif sizeof(S) % sizeof(T) == 0
83✔
608
            # S is bigger than T and contains an integer number of them
609
            s = Ref{S}(a.parent[ind_start + 1, tailinds...])
79✔
610
            GC.@preserve s begin
79✔
611
                tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
79✔
612
                unsafe_store!(tptr + sidx, v)
79✔
613
                a.parent[ind_start + 1, tailinds...] = s[]
79✔
614
            end
615
        else
616
            t = Ref{T}(v)
4✔
617
            s = Ref{S}()
4✔
618
            GC.@preserve t s begin
4✔
619
                tptr = Ptr{UInt8}(unsafe_convert(Ref{T}, t))
4✔
620
                sptr = Ptr{UInt8}(unsafe_convert(Ref{S}, s))
4✔
621
                nbytes_copied = 0
4✔
622
                i = 1
4✔
623
                # Deal with any partial elements at the start. We'll have to copy in the
624
                # element from the original array and overwrite the relevant parts
625
                if sidx != 0
4✔
626
                    s[] = a.parent[ind_start + i, tailinds...]
3✔
627
                    nb = min((sizeof(S) - sidx) % UInt, sizeof(T) % UInt)
2✔
628
                    memcpy(sptr + sidx, tptr, nb)
2✔
629
                    nbytes_copied += nb
2✔
630
                    a.parent[ind_start + i, tailinds...] = s[]
2✔
631
                    i += 1
2✔
632
                    sidx = 0
2✔
633
                end
634
                # Deal with the main body of elements
635
                while nbytes_copied < sizeof(T) && (sizeof(T) - nbytes_copied) > sizeof(S)
6✔
636
                    nb = min(sizeof(S), sizeof(T) - nbytes_copied)
2✔
637
                    memcpy(sptr, tptr + nbytes_copied, nb)
2✔
638
                    nbytes_copied += nb
2✔
639
                    a.parent[ind_start + i, tailinds...] = s[]
2✔
640
                    i += 1
2✔
641
                end
2✔
642
                # Deal with trailing partial elements
643
                if nbytes_copied < sizeof(T)
4✔
644
                    s[] = a.parent[ind_start + i, tailinds...]
6✔
645
                    nb = min(sizeof(S), sizeof(T) - nbytes_copied)
4✔
646
                    memcpy(sptr, tptr + nbytes_copied, nb)
4✔
647
                    a.parent[ind_start + i, tailinds...] = s[]
4✔
648
                end
649
            end
650
        end
651
    end
652
    return a
173,526✔
653
end
654

655
@propagate_inbounds function _setindex_ra!(a::ReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT}
656
    v = convert(T, v)::T
50✔
657
    # Make sure to match the scalar reinterpret if that is applicable
658
    if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0
49✔
659
        if issingletontype(T) # singleton types
12✔
660
            @boundscheck checkbounds(a, i1, tailinds...)
3✔
661
            # setindex! is a noop except for the index check
662
        else
663
            setindex!(a.parent, reinterpret(S, v), i1, tailinds...)
10✔
664
        end
665
    end
666
    @boundscheck checkbounds(a, i1, tailinds...)
48✔
667
    if sizeof(T) >= sizeof(S)
48✔
668
        t = Ref{T}(v)
22✔
669
        GC.@preserve t begin
22✔
670
            sptr = Ptr{S}(unsafe_convert(Ref{T}, t))
22✔
671
            if sizeof(T) > sizeof(S)
22✔
672
                # Extra dimension in the parent array
673
                n = sizeof(T) ÷ sizeof(S)
6✔
674
                if isempty(tailinds) && IndexStyle(a.parent) === IndexLinear()
6✔
675
                    offset = n * (i1 - firstindex(a))
2✔
676
                    for i = 1:n
2✔
677
                        s = unsafe_load(sptr, i)
4✔
678
                        a.parent[i + offset] = s
4✔
679
                    end
6✔
680
                else
681
                    for i = 1:n
4✔
682
                        s = unsafe_load(sptr, i)
8✔
683
                        a.parent[i, i1, tailinds...] = s
8✔
684
                    end
12✔
685
                end
686
            else # sizeof(T) == sizeof(S)
687
                # No extra dimension
688
                s = unsafe_load(sptr)
16✔
689
                a.parent[i1, tailinds...] = s
22✔
690
            end
691
        end
692
    else
693
        # S is bigger than T and contains an integer number of them
694
        s = Ref{S}()
26✔
695
        GC.@preserve s begin
26✔
696
            tptr = Ptr{T}(unsafe_convert(Ref{S}, s))
26✔
697
            s[] = a.parent[tailinds...]
34✔
698
            unsafe_store!(tptr, v, i1)
26✔
699
            a.parent[tailinds...] = s[]
26✔
700
        end
701
    end
702
    return a
48✔
703
end
704

705
# Padding
706
struct Padding
707
    offset::Int # 0-indexed offset of the next valid byte; sizeof(T) indicates trailing padding
139✔
708
    size::Int   # bytes of padding before a valid byte
709
end
710
function intersect(p1::Padding, p2::Padding)
34✔
711
    start = max(p1.offset, p2.offset)
34✔
712
    stop = min(p1.offset + p1.size, p2.offset + p2.size)
34✔
713
    Padding(start, max(0, stop-start))
34✔
714
end
715

716
struct PaddingError <: Exception
717
    S::Type
4✔
718
    T::Type
719
end
720

721
function showerror(io::IO, p::PaddingError)
×
UNCOV
722
    print(io, "Padding of type $(p.S) is not compatible with type $(p.T).")
×
723
end
724

725
"""
726
    CyclePadding(padding, total_size)
727

728
Cycles an iterator of `Padding` structs, restarting the padding at `total_size`.
729
E.g. if `padding` is all the padding in a struct and `total_size` is the total
730
aligned size of that array, `CyclePadding` will correspond to the padding in an
731
infinite vector of such structs.
732
"""
733
struct CyclePadding{P}
734
    padding::P
131✔
735
    total_size::Int
736
end
737
eltype(::Type{<:CyclePadding}) = Padding
×
738
IteratorSize(::Type{<:CyclePadding}) = IsInfinite()
×
UNCOV
739
isempty(cp::CyclePadding) = isempty(cp.padding)
×
740
function iterate(cp::CyclePadding)
347✔
741
    y = iterate(cp.padding)
424✔
742
    y === nothing && return nothing
383✔
743
    y[1], (0, y[2])
41✔
744
end
745
function iterate(cp::CyclePadding, state::Tuple)
68✔
746
    y = iterate(cp.padding, tail(state)...)
102✔
747
    y === nothing && return iterate(cp, (state[1]+cp.total_size,))
68✔
748
    Padding(y[1].offset+state[1], y[1].size), (state[1], tail(y)...)
34✔
749
end
750

751
"""
752
    Compute the location of padding in an isbits datatype. Recursive over the fields of that type.
753
"""
754
@assume_effects :foldable function padding(T::DataType, baseoffset::Int = 0)
1,112✔
755
    pads = Padding[]
1,365✔
UNCOV
756
    last_end::Int = baseoffset
×
757
    for i = 1:fieldcount(T)
1,112✔
758
        offset = baseoffset + Int(fieldoffset(T, i))
738✔
759
        fT = fieldtype(T, i)
738✔
760
        append!(pads, padding(fT, offset))
738✔
761
        if offset != last_end
738✔
762
            push!(pads, Padding(offset, offset-last_end))
63✔
763
        end
764
        last_end = offset + sizeof(fT)
738✔
765
    end
1,242✔
766
    if 0 < last_end - baseoffset < sizeof(T)
1,112✔
767
        push!(pads, Padding(baseoffset + sizeof(T), sizeof(T) - last_end + baseoffset))
4✔
768
    end
769
    return Core.svec(pads...)
1,179✔
770
end
771

772
function CyclePadding(T::DataType)
36✔
773
    a, s = datatype_alignment(T), sizeof(T)
36✔
774
    as = s + (a - (s % a)) % a
36✔
775
    pad = padding(T)
36✔
776
    if s != as
36✔
UNCOV
777
        pad = Core.svec(pad..., Padding(s, as - s))
×
778
    end
779
    CyclePadding(pad, as)
36✔
780
end
781

782
@assume_effects :total function array_subpadding(S, T)
18✔
783
    lcm_size = lcm(sizeof(S), sizeof(T))
18✔
784
    s, t = CyclePadding(S), CyclePadding(T)
18✔
785
    checked_size = 0
18✔
786
    # use of Stateful harms inference and makes this vulnerable to invalidation
787
    (pad, tstate) = let
788
        it = iterate(t)
36✔
789
        it === nothing && return true
18✔
790
        it
18✔
791
    end
792
    (ps, sstate) = let
793
        it = iterate(s)
36✔
794
        it === nothing && return false
18✔
795
        it
18✔
796
    end
797
    while checked_size < lcm_size
34✔
798
        while true
26✔
799
            # See if there's corresponding padding in S
800
            ps.offset > pad.offset && return false
88✔
801
            intersect(ps, pad) == pad && break
34✔
802
            ps, sstate = iterate(s, sstate)
18✔
803
        end
18✔
804
        checked_size = pad.offset + pad.size
16✔
805
        pad, tstate = iterate(t, tstate)
16✔
806
    end
16✔
807
    return true
8✔
808
end
809

810
@assume_effects :foldable function struct_subpadding(::Type{Out}, ::Type{In}) where {Out, In}
74✔
811
    padding(Out) == padding(In)
74✔
812
end
813

814
@assume_effects :foldable function packedsize(::Type{T}) where T
2✔
815
    pads = padding(T)
2✔
816
    return sizeof(T) - sum((p.size for p ∈ pads), init = 0)
2✔
817
end
818

UNCOV
819
@assume_effects :foldable ispacked(::Type{T}) where T = isempty(padding(T))
×
820

821
function _copytopacked!(ptr_out::Ptr{Out}, ptr_in::Ptr{In}) where {Out, In}
3✔
822
    writeoffset = 0
4✔
823
    for i ∈ 1:fieldcount(In)
4✔
824
        readoffset = fieldoffset(In, i)
13✔
825
        fT = fieldtype(In, i)
13✔
826
        if ispacked(fT)
15✔
827
            readsize = sizeof(fT)
14✔
828
            memcpy(ptr_out + writeoffset, ptr_in + readoffset, readsize)
12✔
829
            writeoffset += readsize
12✔
830
        else # nested padded type
831
            _copytopacked!(ptr_out + writeoffset, Ptr{fT}(ptr_in + readoffset))
2✔
832
            writeoffset += packedsize(fT)
1✔
833
        end
834
    end
14✔
835
end
836

837
function _copyfrompacked!(ptr_out::Ptr{Out}, ptr_in::Ptr{In}) where {Out, In}
3✔
838
    readoffset = 0
4✔
839
    for i ∈ 1:fieldcount(Out)
4✔
840
        writeoffset = fieldoffset(Out, i)
13✔
841
        fT = fieldtype(Out, i)
13✔
842
        if ispacked(fT)
15✔
843
            writesize = sizeof(fT)
14✔
844
            memcpy(ptr_out + writeoffset, ptr_in + readoffset, writesize)
12✔
845
            readoffset += writesize
12✔
846
        else # nested padded type
847
            _copyfrompacked!(Ptr{fT}(ptr_out + writeoffset), ptr_in + readoffset)
2✔
848
            readoffset += packedsize(fT)
1✔
849
        end
850
    end
14✔
851
end
852

853
@inline function _reinterpret(::Type{Out}, x::In) where {Out, In}
854
    # handle non-primitive types
855
    isbitstype(Out) || throw(ArgumentError("Target type for `reinterpret` must be isbits"))
3,555✔
856
    isbitstype(In) || throw(ArgumentError("Source type for `reinterpret` must be isbits"))
3,554✔
857
    inpackedsize = packedsize(In)
3,553✔
858
    outpackedsize = packedsize(Out)
3,553✔
859
    inpackedsize == outpackedsize ||
3,553✔
860
        throw(ArgumentError(LazyString("Packed sizes of types ", Out, " and ", In,
861
            " do not match; got ", outpackedsize, " and ", inpackedsize, ", respectively.")))
862
    in = Ref{In}(x)
3,549✔
863
    out = Ref{Out}()
3,549✔
864
    if struct_subpadding(Out, In)
3,549✔
865
        # if packed the same, just copy
866
        GC.@preserve in out begin
3,546✔
867
            ptr_in = unsafe_convert(Ptr{In}, in)
3,546✔
868
            ptr_out = unsafe_convert(Ptr{Out}, out)
3,546✔
869
            memcpy(ptr_out, ptr_in, sizeof(Out))
3,546✔
870
        end
871
        return out[]
3,546✔
872
    else
873
        # mismatched padding
874
        GC.@preserve in out begin
3✔
875
            ptr_in = unsafe_convert(Ptr{In}, in)
3✔
876
            ptr_out = unsafe_convert(Ptr{Out}, out)
3✔
877

878
            if fieldcount(In) > 0 && ispacked(Out)
3✔
UNCOV
879
                _copytopacked!(ptr_out, ptr_in)
×
880
            elseif fieldcount(Out) > 0 && ispacked(In)
3✔
UNCOV
881
                _copyfrompacked!(ptr_out, ptr_in)
×
882
            else
883
                packed = Ref{NTuple{inpackedsize, UInt8}}()
3✔
884
                GC.@preserve packed begin
3✔
885
                    ptr_packed = unsafe_convert(Ptr{NTuple{inpackedsize, UInt8}}, packed)
3✔
886
                    _copytopacked!(ptr_packed, ptr_in)
3✔
887
                    _copyfrompacked!(ptr_out, ptr_packed)
3✔
888
                end
889
            end
890
        end
891
        return out[]
3✔
892
    end
893
end
894

895

896
# Reductions with IndexSCartesian2
897

898
function _mapreduce(f::F, op::OP, style::IndexSCartesian2{K}, A::AbstractArrayOrBroadcasted) where {F,OP,K}
899
    inds = eachindex(style, A)
4✔
900
    n = size(inds)[2]
4✔
901
    if n == 0
4✔
UNCOV
902
        return mapreduce_empty_iter(f, op, A, IteratorEltype(A))
×
903
    else
904
        return mapreduce_impl(f, op, A, first(inds), last(inds))
4✔
905
    end
906
end
907

908
@noinline function mapreduce_impl(f::F, op::OP, A::AbstractArrayOrBroadcasted,
8✔
909
                                  ifirst::SCI, ilast::SCI, blksize::Int) where {F,OP,SCI<:SCartesianIndex2{K}} where K
910
    if ilast.j - ifirst.j < blksize
8✔
911
        # sequential portion
912
        @inbounds a1 = A[ifirst]
6✔
913
        @inbounds a2 = A[SCI(2,ifirst.j)]
6✔
914
        v = op(f(a1), f(a2))
6✔
915
        @simd for i = ifirst.i + 2 : K
6✔
916
            @inbounds ai = A[SCI(i,ifirst.j)]
6✔
917
            v = op(v, f(ai))
6✔
918
        end
919
        # Remaining columns
920
        for j = ifirst.j+1 : ilast.j
6✔
921
            @simd for i = 1:K
2,666✔
922
                @inbounds ai = A[SCI(i,j)]
7,998✔
923
                v = op(v, f(ai))
7,998✔
924
            end
925
        end
5,326✔
926
        return v
6✔
927
    else
928
        # pairwise portion
929
        jmid = ifirst.j + (ilast.j - ifirst.j) >> 1
2✔
930
        v1 = mapreduce_impl(f, op, A, ifirst, SCI(K,jmid), blksize)
2✔
931
        v2 = mapreduce_impl(f, op, A, SCI(1,jmid+1), ilast, blksize)
2✔
932
        return op(v1, v2)
2✔
933
    end
934
end
935

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