Skip to content

Commit efa23ad

Browse files
authored
Merge pull request #17137 from JuliaLang/teh/safer_indices
Refactor API for unconventionally-indexed arrays
2 parents 756faa6 + 1b2f11f commit efa23ad

25 files changed

+385
-462
lines changed

base/abstractarray.jl

Lines changed: 81 additions & 203 deletions
Large diffs are not rendered by default.

base/arraymath.jl

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,17 +211,17 @@ function flipdim{T}(A::Array{T}, d::Integer)
211211
end
212212

213213
function rotl90(A::AbstractMatrix)
214-
B = similar_transpose(A)
215-
ind2 = indices(A,2)
214+
ind1, ind2 = indices(A)
215+
B = similar(A, (ind2,ind1))
216216
n = first(ind2)+last(ind2)
217217
for i=indices(A,1), j=ind2
218218
B[n-j,i] = A[i,j]
219219
end
220220
return B
221221
end
222222
function rotr90(A::AbstractMatrix)
223-
B = similar_transpose(A)
224-
ind1 = indices(A,1)
223+
ind1, ind2 = indices(A)
224+
B = similar(A, (ind2,ind1))
225225
m = first(ind1)+last(ind1)
226226
for i=ind1, j=indices(A,2)
227227
B[j,m-i] = A[i,j]
@@ -246,10 +246,6 @@ end
246246
rotr90(A::AbstractMatrix, k::Integer) = rotl90(A,-k)
247247
rot180(A::AbstractMatrix, k::Integer) = mod(k, 2) == 1 ? rot180(A) : copy(A)
248248

249-
similar_transpose(A::AbstractMatrix) = similar_transpose(indicesbehavior(A), A)
250-
similar_transpose(::IndicesStartAt1, A::AbstractMatrix) = similar(A, (size(A,2), size(A,1)))
251-
similar_transpose(::IndicesBehavior, A::AbstractMatrix) = similar(A, (indices(A,2), indices(A,1)))
252-
253249
## Transpose ##
254250
transpose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(transpose, B, A)
255251
ctranspose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(ctranspose, B, A)
@@ -315,11 +311,13 @@ function ccopy!(B, A)
315311
end
316312

317313
function transpose(A::AbstractMatrix)
318-
B = similar_transpose(A)
314+
ind1, ind2 = indices(A)
315+
B = similar(A, (ind2, ind1))
319316
transpose!(B, A)
320317
end
321318
function ctranspose(A::AbstractMatrix)
322-
B = similar_transpose(A)
319+
ind1, ind2 = indices(A)
320+
B = similar(A, (ind2, ind1))
323321
ctranspose!(B, A)
324322
end
325323
ctranspose{T<:Real}(A::AbstractVecOrMat{T}) = transpose(A)

base/bitarray.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ end
4747

4848
isassigned{N}(B::BitArray{N}, i::Int) = 1 <= i <= length(B)
4949

50+
linearindexing{A<:BitArray}(::Type{A}) = LinearFast()
51+
5052
## aux functions ##
5153

5254
const _msk64 = ~UInt64(0)

base/broadcast.jl

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module Broadcast
44

55
using Base.Cartesian
6-
using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, shape, linearindices, allocate_for, tail, dimlength
6+
using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, linearindices, to_shape, tail, dimlength, OneTo
77
import Base: .+, .-, .*, ./, .\, .//, .==, .<, .!=, .<=, , .%, .<<, .>>, .^
88
export broadcast, broadcast!, bitbroadcast
99
export broadcast_getindex, broadcast_setindex!
@@ -13,8 +13,8 @@ export broadcast_getindex, broadcast_setindex!
1313
## Calculate the broadcast shape of the arguments, or error if incompatible
1414
# array inputs
1515
broadcast_shape() = ()
16-
broadcast_shape(A) = shape(A)
17-
@inline broadcast_shape(A, B...) = broadcast_shape((), shape(A), map(shape, B)...)
16+
broadcast_shape(A) = indices(A)
17+
@inline broadcast_shape(A, B...) = broadcast_shape((), indices(A), map(indices, B)...)
1818
# shape inputs
1919
broadcast_shape(shape::Tuple) = shape
2020
@inline broadcast_shape(shape::Tuple, shape1::Tuple, shapes::Tuple...) = broadcast_shape(_bcs((), shape, shape1), shapes...)
@@ -39,8 +39,10 @@ _bcsm(a::Number, b::Number) = a == b || b == 1
3939
## Check that all arguments are broadcast compatible with shape
4040
## Check that all arguments are broadcast compatible with shape
4141
# comparing one input against a shape
42+
check_broadcast_shape(::Tuple{}) = nothing
43+
check_broadcast_shape(::Tuple{}, A::Union{AbstractArray,Number}) = check_broadcast_shape((), indices(A))
4244
check_broadcast_shape(shp) = nothing
43-
check_broadcast_shape(shp, A) = check_broadcast_shape(shp, shape(A))
45+
check_broadcast_shape(shp, A) = check_broadcast_shape(shp, indices(A))
4446
check_broadcast_shape(::Tuple{}, ::Tuple{}) = nothing
4547
check_broadcast_shape(shp, ::Tuple{}) = nothing
4648
check_broadcast_shape(::Tuple{}, Ashp::Tuple) = throw(DimensionMismatch("cannot broadcast array to have fewer dimensions"))
@@ -133,7 +135,7 @@ end
133135
end
134136

