Skip to content

Commit 8b1a809

Browse files
authored
Bugfix in overflow, CartesianIndices for BigInt ranges (#227)
* Bugfix in overflow, CartesianIndices for BigInt ranges * limit parameters in AbstractUnitRange constrctors * no overflow check for BigInt ranges * conversion to OrdinalRange * Add test * no-op IdOffsetRange eltype coercion * fix version lower limit
1 parent c4757ea commit 8b1a809

File tree

3 files changed

+49
-10
lines changed

3 files changed

+49
-10
lines changed

src/OffsetArrays.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ Type alias and convenience constructor for two-dimensional [`OffsetArray`](@ref)
136136
const OffsetMatrix{T,AA<:AbstractMatrix{T}} = OffsetArray{T,2,AA}
137137

138138
# checks if the offset may be added to the range without overflowing
139-
function overflow_check(r::AbstractUnitRange{T}, offset::T) where T<:Integer
139+
function overflow_check(r::AbstractUnitRange{T}, offset::Integer) where {T<:Integer}
140+
Base.hastypemax(T) || return nothing
140141
# This gives some performance boost https://github.com/JuliaLang/julia/issues/33273
141142
throw_upper_overflow_error(val) = throw(OverflowError("offset should be <= $(typemax(T) - val) corresponding to the axis $r, received an offset $offset"))
142143
throw_lower_overflow_error(val) = throw(OverflowError("offset should be >= $(typemin(T) - val) corresponding to the axis $r, received an offset $offset"))
@@ -153,13 +154,14 @@ function overflow_check(r::AbstractUnitRange{T}, offset::T) where T<:Integer
153154
return nothing
154155
end
155156
# checks if the two offsets may be added together without overflowing
156-
function overflow_check(offset1::T, offset2::T) where {T<:Integer}
157-
throw_upper_overflow_error() = throw(OverflowError("offset should be <= $(typemax(eltype(offset1)) - offset1) given a pre-existing offset of $offset1, received an offset $offset2"))
158-
throw_lower_overflow_error() = throw(OverflowError("offset should be >= $(typemin(eltype(offset1)) - offset1) given a pre-existing offset of $offset1, received an offset $offset2"))
157+
function overflow_check(offset_preexisting::Integer, offset_new::T) where {T<:Integer}
158+
Base.hastypemax(T) || return nothing
159+
throw_upper_overflow_error() = throw(OverflowError("offset should be <= $(typemax(eltype(offset_preexisting)) - offset_preexisting) given a pre-existing offset of $offset_preexisting, received an offset $offset_new"))
160+
throw_lower_overflow_error() = throw(OverflowError("offset should be >= $(typemin(eltype(offset_preexisting)) - offset_preexisting) given a pre-existing offset of $offset_preexisting, received an offset $offset_new"))
159161

160-
if offset1 > 0 && offset2 > typemax(T) - offset1
162+
if offset_preexisting > 0 && offset_new > typemax(T) - offset_preexisting
161163
throw_upper_overflow_error()
162-
elseif offset1 < 0 && offset2 < typemin(T) - offset1
164+
elseif offset_preexisting < 0 && offset_new < typemin(T) - offset_preexisting
163165
throw_lower_overflow_error()
164166
end
165167
return nothing

src/axes.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ function IdOffsetRange{T,I}(r::IdOffsetRange, offset::Integer = 0) where {T<:Int
106106
rc, offset_rc = offset_coerce(I, r.parent)
107107
return IdOffsetRange{T,I}(rc, convert(T, r.offset + offset + offset_rc)::T)
108108
end
109+
IdOffsetRange{T}(r::IdOffsetRange{T}) where {T<:Integer} = r
109110
function IdOffsetRange{T}(r::IdOffsetRange, offset::Integer = 0) where T<:Integer
110111
return IdOffsetRange{T}(r.parent, r.offset + offset)
111112
end
@@ -118,6 +119,15 @@ function IdOffsetRange(; values::AbstractUnitRange{<:Integer}, indices::Abstract
118119
return IdOffsetRange(values .- offset, offset)
119120
end
120121

122+
# Conversions to an AbstractUnitRange{Int} (and to an OrdinalRange{Int,Int} on Julia v"1.6") are necessary
123+
# to evaluate CartesianIndices for BigInt ranges, as their axes are also BigInt ranges
124+
AbstractUnitRange{T}(r::IdOffsetRange) where {T<:Integer} = IdOffsetRange{T}(r)
125+
126+
# A version upper bound on this may be set after https://github.com/JuliaLang/julia/pull/40038 is merged
127+
if v"1.6" <= VERSION
128+
OrdinalRange{T,T}(r::IdOffsetRange) where {T<:Integer} = IdOffsetRange{T}(r)
129+
end
130+
121131
# TODO: uncomment these when Julia is ready
122132
# # Conversion preserves both the values and the indices, throwing an InexactError if this
123133
# # is not possible.

test/runtests.jl

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,19 @@ end
245245
@test typeof(rred) == typeof(r)
246246
@test length(rred) == 1
247247
@test first(rred) == first(r)
248+
249+
@testset "conversion to AbstractUnitRange" begin
250+
r = IdOffsetRange(1:2)
251+
@test AbstractUnitRange{Int}(r) === r
252+
r2 = IdOffsetRange(big(1):big(2))
253+
@test AbstractUnitRange{Int}(r2) === r
254+
@test AbstractUnitRange{BigInt}(r2) === r2
255+
256+
if v"1.5" < VERSION
257+
@test OrdinalRange{Int,Int}(r2) === r
258+
@test OrdinalRange{BigInt,BigInt}(r2) === r2
259+
end
260+
end
248261
end
249262

250263
# used in testing the constructor
@@ -395,12 +408,20 @@ Base.convert(::Type{Int}, a::WeirdInteger) = a
395408
@test_throws OverflowError OffsetArray(ao, (-2, )) # convinient constructor accumulate offsets
396409
@test_throws OverflowError OffsetVector(1:0, typemax(Int))
397410
@test_throws OverflowError OffsetVector(OffsetVector(1:0, 0), typemax(Int))
411+
@test_throws OverflowError OffsetArray(zeros(Int, typemax(Int):typemax(Int)), 2)
398412

399413
@testset "OffsetRange" begin
400-
local r = 1:100
401-
local a = OffsetVector(r, 4)
402-
@test first(r) in a
403-
@test !(last(r) + 1 in a)
414+
for r in Any[1:100, big(1):big(2)]
415+
a = OffsetVector(r, 4)
416+
@test first(r) in a
417+
@test !(last(r) + 1 in a)
418+
end
419+
420+
@testset "BigInt axes" begin
421+
r = OffsetArray(1:big(2)^65, 4000)
422+
@test eltype(r) === BigInt
423+
@test axes(r, 1) == (big(1):big(2)^65) .+ 4000
424+
end
404425
end
405426

406427
# disallow OffsetVector(::Array{<:Any, N}, offsets) where N != 1
@@ -765,6 +786,12 @@ end
765786
@test eachindex(IndexLinear(), S) == eachindex(IndexLinear(), A0)
766787
A = ones(5:6)
767788
@test eachindex(IndexLinear(), A) === axes(A, 1)
789+
790+
A = OffsetArray(big(1):big(2), 1)
791+
B = OffsetArray(1:2, 1)
792+
@test CartesianIndices(A) == CartesianIndices(B)
793+
@test LinearIndices(A) == LinearIndices(B)
794+
@test eachindex(A) == eachindex(B)
768795
end
769796

770797
@testset "Scalar indexing" begin

0 commit comments

Comments
 (0)