Skip to content

Commit

Permalink
Safer, extensible ﹫inbounds
Browse files Browse the repository at this point in the history
  • Loading branch information
simonster committed Sep 4, 2014
1 parent 49d4132 commit 4516065
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 45 deletions.
72 changes: 42 additions & 30 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -241,21 +241,25 @@ collect(itr) = collect(eltype(itr), itr)

## Indexing: getindex ##

getindex(a::Array) = arrayref(a,1)

getindex(A::Array, i0::Real) = arrayref(A,to_index(i0))
getindex(A::Array, i0::Real, i1::Real) = arrayref(A,to_index(i0),to_index(i1))
getindex(A::Array, i0::Real, i1::Real, i2::Real) =
arrayref(A,to_index(i0),to_index(i1),to_index(i2))
getindex(A::Array, i0::Real, i1::Real, i2::Real, i3::Real) =
arrayref(A,to_index(i0),to_index(i1),to_index(i2),to_index(i3))
getindex(A::Array, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real) =
arrayref(A,to_index(i0),to_index(i1),to_index(i2),to_index(i3),to_index(i4))
getindex(A::Array, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real) =
arrayref(A,to_index(i0),to_index(i1),to_index(i2),to_index(i3),to_index(i4),to_index(i5))

getindex(A::Array, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real, I::Real...) =
arrayref(A,to_index(i0),to_index(i1),to_index(i2),to_index(i3),to_index(i4),to_index(i5),to_index(I)...)
for (getindexfn, transform) in ((:getindex, x->x), (:unsafe_getindex, x->:(@boundscheck false return $x)))
@eval begin
$getindexfn(a::Array) = $(transform(:(arrayref(a,1))))

$getindexfn(A::Array, i0::Real) = $(transform(:(arrayref(A,to_index(i0)))))
$getindexfn(A::Array, i0::Real, i1::Real) = $(transform(:(arrayref(A,to_index(i0),to_index(i1)))))
$getindexfn(A::Array, i0::Real, i1::Real, i2::Real) =
$(transform(:(arrayref(A,to_index(i0),to_index(i1),to_index(i2)))))
$getindexfn(A::Array, i0::Real, i1::Real, i2::Real, i3::Real) =
$(transform(:(arrayref(A,to_index(i0),to_index(i1),to_index(i2),to_index(i3)))))
$getindexfn(A::Array, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real) =
$(transform(:(arrayref(A,to_index(i0),to_index(i1),to_index(i2),to_index(i3),to_index(i4)))))
$getindexfn(A::Array, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real) =
$(transform(:(arrayref(A,to_index(i0),to_index(i1),to_index(i2),to_index(i3),to_index(i4),to_index(i5)))))

$getindexfn(A::Array, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real, I::Real...) =
$(transform(:(arrayref(A,to_index(i0),to_index(i1),to_index(i2),to_index(i3),to_index(i4),to_index(i5),to_index(I)...))))
end
end

# Fast copy using copy! for UnitRange
function getindex(A::Array, I::UnitRange{Int})
Expand All @@ -278,6 +282,8 @@ function getindex(A::Range, I::AbstractVector{Bool})
return [ A[i] for i in to_index(I) ]
end

unsafe_getindex(args...) = getindex(args...)


# logical indexing

Expand All @@ -302,21 +308,26 @@ getindex(A::Array, I::AbstractArray{Bool}) = getindex_bool_1d(A, I)


## Indexing: setindex! ##
setindex!{T}(A::Array{T}, x) = arrayset(A, convert(T,x), 1)

setindex!{T}(A::Array{T}, x, i0::Real) = arrayset(A, convert(T,x), to_index(i0))
setindex!{T}(A::Array{T}, x, i0::Real, i1::Real) =
arrayset(A, convert(T,x), to_index(i0), to_index(i1))
setindex!{T}(A::Array{T}, x, i0::Real, i1::Real, i2::Real) =
arrayset(A, convert(T,x), to_index(i0), to_index(i1), to_index(i2))
setindex!{T}(A::Array{T}, x, i0::Real, i1::Real, i2::Real, i3::Real) =
arrayset(A, convert(T,x), to_index(i0), to_index(i1), to_index(i2), to_index(i3))
setindex!{T}(A::Array{T}, x, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real) =
arrayset(A, convert(T,x), to_index(i0), to_index(i1), to_index(i2), to_index(i3), to_index(i4))
setindex!{T}(A::Array{T}, x, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real) =
arrayset(A, convert(T,x), to_index(i0), to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(i5))
setindex!{T}(A::Array{T}, x, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real, I::Real...) =
arrayset(A, convert(T,x), to_index(i0), to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(i5), to_index(I)...)

for (setindexfn, transform) in ((:setindex!, x->x), (:unsafe_setindex!, x->:(@boundscheck false return $x)))
@eval begin
$setindexfn{T}(A::Array{T}, x) = $(transform(:(arrayset(A, convert(T,x), 1))))

