integrate priority queue: adjust imports and doxygen comments

This commit is contained in:
Fischlurch 2013-09-13 04:18:16 +02:00
parent 87a84a931f
commit fc3cc1bc98
4 changed files with 469 additions and 65 deletions

View file

@ -99,6 +99,7 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( gui_dbg, debugging);
NOBUG_CPP_DEFINE_FLAG_PARENT ( library_dbg, debugging); NOBUG_CPP_DEFINE_FLAG_PARENT ( library_dbg, debugging);
NOBUG_CPP_DEFINE_FLAG_PARENT ( mpool_dbg, library_dbg); NOBUG_CPP_DEFINE_FLAG_PARENT ( mpool_dbg, library_dbg);
NOBUG_CPP_DEFINE_FLAG_PARENT ( psplay_dbg, library_dbg); NOBUG_CPP_DEFINE_FLAG_PARENT ( psplay_dbg, library_dbg);
NOBUG_CPP_DEFINE_FLAG_PARENT ( priqueue, library_dbg);
NOBUG_CPP_DEFINE_FLAG_PARENT ( resourcecollector_dbg, library_dbg); NOBUG_CPP_DEFINE_FLAG_PARENT ( resourcecollector_dbg, library_dbg);
NOBUG_CPP_DEFINE_FLAG_PARENT ( mutex_dbg, library_dbg); NOBUG_CPP_DEFINE_FLAG_PARENT ( mutex_dbg, library_dbg);
NOBUG_CPP_DEFINE_FLAG_PARENT ( cond_dbg, library_dbg); NOBUG_CPP_DEFINE_FLAG_PARENT ( cond_dbg, library_dbg);

View file

