Line | Exclusive | Inclusive | Code |
---|---|---|---|
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 | ||
21 | check_parent_index_match(parent, indices) | ||
22 | new(parent, indices, offset1, stride1) | ||
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) | ||
27 | @inline | ||
28 | SubArray(IndexStyle(viewindexing(indices), IndexStyle(parent)), parent, ensure_indexable(indices), index_dimsum(indices...)) | ||
29 | end | ||
30 | function SubArray(::IndexCartesian, parent::P, indices::I, ::NTuple{N,Any}) where {P,I,N} | ||
31 | @inline | ||
32 | SubArray{eltype(P), N, P, I, false}(parent, indices, 0, 0) | ||
33 | end | ||
34 | function SubArray(::IndexLinear, parent::P, indices::I, ::NTuple{N,Any}) where {P,I,N} | ||
35 | @inline | ||
36 | # Compute the stride and offset | ||
37 | stride1 = compute_stride1(parent, indices) | ||
38 | SubArray{eltype(P), N, P, I, true}(parent, indices, compute_offset1(parent, stride1, indices), stride1) | ||
39 | end | ||
40 | |||
41 | check_parent_index_match(parent, indices) = check_parent_index_match(parent, index_ndims(indices...)) | ||
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() | ||
48 | # Leading scalar indices simply increase the stride | ||
49 | viewindexing(I::Tuple{ScalarIndex, Vararg{Any}}) = (@inline; viewindexing(tail(I))) | ||
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))) | ||
52 | # A UnitRange can follow Slices, but only if all other indices are scalar | ||
53 | viewindexing(I::Tuple{Slice, AbstractUnitRange, Vararg{ScalarIndex}}) = IndexLinear() | ||
54 | viewindexing(I::Tuple{Slice, Slice, Vararg{ScalarIndex}}) = IndexLinear() # disambiguate | ||
55 | # In general, ranges are only fast if all other indices are scalar | ||
56 | viewindexing(I::Tuple{AbstractRange, Vararg{ScalarIndex}}) = IndexLinear() | ||
57 | # All other index combinations are slow | ||
58 | viewindexing(I::Tuple{Vararg{Any}}) = IndexCartesian() | ||
59 | # Of course, all other array types are slow | ||
60 | viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = IndexCartesian() | ||
61 | |||
62 | # Simple utilities | ||
63 | size(V::SubArray) = (@inline; map(length, axes(V))) | ||
64 | |||
65 | similar(V::SubArray, T::Type, dims::Dims) = similar(V.parent, T, dims) | ||
66 | |||
67 | sizeof(V::SubArray) = length(V) * sizeof(eltype(V)) | ||
68 | sizeof(V::SubArray{<:Any,<:Any,<:Array}) = length(V) * elsize(V.parent) | ||
69 | |||
70 | function Base.copy(V::SubArray) | ||
71 | v = V.parent[V.indices...] | ||
72 | ndims(V) == 0 || return v | ||
73 | x = similar(V) # ensure proper type of x | ||
74 | x[] = v | ||
75 | return x | ||
76 | end | ||
77 | |||
78 | parent(V::SubArray) = V.parent | ||
79 | parentindices(V::SubArray) = V.indices | ||
80 | |||
81 | """ | ||
82 | parentindices(A) | ||
83 | |||
84 | Return the indices in the [`parent`](@ref) which correspond to the view `A`. | ||
85 | |||
86 | # Examples | ||
87 | ```jldoctest | ||
88 | julia> A = [1 2; 3 4]; | ||
89 | |||
90 | julia> V = view(A, 1, :) | ||
91 | 2-element view(::Matrix{Int64}, 1, :) with eltype Int64: | ||
92 | 1 | ||
93 | 2 | ||
94 | |||
95 | julia> parentindices(V) | ||
96 | (1, Base.Slice(Base.OneTo(2))) | ||
97 | ``` | ||
98 | """ | ||
99 | function parentindices end | ||
100 | |||
101 | parentindices(a::AbstractArray) = map(oneto, size(a)) | ||
102 | |||
103 | ## Aliasing detection | ||
104 | dataids(A::SubArray) = (dataids(A.parent)..., _splatmap(dataids, A.indices)...) | ||
105 | _splatmap(f, ::Tuple{}) = () | ||
106 | _splatmap(f, t::Tuple) = (f(t[1])..., _splatmap(f, tail(t))...) | ||
107 | unaliascopy(A::SubArray) = typeof(A)(unaliascopy(A.parent), map(unaliascopy, A.indices), A.offset1, A.stride1) | ||
108 | |||
109 | # When the parent is an Array we can trim the size down a bit. In the future this | ||
110 | # could possibly be extended to any mutable array. | ||
111 | function unaliascopy(V::SubArray{T,N,A,I,LD}) where {T,N,A<:Array,I<:Tuple{Vararg{Union{Real,AbstractRange,Array}}},LD} | ||
112 | dest = Array{T}(undef, index_lengths(V.indices...)) | ||
113 | copyto!(dest, V) | ||
114 | SubArray{T,N,A,I,LD}(dest, map(_trimmedindex, V.indices), 0, Int(LD)) | ||
115 | end | ||
116 | # Transform indices to be "dense" | ||
117 | _trimmedindex(i::Real) = oftype(i, 1) | ||
118 | _trimmedindex(i::AbstractUnitRange) = oftype(i, oneto(length(i))) | ||
119 | _trimmedindex(i::AbstractArray) = oftype(i, reshape(eachindex(IndexLinear(), i), axes(i))) | ||
120 | |||
121 | ## SubArray creation | ||
122 | # We always assume that the dimensionality of the parent matches the number of | ||
123 | # indices that end up getting passed to it, so we store the parent as a | ||
124 | # ReshapedArray view if necessary. The trouble is that arrays of `CartesianIndex` | ||
125 | # can make the number of effective indices not equal to length(I). | ||
126 | _maybe_reshape_parent(A::AbstractArray, ::NTuple{1, Bool}) = reshape(A, Val(1)) | ||
127 | _maybe_reshape_parent(A::AbstractArray{<:Any,1}, ::NTuple{1, Bool}) = reshape(A, Val(1)) | ||
128 | _maybe_reshape_parent(A::AbstractArray{<:Any,N}, ::NTuple{N, Bool}) where {N} = A | ||
129 | _maybe_reshape_parent(A::AbstractArray, ::NTuple{N, Bool}) where {N} = reshape(A, Val(N)) | ||
130 | """ | ||
131 | view(A, inds...) | ||
132 | |||
133 | Like [`getindex`](@ref), but returns a lightweight array that lazily references | ||
134 | (or is effectively a _view_ into) the parent array `A` at the given index or indices | ||
135 | `inds` instead of eagerly extracting elements or constructing a copied subset. | ||
136 | Calling [`getindex`](@ref) or [`setindex!`](@ref) on the returned value | ||
137 | (often a [`SubArray`](@ref)) computes the indices to access or modify the | ||
138 | parent array on the fly. The behavior is undefined if the shape of the parent array is | ||
139 | changed after `view` is called because there is no bound check for the parent array; e.g., | ||
140 | it may cause a segmentation fault. | ||
141 | |||
142 | Some immutable parent arrays (like ranges) may choose to simply | ||
143 | recompute a new array in some circumstances instead of returning | ||
144 | a `SubArray` if doing so is efficient and provides compatible semantics. | ||
145 | |||
146 | !!! compat "Julia 1.6" | ||
147 | In Julia 1.6 or later, `view` can be called on an `AbstractString`, returning a | ||
148 | `SubString`. | ||
149 | |||
150 | # Examples | ||
151 | ```jldoctest | ||
152 | julia> A = [1 2; 3 4] | ||
153 | 2×2 Matrix{Int64}: | ||
154 | 1 2 | ||
155 | 3 4 | ||
156 | |||
157 | julia> b = view(A, :, 1) | ||
158 | 2-element view(::Matrix{Int64}, :, 1) with eltype Int64: | ||
159 | 1 | ||
160 | 3 | ||
161 | |||
162 | julia> fill!(b, 0) | ||
163 | 2-element view(::Matrix{Int64}, :, 1) with eltype Int64: | ||
164 | 0 | ||
165 | 0 | ||
166 | |||
167 | julia> A # Note A has changed even though we modified b | ||
168 | 2×2 Matrix{Int64}: | ||
169 | 0 2 | ||
170 | 0 4 | ||
171 | |||
172 | julia> view(2:5, 2:3) # returns a range as type is immutable | ||
173 | 3:4 | ||
174 | ``` | ||
175 | """ | ||
176 | function view(A::AbstractArray{<:Any,N}, I::Vararg{Any,M}) where {N,M} | ||
177 | @inline | ||
178 | J = map(i->unalias(A,i), to_indices(A, I)) | ||
179 | @boundscheck checkbounds(A, J...) | ||
180 | if length(J) > ndims(A) && J[N+1:end] isa Tuple{Vararg{Int}} | ||
181 | # view([1,2,3], :, 1) does not need to reshape | ||
182 | return unsafe_view(A, J[1:N]...) | ||
183 | end | ||
184 | unsafe_view(_maybe_reshape_parent(A, index_ndims(J...)), J...) | ||
185 | end | ||
186 | |||
187 | # Ranges implement getindex to return recomputed ranges; use that for views, too (when possible) | ||
188 | function view(r1::OneTo, r2::OneTo) | ||
189 | @_propagate_inbounds_meta | ||
190 | getindex(r1, r2) | ||
191 | end | ||
192 | function view(r1::AbstractUnitRange, r2::AbstractUnitRange{<:Integer}) | ||
193 | @_propagate_inbounds_meta | ||
194 | getindex(r1, r2) | ||
195 | end | ||
196 | function view(r1::AbstractUnitRange, r2::StepRange{<:Integer}) | ||
197 | @_propagate_inbounds_meta | ||
198 | getindex(r1, r2) | ||
199 | end | ||
200 | function view(r1::StepRange, r2::AbstractRange{<:Integer}) | ||
201 | @_propagate_inbounds_meta | ||
202 | getindex(r1, r2) | ||
203 | end | ||
204 | function view(r1::StepRangeLen, r2::OrdinalRange{<:Integer}) | ||
205 | @_propagate_inbounds_meta | ||
206 | getindex(r1, r2) | ||
207 | end | ||
208 | function view(r1::LinRange, r2::OrdinalRange{<:Integer}) | ||
209 | @_propagate_inbounds_meta | ||
210 | getindex(r1, r2) | ||
211 | end | ||
212 | |||
213 | # getindex(r::AbstractRange, ::Colon) returns a copy of the range, and we may do the same for a view | ||
214 | function view(r1::AbstractRange, c::Colon) | ||
215 | @_propagate_inbounds_meta | ||
216 | getindex(r1, c) | ||
217 | end | ||
218 | |||
219 | function unsafe_view(A::AbstractArray, I::Vararg{ViewIndex,N}) where {N} | ||
220 | @inline | ||
221 | SubArray(A, I) | ||
222 | end | ||
223 | # When we take the view of a view, it's often possible to "reindex" the parent | ||
224 | # view's indices such that we can "pop" the parent view and keep just one layer | ||
225 | # of indirection. But we can't always do this because arrays of `CartesianIndex` | ||
226 | # might span multiple parent indices, making the reindex calculation very hard. | ||
227 | # So we use _maybe_reindex to figure out if there are any arrays of | ||
228 | # `CartesianIndex`, and if so, we punt and keep two layers of indirection. | ||
229 | unsafe_view(V::SubArray, I::Vararg{ViewIndex,N}) where {N} = | ||
230 | (@inline; _maybe_reindex(V, I)) | ||
231 | _maybe_reindex(V, I) = (@inline; _maybe_reindex(V, I, I)) | ||
232 | _maybe_reindex(V, I, ::Tuple{AbstractArray{<:AbstractCartesianIndex}, Vararg{Any}}) = | ||
233 | (@inline; SubArray(V, I)) | ||
234 | # But allow arrays of CartesianIndex{1}; they behave just like arrays of Ints | ||
235 | _maybe_reindex(V, I, A::Tuple{AbstractArray{<:AbstractCartesianIndex{1}}, Vararg{Any}}) = | ||
236 | (@inline; _maybe_reindex(V, I, tail(A))) | ||
237 | _maybe_reindex(V, I, A::Tuple{Any, Vararg{Any}}) = (@inline; _maybe_reindex(V, I, tail(A))) | ||
238 | function _maybe_reindex(V, I, ::Tuple{}) | ||
239 | @inline | ||
240 | @inbounds idxs = to_indices(V.parent, reindex(V.indices, I)) | ||
241 | SubArray(V.parent, idxs) | ||
242 | end | ||
243 | |||
244 | ## Re-indexing is the heart of a view, transforming A[i, j][x, y] to A[i[x], j[y]] | ||
245 | # | ||
246 | # Recursively look through the heads of the parent- and sub-indices, considering | ||
247 | # the following cases: | ||
248 | # * Parent index is array -> re-index that with one or more sub-indices (one per dimension) | ||
249 | # * Parent index is Colon -> just use the sub-index as provided | ||
250 | # * Parent index is scalar -> that dimension was dropped, so skip the sub-index and use the index as is | ||
251 | |||
252 | AbstractZeroDimArray{T} = AbstractArray{T, 0} | ||
253 | |||
254 | reindex(::Tuple{}, ::Tuple{}) = () | ||
255 | |||
256 | # Skip dropped scalars, so simply peel them off the parent indices and continue | ||
257 | reindex(idxs::Tuple{ScalarIndex, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) = | ||
258 | (@_propagate_inbounds_meta; (idxs[1], reindex(tail(idxs), subidxs)...)) | ||
259 | |||
260 | # Slices simply pass their subindices straight through | ||
261 | reindex(idxs::Tuple{Slice, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) = | ||
262 | (@_propagate_inbounds_meta; (subidxs[1], reindex(tail(idxs), tail(subidxs))...)) | ||
263 | |||
264 | # Re-index into parent vectors with one subindex | ||
265 | reindex(idxs::Tuple{AbstractVector, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) = | ||
266 | (@_propagate_inbounds_meta; (idxs[1][subidxs[1]], reindex(tail(idxs), tail(subidxs))...)) | ||
267 | |||
268 | # Parent matrices are re-indexed with two sub-indices | ||
269 | reindex(idxs::Tuple{AbstractMatrix, Vararg{Any}}, subidxs::Tuple{Any, Any, Vararg{Any}}) = | ||
270 | (@_propagate_inbounds_meta; (idxs[1][subidxs[1], subidxs[2]], reindex(tail(idxs), tail(tail(subidxs)))...)) | ||
271 | |||
272 | # In general, we index N-dimensional parent arrays with N indices | ||
273 | @generated function reindex(idxs::Tuple{AbstractArray{T,N}, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) where {T,N} | ||
274 | if length(subidxs.parameters) >= N | ||
275 | subs = [:(subidxs[$d]) for d in 1:N] | ||
276 | tail = [:(subidxs[$d]) for d in N+1:length(subidxs.parameters)] | ||
277 | :(@_propagate_inbounds_meta; (idxs[1][$(subs...)], reindex(tail(idxs), ($(tail...),))...)) | ||
278 | else | ||
279 | :(throw(ArgumentError("cannot re-index SubArray with fewer indices than dimensions\nThis should not occur; please submit a bug report."))) | ||
280 | end | ||
281 | end | ||
282 | |||
283 | # In general, we simply re-index the parent indices by the provided ones | ||
284 | SlowSubArray{T,N,P,I} = SubArray{T,N,P,I,false} | ||
285 | function getindex(V::SubArray{T,N}, I::Vararg{Int,N}) where {T,N} | ||
286 | @inline | ||
287 | @boundscheck checkbounds(V, I...) | ||
288 | @inbounds r = V.parent[reindex(V.indices, I)...] | ||
289 | r | ||
290 | end | ||
291 | |||
292 | # But SubArrays with fast linear indexing pre-compute a stride and offset | ||
293 | FastSubArray{T,N,P,I} = SubArray{T,N,P,I,true} | ||
294 | function getindex(V::FastSubArray, i::Int) | ||
295 | @inline | ||
296 | @boundscheck checkbounds(V, i) | ||
297 | @inbounds r = V.parent[V.offset1 + V.stride1*i] | ||
298 | r | ||
299 | end | ||
300 | # We can avoid a multiplication if the first parent index is a Colon or AbstractUnitRange, | ||
301 | # or if all the indices are scalars, i.e. the view is for a single value only | ||
302 | FastContiguousSubArray{T,N,P,I<:Union{Tuple{Union{Slice, AbstractUnitRange}, Vararg{Any}}, | ||
303 | Tuple{Vararg{ScalarIndex}}}} = SubArray{T,N,P,I,true} | ||
304 | function getindex(V::FastContiguousSubArray, i::Int) | ||
305 | @inline | ||
306 | @boundscheck checkbounds(V, i) | ||
307 | @inbounds r = V.parent[V.offset1 + i] | ||
308 | r | ||
309 | end | ||
310 | # For vector views with linear indexing, we disambiguate to favor the stride/offset | ||
311 | # computation as that'll generally be faster than (or just as fast as) re-indexing into a range. | ||
312 | function getindex(V::FastSubArray{<:Any, 1}, i::Int) | ||
313 | @inline | ||
314 | @boundscheck checkbounds(V, i) | ||
315 | @inbounds r = V.parent[V.offset1 + V.stride1*i] | ||
316 | r | ||
317 | end | ||
318 | function getindex(V::FastContiguousSubArray{<:Any, 1}, i::Int) | ||
319 | @inline | ||
320 | @boundscheck checkbounds(V, i) | ||
321 | @inbounds r = V.parent[V.offset1 + i] | ||
322 | r | ||
323 | end | ||
324 | |||
325 | # Indexed assignment follows the same pattern as `getindex` above | ||
326 | function setindex!(V::SubArray{T,N}, x, I::Vararg{Int,N}) where {T,N} | ||
327 | @inline | ||
328 | @boundscheck checkbounds(V, I...) | ||
329 | @inbounds V.parent[reindex(V.indices, I)...] = x | ||
330 | V | ||
331 | end | ||
332 | function setindex!(V::FastSubArray, x, i::Int) | ||
333 | @inline | ||
334 | @boundscheck checkbounds(V, i) | ||
335 | @inbounds V.parent[V.offset1 + V.stride1*i] = x | ||
336 | V | ||
337 | end | ||
338 | function setindex!(V::FastContiguousSubArray, x, i::Int) | ||
339 | @inline | ||
340 | @boundscheck checkbounds(V, i) | ||
341 | 24 (8 %) |
24 (100 %)
samples spent calling
setindex!
@inbounds V.parent[V.offset1 + i] = x
|
|
342 | V | ||
343 | end | ||
344 | function setindex!(V::FastSubArray{<:Any, 1}, x, i::Int) | ||
345 | @inline | ||
346 | @boundscheck checkbounds(V, i) | ||
347 | @inbounds V.parent[V.offset1 + V.stride1*i] = x | ||
348 | V | ||
349 | end | ||
350 | function setindex!(V::FastContiguousSubArray{<:Any, 1}, x, i::Int) | ||
351 | @inline | ||
352 | @boundscheck checkbounds(V, i) | ||
353 | @inbounds V.parent[V.offset1 + i] = x | ||
354 | V | ||
355 | end | ||
356 | |||
357 | function isassigned(V::SubArray{T,N}, I::Vararg{Int,N}) where {T,N} | ||
358 | @inline | ||
359 | @boundscheck checkbounds(Bool, V, I...) || return false | ||
360 | @inbounds r = isassigned(V.parent, reindex(V.indices, I)...) | ||
361 | r | ||
362 | end | ||
363 | function isassigned(V::FastSubArray, i::Int) | ||
364 | @inline | ||
365 | @boundscheck checkbounds(Bool, V, i) || return false | ||
366 | @inbounds r = isassigned(V.parent, V.offset1 + V.stride1*i) | ||
367 | r | ||
368 | end | ||
369 | function isassigned(V::FastContiguousSubArray, i::Int) | ||
370 | @inline | ||
371 | @boundscheck checkbounds(Bool, V, i) || return false | ||
372 | @inbounds r = isassigned(V.parent, V.offset1 + i) | ||
373 | r | ||
374 | end | ||
375 | function isassigned(V::FastSubArray{<:Any, 1}, i::Int) | ||
376 | @inline | ||
377 | @boundscheck checkbounds(Bool, V, i) || return false | ||
378 | @inbounds r = isassigned(V.parent, V.offset1 + V.stride1*i) | ||
379 | r | ||
380 | end | ||
381 | function isassigned(V::FastContiguousSubArray{<:Any, 1}, i::Int) | ||
382 | @inline | ||
383 | @boundscheck checkbounds(Bool, V, i) || return false | ||
384 | @inbounds r = isassigned(V.parent, V.offset1 + i) | ||
385 | r | ||
386 | end | ||
387 | |||
388 | IndexStyle(::Type{<:FastSubArray}) = IndexLinear() | ||
389 | IndexStyle(::Type{<:SubArray}) = IndexCartesian() | ||
390 | |||
391 | # Strides are the distance in memory between adjacent elements in a given dimension | ||
392 | # which we determine from the strides of the parent | ||
393 | strides(V::SubArray) = substrides(strides(V.parent), V.indices) | ||
394 | |||
395 | substrides(strds::Tuple{}, ::Tuple{}) = () | ||
396 | substrides(strds::NTuple{N,Int}, I::Tuple{ScalarIndex, Vararg{Any}}) where N = (substrides(tail(strds), tail(I))...,) | ||
397 | substrides(strds::NTuple{N,Int}, I::Tuple{Slice, Vararg{Any}}) where N = (first(strds), substrides(tail(strds), tail(I))...) | ||
398 | substrides(strds::NTuple{N,Int}, I::Tuple{AbstractRange, Vararg{Any}}) where N = (first(strds)*step(I[1]), substrides(tail(strds), tail(I))...) | ||
399 | substrides(strds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("strides is invalid for SubArrays with indices of type $(typeof(I[1]))")) | ||
400 | |||
401 | stride(V::SubArray, d::Integer) = d <= ndims(V) ? strides(V)[d] : strides(V)[end] * size(V)[end] | ||
402 | |||
403 | compute_stride1(parent::AbstractArray, I::NTuple{N,Any}) where {N} = | ||
404 | (@inline; compute_stride1(1, fill_to_length(axes(parent), OneTo(1), Val(N)), I)) | ||
405 | compute_stride1(s, inds, I::Tuple{}) = s | ||
406 | compute_stride1(s, inds, I::Tuple{Vararg{ScalarIndex}}) = s | ||
407 | compute_stride1(s, inds, I::Tuple{ScalarIndex, Vararg{Any}}) = | ||
408 | (@inline; compute_stride1(s*length(inds[1]), tail(inds), tail(I))) | ||
409 | compute_stride1(s, inds, I::Tuple{AbstractRange, Vararg{Any}}) = s*step(I[1]) | ||
410 | compute_stride1(s, inds, I::Tuple{Slice, Vararg{Any}}) = s | ||
411 | compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("invalid strided index type $(typeof(I[1]))")) | ||
412 | |||
413 | elsize(::Type{<:SubArray{<:Any,<:Any,P}}) where {P} = elsize(P) | ||
414 | |||
415 | iscontiguous(A::SubArray) = iscontiguous(typeof(A)) | ||
416 | iscontiguous(::Type{<:SubArray}) = false | ||
417 | iscontiguous(::Type{<:FastContiguousSubArray}) = true | ||
418 | |||
419 | first_index(V::FastSubArray) = V.offset1 + V.stride1 # cached for fast linear SubArrays | ||
420 | function first_index(V::SubArray) | ||
421 | P, I = parent(V), V.indices | ||
422 | s1 = compute_stride1(P, I) | ||
423 | s1 + compute_offset1(P, s1, I) | ||
424 | end | ||
425 | |||
426 | # Computing the first index simply steps through the indices, accumulating the | ||
427 | # sum of index each multiplied by the parent's stride. | ||
428 | # The running sum is `f`; the cumulative stride product is `s`. | ||
429 | # If the parent is a vector, then we offset the parent's own indices with parameters of I | ||
430 | compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{AbstractRange}) = | ||
431 | (@inline; first(I[1]) - stride1*first(axes1(I[1]))) | ||
432 | # If the result is one-dimensional and it's a Colon, then linear | ||
433 | # indexing uses the indices along the given dimension. | ||
434 | # If the result is one-dimensional and it's a range, then linear | ||
435 | # indexing might be offset if the index itself is offset | ||
436 | # Otherwise linear indexing always matches the parent. | ||
437 | compute_offset1(parent, stride1::Integer, I::Tuple) = | ||
438 | (@inline; compute_offset1(parent, stride1, find_extended_dims(1, I...), find_extended_inds(I...), I)) | ||
439 | compute_offset1(parent, stride1::Integer, dims::Tuple{Int}, inds::Tuple{Slice}, I::Tuple) = | ||
440 | (@inline; compute_linindex(parent, I) - stride1*first(axes(parent, dims[1]))) # index-preserving case | ||
441 | compute_offset1(parent, stride1::Integer, dims, inds::Tuple{AbstractRange}, I::Tuple) = | ||
442 | (@inline; compute_linindex(parent, I) - stride1*first(axes1(inds[1]))) # potentially index-offsetting case | ||
443 | compute_offset1(parent, stride1::Integer, dims, inds, I::Tuple) = | ||
444 | (@inline; compute_linindex(parent, I) - stride1) | ||
445 | function compute_linindex(parent, I::NTuple{N,Any}) where N | ||
446 | @inline | ||
447 | IP = fill_to_length(axes(parent), OneTo(1), Val(N)) | ||
448 | compute_linindex(first(LinearIndices(parent)), 1, IP, I) | ||
449 | end | ||
450 | function compute_linindex(f, s, IP::Tuple, I::Tuple{ScalarIndex, Vararg{Any}}) | ||
451 | @inline | ||
452 | Δi = I[1]-first(IP[1]) | ||
453 | compute_linindex(f + Δi*s, s*length(IP[1]), tail(IP), tail(I)) | ||
454 | end | ||
455 | function compute_linindex(f, s, IP::Tuple, I::Tuple{Any, Vararg{Any}}) | ||
456 | @inline | ||
457 | Δi = first(I[1])-first(IP[1]) | ||
458 | compute_linindex(f + Δi*s, s*length(IP[1]), tail(IP), tail(I)) | ||
459 | end | ||
460 | compute_linindex(f, s, IP::Tuple, I::Tuple{}) = f | ||
461 | |||
462 | find_extended_dims(dim, ::ScalarIndex, I...) = (@inline; find_extended_dims(dim + 1, I...)) | ||
463 | find_extended_dims(dim, i1, I...) = (@inline; (dim, find_extended_dims(dim + 1, I...)...)) | ||
464 | find_extended_dims(dim) = () | ||
465 | find_extended_inds(::ScalarIndex, I...) = (@inline; find_extended_inds(I...)) | ||
466 | find_extended_inds(i1, I...) = (@inline; (i1, find_extended_inds(I...)...)) | ||
467 | find_extended_inds() = () | ||
468 | |||
469 | function unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{RangeIndex}}}) where {T,N,P} | ||
470 | return unsafe_convert(Ptr{T}, V.parent) + _memory_offset(V.parent, map(first, V.indices)...) | ||
471 | end | ||
472 | |||
473 | pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i) | ||
474 | pointer(V::FastContiguousSubArray, i::Int) = pointer(V.parent, V.offset1 + i) | ||
475 | |||
476 | function pointer(V::SubArray{<:Any,<:Any,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::AbstractCartesianIndex{N}) where {N} | ||
477 | index = first_index(V) | ||
478 | strds = strides(V) | ||
479 | for d = 1:N | ||
480 | index += (is[d]-1)*strds[d] | ||
481 | end | ||
482 | return pointer(V.parent, index) | ||
483 | end | ||
484 | |||
485 | # indices are taken from the range/vector | ||
486 | # Since bounds-checking is performance-critical and uses | ||
487 | # indices, it's worth optimizing these implementations thoroughly | ||
488 | axes(S::SubArray) = (@inline; _indices_sub(S.indices...)) | ||
489 | _indices_sub(::Real, I...) = (@inline; _indices_sub(I...)) | ||
490 | _indices_sub() = () | ||
491 | function _indices_sub(i1::AbstractArray, I...) | ||
492 | @inline | ||
493 | (axes(i1)..., _indices_sub(I...)...) | ||
494 | end | ||
495 | |||
496 | has_offset_axes(S::SubArray) = has_offset_axes(S.indices...) |