Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 62 additions & 114 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -416,49 +416,51 @@ pointer{T}(x::AbstractArray{T}, i::Integer) = unsafe_convert(Ptr{T},x) + (i-1)*e
# fallback to the safe version if the subtype hasn't defined the required
# unsafe method.

# When getindex is called directly without the boundschecking trait, we need to
# first determine if it was the 'canonical' method that *must* be implemented
# by the type in order to throw an error (instead of a recursive stack overflow)
function getindex(A::AbstractArray, I...)
@_inline_meta
_getindex(linearindexing(A), A, I...)
getindex(BoundsCheckOn(), A, I...)
end
function unsafe_getindex(A::AbstractArray, I...)
function getindex(b::BoundsCheck, A::AbstractArray, I...)
@_inline_meta
_unsafe_getindex(linearindexing(A), A, I...)
_getindex(b, linearindexing(A), A, I...)
end
## Internal defitions
_getindex(::LinearFast, A::AbstractArray) = (@_inline_meta; getindex(A, 1))
_getindex(::LinearSlow, A::AbstractArray) = (@_inline_meta; _getindex(A, 1))
_unsafe_getindex(::LinearFast, A::AbstractArray) = (@_inline_meta; unsafe_getindex(A, 1))
_unsafe_getindex(::LinearSlow, A::AbstractArray) = (@_inline_meta; _unsafe_getindex(A, 1))
_getindex(::LinearIndexing, A::AbstractArray, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported")
_unsafe_getindex(::LinearIndexing, A::AbstractArray, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported")
_getindex(b::BoundsCheckOn, ::LinearFast, A::AbstractArray) = (@_inline_meta; getindex(A, 1))
_getindex(b::BoundsCheckOff, ::LinearFast, A::AbstractArray) = (@_inline_meta; getindex(b, A, 1))
_getindex(b::BoundsCheck, ::LinearSlow, A::AbstractArray) = (@_inline_meta; _getindex(b, A, 1))
_getindex(::BoundsCheck, ::LinearIndexing, A::AbstractArray, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported")

## LinearFast Scalar indexing
_getindex(::LinearFast, A::AbstractArray, I::Int) = error("indexing not defined for ", typeof(A))
function _getindex(::LinearFast, A::AbstractArray, I::Real...)
_getindex(::BoundsCheckOn, ::LinearFast, A::AbstractArray, I::Int) = error("indexing not defined for ", typeof(A))
_getindex(::BoundsCheckOff, ::LinearFast, A::AbstractArray, I::Int) = (@_inline_meta; getindex(A, I))
function _getindex(b::BoundsCheck, ::LinearFast, A::AbstractArray, I::Real...)
@_inline_meta
# We must check bounds for sub2ind; so we can then call unsafe_getindex
checkbounds(A, I...)
unsafe_getindex(A, sub2ind(size(A), to_index(I)...))
end
_unsafe_getindex(::LinearFast, A::AbstractArray, I::Int) = (@_inline_meta; getindex(A, I))
function _unsafe_getindex(::LinearFast, A::AbstractArray, I::Real...)
@_inline_meta
unsafe_getindex(A, sub2ind(size(A), to_index(I)...))
# We must check bounds for sub2ind; so afterwards we can turn off boundscheck
Bool(b) && checkbounds(A, I...)
getindex(BoundsCheckOff(), A, sub2ind(size(A), to_index(I)...))
end

# LinearSlow Scalar indexing
@generated function _getindex{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, I::Real...)
@generated function _getindex{T,AN}(b::BoundsCheck, ::LinearSlow, A::AbstractArray{T,AN}, I::Real...)
N = length(I)
if N == AN
:(error("indexing not defined for ", typeof(A)))
elseif N > AN
# Drop trailing ones
if N == AN && I == ntuple(i->Int, AN)
if b <: BoundsCheckOn
:(error("indexing not defined for ", typeof(A)))
else
:($(Expr(:meta, :inline)); getindex(A, I...))
end
elseif N >= AN
# Drop trailing ones and convert to_index
Isplat = Expr[:(to_index(I[$d])) for d = 1:AN]
Osplat = Expr[:(to_index(I[$d]) == 1) for d = AN+1:N]
Osplat = Expr[:(to_index(I[$d])) for d = AN+1:N]
quote
$(Expr(:meta, :inline))
(&)($(Osplat...)) || throw_boundserror(A, I)
getindex(A, $(Isplat...))
# Remove trailing indices; ensure all were 1 if bounds checks were on
Bool(b) && checkbounds(A, $(Isplat...), $(Osplat...))
getindex(BoundsCheckOff(), A, $(Isplat...))
end
else
# Expand the last index into the appropriate number of indices
Expand All @@ -472,85 +474,57 @@ end
quote
$(Expr(:meta, :inline))
# ind2sub requires all dimensions to be nonzero, so checkbounds first
checkbounds(A, I...)
Bool(b) && checkbounds(A, I...)
s = ind2sub($sz, to_index(I[$N]))
unsafe_getindex(A, $(Isplat...))
end
end
end
@generated function _unsafe_getindex{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, I::Real...)
N = length(I)
if N == AN
Isplat = Expr[:(to_index(I[$d])) for d = 1:N]
:($(Expr(:meta, :inline)); getindex(A, $(Isplat...)))
elseif N > AN
# Drop trailing dimensions (unchecked)
Isplat = Expr[:(to_index(I[$d])) for d = 1:AN]
quote
$(Expr(:meta, :inline))
unsafe_getindex(A, $(Isplat...))
end
else
# Expand the last index into the appropriate number of indices
Isplat = Expr[:(to_index(I[$d])) for d = 1:N-1]
for d=N:AN
push!(Isplat, :(s[$(d-N+1)]))
end
sz = Expr(:tuple)
sz.args = Expr[:(size(A, $d)) for d=N:AN]
quote
$(Expr(:meta, :inline))
s = ind2sub($sz, to_index(I[$N]))
unsafe_getindex(A, $(Isplat...))
getindex(BoundsCheckOff(), A, $(Isplat...))
end
end
end

## Setindex! is defined similarly. We first dispatch to an internal _setindex!
# function that allows dispatch on array storage
## Setindex! is defined identically. It'd be nice to use metaprogramming here
# but a necessary tool (tuple splatting) isn't available yet in bootstrap
function setindex!(A::AbstractArray, v, I...)
@_inline_meta
_setindex!(linearindexing(A), A, v, I...)
setindex!(BoundsCheckOn(), A, v, I...)
end
function unsafe_setindex!(A::AbstractArray, v, I...)
function setindex!(b::BoundsCheck, A::AbstractArray, v, I...)
@_inline_meta
_unsafe_setindex!(linearindexing(A), A, v, I...)
_setindex!(b, linearindexing(A), A, v, I...)
end
## Internal defitions
_setindex!(::LinearFast, A::AbstractArray, v) = (@_inline_meta; setindex!(A, v, 1))
_setindex!(::LinearSlow, A::AbstractArray, v) = (@_inline_meta; _setindex!(A, v, 1))
_unsafe_setindex!(::LinearFast, A::AbstractArray, v) = (@_inline_meta; unsafe_setindex!(A, v, 1))
_unsafe_setindex!(::LinearSlow, A::AbstractArray, v) = (@_inline_meta; _unsafe_setindex!(A, v, 1))
_setindex!(::LinearIndexing, A::AbstractArray, v, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported")
_unsafe_setindex!(::LinearIndexing, A::AbstractArray, v, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported")
_setindex!(b::BoundsCheckOn, ::LinearFast, A::AbstractArray, v) = (@_inline_meta; setindex!(A, v, 1))
_setindex!(b::BoundsCheckOff, ::LinearFast, A::AbstractArray, v) = (@_inline_meta; setindex!(b, A, v, 1))
_setindex!(b::BoundsCheck, ::LinearSlow, A::AbstractArray, v) = (@_inline_meta; _setindex!(b, A, v, 1))
_setindex!(::BoundsCheck, ::LinearIndexing, A::AbstractArray, v, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported")

## LinearFast Scalar indexing
_setindex!(::LinearFast, A::AbstractArray, v, I::Int) = error("indexed assignment not defined for ", typeof(A))
function _setindex!(::LinearFast, A::AbstractArray, v, I::Real...)
_setindex!(::BoundsCheckOn, ::LinearFast, A::AbstractArray, v, I::Int) = error("indexing not defined for ", typeof(A))
_setindex!(::BoundsCheckOff, ::LinearFast, A::AbstractArray, v, I::Int) = setindex!(A, v, I)
function _setindex!(b::BoundsCheck, ::LinearFast, A::AbstractArray, v, I::Real...)
@_inline_meta
# We must check bounds for sub2ind; so we can then call unsafe_setindex!
checkbounds(A, I...)
unsafe_setindex!(A, v, sub2ind(size(A), to_index(I)...))
end
_unsafe_setindex!(::LinearFast, A::AbstractArray, v, I::Int) = (@_inline_meta; setindex!(A, v, I))
function _unsafe_setindex!(::LinearFast, A::AbstractArray, v, I::Real...)
@_inline_meta
unsafe_setindex!(A, v, sub2ind(size(A), to_index(I)...))
# We must check bounds for sub2ind; so afterwards we can turn off boundscheck
Bool(b) && checkbounds(A, I...)
setindex!(BoundsCheckOff(), A, v, sub2ind(size(A), to_index(I)...))
end

# LinearSlow Scalar indexing
@generated function _setindex!{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, v, I::Real...)
@generated function _setindex!{T,AN}(b::BoundsCheck, ::LinearSlow, A::AbstractArray{T,AN}, v, I::Real...)
N = length(I)
if N == AN
:(error("indexed assignment not defined for ", typeof(A)))
elseif N > AN
# Drop trailing ones
if N == AN && I == ntuple(i->Int, AN)
if b <: BoundsCheckOn
:(error("indexing not defined for ", typeof(A)))
else
:($(Expr(:meta, :inline)); setindex!(A, v, I...))
end
elseif N >= AN
# Drop trailing ones and convert to_index
Isplat = Expr[:(to_index(I[$d])) for d = 1:AN]
Osplat = Expr[:(to_index(I[$d]) == 1) for d = AN+1:N]
Osplat = Expr[:(to_index(I[$d])) for d = AN+1:N]
quote
$(Expr(:meta, :inline))
(&)($(Osplat...)) || throw_boundserror(A, I)
setindex!(A, v, $(Isplat...))
# Remove trailing indices; ensure all were 1 if bounds checks were on
Bool(b) && checkbounds(A, $(Isplat...), $(Osplat...))
setindex!(BoundsCheckOff(), A, v, $(Isplat...))
end
else
# Expand the last index into the appropriate number of indices
Expand All @@ -563,36 +537,10 @@ end
sz.args = Expr[:(size(A, $d)) for d=N:AN]
quote
$(Expr(:meta, :inline))
checkbounds(A, I...)
s = ind2sub($sz, to_index(I[$N]))
unsafe_setindex!(A, v, $(Isplat...))
end
end
end
@generated function _unsafe_setindex!{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, v, I::Real...)
N = length(I)
if N == AN
Isplat = Expr[:(to_index(I[$d])) for d = 1:N]
:(setindex!(A, v, $(Isplat...)))
elseif N > AN
# Drop trailing dimensions (unchecked)
Isplat = Expr[:(to_index(I[$d])) for d = 1:AN]
quote
$(Expr(:meta, :inline))
unsafe_setindex!(A, v, $(Isplat...))
end
else
# Expand the last index into the appropriate number of indices
Isplat = Expr[:(to_index(I[$d])) for d = 1:N-1]
for d=N:AN
push!(Isplat, :(s[$(d-N+1)]))
end
sz = Expr(:tuple)
sz.args = Expr[:(size(A, $d)) for d=N:AN]
quote
$(Expr(:meta, :inline))
# ind2sub requires all dimensions to be nonzero, so checkbounds first
Bool(b) && checkbounds(A, I...)
s = ind2sub($sz, to_index(I[$N]))
unsafe_setindex!(A, v, $(Isplat...))
setindex!(BoundsCheckOff(), A, v, $(Isplat...))
end
end
end
Expand Down
36 changes: 18 additions & 18 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -295,24 +295,24 @@ getindex(A::Array, i1::Real, i2::Real, i3::Real) = arrayref(A, to_index(i1), to_
getindex(A::Array, i1::Real, i2::Real, i3::Real, i4::Real) = arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4))
getindex(A::Array, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...)

unsafe_getindex(A::Array, i1::Real) = @inbounds return arrayref(A, to_index(i1))
unsafe_getindex(A::Array, i1::Real, i2::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2))
unsafe_getindex(A::Array, i1::Real, i2::Real, i3::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3))
unsafe_getindex(A::Array, i1::Real, i2::Real, i3::Real, i4::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4))
unsafe_getindex(A::Array, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...)
getindex(::BoundsCheckOff, A::Array, i1::Real) = @inbounds return arrayref(A, to_index(i1))
getindex(::BoundsCheckOff, A::Array, i1::Real, i2::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2))
getindex(::BoundsCheckOff, A::Array, i1::Real, i2::Real, i3::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3))
getindex(::BoundsCheckOff, A::Array, i1::Real, i2::Real, i3::Real, i4::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4))
getindex(::BoundsCheckOff, A::Array, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...)

