Skip to content

Commit 1c16afd

Browse files
andyferrisAndy Ferrismbauman
authored
Make view of range with range be a range (#26872)
* Make view of range with range be a range Typical `AbstractRange` types are compact and immutable, and we can remove the overhead of `SubArray` in this case while the cost of `view` remains O(1). Here we specialize only on valid combinations of integer ranges, otherwise still relying on `SubArray`. * Cover all implementations of `getindex(::AbstractRange,...)` * Add NEWS Co-authored-by: Andy Ferris <andy.ferris@roames.com.au> Co-authored-by: Matt Bauman <mbauman@juliacomputing.com>
1 parent 5e1083c commit 1c16afd

File tree

3 files changed

+44
-0
lines changed

3 files changed

+44
-0
lines changed

NEWS.md

+2
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ Standard library changes
143143
* A 1-d `Zip` iterator (where `Base.IteratorSize` is `Base.HasShape{1}()`) with defined length of `n` has now also size of `(n,)` (instead of throwing an error with truncated iterators) ([#29927]).
144144
* The `@timed` macro now returns a `NamedTuple` ([#34149])
145145
* New `supertypes(T)` function returns a tuple of all supertypes of `T` ([#34419]).
146+
* Views of builtin ranges are now recomputed ranges (like indexing returns) instead of
147+
`SubArray`s ([#26872]).
146148
* Sorting-related functions such as `sort` that take the keyword arguments `lt`, `rev`, `order`
147149
and `by` now do not discard `order` if `by` or `lt` are passed. In the former case, the
148150
order from `order` is used to compare the values of `by(element)`. In the latter case,

base/subarray.jl

+26
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,32 @@ function view(A::AbstractArray, I::Vararg{Any,N}) where {N}
164164
unsafe_view(_maybe_reshape_parent(A, index_ndims(J...)), J...)
165165
end
166166

167+
# Ranges implement getindex to return recomputed ranges; use that for views, too (when possible)
168+
function view(r1::OneTo, r2::OneTo)
169+
@_propagate_inbounds_meta
170+
getindex(r1, r2)
171+
end
172+
function view(r1::AbstractUnitRange, r2::AbstractUnitRange{<:Integer})
173+
@_propagate_inbounds_meta
174+
getindex(r1, r2)
175+
end
176+
function view(r1::AbstractUnitRange, r2::StepRange{<:Integer})
177+
@_propagate_inbounds_meta
178+
getindex(r1, r2)
179+
end
180+
function view(r1::StepRange, r2::AbstractRange{<:Integer})
181+
@_propagate_inbounds_meta
182+
getindex(r1, r2)
183+
end
184+
function view(r1::StepRangeLen, r2::OrdinalRange{<:Integer})
185+
@_propagate_inbounds_meta
186+
getindex(r1, r2)
187+
end
188+
function view(r1::LinRange, r2::OrdinalRange{<:Integer})
189+
@_propagate_inbounds_meta
190+
getindex(r1, r2)
191+
end
192+
167193
function unsafe_view(A::AbstractArray, I::Vararg{ViewIndex,N}) where {N}
168194
@_inline_meta
169195
SubArray(A, I)

test/ranges.jl

+16
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,22 @@ end
14011401
@test x isa StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}
14021402
end
14031403

1404+
@testset "Views of ranges" begin
1405+
@test view(Base.OneTo(10), Base.OneTo(5)) === Base.OneTo(5)
1406+
@test view(1:10, 1:5) === 1:5
1407+
@test view(1:10, 1:2:5) === 1:2:5
1408+
@test view(1:2:9, 1:5) === 1:2:9
1409+
1410+
# Ensure we don't hit a fallback `view` if there's a better `getindex` implementation
1411+
vmt = collect(methods(view, Tuple{AbstractRange, AbstractRange}))
1412+
for m in methods(getindex, Tuple{AbstractRange, AbstractRange})
1413+
tt = Base.tuple_type_tail(m.sig)
1414+
tt == Tuple{AbstractArray,Vararg{Any,N}} where N && continue
1415+
vm = findfirst(sig->tt <: Base.tuple_type_tail(sig.sig), vmt)
1416+
@test vmt[vm].sig != Tuple{typeof(view),AbstractArray,Vararg{Any,N}} where N
1417+
end
1418+
end
1419+
14041420
@testset "Issue #26608" begin
14051421
@test_throws BoundsError (Int8(-100):Int8(100))[400]
14061422
@test_throws BoundsError (-100:100)[typemax(UInt)]

0 commit comments

Comments
 (0)