135137
@inline function broadcast!{nargs}(f, B::AbstractArray, As::Vararg{Any,nargs})
136-
check_broadcast_shape(shape(B), As...)
138+
check_broadcast_shape(indices(B), As...)
137139
sz = size(B)
138140
mapindex = map(x->newindexer(sz, x), As)
139141
_broadcast!(f, B, mapindex, As, Val{nargs})
@@ -179,20 +181,20 @@ function broadcast_t(f, ::Type{Any}, As...)
179181
shp = broadcast_shape(As...)
180182
iter = CartesianRange(shp)
181183
if isempty(iter)
182-
return allocate_for(Array{Union{}}, As, shp)
184+
return similar(Array{Union{}}, shp)
183185
end
184186
nargs = length(As)
185187
sz = size(iter)
186188
indexmaps = map(x->newindexer(sz, x), As)
187189
st = start(iter)
188190
I, st = next(iter, st)
189191
val = f([ As[i][newindex(I, indexmaps[i])] for i=1:nargs ]...)
190-
B = allocate_for(Array{typeof(val)}, As, shp)
192+
B = similar(Array{typeof(val)}, shp)
191193
B[I] = val
192194
return _broadcast!(f, B, indexmaps, As, Val{nargs}, iter, st, 1)
193195
end
194196

195-
@inline broadcast_t(f, T, As...) = broadcast!(f, allocate_for(Array{T}, As, broadcast_shape(As...)), As...)
197+
@inline broadcast_t(f, T, As...) = broadcast!(f, similar(Array{T}, broadcast_shape(As...)), As...)
196198

197199
@inline broadcast(f, As...) = broadcast_t(f, promote_eltype_op(f, As...), As...)
198200

@@ -215,15 +217,15 @@ function broadcast(f, As...)
215217
end
216218
=#
217219

218-
@inline bitbroadcast(f, As...) = broadcast!(f, allocate_for(BitArray, As, broadcast_shape(As...)), As...)
220+
@inline bitbroadcast(f, As...) = broadcast!(f, similar(BitArray, broadcast_shape(As...)), As...)
219221

