Skip to content

Commit 708eeae

Browse files
committed
Improve bounds checks in heap operations
1 parent c86628e commit 708eeae

File tree

3 files changed

+38
-28
lines changed

3 files changed

+38
-28
lines changed

src/heaps.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,10 @@ function nextreme(ord::Base.Ordering, n::Int, arr::AbstractVector{T}) where T
117117

118118
rev = Base.ReverseOrdering(ord)
119119

120-
buffer = heapify(arr[1:n], rev)
120+
buffer = heapify!(arr[1:n], rev)
121121

122-
for i = n + 1 : length(arr)
123-
@inbounds xi = arr[i]
122+
@inbounds for i = n + 1 : length(arr)
123+
xi = arr[i]
124124
if Base.lt(rev, buffer[1], xi)
125125
buffer[1] = xi
126126
percolate_down!(buffer, 1, rev)

src/heaps/arrays_as_heaps.jl

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,35 @@ heapleft(i::Integer) = 2i
1313
heapright(i::Integer) = 2i + 1
1414
heapparent(i::Integer) = div(i, 2)
1515

16-
1716
# Binary min-heap percolate down.
18-
function percolate_down!(xs::AbstractArray, i::Integer, x=xs[i], o::Ordering=Forward, len::Integer=length(xs))
17+
Base.@propagate_inbounds function percolate_down!(xs::AbstractArray, i::Integer, x, o::Ordering=Forward, len::Integer=length(xs))
18+
@boundscheck checkbounds(xs, i)
19+
@boundscheck checkbounds(xs, len)
20+
1921
@inbounds while (l = heapleft(i)) <= len
2022
r = heapright(i)
2123
j = r > len || lt(o, xs[l], xs[r]) ? l : r
2224
lt(o, xs[j], x) || break
2325
xs[i] = xs[j]
2426
i = j
2527
end
26-
xs[i] = x
28+
@inbounds xs[i] = x
2729
end
28-
29-
percolate_down!(xs::AbstractArray, i::Integer, o::Ordering, len::Integer=length(xs)) = percolate_down!(xs, i, xs[i], o, len)
30+
Base.@propagate_inbounds percolate_down!(xs::AbstractArray, i::Integer, o::Ordering, len::Integer=length(xs)) = percolate_down!(xs, i, xs[i], o, len)
3031

3132

3233
# Binary min-heap percolate up.
33-
function percolate_up!(xs::AbstractArray, i::Integer, x=xs[i], o::Ordering=Forward)
34+
Base.@propagate_inbounds function percolate_up!(xs::AbstractArray, i::Integer, x, o::Ordering=Forward)
35+
@boundscheck checkbounds(xs, i)
36+
3437
@inbounds while (j = heapparent(i)) >= 1
3538
lt(o, x, xs[j]) || break
3639
xs[i] = xs[j]
3740
i = j
3841
end
39-
xs[i] = x
42+
@inbounds xs[i] = x
4043
end
41-
42-
@inline percolate_up!(xs::AbstractArray, i::Integer, o::Ordering) = percolate_up!(xs, i, xs[i], o)
44+
Base.@propagate_inbounds percolate_up!(xs::AbstractArray, i::Integer, o::Ordering) = percolate_up!(xs, i, xs[i], o)
4345