$setindexfn{T}(A::Array{T}, x, i0::Real) = $(transform(:(arrayset(A, convert(T,x), to_index(i0)))))
$setindexfn{T}(A::Array{T}, x, i0::Real, i1::Real) =
$(transform(:(arrayset(A, convert(T,x), to_index(i0), to_index(i1)))))
$setindexfn{T}(A::Array{T}, x, i0::Real, i1::Real, i2::Real) =
$(transform(:(arrayset(A, convert(T,x), to_index(i0), to_index(i1), to_index(i2)))))
$setindexfn{T}(A::Array{T}, x, i0::Real, i1::Real, i2::Real, i3::Real) =
$(transform(:(arrayset(A, convert(T,x), to_index(i0), to_index(i1), to_index(i2), to_index(i3)))))
$setindexfn{T}(A::Array{T}, x, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real) =
$(transform(:(arrayset(A, convert(T,x), to_index(i0), to_index(i1), to_index(i2), to_index(i3), to_index(i4)))))
$setindexfn{T}(A::Array{T}, x, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real) =
$(transform(:(arrayset(A, convert(T,x), to_index(i0), to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(i5)))))
$setindexfn{T}(A::Array{T}, x, i0::Real, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real, I::Real...) =
$(transform(:(arrayset(A, convert(T,x), to_index(i0), to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(i5), to_index(I)...))))
end
end

function setindex!{T<:Real}(A::Array, x, I::AbstractVector{T})
for i in I
Expand Down Expand Up @@ -348,6 +359,7 @@ function setindex!{T<:Real}(A::Array, X::AbstractArray, I::AbstractVector{T})
return A
end

unsafe_setindex!(args...) = setindex!(args...)

# logical indexing

Expand Down
114 changes: 112 additions & 2 deletions base/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,118 @@ macro boundscheck(yesno,blk)
$(Expr(:boundscheck,:pop)))
end

macro inbounds(blk)
:(@boundscheck false $(esc(blk)))

expand_end!(var, dim::Int, sym::Symbol) =
sym === :end ? (:(size($var, $dim))) : sym
function expand_end!(var, dim::Int, ast::Expr)
if ast.head === :ref
expand_ref!(ast)
else
args = ast.args
for i = 1:arraylen(args)
arrayset(args, expand_end!(var, dim, arrayref(ast.args, i)), i)
end
end
ast
end
expand_end!(::Any, ::Int, x) = x

function expand_ref!(ast::Expr)
args = ast.args
var = arrayref(args, 1)
for i = 2:arraylen(args)
arg = arrayref(args, i)
arrayset(args, arg === :(:) ? :(1:size($var, $(i-1))) : expand_end!(var, i-1, arg), i)
end
end

function rewrite_ref(getindexfn, setindexfn, ast::Expr)
head = ast.head
if head === :ref
expand_ref!(ast)
out = Expr(:call)
out.args = Array(Any, arraylen(ast.args)+1)
arrayset(out.args, getindexfn, 1)
for i = 1:arraylen(ast.args)
arrayset(out.args, arrayref(ast.args, i), i+1)
end
out
elseif head === :(=) && isa(arrayref(ast.args, 1), Expr)
lhs = arrayref(ast.args, 1)
rhs = arrayref(ast.args, 2)
if isa(lhs, Expr) && (lhs.head === :tuple || lhs.head === :ref)
if lhs.head === :tuple
out = Expr(:block)
if isa(rhs, Expr) && rhs.head === :tuple && arraylen(rhs.args) == arraylen(lhs.args)
n = arraylen(lhs.args)
out.args = Array(Any, 2*n)
for i = 1:n
x = gensym()
arrayset(out.args, :($x = $(rewrite_ref(getindexfn, setindexfn, arrayref(rhs.args, i)))), i)
arrayset(out.args, rewrite_ref(getindexfn, setindexfn, :($(arrayref(lhs.args, i)) = $x)), n+i)
end
else
out.args = Array(Any, arraylen(lhs.args)+1)
rhsval = gensym()
stateval = gensym()
arrayset(out.args, quote
$rhsval = $(rewrite_ref(getindexfn, setindexfn, rhs))
$stateval = $start($rhsval)
end, 1)
for i = 1:arraylen(lhs.args)
nextval = gensym()
arrayset(out.args, quote
$nextval = $indexed_next($rhsval, $i, $stateval)
$(rewrite_ref(getindexfn, setindexfn,
:($(arrayref(lhs.args, i)) = $tupleref($nextval, 1))))
$stateval = $tupleref($nextval, 2)
end, i+1)
end
end
else
expand_ref!(lhs)
out = Expr(:call)
out.args = Array(Any, arraylen(lhs.args)+2)
arrayset(out.args, setindexfn, 1)
arrayset(out.args, arrayref(lhs.args, 1), 2)
arrayset(out.args, rewrite_ref(getindexfn, setindexfn, rhs), 3)
for i = 2:arraylen(lhs.args)
arrayset(out.args, rewrite_ref(getindexfn, setindexfn, arrayref(lhs.args, i)), i+2)
end
end
out
else
:($lhs = $(rewrite_ref(getindexfn, setindexfn, rhs)))
end
elseif (head === :(+=) || head === :(-=) || head === :(*=) ||
head === :(.*=) || head === :(/=) || head === :(./=) || head === :(//=) ||
head === :(.//=) || head === :(\=) || head === :(.\=) || head === :(.+=) ||
head === :(.-=) || head === :(^=) || head === :(.^=) || head === :(%=) ||
head === :(.%=) || head === :(|=) || head === :(&=) || head === :($=) ||
head === :(<<=) || head === :(>>=) || head === :(>>>=)) &&
isa(arrayref(ast.args, 1), Expr)
lhs = arrayref(ast.args, 1)
rhs = arrayref(ast.args, 2)
nonupdating_sym = head === :+= ? (:+) : head === :-= ? (:-) : head === :*= ? (:*) :
head === :.*= ? (:.*) : head === :/= ? (:/) : head === :./= ? (:./) :
head === ://= ? (://) : head === :.//= ? (:.//) : head === :\= ? (:\) :
head === :.\= ? (:.\) : head === :.+= ? (:.+) : head === :.-= ? (:.-) :
head === :^= ? (:^) : head === :.^= ? (:.^) : head === :%= ? (:%) :
head === :.%= ? (:.%) : head === :|= ? (:|) : head === :&= ? (:&) :
head === :$= ? (:$) : head === :<<= ? (:<<) : head === :>>= ? (:>>) : (:>>>)
out = :($lhs = $nonupdating_sym($lhs, $rhs))
rewrite_ref(getindexfn, setindexfn, out)
else
for i = 1:arraylen(ast.args)
arrayset(ast.args, rewrite_ref(getindexfn, setindexfn, arrayref(ast.args, i)), i)
end
ast
end
end
rewrite_ref(getindexfn, setindexfn, ast) = ast

macro inbounds(ex)
esc(rewrite_ref(:(Base.unsafe_getindex), :(Base.unsafe_setindex!), ex))
end

macro label(name::Symbol)
Expand Down
4 changes: 3 additions & 1 deletion base/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,9 @@ end
## Indexing: getindex ##

function unsafe_bitgetindex(Bc::Vector{Uint64}, i::Int)
return (Bc[@_div64(i-1)+1] & (uint64(1)<<@_mod64(i-1))) != 0
return @inbounds (Bc[@_div64(i-1)+1] & (uint64(1)<<@_mod64(i-1))) != 0
end
unsafe_getindex(v::BitArray, ind::Int) = Base.unsafe_bitgetindex(v.chunks, ind)

function getindex(B::BitArray, i::Int)
1 <= i <= length(B) || throw(BoundsError())
Expand Down Expand Up @@ -408,6 +409,7 @@ function unsafe_bitsetindex!(Bc::Array{Uint64}, x::Bool, i::Int)
end
end
end
unsafe_setindex!(v::BitArray, x::Bool, ind::Int) = (Base.unsafe_bitsetindex!(v.chunks, x, ind); v)

setindex!(B::BitArray, x) = setindex!(B, convert(Bool,x), 1)

Expand Down
10 changes: 0 additions & 10 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,6 @@
nothing
end

unsafe_getindex(v::Real, ind::Int) = v
unsafe_getindex(v::Range, ind::Int) = first(v) + (ind-1)*step(v)
unsafe_getindex(v::BitArray, ind::Int) = Base.unsafe_bitgetindex(v.chunks, ind)
unsafe_getindex(v::AbstractArray, ind::Int) = v[ind]
unsafe_getindex(v, ind::Real) = unsafe_getindex(v, to_index(ind))

unsafe_setindex!{T}(v::AbstractArray{T}, x::T, ind::Int) = (v[ind] = x; v)
unsafe_setindex!(v::BitArray, x::Bool, ind::Int) = (Base.unsafe_bitsetindex!(v.chunks, x, ind); v)
unsafe_setindex!{T}(v::AbstractArray{T}, x::T, ind::Real) = unsafe_setindex!(v, x, to_index(ind))

# Version that uses cartesian indexing for src
@ngenerate N typeof(dest) function _getindex!(dest::Array, src::AbstractArray, I::NTuple{N,Union(Int,AbstractVector)}...)
checksize(dest, I...)
Expand Down
4 changes: 2 additions & 2 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ function vcat{T}(r::Range{T})
a = Array(T,n)
i = 1
for x in r
@inbounds a[i] = x
@boundscheck false a[i] = x
i += 1
end
return a
Expand All @@ -523,7 +523,7 @@ function vcat{T}(rs::Range{T}...)
i = 1
for r in rs
for x in r
@inbounds a[i] = x
@boundscheck false a[i] = x
i += 1
end
end
Expand Down

0 comments on commit 4516065

Please sign in to comment.