StatProfilerHTML.jl report
Generated on Thu, 21 Dec 2023 13:06:16
File source code
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 26 (10 %)
26 (10 %) samples spent in setindex!
26 (100 %) (incl.) when called from _setindex! line 1416
26 (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...)