# Faster contiguous indexing using copy! for UnitRange and Colon
getindex(A::Array, I::UnitRange{Int}) = (checkbounds(A, I); unsafe_getindex(A, I))
function unsafe_getindex(A::Array, I::UnitRange{Int})
function getindex(b::BoundsCheck, A::Array, I::UnitRange{Int})
Bool(b) && checkbounds(A, I)
lI = length(I)
X = similar(A, lI)
if lI > 0
unsafe_copy!(X, 1, A, first(I), lI)
end
return X
end
getindex(A::Array, c::Colon) = unsafe_getindex(A, c)
function unsafe_getindex(A::Array, ::Colon)
function getindex(::BoundsCheck, A::Array, ::Colon)
# Colon is always inbounds
lI = length(A)
X = similar(A, lI)
if lI > 0
Expand All @@ -333,11 +333,11 @@ setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real) = arrayset(A, convert
setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real) = arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4))
setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...)

unsafe_setindex!{T}(A::Array{T}, x, i1::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1))
unsafe_setindex!{T}(A::Array{T}, x, i1::Real, i2::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2))
unsafe_setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3))
unsafe_setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4))
unsafe_setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...)
setindex!{T}(::BoundsCheckOff, A::Array{T}, x, i1::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1))
setindex!{T}(::BoundsCheckOff, A::Array{T}, x, i1::Real, i2::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2))
setindex!{T}(::BoundsCheckOff, A::Array{T}, x, i1::Real, i2::Real, i3::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3))
setindex!{T}(::BoundsCheckOff, A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4))
setindex!{T}(::BoundsCheckOff, A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...)

# These are redundant with the abstract fallbacks but needed for bootstrap
function setindex!(A::Array, x, I::AbstractVector{Int})
Expand All @@ -364,17 +364,17 @@ function setindex!(A::Array, X::AbstractArray, I::AbstractVector{Int})
end

# Faster contiguous setindex! with copy!
setindex!{T}(A::Array{T}, X::Array{T}, I::UnitRange{Int}) = (checkbounds(A, I); unsafe_setindex!(A, X, I))
function unsafe_setindex!{T}(A::Array{T}, X::Array{T}, I::UnitRange{Int})
function setindex!{T}(b::BoundsCheck, A::Array{T}, X::Array{T}, I::UnitRange{Int})
Bool(b) && checkbounds(A, I)
lI = length(I)
setindex_shape_check(X, lI)
if lI > 0
unsafe_copy!(A, first(I), X, 1, lI)
end
return A
end
setindex!{T}(A::Array{T}, X::Array{T}, c::Colon) = unsafe_setindex!(A, X, c)
function unsafe_setindex!{T}(A::Array{T}, X::Array{T}, ::Colon)
function setindex!{T}(::BoundsCheck, A::Array{T}, X::Array{T}, ::Colon)
# Colon is always inbounds, no need to checkbounds
lI = length(A)
setindex_shape_check(X, lI)
if lI > 0
Expand Down
20 changes: 11 additions & 9 deletions base/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,10 @@ bitpack{T,N}(A::AbstractArray{T,N}) = convert(BitArray{N}, A)
return r
end