@ -1,8 +1,8 @@
/* /*
priqueue - priority queue PriQueue - simple heap based priority queue
Copyright (C) Copyright (C) Lumiera.org
2011 Christian Thaeter <ct@pipapo.org> 2011, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as modify it under the terms of the GNU General Public License as
@ -17,16 +17,18 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "priqueue.h" * *****************************************************/
#include <nobug.h> #include "lib/priqueue.h"
#include "include/logging.h"
#include <stdint.h> #include <stdint.h>
#include <nobug.h>
NOBUG_DEFINE_FLAG (priqueue);
PriQueue PriQueue
@ -36,7 +38,6 @@ priqueue_init (PriQueue self,
priqueue_copy_fn copyfn, priqueue_copy_fn copyfn,
priqueue_resize_fn resizefn) priqueue_resize_fn resizefn)
{ {
NOBUG_INIT_FLAG (priqueue);
TRACE (priqueue, "%p", self); TRACE (priqueue, "%p", self);
REQUIRE (element_size); REQUIRE (element_size);
@ -78,6 +79,7 @@ priqueue_destroy (PriQueue self)
} }
PriQueue PriQueue
priqueue_reserve (PriQueue self, unsigned elements) priqueue_reserve (PriQueue self, unsigned elements)
{ {
@ -102,9 +104,7 @@ priqueue_reserve (PriQueue self, unsigned elements)
} }
/*
supplied/default resize function based on realloc
*/
PriQueue PriQueue
priqueue_clib_resize (PriQueue self) priqueue_clib_resize (PriQueue self)
{ {

View file

@ -1,8 +1,8 @@
/* /*
priqueue - priority queue PRIQUEUE.h - simple heap based priority queue
Copyright (C) Copyright (C) Lumiera.org
2011 Christian Thaeter <ct@pipapo.org> 2011, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as modify it under the terms of the GNU General Public License as
@ -17,54 +17,67 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#ifndef PRIQUEUE_H /** @file priqueue.h
#define PRIQUEUE_H ** Simple 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.
This is a very minimalistic priority queue implementation based on a binary heap. Only 'insert' 'remove' and 'peek' operations are supported ** Elements in the queue have a user-define size but should kept as small as possible.
Memory is dynamically managed through an optionally user supplied 'resize' function. ** This is only intended to associate lightweight data such as a key and a pointer,
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 ** storing the key in the element can save dereferencing cost and thus improve
a key and a pointer, storing the key in the element can save dereferencing cost and thus improve cache locality. It must be noted ** cache locality.
that elements in the queue get moved in memory, so referencing them is invalid. **
** @warning elements in the queue get moved in memory, so referencing them is not allowed.
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. ** @todo we might add operations to change the priority of an arbitrary element or remove
*/ ** any but the topmost element. The idea is to let expired elements sink to the top
** and just detect and remove them on next access.
**
** @see backend::engine::SchedulerFrontend
**
*/
#include <stdlib.h> #ifndef LIB_PRIQUEUE_H
#define LIB_PRIQUEUE_H
#include "lib/error.h"
typedef struct priqueue_struct priqueue; typedef struct priqueue_struct priqueue;
typedef priqueue* PriQueue; typedef priqueue* PriQueue;
/* function to compare 2 keys, mandatory */ /** function to compare 2 keys, mandatory */
typedef int (*priqueue_cmp_fn)(void*, void*); typedef int (*priqueue_cmp_fn)(void*, void*);
/* function to copy elements, optional. Has the same prototype as memcpy which is used by default */ /** 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); 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) /** 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), * or at priqueue_destroy (with queue != NULL, used elements == 0), optional.
optional. *
* @note must be aware of resizes by more than just incrementing the queue by one
Must be aware of resizes by more than just incrementing the queue by one
*/ */
typedef PriQueue (*priqueue_resize_fn) (PriQueue); 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 * @remarks this structure is not opaque to make it possible
the queue and update the high and low water marks. * to implement a low level resize operation
* which has to reallocate the queue and update
* the high and low water marks.
*/ */
struct priqueue_struct struct priqueue_struct
{ {
void* queue; void* queue;
size_t element_size; size_t element_size;
unsigned used; unsigned used;
unsigned high_water; // elements in the queue unsigned high_water; ///< elements in the queue
unsigned low_water; // size for shrinking the queue unsigned low_water; ///< size for shrinking the queue
priqueue_cmp_fn cmpfn; priqueue_cmp_fn cmpfn;
priqueue_copy_fn copyfn; priqueue_copy_fn copyfn;
@ -90,40 +103,41 @@ PriQueue
priqueue_destroy (PriQueue self); priqueue_destroy (PriQueue self);
/* /**
calls resize to match for at least 'elements' in the queue and then sets low_water to 0, disabling shrinking * calls resize to match for at least 'elements' in the queue
note that on overflow resize will re-enable low_water if it is not aware of this * and then sets low_water to 0, disabling shrinking
*/ * @note on overflow resize will re-enable low_water if it is not aware of this
*/
PriQueue PriQueue
priqueue_reserve (PriQueue self, unsigned elements); priqueue_reserve (PriQueue self, unsigned elements);
/* /**
supplied/default resize function based on realloc * supplied/default resize function based on realloc
initially allocates an array for 64 elements, * initially allocates an array for 64 elements,
doubles this when the high water mark is hit, * doubles this when the high water mark is hit,
shrinks at high_water/8-8 (that is, 64 is the minimum size) * shrinks at high_water/8-8 (that is, 64 is the minimum size)
*/ */
PriQueue PriQueue
priqueue_clib_resize (PriQueue self); priqueue_clib_resize (PriQueue self);
/* /**
insert a new element into the priority queue * insert a new element into the priority queue
the element will be copied * the element will be copied
returns NULL on error * @return \c NULL on error
*/ */
PriQueue PriQueue
priqueue_insert (PriQueue self, void* element); priqueue_insert (PriQueue self, void* element);
/* /**
returns a pointer to the topmost element * @return pointer to the topmost element, \NULL on empty queue
note that this pointer is only valid as long no insert or remove is called * @note returned pointer is only valid as long
returns NULL when the queue is empty * as no insert or remove is called
*/ */
static inline void* static inline void*
priqueue_peek (PriQueue self) priqueue_peek (PriQueue self)
{ {
@ -134,14 +148,13 @@ priqueue_peek (PriQueue self)
} }
/* /**
removes the topmost element * removes the topmost element
returns NULL on error (queue emtpy, resize failure) * @return \c NULL on error (empty queue, resize failure)
*/ */
PriQueue PriQueue
priqueue_remove (PriQueue self); priqueue_remove (PriQueue self);
#endif #endif/*LIB_PRIQUEUE_H*/

View file

@ -0,0 +1,390 @@
/*
PriQueue - simple heap based priority queue
Copyright (C) Lumiera.org
2011, Christian Thaeter <ct@pipapo.org>
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 "lib/priqueue.h"
#include "include/logging.h"
#include <stdint.h>
#include <nobug.h>
PriQueue
priqueue_init (PriQueue self,
size_t element_size,
priqueue_cmp_fn cmpfn,
priqueue_copy_fn copyfn,
priqueue_resize_fn resizefn)
{
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 (!copyfn)
copyfn = memcpy;
self->copyfn = copyfn;
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;
}
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)
{
self->copyfn (pq_index (self, i-1), pq_index (self, p-1), self->element_size);
i=p; p=i/2;
}
self->copyfn (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 (n<self->used && self->cmpfn (pq_index(self, n-1), pq_index(self, n)) >= 0)
++n;
if (self->cmpfn (tmp, pq_index(self, n-1)) < 0)
break;
self->copyfn (pq_index (self, i-1), pq_index (self, n-1), self->element_size);
i = n;
}
self->copyfn (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;
}
#ifdef PRIQUEUE_TEST /* testing */
#include <stdio.h>
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 (m<self->used)
{
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;
}
NOBUG_DEFINE_FLAG (priqueue_test);
int main()
{
NOBUG_INIT;
NOBUG_INIT_FLAG (priqueue_test);
priqueue pq;
PriQueue r;
int data;
r = priqueue_init (&pq,
sizeof (int),
cmpintptr,
NULL,
NULL);
ENSURE (r==&pq);
#if 1
data = 10;
r = priqueue_insert (&pq, &data);
ENSURE (r==&pq);
TRACE (priqueue_test, "inserted %d", data);
#endif
#if 0
data = 5;
r = priqueue_insert (&pq, &data);
ENSURE (r==&pq);
TRACE (priqueue_test, "inserted %d", data);
#endif
#if 0
data = 15;
r = priqueue_insert (&pq, &data);
ENSURE (r==&pq);
TRACE (priqueue_test, "inserted %d", data);
data = 20;
r = priqueue_insert (&pq, &data);
ENSURE (r==&pq);
TRACE (priqueue_test, "inserted %d", data);
#endif
#if 1
for (int i = 0; i < 1000000; ++i)
{
data = i;
r = priqueue_insert (&pq, &data);
ENSURE (r==&pq);
TRACE (priqueue_test, "inserted %d", data);
}
#endif
#if 1
for (int i = 0; i < 1000000; ++i)
{
data = rand()%1000000;
r = priqueue_insert (&pq, &data);
ENSURE (r==&pq);
TRACE (priqueue_test, "inserted %d", data);
}
#endif
NOBUG_INVARIANT(priqueue, &pq, 100, NULL);
#if 1
for (int i = 0; pq.used; ++i)
{
TRACE (priqueue_test, "TOP: %d", *(int*)priqueue_peek (&pq));
r = priqueue_remove (&pq);
ENSURE (r==&pq);
}
#endif
r = priqueue_destroy (&pq);
ENSURE (r==&pq);
return 0;
}
#endif