Commit 69f15678 authored by Pekka Pessi's avatar Pekka Pessi

su: added sofia-sip/heap.h and test for it, torture_heap.c.

darcs-hash:20070702150206-65a35-d69eddcc83d8c27d2c688de04dbc4f201dfdbb05.gz
parent 7780cd42
......@@ -18,7 +18,7 @@ bin_PROGRAMS = addrinfo localinfo
check_PROGRAMS = torture_su torture_su_port \
torture_su_alloc torture_su_time torture_su_tag \
test_htable torture_rbtree \
test_htable torture_rbtree torture_heap \
test_memmem torture_su_bm \
torture_su_root torture_su_timer \
test_su su_proxy test_poll $(OSXPROGS)
......@@ -28,7 +28,7 @@ check_PROGRAMS = torture_su torture_su_port \
TESTS = torture_su torture_su_port \
torture_su_alloc torture_su_time torture_su_tag \
test_htable torture_rbtree \
test_htable torture_rbtree torture_heap \
test_memmem torture_su_bm \
torture_su_root torture_su_timer \
run_addrinfo run_localinfo run_test_su \
......@@ -49,8 +49,8 @@ nobase_include_sofia_HEADERS = \
sofia-sip/su_tag_class.h sofia-sip/su_tagarg.h \
sofia-sip/su_tag_io.h sofia-sip/su_tag_inline.h \
sofia-sip/htable.h sofia-sip/htable2.h \
sofia-sip/rbtree.h sofia-sip/su_debug.h \
sofia-sip/su_log.h \
sofia-sip/rbtree.h sofia-sip/heap.h \
sofia-sip/su_debug.h sofia-sip/su_log.h \
sofia-sip/su_config.h sofia-sip/su_md5.h \
sofia-sip/su_uniqueid.h sofia-sip/su_bm.h \
sofia-sip/tstdef.h sofia-sip/su_os_nw.h \
......
/*
* This file is part of the Sofia-SIP package
*
* Copyright (C) 2005 Nokia Corporation.
*
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef SOFIA_SIP_HEAP_H
/** Defined when <sofia-sip/heap.h> has been included. */
#define SOFIA_SIP_HEAP_H
/**@file sofia-sip/heap.h
*
* Heap implementation.
*
* Note: this version can handle structures as entries, and it can be used
* without <su_alloc.h>.
*
* This file contain a hash table template for C. The hash tables are
* resizeable, and they usually contain pointers to entries. The
* declaration for template datatypes is instantiated with macro
* HEAP_DECLARE(). The prototypes for hashing functions are instantiated
* with macro HEAP_PROTOS(). The implementation is instantiated with
* macro HEAP_BODIES().
*
* The hash table template is most efficient when the hash value is
* precalculated and stored in each entry. The hash "function" given to the
* HEAP_BODIES() would then be something like macro
* @code
* #define HEAP_ENTRY_HASH(e) ((e).e_hash_value)
* @endcode
*
* When a entry with new identical hash key is added to the table, it can be
* either @e inserted (before any other entry with same key value) or
* @e appended.
*
* Example code can be found from <htable_test.c>.
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>.
*
* @date Created: Tue Sep 25 17:42:40 2001 ppessi
*
*/
/** Minimum size of heap */
#define HEAP_MIN_SIZE 31
/** Declare heap structure type.
*
* The macro HEAP_DECLARE() expands to a declaration for heap
* structure. The its typedef will be <em>prefix</em><code>_t</code>, the
* field names start with @a pr. The entry type is @a entrytype.
*
* @param sname name of struct
* @param pr heap type prefix
* @param entrytype entry type
*/
#define HEAP_DECLARE(sname, pr, entrytype) \
struct sname { \
unsigned pr##size; \
unsigned pr##used; \
entrytype *pr##heap; /**< Heap table itself */ \
}
/** Prototypes for heap.
*
* The macro HEAP_PROTOS() expands to the prototypes of heap
* functions. The function and type names start with @a prefix, the field
* names start with @a pr. The entry type is @a entrytype.
*
* @param scope scope of functions
* @param type heap type or typedef
* @param prefix function prefix
* @param entrytype entry type
*/
#define HEAP_PROTOS(scope, type, prefix, entrytype) \
scope int prefix##resize(void *a, type pr[1], size_t); \
scope int prefix##is_full(type const *); \
scope int prefix##add(type *pr, entrytype e); \
scope int prefix##remove(type *, size_t index)
/** Hash table implementation.
*
* The macro HEAP_BODIES() expands the heap functions. The function
* and type names start with @a prefix, the field names start with @a pr.
* The entry type is @a entrytype. The function (or macro) name returning
* hash value of each entry is given as @a hfun.
*
* @param scope scope of functions
* @param type hash table type
* @param prefix function prefix for heap
* @param pr field prefix for heap structure
* @param entrytype type of element
* @param cmp function or macro comparing two entries
* @param set function or macro assigning entry to array
* @param halloc function allocating or freeing memory
*/
#define HEAP_BODIES(scope, type, prefix, pr, entrytype, cmp, set, alloc) \
/** Resize heap. */ \
scope int prefix##resize(void *realloc_arg, \
type pr[1], \
size_t new_size) \
{ \
entrytype *heap; \
size_t bytes; \
\
(void)realloc_arg; \
\
if (new_size == 0) \
new_size = 2 * pr->pr##size + 1; \
if (new_size < HEAP_MIN_SIZE) \
new_size = HEAP_MIN_SIZE; \
\
bytes = new_size * (sizeof heap[0]); \
\
heap = alloc(realloc_arg, pr->pr##heap, bytes); \
if (!heap) \
return -1; \
\
pr->pr##size = new_size; \
if (pr->pr##used > new_size) \
pr->pr##used = new_size; \
pr->pr##heap = heap; \
\
return 0; \
} \
\
/** Check if heap is full */ \
scope \
int prefix##is_full(type const *pr) \
{ \
return pr->pr##heap == NULL || pr->pr##used >= pr->pr##size; \
} \
\
/** Sort heap from element at index upwards */ \
scope \
void prefix##sort(type *pr, size_t index) \
{ \
size_t top, left, right; \
entrytype *heap = pr->pr##heap; \
size_t used = pr->pr##used; \
\
top = index; \
\
for (;;) { \
entrytype swap; \
\
left = 2 * top; \
right = left + 1; \
\
if (left < used && cmp(heap[top], heap[left]) > 0) \
top = left; \
if (right < used && cmp(heap[top], heap[right]) > 0) \
top = right; \
\
if (top == index) \
break; \
\
swap = heap[index]; \
set(heap, index, heap[top]); \
set(heap, top, swap); \
index = top; \
} \
} \
\
/** Add an element to the heap */ \
scope \
int prefix##add(type *pr, entrytype e) \
{ \
size_t i, parent; \
entrytype *heap = pr->pr##heap; \
\
if (pr->pr##used >= pr->pr##size) \
return -1; \
\
for (i = pr->pr##used++; i > 0; i = parent) { \
parent = i / 2; \
if (cmp(e, heap[parent]) >= 0) \
break; \
set(heap, i, heap[parent]); \
} \
\
set(heap, i, e); \
\
return 0; \
} \
\
/** Remove element from heap */ \
scope \
int prefix##remove(type *pr, size_t index) \
{ \
entrytype *heap = pr->pr##heap; \
\
if (index >= pr->pr##used) \
return -1; \
\
set(heap, index, heap[--pr->pr##used]); \
\
prefix##sort(pr, index); \
\
return 0; \
} \
extern int const prefix##dummy
#endif /** !defined(HEAP_H) */
#include "config.h"
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
typedef struct {
unsigned key, value;
size_t index;
} entrytype;
#include <sofia-sip/heap.h>
typedef struct Heap Heap;
HEAP_DECLARE(Heap, pr_, entrytype);
HEAP_PROTOS(static inline, Heap, heapXX_, entrytype);
static inline
int cmp_entry(entrytype a, entrytype b)
{
if (a.key < b.key)
return -1;
else if (a.key > b.key)
return 1;
else
return 0;
}
static inline
void set_entry(entrytype *heap, size_t index, entrytype entry)
{
entry.index = index;
heap[index] = entry;
}
#define alloc(a, o, size) realloc((o), (size))
HEAP_BODIES(static inline, Heap, heapXX_, pr_, entrytype,
cmp_entry, set_entry, alloc);
/* ====================================================================== */
int tstflags;
#define TSTFLAGS tstflags
#include <sofia-sip/tstdef.h>
char name[] = "torture_heap";
int test_speed()
{
BEGIN();
Heap heap[1];
unsigned i, previous, n, N;
memset(heap, 0, sizeof heap);
TEST(heapXX_resize(NULL, heap, 0), 0);
N = 300000;
/* Add N entries in reverse order */
for (i = N; i > 0; i--) {
entrytype e = { i / 10, i };
if (heapXX_is_full(heap))
TEST(heapXX_resize(NULL, heap, 0), 0);
TEST(heapXX_is_full(heap), 0);
TEST(heapXX_add(heap, e), 0);
}
TEST(heap->pr_used, N);
for (i = 0; i < N; i++) {
TEST(heap->pr_heap[i].index, i);
}
for (i = 0; i < N; i++) {
heapXX_sort(heap, i);
}
for (i = 0; i < N; i++) {
TEST(heap->pr_heap[i].index, i);
}
/* Remove N entries */
previous = 0;
for (n = 0; heap->pr_used > 0; n++) {
TEST_1(previous <= heap->pr_heap[0].key);
previous = heap->pr_heap[0].key;
heapXX_remove(heap, 0);
}
TEST(n, N);
TEST(heapXX_resize(NULL, heap, 31), 0);
END();
}
void usage(int exitcode)
{
fprintf(stderr,
"usage: %s [-v] [-a]\n",
name);
exit(exitcode);
}
int main(int argc, char *argv[])
{
int retval = 0;
int i;
for (i = 1; argv[i]; i++) {
if (strcmp(argv[i], "-v") == 0)
tstflags |= tst_verbatim;
else if (strcmp(argv[i], "-a") == 0)
tstflags |= tst_abort;
else
usage(1);
}
retval |= test_speed(); fflush(stdout);
return retval;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment