diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 64d58c2bc8a95..a1b45a4b65b91 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -384,6 +384,7 @@ false checkindex(::Type{Bool}, inds::AbstractUnitRange, i) = throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true +checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Slice) = true function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::Range) @_propagate_inbounds_meta isempty(r) | (checkindex(Bool, inds, first(r)) & checkindex(Bool, inds, last(r))) @@ -799,7 +800,6 @@ end pointer{T}(x::AbstractArray{T}) = unsafe_convert(Ptr{T}, x) pointer{T}(x::AbstractArray{T}, i::Integer) = (@_inline_meta; unsafe_convert(Ptr{T},x) + (i-first(linearindices(x)))*elsize(x)) - ## Approach: # We only define one fallback method on getindex for all argument types. # That dispatches to an (inlined) internal _getindex function, where the goal is @@ -812,61 +812,62 @@ pointer{T}(x::AbstractArray{T}, i::Integer) = (@_inline_meta; unsafe_convert(Ptr function getindex(A::AbstractArray, I...) @_propagate_inbounds_meta - _getindex(linearindexing(A), A, I...) + error_if_canonical_indexing(linearindexing(A), A, I...) + _getindex(linearindexing(A), A, to_indices(A, I)...) end function unsafe_getindex(A::AbstractArray, I...) @_inline_meta @inbounds r = getindex(A, I...) r end + +error_if_canonical_indexing(::LinearFast, A::AbstractArray, ::Int) = error("indexing not defined for ", typeof(A)) +error_if_canonical_indexing{T,N}(::LinearSlow, A::AbstractArray{T,N}, ::Vararg{Int, N}) = error("indexing not defined for ", typeof(A)) +error_if_canonical_indexing(::LinearIndexing, ::AbstractArray, ::Any...) = nothing + ## Internal definitions _getindex(::LinearIndexing, A::AbstractArray, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") ## LinearFast Scalar indexing: canonical method is one Int -_getindex(::LinearFast, A::AbstractVector, ::Int) = error("indexing not defined for ", typeof(A)) -_getindex(::LinearFast, A::AbstractArray, ::Int) = error("indexing not defined for ", typeof(A)) +_getindex(::LinearFast, A::AbstractVector, i::Int) = (@_propagate_inbounds_meta; getindex(A, i)) +_getindex(::LinearFast, A::AbstractArray, i::Int) = (@_propagate_inbounds_meta; getindex(A, i)) _getindex{T}(::LinearFast, A::AbstractArray{T,0}) = A[1] -_getindex(::LinearFast, A::AbstractArray, i::Real) = (@_propagate_inbounds_meta; getindex(A, to_index(i))) -function _getindex{T,N}(::LinearFast, A::AbstractArray{T,N}, I::Vararg{Real,N}) +function _getindex{T,N}(::LinearFast, A::AbstractArray{T,N}, I::Vararg{Int,N}) # We must check bounds for sub2ind; so we can then use @inbounds @_inline_meta - J = to_indexes(I...) - @boundscheck checkbounds(A, J...) - @inbounds r = getindex(A, sub2ind(A, J...)) + @boundscheck checkbounds(A, I...) + @inbounds r = getindex(A, sub2ind(A, I...)) r end -function _getindex(::LinearFast, A::AbstractVector, I1::Real, I::Real...) +function _getindex(::LinearFast, A::AbstractVector, I1::Int, I::Int...) @_inline_meta - J = to_indexes(I1, I...) - @boundscheck checkbounds(A, J...) - @inbounds r = getindex(A, J[1]) + @boundscheck checkbounds(A, I1, I...) + @inbounds r = getindex(A, I1) r end -function _getindex(::LinearFast, A::AbstractArray, I::Real...) # TODO: DEPRECATE FOR #14770 +function _getindex(::LinearFast, A::AbstractArray, I::Int...) # TODO: DEPRECATE FOR #14770 @_inline_meta - J = to_indexes(I...) - @boundscheck checkbounds(A, J...) - @inbounds r = getindex(A, sub2ind(A, J...)) + @boundscheck checkbounds(A, I...) + @inbounds r = getindex(A, sub2ind(A, I...)) r end ## LinearSlow Scalar indexing: Canonical method is full dimensionality of Ints -_getindex{T,N}(::LinearSlow, A::AbstractArray{T,N}, ::Vararg{Int, N}) = error("indexing not defined for ", typeof(A)) -_getindex{T,N}(::LinearSlow, A::AbstractArray{T,N}, I::Vararg{Real, N}) = (@_propagate_inbounds_meta; getindex(A, to_indexes(I...)...)) -function _getindex(::LinearSlow, A::AbstractArray, i::Real) +_getindex{T,N}(::LinearSlow, A::AbstractArray{T,N}, I::Vararg{Int, N}) = (@_propagate_inbounds_meta; getindex(A, I...)) +function _getindex(::LinearSlow, A::AbstractArray, i::Int) # ind2sub requires all dimensions to be > 0; may as well just check bounds @_inline_meta @boundscheck checkbounds(A, i) - @inbounds r = getindex(A, ind2sub(A, to_index(i))...) + @inbounds r = getindex(A, ind2sub(A, i)...) r end -@generated function _getindex{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, I::Real...) # TODO: DEPRECATE FOR #14770 +@generated function _getindex{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, I::Int...) # TODO: DEPRECATE FOR #14770 N = length(I) if N > AN # Drop trailing ones Isplat = Expr[:(I[$d]) for d = 1:AN] - Osplat = Expr[:(to_index(I[$d]) == 1) for d = AN+1:N] + Osplat = Expr[:(I[$d] == 1) for d = AN+1:N] quote @_propagate_inbounds_meta @boundscheck (&)($(Osplat...)) || throw_boundserror(A, I) @@ -878,7 +879,7 @@ end sz = Expr(:tuple) sz.args = Expr[:(size(A, $d)) for d=max(N,1):AN] szcheck = Expr[:(size(A, $d) > 0) for d=max(N,1):AN] - last_idx = N > 0 ? :(to_index(I[$N])) : 1 + last_idx = N > 0 ? :(I[$N]) : 1 quote # ind2sub requires all dimensions to be > 0: @_propagate_inbounds_meta @@ -892,7 +893,8 @@ end # function that allows dispatch on array storage function setindex!(A::AbstractArray, v, I...) @_propagate_inbounds_meta - _setindex!(linearindexing(A), A, v, I...) + error_if_canonical_indexing(linearindexing(A), A, I...) + _setindex!(linearindexing(A), A, v, to_indices(A, I)...) end function unsafe_setindex!(A::AbstractArray, v, I...) @_inline_meta @@ -903,49 +905,44 @@ end _setindex!(::LinearIndexing, A::AbstractArray, v, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") ## LinearFast Scalar indexing -_setindex!(::LinearFast, A::AbstractVector, v, ::Int) = error("indexed assignment not defined for ", typeof(A)) -_setindex!(::LinearFast, A::AbstractArray, v, ::Int) = error("indexed assignment not defined for ", typeof(A)) +_setindex!(::LinearFast, A::AbstractVector, v, i::Int) = (@_propagate_inbounds_meta; setindex!(A, v, i)) +_setindex!(::LinearFast, A::AbstractArray, v, i::Int) = (@_propagate_inbounds_meta; setindex!(A, v, i)) _setindex!{T}(::LinearFast, A::AbstractArray{T,0}, v) = (@_propagate_inbounds_meta; setindex!(A, v, 1)) -_setindex!(::LinearFast, A::AbstractArray, v, i::Real) = (@_propagate_inbounds_meta; setindex!(A, v, to_index(i))) -function _setindex!{T,N}(::LinearFast, A::AbstractArray{T,N}, v, I::Vararg{Real,N}) +function _setindex!{T,N}(::LinearFast, A::AbstractArray{T,N}, v, I::Vararg{Int,N}) # We must check bounds for sub2ind; so we can then use @inbounds @_inline_meta - J = to_indexes(I...) - @boundscheck checkbounds(A, J...) - @inbounds r = setindex!(A, v, sub2ind(A, J...)) + @boundscheck checkbounds(A, I...) + @inbounds r = setindex!(A, v, sub2ind(A, I...)) r end -function _setindex!(::LinearFast, A::AbstractVector, v, I1::Real, I::Real...) +function _setindex!(::LinearFast, A::AbstractVector, v, I1::Int, I::Int...) @_inline_meta - J = to_indexes(I1, I...) - @boundscheck checkbounds(A, J...) - @inbounds r = setindex!(A, v, J[1]) + @boundscheck checkbounds(A, I1, I...) + @inbounds r = setindex!(A, v, I1) r end -function _setindex!(::LinearFast, A::AbstractArray, v, I::Real...) # TODO: DEPRECATE FOR #14770 +function _setindex!(::LinearFast, A::AbstractArray, v, I::Int...) # TODO: DEPRECATE FOR #14770 @_inline_meta - J = to_indexes(I...) - @boundscheck checkbounds(A, J...) - @inbounds r = setindex!(A, v, sub2ind(A, J...)) + @boundscheck checkbounds(A, I...) + @inbounds r = setindex!(A, v, sub2ind(A, I...)) r end # LinearSlow Scalar indexing -_setindex!{T,N}(::LinearSlow, A::AbstractArray{T,N}, v, ::Vararg{Int, N}) = error("indexed assignment not defined for ", typeof(A)) -_setindex!{T,N}(::LinearSlow, A::AbstractArray{T,N}, v, I::Vararg{Real, N}) = (@_propagate_inbounds_meta; setindex!(A, v, to_indexes(I...)...)) -function _setindex!(::LinearSlow, A::AbstractArray, v, i::Real) +_setindex!{T,N}(::LinearSlow, A::AbstractArray{T,N}, v, I::Vararg{Int, N}) = (@_propagate_inbounds_meta; setindex!(A, v, I...)) +function _setindex!(::LinearSlow, A::AbstractArray, v, i::Int) # ind2sub requires all dimensions to be > 0; may as well just check bounds @_inline_meta @boundscheck checkbounds(A, i) - @inbounds r = setindex!(A, v, ind2sub(A, to_index(i))...) + @inbounds r = setindex!(A, v, ind2sub(A, i)...) r end -@generated function _setindex!{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, v, I::Real...) # TODO: DEPRECATE FOR #14770 +@generated function _setindex!{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, v, I::Int...) # TODO: DEPRECATE FOR #14770 N = length(I) if N > AN # Drop trailing ones Isplat = Expr[:(I[$d]) for d = 1:AN] - Osplat = Expr[:(to_index(I[$d]) == 1) for d = AN+1:N] + Osplat = Expr[:(I[$d] == 1) for d = AN+1:N] quote # We only check the trailing ones, so just propagate @inbounds state @_propagate_inbounds_meta @@ -958,7 +955,7 @@ end sz = Expr(:tuple) sz.args = Expr[:(size(A, $d)) for d=max(N,1):AN] szcheck = Expr[:(size(A, $d) > 0) for d=max(N,1):AN] - last_idx = N > 0 ? :(to_index(I[$N])) : 1 + last_idx = N > 0 ? :(I[$N]) : 1 quote # ind2sub requires all dimensions to be > 0: @_propagate_inbounds_meta @@ -994,7 +991,7 @@ function get!{T}(X::AbstractArray{T}, A::AbstractArray, I::Union{Range, Abstract X end -get(A::AbstractArray, I::Range, default) = get!(similar(A, typeof(default), index_shape(A, I)), A, I, default) +get(A::AbstractArray, I::Range, default) = get!(similar(A, typeof(default), index_shape(I)), A, I, default) # TODO: DEPRECATE FOR #14770 (just the partial linear indexing part) function get!{T}(X::AbstractArray{T}, A::AbstractArray, I::RangeVecIntList, default::T) @@ -1004,7 +1001,7 @@ function get!{T}(X::AbstractArray{T}, A::AbstractArray, I::RangeVecIntList, defa X end -get(A::AbstractArray, I::RangeVecIntList, default) = get!(similar(A, typeof(default), index_shape(A, I...)), A, I, default) +get(A::AbstractArray, I::RangeVecIntList, default) = get!(similar(A, typeof(default), index_shape(I...)), A, I, default) ## structured matrix methods ## replace_in_print_matrix(A::AbstractMatrix,i::Integer,j::Integer,s::AbstractString) = s @@ -1173,8 +1170,7 @@ function cat_t(dims, T::Type, X...) return _cat(A, shape, catdims, X...) end -function _cat(A, shape, catdims, X...) - N = length(shape) +function _cat{N}(A, shape::NTuple{N}, catdims, X...) offsets = zeros(Int, N) inds = Vector{UnitRange{Int}}(N) concat = copy!(zeros(Bool, N), catdims) @@ -1187,7 +1183,8 @@ function _cat(A, shape, catdims, X...) inds[i] = 1:shape[i] end end - A[inds...] = x + I::NTuple{N, UnitRange{Int}} = (inds...,) + A[I...] = x end return A end @@ -1759,7 +1756,7 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector) idx = Any[first(ind) for ind in indices(A)] itershape = tuple(dimsA[otherdims]...) for d in dims - idx[d] = Colon() + idx[d] = Slice(indices(A, d)) end Aslice = A[idx...] diff --git a/base/array.jl b/base/array.jl index 9d0e73d2c9e8b..a4645f87103e0 100644 --- a/base/array.jl +++ b/base/array.jl @@ -7,7 +7,7 @@ typealias AbstractVector{T} AbstractArray{T,1} typealias AbstractMatrix{T} AbstractArray{T,2} typealias AbstractVecOrMat{T} Union{AbstractVector{T}, AbstractMatrix{T}} -typealias RangeIndex Union{Int, Range{Int}, AbstractUnitRange{Int}, Colon} +typealias RangeIndex Union{Int, Range{Int}, AbstractUnitRange{Int}} typealias DimOrInd Union{Integer, AbstractUnitRange} typealias IntOrInd Union{Int, AbstractUnitRange} typealias DimsOrInds{N} NTuple{N,DimOrInd} @@ -448,8 +448,8 @@ done(a::Array,i) = (@_inline_meta; i == length(a)+1) ## Indexing: getindex ## # This is more complicated than it needs to be in order to get Win64 through bootstrap -getindex(A::Array, i1::Real) = arrayref(A, to_index(i1)) -getindex(A::Array, i1::Real, i2::Real, I::Real...) = (@_inline_meta; arrayref(A, to_index(i1), to_index(i2), to_indexes(I...)...)) # TODO: REMOVE FOR #14770 +getindex(A::Array, i1::Int) = arrayref(A, i1) +getindex(A::Array, i1::Int, i2::Int, I::Int...) = (@_inline_meta; arrayref(A, i1, i2, I...)) # TODO: REMOVE FOR #14770 # Faster contiguous indexing using copy! for UnitRange and Colon function getindex(A::Array, I::UnitRange{Int}) @@ -472,13 +472,13 @@ function getindex(A::Array, c::Colon) end # This is redundant with the abstract fallbacks, but needed for bootstrap -function getindex{S,T<:Real}(A::Array{S}, I::Range{T}) - return S[ A[to_index(i)] for i in I ] +function getindex{S}(A::Array{S}, I::Range{Int}) + return S[ A[i] for i in I ] end ## Indexing: setindex! ## -setindex!{T}(A::Array{T}, x, i1::Real) = arrayset(A, convert(T,x)::T, to_index(i1)) -setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, I::Real...) = (@_inline_meta; arrayset(A, convert(T,x)::T, to_index(i1), to_index(i2), to_indexes(I...)...)) # TODO: REMOVE FOR #14770 +setindex!{T}(A::Array{T}, x, i1::Int) = arrayset(A, convert(T,x)::T, i1) +setindex!{T}(A::Array{T}, x, i1::Int, i2::Int, I::Int...) = (@_inline_meta; arrayset(A, convert(T,x)::T, i1, i2, I...)) # TODO: REMOVE FOR #14770 # These are redundant with the abstract fallbacks but needed for bootstrap function setindex!(A::Array, x, I::AbstractVector{Int}) @@ -1358,7 +1358,7 @@ function find(testf::Function, A) return I end _index_remapper(A::AbstractArray) = linearindices(A) -_index_remapper(iter) = Colon() # safe for objects that don't implement length +_index_remapper(iter) = OneTo(typemax(Int)) # safe for objects that don't implement length """ find(A) diff --git a/base/deprecated.jl b/base/deprecated.jl index a93744db4b5bd..4d2a0f5e8e98f 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1125,6 +1125,40 @@ eval(Base.Dates, quote recur{T<:TimeType}(fun::Function, start::T, stop::T; step::Period=Day(1), negate::Bool=false, limit::Int=10000) = recur(fun, start:step:stop; negate=negate) end) +# Index conversions revamp; #19730 +function getindex(A::LogicalIndex, i::Int) + depwarn("getindex(A::LogicalIndex, i) is deprecated; use iteration or index into the result of `collect(A)` instead.", :getindex) + checkbounds(A, i) + first(Iterators.drop(A, i-1)) +end +function to_indexes(I...) + depwarn("to_indexes is deprecated; pass both the source array `A` and indices as `to_indices(A, $(I...))` instead.", :to_indexes) + map(_to_index, I) +end +_to_index(i) = to_index(I) +_to_index(c::Colon) = c +const _colon_usage_msg = "convert Colons to a set of indices for indexing into array `A` by passing them in a complete tuple of indices `I` to `to_indices(A, I)`" +function getindex(::Colon, i) + depwarn("getindex(::Colon, i) is deprecated; $_colon_usage_msg", :getindex) + to_index(i) +end +function unsafe_getindex(::Colon, i::Integer) + depwarn("getindex(::Colon, i) is deprecated; $_colon_usage_msg", :unsafe_getindex) + to_index(i) +end +function step(::Colon) + depwarn("step(::Colon) is deprecated; $_colon_usage_msg", :step) + 1 +end +function isempty(::Colon) + depwarn("isempty(::Colon) is deprecated; $_colon_usage_msg", :isempty) + false +end +function in(::Integer, ::Colon) + depwarn("in(::Integer, ::Colon) is deprecated; $_colon_usage_msg", :in) + true +end + # #18931 @deprecate cummin(A, dim=1) accumulate(min, A, dim) @deprecate cummax(A, dim=1) accumulate(max, A, dim) diff --git a/base/essentials.jl b/base/essentials.jl index 8f5289ac40812..56490c1c469cf 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -229,7 +229,15 @@ function isassigned(v::SimpleVector, i::Int) return x != C_NULL end -# index colon +""" + Colon() + +Colons (:) are used to signify indexing entire objects or dimensions at once. + +Very few operations are defined on Colons directly; instead they are converted +by `to_indices` to an internal vector type (`Base.Slice`) to represent the +collection of indices they span before being used. +""" immutable Colon end const (:) = Colon() diff --git a/base/exports.jl b/base/exports.jl index 81caba8fb65c7..8a91691864c68 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -578,6 +578,7 @@ export sum!, sum, sum_kbn, + to_indices, vcat, vec, view, diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 7f0a41da063f1..58d4570b3b6d1 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -4,7 +4,7 @@ module IteratorsMD import Base: eltype, length, size, start, done, next, last, in, getindex, setindex!, linearindexing, min, max, zero, one, isless, eachindex, - ndims, iteratorsize, to_index + ndims, iteratorsize importall ..Base.Operators import Base: simd_outer_range, simd_inner_length, simd_index @@ -132,8 +132,6 @@ module IteratorsMD last(iter::CartesianRange) = iter.stop - to_index(c::CartesianIndex) = c - @inline function in{I<:CartesianIndex}(i::I, r::CartesianRange{I}) _in(true, i.I, r.start.I, r.stop.I) end @@ -167,12 +165,6 @@ end # IteratorsMD using .IteratorsMD -## Support for SubArray with arrays of CartesianIndex -function _indices_sub{N}(S::SubArray, pinds, i1::AbstractArray{CartesianIndex{N}}, I...) - @_inline_meta - (unsafe_indices(i1)..., _indices_sub(S, IteratorsMD.split(pinds, Val{N})[2], I...)...) -end - ## Bounds-checking with CartesianIndex @inline checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{CartesianIndex,Vararg{Any}}) = checkbounds_indices(Bool, (), (I[1].I..., tail(I)...)) @@ -181,12 +173,23 @@ end @inline checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{CartesianIndex,Vararg{Any}}) = checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...)) +# Indexing into Array with mixtures of Integers and CartesianIndices is +# extremely performance-sensitive. While the abstract fallbacks support this, +# codegen has extra support for SIMDification that sub2ind doesn't (yet) support +@propagate_inbounds getindex(A::Array, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) = + A[to_indices(A, (i1, I...))...] +@propagate_inbounds setindex!(A::Array, v, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) = + (A[to_indices(A, (i1, I...))...] = v; A) + # Support indexing with an array of CartesianIndex{N}s # Here we try to consume N of the indices (if there are that many available) # The first two simply handle ambiguities @inline function checkbounds_indices{N}(::Type{Bool}, ::Tuple{}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) checkindex(Bool, (), I[1]) & checkbounds_indices(Bool, (), tail(I)) end +@inline function checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{AbstractArray{CartesianIndex{0}},Vararg{Any}}) + checkbounds_indices(Bool, IA, tail(I)) +end @inline function checkbounds_indices{N}(::Type{Bool}, IA::Tuple{Any}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) checkindex(Bool, IA, I[1]) & checkbounds_indices(Bool, (), tail(I)) end @@ -226,70 +229,131 @@ end index_dimsum() = () # Recursively compute the lengths of a list of indices, without dropping scalars -# These need to be inlined for more than 3 indexes -# Trailing CartesianIndex{0}s and arrays thereof are strange when used as -# trailing indexes -- they behave as though they were never there for the -# purposes of generalized linear indexing. -typealias CI0 Union{CartesianIndex{0}, AbstractArray{CartesianIndex{0}}} -index_lengths(A::AbstractArray, I::Colon) = (_length(A),) -@inline index_lengths(A::AbstractArray, I...) = index_lengths_dim(A, 1, I...) -index_lengths_dim(A, dim) = () -index_lengths_dim(A, dim, ::Colon) = (trailingsize(indices(A), dim),) -index_lengths_dim(A, dim, ::Colon, i::CI0, I::CI0...) = (trailingsize(indices(A), dim), index_lengths_dim(A, dim+1, i, I...)...) -@inline index_lengths_dim(A, dim, ::Colon, i, I...) = (_length(indices(A, dim)), index_lengths_dim(A, dim+1, i, I...)...) -@inline index_lengths_dim(A, dim, ::Real, I...) = (1, index_lengths_dim(A, dim+1, I...)...) -@inline index_lengths_dim{N}(A, dim, ::CartesianIndex{N}, I...) = (1, index_lengths_dim(A, dim+N, I...)...) -@inline index_lengths_dim(A, dim, i::AbstractArray, I...) = (length(i), index_lengths_dim(A, dim+1, I...)...) -@inline index_lengths_dim(A, dim, i::AbstractArray{Bool}, I...) = (sum(i), index_lengths_dim(A, dim+1, I...)...) -@inline index_lengths_dim{N}(A, dim, i::AbstractArray{CartesianIndex{N}}, I...) = (length(i), index_lengths_dim(A, dim+N, I...)...) +index_lengths() = () +@inline index_lengths(::Real, rest...) = (1, index_lengths(rest...)...) +@inline index_lengths(A::AbstractArray, rest...) = (length(A), index_lengths(rest...)...) +@inline index_lengths(A::Slice, rest...) = (length(indices1(A)), index_lengths(rest...)...) # shape of array to create for getindex() with indexes I, dropping scalars # returns a Tuple{Vararg{AbstractUnitRange}} of indices -index_shape(A::AbstractArray, I::Colon) = (linearindices(A),) -@inline index_shape(A::AbstractArray, I...) = index_shape_dim(indices(A), I...) -@inline index_shape_dim(inds::Tuple{Any}, ::Colon) = inds -@inline index_shape_dim(inds, ::Colon) = (OneTo(trailingsize(inds)),) -@inline index_shape_dim(inds, ::Colon, i::CI0, I::CI0...) = - (OneTo(trailingsize(inds)), index_shape_dim((), i, I...)...) -@inline function index_shape_dim(inds, ::Colon, i, I...) - inds1, indstail = IteratorsMD.split(inds, Val{1}) - (_gimme_a_range(inds1...), index_shape_dim(indstail, i, I...)...) -end -@inline index_shape_dim(inds, ::Real...) = () -@inline index_shape_dim(inds, ::Real, I...) = index_shape_dim(safe_tail(inds), I...) -@inline index_shape_dim{N}(inds, ::CartesianIndex{N}, I...) = - index_shape_dim(IteratorsMD.split(inds, Val{N})[2], I...) -@inline index_shape_dim(inds, i::AbstractArray, I...) = - (indices(i)..., index_shape_dim(safe_tail(inds), I...)...) -@inline index_shape_dim(inds, i::AbstractArray{Bool}, I...) = - (OneTo(sum(i)), index_shape_dim(safe_tail(inds), I...)...) -# single CartesianIndex version not needed because of call to flatten in _getindex... -# ...but array of CartesianIndex is not covered -@inline function index_shape_dim{N}(inds, i::AbstractArray{CartesianIndex{N}}, I...) - indsN, indstail = IteratorsMD.split(inds, Val{N}) - (indices(i)..., index_shape_dim(indstail, I...)...) -end -_gimme_a_range() = OneTo(1) -_gimme_a_range(inds::Range) = inds - -# Convert Colon indices into explicit indices -@inline decolon(A::AbstractArray, ::Colon) = (linearindices(A),) -@inline decolon(A::AbstractArray, I...) = decolon_dim(indices(A), I...) -@inline decolon_dim(inds) = () -@inline decolon_dim(inds::Tuple{Any}, ::Colon) = inds -@inline decolon_dim(inds, ::Colon) = (OneTo(trailingsize(inds)),) -@inline decolon_dim(inds, ::Colon, i::CI0, I::CI0...) = (OneTo(trailingsize(inds)), i, I...) -@inline function decolon_dim(inds, ::Colon, I...) - inds1, indstail = IteratorsMD.split(inds, Val{1}) - (maybe_oneto(inds1...), decolon_dim(indstail, I...)...) +index_shape() = () +@inline index_shape(::Real, rest...) = index_shape(rest...) +@inline index_shape(A::AbstractArray, rest...) = (indices(A)..., index_shape(rest...)...) + +""" + LogicalIndex(mask) + +The `LogicalIndex` type is a special vector that simply contains all indices I +where `mask[I]` is true. This specialized type does not support indexing +directly as doing so would require O(n) lookup time. `AbstractArray{Bool}` are +wrapped with `LogicalIndex` upon calling `to_indices`. +""" +immutable LogicalIndex{T, A<:AbstractArray{Bool}} <: AbstractVector{T} + mask::A + sum::Int + LogicalIndex(mask::A) = new(mask, countnz(mask)) +end +LogicalIndex(mask::AbstractVector{Bool}) = LogicalIndex{Int, typeof(mask)}(mask) +LogicalIndex{N}(mask::AbstractArray{Bool, N}) = LogicalIndex{CartesianIndex{N}, typeof(mask)}(mask) +(::Type{LogicalIndex{Int}})(mask::AbstractArray) = LogicalIndex{Int, typeof(mask)}(mask) +size(L::LogicalIndex) = (L.sum,) +length(L::LogicalIndex) = L.sum +collect(L::LogicalIndex) = [i for i in L] +show(io::IO, r::LogicalIndex) = print(io, "Base.LogicalIndex(", r.mask, ")") +# Iteration over LogicalIndex is very performance-critical, but it also must +# support arbitrary AbstractArray{Bool}s with both Int and CartesianIndex. +# Thus the iteration state contains an index iterator and its state. We also +# keep track of the count of elements since we already know how many there +# should be -- this way we don't need to look at future indices to check done. +@inline function start(L::LogicalIndex{Int}) + r = linearindices(L.mask) + return (r, start(r), 1) +end +@inline function start{C<:CartesianIndex}(L::LogicalIndex{C}) + r = CartesianRange(indices(L.mask)) + return (r, start(r), 1) +end +@inline function next(L::LogicalIndex, s) + # We're looking for the n-th true element, using iterator r at state i + r, i, n = s + while true + done(r, i) # Call done(r, i) for the iteration protocol, but trust done(L, s) was called + idx, i = next(r, i) + L.mask[idx] && return (idx, (r, i, n+1)) + end end -@inline decolon_dim(inds, i1, I...) = (i1, decolon_dim(safe_tail(inds), I...)...) -@inline function decolon_dim{N}(inds, i1::AbstractArray{CartesianIndex{N}}, I...) - indsN, indstail = IteratorsMD.split(inds, Val{N}) - (i1, decolon_dim(indstail, I...)...) +done(L::LogicalIndex, s) = s[3] > length(L) +# When wrapping a BitArray, lean heavily upon its internals -- this is a common +# case. Just use the Int index and count as its state. +@inline start{B<:BitArray}(L::LogicalIndex{Int, B}) = (0, 1) +@inline function next{B<:BitArray}(L::LogicalIndex{Int, B}, s) + i, n = s + Bc = L.mask.chunks + while true + if Bc[_div64(i)+1] & (UInt64(1)<<_mod64(i)) != 0 + i += 1 + return (i, (i, n+1)) + end + i += 1 + end end -maybe_oneto(i) = i -maybe_oneto() = OneTo(1) +@inline done{B<:BitArray}(L::LogicalIndex{Int, B}, s) = s[2] > length(L) + +# Checking bounds with LogicalIndex{Int} is tricky since we allow linear indexing over trailing dimensions +@inline checkbounds_indices{N}(::Type{Bool},IA::Tuple{},I::Tuple{LogicalIndex{Int,AbstractArray{Bool,N}}}) = + checkindex(Bool, IA, I[1]) +@inline checkbounds_indices{N}(::Type{Bool},IA::Tuple{Any},I::Tuple{LogicalIndex{Int,AbstractArray{Bool,N}}}) = + checkindex(Bool, IA[1], I[1]) +@inline function checkbounds_indices{N}(::Type{Bool}, IA::Tuple, I::Tuple{LogicalIndex{Int,AbstractArray{Bool,N}}}) + IA1, IArest = IteratorsMD.split(IA, Val{N}) + checkindex(Bool, IA1, I[1]) +end +@inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex) = indices(A) == indices(I.mask) +@inline checkindex(::Type{Bool}, indx::AbstractUnitRange, I::LogicalIndex) = (indx,) == indices(I.mask) +checkindex(::Type{Bool}, inds::Tuple, I::LogicalIndex) = false + +ensure_indexable(I::Tuple{}) = () +@inline ensure_indexable(I::Tuple{Any, Vararg{Any}}) = (I[1], ensure_indexable(tail(I))...) +@inline ensure_indexable(I::Tuple{LogicalIndex, Vararg{Any}}) = (collect(I[1]), ensure_indexable(tail(I))...) + +# In simple cases, we know that we don't need to use indices(A). Optimize those +# until Julia gets smart enough to elide the call on its own: +to_indices(A, I::Tuple{}) = () +@inline to_indices(A, I::Tuple{Vararg{Union{Integer, CartesianIndex}}}) = to_indices(A, (), I) +# But some index types require more context spanning multiple indices +# CartesianIndexes are simple; they just splat out +@inline to_indices(A, inds, I::Tuple{CartesianIndex, Vararg{Any}}) = + to_indices(A, inds, (I[1].I..., tail(I)...)) +# But for arrays of CartesianIndex, we just skip the appropriate number of inds +@inline function to_indices{N}(A, inds, I::Tuple{AbstractArray{CartesianIndex{N}}, Vararg{Any}}) + _, indstail = IteratorsMD.split(inds, Val{N}) + (to_index(A, I[1]), to_indices(A, indstail, tail(I))...) +end +# And boolean arrays behave similarly; they also skip their number of dimensions +@inline function to_indices{N}(A, inds, I::Tuple{AbstractArray{Bool, N}, Vararg{Any}}) + _, indstail = IteratorsMD.split(inds, Val{N}) + (to_index(A, I[1]), to_indices(A, indstail, tail(I))...) +end +# As an optimization, we allow trailing Array{Bool} and BitArray to be linear over trailing dimensions +@inline to_indices{N}(A, inds, I::Tuple{Union{Array{Bool,N}, BitArray{N}}}) = + (_maybe_linear_logical_index(linearindexing(A), A, I[1]),) +_maybe_linear_logical_index(::LinearIndexing, A, i) = to_index(A, i) +_maybe_linear_logical_index(::LinearFast, A, i) = LogicalIndex{Int}(i) + +# Colons get converted to slices by `uncolon` +@inline to_indices(A, inds, I::Tuple{Colon, Vararg{Any}}) = + (uncolon(inds, I), to_indices(A, _maybetail(inds), tail(I))...) + +typealias CI0 Union{CartesianIndex{0}, AbstractArray{CartesianIndex{0}}} +uncolon(inds::Tuple{}, I::Tuple{Colon}) = Slice(OneTo(1)) +uncolon(inds::Tuple{}, I::Tuple{Colon, Vararg{Any}}) = Slice(OneTo(1)) +uncolon(inds::Tuple{}, I::Tuple{Colon, Vararg{CI0}}) = Slice(OneTo(1)) +uncolon(inds::Tuple{Any}, I::Tuple{Colon}) = Slice(inds[1]) +uncolon(inds::Tuple{Any}, I::Tuple{Colon, Vararg{Any}}) = Slice(inds[1]) +uncolon(inds::Tuple{Any}, I::Tuple{Colon, Vararg{CI0}}) = Slice(inds[1]) +uncolon(inds::Tuple, I::Tuple{Colon, Vararg{Any}}) = Slice(inds[1]) +uncolon(inds::Tuple, I::Tuple{Colon}) = Slice(OneTo(trailingsize(inds))) +uncolon(inds::Tuple, I::Tuple{Colon, Vararg{CI0}}) = Slice(OneTo(trailingsize(inds))) ### From abstractarray.jl: Internal multidimensional indexing definitions ### getindex(x::Number, i::CartesianIndex{0}) = x @@ -298,84 +362,39 @@ getindex(t::Tuple, I...) = getindex(t, IteratorsMD.flatten(I)...) # These are not defined on directly on getindex to avoid # ambiguities for AbstractArray subtypes. See the note in abstractarray.jl -# Note that it's most efficient to call checkbounds first, and then to_index -@inline function _getindex{T,N}(l::LinearIndexing, A::AbstractArray{T,N}, I::Vararg{Union{Real, AbstractArray, Colon},N}) - @boundscheck checkbounds(A, I...) - _unsafe_getindex(l, A, I...) -end -# Explicitly allow linear indexing with one non-scalar index -@inline function _getindex(l::LinearIndexing, A::AbstractArray, i::Union{Real, AbstractArray, Colon}) - @boundscheck checkbounds(A, i) - _unsafe_getindex(l, _maybe_reshape(l, A, (i,)), i) -end -# But we can speed up LinearSlow arrays by reshaping them to vectors: -_maybe_reshape(::LinearFast, A::AbstractArray, i) = A -_maybe_reshape(::LinearSlow, A::AbstractVector, i) = A -@inline _maybe_reshape(::LinearSlow, A::AbstractArray, i) = _maybe_reshape(LinearSlow(), index_ndims(i...), A) -@inline _maybe_reshape{T,N}(::LinearIndexing, ::NTuple{N}, A::AbstractArray{T,N}) = A -@inline _maybe_reshape{N}(::LinearIndexing, ::NTuple{N}, A) = reshape(A, Val{N}) - -@inline function _getindex{N}(l::LinearIndexing, A::AbstractArray, I::Vararg{Union{Real, AbstractArray, Colon},N}) # TODO: DEPRECATE FOR #14770 - @boundscheck checkbounds(A, I...) - _unsafe_getindex(l, _maybe_reshape(l, A, I), I...) +@generated function _getindex(l::LinearIndexing, A::AbstractArray, I::Union{Real, AbstractArray}...) + N = length(I) + quote + @_inline_meta + @boundscheck checkbounds(A, I...) + _unsafe_getindex(l, _maybe_reshape(l, A, I...), I...) + end end +# But we can speed up LinearSlow arrays by reshaping them to the appropriate dimensionality: +_maybe_reshape(::LinearFast, A::AbstractArray, I...) = A +_maybe_reshape(::LinearSlow, A::AbstractVector, I...) = A +@inline _maybe_reshape(::LinearSlow, A::AbstractArray, I...) = __maybe_reshape(A, index_ndims(I...)) +@inline __maybe_reshape{T,N}(A::AbstractArray{T,N}, ::NTuple{N}) = A +@inline __maybe_reshape{N}(A::AbstractArray, ::NTuple{N}) = reshape(A, Val{N}) -@generated function _unsafe_getindex(::LinearIndexing, A::AbstractArray, I::Union{Real, AbstractArray, Colon}...) +@generated function _unsafe_getindex(::LinearIndexing, A::AbstractArray, I::Union{Real, AbstractArray}...) N = length(I) quote - # This is specifically *not* inlined. - @nexprs $N d->(I_d = to_index(I[d])) - shape = @ncall $N index_shape A I + # This is specifically not inlined to prevent exessive allocations in type unstable code + @nexprs $N d->(I_d = I[d]) + shape = @ncall $N index_shape I dest = similar(A, shape) map(unsafe_length, indices(dest)) == map(unsafe_length, shape) || throw_checksize_error(dest, shape) @ncall $N _unsafe_getindex! dest A I end end -# logical indexing optimization - don't use find (within to_index) -function _unsafe_getindex(::LinearIndexing, src::AbstractArray, I::AbstractArray{Bool}) - shape = index_shape(src, I) - dest = similar(src, shape) - map(unsafe_length, indices(dest)) == map(unsafe_length, shape) || throw_checksize_error(dest, shape) - - D = eachindex(dest) - Ds = start(D) - for (b, s) in zip(I, eachindex(src)) - @inbounds if b - d, Ds = next(D, Ds) - dest[d] = src[s] - end - end - dest -end - -# specialized form for LinearFast -function _unsafe_getindex(::LinearFast, src::AbstractArray, I::AbstractArray{Bool}) - shape = index_shape(src, I) - dest = similar(src, shape) - map(unsafe_length, indices(dest)) == map(unsafe_length, shape) || throw_checksize_error(dest, shape) - - D = eachindex(dest) - Ds = start(D) - s = first(linearindices(src))-1 - for i in eachindex(I) - s += 1 - @inbounds if I[i] - d, Ds = next(D, Ds) - dest[d] = src[s] - end - end - dest -end - # Always index with the exactly indices provided. -@generated function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Union{Real, AbstractArray, Colon}...) +@generated function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Union{Real, AbstractArray}...) N = length(I) quote $(Expr(:meta, :inline)) - @nexprs $N d->(I_d = I[d]) - J = @ncall $N decolon src I - @nexprs $N d->(J_d = J[d]) + @nexprs $N d->(J_d = I[d]) D = eachindex(dest) Ds = start(D) @inbounds @nloops $N j d->J_d begin @@ -389,91 +408,34 @@ end @noinline throw_checksize_error(A, sz) = throw(DimensionMismatch("output array is the wrong size; expected $sz, got $(size(A))")) ## setindex! ## -# For multi-element setindex!, we check bounds, convert the indices (to_index), -# and ensure the value to set is either an AbstractArray or a Repeated scalar -# before redispatching to the _unsafe_batchsetindex! -_iterable(v::AbstractArray) = v -_iterable(v) = Iterators.repeated(v) -@inline function _setindex!{T,N}(l::LinearIndexing, A::AbstractArray{T,N}, x, J::Vararg{Union{Real,AbstractArray,Colon},N}) - @boundscheck checkbounds(A, J...) - _unsafe_setindex!(l, A, x, J...) -end -@inline function _setindex!(l::LinearIndexing, A::AbstractArray, x, j::Union{Real,AbstractArray,Colon}) - @boundscheck checkbounds(A, j) - _unsafe_setindex!(l, _maybe_reshape(l, A, (j,)), x, j) - A -end -@inline function _setindex!{N}(l::LinearIndexing, A::AbstractArray, x, J::Vararg{Union{Real, AbstractArray, Colon},N}) # TODO: DEPRECATE FOR #14770 - @boundscheck checkbounds(A, J...) - _unsafe_setindex!(l, _maybe_reshape(l, A, J), x, J...) - A -end - -@inline function _unsafe_setindex!(::LinearIndexing, A::AbstractArray, x, J::Union{Real,AbstractArray,Colon}...) - _unsafe_batchsetindex!(A, _iterable(x), to_indexes(J...)...) -end - -# 1-d logical indexing: override the above to avoid calling find (in to_index) -function _unsafe_setindex!(::LinearIndexing, A::AbstractArray, x, I::AbstractArray{Bool}) - X = _iterable(x) - Xs = start(X) - c = 0 - @inbounds for (iA, i) in zip(eachindex(A), eachindex(I)) - Ii = I[i] - if Ii - done(X, Xs) && throw_setindex_mismatch(x, c+1) - (v, Xs) = next(X, Xs) - A[iA] = v - c += 1 - end - end - setindex_shape_check(X, c) - A -end - -# specialized form for LinearFast -function _unsafe_setindex!(::LinearFast, A::AbstractArray, x, I::AbstractArray{Bool}) - X = _iterable(x) - Xs = start(X) - iA = 0 - c = 0 - for i in eachindex(I) - iA += 1 - @inbounds if I[i] - done(X, Xs) && throw_setindex_mismatch(x, c+1) - (v, Xs) = next(X, Xs) - A[iA] = v - c += 1 - end +@generated function _setindex!(l::LinearIndexing, A::AbstractArray, x, I::Union{Real, AbstractArray}...) + N = length(I) + quote + @_inline_meta + @boundscheck checkbounds(A, I...) + _unsafe_setindex!(l, _maybe_reshape(l, A, I...), x, I...) + A end - setindex_shape_check(X, c) - A end -@generated function _unsafe_batchsetindex!(A::AbstractArray, X, I::Union{Real,AbstractArray,Colon}...) +_iterable(v::AbstractArray) = v +_iterable(v) = Iterators.repeated(v) +@generated function _unsafe_setindex!(::LinearIndexing, A::AbstractArray, x, I::Union{Real,AbstractArray}...) N = length(I) quote + X = _iterable(x) @nexprs $N d->(I_d = I[d]) - idxlens = @ncall $N index_lengths A I + idxlens = @ncall $N index_lengths I @ncall $N setindex_shape_check X (d->idxlens[d]) - J = @ncall $N decolon A I - @nexprs $N d->(J_d = J[d]) Xs = start(X) - @inbounds @nloops $N j d->J_d begin + @inbounds @nloops $N i d->I_d begin v, Xs = next(X, Xs) - @ncall $N setindex! A v j + @ncall $N setindex! A v i end A end end -@propagate_inbounds function _getindex{T,N}(l::LinearIndexing, A::AbstractArray{T,N}, I::Union{Real,AbstractArray,Colon,CartesianIndex}...) - getindex(A, IteratorsMD.flatten(I)...) -end -@propagate_inbounds function _setindex!{T,N}(l::LinearIndexing, A::AbstractArray{T,N}, v, I::Union{Real,AbstractArray,Colon,CartesianIndex}...) - setindex!(A, v, IteratorsMD.flatten(I)...) -end - ## @generated function findn{T,N}(A::AbstractArray{T,N}) @@ -902,19 +864,19 @@ end # contiguous multidimensional indexing: if the first dimension is a range, # we can get some performance from using copy_chunks! -@inline function _unsafe_getindex!(X::BitArray, B::BitArray, I0::Union{UnitRange{Int},Colon}) - copy_chunks!(X.chunks, 1, B.chunks, indexoffset(I0)+1, index_lengths(B, I0)[1]) +@inline function _unsafe_getindex!(X::BitArray, B::BitArray, I0::Union{UnitRange{Int},Slice}) + copy_chunks!(X.chunks, 1, B.chunks, indexoffset(I0)+1, length(I0)) return X end # Optimization where the inner dimension is contiguous improves perf dramatically -@generated function _unsafe_getindex!(X::BitArray, B::BitArray, I0::Union{Colon,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Colon}...) +@generated function _unsafe_getindex!(X::BitArray, B::BitArray, I0::Union{Slice,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Slice}...) N = length(I) quote $(Expr(:meta, :inline)) @nexprs $N d->(I_d = I[d]) - idxlens = @ncall $N index_lengths B I0 d->I[d] + idxlens = @ncall $N index_lengths I0 I f0 = indexoffset(I0)+1 l0 = idxlens[1] @@ -946,7 +908,7 @@ end # in the general multidimensional non-scalar case, can we do about 10% better # in most cases by manually hoisting the bitarray chunks access out of the loop # (This should really be handled by the compiler or with an immutable BitArray) -@generated function _unsafe_getindex!(X::BitArray, B::BitArray, I::Union{Int,AbstractArray{Int},Colon}...) +@generated function _unsafe_getindex!(X::BitArray, B::BitArray, I::Union{Int,AbstractArray{Int}}...) N = length(I) quote $(Expr(:meta, :inline)) @@ -955,8 +917,7 @@ end $(Symbol(:offset_, N)) = 1 ind = 0 Xc, Bc = X.chunks, B.chunks - idxlens = @ncall $N index_lengths B d->I[d] - @nloops $N i d->(1:idxlens[d]) d->(@inbounds offset_{d-1} = offset_d + (I[d][i_d]-1)*stride_d) begin + @nloops $N i d->I[d] d->(@inbounds offset_{d-1} = offset_d + (i_d-1)*stride_d) begin ind += 1 unsafe_bitsetindex!(Xc, unsafe_bitgetindex(Bc, offset_0), ind) end @@ -969,9 +930,10 @@ end # contiguous multidimensional indexing: if the first dimension is a range, # we can get some performance from using copy_chunks! -@inline function setindex!(B::BitArray, X::Union{BitArray,Array}, I0::Union{Colon,UnitRange{Int}}) +@inline function setindex!(B::BitArray, X::Union{BitArray,Array}, J0::Union{Colon,UnitRange{Int}}) + I0 = to_indices(B, (J0,))[1] @boundscheck checkbounds(B, I0) - l0 = index_lengths(B, I0)[1] + l0 = length(I0) setindex_shape_check(X, l0) l0 == 0 && return B f0 = indexoffset(I0)+1 @@ -979,10 +941,11 @@ end return B end -@inline function setindex!(B::BitArray, x, I0::Union{Colon,UnitRange{Int}}) +@inline function setindex!(B::BitArray, x, J0::Union{Colon,UnitRange{Int}}) + I0 = to_indices(B, (J0,))[1] @boundscheck checkbounds(B, I0) y = Bool(x) - l0 = index_lengths(B, I0)[1] + l0 = length(I0) l0 == 0 && return B f0 = indexoffset(I0)+1 fill_chunks!(B.chunks, y, f0, l0) @@ -990,14 +953,14 @@ end end @inline function setindex!(B::BitArray, X::Union{BitArray,Array}, I0::Union{Colon,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Colon}...) - @boundscheck checkbounds(B, I0, I...) - _unsafe_setindex!(B, X, I0, I...) + J = to_indices(B, (I0, I...)) + @boundscheck checkbounds(B, J...) + _unsafe_setindex!(B, X, J...) end -@generated function _unsafe_setindex!(B::BitArray, X::Union{BitArray,Array}, I0::Union{Colon,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Colon}...) +@generated function _unsafe_setindex!(B::BitArray, X::Union{BitArray,Array}, I0::Union{Slice,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Slice}...) N = length(I) - rangeexp = [I[d] === Colon ? :(1:size(B, $(d+1))) : :(I[$d]) for d = 1:N] quote - idxlens = @ncall $N index_lengths B I0 d->I[d] + idxlens = @ncall $N index_lengths I0 d->I[d] @ncall $N setindex_shape_check X idxlens[1] d->idxlens[d+1] isempty(X) && return B f0 = indexoffset(I0)+1 @@ -1016,7 +979,7 @@ end refind = 1 Bc = B.chunks - @nloops($N, i, d->$rangeexp[d], + @nloops($N, i, d->I[d], d->nothing, # PRE d->(ind += stride_lst_d - gap_lst_d), # POST begin # BODY @@ -1029,15 +992,15 @@ end end @inline function setindex!(B::BitArray, x, I0::Union{Colon,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Colon}...) - @boundscheck checkbounds(B, I0, I...) - _unsafe_setindex!(B, x, I0, I...) + J = to_indices(B, (I0, I...)) + @boundscheck checkbounds(B, J...) + _unsafe_setindex!(B, x, J...) end -@generated function _unsafe_setindex!(B::BitArray, x, I0::Union{Colon,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Colon}...) +@generated function _unsafe_setindex!(B::BitArray, x, I0::Union{Slice,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Slice}...) N = length(I) - rangeexp = [I[d] === Colon ? :(1:size(B, $(d+1))) : :(I[$d]) for d = 1:N] quote y = Bool(x) - idxlens = @ncall $N index_lengths B I0 d->I[d] + idxlens = @ncall $N index_lengths I0 d->I[d] f0 = indexoffset(I0)+1 l0 = idxlens[1] @@ -1055,7 +1018,7 @@ end gap_lst_{d+1} *= stride end - @nloops($N, i, d->$rangeexp[d], + @nloops($N, i, d->I[d], d->nothing, # PRE d->(ind += stride_lst_d - gap_lst_d), # POST fill_chunks!(B.chunks, y, ind, l0) # BODY diff --git a/base/number.jl b/base/number.jl index 8d5efb5ce7c33..303ab5cb460c5 100644 --- a/base/number.jl +++ b/base/number.jl @@ -46,7 +46,6 @@ function getindex(x::Number, I::Integer...) @boundscheck all([i == 1 for i in I]) || throw(BoundsError()) x end -getindex(x::Number, I::Real...) = getindex(x, to_indexes(I...)...) first(x::Number) = x last(x::Number) = x copy(x::Number) = x # some code treats numbers as collection-like diff --git a/base/operators.jl b/base/operators.jl index 460a5fc42132f..80b0cd27547c5 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -868,19 +868,95 @@ function setindex_shape_check{T}(X::AbstractArray{T,2}, i::Integer, j::Integer) end setindex_shape_check(X, I...) = nothing # Non-arrays broadcast to all idxs -# convert to a supported index type (Array, Colon, or Int) -to_index(i::Int) = i +# convert to a supported index type (array or Int) +""" + to_index(A, i) + +Convert index `i` to an `Int` or array of indices to be used as an index into array `A`. + +Custom array types may specialize `to_index(::CustomArray, i)` to provide +special indexing behaviors. Note that some index types (like `Colon`) require +more context in order to transform them into an array of indices; those get +converted in the more complicated `to_indices` function. By default, this +simply calls the generic `to_index(i)`. This must return either an `Int` or an +`AbstractArray` of scalar indices that are supported by `A`. +""" +to_index(A, i) = to_index(i) + +""" + to_index(i) + +Convert index `i` to an `Int` or array of `Int`s to be used as an index for all arrays. + +Custom index types may specialize `to_index(::CustomIndex)` to provide special +indexing behaviors. This must return either an `Int` or an `AbstractArray` of +`Int`s. +""" to_index(i::Integer) = convert(Int,i)::Int -to_index(c::Colon) = c -to_index(I::AbstractArray{Bool}) = find(I) -to_index(A::AbstractArray) = A -to_index{T<:AbstractArray}(A::AbstractArray{T}) = throw(ArgumentError("invalid index: $A")) -to_index(A::AbstractArray{Colon}) = throw(ArgumentError("invalid index: $A")) +to_index(I::AbstractArray{Bool}) = LogicalIndex(I) +to_index(I::AbstractArray) = I +to_index{T<:Union{AbstractArray, Colon}}(I::AbstractArray{T}) = throw(ArgumentError("invalid index: $I")) +to_index(::Colon) = throw(ArgumentError("colons must be converted by to_indices(...)")) to_index(i) = throw(ArgumentError("invalid index: $i")) -to_indexes() = () -to_indexes(i1) = (to_index(i1),) -to_indexes(i1, I...) = (@_inline_meta; (to_index(i1), to_indexes(I...)...)) +# The general to_indices is mostly defined in multidimensional.jl, but this +# definition is required for bootstrap: +""" + to_indices(A, I::Tuple) + +Convert the tuple `I` to a tuple of indices for use in indexing into array `A`. + +The returned tuple must only contain either `Int`s or `AbstractArray`s of +scalar indices that are supported by array `A`. It will error upon encountering +a novel index type that it does not know how to process. + +For simple index types, it defers to the unexported `Base.to_index(A, i)` to +process each index `i`. While this internal function is not intended to be +called directly, `Base.to_index` may be extended by custom array or index types +to provide custom indexing behaviors. + +More complicated index types may require more context about the dimension into +which they index. To support those cases, `to_indices(A, I)` calls +`to_indices(A, indices(A), I)`, which then recursively walks through both the +given tuple of indices and the dimensional indices of `A` in tandem. As such, +not all index types are guaranteed to propagate to `Base.to_index`. +""" +to_indices(A, I::Tuple) = (@_inline_meta; to_indices(A, indices(A), I)) +to_indices(A, inds, ::Tuple{}) = () +to_indices(A, inds, I::Tuple{Any, Vararg{Any}}) = + (@_inline_meta; (to_index(A, I[1]), to_indices(A, _maybetail(inds), tail(I))...)) + +_maybetail(::Tuple{}) = () +_maybetail(t::Tuple) = tail(t) + +""" + Slice(indices) + +Represent an AbstractUnitRange of indices as a vector of the indices themselves. + +Upon calling `to_indices()`, Colons are converted to Slice objects to represent +the indices over which the Colon spans. Slice objects are themselves unit +ranges with the same indices as those they wrap. This means that indexing into +Slice objects with an integer always returns that exact integer, and they +iterate over all the wrapped indices, even supporting offset indices. +""" +immutable Slice{T<:AbstractUnitRange} <: AbstractUnitRange{Int} + indices::T +end +indices(S::Slice) = (S.indices,) +unsafe_indices(S::Slice) = (S.indices,) +indices1(S::Slice) = S.indices +first(S::Slice) = first(S.indices) +last(S::Slice) = last(S.indices) +errmsg(A) = error("size not supported for arrays with indices $(indices(A)); see http://docs.julialang.org/en/latest/devdocs/offset-arrays/") +size(S::Slice) = first(S.indices) == 1 ? (length(S.indices),) : errmsg(S) +length(S::Slice) = first(S.indices) == 1 ? length(S.indices) : errmsg(S) +unsafe_length(S::Slice) = first(S.indices) == 1 ? unsafe_length(S.indices) : errmsg(S) +getindex(S::Slice, i::Int) = (@_inline_meta; @boundscheck checkbounds(S, i); i) +show(io::IO, r::Slice) = print(io, "Base.Slice(", r.indices, ")") +start(S::Slice) = start(S.indices) +next(S::Slice, s) = next(S.indices, s) +done(S::Slice, s) = done(S.indices, s) # Addition/subtraction of ranges for f in (:+, :-) diff --git a/base/serialize.jl b/base/serialize.jl index 72b18b199b852..e55be50eb26bc 100644 --- a/base/serialize.jl +++ b/base/serialize.jl @@ -4,7 +4,7 @@ module Serializer import Base: GMP, Bottom, unsafe_convert, uncompressed_ast, datatype_pointerfree import Core: svec -using Base: ViewIndex, index_lengths +using Base: ViewIndex, Slice, index_lengths export serialize, deserialize, SerializationState @@ -233,7 +233,7 @@ function trimmedsubarray{T,N,A<:Array}(V::SubArray{T,N,A}) _trimmedsubarray(dest, V, (), V.indexes...) end -trimmedsize(V) = index_lengths(V.parent, V.indexes...) +trimmedsize(V) = index_lengths(V.indexes...) function _trimmedsubarray{T,N,P,I,LD}(A, V::SubArray{T,N,P,I,LD}, newindexes) LD && return SubArray{T,N,P,I,LD}(A, newindexes, Base.compute_offset1(A, 1, newindexes), 1) @@ -243,6 +243,7 @@ _trimmedsubarray(A, V, newindexes, index::ViewIndex, indexes...) = _trimmedsubar trimmedindex(P, d, i::Real) = oftype(i, 1) trimmedindex(P, d, i::Colon) = i +trimmedindex(P, d, i::Slice) = i trimmedindex(P, d, i::AbstractArray) = oftype(i, reshape(linearindices(i), indices(i))) function serialize(s::AbstractSerializer, ss::String) diff --git a/base/sort.jl b/base/sort.jl index e5cff84935a17..df34f0b092357 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -8,7 +8,9 @@ import Base.sort, Base.sort!, Base.issorted, - Base.sortperm + Base.sortperm, + Base.Slice, + Base.to_indices export # also exported by Base # order-only: @@ -669,11 +671,11 @@ function sortcols(A::AbstractMatrix; kws...) end function slicetypeof{T,N}(A::AbstractArray{T,N}, i1, i2) - I = (slice_dummy(i1),slice_dummy(i2)) + I = map(slice_dummy, to_indices(A, (i1, i2))) fast = isa(linearindexing(viewindexing(I), linearindexing(A)), LinearFast) SubArray{T,1,typeof(A),typeof(I),fast} end -slice_dummy(::Colon) = Colon() +slice_dummy(S::Slice) = S slice_dummy{T}(::AbstractUnitRange{T}) = one(T) ## fast clever sorting for floats ## diff --git a/base/subarray.jl b/base/subarray.jl index 1a6b8d5c2e3bb..54af2bd3d2dc7 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -1,9 +1,8 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license abstract AbstractCartesianIndex{N} # This is a hacky forward declaration for CartesianIndex -typealias NonSliceIndex Union{Colon, AbstractArray} -typealias ViewIndex Union{Real, NonSliceIndex} -typealias ScalarIndex Union{Real, AbstractCartesianIndex} +typealias ViewIndex Union{Real, AbstractArray} +typealias ScalarIndex Real # L is true if the view itself supports fast linear indexing immutable SubArray{T,N,P,I,L} <: AbstractArray{T,N} @@ -20,7 +19,7 @@ end # Compute the linear indexability of the indices, and combine it with the linear indexing of the parent function SubArray(parent::AbstractArray, indexes::Tuple) @_inline_meta - SubArray(linearindexing(viewindexing(indexes), linearindexing(parent)), parent, indexes, index_dimsum(indexes...)) + SubArray(linearindexing(viewindexing(indexes), linearindexing(parent)), parent, ensure_indexable(indexes), index_dimsum(indexes...)) end function SubArray{P, I, N}(::LinearSlow, parent::P, indexes::I, ::NTuple{N}) @_inline_meta @@ -42,12 +41,12 @@ check_parent_index_match{N}(parent, ::NTuple{N, Bool}) = viewindexing() = LinearFast() # Leading scalar indexes simply increase the stride viewindexing(I::Tuple{ScalarIndex, Vararg{Any}}) = (@_inline_meta; viewindexing(tail(I))) -# Colons may begin a section which may be followed by any number of Colons -viewindexing(I::Tuple{Colon, Colon, Vararg{Any}}) = (@_inline_meta; viewindexing(tail(I))) -# A UnitRange can follow Colons, but only if all other indices are scalar -viewindexing(I::Tuple{Colon, UnitRange, Vararg{ScalarIndex}}) = LinearFast() +# Slices may begin a section which may be followed by any number of Slices +viewindexing(I::Tuple{Slice, Slice, Vararg{Any}}) = (@_inline_meta; viewindexing(tail(I))) +# A UnitRange can follow Slices, but only if all other indices are scalar +viewindexing(I::Tuple{Slice, UnitRange, Vararg{ScalarIndex}}) = LinearFast() # In general, ranges are only fast if all other indices are scalar -viewindexing(I::Tuple{Union{Range, Colon}, Vararg{ScalarIndex}}) = LinearFast() +viewindexing(I::Tuple{Union{Range, Slice}, Vararg{ScalarIndex}}) = LinearFast() # All other index combinations are slow viewindexing(I::Tuple{Vararg{Any}}) = LinearSlow() # Of course, all other array types are slow @@ -85,18 +84,16 @@ given indices instead of making a copy. Calling [`getindex`](@ref) or [`setindex!`](@ref) on the returned `SubArray` computes the indices to the parent array on the fly without checking bounds. """ -function view(A::AbstractArray, I::ViewIndex...) +function view(A::AbstractArray, I...) @_inline_meta - @boundscheck checkbounds(A, I...) - unsafe_view(_maybe_reshape_parent(A, index_ndims(I...)), I...) + J = to_indices(A, I) + @boundscheck checkbounds(A, J...) + unsafe_view(_maybe_reshape_parent(A, index_ndims(J...)), J...) end -# But we can simply flatten scalar `CartesianIndex`s first to make life easier -view(A::AbstractArray, I::Union{ViewIndex, AbstractCartesianIndex}...) = view(A, IteratorsMD.flatten(I)...) function unsafe_view(A::AbstractArray, I::ViewIndex...) @_inline_meta - J = to_indexes(I...) - SubArray(A, J) + SubArray(A, I) end # When we take the view of a view, it's often possible to "reindex" the parent # view's indices such that we can "pop" the parent view and keep just one layer @@ -104,7 +101,7 @@ end # might span multiple parent indices, making the reindex calculation very hard. # So we use _maybe_reindex to figure out if there are any arrays of # `CartesianIndex`, and if so, we punt and keep two layers of indirection. -unsafe_view(V::SubArray, I::ViewIndex...) = (@_inline_meta; _maybe_reindex(V, to_indexes(I...))) +unsafe_view(V::SubArray, I::ViewIndex...) = (@_inline_meta; _maybe_reindex(V, I)) _maybe_reindex(V, I) = (@_inline_meta; _maybe_reindex(V, I, I)) _maybe_reindex{C<:AbstractCartesianIndex}(V, I, ::Tuple{AbstractArray{C}, Vararg{Any}}) = (@_inline_meta; SubArray(V, I)) @@ -114,7 +111,7 @@ _maybe_reindex{C<:AbstractCartesianIndex{1}}(V, I, A::Tuple{AbstractArray{C}, Va _maybe_reindex(V, I, A::Tuple{Any, Vararg{Any}}) = (@_inline_meta; _maybe_reindex(V, I, tail(A))) function _maybe_reindex(V, I, ::Tuple{}) @_inline_meta - idxs = reindex(V, V.indexes, to_indexes(I...)) + idxs = to_indices(V.parent, reindex(V, V.indexes, I)) SubArray(V.parent, idxs) end @@ -134,8 +131,8 @@ reindex(V, ::Tuple{}, ::Tuple{}) = () reindex(V, idxs::Tuple{ScalarIndex, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) = (@_propagate_inbounds_meta; (idxs[1], reindex(V, tail(idxs), subidxs)...)) -# Colons simply pass their subindexes straight through -reindex(V, idxs::Tuple{Colon, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) = +# Slices simply pass their subindexes straight through +reindex(V, idxs::Tuple{Slice, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) = (@_propagate_inbounds_meta; (subidxs[1], reindex(V, tail(idxs), tail(subidxs))...)) # Re-index into parent vectors with one subindex @@ -159,67 +156,59 @@ end # In general, we simply re-index the parent indices by the provided ones typealias SlowSubArray{T,N,P,I} SubArray{T,N,P,I,false} -function getindex{T,N}(V::SlowSubArray{T,N}, I::Vararg{Real,N}) +function getindex{T,N}(V::SlowSubArray{T,N}, I::Vararg{Int,N}) @_inline_meta @boundscheck checkbounds(V, I...) - @inbounds r = V.parent[reindex(V, V.indexes, to_indexes(I...))...] + @inbounds r = V.parent[reindex(V, V.indexes, I)...] r end typealias FastSubArray{T,N,P,I} SubArray{T,N,P,I,true} -function getindex(V::FastSubArray, i::Real) +function getindex(V::FastSubArray, i::Int) @_inline_meta @boundscheck checkbounds(V, i) - @inbounds r = V.parent[V.offset1 + V.stride1*to_index(i)] + @inbounds r = V.parent[V.offset1 + V.stride1*i] r end # We can avoid a multiplication if the first parent index is a Colon or UnitRange -typealias FastContiguousSubArray{T,N,P,I<:Tuple{Union{Colon, UnitRange}, Vararg{Any}}} SubArray{T,N,P,I,true} -function getindex(V::FastContiguousSubArray, i::Real) +typealias FastContiguousSubArray{T,N,P,I<:Tuple{Union{Slice, UnitRange}, Vararg{Any}}} SubArray{T,N,P,I,true} +function getindex(V::FastContiguousSubArray, i::Int) @_inline_meta @boundscheck checkbounds(V, i) - @inbounds r = V.parent[V.offset1 + to_index(i)] + @inbounds r = V.parent[V.offset1 + i] r end -function setindex!{T,N}(V::SlowSubArray{T,N}, x, I::Vararg{Real,N}) +function setindex!{T,N}(V::SlowSubArray{T,N}, x, I::Vararg{Int,N}) @_inline_meta @boundscheck checkbounds(V, I...) - @inbounds V.parent[reindex(V, V.indexes, to_indexes(I...))...] = x + @inbounds V.parent[reindex(V, V.indexes, I)...] = x V end -function setindex!(V::FastSubArray, x, i::Real) +function setindex!(V::FastSubArray, x, i::Int) @_inline_meta @boundscheck checkbounds(V, i) - @inbounds V.parent[V.offset1 + V.stride1*to_index(i)] = x + @inbounds V.parent[V.offset1 + V.stride1*i] = x V end -function setindex!(V::FastContiguousSubArray, x, i::Real) +function setindex!(V::FastContiguousSubArray, x, i::Int) @_inline_meta @boundscheck checkbounds(V, i) - @inbounds V.parent[V.offset1 + to_index(i)] = x + @inbounds V.parent[V.offset1 + i] = x V end linearindexing{T<:FastSubArray}(::Type{T}) = LinearFast() linearindexing{T<:SubArray}(::Type{T}) = LinearSlow() -getindex(::Colon, i) = to_index(i) -unsafe_getindex(::Colon, i) = to_index(i) - -step(::Colon) = 1 -isempty(::Colon) = false -in(::Integer, ::Colon) = true - # Strides are the distance between adjacent elements in a given dimension, # so they are well-defined even for non-linear memory layouts strides{T,N,P,I}(V::SubArray{T,N,P,I}) = substrides(V.parent, V.indexes) substrides(parent, I::Tuple) = substrides(1, parent, 1, I) substrides(s, parent, dim, ::Tuple{}) = () -substrides(s, parent, dim, I::Tuple{Real, Vararg{Any}}) = (substrides(s*size(parent, dim), parent, dim+1, tail(I))...) -substrides(s, parent, dim, I::Tuple{AbstractCartesianIndex, Vararg{Any}}) = substrides(s, parent, dim, (I[1].I..., tail(I)...)) -substrides(s, parent, dim, I::Tuple{Colon, Vararg{Any}}) = (s, substrides(s*size(parent, dim), parent, dim+1, tail(I))...) +substrides(s, parent, dim, I::Tuple{ScalarIndex, Vararg{Any}}) = (substrides(s*size(parent, dim), parent, dim+1, tail(I))...) +substrides(s, parent, dim, I::Tuple{Slice, Vararg{Any}}) = (s, substrides(s*size(parent, dim), parent, dim+1, tail(I))...) substrides(s, parent, dim, I::Tuple{Range, Vararg{Any}}) = (s*step(I[1]), substrides(s*size(parent, dim), parent, dim+1, tail(I))...) substrides(s, parent, dim, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("strides is invalid for SubArrays with indices of type $(typeof(I[1]))")) @@ -228,15 +217,10 @@ stride(V::SubArray, d::Integer) = d <= ndims(V) ? strides(V)[d] : strides(V)[end compute_stride1{N}(parent::AbstractArray, I::NTuple{N}) = (@_inline_meta; compute_stride1(1, fill_to_length(indices(parent), OneTo(1), Val{N}), I)) compute_stride1(s, inds, I::Tuple{}) = s -compute_stride1(s, inds, I::Tuple{Real, Vararg{Any}}) = +compute_stride1(s, inds, I::Tuple{ScalarIndex, Vararg{Any}}) = (@_inline_meta; compute_stride1(s*unsafe_length(inds[1]), tail(inds), tail(I))) compute_stride1(s, inds, I::Tuple{Range, Vararg{Any}}) = s*step(I[1]) -compute_stride1(s, inds, I::Tuple{Colon, Vararg{Any}}) = s -function compute_stride1{N}(s, inds, I::Tuple{AbstractCartesianIndex{N}, Vararg{Any}}) - @_inline_meta - h, t = IteratorsMD.split(inds, Val{N}) - compute_stride1(s*prod(map(unsafe_length, h)), t, tail(I)) -end +compute_stride1(s, inds, I::Tuple{Slice, Vararg{Any}}) = s compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("invalid strided index type $(typeof(I[1]))")) iscontiguous(A::SubArray) = iscontiguous(typeof(A)) @@ -258,7 +242,7 @@ end # linear indexing always starts with 1. compute_offset1(parent, stride1::Integer, I::Tuple) = (@_inline_meta; compute_offset1(parent, stride1, find_extended_dims(I)..., I)) -compute_offset1(parent, stride1::Integer, dims::Tuple{Int}, inds::Tuple{Colon}, I::Tuple) = +compute_offset1(parent, stride1::Integer, dims::Tuple{Int}, inds::Tuple{Slice}, I::Tuple) = (@_inline_meta; compute_linindex(parent, I) - stride1*first(indices(parent, dims[1]))) # index-preserving case compute_offset1(parent, stride1::Integer, dims, inds, I::Tuple) = (@_inline_meta; compute_linindex(parent, I) - stride1) # linear indexing starts with 1 @@ -268,16 +252,11 @@ function compute_linindex{N}(parent, I::NTuple{N}) IP = fill_to_length(indices(parent), OneTo(1), Val{N}) compute_linindex(1, 1, IP, I) end -function compute_linindex(f, s, IP::Tuple, I::Tuple{Real, Vararg{Any}}) +function compute_linindex(f, s, IP::Tuple, I::Tuple{ScalarIndex, Vararg{Any}}) @_inline_meta Δi = I[1]-first(IP[1]) compute_linindex(f + Δi*s, s*unsafe_length(IP[1]), tail(IP), tail(I)) end -# Just splat out the cartesian indices and continue -compute_linindex(f, s, IP::Tuple, I::Tuple{AbstractCartesianIndex, Vararg{Any}}) = - (@_inline_meta; compute_linindex(f, s, IP, (I[1].I..., tail(I)...))) -compute_linindex(f, s, IP::Tuple, I::Tuple{Colon, Vararg{Any}}) = - (@_inline_meta; compute_linindex(f, s*unsafe_length(IP[1]), tail(IP), tail(I))) function compute_linindex(f, s, IP::Tuple, I::Tuple{Any, Vararg{Any}}) @_inline_meta Δi = first(I[1])-first(IP[1]) @@ -286,11 +265,9 @@ end compute_linindex(f, s, IP::Tuple, I::Tuple{}) = f find_extended_dims(I) = (@_inline_meta; _find_extended_dims((), (), 1, I...)) -_find_extended_dims(dims, inds, dim) = (@_inline_meta; return (dims, inds)) -_find_extended_dims(dims, inds, dim, ::Real, I...) = +_find_extended_dims(dims, inds, dim) = dims, inds +_find_extended_dims(dims, inds, dim, ::ScalarIndex, I...) = (@_inline_meta; _find_extended_dims(dims, inds, dim+1, I...)) -_find_extended_dims(dims, inds, dim, i1::AbstractCartesianIndex, I...) = - (@_inline_meta; _find_extended_dims(dims, inds, dim, i1.I..., I...)) _find_extended_dims(dims, inds, dim, i1, I...) = (@_inline_meta; _find_extended_dims((dims..., dim), (inds..., i1), dim+1, I...)) @@ -312,29 +289,16 @@ function pointer{T,N,P<:Array,I<:Tuple{Vararg{RangeIndex}}}(V::SubArray{T,N,P,I} return pointer(V.parent, index) end -# indices of the parent are preserved for ::Colon indices, otherwise -# they are taken from the range/vector +# indices are taken from the range/vector # Since bounds-checking is performance-critical and uses # indices, it's worth optimizing these implementations thoroughly -indices(S::SubArray) = (@_inline_meta; _indices_sub(S, indices(S.parent), S.indexes...)) -_indices_sub(S::SubArray, pinds) = () -function _indices_sub(S::SubArray, pinds, ::Real, I...) - @_inline_meta - _indices_sub(S, tail(pinds), I...) -end -function _indices_sub(S::SubArray, pinds, ::Colon, I...) - @_inline_meta - (pinds[1], _indices_sub(S, tail(pinds), I...)...) -end -function _indices_sub(S::SubArray, pinds, i1::AbstractArray, I...) - @_inline_meta - (unsafe_indices(i1)..., _indices_sub(S, tail(pinds), I...)...) -end -function _indices_sub{N}(S::SubArray, pinds, ::AbstractCartesianIndex{N}, I...) +indices(S::SubArray) = (@_inline_meta; _indices_sub(S, S.indexes...)) +_indices_sub(S::SubArray) = () +_indices_sub(S::SubArray, ::Real, I...) = (@_inline_meta; _indices_sub(S, I...)) +function _indices_sub(S::SubArray, i1::AbstractArray, I...) @_inline_meta - _indices_sub(S, IteratorsMD.split(pinds, Val{N})[2], I...) + (unsafe_indices(i1)..., _indices_sub(S, I...)...) end -# _indices_sub for arrays of CartesianIndex is defined in multidimensional.jl ## Compatability # deprecate? @@ -346,7 +310,7 @@ function parentdims(s::SubArray) j = 1 for i = 1:ndims(s.parent) r = s.indexes[i] - if j <= nd && (isa(r,Union{Colon,Range}) ? sp[i]*step(r) : sp[i]) == sv[j] + if j <= nd && (isa(r,Union{Slice,Range}) ? sp[i]*step(r) : sp[i]) == sv[j] dimindex[j] = i j += 1 end diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index 8d400d7559ce1..9171c68d652fc 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -56,6 +56,8 @@ Base.Broadcast.broadcast! Base.getindex(::AbstractArray, ::Any...) Base.view Base.@view +Base.to_indices +Base.Colon Base.parent Base.parentindexes Base.slicedim diff --git a/test/TestHelpers.jl b/test/TestHelpers.jl index 588e8e649e1bf..4501468505ed3 100644 --- a/test/TestHelpers.jl +++ b/test/TestHelpers.jl @@ -104,12 +104,14 @@ Base.reshape(A::AbstractArray, inds::Tuple{UnitRange,Vararg{UnitRange}}) = Offse @inbounds ret = parent(A)[offset(A.offsets, I)...] ret end -@inline function Base._getindex(::LinearFast, A::OffsetVector, i::Int) +# Vectors don't support one-based linear indexing; they always use the offsets +@inline function Base.getindex(A::OffsetVector, i::Int) checkbounds(A, i) @inbounds ret = parent(A)[offset(A.offsets, (i,))[1]] ret end -@inline function Base._getindex(::LinearFast, A::OffsetArray, i::Int) +# But multidimensional arrays allow one-based linear indexing +@inline function Base.getindex(A::OffsetArray, i::Int) checkbounds(A, i) @inbounds ret = parent(A)[i] ret @@ -119,12 +121,12 @@ end @inbounds parent(A)[offset(A.offsets, I)...] = val val end -@inline function Base._setindex!(::LinearFast, A::OffsetVector, val, i::Int) +@inline function Base.setindex!(A::OffsetVector, val, i::Int) checkbounds(A, i) @inbounds parent(A)[offset(A.offsets, (i,))[1]] = val val end -@inline function Base._setindex!(::LinearFast, A::OffsetArray, val, i::Int) +@inline function Base.setindex!(A::OffsetArray, val, i::Int) checkbounds(A, i) @inbounds parent(A)[i] = val val diff --git a/test/subarray.jl b/test/subarray.jl index 6b75754b0af60..8cb911b03e4e6 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -320,7 +320,7 @@ A = copy(reshape(1:120, 3, 5, 8)) sA = view(A, 2:2, 1:5, :) @test strides(sA) == (1, 3, 15) @test parent(sA) == A -@test parentindexes(sA) == (2:2, 1:5, :) +@test parentindexes(sA) == (2:2, 1:5, Base.Slice(1:8)) @test Base.parentdims(sA) == [1:3;] @test size(sA) == (1, 5, 8) @test indices(sA) === (Base.OneTo(1), Base.OneTo(5), Base.OneTo(8)) @@ -365,18 +365,18 @@ sA = view(A, 1:2, 3, [1 3; 4 2]) # logical indexing #4763 A = view([1:10;], 5:8) -@test A[A.<7] == [5, 6] +@test A[A.<7] == view(A, A.<7) == [5, 6] @test Base.unsafe_getindex(A, A.<7) == [5, 6] B = reshape(1:16, 4, 4) sB = view(B, 2:3, 2:3) -@test sB[sB.>8] == [10, 11] +@test sB[sB.>8] == view(sB, sB.>8) == [10, 11] @test Base.unsafe_getindex(sB, sB.>8) == [10, 11] # Tests where dimensions are dropped A = copy(reshape(1:120, 3, 5, 8)) sA = view(A, 2, :, 1:8) @test parent(sA) == A -@test parentindexes(sA) == (2, :, 1:8) +@test parentindexes(sA) == (2, Base.Slice(1:5), 1:8) @test Base.parentdims(sA) == [2:3;] @test size(sA) == (5, 8) @test indices(sA) === (Base.OneTo(5), Base.OneTo(8))