|  | 
| 23 | 23 | 
 | 
| 24 | 24 | size{T,N}(t::AbstractArray{T,N}, d) = d <= N ? size(t)[d] : 1 | 
| 25 | 25 | size{N}(x, d1::Integer, d2::Integer, dx::Vararg{Integer, N}) = (size(x, d1), size(x, d2), ntuple(k->size(x, dx[k]), Val{N})...) | 
|  | 26 | + | 
|  | 27 | +# trait to indicate "vetted" usage for indexing that may not start at 1 | 
|  | 28 | +""" | 
|  | 29 | +    IndicesSafety | 
|  | 30 | +
 | 
|  | 31 | +is an abstract-trait intended to help migrate code that assumes that | 
|  | 32 | +array indexing starts with 1. | 
|  | 33 | +
 | 
|  | 34 | +See `@safeindices`, `SafeIndices`, and `UnsafeIndices` for more information. | 
|  | 35 | +
 | 
|  | 36 | +""" | 
|  | 37 | +abstract IndicesSafety | 
|  | 38 | +""" | 
|  | 39 | +    SafeIndices() | 
|  | 40 | +
 | 
|  | 41 | +is a trait-value whose purpose is to help migrate functions to a form | 
|  | 42 | +safe for arrays that have indexing that does not necessarily start | 
|  | 43 | +with 1. For example, a great deal of "legacy" code uses `for i = | 
|  | 44 | +1:size(A,d)` to iterate over dimension `d`, but this usage assumes | 
|  | 45 | +that indexing starts with 1. (One should use `for i in indices(A, d)` | 
|  | 46 | +instead.) | 
|  | 47 | +
 | 
|  | 48 | +To help discover code that makes such assumptions, `size(A, d)` should | 
|  | 49 | +throw an error when passed an array `A` with non-1 indexing. | 
|  | 50 | +`SafeIndices()` can then be used to mark a call as having been | 
|  | 51 | +"vetted" for its correctness. For example, | 
|  | 52 | +
 | 
|  | 53 | +    size(SafeIndices(), A, d) | 
|  | 54 | +
 | 
|  | 55 | +should return the size of `A` along dimension `d` even in cases where | 
|  | 56 | +`A` uses non-1 indexing. | 
|  | 57 | +
 | 
|  | 58 | +See also @safeindices, UnsafeIndices, and IndicesSafety. | 
|  | 59 | +""" | 
|  | 60 | +immutable SafeIndices <: IndicesSafety end | 
|  | 61 | +""" | 
|  | 62 | +    UnsafeIndices() | 
|  | 63 | +
 | 
|  | 64 | +is used as a default value that can be used to make a call "brittle" | 
|  | 65 | +for arrays whose indices may not start with 1. See `@safeindices` or | 
|  | 66 | +`SafeIndices` for more information.  Example: | 
|  | 67 | +
 | 
|  | 68 | +    trailingsize(A, n) = trailingsize(UnsafeIndices(), A, n) | 
|  | 69 | +    function trailingsize(s::IndicesSafety, A, n) | 
|  | 70 | +        sz = size(s, A, n) | 
|  | 71 | +        for i = n+1:ndims(A) | 
|  | 72 | +            sz *= size(s, A, i) | 
|  | 73 | +        end | 
|  | 74 | +        sz | 
|  | 75 | +    end | 
|  | 76 | +
 | 
|  | 77 | +would make `trailingsize` by-default unsafe for non-1 arrays, forcing | 
|  | 78 | +the user to make the call as `trailingsize(SafeIndices(), A, n)` if | 
|  | 79 | +s/he is certain that the usage is safe. | 
|  | 80 | +""" | 
|  | 81 | +immutable UnsafeIndices <: IndicesSafety end | 
|  | 82 | + | 
|  | 83 | +""" | 
|  | 84 | +    @safeindices ex | 
|  | 85 | +
 | 
|  | 86 | +
 | 
|  | 87 | +Marks `ex` as being safe for arrays that have indexing that does not | 
|  | 88 | +start at 1. Functions such as `size` throw errors on such arrays, | 
|  | 89 | +unless such calls have been wrapped in `@safeindices`. | 
|  | 90 | +
 | 
|  | 91 | +Internally, this macro simply rewrites such calls as | 
|  | 92 | +`size(Base.SafeIndices(), A)`. | 
|  | 93 | +
 | 
|  | 94 | +Example: | 
|  | 95 | +
 | 
|  | 96 | +    @safeindices function foo(args...) | 
|  | 97 | +        body | 
|  | 98 | +    end | 
|  | 99 | +
 | 
|  | 100 | +will annotate all of `foo`'s calls to `length` and `size` with | 
|  | 101 | +`SafeIndices`. | 
|  | 102 | +""" | 
|  | 103 | +macro safeindices(ex) | 
|  | 104 | +    esc(_safeindices(ex)) | 
|  | 105 | +end | 
|  | 106 | + | 
|  | 107 | +function _safeindices(ex::Expr) | 
|  | 108 | +    if ex.head == :call | 
|  | 109 | +        f = ex.args[1] | 
|  | 110 | +        if f == :size || f == :length | 
|  | 111 | +            return Expr(:call, f, :(Base.SafeIndices()), ex.args[2:end]...) | 
|  | 112 | +        end | 
|  | 113 | +    end | 
|  | 114 | +    return Expr(ex.head, map(_safeindices, ex.args)...) | 
|  | 115 | +end | 
|  | 116 | + | 
|  | 117 | +_safeindices(arg) = arg | 
|  | 118 | + | 
|  | 119 | +# The default is that size is safe, but array types that use non-1 | 
|  | 120 | +# indexing should specialize this to make it unsafe without | 
|  | 121 | +# SafeIndices(). | 
|  | 122 | +size( ::IndicesSafety, A)                   = size(A) | 
|  | 123 | +size( ::IndicesSafety, A::AbstractArray, d) = size(A,d)  # fixme | 
|  | 124 | +# size(s::IndicesSafety, A::AbstractArray, d) = d <= ndims(A) ? size(s, A)[d] : 1 | 
|  | 125 | +size{N}(s::IndicesSafety, A::AbstractArray, d1::Integer, d2::Integer, dx::Vararg{Integer, N}) = | 
|  | 126 | +    (size(s, A, d1), size(s, A, d2), ntuple(k->size(s, A, dx[k]), Val{N})...) | 
|  | 127 | + | 
| 26 | 128 | """ | 
| 27 | 129 |     indices(A, d) | 
| 28 | 130 | 
 | 
| 29 | 131 | Returns the valid range of indices for array `A` along dimension `d`. | 
| 30 | 132 | """ | 
| 31 | 133 | function indices(A::AbstractArray, d) | 
| 32 | 134 |     @_inline_meta | 
| 33 |  | -    1:size(A,d) | 
|  | 135 | +    1:size(SafeIndices(),A,d) | 
| 34 | 136 | end | 
| 35 | 137 | """ | 
| 36 | 138 |     indices(A) | 
| @@ -61,15 +163,17 @@ is `indices(A, 1)`. | 
| 61 | 163 | Calling this function is the "safe" way to write algorithms that | 
| 62 | 164 | exploit linear indexing. | 
| 63 | 165 | """ | 
| 64 |  | -linearindices(A) = 1:length(A) | 
|  | 166 | +linearindices(A) = 1:length(SafeIndices(), A) | 
| 65 | 167 | linearindices(A::AbstractVector) = indices1(A) | 
| 66 | 168 | eltype{T}(::Type{AbstractArray{T}}) = T | 
| 67 | 169 | eltype{T,N}(::Type{AbstractArray{T,N}}) = T | 
| 68 | 170 | elsize{T}(::AbstractArray{T}) = sizeof(T) | 
| 69 | 171 | ndims{T,N}(::AbstractArray{T,N}) = N | 
| 70 | 172 | ndims{T,N}(::Type{AbstractArray{T,N}}) = N | 
| 71 | 173 | ndims{T<:AbstractArray}(::Type{T}) = ndims(supertype(T)) | 
| 72 |  | -length(t::AbstractArray) = prod(size(t))::Int | 
|  | 174 | +length(s::IndicesSafety, t::AbstractArray) = prod(size(s,t)) | 
|  | 175 | +length(s::IndicesSafety, t) = length(t) | 
|  | 176 | +length(t::AbstractArray) = length(UnsafeIndices(), t) | 
| 73 | 177 | endof(a::AbstractArray) = length(a) | 
| 74 | 178 | first(a::AbstractArray) = a[first(eachindex(a))] | 
| 75 | 179 | 
 | 
