Skip to content

Commit 44f9b50

Browse files
authored
Fix overflow in searchsorted* (#286)
* partly fix overflow in searchsorte* * version bump to v1.11.2 * fix searchsorted insertion indices * forward searchsorted* to parent * Add searchsorted test for typemin(Int) * Tests for nested offset arrays
1 parent d8c8d42 commit 44f9b50

File tree

3 files changed

+83
-65
lines changed

3 files changed

+83
-65
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "OffsetArrays"
22
uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
3-
version = "1.11.1"
3+
version = "1.11.2"
44

55
[deps]
66
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"

src/OffsetArrays.jl

Lines changed: 15 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -771,74 +771,25 @@ centered(A::AbstractArray, cp::Dims=center(A)) = OffsetArray(A, .-cp)
771771

772772
centered(A::AbstractArray, i::CartesianIndex) = centered(A, Tuple(i))
773773

774-
####
775-
# work around for segfault in searchsorted*
776-
# https://github.com/JuliaLang/julia/issues/33977
777-
####
778-
779-
function _safe_searchsorted(v::OffsetArray, x, ilo::T, ihi::T, o::Base.Ordering) where T<:Integer
780-
u = T(1)
781-
lo = ilo - u
782-
hi = ihi + u
783-
@inbounds while lo < hi - u
784-
m = (lo + hi) ÷ 2
785-
if Base.lt(o, v[m], x)
786-
lo = m
787-
elseif Base.lt(o, x, v[m])
788-
hi = m
789-
else
790-
a = searchsortedfirst(v, x, max(lo,ilo), m, o)
791-
b = searchsortedlast(v, x, m, min(hi,ihi), o)
792-
return a : b
793-
end
774+
# we may pass the searchsorted* functions to the parent, and wrap the offset
775+
for f in [:searchsortedfirst, :searchsortedlast, :searchsorted]
776+
_safe_f = Symbol("_safe_" * String(f))
777+
@eval function $_safe_f(v::OffsetVector, x, ilo, ihi, o::Base.Ordering)
778+
offset = v.offsets[1]
779+
$f(parent(v), x, ilo - offset, ihi - offset, o) .+ offset
794780
end
795-
return (lo + 1) : (hi - 1)
796-
end
797-
function _safe_searchsortedfirst(v::OffsetArray, x, lo::T, hi::T, o::Base.Ordering) where T<:Integer
798-
u = T(1)
799-
lo = lo - u
800-
hi = hi + u
801-
@inbounds while lo < hi - u
802-
m = (lo + hi) ÷ 2
803-
if Base.lt(o, v[m], x)
804-
lo = m
805-
else
806-
hi = m
807-
end
808-
end
809-
return hi
810-
end
811-
function _safe_searchsortedlast(v::OffsetArray, x, lo::T, hi::T, o::Base.Ordering) where T<:Integer
812-
u = T(1)
813-
lo = lo - u
814-
hi = hi + u
815-
@inbounds while lo < hi - u
816-
m = (lo + hi) ÷ 2
817-
if Base.lt(o, x, v[m])
818-
hi = m
819-
else
820-
lo = m
821-
end
822-
end
823-
return lo
781+
@eval Base.$f(v::OffsetVector, x, ilo::T, ihi::T, o::Base.Ordering) where T<:Integer =
782+
$_safe_f(v, x, ilo, ihi, o)
824783
end
825784

826-
if VERSION  v"1.2"
785+
if VERSION <= v"1.2"
827786
# ambiguity warnings in earlier versions
828-
Base.searchsorted(v::OffsetArray, x, ilo::Int, ihi::Int, o::Base.Ordering) =
829-
_safe_searchsorted(v, x, ilo, ihi, o)
830-
Base.searchsortedfirst(v::OffsetArray, x, lo::Int, hi::Int, o::Base.Ordering) =
831-
_safe_searchsortedfirst(v, x, lo, hi, o)
832-
Base.searchsortedlast(v::OffsetArray, x, lo::Int, hi::Int, o::Base.Ordering) =
833-
_safe_searchsortedlast(v, x, lo, hi, o)
834-
end
835-
836-
Base.searchsorted(v::OffsetArray, x, ilo::T, ihi::T, o::Base.Ordering) where T<:Integer =
837-
_safe_searchsorted(v, x, ilo, ihi, o)
838-
Base.searchsortedfirst(v::OffsetArray, x, lo::T, hi::T, o::Base.Ordering) where T<:Integer =
839-
_safe_searchsortedfirst(v, x, lo, hi, o)
840-
Base.searchsortedlast(v::OffsetArray, x, lo::T, hi::T, o::Base.Ordering) where T<:Integer =
841-
_safe_searchsortedlast(v, x, lo, hi, o)
787+
for f in [:searchsortedfirst, :searchsortedlast, :searchsorted]
788+
_safe_f = Symbol("_safe_" * String(f))
789+
@eval Base.$f(v::OffsetVector, x, ilo::Int, ihi::Int, o::Base.Ordering) =
790+
$_safe_f(v, x, ilo, ihi, o)
791+
end
792+
end
842793

843794
if VERSION < v"1.1.0-DEV.783"
844795
Base.copyfirst!(dest::OffsetArray, src::OffsetArray) = (maximum!(parent(dest), parent(src)); return dest)

test/runtests.jl

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2320,6 +2320,73 @@ end
23202320
@test searchsorted(o, 2) == 0:-1
23212321
@test searchsorted(o, 5) == 2:2
23222322
@test searchsorted(o, 6) == 3:2
2323+
2324+
if VERSION > v"1.2"
2325+
# OffsetVector of another offset vector
2326+
v = OffsetVector(Base.IdentityUnitRange(4:10),-2)
2327+
@test searchsortedfirst(v, first(v)-1) == firstindex(v)
2328+
for i in axes(v,1)
2329+
@test searchsortedfirst(v, v[i]) == i
2330+
end
2331+
@test searchsortedfirst(v, last(v)+1) == lastindex(v)+1
2332+
@test searchsortedlast(v, first(v)-1) == firstindex(v)-1
2333+
for i in axes(v,1)
2334+
@test searchsortedlast(v, v[i]) == i
2335+
end
2336+
@test searchsortedlast(v, last(v)+1) == lastindex(v)
2337+
@test searchsorted(v, first(v)-1) === firstindex(v) .+ (0:-1)
2338+
for i in axes(v,1)
2339+
@test searchsorted(v, v[i]) == i:i
2340+
end
2341+
@test searchsorted(v, last(v)+1) === lastindex(v) .+ (1:0)
2342+
end
2343+
2344+
v = OffsetVector{Float64, OffsetVector{Float64, Vector{Float64}}}(OffsetVector([2,2,3,3,3,4], 3), 4)
2345+
@test searchsortedfirst(v, minimum(v)-1) == firstindex(v)
2346+
for el in unique(v)
2347+
@test searchsortedfirst(v, el) == findfirst(isequal(el), v)
2348+
end
2349+
@test searchsortedfirst(v, maximum(v)+1) == lastindex(v)+1
2350+
2351+
@test searchsortedlast(v, minimum(v)-1) == firstindex(v)-1
2352+
for el in unique(v)
2353+
@test searchsortedlast(v, el) == findlast(isequal(el), v)
2354+
end
2355+
@test searchsortedlast(v, maximum(v)+1) == lastindex(v)
2356+
2357+
@test searchsorted(v, minimum(v)-1) === firstindex(v) .+ (0:-1)
2358+
for el in unique(v)
2359+
@test searchsorted(v, el) == findfirst(isequal(el), v):findlast(isequal(el), v)
2360+
end
2361+
@test searchsorted(v, maximum(v)+1) === lastindex(v) .+ (1:0)
2362+
2363+
soa = OffsetArray([2,2,3], typemax(Int)-3)
2364+
@test searchsortedfirst(soa, 1) == firstindex(soa) == typemax(Int)-2
2365+
@test searchsortedfirst(soa, 2) == firstindex(soa) == typemax(Int)-2
2366+
@test searchsortedfirst(soa, 3) == lastindex(soa) == typemax(Int)
2367+
2368+
soa = OffsetArray([2,2,3], typemin(Int))
2369+
@test searchsortedlast(soa, 2) == firstindex(soa) + 1 == typemin(Int) + 2
2370+
@test searchsortedlast(soa, 3) == lastindex(soa) == typemin(Int) + 3
2371+
@test searchsortedlast(soa, 1) == typemin(Int)
2372+
2373+
soa = OffsetArray([2,2,3], typemax(Int)-4)
2374+
@test searchsorted(soa, 1) === firstindex(soa) .+ (0:-1)
2375+
@test searchsorted(soa, 2) == firstindex(soa) .+ (0:1) == typemax(Int) .+ (-3:-2)
2376+
@test searchsorted(soa, 3) == lastindex(soa) .+ (0:0) == typemax(Int) .+ (-1:-1)
2377+
@test searchsorted(soa, 4) === lastindex(soa) .+ (1:0)
2378+
2379+
soa = OffsetArray([2,2,3], typemax(Int)-3)
2380+
@test searchsorted(soa, 1) === firstindex(soa) .+ (0:-1)
2381+
@test searchsorted(soa, 2) == firstindex(soa) .+ (0:1) == typemax(Int) .+ (-2:-1)
2382+
@test searchsorted(soa, 3) == lastindex(soa) .+ (0:0) == typemax(Int) .+ (0:0)
2383+
@test searchsorted(soa, 4) === lastindex(soa) .+ (1:0)
2384+
2385+
soa = OffsetArray([2,2,3], typemin(Int))
2386+
@test searchsorted(soa, 1) === firstindex(soa) .+ (0:-1)
2387+
@test searchsorted(soa, 2) == firstindex(soa) .+ (0:1) == typemin(Int) .+ (1:2)
2388+
@test searchsorted(soa, 3) == lastindex(soa) .+ (0:0) == typemin(Int) .+ (3:3)
2389+
@test searchsorted(soa, 4) === lastindex(soa) .+ (1:0)
23232390
end
23242391

23252392
@testset "Adapt" begin

0 commit comments

Comments
 (0)