Skip to content

Commit

Permalink
heap: New library that implements a binary heap-based priority queue.
Browse files Browse the repository at this point in the history
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
blp committed Feb 1, 2012
1 parent 1745cd0 commit 9597444
Show file tree
Hide file tree
Showing 8 changed files with 891 additions and 2 deletions.
4 changes: 3 additions & 1 deletion lib/automake.mk
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
Expand Down Expand Up @@ -45,6 +45,8 @@ lib_libopenvswitch_a_SOURCES = \
lib/dpif-provider.h \
lib/dpif.c \
lib/dpif.h \
lib/heap.c \
lib/heap.h \
lib/dynamic-string.c \
lib/dynamic-string.h \
lib/entropy.c \
Expand Down
216 changes: 216 additions & 0 deletions lib/heap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/*
* Copyright (c) 2012 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <config.h>
#include "heap.h"
#include <stdlib.h>
#include "util.h"

static void put_node(struct heap *, struct heap_node *, size_t i);
static void swap_nodes(struct heap *, size_t i, size_t j);
static bool float_up(struct heap *, size_t i);
static void float_down(struct heap *, size_t i);
static void float_up_or_down(struct heap *, size_t i);

/* Initializes 'heap' as an empty heap. */
void
heap_init(struct heap *heap)
{
heap->array = NULL;
heap->n = 0;
heap->allocated = 0;
}

/* Frees memory owned internally by 'heap'. The caller is responsible for
* freeing 'heap' itself, if necessary. */
void
heap_destroy(struct heap *heap)
{
if (heap) {
free(heap->array);
}
}

/* Removes all of the elements from 'heap', without freeing any allocated
* memory. */
void
heap_clear(struct heap *heap)
{
heap->n = 0;
}

/* Exchanges the contents of 'a' and 'b'. */
void
heap_swap(struct heap *a, struct heap *b)
{
struct heap tmp = *a;
*a = *b;
*b = tmp;
}

/* Inserts 'node' into 'heap' with the specified 'priority'.
*
* This takes time O(lg n). */
void
heap_insert(struct heap *heap, struct heap_node *node, uint32_t priority)
{
heap_raw_insert(heap, node, priority);
float_up(heap, node->idx);
}

/* Removes 'node' from 'heap'.
*
* This takes time O(lg n). */
void
heap_remove(struct heap *heap, struct heap_node *node)
{
size_t i = node->idx;

heap_raw_remove(heap, node);
if (i <= heap->n) {
float_up_or_down(heap, i);
}
}

/* Changes the priority of 'node' (which must be in 'heap') to 'priority'.
*
* This takes time O(lg n). */
void
heap_change(struct heap *heap, struct heap_node *node, uint32_t priority)
{
heap_raw_change(node, priority);
float_up_or_down(heap, node->idx);
}

/* Inserts 'node' into 'heap' with the specified 'priority', without
* maintaining the heap invariant.
*
* After this call, heap_max() will no longer necessarily return the maximum
* value in the heap, and HEAP_FOR_EACH will no longer necessarily iterate in
* heap level order, until the next call to heap_rebuild(heap).
*
* This takes time O(1). */
void
heap_raw_insert(struct heap *heap, struct heap_node *node, uint32_t priority)
{
if (heap->n >= heap->allocated) {
heap->allocated = heap->n == 0 ? 1 : 2 * heap->n;
heap->array = xrealloc(heap->array,
(heap->allocated + 1) * sizeof *heap->array);
}

put_node(heap, node, ++heap->n);
node->priority = priority;
}

/* Removes 'node' from 'heap', without maintaining the heap invariant.
*
* After this call, heap_max() will no longer necessarily return the maximum
* value in the heap, and HEAP_FOR_EACH will no longer necessarily iterate in
* heap level order, until the next call to heap_rebuild(heap).
*
* This takes time O(1). */
void
heap_raw_remove(struct heap *heap, struct heap_node *node)
{
size_t i = node->idx;
if (i < heap->n) {
put_node(heap, heap->array[heap->n], i);
}
heap->n--;
}

/* Rebuilds 'heap' to restore the heap invariant following a series of one or
* more calls to heap_raw_*() functions. (Otherwise this function need not be
* called.)
*
* This takes time O(n) in the current size of the heap. */
void
heap_rebuild(struct heap *heap)
{
size_t i;

for (i = heap->n / 2; i >= 1; i--) {
float_down(heap, i);
}
}

static void
put_node(struct heap *heap, struct heap_node *node, size_t i)
{
heap->array[i] = node;
node->idx = i;
}

static void
swap_nodes(struct heap *heap, size_t i, size_t j)
{
struct heap_node *old_i = heap->array[i];
struct heap_node *old_j = heap->array[j];

put_node(heap, old_j, i);
put_node(heap, old_i, j);
}

static bool
float_up(struct heap *heap, size_t i)
{
bool moved = false;
size_t parent;

for (; i > 1; i = parent) {
parent = heap_parent__(i);
if (heap->array[parent]->priority >= heap->array[i]->priority) {
break;
}
swap_nodes(heap, parent, i);
moved = true;
}
return moved;
}