220-
broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(Array{eltype(src)}(broadcast_shape(I...)), src, I...)
222+
broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(Array{eltype(src)}(to_shape(broadcast_shape(I...))), src, I...)
221223
@generated function broadcast_getindex!(dest::AbstractArray, src::AbstractArray, I::AbstractArray...)
222224
N = length(I)
223225
Isplat = Expr[:(I[$d]) for d = 1:N]
224226
quote
225227
@nexprs $N d->(I_d = I[d])
226-
check_broadcast_shape(size(dest), $(Isplat...)) # unnecessary if this function is never called directly
228+
check_broadcast_shape(indices(dest), $(Isplat...)) # unnecessary if this function is never called directly
227229
checkbounds(src, $(Isplat...))
228230
@nloops $N i dest d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin
229231
@nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k)
@@ -240,22 +242,22 @@ end
240242
@nexprs $N d->(I_d = I[d])
241243
checkbounds(A, $(Isplat...))
242244
shape = broadcast_shape($(Isplat...))
243-
@nextract $N shape d->(length(shape) < d ? 1 : shape[d])
245+
@nextract $N shape d->(length(shape) < d ? OneTo(1) : shape[d])
244246
if !isa(x, AbstractArray)
245-
@nloops $N i d->(1:shape_d) d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin
247+
xA = convert(eltype(A), x)
248+
@nloops $N i d->shape_d d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin
246249
@nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k)
247-
@inbounds (@nref $N A J) = x
250+
@inbounds (@nref $N A J) = xA
248251
end
249252
else
250253
X = x
251-
# To call setindex_shape_check, we need to create fake 1-d indexes of the proper size
252-
@nexprs $N d->(fakeI_d = 1:shape_d)
253-
@ncall $N Base.setindex_shape_check X shape
254-
k = 1
255-
@nloops $N i d->(1:shape_d) d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin
256-
@nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k)
257-
@inbounds (@nref $N A J) = X[k]
258-
k += 1
254+
@nexprs $N d->(shapelen_d = dimlength(shape_d))
255+
@ncall $N Base.setindex_shape_check X shapelen
256+
Xstate = start(X)
257+
@inbounds @nloops $N i d->shape_d d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin
258+
@nexprs $N k->(J_k = @nref $N I_k d->j_d_k)
259+
x_el, Xstate = next(X, Xstate)
260+
(@nref $N A J) = x_el
259261
end
260262
end
261263
A
@@ -271,22 +273,22 @@ end
271273

272274
eltype_plus(As::AbstractArray...) = promote_eltype_op(+, As...)
273275

274-
.+(As::AbstractArray...) = broadcast!(+, Array{eltype_plus(As...)}(broadcast_shape(As...)), As...)
276+
.+(As::AbstractArray...) = broadcast!(+, Array{eltype_plus(As...)}(to_shape(broadcast_shape(As...))), As...)
275277

276278
function .-(A::AbstractArray, B::AbstractArray)
277-
broadcast!(-, Array{promote_op(-, eltype(A), eltype(B))}(broadcast_shape(A,B)), A, B)
279+
broadcast!(-, Array{promote_op(-, eltype(A), eltype(B))}(to_shape(broadcast_shape(A,B))), A, B)
278280
end
279281

280282
eltype_mul(As::AbstractArray...) = promote_eltype_op(*, As...)
281283

282-
.*(As::AbstractArray...) = broadcast!(*, Array{eltype_mul(As...)}(broadcast_shape(As...)), As...)
284+
.*(As::AbstractArray...) = broadcast!(*, Array{eltype_mul(As...)}(to_shape(broadcast_shape(As...))), As...)
283285

