From 76b2d2e5e07609b0d5b2847c4ad59f6ec30bc9b0 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 29 Apr 2011 21:52:22 +0200 Subject: [PATCH 1/4] Collection of a lot library worthy sources, many extracted from lumiera --- README | 1 + 1 file changed, 1 insertion(+) create mode 100644 README diff --git a/README b/README new file mode 100644 index 000000000..04bde299f --- /dev/null +++ b/README @@ -0,0 +1 @@ +Collection of various library alike sources which may eventually become a runtime library From 98d6ba396797755db0fc98ccd1a9340653708f86 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sun, 12 Jun 2011 03:55:19 +0200 Subject: [PATCH 2/4] priqueue implementation this adds a minimalistic priority queue based on a binary heap --- src/container/priqueue.c | 384 +++++++++++++++++++++++++++++++++++++++ src/container/priqueue.h | 142 +++++++++++++++ 2 files changed, 526 insertions(+) create mode 100644 src/container/priqueue.c create mode 100644 src/container/priqueue.h diff --git a/src/container/priqueue.c b/src/container/priqueue.c new file mode 100644 index 000000000..d4b4c1c4a --- /dev/null +++ b/src/container/priqueue.c @@ -0,0 +1,384 @@ +/* + priqueue - priority queue + + Copyright (C) + 2011 Christian Thaeter + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "priqueue.h" + + +#include +#include + + +NOBUG_DEFINE_FLAG (priqueue); + + +PriQueue +priqueue_init (PriQueue self, + size_t element_size, + priqueue_cmp_fn cmpfn, + priqueue_resize_fn resizefn) +{ + NOBUG_INIT_FLAG (priqueue); + TRACE (priqueue, "%p", self); + + REQUIRE (element_size); + REQUIRE (cmpfn); + + if (self) + { + self->queue = NULL; + self->element_size = element_size; + self->used = self->high_water = self->low_water = 0; + self->cmpfn = cmpfn; + + if (!resizefn) + resizefn = priqueue_clib_resize; + self->resizefn = resizefn; + + self = self->resizefn (self); + } + return self; +} + + + +PriQueue +priqueue_destroy (PriQueue self) +{ + TRACE (priqueue, "%p", self); + if (self) + { + WARN_IF (self->used, priqueue, "queue was not empty"); + self->used = 0; + self = self->resizefn (self); + } + return self; +} + + +PriQueue +priqueue_reserve (PriQueue self, unsigned elements) +{ + TRACE (priqueue, "%p %d", self, elements); + if (self) + { + if (self->used+elements >= self->high_water) + { + self->used += elements; + self = self->resizefn (self); + if (!self) + { + ERROR (priqueue, "resize failed"); + return NULL; + } + self->used -= elements; + } + self->low_water = 0; + } + + return self; +} + + +/* + supplied/default resize function based on realloc +*/ +PriQueue +priqueue_clib_resize (PriQueue self) +{ + if (self) + { + if (!self->queue) + { + INFO (priqueue, "%p: initial alloc", self); + self->queue = malloc (64*self->element_size); + ERROR_IF (!self->queue, priqueue, "%p: allocation failed", self); + if (!self->queue) + return NULL; + self->high_water = 64; + } + else + { + if (self->used) + { + if (self->used >= self->high_water) + { + unsigned newwater = self->high_water; + while (self->used >= newwater) + newwater *= 2; + + INFO (priqueue, "%p: resize %d -> %d", self, self->high_water, newwater); + + void* newqueue = realloc (self->queue, self->element_size * newwater); + ERROR_IF (!newqueue, priqueue, "%p: allocation failed", self); + if (!newqueue) + return NULL; + self->queue = newqueue; + self->high_water = newwater; + self->low_water = self->high_water/8-8; + TRACE (priqueue, "%p: low_water: %d", self, self->low_water); + } + else + { + INFO (priqueue, "%p: shrink %d -> %d", self, self->high_water, (self->low_water+8)*4); + void* newqueue = realloc (self->queue, self->element_size * (self->low_water+8)*4); + + ERROR_IF (!newqueue, priqueue, "allocation failed"); + if (!newqueue) + return NULL; + self->queue = newqueue; + self->high_water = (self->low_water+8)*4; + self->low_water = self->high_water/8-8; + TRACE (priqueue, "%p: low_water: %d", self, self->low_water); + } + } + else + { + INFO (priqueue, "%p: freeing", self); + free (self->queue); + self->queue = NULL; + } + } + } + return self; +} + + + +static inline void* +pq_index (PriQueue self, unsigned nth) +{ + return (char*)self->queue+self->element_size*nth; +} + + +static inline void +pq_up (PriQueue self, void* tmp) +{ + unsigned i = self->used; + unsigned p = i/2; + + while (p && self->cmpfn (tmp, pq_index(self, p-1)) < 0) + { + memcpy (pq_index (self, i-1), pq_index (self, p-1), self->element_size); + i=p; p=i/2; + } + + memcpy (pq_index (self, i-1), tmp, self->element_size); +} + + + +PriQueue +priqueue_insert (PriQueue self, void* element) +{ + TRACE (priqueue, "%p: insert %p", self, element); + + if (self && self->used >= self->high_water) + self = self->resizefn (self); + + if (self) + { + ++self->used; + pq_up (self, element); + } + return self; +} + + + + +static inline void +pq_down (PriQueue self, void* tmp) +{ + if (!self->used) + return; + + unsigned i = 1; + + while (i <= self->used/2) + { + unsigned n=i+i; + if (nused && self->cmpfn (pq_index(self, n-1), pq_index(self, n)) >= 0) + ++n; + + if (self->cmpfn (tmp, pq_index(self, n-1)) < 0) + break; + + memcpy (pq_index (self, i-1), pq_index (self, n-1), self->element_size); + i = n; + } + memcpy (pq_index (self, i-1), tmp, self->element_size); +} + + +PriQueue +priqueue_remove (PriQueue self) +{ + TRACE (priqueue, "%p: remove", self); + + if (self) + { + if (!self->used) + return NULL; + + --self->used; + pq_down (self, pq_index (self, self->used)); + + if (self->used < self->low_water) + self = self->resizefn (self); + } + + return self; +} + + + + + + + + + +#if 0 /* testing */ + +#include + + +void +nobug_priqueue_invariant (PriQueue self, int depth, const struct nobug_context invariant_context, void* extra) +{ + intptr_t n = 1+(intptr_t)extra; + + intptr_t m=n+n; + + if (self && depth && m <= self->used) + { + INVARIANT_ASSERT (self->cmpfn (pq_index(self, n-1), pq_index(self, m-1)) <= 0, "%d %d", (int)n-1, (int)m-2); + nobug_priqueue_invariant (self, depth-1, invariant_context, (void*)m-1); + + if (mused) + { + INVARIANT_ASSERT (self->cmpfn (pq_index(self, n-1), pq_index(self, m)) <= 0, "%d %d", (int)n-1, (int)m-1); + nobug_priqueue_invariant (self, depth-1, invariant_context, (void*)m); + } + } +} + + +static int +cmpintptr (void* a, void* b) +{ + return *(int*)a - *(int*)b; +} + + +int main() +{ + NOBUG_INIT; + + priqueue pq; + + PriQueue r; + + int data; + + r = priqueue_init (&pq, + sizeof (int), + cmpintptr, + NULL); + ENSURE (r==&pq); + +#if 0 + data = 10; + r = priqueue_insert (&pq, &data); + ENSURE (r==&pq); + ECHO("inserted %d", data); +#endif + +#if 0 + data = 5; + r = priqueue_insert (&pq, &data); + ENSURE (r==&pq); + ECHO("inserted %d", data); +#endif + + +#if 0 + data = 15; + r = priqueue_insert (&pq, &data); + ENSURE (r==&pq); + ECHO("inserted %d", data); + + data = 20; + r = priqueue_insert (&pq, &data); + ENSURE (r==&pq); + ECHO("inserted %d", data); +#endif + + + + +#if 1 + for (int i = 0; i < 100000; ++i) + { + data = i; + r = priqueue_insert (&pq, &data); + ENSURE (r==&pq); + ECHO("inserted %d", data); + } + +#endif + +#if 1 + for (int i = 0; i < 100000; ++i) + { + ECHO("PRE %d", i); + data = rand()%100000; + r = priqueue_insert (&pq, &data); + ENSURE (r==&pq); + ECHO("inserted %d", data); + } +#endif + + NOBUG_INVARIANT(priqueue, &pq, 100, NULL); + + +#if 1 + for (int i = 0; pq.used; ++i) + { + ECHO ("TOP: %d", *(int*)priqueue_peek (&pq)); + r = priqueue_remove (&pq); + ENSURE (r==&pq); + ECHO("poped"); + } +#endif + + + r = priqueue_destroy (&pq); + ENSURE (r==&pq); + + return 0; +} + + + + +#endif diff --git a/src/container/priqueue.h b/src/container/priqueue.h new file mode 100644 index 000000000..16c0e7ce3 --- /dev/null +++ b/src/container/priqueue.h @@ -0,0 +1,142 @@ +/* + priqueue - priority queue + + Copyright (C) + 2011 Christian Thaeter + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef PRIQUEUE_H +#define PRIQUEUE_H + +/* + This is a very minimalistic priority queue implementation based on a binary heap. Only 'insert' 'remove' and 'peek' operations are supported + Memory is dynamically managed through an optionally user supplied 'resize' function. + Elements in the queue have a user-define size but should kept as small as possible. This is only intended to associate lightweight data such as + a key and a pointer, storing the key in the element can save dereferencing cost and thus improve cache locality. It must be noted + that elements in the queue get moved in memory, so referencing them is invalid. + + There are API's (yet) to change the priority of an arbitary element or remove any but the topmost element. The idea is to let expired elements + sink to the top and detect that and then remove them. +*/ + + +#include + +typedef struct priqueue_struct priqueue; +typedef priqueue* PriQueue; + +/* function to compare 2 keys, mandatory */ +typedef int (*priqueue_cmp_fn)(void*, void*); + +/* called when used hits the high or low water marks and initially by priqueue_init() (with queue==NULL) + or at priqueue_destroy (with queue != NULL, used elements == 0), + optional. + + Must be aware of resizes by more than just incrementing the queue by one + */ +typedef PriQueue (*priqueue_resize_fn) (PriQueue); + + +/* + this structure is not opaque to make it possible to implement a low level resize operation which has to reallocate + the queue and update the high and low water marks. + */ +struct priqueue_struct +{ + void* queue; + size_t element_size; + unsigned used; + unsigned high_water; // elements in the queue + unsigned low_water; // size for shrinking the queue + + priqueue_cmp_fn cmpfn; + + priqueue_resize_fn resizefn; +}; + + + + + + +PriQueue +priqueue_init (PriQueue self, + size_t element_size, + priqueue_cmp_fn cmpfn, + priqueue_resize_fn resizefn); + + + +PriQueue +priqueue_destroy (PriQueue self); + + +/* + calls resize to match for at least 'elements' in the queue and then sets low_water to 0, disabling shrinking + note that on overflow resize will re-enable low_water if it is not aware of this +*/ +PriQueue +priqueue_reserve (PriQueue self, unsigned elements); + + +/* + supplied/default resize function based on realloc + initially allocates an array for 64 elements, + doubles this when the high water mark is hit, + shrinks at high_water/8-8 (that is, 64 is the minimum size) +*/ +PriQueue +priqueue_clib_resize (PriQueue self); + + + + +/* + insert a new element into the priority queue + the element will be copied + returns NULL on error +*/ +PriQueue +priqueue_insert (PriQueue self, void* element); + + +/* + returns a pointer to the topmost element + note that this pointer is only valid as long no insert or remove is called + returns NULL when the queue is empty +*/ +static inline void* +priqueue_peek (PriQueue self) +{ + if (self && self->queue) + return self->queue; + + return NULL; +} + + +/* + removes the topmost element + returns NULL on error (queue emtpy, resize failure) + */ +PriQueue +priqueue_remove (PriQueue self); + + + +#endif + From ff51ea54e665eace07b78af463105a985f0e5b08 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Tue, 21 Jun 2011 23:35:50 +0200 Subject: [PATCH 3/4] Add a copy function to the priqueue by providing a custom copy function one can adjust otherwise non-copyable elements. This should be used cautionary because dereferencing elements may poison the cache and thus have some considerable performance impact (profile this) --- src/container/priqueue.c | 18 ++++++++++++------ src/container/priqueue.h | 5 +++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/container/priqueue.c b/src/container/priqueue.c index d4b4c1c4a..b06c70d77 100644 --- a/src/container/priqueue.c +++ b/src/container/priqueue.c @@ -33,6 +33,7 @@ PriQueue priqueue_init (PriQueue self, size_t element_size, priqueue_cmp_fn cmpfn, + priqueue_copy_fn copyfn, priqueue_resize_fn resizefn) { NOBUG_INIT_FLAG (priqueue); @@ -48,6 +49,10 @@ priqueue_init (PriQueue self, self->used = self->high_water = self->low_water = 0; self->cmpfn = cmpfn; + if (!copyfn) + copyfn = memcpy; + self->copyfn = copyfn; + if (!resizefn) resizefn = priqueue_clib_resize; self->resizefn = resizefn; @@ -177,11 +182,11 @@ pq_up (PriQueue self, void* tmp) while (p && self->cmpfn (tmp, pq_index(self, p-1)) < 0) { - memcpy (pq_index (self, i-1), pq_index (self, p-1), self->element_size); + self->copyfn (pq_index (self, i-1), pq_index (self, p-1), self->element_size); i=p; p=i/2; } - memcpy (pq_index (self, i-1), tmp, self->element_size); + self->copyfn (pq_index (self, i-1), tmp, self->element_size); } @@ -222,10 +227,10 @@ pq_down (PriQueue self, void* tmp) if (self->cmpfn (tmp, pq_index(self, n-1)) < 0) break; - memcpy (pq_index (self, i-1), pq_index (self, n-1), self->element_size); + self->copyfn (pq_index (self, i-1), pq_index (self, n-1), self->element_size); i = n; } - memcpy (pq_index (self, i-1), tmp, self->element_size); + self->copyfn (pq_index (self, i-1), tmp, self->element_size); } @@ -257,7 +262,7 @@ priqueue_remove (PriQueue self) -#if 0 /* testing */ +#ifdef PRIQUEUE_TEST /* testing */ #include @@ -303,10 +308,11 @@ int main() r = priqueue_init (&pq, sizeof (int), cmpintptr, + NULL, NULL); ENSURE (r==&pq); -#if 0 +#if 1 data = 10; r = priqueue_insert (&pq, &data); ENSURE (r==&pq); diff --git a/src/container/priqueue.h b/src/container/priqueue.h index 16c0e7ce3..b74062d80 100644 --- a/src/container/priqueue.h +++ b/src/container/priqueue.h @@ -42,6 +42,9 @@ typedef priqueue* PriQueue; /* function to compare 2 keys, mandatory */ typedef int (*priqueue_cmp_fn)(void*, void*); +/* function to copy elements, optional. Has the same prototype as memcpy which is used by default */ +typedef void *(*priqueue_copy_fn)(void *dest, const void *src, size_t n); + /* called when used hits the high or low water marks and initially by priqueue_init() (with queue==NULL) or at priqueue_destroy (with queue != NULL, used elements == 0), optional. @@ -64,6 +67,7 @@ struct priqueue_struct unsigned low_water; // size for shrinking the queue priqueue_cmp_fn cmpfn; + priqueue_copy_fn copyfn; priqueue_resize_fn resizefn; }; @@ -77,6 +81,7 @@ PriQueue priqueue_init (PriQueue self, size_t element_size, priqueue_cmp_fn cmpfn, + priqueue_copy_fn copyfn, priqueue_resize_fn resizefn); From df749271d00294a9084518ceca4d4f9d2fa17678 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Tue, 5 Jul 2011 05:23:27 +0200 Subject: [PATCH 4/4] cleanup in the priqueue test-code --- src/container/priqueue.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/container/priqueue.c b/src/container/priqueue.c index b06c70d77..3db1034b5 100644 --- a/src/container/priqueue.c +++ b/src/container/priqueue.c @@ -294,10 +294,12 @@ cmpintptr (void* a, void* b) return *(int*)a - *(int*)b; } +NOBUG_DEFINE_FLAG (priqueue_test); int main() { NOBUG_INIT; + NOBUG_INIT_FLAG (priqueue_test); priqueue pq; @@ -316,14 +318,14 @@ int main() data = 10; r = priqueue_insert (&pq, &data); ENSURE (r==&pq); - ECHO("inserted %d", data); + TRACE (priqueue_test, "inserted %d", data); #endif #if 0 data = 5; r = priqueue_insert (&pq, &data); ENSURE (r==&pq); - ECHO("inserted %d", data); + TRACE (priqueue_test, "inserted %d", data); #endif @@ -331,36 +333,35 @@ int main() data = 15; r = priqueue_insert (&pq, &data); ENSURE (r==&pq); - ECHO("inserted %d", data); + TRACE (priqueue_test, "inserted %d", data); data = 20; r = priqueue_insert (&pq, &data); ENSURE (r==&pq); - ECHO("inserted %d", data); + TRACE (priqueue_test, "inserted %d", data); #endif #if 1 - for (int i = 0; i < 100000; ++i) + for (int i = 0; i < 1000000; ++i) { data = i; r = priqueue_insert (&pq, &data); ENSURE (r==&pq); - ECHO("inserted %d", data); + TRACE (priqueue_test, "inserted %d", data); } #endif #if 1 - for (int i = 0; i < 100000; ++i) + for (int i = 0; i < 1000000; ++i) { - ECHO("PRE %d", i); - data = rand()%100000; + data = rand()%1000000; r = priqueue_insert (&pq, &data); ENSURE (r==&pq); - ECHO("inserted %d", data); + TRACE (priqueue_test, "inserted %d", data); } #endif @@ -370,10 +371,9 @@ int main() #if 1 for (int i = 0; pq.used; ++i) { - ECHO ("TOP: %d", *(int*)priqueue_peek (&pq)); + TRACE (priqueue_test, "TOP: %d", *(int*)priqueue_peek (&pq)); r = priqueue_remove (&pq); ENSURE (r==&pq); - ECHO("poped"); } #endif