4446
"""
4547
heappop!(v, [ord])
@@ -48,10 +50,11 @@ Given a binary heap-ordered array, remove and return the lowest ordered element.
4850
For efficiency, this function does not check that the array is indeed heap-ordered.
4951
"""
5052
function heappop!(xs::AbstractArray, o::Ordering=Forward)
53+
Base.require_one_based_indexing(xs)
5154
x = xs[1]
5255
y = pop!(xs)
5356
if !isempty(xs)
54-
percolate_down!(xs, 1, y, o)
57+
@inbounds percolate_down!(xs, 1, y, o)
5558
end
5659
return x
5760
end
@@ -63,8 +66,9 @@ Given a binary heap-ordered array, push a new element `x`, preserving the heap p
6366
For efficiency, this function does not check that the array is indeed heap-ordered.
6467
"""
6568
@inline function heappush!(xs::AbstractArray, x, o::Ordering=Forward)
69+
Base.require_one_based_indexing(xs)
6670
push!(xs, x)
67-
percolate_up!(xs, length(xs), o)
71+
@inbounds percolate_up!(xs, length(xs), o)
6872
return xs
6973
end
7074

@@ -76,8 +80,9 @@ end
7680
In-place [`heapify`](@ref).
7781
"""
7882
@inline function heapify!(xs::AbstractArray, o::Ordering=Forward)
83+
Base.require_one_based_indexing(xs)
7984
for i in heapparent(length(xs)):-1:1
80-
percolate_down!(xs, i, o)
85+
@inbounds percolate_down!(xs, i, o)
8186
end
8287
return xs
8388
end
@@ -129,6 +134,7 @@ false
129134
```
130135
"""
131136
function isheap(xs::AbstractArray, o::Ordering=Forward)
137+
Base.require_one_based_indexing(xs)
132138
for i in 1:div(length(xs), 2)
133139
if lt(o, xs[heapleft(i)], xs[i]) ||
134140
(heapright(i) <= length(xs) && lt(o, xs[heapright(i)], xs[i]))

src/priorityqueue.jl

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ struct PriorityQueue{K,V,O<:Ordering} <: AbstractDict{K,V}
4949
function PriorityQueue{K,V,O}(o::O, itr) where {K,V,O<:Ordering}
5050
xs = Vector{Pair{K,V}}(undef, length(itr))
5151
index = Dict{K, Int}()
52-
for (i, (k, v)) in enumerate(itr)
52+
@inbounds for (i, (k, v)) in enumerate(itr)
5353
xs[i] = Pair{K,V}(k, v)
5454
if haskey(index, k)
5555
throw(ArgumentError("PriorityQueue keys must be unique"))
@@ -60,7 +60,7 @@ struct PriorityQueue{K,V,O<:Ordering} <: AbstractDict{K,V}
6060

6161
# heapify
6262
for i in heapparent(length(pq.xs)):-1:1
63-
percolate_down!(pq, i)
63+
@inbounds percolate_down!(pq, i)
6464
end
6565

6666
return pq
@@ -167,8 +167,10 @@ priority queue.
167167
"""
168168
Base.first(pq::PriorityQueue) = first(pq.xs)
169169

170-
function percolate_down!(pq::PriorityQueue, i::Integer)
171-
x = pq.xs[i]
170+
Base.@propagate_inbounds function percolate_down!(pq::PriorityQueue, i::Integer)
171+
@boundscheck checkbounds(pq.xs, i)
172+
173+
@inbounds x = pq.xs[i]
172174
@inbounds while (l = heapleft(i)) <= length(pq)
173175
r = heapright(i)
174176
j = r > length(pq) || lt(pq.o, pq.xs[l].second, pq.xs[r].second) ? l : r
@@ -182,12 +184,14 @@ function percolate_down!(pq::PriorityQueue, i::Integer)
182184
end
183185
end
184186
pq.index[x.first] = i
185-
pq.xs[i] = x
187+
@inbounds pq.xs[i] = x
186188
end
187189

188190

189-
function percolate_up!(pq::PriorityQueue, i::Integer)
190-
x = pq.xs[i]
191+
Base.@propagate_inbounds function percolate_up!(pq::PriorityQueue, i::Integer)
192+
@boundscheck checkbounds(pq.xs, i)
193+
194+
@inbounds x = pq.xs[i]
191195
@inbounds while i > 1
192196
j = heapparent(i)
193197
xj = pq.xs[j]
@@ -200,7 +204,7 @@ function percolate_up!(pq::PriorityQueue, i::Integer)
200204
end
201205
end
202206
pq.index[x.first] = i
203-
pq.xs[i] = x
207+
@inbounds pq.xs[i] = x
204208
end
205209

206210
# Equivalent to percolate_up! with an element having lower priority than any other
@@ -236,8 +240,8 @@ end
236240
# Change the priority of an existing element, or enqueue it if it isn't present.
237241
function Base.setindex!(pq::PriorityQueue{K, V}, value, key) where {K,V}
238242
i = get(pq.index, key, 0)
239-
if i != 0
240-
@inbounds oldvalue = pq.xs[i].second
243+
@inbounds if i != 0
244+
oldvalue = pq.xs[i].second
241245
pq.xs[i] = Pair{K,V}(key, value)
242246
if lt(pq.o, oldvalue, value)
243247
percolate_down!(pq, i)
@@ -255,7 +259,7 @@ end
255259
256260
Insert the a key `k` into a priority queue `pq` with priority `v`.
257261
258-
# Examples
262+
# Examples
259263
260264
```jldoctest
261265
julia> a = PriorityQueue("a" => 1, "b" => 2, "c" => 3, "e" => 5)
@@ -317,7 +321,7 @@ function Base.popfirst!(pq::PriorityQueue)
317321
if !isempty(pq)
318322
@inbounds pq.xs[1] = y
319323
pq.index[y.first] = 1
320-
percolate_down!(pq, 1)
324+
@inbounds percolate_down!(pq, 1)
321325
end
322326
delete!(pq.index, x.first)
323327
return x
@@ -354,7 +358,7 @@ function Base.delete!(pq::PriorityQueue, key)
354358
end
355359

356360
"""
357-
empty!(pq::PriorityQueue)
361+
empty!(pq::PriorityQueue)
358362
359363
Reset priority queue `pq`.
360364
"""

0 commit comments

Comments
 (0)