284286
function ./(A::AbstractArray, B::AbstractArray)
285-
broadcast!(/, Array{promote_op(/, eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B)
287+
broadcast!(/, Array{promote_op(/, eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B)
286288
end
287289

288290
function .\(A::AbstractArray, B::AbstractArray)
289-
broadcast!(\, Array{promote_op(\, eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B)
291+
broadcast!(\, Array{promote_op(\, eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B)
290292
end
291293

292294
typealias RatIntT{T<:Integer} Union{Type{Rational{T}},Type{T}}
@@ -296,11 +298,11 @@ type_rdiv{T<:Integer,S<:Integer}(::RatIntT{T}, ::RatIntT{S}) =
296298
type_rdiv{T<:Integer,S<:Integer}(::CRatIntT{T}, ::CRatIntT{S}) =
297299
Complex{Rational{promote_type(T,S)}}
298300
function .//(A::AbstractArray, B::AbstractArray)
299-
broadcast!(//, Array{type_rdiv(eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B)
301+
broadcast!(//, Array{type_rdiv(eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B)
300302
end
301303

302304
function .^(A::AbstractArray, B::AbstractArray)
303-
broadcast!(^, Array{promote_op(^, eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B)
305+
broadcast!(^, Array{promote_op(^, eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B)
304306
end
305307

306308
# ## element-wise comparison operators returning BitArray ##
@@ -363,10 +365,10 @@ for (f, scalarf) in ((:.==, :(==)),
363365
:((A,ind)->A), :((B,ind)->B[ind])),
364366
(:AbstractArray, :Any, :A,
365367
:((A,ind)->A[ind]), :((B,ind)->B)))
366-
shape = :(shape($active))
368+
shape = :(indices($active))
367369
@eval begin
368370
function ($f)(A::$sigA, B::$sigB)
369-
P = allocate_for(BitArray, $active, $shape)
371+
P = similar(BitArray, $shape)
370372
F = parent(P)
371373
l = length(F)
372374
l == 0 && return F

base/deprecated.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,12 @@ function first(::Colon)
766766
1
767767
end
768768

769+
# Not exported, but may be useful just in case
770+
function Broadcast.check_broadcast_shape(sz::Dims, As::Union{AbstractArray,Number}...)
771+
depwarn("check_broadcast_shape(size(A), B...) should be replaced with check_broadcast_shape(indices(A), B...)", :check_broadcast_shape)
772+
Broadcast.check_broadcast_shape(map(OneTo, sz), As...)
773+
end
774+
769775
@deprecate slice view
770776
@deprecate sub view
771777

base/essentials.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ start(v::SimpleVector) = 1
171171
next(v::SimpleVector,i) = (v[i],i+1)
172172
done(v::SimpleVector,i) = (i > v.length)
173173
isempty(v::SimpleVector) = (v.length == 0)
174-
indices(v::SimpleVector, d) = d == 1 ? (1:length(v)) : (1:1)
174+
indices(v::SimpleVector) = (OneTo(length(v)),)
175175
linearindices(v::SimpleVector) = indices(v, 1)
176176

177177
function ==(v1::SimpleVector, v2::SimpleVector)

base/exports.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export
2828
# Types
2929
AbstractChannel,
3030
AbstractMatrix,
31+
AbstractUnitRange,
3132
AbstractVector,
3233
AbstractVecOrMat,
3334
Array,
@@ -485,7 +486,6 @@ export
485486
zeta,
486487

487488
# arrays
488-
allocate_for,
489489
bitbroadcast,
490490
broadcast!,
491491
broadcast,
@@ -585,7 +585,6 @@ export
585585
searchsortedlast,
586586
select!,
587587
select,
588-
shape,
589588
shuffle,
590589
shuffle!,
591590
size,

base/multidimensional.jl

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@ using Base: LinearFast, LinearSlow, AbstractCartesianIndex, fill_to_length, tail
1010

1111
export CartesianIndex, CartesianRange
1212

13-
# Traits for linear indexing
14-
linearindexing{A<:BitArray}(::Type{A}) = LinearFast()
15-
1613
# CartesianIndex
1714
immutable CartesianIndex{N} <: AbstractCartesianIndex{N}
1815
I::NTuple{N,Int}
@@ -78,12 +75,12 @@ end
7875
CartesianRange{N}(index::CartesianIndex{N}) = CartesianRange(one(index), index)
7976
CartesianRange(::Tuple{}) = CartesianRange{CartesianIndex{0}}(CartesianIndex{0}(()),CartesianIndex{0}(()))
8077
CartesianRange{N}(sz::NTuple{N,Int}) = CartesianRange(CartesianIndex(sz))
81-
CartesianRange{N}(rngs::NTuple{N,Union{Int,UnitRange{Int}}}) = CartesianRange(CartesianIndex(map(r->first(r), rngs)), CartesianIndex(map(r->last(r), rngs)))
78+
CartesianRange{N}(rngs::NTuple{N,Union{Integer,AbstractUnitRange}}) = CartesianRange(CartesianIndex(map(r->first(r), rngs)), CartesianIndex(map(r->last(r), rngs)))
8279

8380
ndims(R::CartesianRange) = length(R.start)
8481
ndims{I<:CartesianIndex}(::Type{CartesianRange{I}}) = length(I)
8582

86-
eachindex(::LinearSlow, A::AbstractArray) = CartesianRange(size(A))
83+
eachindex(::LinearSlow, A::AbstractArray) = CartesianRange(indices(A))
8784

8885
@inline eachindex(::LinearSlow, A::AbstractArray, B::AbstractArray...) = CartesianRange(maxsize((), A, B...))
8986
maxsize(sz) = sz
@@ -178,18 +175,18 @@ index_lengths_dim(A, dim, ::Colon) = (trailingsize(A, dim),)
178175
# whose length is equal to the dimension we're to process next. This
179176
# allows us to dispatch, which is important for the type-stability of
180177
# the lines involving Colon as the final index.
181-
index_shape(A::AbstractVector, I::Colon) = shape(A)
178+
index_shape(A::AbstractVector, I::Colon) = indices(A)
182179
index_shape(A::AbstractArray, I::Colon) = (length(A),)
183180
@inline index_shape(A::AbstractArray, I...) = index_shape_dim(A, (true,), I...)
184181
@inline index_shape_dim(A, dim, ::Colon) = (trailingsize(A, length(dim)),)
185-
@inline index_shape_dim{T,N}(A::AbstractArray{T,N}, dim::NTuple{N}, ::Colon) = (shape(A, N),)
182+
@inline index_shape_dim{T,N}(A::AbstractArray{T,N}, dim::NTuple{N}, ::Colon) = (indices(A, N),)
186183
@inline index_shape_dim(A, dim, I::Real...) = ()
187-
@inline index_shape_dim(A, dim, ::Colon, i, I...) = (shape(A, length(dim)), index_shape_dim(A, (dim...,true), i, I...)...)
184+
@inline index_shape_dim(A, dim, ::Colon, i, I...) = (indices(A, length(dim)), index_shape_dim(A, (dim...,true), i, I...)...)
188185
@inline index_shape_dim(A, dim, ::Real, I...) = (index_shape_dim(A, (dim...,true), I...)...)
189186
@inline index_shape_dim{N}(A, dim, ::CartesianIndex{N}, I...) = (index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...)
190-
@inline index_shape_dim(A, dim, i::AbstractArray, I...) = (shape(i)..., index_shape_dim(A, (dim...,true), I...)...)
187+
@inline index_shape_dim(A, dim, i::AbstractArray, I...) = (indices(i)..., index_shape_dim(A, (dim...,true), I...)...)
191188
@inline index_shape_dim(A, dim, i::AbstractArray{Bool}, I...) = (sum(i), index_shape_dim(A, (dim...,true), I...)...)
192-
@inline index_shape_dim{N}(A, dim, i::AbstractArray{CartesianIndex{N}}, I...) = (shape(i)..., index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...)
189+
@inline index_shape_dim{N}(A, dim, i::AbstractArray{CartesianIndex{N}}, I...) = (indices(i)..., index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...)
193190

194191
@inline decolon(A::AbstractVector, ::Colon) = (indices(A,1),)
195192
@inline decolon(A::AbstractArray, ::Colon) = (1:length(A),)
@@ -291,8 +288,6 @@ end
291288
end
292289
end
293290

294-
dimlength(r::Range) = length(r)
295-
dimlength(i::Integer) = i
296291
@noinline throw_checksize_error(A, sz) = throw(DimensionMismatch("output array is the wrong size; expected $sz, got $(size(A))"))
297292

298293
## setindex! ##
@@ -787,7 +782,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`.
787782
@generated function unique{T,N}(A::AbstractArray{T,N}, dim::Int)
788783
quote
789784
1 <= dim <= $N || return copy(A)
790-
hashes = allocate_for(inds->zeros(UInt, inds), A, shape(A, dim))
785+
hashes = similar(inds->zeros(UInt, inds), indices(A, dim))
791786

792787
# Compute hash for each row
793788
k = 0
@@ -796,15 +791,15 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`.
796791
end
797792

798793
# Collect index of first row for each hash
799-
uniquerow = allocate_for(Array{Int}, A, shape(A, dim))
794+
uniquerow = similar(Array{Int}, indices(A, dim))
800795
firstrow = Dict{Prehashed,Int}()
801796
for k = indices(A, dim)
802797
uniquerow[k] = get!(firstrow, Prehashed(hashes[k]), k)
803798
end
804799
uniquerows = collect(values(firstrow))
805800

806801
# Check for collisions
807-
collided = allocate_for(falses, A, shape(A, dim))
802+
collided = similar(falses, indices(A, dim))
808803
@inbounds begin
809804
@nloops $N i A d->(if d == dim
810805
k = i_d
@@ -819,7 +814,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`.
819814
end
820815

821816
if any(collided)
822-
nowcollided = allocate_for(BitArray, A, shape(A, dim))
817+
nowcollided = similar(BitArray, indices(A, dim))
823818
while any(collided)
824819
# Collect index of first row for each collided hash
825820
empty!(firstrow)

base/number.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ isinteger(x::Integer) = true
77
size(x::Number) = ()
88
size(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : 1
99
indices(x::Number) = ()
10-
indices(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : (1:1)
10+
indices(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : OneTo(1)
1111
eltype{T<:Number}(::Type{T}) = T
1212
ndims(x::Number) = 0
1313
ndims{T<:Number}(::Type{T}) = 0

0 commit comments

Comments
 (0)