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

JuliaLang / julia / #37745

11 Apr 2024 03:54AM UTC coverage: 87.241% (+5.8%) from 81.402%
#37745

push

local

web-flow
Fix comparison base for line table compression (#54032)

I'm not entirely sure what the original intent of this statement was,
but the effect ends up being that some codeloc entries end up negative
in the compressed representation, but the code always assumes unsigned
integers, so things roundtripped badly, leading to badly corrupted stack
traces. I guess this might have been a rebase mistake,
since the same line exists (correctly) a few lines prior. Fixes #54031.

75950 of 87058 relevant lines covered (87.24%)

15852930.37 hits per line

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

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

3
abstract type AbstractCartesianIndex{N} end # This is a hacky forward declaration for CartesianIndex
4
const ViewIndex = Union{Real, AbstractArray}
5
const ScalarIndex = Real
6

7
"""
8
    SubArray{T,N,P,I,L} <: AbstractArray{T,N}
9

10
`N`-dimensional view into a parent array (of type `P`) with an element type `T`, restricted by a tuple of indices (of type `I`). `L` is true for types that support fast linear indexing, and `false` otherwise.
11

12
Construct `SubArray`s using the [`view`](@ref) function.
13
"""
14
struct SubArray{T,N,P,I,L} <: AbstractArray{T,N}
15
    parent::P
16
    indices::I
17
    offset1::Int       # for linear indexing and pointer, only valid when L==true
18
    stride1::Int       # used only for linear indexing
19
    function SubArray{T,N,P,I,L}(parent, indices, offset1, stride1) where {T,N,P,I,L}
20
        @inline
30,443,146✔
21
        check_parent_index_match(parent, indices)
30,443,146✔
22
        new(parent, indices, offset1, stride1)
52,006,665✔
23
    end
24
end
25
# Compute the linear indexability of the indices, and combine it with the linear indexing of the parent
26
function SubArray(parent::AbstractArray, indices::Tuple)
2✔
27
    @inline
30,442,591✔
28
    SubArray(IndexStyle(viewindexing(indices), IndexStyle(parent)), parent, ensure_indexable(indices), index_dimsum(indices...))
52,006,124✔
29
end
30
function SubArray(::IndexCartesian, parent::P, indices::I, ::NTuple{N,Any}) where {P,I,N}
31
    @inline
444,946✔
32
    SubArray{eltype(P), N, P, I, false}(parent, indices, 0, 0)
444,961✔
33
end
34
function SubArray(::IndexLinear, parent::P, indices::I, ::NTuple{N,Any}) where {P,I,N}
36✔
35
    @inline
29,997,645✔
36
    # Compute the stride and offset
37
    stride1 = compute_stride1(parent, indices)
30,002,588✔
38
    SubArray{eltype(P), N, P, I, true}(parent, indices, compute_offset1(parent, stride1, indices), stride1)
51,561,163✔
39
end
40

41
check_parent_index_match(parent, indices) = check_parent_index_match(parent, index_ndims(indices...))
30,443,138✔
42
check_parent_index_match(parent::AbstractArray{T,N}, ::NTuple{N, Bool}) where {T,N} = nothing
×
43
check_parent_index_match(parent, ::NTuple{N, Bool}) where {N} =
×
44
    throw(ArgumentError("number of indices ($N) must match the parent dimensionality ($(ndims(parent)))"))
45

46
# This computes the linear indexing compatibility for a given tuple of indices
47
viewindexing(I::Tuple{}) = IndexLinear()
138✔
48
# Leading scalar indices simply increase the stride
49
viewindexing(I::Tuple{ScalarIndex, Vararg{Any}}) = (@inline; viewindexing(tail(I)))
265,124✔
50
# Slices may begin a section which may be followed by any number of Slices
51
viewindexing(I::Tuple{Slice, Slice, Vararg{Any}}) = (@inline; viewindexing(tail(I)))
2,135✔
52
# A UnitRange can follow Slices, but only if all other indices are scalar
53
viewindexing(I::Tuple{Slice, AbstractUnitRange, Vararg{ScalarIndex}}) = IndexLinear()
28,484✔
54
viewindexing(I::Tuple{Slice, Slice, Vararg{ScalarIndex}}) = IndexLinear() # disambiguate
1,463✔
55
# In general, scalar ranges are only fast if all other indices are scalar
56
# Other ranges, such as those of `CartesianIndex`es, are not fast even if these
57
# are followed by `ScalarIndex`es
58
viewindexing(I::Tuple{AbstractRange{<:ScalarIndex}, Vararg{ScalarIndex}}) = IndexLinear()
720,954✔
59
# All other index combinations are slow
60
viewindexing(I::Tuple{Vararg{Any}}) = IndexCartesian()
×
61
# Of course, all other array types are slow
62
viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = IndexCartesian()
228,173✔
63

64
# Simple utilities
65
size(V::SubArray) = (@inline; map(length, axes(V)))
35,249,745✔
66

67
similar(V::SubArray, T::Type, dims::Dims) = similar(V.parent, T, dims)
24,948✔
68

69
sizeof(V::SubArray) = length(V) * sizeof(eltype(V))
5✔
70
sizeof(V::SubArray{<:Any,<:Any,<:Array}) = length(V) * elsize(V.parent)
59,801✔
71

72
function Base.copy(V::SubArray)
472✔
73
    v = V.parent[V.indices...]
2,199✔
74
    ndims(V) == 0 || return v
1,102✔
75
    x = similar(V) # ensure proper type of x
1✔
76
    x[] = v
1✔
77
    return x
1✔
78
end
79

80
parent(V::SubArray) = V.parent
18,814,181✔
81
parentindices(V::SubArray) = V.indices
117,411✔
82

83
"""
84
    parentindices(A)
85

86
Return the indices in the [`parent`](@ref) which correspond to the view `A`.
87

88
# Examples
89
```jldoctest
90
julia> A = [1 2; 3 4];
91

92
julia> V = view(A, 1, :)
93
2-element view(::Matrix{Int64}, 1, :) with eltype Int64:
94
 1
95
 2
96

97
julia> parentindices(V)
98
(1, Base.Slice(Base.OneTo(2)))
99
```
100
"""
101
function parentindices end
102

103
parentindices(a::AbstractArray) = map(oneto, size(a))
1✔
104

105
## Aliasing detection
106
dataids(A::SubArray) = (dataids(A.parent)..., _splatmap(dataids, A.indices)...)
3,317,024✔
107
_splatmap(f, ::Tuple{}) = ()
2,697,383✔
108
_splatmap(f, t::Tuple) = (f(t[1])..., _splatmap(f, tail(t))...)
2,746,119✔
109
unaliascopy(A::SubArray) = typeof(A)(unaliascopy(A.parent), map(unaliascopy, A.indices), A.offset1, A.stride1)
4✔
110

111
# When the parent is an Array we can trim the size down a bit. In the future this
112
# could possibly be extended to any mutable array.
113
function unaliascopy(V::SubArray{T,N,A,I,LD}) where {T,N,A<:Array,I<:Tuple{Vararg{Union{ScalarIndex,AbstractRange{<:ScalarIndex},Array{<:Union{ScalarIndex,AbstractCartesianIndex}}}}},LD}
6✔
114
    dest = Array{T}(undef, _trimmedshape(V.indices...))
1,106✔
115
    trimmedpind = _trimmedpind(V.indices...)
553✔
116
    vdest = trimmedpind isa Tuple{Vararg{Union{Slice,Colon}}} ? dest : view(dest, trimmedpind...)
553✔
117
    copyto!(vdest, view(V, _trimmedvind(V.indices...)...))
557✔
118
    SubArray{T,N,A,I,LD}(dest, map(_trimmedindex, V.indices), 0, Int(LD))
553✔
119
end
120
# Get the proper trimmed shape
121
_trimmedshape(::ScalarIndex, rest...) = (1, _trimmedshape(rest...)...)
540✔
122
_trimmedshape(i::AbstractRange, rest...) = (maximum(i), _trimmedshape(rest...)...)
10✔
123
_trimmedshape(i::Union{UnitRange,StepRange,OneTo}, rest...) = (length(i), _trimmedshape(rest...)...)
550✔
124
_trimmedshape(i::AbstractArray{<:ScalarIndex}, rest...) = (length(i), _trimmedshape(rest...)...)
×
125
_trimmedshape(i::AbstractArray{<:AbstractCartesianIndex{0}}, rest...) = _trimmedshape(rest...)
2✔
126
_trimmedshape(i::AbstractArray{<:AbstractCartesianIndex{N}}, rest...) where {N} = (length(i), ntuple(Returns(1), Val(N - 1))..., _trimmedshape(rest...)...)
4✔
127
_trimmedshape() = ()
553✔
128
# We can avoid the repeation from `AbstractArray{CartesianIndex{0}}`
129
_trimmedpind(i, rest...) = (map(Returns(:), axes(i))..., _trimmedpind(rest...)...)
544✔
130
_trimmedpind(i::AbstractRange, rest...) = (i, _trimmedpind(rest...)...)
10✔
131
_trimmedpind(i::Union{UnitRange,StepRange,OneTo}, rest...) = ((:), _trimmedpind(rest...)...)
550✔
132
_trimmedpind(i::AbstractArray{<:AbstractCartesianIndex{0}}, rest...) = _trimmedpind(rest...)
2✔
133
_trimmedpind() = ()
553✔
134
_trimmedvind(i, rest...) = (map(Returns(:), axes(i))..., _trimmedvind(rest...)...)
1,104✔
135
_trimmedvind(i::AbstractArray{<:AbstractCartesianIndex{0}}, rest...) = (map(first, axes(i))..., _trimmedvind(rest...)...)
2✔
136
_trimmedvind() = ()
553✔
137
# Transform indices to be "dense"
138
_trimmedindex(i::ScalarIndex) = oftype(i, 1)
540✔
139
_trimmedindex(i::AbstractRange) = i
10✔
140
_trimmedindex(i::Union{UnitRange,StepRange,OneTo}) = oftype(i, oneto(length(i)))
552✔
141
_trimmedindex(i::AbstractArray{<:ScalarIndex}) = oftype(i, reshape(eachindex(IndexLinear(), i), axes(i)))
×
142
_trimmedindex(i::AbstractArray{<:AbstractCartesianIndex{0}}) = oftype(i, copy(i))
2✔
143
function _trimmedindex(i::AbstractArray{<:AbstractCartesianIndex{N}}) where {N}
2✔
144
    padding = ntuple(Returns(1), Val(N - 1))
4✔
145
    ax1 = eachindex(IndexLinear(), i)
4✔
146
    return oftype(i, reshape(CartesianIndices((ax1, padding...)), axes(i)))
6✔
147
end
148
## SubArray creation
149
# We always assume that the dimensionality of the parent matches the number of
150
# indices that end up getting passed to it, so we store the parent as a
151
# ReshapedArray view if necessary. The trouble is that arrays of `CartesianIndex`
152
# can make the number of effective indices not equal to length(I).
153
_maybe_reshape_parent(A::AbstractArray, ::NTuple{1, Bool}) = reshape(A, Val(1))
22,276✔
154
_maybe_reshape_parent(A::AbstractArray{<:Any,1}, ::NTuple{1, Bool}) = reshape(A, Val(1))
573,731✔
155
_maybe_reshape_parent(A::AbstractArray{<:Any,N}, ::NTuple{N, Bool}) where {N} = A
969,527✔
156
_maybe_reshape_parent(A::AbstractArray, ::NTuple{N, Bool}) where {N} = reshape(A, Val(N))
450✔
157
# The trailing singleton indices could be eliminated after bounds checking.
158
rm_singleton_indices(ndims::Tuple, J1, Js...) = (J1, rm_singleton_indices(IteratorsMD._splitrest(ndims, index_ndims(J1)), Js...)...)
1,920,555✔
159
rm_singleton_indices(::Tuple{}, ::ScalarIndex, Js...) = rm_singleton_indices((), Js...)
9,502✔
160
rm_singleton_indices(::Tuple) = ()
22,276✔
161

162
"""
163
    view(A, inds...)
164

165
Like [`getindex`](@ref), but returns a lightweight array that lazily references
166
(or is effectively a _view_ into) the parent array `A` at the given index or indices
167
`inds` instead of eagerly extracting elements or constructing a copied subset.
168
Calling [`getindex`](@ref) or [`setindex!`](@ref) on the returned value
169
(often a [`SubArray`](@ref)) computes the indices to access or modify the
170
parent array on the fly.  The behavior is undefined if the shape of the parent array is
171
changed after `view` is called because there is no bound check for the parent array; e.g.,
172
it may cause a segmentation fault.
173

174
Some immutable parent arrays (like ranges) may choose to simply
175
recompute a new array in some circumstances instead of returning
176
a `SubArray` if doing so is efficient and provides compatible semantics.
177

178
!!! compat "Julia 1.6"
179
    In Julia 1.6 or later, `view` can be called on an `AbstractString`, returning a
180
    `SubString`.
181

182
# Examples
183
```jldoctest
184
julia> A = [1 2; 3 4]
185
2×2 Matrix{Int64}:
186
 1  2
187
 3  4
188

189
julia> b = view(A, :, 1)
190
2-element view(::Matrix{Int64}, :, 1) with eltype Int64:
191
 1
192
 3
193

194
julia> fill!(b, 0)
195
2-element view(::Matrix{Int64}, :, 1) with eltype Int64:
196
 0
197
 0
198

199
julia> A # Note A has changed even though we modified b
200
2×2 Matrix{Int64}:
201
 0  2
202
 0  4
203

204
julia> view(2:5, 2:3) # returns a range as type is immutable
205
3:4
206
```
207
"""
208
function view(A::AbstractArray, I::Vararg{Any,M}) where {M}
8,833✔
209
    @inline
31,275,749✔
210
    J = map(i->unalias(A,i), to_indices(A, I))
62,820,193✔
211
    @boundscheck checkbounds(A, J...)
52,007,054✔
212
    J′ = rm_singleton_indices(ntuple(Returns(true), Val(ndims(A))), J...)
31,275,724✔
213
    unsafe_view(_maybe_reshape_parent(A, index_ndims(J′...)), J′...)
52,006,279✔
214
end
215

216
# Ranges implement getindex to return recomputed ranges; use that for views, too (when possible)
217
function view(r1::AbstractUnitRange, r2::AbstractUnitRange{<:Integer})
10✔
218
    @_propagate_inbounds_meta
153,834✔
219
    getindex(r1, r2)
153,962✔
220
end
221
function view(r1::AbstractUnitRange, r2::StepRange{<:Integer})
3✔
222
    @_propagate_inbounds_meta
4✔
223
    getindex(r1, r2)
5✔
224
end
225
function view(r1::StepRange, r2::AbstractRange{<:Integer})
1✔
226
    @_propagate_inbounds_meta
110✔
227
    getindex(r1, r2)
216✔
228
end
229
function view(r1::StepRangeLen, r2::OrdinalRange{<:Integer})
230
    @_propagate_inbounds_meta
1✔
231
    getindex(r1, r2)
1✔
232
end
233
function view(r1::LinRange, r2::OrdinalRange{<:Integer})
×
234
    @_propagate_inbounds_meta
×
235
    getindex(r1, r2)
×
236
end
237

238
# getindex(r::AbstractRange, ::Colon) returns a copy of the range, and we may do the same for a view
239
function view(r1::AbstractRange, c::Colon)
3✔
240
    @_propagate_inbounds_meta
3✔
241
    getindex(r1, c)
3✔
242
end
243

244
function unsafe_view(A::AbstractArray, I::Vararg{ViewIndex,N}) where {N}
245
    @inline
30,426,056✔
246
    SubArray(A, I)
51,851,169✔
247
end
248
# When we take the view of a view, it's often possible to "reindex" the parent
249
# view's indices such that we can "pop" the parent view and keep just one layer
250
# of indirection. But we can't always do this because arrays of `CartesianIndex`
251
# might span multiple parent indices, making the reindex calculation very hard.
252
# So we use _maybe_reindex to figure out if there are any arrays of
253
# `CartesianIndex`, and if so, we punt and keep two layers of indirection.
254
unsafe_view(V::SubArray, I::Vararg{ViewIndex,N}) where {N} =
255
    (@inline; _maybe_reindex(V, I))
155,043✔
256
_maybe_reindex(V, I) = (@inline; _maybe_reindex(V, I, I))
155,043✔
257
_maybe_reindex(V, I, ::Tuple{AbstractArray{<:AbstractCartesianIndex}, Vararg{Any}}) =
258
    (@inline; SubArray(V, I))
18✔
259
# But allow arrays of CartesianIndex{1}; they behave just like arrays of Ints
260
_maybe_reindex(V, I, A::Tuple{AbstractArray{<:AbstractCartesianIndex{1}}, Vararg{Any}}) =
×
261
    (@inline; _maybe_reindex(V, I, tail(A)))
×
262
_maybe_reindex(V, I, A::Tuple{Any, Vararg{Any}}) = (@inline; _maybe_reindex(V, I, tail(A)))
168,231✔
263
function _maybe_reindex(V, I, ::Tuple{})
264
    @inline
154,934✔
265
    @inbounds idxs = to_indices(V.parent, reindex(V.indices, I))
155,110✔
266
    SubArray(V.parent, idxs)
154,934✔
267
end
268

269
## Re-indexing is the heart of a view, transforming A[i, j][x, y] to A[i[x], j[y]]
270
#
271
# Recursively look through the heads of the parent- and sub-indices, considering
272
# the following cases:
273
# * Parent index is array  -> re-index that with one or more sub-indices (one per dimension)
274
# * Parent index is Colon  -> just use the sub-index as provided
275
# * Parent index is scalar -> that dimension was dropped, so skip the sub-index and use the index as is
276

277
AbstractZeroDimArray{T} = AbstractArray{T, 0}
278

279
reindex(::Tuple{}, ::Tuple{}) = ()
28,606,441✔
280

281
# Skip dropped scalars, so simply peel them off the parent indices and continue
282
reindex(idxs::Tuple{ScalarIndex, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) =
283
    (@_propagate_inbounds_meta; (idxs[1], reindex(tail(idxs), subidxs)...))
2,780,104✔
284

285
# Slices simply pass their subindices straight through
286
reindex(idxs::Tuple{Slice, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) =
287
    (@_propagate_inbounds_meta; (subidxs[1], reindex(tail(idxs), tail(subidxs))...))
17,041,542✔
288

289
# Re-index into parent vectors with one subindex
290
reindex(idxs::Tuple{AbstractVector, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) =
291
    (@_propagate_inbounds_meta; (maybeview(idxs[1], subidxs[1]), reindex(tail(idxs), tail(subidxs))...))
36,709,490✔
292

293
# Parent matrices are re-indexed with two sub-indices
294
reindex(idxs::Tuple{AbstractMatrix, Vararg{Any}}, subidxs::Tuple{Any, Any, Vararg{Any}}) =
295
    (@_propagate_inbounds_meta; (maybeview(idxs[1], subidxs[1], subidxs[2]), reindex(tail(idxs), tail(tail(subidxs)))...))
853,871✔
296

297
# In general, we index N-dimensional parent arrays with N indices
298
@generated function reindex(idxs::Tuple{AbstractArray{T,N}, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) where {T,N}
141,424✔
299
    if length(subidxs.parameters) >= N
950✔
300
        subs = [:(subidxs[$d]) for d in 1:N]
473✔
301
        tail = [:(subidxs[$d]) for d in N+1:length(subidxs.parameters)]
946✔
302
        :(@_propagate_inbounds_meta; (maybeview(idxs[1], $(subs...)), reindex(tail(idxs), ($(tail...),))...))
71,185✔
303
    else
304
        :(throw(ArgumentError("cannot re-index SubArray with fewer indices than dimensions\nThis should not occur; please submit a bug report.")))
477✔
305
    end
306
end
307

308
# In general, we simply re-index the parent indices by the provided ones
309
SlowSubArray{T,N,P,I} = SubArray{T,N,P,I,false}
310
function getindex(V::SubArray{T,N}, I::Vararg{Int,N}) where {T,N}
14,530✔
311
    @inline
16,268,315✔
312
    @boundscheck checkbounds(V, I...)
16,268,410✔
313
    @inbounds r = V.parent[reindex(V.indices, I)...]
17,214,669✔
314
    r
16,268,220✔
315
end
316

317
# But SubArrays with fast linear indexing pre-compute a stride and offset
318
FastSubArray{T,N,P,I} = SubArray{T,N,P,I,true}
319
# We define a convenience functions to compute the shifted parent index
320
# This differs from reindex as this accepts the view directly, instead of its indices
321
@inline _reindexlinear(V::FastSubArray, i::Int) = V.offset1 + V.stride1*i
21,857,245✔
322
@inline _reindexlinear(V::FastSubArray, i::AbstractUnitRange{Int}) = V.offset1 .+ V.stride1 .* i
54✔
323

324
function getindex(V::FastSubArray, i::Int)
8,746✔
325
    @inline
663,424✔
326
    @boundscheck checkbounds(V, i)
663,428✔
327
    @inbounds r = V.parent[_reindexlinear(V, i)]
663,420✔
328
    r
663,420✔
329
end
330

331
# For vector views with linear indexing, we disambiguate to favor the stride/offset
332
# computation as that'll generally be faster than (or just as fast as) re-indexing into a range.
333
function getindex(V::FastSubArray{<:Any, 1}, i::Int)
4,075✔
334
    @inline
311,326,759✔
335
    @boundscheck checkbounds(V, i)
383,651,488✔
336
    @inbounds r = V.parent[_reindexlinear(V, i)]
383,651,476✔
337
    r
311,326,753✔
338
end
339

340
# We can avoid a multiplication if the first parent index is a Colon or AbstractUnitRange,
341
# or if all the indices are scalars, i.e. the view is for a single value only
342
FastContiguousSubArray{T,N,P,I<:Union{Tuple{Union{Slice, AbstractUnitRange}, Vararg{Any}},
343
                                      Tuple{Vararg{ScalarIndex}}}} = SubArray{T,N,P,I,true}
344

345
@inline _reindexlinear(V::FastContiguousSubArray, i::Int) = V.offset1 + i
817,778,828✔
346
@inline _reindexlinear(V::FastContiguousSubArray, i::AbstractUnitRange{Int}) = V.offset1 .+ i
564✔
347

348
# parents of FastContiguousSubArrays may support fast indexing with AbstractUnitRanges,
349
# so we may just forward the indexing to the parent
350
# This may only be done for non-offset ranges, as the result would otherwise have offset axes
351
const OneBasedRanges = Union{OneTo{Int}, UnitRange{Int}, Slice{OneTo{Int}}, IdentityUnitRange{OneTo{Int}}}
352
function getindex(V::FastContiguousSubArray, i::OneBasedRanges)
539✔
353
    @inline
555✔
354
    @boundscheck checkbounds(V, i)
555✔
355
    @inbounds r = V.parent[_reindexlinear(V, i)]
1,148✔
356
    r
555✔
357
end
358

359
@inline getindex(V::FastContiguousSubArray, i::Colon) = getindex(V, to_indices(V, (:,))...)
14✔
360

361
# Indexed assignment follows the same pattern as `getindex` above
362
function setindex!(V::SubArray{T,N}, x, I::Vararg{Int,N}) where {T,N}
188,117✔
363
    @inline
9,618,490✔
364
    @boundscheck checkbounds(V, I...)
9,618,554✔
365
    @inbounds V.parent[reindex(V.indices, I)...] = x
9,618,528✔
366
    V
9,618,477✔
367
end
368
function setindex!(V::FastSubArray, x, i::Int)
369
    @inline
1,022,144✔
370
    @boundscheck checkbounds(V, i)
1,022,144✔
371
    @inbounds V.parent[_reindexlinear(V, i)] = x
1,022,144✔
372
    V
1,022,144✔
373
end
374
function setindex!(V::FastSubArray{<:Any, 1}, x, i::Int)
375
    @inline
423,076,446✔
376
    @boundscheck checkbounds(V, i)
423,464,892✔
377
    @inbounds V.parent[_reindexlinear(V, i)] = x
423,464,892✔
378
    V
423,076,446✔
379
end
380

381
function setindex!(V::FastSubArray, x, i::AbstractUnitRange{Int})
382
    @inline
63✔
383
    @boundscheck checkbounds(V, i)
63✔
384
    @inbounds V.parent[_reindexlinear(V, i)] = x
245✔
385
    V
63✔
386
end
387

388
@inline setindex!(V::FastSubArray, x, i::Colon) = setindex!(V, x, to_indices(V, (i,))...)
64✔
389

390
function isassigned(V::SubArray{T,N}, I::Vararg{Int,N}) where {T,N}
5,661✔
391
    @inline
2,564,784✔
392
    @boundscheck checkbounds(Bool, V, I...) || return false
2,564,789✔
393
    @inbounds r = isassigned(V.parent, reindex(V.indices, I)...)
2,565,849✔
394
    r
2,564,779✔
395
end
396
function isassigned(V::FastSubArray, i::Int)
397
    @inline
615,292✔
398
    @boundscheck checkbounds(Bool, V, i) || return false
615,292✔
399
    @inbounds r = isassigned(V.parent, _reindexlinear(V, i))
615,292✔
400
    r
615,292✔
401
end
402
function isassigned(V::FastSubArray{<:Any, 1}, i::Int)
8✔
403
    @inline
30,218,825✔
404
    @boundscheck checkbounds(Bool, V, i) || return false
30,218,829✔
405
    @inbounds r = isassigned(V.parent, _reindexlinear(V, i))
30,218,821✔
406
    r
30,218,821✔
407
end
408

409
function _unsetindex!(V::FastSubArray, i::Int)
410
    @inline
18✔
411
    @boundscheck checkbounds(V, i)
20✔
412
    @inbounds _unsetindex!(V.parent, _reindexlinear(V, i))
12✔
413
    return V
12✔
414
end
415
function _unsetindex!(V::FastSubArray{<:Any,1}, i::Int)
416
    @inline
24✔
417
    @boundscheck checkbounds(V, i)
28✔
418
    @inbounds _unsetindex!(V.parent, _reindexlinear(V, i))
20✔
419
    return V
20✔
420
end
421
function _unsetindex!(V::SubArray{T,N}, i::Vararg{Int,N}) where {T,N}
422
    @inline
10✔
423
    @boundscheck checkbounds(V, i...)
12✔
424
    @inbounds _unsetindex!(V.parent, reindex(V.indices, i)...)
8✔
425
    return V
8✔
426
end
427

428
IndexStyle(::Type{<:FastSubArray}) = IndexLinear()
6,345,500✔
429

430
# Strides are the distance in memory between adjacent elements in a given dimension
431
# which we determine from the strides of the parent
432
strides(V::SubArray) = substrides(strides(V.parent), V.indices)
132,120✔
433

434
substrides(strds::Tuple{}, ::Tuple{}) = ()
132,120✔
435
substrides(strds::NTuple{N,Int}, I::Tuple{ScalarIndex, Vararg{Any}}) where N = (substrides(tail(strds), tail(I))...,)
19,245✔
436
substrides(strds::NTuple{N,Int}, I::Tuple{Slice, Vararg{Any}}) where N = (first(strds), substrides(tail(strds), tail(I))...)
107,504✔
437
substrides(strds::NTuple{N,Int}, I::Tuple{AbstractRange, Vararg{Any}}) where N = (first(strds)*step(I[1]), substrides(tail(strds), tail(I))...)
216,526✔
438
substrides(strds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("strides is invalid for SubArrays with indices of type $(typeof(I[1]))"))
×
439

440
stride(V::SubArray, d::Integer) = d <= ndims(V) ? strides(V)[d] : strides(V)[end] * size(V)[end]
32,744✔
441

442
compute_stride1(parent::AbstractArray, I::NTuple{N,Any}) where {N} =
443
    (@inline; compute_stride1(1, fill_to_length(axes(parent), OneTo(1), Val(N)), I))
30,002,588✔
444
compute_stride1(s, inds, I::Tuple{}) = s
8✔
445
compute_stride1(s, inds, I::Tuple{Vararg{ScalarIndex}}) = s
112✔
446
compute_stride1(s, inds, I::Tuple{ScalarIndex, Vararg{Any}}) =
447
    (@inline; compute_stride1(s*length(inds[1]), tail(inds), tail(I)))
261,759✔
448
compute_stride1(s, inds, I::Tuple{AbstractRange, Vararg{Any}}) = s*step(I[1])
446,864✔
449
compute_stride1(s, inds, I::Tuple{Slice, Vararg{Any}}) = s
318,917✔
450
compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("invalid strided index type $(typeof(I[1]))"))
×
451

452
elsize(::Type{<:SubArray{<:Any,<:Any,P}}) where {P} = elsize(P)
18,160,549✔
453

454
iscontiguous(A::SubArray) = iscontiguous(typeof(A))
61✔
455
iscontiguous(::Type{<:SubArray}) = false
×
456
iscontiguous(::Type{<:FastContiguousSubArray}) = true
×
457

458
first_index(V::FastSubArray) = V.offset1 + V.stride1 * firstindex(V) # cached for fast linear SubArrays
17,571,904✔
459
first_index(V::SubArray) = compute_linindex(parent(V), V.indices)
67,822✔
460

461
# Computing the first index simply steps through the indices, accumulating the
462
# sum of index each multiplied by the parent's stride.
463
# The running sum is `f`; the cumulative stride product is `s`.
464
# If the parent is a vector, then we offset the parent's own indices with parameters of I
465
compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{AbstractRange}) =
466
    (@inline; first(I[1]) - stride1*first(axes1(I[1])))
49,424,967✔
467
# If the result is one-dimensional and it's a Colon, then linear
468
# indexing uses the indices along the given dimension.
469
# If the result is one-dimensional and it's a range, then linear
470
# indexing might be offset if the index itself is offset
471
# Otherwise linear indexing always matches the parent.
472
compute_offset1(parent, stride1::Integer, I::Tuple) =
473
    (@inline; compute_offset1(parent, stride1, find_extended_dims(1, I...), find_extended_inds(I...), I))
731,423✔
474
compute_offset1(parent, stride1::Integer, dims::Tuple{Int}, inds::Tuple{Slice}, I::Tuple) =
475
    (@inline; compute_linindex(parent, I) - stride1*first(axes(parent, dims[1])))  # index-preserving case
277,449✔
476
compute_offset1(parent, stride1::Integer, dims, inds::Tuple{AbstractRange}, I::Tuple) =
477
    (@inline; compute_linindex(parent, I) - stride1*first(axes1(inds[1]))) # potentially index-offsetting case
423,982✔
478
compute_offset1(parent, stride1::Integer, dims, inds, I::Tuple) =
479
    (@inline; compute_linindex(parent, I) - stride1)
29,992✔
480
function compute_linindex(parent, I::NTuple{N,Any}) where N
481
    @inline
799,245✔
482
    IP = fill_to_length(axes(parent), OneTo(1), Val(N))
799,245✔
483
    compute_linindex(first(LinearIndices(parent)), 1, IP, I)
799,245✔
484
end
485
function compute_linindex(f, s, IP::Tuple, I::Tuple{Any, Vararg{Any}})
486
    @inline
1,658,500✔
487
    Δi = first(I[1])-first(IP[1])
1,658,500✔
488
    compute_linindex(f + Δi*s, s*length(IP[1]), tail(IP), tail(I))
1,658,500✔
489
end
490
compute_linindex(f, s, IP::Tuple, I::Tuple{}) = f
799,245✔
491

492
find_extended_dims(dim, ::ScalarIndex, I...) = (@inline; find_extended_dims(dim + 1, I...))
706,909✔
493
find_extended_dims(dim, i1, I...) = (@inline; (dim, find_extended_dims(dim + 1, I...)...))
762,267✔
494
find_extended_dims(dim) = ()
731,423✔
495
find_extended_inds(::ScalarIndex, I...) = (@inline; find_extended_inds(I...))
706,909✔
496
find_extended_inds(i1, I...) = (@inline; (i1, find_extended_inds(I...)...))
762,267✔
497
find_extended_inds() = ()
731,423✔
498

499
pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i)
2,610✔
500
pointer(V::FastContiguousSubArray, i::Int) = pointer(V.parent, V.offset1 + i)
9,635✔
501

502
function pointer(V::SubArray{<:Any,<:Any,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::AbstractCartesianIndex{N}) where {N}
×
503
    index = first_index(V)
×
504
    strds = strides(V)
×
505
    for d = 1:N
×
506
        index += (is[d]-1)*strds[d]
×
507
    end
×
508
    return pointer(V.parent, index)
×
509
end
510

511
# indices are taken from the range/vector
512
# Since bounds-checking is performance-critical and uses
513
# indices, it's worth optimizing these implementations thoroughly
514
axes(S::SubArray) = (@inline; _indices_sub(S.indices...))
932,462,148✔
515
_indices_sub(::Real, I...) = (@inline; _indices_sub(I...))
17,612,523✔
516
_indices_sub() = ()
×
517
function _indices_sub(i1::AbstractArray, I...)
98✔
518
    @inline
67,205,381✔
519
    (axes(i1)..., _indices_sub(I...)...)
935,938,862✔
520
end
521

522
has_offset_axes(S::SubArray) = has_offset_axes(S.indices...)
751,751✔
523

524
function replace_in_print_matrix(S::SubArray{<:Any,2,<:AbstractMatrix}, i::Integer, j::Integer, s::AbstractString)
7✔
525
    replace_in_print_matrix(S.parent, to_indices(S.parent, reindex(S.indices, (i,j)))..., s)
7✔
526
end
527
function replace_in_print_matrix(S::SubArray{<:Any,1,<:AbstractVector}, i::Integer, j::Integer, s::AbstractString)
16✔
528
    replace_in_print_matrix(S.parent, to_indices(S.parent, reindex(S.indices, (i,)))..., j, s)
16✔
529
end
530

531
# XXX: this is considerably more unsafe than the other similarly named methods
532
unsafe_wrap(::Type{Vector{UInt8}}, s::FastContiguousSubArray{UInt8,1,Vector{UInt8}}) = unsafe_wrap(Vector{UInt8}, pointer(s), size(s))
×
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