static void
float_down(struct heap *heap, size_t i)
{
while (!heap_is_leaf__(heap, i)) {
size_t left = heap_left__(i);
size_t right = heap_right__(i);
size_t max = i;

if (heap->array[left]->priority > heap->array[max]->priority) {
max = left;
}
if (right <= heap->n
&& heap->array[right]->priority > heap->array[max]->priority) {
max = right;
}
if (max == i) {
break;
}

swap_nodes(heap, max, i);
i = max;
}
}

static void
float_up_or_down(struct heap *heap, size_t i)
{
if (!float_up(heap, i)) {
float_down(heap, i);
}
}

163 changes: 163 additions & 0 deletions lib/heap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* Copyright (c) 2012 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef HEAP_H
#define HEAP_H 1

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/* A heap node, to be embedded inside the data structure in the heap. */
struct heap_node {
size_t idx;
uint32_t priority;
};

/* A max-heap. */
struct heap {
struct heap_node **array; /* Data in elements 1...n, element 0 unused. */
size_t n; /* Number of nodes currently in the heap. */
size_t allocated; /* Max 'n' before 'array' must be enlarged. */
};

/* Initialization. */
void heap_init(struct heap *);
void heap_destroy(struct heap *);
void heap_clear(struct heap *);
void heap_swap(struct heap *a, struct heap *b);
static inline size_t heap_count(const struct heap *);
static inline bool heap_is_empty(const struct heap *);

/* Insertion and deletion. */
void heap_insert(struct heap *, struct heap_node *, uint32_t priority);
void heap_change(struct heap *, struct heap_node *, uint32_t priority);
void heap_remove(struct heap *, struct heap_node *);
static inline struct heap_node *heap_pop(struct heap *);

/* Maximum. */
static inline struct heap_node *heap_max(const struct heap *);

/* The "raw" functions below do not preserve the heap invariants. After you
* call them, heap_max() will not necessarily return the right value until you
* subsequently call heap_rebuild(). */
void heap_raw_insert(struct heap *, struct heap_node *, uint32_t priority);
static inline void heap_raw_change(struct heap_node *, uint32_t priority);
void heap_raw_remove(struct heap *, struct heap_node *);
void heap_rebuild(struct heap *);

/* Iterates through each NODE in HEAP, where NODE->MEMBER must be a "struct
* heap_node". Iterates in heap level order, which in particular means that
* the first node visited is the maximum value in the heap.
*
* If a heap_raw_*() function has been called without a later call to
* heap_rebuild(), then the first node visited may not be the maximum
* element. */
#define HEAP_FOR_EACH(NODE, MEMBER, HEAP) \
for (((HEAP)->n > 0 \
? ASSIGN_CONTAINER(NODE, (HEAP)->array[1], MEMBER) \
: ((NODE) = NULL, 1)); \
(NODE) != NULL; \
((NODE)->MEMBER.idx < (HEAP)->n \
? ASSIGN_CONTAINER(NODE, \
(HEAP)->array[(NODE)->MEMBER.idx + 1], \
MEMBER) \
: ((NODE) = NULL, 1)))

/* Returns the index of the node that is the parent of the node with the given
* 'idx' within a heap. */
static inline size_t
heap_parent__(size_t idx)
{
return idx / 2;
}

/* Returns the index of the node that is the left child of the node with the
* given 'idx' within a heap. */
static inline size_t
heap_left__(size_t idx)
{
return idx * 2;
}

/* Returns the index of the node that is the right child of the node with the
* given 'idx' within a heap. */
static inline size_t
heap_right__(size_t idx)
{
return idx * 2 + 1;
}

/* Returns true if 'idx' is the index of a leaf node in 'heap', false
* otherwise. */
static inline bool
heap_is_leaf__(const struct heap *heap, size_t idx)
{
return heap_left__(idx) > heap->n;
}

/* Returns the number of elements in 'heap'. */
static inline size_t
heap_count(const struct heap *heap)
{
return heap->n;
}

/* Returns true if 'heap' is empty, false if it contains at least one
* element. */
static inline bool
heap_is_empty(const struct heap *heap)
{
return heap->n == 0;
}

/* Returns the largest element in 'heap'.
*
* The caller must ensure that 'heap' contains at least one element.
*
* The return value may be wrong (i.e. not the maximum element but some other
* element) if a heap_raw_*() function has been called without a later call to
* heap_rebuild(). */
static inline struct heap_node *
heap_max(const struct heap *heap)
{
return heap->array[1];
}

/* Removes an arbitrary node from 'heap', in O(1), maintaining the heap
* invariant. Returns the node removed.
*
* The caller must ensure that 'heap' contains at least one element. */
static inline struct heap_node *
heap_pop(struct heap *heap)
{
return heap->array[heap->n--];
}

/* Changes the priority of 'node' (which must be in 'heap') to 'priority'.
*
* After this call, heap_max() will no longer necessarily return the maximum
* value in the heap, and HEAP_FOR_EACH will no longer necessarily iterate in
* heap level order, until the next call to heap_rebuild(heap).
*
* This takes time O(1). */
static inline void
heap_raw_change(struct heap_node *node, uint32_t priority)
{
node->priority = priority;
}

#endif /* heap.h */
Loading

0 comments on commit 9597444

Please sign in to comment.