@inline getindex(B::BitArray, i::Int) = (checkbounds(B, i); unsafe_getindex(B, i))
@inline unsafe_getindex(B::BitArray, i::Int) = unsafe_bitgetindex(B.chunks, i)
@inline function getindex(b::BoundsCheck, B::BitArray, i::Int)
Bool(b) && checkbounds(B, i)
unsafe_bitgetindex(B.chunks, i)
end

## Indexing: setindex! ##

Expand All @@ -365,8 +367,8 @@ end
end
end

setindex!(B::BitArray, x, i::Int) = (checkbounds(B, i); unsafe_setindex!(B, x, i))
@inline function unsafe_setindex!(B::BitArray, x, i::Int)
@inline function setindex!(b::BoundsCheck, B::BitArray, x, i::Int)
Bool(b) && checkbounds(B, i);
unsafe_bitsetindex!(B.chunks, convert(Bool, x), i)
return B
end
Expand All @@ -377,8 +379,8 @@ end
# we can't and must use getindex, otherwise silent corruption can happen)

# When indexing with a BitArray, we can operate whole chunks at a time for a ~100x gain
setindex!(B::BitArray, x, I::BitArray) = (checkbounds(B, I); unsafe_setindex!(B, x, I))
function unsafe_setindex!(B::BitArray, x, I::BitArray)
function setindex!(b::BoundsCheck, B::BitArray, x, I::BitArray)
Bool(b) && checkbounds(B, I)
y = convert(Bool, x)
Bc = B.chunks
Ic = I.chunks
Expand All @@ -397,8 +399,8 @@ end

