heap: convert inline implemenatation to regular one
authorMarko Kreen <markokr@gmail.com>
Tue, 12 Oct 2010 10:47:55 +0000 (13:47 +0300)
committerMarko Kreen <markokr@gmail.com>
Tue, 12 Oct 2010 10:58:48 +0000 (13:58 +0300)
the minor performance advantages are not worth
more complex use.

test/compile.c
test/test_heap.c
usual/event.c
usual/heap-impl.h [deleted file]
usual/heap.c [new file with mode: 0644]
usual/heap.h [new file with mode: 0644]

index be28720491a5e8499f9a210cb35f94cb2830d215..5b1b3b90497a1fe831d7f9c50b8396be5a54a40d 100644 (file)
@@ -9,7 +9,7 @@
 #include <usual/event.h>
 #include <usual/fileutil.h>
 #include <usual/hashtab-impl.h>
-#include <usual/heap-impl.h>
+#include <usual/heap.h>
 #include <usual/list.h>
 #include <usual/logging.h>
 #include <usual/lookup3.h>
index b77005e7acc9172c66f58b52c463906e67cfff98..cc451c36b64f49e5e75238f662c1a84fec9a7a5d 100644 (file)
@@ -1,4 +1,4 @@
-#include <usual/base.h>
+#include <usual/heap.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -7,11 +7,7 @@
 
 #include "test_common.h"
 
-static void my_save_pos(void *p, unsigned i);
-
-#define SAVE_POS(p, i) my_save_pos(p, i)
-
-#include <usual/heap-impl.h>
+#include <usual/heap.c>
 
 struct MyNode {
        int value;
@@ -19,7 +15,7 @@ struct MyNode {
 };
 
 /* min-heap */
-static inline bool heap_is_better(const void *a, const void *b)
+static bool heap_is_better(const void *a, const void *b)
 {
        const struct MyNode *aa = a, *bb = b;
        return (aa->value < bb->value);
@@ -89,7 +85,7 @@ static const char *check(struct Heap *heap, int i)
 static const char *my_insert(struct Heap *heap, int value)
 {
        struct MyNode *my = make_node(value);
-       if (!heap_insert(heap, my))
+       if (!heap_push(heap, my))
                return "FAIL";
        return check(heap, value);
 }
@@ -100,7 +96,7 @@ static const char *my_remove(struct Heap *heap, unsigned idx)
        if (idx >= heap->used)
                return "NEXIST";
        n = heap->data[idx];
-       heap_delete_pos(heap, idx);
+       heap_remove(heap, idx);
        free(n);
        return check(heap, 0);
 }
@@ -122,10 +118,10 @@ static const char *my_clean(struct Heap *heap)
 
 static void test_heap_basic(void *p)
 {
-       struct Heap heap[1];
+       struct Heap *heap;
        int i;
 
-       heap_init(heap);
+       heap = heap_create(heap_is_better, my_save_pos, USUAL_ALLOC);
 
        str_check(my_remove(heap, 0), "NEXIST");
        str_check(my_insert(heap, 0), "OK");
index 183ff5da1443c0daa34beaa42e0d490a8d4106bd..908bf5b953f961a4e97b6f349553eaf6a8587993 100644 (file)
@@ -31,6 +31,7 @@
 #include <usual/statlist.h>
 #include <usual/socket.h>
 #include <usual/signal.h>
+#include <usual/heap.h>
 
 #ifndef MSG_NOSIGNAL
 #define MSG_NOSIGNAL 0
 /* extra event flag to track if event is added */
 #define EV_ACTIVE 0x80
 
-/* load heap code */
-static inline void ev_save_pos(struct event *ev, int pos);
-#define SAVE_POS(ev, pos) ev_save_pos(ev, pos)
-#include <usual/heap-impl.h>
-
 
 struct event_base {
        /* pending timeouts */
-       struct Heap timeout_heap;
+       struct Heap *timeout_heap;
 
        /* fd events */
        struct StatList fd_list;
@@ -172,14 +168,15 @@ static usec_t convert_timeout(struct event_base *base, struct timeval *tv)
        return val;
 }
 
-static inline bool heap_is_better(const void *a, const void *b)
+static bool ev_is_better(const void *a, const void *b)
 {
        const struct event *ev1 = a, *ev2 = b;
        return ev1->timeout_val < ev2->timeout_val;
 }
 
-static inline void ev_save_pos(struct event *ev, int pos)
+static void ev_save_pos(void *obj, unsigned pos)
 {
+       struct event *ev = obj;
        ev->timeout_idx = pos;
 }
 
@@ -263,7 +260,11 @@ struct event_base *event_base_new(void)
                return NULL;
 
        /* initialize timeout and fd areas */
-       heap_init(&base->timeout_heap);
+       base->timeout_heap = heap_create(ev_is_better, ev_save_pos, USUAL_ALLOC);
+       if (!base->timeout_heap) {
+               free(base);
+               return NULL;
+       }
        statlist_init(&base->fd_list, "fd_list");
 
        /* initialize signal areas */
@@ -289,7 +290,7 @@ void event_base_free(struct event_base *base)
        }
        if (base == current_base)
                current_base = NULL;
-       heap_destroy(&base->timeout_heap);
+       heap_destroy(base->timeout_heap);
        free(base->pfd_event);
        free(base->pfd_list);
        sig_close(base);
@@ -363,7 +364,7 @@ int event_del(struct event *ev)
 
        /* remove from timeout tree */
        if (ev->flags & EV_TIMEOUT) {
-               heap_delete_pos(&ev->base->timeout_heap, ev->timeout_idx);
+               heap_remove(ev->base->timeout_heap, ev->timeout_idx);
                ev->flags &= ~EV_TIMEOUT;
        }
 
@@ -392,7 +393,7 @@ int event_add(struct event *ev, struct timeval *timeout)
        if (timeout) {
                if (ev->flags & EV_PERSIST)
                        goto err_inval;
-               if (!heap_reserve(&base->timeout_heap, 1))
+               if (!heap_reserve(base->timeout_heap, 1))
                        return -1;
        } else {
                if (ev->flags & EV_TIMEOUT)
@@ -416,7 +417,7 @@ int event_add(struct event *ev, struct timeval *timeout)
        if (timeout) {
                ev->timeout_val = convert_timeout(base, timeout);
                ev->flags |= EV_TIMEOUT;
-               heap_insert(&base->timeout_heap, ev);
+               heap_push(base->timeout_heap, ev);
        }
        ev->ev_idx = -1;
        ev->flags |= EV_ACTIVE;
@@ -447,7 +448,7 @@ static void deliver_event(struct event *ev, short flags)
 
 static inline struct event *get_smallest_timeout(struct event_base *base)
 {
-       return heap_get_top(&base->timeout_heap);
+       return heap_top(base->timeout_heap);
 }
 
 /* decide how long poll() should sleep */
diff --git a/usual/heap-impl.h b/usual/heap-impl.h
deleted file mode 100644 (file)
index 865f432..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Binary Heap.
- *
- * Copyright (c) 2009  Marko Kreen, Skype Technologies OÜ
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-/** @file
- * Binary heap.
- *
- * Summary - binary heap is sort of binary tree held inside array,
- * with following 2 properties:
- * - heap property: each node is "better" than it's childs.
- * - shape property: binary tree is complete, meaning all levels
- *   except the last one are fully filled.
- *
- * Instead of "min"- or "max"-heap, this is "best"-heap,
- * as it operates with user-defined heap_is_better() functions,
- * which is used to bubble elements on top.  Main reason
- * for such design is to make the comparisions inline.
- */
-
-/*
- * If user wants to delete elements from the middle of heap,
- * this macro should be used to keep track where the element
- * is located.
- */
-#ifndef SAVE_POS
-#define SAVE_POS(ptr, pos)
-#endif
-
-#include <usual/base.h>
-
-struct Heap {
-       void **data;
-       unsigned allocated;
-       unsigned used;
-};
-
-/*
- * For user to be defined.
- *
- * Should return true if a needs to reach top before b,
- * false if not or equal.
- */
-static inline bool heap_is_better(const void *a, const void *b);
-
-
-/*
- * Low-level operations.
- */
-
-static inline unsigned _heap_get_parent(unsigned i)
-{
-       return (i - 1) / 2;
-}
-
-static inline unsigned _heap_get_child(unsigned i, unsigned child_nr)
-{
-       return 2*i + 1 + child_nr;
-}
-
-static inline bool _heap_is_better(struct Heap *h, unsigned i1, unsigned i2)
-{
-       return heap_is_better(h->data[i1], h->data[i2]);
-}
-
-static inline void _heap_set(struct Heap *h, unsigned i, void *ptr)
-{
-       h->data[i] = ptr;
-       SAVE_POS(ptr, i);
-}
-
-static inline void _heap_swap(struct Heap *h, unsigned i1, unsigned i2)
-{
-       void *tmp = h->data[i1];
-       _heap_set(h, i1, h->data[i2]);
-       _heap_set(h, i2, tmp);
-}
-
-static void _heap_bubble_up(struct Heap *h, unsigned i)
-{
-       unsigned p;
-       while (i > 0) {
-               p = _heap_get_parent(i);
-               if (!_heap_is_better(h, i, p))
-                       break;
-               _heap_swap(h, i, p);
-               i = p;
-       }
-}
-
-static void _heap_bubble_down(struct Heap *h, unsigned i)
-{
-       unsigned c = _heap_get_child(i, 0);
-       while (c < h->used) {
-               if (c + 1 < h->used) {
-                       if (_heap_is_better(h, c + 1, c))
-                               c = c + 1;
-               }
-               if (!_heap_is_better(h, c, i))
-                       break;
-               _heap_swap(h, i, c);
-               i = c;
-               c = _heap_get_child(i, 0);
-       }
-}
-
-static bool _heap_make_room(struct Heap *h, unsigned extra)
-{
-       void *tmp;
-       unsigned newalloc;
-
-       if (h->used + extra < h->allocated)
-               return true;
-
-       newalloc = h->allocated * 2;
-       if (newalloc < 32)
-               newalloc = 32;
-       if (newalloc < h->used + extra)
-               newalloc = h->used + extra;
-
-       tmp = realloc(h->data, newalloc * sizeof(void *));
-       if (!tmp)
-               return false;
-       h->data = tmp;
-       h->allocated = newalloc;
-       return true;
-}
-
-
-/*
- * Actual API.
- */
-
-
-static void heap_init(struct Heap *h)
-{
-       h->data = NULL;
-       h->allocated = 0;
-       h->used = 0;
-}
-
-static void heap_destroy(struct Heap *h)
-{
-       free(h->data);
-       heap_init(h);
-}
-
-static inline void *heap_get_top(struct Heap *h)
-{
-       return (h->used > 0) ? h->data[0] : NULL;
-}
-
-static inline bool heap_reserve(struct Heap *h, unsigned extra)
-{
-       if (h->used + extra < h->allocated)
-               return true;
-
-       return _heap_make_room(h, extra);
-}
-
-static bool heap_insert(struct Heap *h, void *ptr)
-{
-       unsigned i;
-
-       if (!heap_reserve(h, 1))
-               return false;
-
-       i = h->used++;
-       _heap_set(h, i, ptr);
-       _heap_bubble_up(h, i);
-       return true;
-}
-
-static void heap_delete_pos(struct Heap *h, unsigned pos)
-{
-       unsigned last;
-
-       if (pos >= h->used)
-               return;
-
-       last = --h->used;
-       if (pos == last) {
-               h->data[last] = NULL;
-               return;
-       }
-
-       _heap_set(h, pos, h->data[last]);
-       h->data[last] = NULL;
-
-       if (pos > 0 && _heap_is_better(h, pos, _heap_get_parent(pos)))
-               _heap_bubble_up(h, pos);
-       else
-               _heap_bubble_down(h, pos);
-}
-
-static void heap_delete_top(struct Heap *h)
-{
-       heap_delete_pos(h, 0);
-}
-
-/* example and avoid 'unused' warnings */
-static inline void _heap_example(void *el)
-{
-       struct Heap h;
-       void *top;
-       heap_init(&h);
-       heap_insert(&h, el);
-       top = heap_get_top(&h);
-       heap_delete_top(&h);
-       heap_destroy(&h);
-}
-
diff --git a/usual/heap.c b/usual/heap.c
new file mode 100644 (file)
index 0000000..27b807b
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Binary Heap.
+ *
+ * Copyright (c) 2009  Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <usual/heap.h>
+
+struct Heap {
+       void **data;
+
+       unsigned allocated;
+       unsigned used;
+
+       heap_is_better_f is_better;
+       heap_save_pos_f save_pos;
+
+       CxMem *cx;
+};
+
+/*
+ * Low-level operations.
+ */
+
+#define inline __attribute__((always_inline))
+
+static unsigned _heap_get_parent(unsigned i)
+{
+       return (i - 1) / 2;
+}
+
+static unsigned _heap_get_child(unsigned i, unsigned child_nr)
+{
+       return 2*i + 1 + child_nr;
+}
+
+static bool _heap_is_better(struct Heap *h, unsigned i1, unsigned i2)
+{
+       return h->is_better(h->data[i1], h->data[i2]);
+}
+
+static void _heap_set(struct Heap *h, unsigned i, void *ptr)
+{
+       h->data[i] = ptr;
+       if (h->save_pos)
+               h->save_pos(ptr, i);
+}
+
+static void _heap_swap(struct Heap *h, unsigned i1, unsigned i2)
+{
+       void *tmp = h->data[i1];
+       _heap_set(h, i1, h->data[i2]);
+       _heap_set(h, i2, tmp);
+}
+
+static void _heap_bubble_up(struct Heap *h, unsigned i)
+{
+       unsigned p;
+       while (i > 0) {
+               p = _heap_get_parent(i);
+               if (!_heap_is_better(h, i, p))
+                       break;
+               _heap_swap(h, i, p);
+               i = p;
+       }
+}
+
+static void _heap_bubble_down(struct Heap *h, unsigned i)
+{
+       unsigned c = _heap_get_child(i, 0);
+       while (c < h->used) {
+               if (c + 1 < h->used) {
+                       if (_heap_is_better(h, c + 1, c))
+                               c = c + 1;
+               }
+               if (!_heap_is_better(h, c, i))
+                       break;
+               _heap_swap(h, i, c);
+               i = c;
+               c = _heap_get_child(i, 0);
+       }
+}
+
+static void rebalance(struct Heap *h, unsigned pos)
+{
+       if (pos == 0) {
+               _heap_bubble_down(h, pos);
+       } else if (pos == h->used - 1) {
+               _heap_bubble_up(h, pos);
+       } else if (_heap_is_better(h, pos, _heap_get_parent(pos))) {
+               _heap_bubble_up(h, pos);
+       } else {
+               _heap_bubble_down(h, pos);
+       }
+}
+
+/*
+ * Actual API.
+ */
+
+
+struct Heap *heap_create(heap_is_better_f is_better_cb, heap_save_pos_f save_pos_cb, CxMem *cx)
+{
+       struct Heap *h;
+       
+       h = cx_alloc0(cx, sizeof(*cx));
+       if (!h)
+               return NULL;
+
+       h->save_pos = save_pos_cb;
+       h->is_better = is_better_cb;
+       h->cx = cx;
+
+       return h;
+}
+
+void heap_destroy(struct Heap *h)
+{
+       cx_free(h->cx, h->data);
+       cx_free(h->cx, h);
+}
+
+bool heap_reserve(struct Heap *h, unsigned extra)
+{
+       void *tmp;
+       unsigned newalloc;
+
+       if (h->used + extra < h->allocated)
+               return true;
+
+       newalloc = h->allocated * 2;
+       if (newalloc < 32)
+               newalloc = 32;
+       if (newalloc < h->used + extra)
+               newalloc = h->used + extra;
+
+       tmp = realloc(h->data, newalloc * sizeof(void *));
+       if (!tmp)
+               return false;
+       h->data = tmp;
+       h->allocated = newalloc;
+       return true;
+}
+
+void *heap_top(struct Heap *h)
+{
+       return (h->used > 0) ? h->data[0] : NULL;
+}
+
+bool heap_push(struct Heap *h, void *ptr)
+{
+       unsigned pos;
+
+       if (h->used >= h->allocated) {
+               if (!heap_reserve(h, 1))
+                       return false;
+       }
+
+       pos = h->used++;
+       _heap_set(h, pos, ptr);
+       _heap_bubble_up(h, pos);
+       return true;
+}
+
+void *heap_remove(struct Heap *h, unsigned pos)
+{
+       unsigned last;
+       void *obj;
+
+       if (pos >= h->used)
+               return NULL;
+
+       obj = h->data[pos];
+
+       last = --h->used;
+       _heap_set(h, pos, h->data[last]);
+       h->data[last] = NULL;
+
+       rebalance(h, pos);
+
+       return obj;
+}
+
+void *heap_pop(struct Heap *h)
+{
+       return heap_remove(h, 0);
+}
+
diff --git a/usual/heap.h b/usual/heap.h
new file mode 100644 (file)
index 0000000..44a3ceb
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Binary Heap.
+ *
+ * Copyright (c) 2009  Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/** @file
+ * Binary heap.
+ *
+ * Binary heap is sort of binary tree held inside array,
+ * with following 2 properties:
+ * - heap property: each node is "better" than it's childs.
+ * - shape property: binary tree is complete, meaning all levels
+ *   except the last one are fully filled.
+ *
+ * Instead of "min"- or "max"-heap, this is "best"-heap,
+ * as it operates with user-defined heap_is_better() functions,
+ * which is used to bubble elements on top.
+ */
+
+#ifndef _USUAL_HEAP_H_
+#define _USUAL_HEAP_H_
+
+#include <usual/cxalloc.h>
+
+/**
+ * Object comparision function.
+ *
+ * Should return true if a needs to reach top before b,
+ * false if not or equal.
+ */
+typedef bool (*heap_is_better_f)(const void *a, const void *b);
+
+/**
+ * Heap position storage.
+ *
+ * If user wants to delete elements from the middle of heap,
+ * this function should be used to keep track where the element
+ * is located.
+ */
+typedef void (*heap_save_pos_f)(void *a, unsigned pos);
+
+/**
+ * Heap object.
+ */
+struct Heap;
+
+
+/**
+ * Create new heap object.
+ *
+ * @param is_better_cb  Callback to decide priority.
+ * @param save_pos_cb   Callback to store current index.
+ * @param cx            Allocation context.
+ */
+struct Heap *heap_create(
+       heap_is_better_f is_better_cb,
+       heap_save_pos_f save_pos_cb,
+       CxMem *cx);
+
+/** Release memory allocated by heap */
+void heap_destroy(struct Heap *h);
+
+/** Put new object into heap */
+bool heap_push(struct Heap *h, void *ptr);
+
+/** Remove and return topmost object from heap */
+void *heap_pop(struct Heap *h);
+
+/** Return topmost object in heap */
+void *heap_top(struct Heap *h);
+
+/** Remove and return any object from heap by index */
+void *heap_remove(struct Heap *h, unsigned pos);
+
+/**
+ * Reserve room for more elements.
+ *
+ * Returns false if allocation failed.
+ */
+bool heap_reserve(struct Heap *h, unsigned extra);
+
+#endif
+