| @@ -120,13 +224,14 @@ function isassigned(a::AbstractArray, i::Int...) | 
| 120 | 224 | end | 
| 121 | 225 | 
 | 
| 122 | 226 | # used to compute "end" for last index | 
| 123 |  | -function trailingsize(A, n) | 
| 124 |  | -    s = 1 | 
|  | 227 | +function trailingsize(s::IndicesSafety, A, n) | 
|  | 228 | +    sz = 1 | 
| 125 | 229 |     for i=n:ndims(A) | 
| 126 |  | -        s *= size(A,i) | 
|  | 230 | +        sz *= size(s,A,i) | 
| 127 | 231 |     end | 
| 128 |  | -    return s | 
|  | 232 | +    return sz | 
| 129 | 233 | end | 
|  | 234 | +trailingsize(A, n) = trailingsize(UnsafeIndices(), A, n) | 
| 130 | 235 | 
 | 
| 131 | 236 | ## Traits for array types ## | 
| 132 | 237 | 
 | 
| @@ -178,21 +283,22 @@ start at something different from 1), it is equivalent to `indices(A, | 
| 178 | 283 | d)`. | 
| 179 | 284 | """ | 
| 180 | 285 | shape(a, d) = shape(indicesbehavior(a), a, d) | 
| 181 |  | -shape(::IndicesStartAt1, a) = size(a) | 
| 182 |  | -shape(::IndicesStartAt1, a, d) = size(a, d) | 
|  | 286 | +shape(::IndicesStartAt1, a) = size(SafeIndices(), a) | 
|  | 287 | +shape(::IndicesStartAt1, a, d) = size(SafeIndices(), a, d) | 
| 183 | 288 | shape(::IndicesBehavior, a) = indices(a) | 
| 184 | 289 | shape(::IndicesBehavior, a, d) = indices(a, d) | 
| 185 | 290 | 
 | 
| 186 | 291 | ## Bounds checking ## | 
| 187 |  | -@generated function trailingsize{T,N,n}(A::AbstractArray{T,N}, ::Type{Val{n}}) | 
|  | 292 | +@generated function trailingsize{T,N,n}(s::IndicesSafety, A::AbstractArray{T,N}, ::Type{Val{n}}) | 
| 188 | 293 |     (isa(n, Int) && isa(N, Int)) || error("Must have concrete type") | 
| 189 | 294 |     n > N && return 1 | 
| 190 |  | -    ex = :(size(A, $n)) | 
|  | 295 | +    ex = :(size(s, A, $n)) | 
| 191 | 296 |     for m = n+1:N | 
| 192 |  | -        ex = :($ex * size(A, $m)) | 
|  | 297 | +        ex = :($ex * size(s, A, $m)) | 
| 193 | 298 |     end | 
| 194 | 299 |     Expr(:block, Expr(:meta, :inline), ex) | 
| 195 | 300 | end | 
|  | 301 | +trailingsize{n}(A::AbstractArray, ::Type{Val{n}}) = trailingsize(UnsafeIndices(), A, Val{n}) | 
| 196 | 302 | 
 | 
| 197 | 303 | # check along a single dimension | 
| 198 | 304 | """ | 
| @@ -265,13 +371,9 @@ _chkbnds(A::AbstractArray, ::NTuple{1,Bool}, I::AbstractVector{Bool}) = length(A | 
| 265 | 371 | _chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I::AbstractArray{Bool}) = length(A) == length(I) | 
| 266 | 372 | _chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I::AbstractVector{Bool}) = indices(A) == indices(I) | 
| 267 | 373 | # Linear indexing: | 
| 268 |  | -function _chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I) | 
| 269 |  | -    @_inline_meta | 
| 270 |  | -    checkindex(Bool, indices1(A), I) | 
| 271 |  | -end | 
| 272 | 374 | function _chkbnds(A::AbstractArray, ::NTuple{1,Bool}, I) | 
| 273 | 375 |     @_inline_meta | 
| 274 |  | -    checkindex(Bool, 1:length(A), I) | 
|  | 376 | +    checkindex(Bool, linearindices(A), I) | 
| 275 | 377 | end | 
| 276 | 378 | # When all indices have been checked: | 
| 277 | 379 | _chkbnds{M}(A, checked::NTuple{M,Bool}) = checked[M] | 
| @@ -359,7 +461,7 @@ similar(   a::AbstractArray, T::Type, dims::DimsInteger) = similar(a, T, convert | 
| 359 | 461 | # similar creates an Array by default | 
| 360 | 462 | similar(   a::AbstractArray, T::Type, dims::Dims)        = Array(T, dims) | 
| 361 | 463 | 
 | 
| 362 |  | -_similar(::IndicesStartAt1, a::AbstractArray, T::Type)   = similar(a, T, size(a)) | 
|  | 464 | +_similar(::IndicesStartAt1, a::AbstractArray, T::Type)   = similar(a, T, size(SafeIndices(), a)) | 
| 363 | 465 | _similar(::IndicesBehavior, a::AbstractArray, T::Type)   = similar(a, T, indices(a)) | 
| 364 | 466 | 
 | 
| 365 | 467 | """ | 
| @@ -525,7 +627,7 @@ function copy!(::LinearIndexing, dest::AbstractArray, ::LinearSlow, src::Abstrac | 
| 525 | 627 | end | 
| 526 | 628 | 
 | 
| 527 | 629 | function copy!(dest::AbstractArray, dstart::Integer, src::AbstractArray) | 
| 528 |  | -    copy!(dest, dstart, src, first(linearindices(src)), length(src)) | 
|  | 630 | +    copy!(dest, dstart, src, first(linearindices(src)), length(SafeIndices(), src)) | 
| 529 | 631 | end | 
| 530 | 632 | 
 | 
| 531 | 633 | function copy!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer) | 
| @@ -650,7 +752,7 @@ function _maxlength(A, B, C...) | 
| 650 | 752 |     max(length(A), _maxlength(B, C...)) | 
| 651 | 753 | end | 
| 652 | 754 | 
 | 
| 653 |  | -isempty(a::AbstractArray) = (length(a) == 0) | 
|  | 755 | +isempty(a::AbstractArray) = (length(SafeIndices(), a) == 0) | 
| 654 | 756 | 
 | 
| 655 | 757 | ## Conversions ## | 
| 656 | 758 | 
 | 
| @@ -1427,7 +1529,7 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector) | 
| 1427 | 1529 |     end | 
| 1428 | 1530 |     nextra = max(0,length(dims)-ndims(r1)) | 
| 1429 | 1531 |     if eltype(Rsize) == Int | 
| 1430 |  | -        Rsize[dims] = [size(r1)..., ntuple(d->1, nextra)...] | 
|  | 1532 | +        Rsize[dims] = [size(SafeIndices(), r1)..., ntuple(d->1, nextra)...] | 
| 1431 | 1533 |     else | 
| 1432 | 1534 |         Rsize[dims] = [indices(r1)..., ntuple(d->1:1, nextra)...] | 
| 1433 | 1535 |     end | 
|  | 
0 commit comments