# Assigning an array of bools is more complicated, but we can still do some
# work on chunks by combining X and I 64 bits at a time to improve perf by ~40%
setindex!(B::BitArray, X::AbstractArray, I::BitArray) = (checkbounds(B, I); unsafe_setindex!(B, X, I))
function unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray)
function setindex!(b::BoundsCheck, B::BitArray, X::AbstractArray, I::BitArray)
Bool(b) && checkbounds(B, I)
Bc = B.chunks
Ic = I.chunks
length(Bc) == length(Ic) || throw_boundserror(B, I)
Expand All @@ -414,7 +416,7 @@ function unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray)
for j = 1:(i < lc ? 64 : last_chunk_len)
if Imsk & u != 0
lx < c && throw_setindex_mismatch(X, c)
x = convert(Bool, unsafe_getindex(X, c))
x = convert(Bool, getindex(BoundsCheckOff(), X, c))
if x
C |= u
else
Expand Down
6 changes: 3 additions & 3 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -494,9 +494,9 @@ _ensure_vector(A::AbstractArray) = vec(A)
_ensure_vector(A) = A
_ensure_vectors() = ()
_ensure_vectors(A, As...) = (_ensure_vector(A), _ensure_vectors(As...)...)
function _unsafe_setindex!(l::LinearIndexing, A::AbstractArray, x, J::Union{Real,AbstractArray,Colon}...)
depwarn("multidimensional indexed assignment with multidimensional arrays is deprecated, use vec to convert indices to vectors", :_unsafe_setindex!)
_unsafe_setindex!(l, A, x, _ensure_vectors(J...)...)
function _setindex!(b::BoundsCheck, l::LinearIndexing, A::AbstractArray, x, J::Union{Real,AbstractArray,Colon}...)
depwarn("multidimensional indexed assignment with multidimensional arrays is deprecated, use vec to convert indices to vectors", :_setindex!)
_setindex!(b, l, A, x, _ensure_vectors(J...)...)
end

# 11554
Expand Down
7 changes: 7 additions & 0 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,10 @@ const (:) = Colon()
# For passing constants through type inference
immutable Val{T}
end

# A trait for bounds check dispatch
abstract BoundsCheck
immutable BoundsCheckOff <: BoundsCheck end
immutable BoundsCheckOn <: BoundsCheck end
Bool(::BoundsCheckOn) = true
Bool(::BoundsCheckOff) = false
Loading