diff --git a/Makefile.am b/Makefile.am index bf7ebec19..69cc34288 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,7 +34,7 @@ include $(top_srcdir)/admin/Makefile.am # core include $(top_srcdir)/src/lib/Makefile.am -include $(top_srcdir)/src/backend/Makefile.am +#include $(top_srcdir)/src/backend/Makefile.am # plugins #include $(top_srcdir)/src... diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 77fc26d90..0e0c2f22e 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -24,20 +24,25 @@ liblumi_a_CPPFLAGS = -I$(top_srcdir)/src/ liblumi_a_SOURCES = \ $(liblumi_a_srcdir)/plugin.c \ $(liblumi_a_srcdir)/error.c \ - $(liblumi_a_srcdir)/framerate.c \ $(liblumi_a_srcdir)/mutex.c \ $(liblumi_a_srcdir)/rwlock.c \ $(liblumi_a_srcdir)/condition.c \ - $(liblumi_a_srcdir)/references.c + $(liblumi_a_srcdir)/references.c \ + $(liblumi_a_srcdir)/uuid.c \ + $(liblumi_a_srcdir)/safeclib.c \ + $(liblumi_a_srcdir)/cuckoo.c \ + $(liblumi_a_srcdir)/mrucache.c noinst_HEADERS += \ $(liblumi_a_srcdir)/plugin.h \ $(liblumi_a_srcdir)/error.h \ - $(liblumi_a_srcdir)/framerate.h \ $(liblumi_a_srcdir)/locking.h \ $(liblumi_a_srcdir)/mutex.h \ $(liblumi_a_srcdir)/rwlock.h \ $(liblumi_a_srcdir)/condition.h \ - $(liblumi_a_srcdir)/references.h - + $(liblumi_a_srcdir)/references.h \ + $(liblumi_a_srcdir)/uuid.h \ + $(liblumi_a_srcdir)/safeclib.h \ + $(liblumi_a_srcdir)/cuckoo.h \ + $(liblumi_a_srcdir)/mrucache.h diff --git a/src/lib/condition.c b/src/lib/condition.c index 7427b431d..1bfa2ef96 100644 --- a/src/lib/condition.c +++ b/src/lib/condition.c @@ -25,6 +25,8 @@ * @file Condition variables */ +LUMIERA_ERROR_DEFINE (CONDITION_DESTROY, "condition destroy failed"); + /** * Initialize a condition variable * @param self is a pointer to the condition variable to be initialized @@ -53,9 +55,9 @@ lumiera_condition_destroy (LumieraCondition self) if (self) { if (pthread_mutex_destroy (&self->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_DESTROY); else if (pthread_cond_destroy (&self->cond)) - LUMIERA_DIE; + LUMIERA_DIE (CONDITION_DESTROY); } return self; } diff --git a/src/lib/condition.h b/src/lib/condition.h index b2fd76c63..066c6b267 100644 --- a/src/lib/condition.h +++ b/src/lib/condition.h @@ -28,6 +28,7 @@ * @file Condition variables, header */ +LUMIERA_ERROR_DECLARE (CONDITION_DESTROY); /** * Condition variables. @@ -59,10 +60,10 @@ lumiera_condition_signal (LumieraCondition self) { REQUIRE (self); if (pthread_mutex_lock (&self->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_LOCK); pthread_cond_signal (&self->cond); if (pthread_mutex_unlock (&self->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_UNLOCK); } /** @@ -74,10 +75,10 @@ lumiera_condition_broadcast (LumieraCondition self) { REQUIRE (self); if (pthread_mutex_lock (&self->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_LOCK); pthread_cond_broadcast (&self->cond); if (pthread_mutex_unlock (&self->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_UNLOCK); } @@ -124,7 +125,7 @@ lumiera_conditionacquirer_init (LumieraConditionacquirer self, LumieraCondition self->state = state; if (state == LUMIERA_LOCKED) if (pthread_mutex_lock (&cond->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_LOCK); return self; } @@ -141,7 +142,7 @@ lumiera_conditionacquirer_lock (LumieraConditionacquirer self) REQUIRE (self->state == LUMIERA_UNLOCKED, "mutex already locked"); if (pthread_mutex_lock (&self->cond->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_LOCK); self->state = LUMIERA_LOCKED; } @@ -172,7 +173,7 @@ lumiera_conditionacquirer_unlock (LumieraConditionacquirer self) REQUIRE (self); REQUIRE (self->state == LUMIERA_LOCKED, "mutex was not locked"); if (pthread_mutex_unlock (&self->cond->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_UNLOCK); self->state = LUMIERA_UNLOCKED; } diff --git a/src/lib/cuckoo.c b/src/lib/cuckoo.c new file mode 100644 index 000000000..0d0b4efef --- /dev/null +++ b/src/lib/cuckoo.c @@ -0,0 +1,497 @@ +/* + A cuckoo hash implementation + + Copyright (C) + 2008, 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 "cuckoo.h" + +#include + +enum compact_state + { + COMPACTING_OFF, + COMPACTING_AUTO, + COMPACTED + }; + +struct cuckoo_struct +{ + size_t size; /* t1 = 4*size; t2 = 2*size; t3 = size */ + size_t itemsize; + + cuckoo_hashfunc h1; /* hash function */ + uint32_t r1; /* random, reset for each rehash */ + cuckoo_hashfunc h2; + uint32_t r2; + cuckoo_hashfunc h3; + uint32_t r3; + + cuckoo_cmpfunc cmp; + + void* t1; + void* t2; + void* t3; + + unsigned maxloops; /* sqrt (4 * size) */ + + enum compact_state autocompact; + size_t elements; +}; + + +static inline uint32_t cuckoo_fast_prng () +{ + static uint32_t rnd = 0xbabeface; + return rnd = rnd<<1 ^ ((rnd>>30) & 1) ^ ((rnd>>2) & 1); +} + +Cuckoo +cuckoo_init (Cuckoo self, + cuckoo_hashfunc h1, + cuckoo_hashfunc h2, + cuckoo_hashfunc h3, + cuckoo_cmpfunc cmp, + size_t itemsize, + unsigned startsize) +{ + if (!self) + return NULL; + + self->size = 1<itemsize = itemsize; + self->h1 = h1; + self->r1 = cuckoo_fast_prng (); + self->h2 = h2; + self->r2 = cuckoo_fast_prng (); + self->h3 = h3; + self->r3 = cuckoo_fast_prng (); + + self->cmp = cmp; + + self->t1 = calloc (self->size * 4, itemsize); + self->t2 = calloc (self->size * 2, itemsize); + self->t3 = calloc (self->size, itemsize); + if (!self->t1 || !self->t2 || !self->t3) + { + free (self->t1); + free (self->t2); + free (self->t3); + return NULL; + } + + self->maxloops = 1; + while (self->maxloops * self->maxloops < self->size * 4) + ++self->maxloops; + + self->autocompact = COMPACTING_AUTO; + self->elements = 0; + return self; +} + +Cuckoo +cuckoo_new (cuckoo_hashfunc h1, + cuckoo_hashfunc h2, + cuckoo_hashfunc h3, + cuckoo_cmpfunc cmp, + size_t itemsize, + unsigned startsize) +{ + Cuckoo self = malloc (sizeof (struct cuckoo_struct)); + if (!cuckoo_init (self, h1, h2, h3, cmp, itemsize, startsize)) + { + free (self); + return NULL; + } + return self; +} + +Cuckoo +cuckoo_destroy (Cuckoo self) +{ + if (self) + { + free (self->t1); + free (self->t2); + free (self->t3); + } + return self; +} + + +void +cuckoo_free (Cuckoo self) +{ + free (cuckoo_destroy (self)); +} + + +static inline int +iszero (void* mem, size_t size) +{ + while (size && !*(int*)mem) + { + size -= sizeof (int); + mem += sizeof (int); + } + return !size; +} + +static inline void +xmemcpy (void* dst, void* src, size_t size) +{ + while (size) + { + size -= sizeof (int); + *(int*)(dst + size) = *(int*)(src + size); + } +} + + +static int +cuckoo_insert_internal_ (Cuckoo self, void* item) +{ + void* pos; + char tmp[self->itemsize]; + + for (unsigned n = 0; n < self->maxloops; ++n) + { + /* find nest */ + pos = self->t1 + self->itemsize * (self->h1 (item, self->r1) % (4*self->size)); + /* kick old egg out */ + xmemcpy (tmp, pos, self->itemsize); + /* lay egg */ + xmemcpy (pos, item, self->itemsize); + + if (iszero (tmp, self->itemsize)) + return 1; + + /* find nest */ + pos = self->t2 + self->itemsize * (self->h2 (tmp, self->r2) % (2*self->size)); + /* kick old egg out */ + xmemcpy (item, pos, self->itemsize); + /* lay egg */ + xmemcpy (pos, tmp, self->itemsize); + + if (iszero (item, self->itemsize)) + return 1; + + /* find nest */ + pos = self->t3 + self->itemsize * (self->h3 (item, self->r3) % self->size); + /* kick old egg out */ + xmemcpy (tmp, pos, self->itemsize); + /* lay egg */ + xmemcpy (pos, item, self->itemsize); + + if (iszero (tmp, self->itemsize)) + return 1; + + /* copy tmp to item, which will be reinserted on next interation / after rehashing */ + xmemcpy (item, tmp, self->itemsize); + } + return 0; +} + + +static void +cuckoo_rehash (Cuckoo self) +{ + retry1: + + self->r1 = cuckoo_fast_prng (); + + for (size_t i = 0; i < 4*self->size; ++i) + { + unsigned n; + void* pos = self->t1 + self->itemsize * i; + if (!iszero (pos, self->itemsize)) + { + for (n = 0; n < self->maxloops; ++n) + { + unsigned hash = self->h1 (pos, self->r1) % (4*self->size); + if (hash != i) + { + char t[self->itemsize]; + void* hpos = self->t1 + self->itemsize * hash; + xmemcpy (t, hpos, self->itemsize); + xmemcpy (hpos, pos, self->itemsize); + xmemcpy (pos, t, self->itemsize); + if (iszero (t, self->itemsize)) + break; + } + else + break; + } + if (n == self->maxloops) + goto retry1; + } + } + + retry2: + self->r2 = cuckoo_fast_prng (); + + for (size_t i = 0; i < 2*self->size; ++i) + { + unsigned n; + void* pos = self->t2 + self->itemsize * i; + if (!iszero (pos, self->itemsize)) + { + for (n = 0; n < self->maxloops; ++n) + { + unsigned hash = self->h2 (pos, self->r2) % (2*self->size); + if (hash != i) + { + char t[self->itemsize]; + void* hpos = self->t2 + self->itemsize * hash; + xmemcpy (t, hpos, self->itemsize); + xmemcpy (hpos, pos, self->itemsize); + xmemcpy (pos, t, self->itemsize); + if (iszero (t, self->itemsize)) + break; + } + else + break; + } + if (n == self->maxloops) + goto retry2; + } + } + + retry3: + self->r3 = cuckoo_fast_prng (); + + for (size_t i = 0; i < self->size; ++i) + { + unsigned n; + void* pos = self->t3 + self->itemsize * i; + if (!iszero (pos, self->itemsize)) + { + for (n = 0; n < self->maxloops; ++n) + { + unsigned hash = self->h3 (pos, self->r3) % self->size; + if (hash != i) + { + char t[self->itemsize]; + void* hpos = self->t3 + self->itemsize * hash; + xmemcpy (t, hpos, self->itemsize); + xmemcpy (hpos, pos, self->itemsize); + xmemcpy (pos, t, self->itemsize); + if (iszero (t, self->itemsize)) + break; + } + else + break; + } + if (n == self->maxloops) + goto retry3; + } + } +} + +static int +cuckoo_grow (Cuckoo self) +{ + /* rotate hashfuncs, tables, randoms */ + cuckoo_hashfunc th = self->h3; + self->h3 = self->h2; + self->h2 = self->h1; + self->h1 = th; + + uint32_t tr = self->r3; + self->r3 = self->r2; + self->r2 = self->r1; + self->r1 = tr; + + void* tt = self->t3; + self->t3 = self->t2; + self->t2 = self->t1; + + /* double new base size */ + self->size *= 2; + while (self->maxloops * self->maxloops < self->size * 4) + ++self->maxloops; + + /* alloc new t1 */ + self->t1 = calloc (self->size * 4, self->itemsize); + if (!self->t1) + { + self->t1 = tt; + return 0; + } + + /* reinsert tt */ + size_t ttsize = self->size / 2; + for (size_t i = 0; i < ttsize; ++i) + { + void* pos = tt + i * self->itemsize; + if (!iszero (pos, self->itemsize)) + { + while (!cuckoo_insert_internal_ (self, pos)) + cuckoo_rehash (self); + } + } + free (tt); + + self->autocompact = COMPACTING_AUTO; + return 1; +} + + +int +cuckoo_reserve (Cuckoo self, size_t more) +{ + int ret = 1; + if (more) + while (self->elements+self->maxloops+more >= 6*self->size) + ret = cuckoo_grow (self); + + self->autocompact = COMPACTING_OFF; + return ret; +} + + +int +cuckoo_compact (Cuckoo self) +{ + if (self->autocompact == COMPACTED) + return 1; + + if (self->size > 2 && self->elements < self->size * 3) + { + cuckoo_hashfunc th = self->h1; + self->h1 = self->h2; + self->h2 = self->h3; + self->h3 = th; + + uint32_t tr = self->r1; + self->r1 = self->r2; + self->r2 = self->r3; + self->r3 = tr; + + void* tt = self->t1; + self->t1 = self->t2; + self->t2 = self->t3; + + /* halve base size */ + self->size /= 2; + while (self->maxloops * self->maxloops >= self->size * 4) + --self->maxloops; + + /* alloc new t3 */ + self->t3 = calloc (self->size, self->itemsize); + if (!self->t3) + { + self->t3 = tt; + return 0; + } + + /* reinsert tt */ + size_t ttsize = self->size * 8; + for (size_t i = 0; i < ttsize; ++i) + { + void* pos = tt + i * self->itemsize; + if (!iszero (pos, self->itemsize)) + { + --self->elements; + cuckoo_insert (self, pos); + } + } + free (tt); + self->autocompact = COMPACTED; + } + return 1; +} + + +int +cuckoo_insert (Cuckoo self, void* item) +{ + char tmp[self->itemsize]; + + void* found; + if ((found = cuckoo_find (self, item))) + { + xmemcpy (found, item, self->itemsize); + return 1; + } + + xmemcpy (tmp, item, self->itemsize); + + for (unsigned n = 6; n; --n) /* rehash/grow loop */ + { + if (cuckoo_insert_internal_ (self, tmp)) + { + ++self->elements; + return 1; + } + + if (self->elements > n*self->size) + { + n = 6; + if (!cuckoo_grow (self)) + return 0; + } + else + cuckoo_rehash (self); + } + return 0; +} + + +void* +cuckoo_find (Cuckoo self, void* item) +{ + void* pos; + + pos = self->t1 + self->itemsize * (self->h1 (item, self->r1) % (4*self->size)); + if (!iszero (pos, self->itemsize) && self->cmp (item, pos)) + return pos; + + pos = self->t2 + self->itemsize * (self->h2 (item, self->r2) % (2*self->size)); + if (!iszero (pos, self->itemsize) && self->cmp (item, pos)) + return pos; + + pos = self->t3 + self->itemsize * (self->h3 (item, self->r3) % self->size); + if (!iszero (pos, self->itemsize) && self->cmp (item, pos)) + return pos; + + return NULL; +} + + +void +cuckoo_remove (Cuckoo self, void* item) +{ + if (item) + { + memset (item, 0, self->itemsize); + --self->elements; + + if (self->autocompact == COMPACTING_AUTO && self->size > 2 && self->elements <= self->size*2) + cuckoo_compact (self); + } +} + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/cuckoo.h b/src/lib/cuckoo.h new file mode 100644 index 000000000..db1d33da3 --- /dev/null +++ b/src/lib/cuckoo.h @@ -0,0 +1,167 @@ +/* + A cuckoo hash implementation + + Copyright (C) + 2008, 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 CUCKOO_H +#define CUCKOO_H + +/** + * @file Cuckoo hashing + * This hashing gives guaranteed O(1) lookup complexity and amortized O(1) insert and remove complexity. + * Hash tables by default grow and shrink automatically. It is posible to preallocate entries and turn + * automatic shrinking off taking out the memory management factors for insert and remove operations. + * This implementation uses 3 Tables which exponential growing sizes. + */ + +#include +#include + +struct cuckoo_struct; +typedef struct cuckoo_struct * Cuckoo; + +/** + * Hash function. + * User supplied universal hash function + * @param item address of the item to be hashed + * @param r pseudo random number, 31 significant bits, regenerated on each rehash + * @return hash value + */ +typedef size_t (*cuckoo_hashfunc)(const void* item, const uint32_t r); + +/** + * Compare function. + * User supplied compare function + * @param item1 address of the first item + * @param item2 address of the second item + * @return 1 when the items are identical, 0 otherwise + */ +typedef int (*cuckoo_cmpfunc)(const void* item1, const void* item2); + +/** + * Initialize a cuckoo hash. + * @param self pointer to a uninitialized cuckoo datastructure + * @param h1 hash function for the first table + * @param h2 hash function for the second table + * @param h3 hash function for the third table + * @param cmp function which compares two keys + * @param startsize initial size of table t3, as 2's exponent + * @return The initialized hashtable or NULL at allocation failure + */ +Cuckoo +cuckoo_init (Cuckoo self, + cuckoo_hashfunc h1, + cuckoo_hashfunc h2, + cuckoo_hashfunc h3, + cuckoo_cmpfunc cmp, + size_t itemsize, + unsigned startsize); + +/** + * Allocate a new cuckoo hash. + * @param h1 hash function for the first table + * @param h2 hash function for the second table + * @param h3 hash function for the third table + * @param cmp function which compares two keys + * @param startsize initial size of table t3, as 2's exponent + * @return The initialized hashtable or NULL at allocation failure + */ +Cuckoo +cuckoo_new (cuckoo_hashfunc h1, + cuckoo_hashfunc h2, + cuckoo_hashfunc h3, + cuckoo_cmpfunc cmp, + size_t itemsize, + unsigned startsize); + +/** + * Destroy a cuckoo hash. + * Frees internal used resources. + * @param self cuckoo hash to destroy + * @return The now uninitialized hashtable + */ +Cuckoo +cuckoo_destroy (Cuckoo self); + +/** + * Deallocate a cuckoo hash. + * @param self handle of the hash table to be freed + */ +void +cuckoo_free (Cuckoo self); + +/** + * Insert an element into a hash. + * amortized O(1) complexity because it may rarely rehash the tables or even grow them. + * see cuckoo_reserve() about how to preallocate entries to prevent the growing costs. + * @param self handle to the hash table + * @param item pointer to a item to be inserted + * @return 1 on successful insert, 0 on allocation failure + * Note: at failure there is one arbitary hash cell lost! + */ +int +cuckoo_insert (Cuckoo self, void* item); + +/** + * Find a value by key in a hash. + * @param self handle to the hash table + * @param item pointer to an item to be looked up + * @return found object or NULL if not present + */ +void* +cuckoo_find (Cuckoo self, void* item); + +/** + * Remove a item from a hash. + * amortized O(1) complexity when it automatically shrink its tables. + * guaranteed O(1) complexity when automatic shrinking is turned off. + * see cuckoo_reserve() about how turn automatic shrinking off. + * @param self handle to the hash table + * @param item pointer to the item to be removed + */ +void +cuckoo_remove (Cuckoo self, void* item); + +/** + * Shrink the hash sizes when possible and turn autocompacting on. + * only useful when autmatic shrinking is turned off. + * see cuckoo_reserve() about how turn shrinking off. + * @param self handle to the hash table + */ +int +cuckoo_compact (Cuckoo self); + +/** + * Prereserve hash entries and turn autocompacting off. + * @param self handle to the hash table + * @param more number of entries to be additionally reserved + * In rare circumstances inserting into a hash which has reserved some entries + * it may still need to rehash (or even rarer) to grow the table. + * While autocompacting is turned off, removing is completely O(1). + */ +int +cuckoo_reserve (Cuckoo self, size_t more); + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/error.h b/src/lib/error.h index 24c73f0a5..e71c0cdfa 100644 --- a/src/lib/error.h +++ b/src/lib/error.h @@ -39,7 +39,8 @@ extern "C" { * Abort unconditionally with a 'Fatal Error!' message. * This macro is used whenever the program end up in a invalid state from which no runtime recovery is possible */ -#define LUMIERA_DIE do { NOBUG_ERROR(NOBUG_ON, "Fatal Error!"); abort(); } while(0) +#define LUMIERA_DIE(err) \ + do { NOBUG_ERROR(NOBUG_ON, "Fatal Error: %s ", strchr(LUMIERA_ERROR_##err, ':')); abort(); } while(0) /** * Forward declare an error constant. @@ -58,7 +59,8 @@ extern const char* LUMIERA_ERROR_##err #define LUMIERA_ERROR_DEFINE(err, msg) \ const char* LUMIERA_ERROR_##err = "LUMIERA_ERROR_" #err ":" msg -/** Helper macro to raise an error for the current thread. +/** + * Helper macro to raise an error for the current thread. * This macro eases setting an error. It adds NoBug logging support to the low level error handling. * @param flag NoBug flag describing the subsystem where the error was raised * @param err name of the error without the 'LUMIERA_ERROR_' prefix (example: NO_MEMORY) diff --git a/src/lib/framerate.h b/src/lib/framerate.h deleted file mode 100644 index 581d7fa78..000000000 --- a/src/lib/framerate.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - framerate.h - framerate calculations - - Copyright (C) Lumiera.org - 2008, 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 LUMIERA_FRAMERATE_H -#define LUMIERA_FRAMERATE_H - -#include - -#include "lib/error.h" - -/** - * @file Framerate calculations, header. - */ - - -/** - * framerates are defined as a rational number - * for example NTSC with 30000/1001fps - */ -struct lumiera_framerate_struct -{ - unsigned n; //numerator - unsigned d; //denominator -}; - -typedef struct lumiera_framerate_struct lumiera_framerate; -typedef lumiera_framerate* LumieraFramerate; - -typedef signed long lumiera_framepos; - -LUMIERA_ERROR_DECLARE(FRAMERATE_ILLEGAL_TIME); -LUMIERA_ERROR_DECLARE(FRAMERATE_ILLEGAL_FRAME); - -#define LUMIERA_FRAMEPOS_ERROR LONG_MIN - -/** - * Get the frame number of a given time at a given frame rate. - * frame indexing starts with 1 - * @param framerate is a pointer to the framerate used, defined as rational number. Must be given. - * @param time is a pointer to a lumiera_time which shall be converted. - * @return frame at the given time or LUMIERA_FRAMEPOS_ERROR on error. - */ -static inline lumiera_framepos -lumiera_framerate_frame_get_time (const LumieraFramerate framerate, LumieraTime time) -{ - REQUIRE (framerate); - if (!time || time->tv_sec == (time_t)-1) - { - lumiera_error_set(LUMIERA_ERROR_FRAMERATE_ILLEGAL_TIME); - return LUMIERA_FRAMEPOS_ERROR; - } - - /* we add a magic microsecond for rounding, because of integer truncation frames may be calculated at most 1us earlier, - the idea is to compensate odd framerates which fall out off microsecond precision. - */ - return ((time->tv_sec * 1000000ULL + time->tv_usec + /*magic*/1) * framerate->n)/(framerate->d * 1000000ULL) + 1; -} - -/** - * Get the start time for a frame. - * frame indexing starts with 1 - * @param framerate is a pointer to the framerate used, defined as rational number. Must be given. - * @param time is a pointer to a lumiera_time which shall take the result. - * @param frame frame number to be converted to time. This frame number must be greater than 0. - * @return the pointer given in time or NULL on error (or when it was given as time). - */ -static inline LumieraTime -lumiera_framerate_time_get_time_frame (const LumieraFramerate framerate, - LumieraTime time, - lumiera_framepos frame) -{ - REQUIRE (framerate); - if (time) - { - if (frame < 1) - { - lumiera_error_set(LUMIERA_ERROR_FRAMERATE_ILLEGAL_FRAME); - return NULL; - } - - unsigned long long usec = ((frame-1) * framerate->d * 1000000ULL - (/*magic*/frame>1?1:0)) / framerate->n; - - time->tv_sec = usec / 1000000; - time->tv_usec = usec % 1000000; - } - return time; -} - -#endif diff --git a/src/lib/llist.h b/src/lib/llist.h index 4f77b20dc..efb3f5202 100644 --- a/src/lib/llist.h +++ b/src/lib/llist.h @@ -88,6 +88,7 @@ typedef llist * LList; typedef const llist * const_LList; typedef llist ** LList_ref; + /** * Macro to instantiate a local llist. * @param name of the llist node @@ -95,6 +96,14 @@ typedef llist ** LList_ref; #define LLIST_AUTO(name) llist name = {&name,&name} +/* + some macros for convenience +*/ +#define llist_insert_head(list, element) llist_insert_next (list, element) +#define llist_insert_tail(list, element) llist_insert_prev (list, element) +#define llist_head llist_next +#define llist_tail llist_prev + /** * cast back from a member of a structure to a pointer of the structure */ @@ -129,6 +138,30 @@ typedef llist ** LList_ref; ! llist_is_end (node, list); \ llist_backward (&node)) + +/** + * Iterate forward over a range. + * @param start first node to be interated + * @param end node after the last node be iterated + * @param node pointer to the iterated node + */ +#define LLIST_FORRANGE(start, end, node) \ + for (LList node = start; \ + node != end; \ + llist_forward (&node)) + +/** + * Iterate backward over a range. + * @param rstart first node to be interated + * @param rend node before the last node be iterated + * @param node pointer to the iterated node + */ +#define LLIST_FORRANGE_REV(rstart, rend, node) \ + for (LList node = rstart; \ + node != rend; \ + llist_backward (&node)) + + /** * Consume a list from head. * The body of this statement should remove the head from the list, else it would be a infinite loop @@ -290,7 +323,7 @@ LLIST_FUNC (LList llist_insert_next (LList self, LList next), /** * Insert a node before another. * @param self node before which we want to insert - * @param prev node which shall be inserted nefore self. Could already linked to a list from where it will be removed. + * @param prev node which shall be inserted before self. Could already linked to a list from where it will be removed. * @return self */ LLIST_FUNC (LList llist_insert_prev (LList self, LList prev), @@ -314,10 +347,9 @@ LLIST_FUNC (LList llist_insertlist_next (LList self, LList next), if (!llist_is_empty (next)) { self->next->prev = next->prev; - self->next = next->next; - - next->next->prev = self; next->prev->next = self->next; + self->next = next->next; + next->next->prev = self; next->prev = next->next = next; } @@ -335,10 +367,9 @@ LLIST_FUNC (LList llist_insertlist_prev (LList self, LList prev), if (!llist_is_empty (prev)) { self->prev->next = prev->next; - self->prev = prev->prev; - - prev->prev->next = self; prev->next->prev = self->prev; + self->prev = prev->prev; + prev->prev->next = self; prev->prev = prev->next = prev; } @@ -494,20 +525,45 @@ LLIST_FUNC (LList llist_get_nth_stop (LList self, int n, const_LList stop), return self; ); -/* - some macros for convenience -*/ -#define llist_insert_head(list, element) llist_insert_next (list, element) -#define llist_insert_tail(list, element) llist_insert_prev (list, element) -#define llist_head llist_next -#define llist_tail llist_prev + +/** + * Sort a list. + * @param self list to be sorted + * @param cmp function takeing 2 LLists + * simple recursive mergesort + */ +typedef int (*llist_cmpfn)(LList a, LList b); + +LLIST_FUNC (LList llist_sort (LList self, llist_cmpfn cmp), + llist left; + llist right; + llist_init (&left); + llist_init (&right); + unsigned n = 0; + if (!llist_is_single (self)) + { + LLIST_WHILE_HEAD (self, head) + llist_insert_prev (++n & 1 ? &left : &right, head); + + llist_sort (&left, cmp); + llist_sort (&right, cmp); + + while (!llist_is_empty (&left) && !llist_is_empty (&right)) + llist_insert_prev (self, cmp (left.next, right.next) < 0 ? left.next : right.next); + + if (!llist_is_empty (&left)) + llist_insertlist_prev (self, &left); + if (!llist_is_empty (&right)) + llist_insertlist_prev (self, &right); + } + return self; +) #endif /* LLIST_H */ /* -// Local Variables: -// mode: C -// c-file-style: "gnu" -// End: -// arch-tag: e8fe4a59-fd55-4c45-b860-5cd1e0771213 -// end_of_file +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: */ diff --git a/src/lib/locking.h b/src/lib/locking.h index 01ad8bb29..f62dd6c29 100644 --- a/src/lib/locking.h +++ b/src/lib/locking.h @@ -28,6 +28,11 @@ #include "lib/error.h" + +LUMIERA_ERROR_DECLARE (MUTEX_LOCK); +LUMIERA_ERROR_DECLARE (MUTEX_UNLOCK); +LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); + /** * @file Shared declarations for all locking primitives. */ diff --git a/src/lib/mrucache.c b/src/lib/mrucache.c new file mode 100644 index 000000000..d98976184 --- /dev/null +++ b/src/lib/mrucache.c @@ -0,0 +1,67 @@ +/* + mrucache.h - most recent used cache + + Copyright (C) Lumiera.org + 2008, 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 "lib/mrucache.h" + +/** + * @file Most recent used cache + * Elements (addressed by a LList node) are either in the cache and thereby subject of aging + * or checked out under control of the user. + */ + + +LumieraMruCache +lumiera_mrucache_init (LumieraMruCache self, lumiera_cache_destructor_fn destructor_cb) +{ + REQUIRE (self); + llist_init (&self->cache_list); + self->cached = 0; + self->destructor_cb = destructor_cb; + return self; +} + +LumieraMruCache +lumiera_mrucache_destroy (LumieraMruCache self) +{ + LLIST_WHILE_TAIL (&self->cache_list, node) + { + if (self->destructor_cb) + free (self->destructor_cb (node)); + else + free (node); + } + self->cached = 0; + return self; +} + + +/** + * destroy and free the nelem oldest elements + */ +int +lumiera_mrucache_age (LumieraMruCache self, int nelem) +{ + REQUIRE (self); + while (self->cached && nelem--) + free (lumiera_mrucache_pop (self)); + + return nelem; +} diff --git a/src/lib/mrucache.h b/src/lib/mrucache.h new file mode 100644 index 000000000..8af7434d9 --- /dev/null +++ b/src/lib/mrucache.h @@ -0,0 +1,119 @@ +/* + mrucache.h - most recent used cache + + Copyright (C) Lumiera.org + 2008, 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 LUMIERA_CACHE_H +#define LUMIERA_CACHE_H + +#include "lib/llist.h" + +#include + +/** + * @file Most recent used cache + * Elements (addressed by a LList node) are either in the cache and thereby subject of aging + * or checked out under control of the user. + */ + +/** + * Callback function used to destruct/cleanup aged elements. + * shall clean the element sufficiently up to be ready for being freed or reused + * @param node the llist node used to link cache elements (will be empty at call) + * @return pointer to the begin of the element. + */ +typedef void* (*lumiera_cache_destructor_fn)(LList node); + +struct lumiera_mrucache_struct +{ + llist cache_list; + size_t cached; + lumiera_cache_destructor_fn destructor_cb; +}; +typedef struct lumiera_mrucache_struct lumiera_mrucache; +typedef lumiera_mrucache* LumieraMruCache; + +/** + * Initialize a mrucache + */ +LumieraMruCache +lumiera_mrucache_init (LumieraMruCache self, lumiera_cache_destructor_fn destructor_cb); + + +/** + * Destroy a mrucache + */ +LumieraMruCache +lumiera_mrucache_destroy (LumieraMruCache self); + + +/** + * add an element to a mrucache + */ +static inline void +lumiera_mrucache_add (LumieraMruCache self, LList node) +{ + REQUIRE (self); + REQUIRE (node && llist_is_empty (node)); + llist_insert_head (&self->cache_list, node); + ++self->cached; +} + + +/** + * Remove an element from a mrucache. + */ +static inline void +lumiera_mrucache_remove (LumieraMruCache self, LList node) +{ + REQUIRE (self); + REQUIRE (node && llist_is_member (&self->cache_list, node)); /* speedup loop warning :P, this check is costly */ + llist_unlink (node); + --self->cached; +} + +/** + * destroy the oldest element and return a pointer to it for reuse. + */ +static inline void* +lumiera_mrucache_pop (LumieraMruCache self) +{ + REQUIRE (self); + if (llist_is_empty (&self->cache_list)) + return NULL; + + LList node = llist_tail (&self->cache_list); + llist_unlink (node); + --self->cached; + + if (self->destructor_cb) + return self->destructor_cb (node); + else + return node; +} + + +/** + * destroy and free the nelem oldest elements + */ +int +lumiera_mrucache_age (LumieraMruCache self, int nelem); + + +#endif diff --git a/src/lib/mutex.c b/src/lib/mutex.c index a08d89e51..009502e5a 100644 --- a/src/lib/mutex.c +++ b/src/lib/mutex.c @@ -25,6 +25,10 @@ * @file Mutual exclusion locking. */ +LUMIERA_ERROR_DEFINE (MUTEX_LOCK, "Mutex locking failed"); +LUMIERA_ERROR_DEFINE (MUTEX_UNLOCK, "Mutex unlocking failed"); +LUMIERA_ERROR_DEFINE (MUTEX_DESTROY, "Mutex destroy failed"); + /** * Initialize a mutex variable @@ -52,7 +56,7 @@ lumiera_mutex_destroy (LumieraMutex self) if (self) { if (pthread_mutex_destroy (&self->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_DESTROY); } return self; } diff --git a/src/lib/mutex.h b/src/lib/mutex.h index ced7118e2..9381c3ca1 100644 --- a/src/lib/mutex.h +++ b/src/lib/mutex.h @@ -28,6 +28,12 @@ * @file Mutual exclusion locking, header. */ +#define LUMIERA_MUTEX_SECTION(mutex) \ +lumiera_mutexacquirer lock_##__LINE__##_; \ +for (lumiera_mutexacquirer_init_mutex (&lock_##__LINE__##_, mutex, LUMIERA_LOCKED); \ + lock_##__LINE__##_.state == LUMIERA_LOCKED; \ + lumiera_mutexacquirer_unlock (&lock_##__LINE__##_)) + /** * Mutex. @@ -106,7 +112,7 @@ lumiera_mutexacquirer_init_mutex (LumieraMutexacquirer self, LumieraMutex mutex, self->state = state; if (state == LUMIERA_LOCKED) if (pthread_mutex_lock (&mutex->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_LOCK); return self; } @@ -124,7 +130,7 @@ lumiera_mutexacquirer_lock (LumieraMutexacquirer self) REQUIRE (self->state == LUMIERA_UNLOCKED, "mutex already locked"); if (pthread_mutex_lock (&self->mutex->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_LOCK); self->state = LUMIERA_LOCKED; } @@ -164,7 +170,7 @@ lumiera_mutexacquirer_try_mutex (LumieraMutexacquirer self, LumieraMutex mutex) case EBUSY: return LUMIERA_UNLOCKED; default: - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_LOCK); } } @@ -180,7 +186,7 @@ lumiera_mutexacquirer_unlock (LumieraMutexacquirer self) REQUIRE (self); REQUIRE (self->state == LUMIERA_LOCKED, "mutex was not locked"); if (pthread_mutex_unlock (&self->mutex->mutex)) - LUMIERA_DIE; + LUMIERA_DIE (MUTEX_UNLOCK); self->state = LUMIERA_UNLOCKED; } diff --git a/src/lib/plugin.c b/src/lib/plugin.c index 87939533d..8345d9887 100644 --- a/src/lib/plugin.c +++ b/src/lib/plugin.c @@ -25,8 +25,10 @@ #include #include #include +#include #include "plugin.h" +#include "safeclib.h" /** * @file Plugin loader. @@ -159,8 +161,8 @@ lumiera_plugin_lookup (struct lumiera_plugin* self, const char* path) { /* got it */ TRACE (lumiera_plugin, "found %s", pathname); - self->pathname = strdup (pathname); - if (!self->pathname) LUMIERA_DIE; + self->pathname = lumiera_strndup (pathname, PATH_MAX); + self->type = lumiera_plugin_extensions[i].type; return 0; } @@ -195,21 +197,20 @@ lumiera_interface_open (const char* name, const char* interface, size_t min_revi plugin.name = name; /* for searching */ found = tsearch (&plugin, &lumiera_plugin_registry, lumiera_plugin_name_cmp); - if (!found) LUMIERA_DIE; + if (!found) + LUMIERA_DIE (NO_MEMORY); if (*found == &plugin) { NOTICE (lumiera_plugin, "new plugin"); /* now really create new item */ - *found = malloc (sizeof (struct lumiera_plugin)); - if (!*found) LUMIERA_DIE; + *found = lumiera_malloc (sizeof (struct lumiera_plugin)); if (name) /* NULL is main app, no lookup needed */ { /*lookup for $LUMIERA_PLUGIN_PATH*/ - (*found)->name = strdup (name); - if (!(*found)->name) LUMIERA_DIE; + (*found)->name = lumiera_strndup (name, PATH_MAX); if (!!lumiera_plugin_lookup (*found, getenv("LUMIERA_PLUGIN_PATH")) #ifdef LUMIERA_PLUGIN_PATH diff --git a/src/lib/references.c b/src/lib/references.c index ad4f9562f..3e9d5f260 100644 --- a/src/lib/references.c +++ b/src/lib/references.c @@ -19,6 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "lib/references.h" +#include "lib/safeclib.h" /** * @file Strong and Weak references @@ -43,8 +44,7 @@ LumieraReference lumiera_reference_strong_init_once (LumieraReference self, void* obj, void (*dtor)(void*)) { - LumieraReftarget target = malloc (sizeof(lumiera_reftarget)); - if (!target) LUMIERA_DIE; + LumieraReftarget target = lumiera_malloc (sizeof(lumiera_reftarget)); target->object = obj; target->dtor = dtor; diff --git a/src/lib/rwlock.c b/src/lib/rwlock.c index c7ee3a37d..0862aa808 100644 --- a/src/lib/rwlock.c +++ b/src/lib/rwlock.c @@ -25,6 +25,10 @@ LUMIERA_ERROR_DEFINE(RWLOCK_AGAIN, "maximum number of readlocks exceed"); LUMIERA_ERROR_DEFINE(RWLOCK_DEADLOCK, "deadlock detected"); +LUMIERA_ERROR_DEFINE(RWLOCK_DESTROY, "destroy rwlock"); +LUMIERA_ERROR_DEFINE(RWLOCK_UNLOCK, "unlock"); +LUMIERA_ERROR_DEFINE(RWLOCK_RLOCK, "rlock"); +LUMIERA_ERROR_DEFINE(RWLOCK_WLOCK, "wlock"); /** * @file Read/write locks. @@ -57,7 +61,7 @@ lumiera_rwlock_destroy (LumieraRWLock self) if (self) { if (pthread_rwlock_destroy (&self->rwlock)) - LUMIERA_DIE; + LUMIERA_DIE (RWLOCK_DESTROY); } return self; } @@ -95,7 +99,7 @@ lumiera_rwlockacquirer_init (LumieraRWLockacquirer self, LumieraRWLock rwlock, e lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK); return NULL; default: - LUMIERA_DIE; + LUMIERA_DIE (RWLOCK_RLOCK); } case LUMIERA_WRLOCKED: switch (pthread_rwlock_wrlock (&rwlock->rwlock)) @@ -106,7 +110,7 @@ lumiera_rwlockacquirer_init (LumieraRWLockacquirer self, LumieraRWLock rwlock, e lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK); return NULL; default: - LUMIERA_DIE; + LUMIERA_DIE (RWLOCK_WLOCK); } default: break; @@ -138,7 +142,7 @@ lumiera_rwlockacquirer_rdlock (LumieraRWLockacquirer self) lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK); return NULL; default: - LUMIERA_DIE; + LUMIERA_DIE (RWLOCK_RLOCK); } self->state = LUMIERA_RDLOCKED; @@ -166,7 +170,7 @@ lumiera_rwlockacquirer_wrlock (LumieraRWLockacquirer self) lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK); return NULL; default: - LUMIERA_DIE; + LUMIERA_DIE (RWLOCK_WLOCK); } self->state = LUMIERA_WRLOCKED; diff --git a/src/lib/rwlock.h b/src/lib/rwlock.h index 922a05e7b..5e121fbcc 100644 --- a/src/lib/rwlock.h +++ b/src/lib/rwlock.h @@ -33,6 +33,10 @@ LUMIERA_ERROR_DECLARE(RWLOCK_AGAIN); LUMIERA_ERROR_DECLARE(RWLOCK_DEADLOCK); +LUMIERA_ERROR_DECLARE(RWLOCK_DESTROY); +LUMIERA_ERROR_DECLARE(RWLOCK_UNLOCK); +LUMIERA_ERROR_DECLARE(RWLOCK_RLOCK); +LUMIERA_ERROR_DECLARE(RWLOCK_WLOCK); /** * @file Read/write locks, header. @@ -106,7 +110,7 @@ lumiera_rwlockacquirer_unlock (LumieraRWLockacquirer self) REQUIRE (self); REQUIRE (self->state != LUMIERA_UNLOCKED, "rwlock was not locked"); if (pthread_rwlock_unlock (&self->rwlock->rwlock)) - LUMIERA_DIE; + LUMIERA_DIE (RWLOCK_UNLOCK); self->state = LUMIERA_UNLOCKED; } diff --git a/src/lib/safeclib.c b/src/lib/safeclib.c new file mode 100644 index 000000000..71e347ab7 --- /dev/null +++ b/src/lib/safeclib.c @@ -0,0 +1,214 @@ +/* + safe_clib.c - Portable and safe wrapers around some clib functions and some tools + + Copyright (C) CinelerraCV + 2008, 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. +*/ +#define _GNU_SOURCE + +#include "error.h" + +#include +#include +#include +#include +#include + +/** + * @file Portable and safe wrapers around some clib functions and some tools + */ + +LUMIERA_ERROR_DEFINE (NO_MEMORY, "Out of Memory!"); + +/** + * Allocate memory. + * always succeeds or dies + * @param size memory to be allocated + * @return pointer to the allocated memory + */ +void* +lumiera_malloc (size_t sz) +{ + void* o = sz? malloc (sz) : NULL; + if (!o) + LUMIERA_DIE (NO_MEMORY); + return o; +} + + +/** + * Duplicate a C string. + * always succeeds or dies + * @param str string to be copied + * @param len maximal length to be copied + * @return pointer to the new string, "" if NULL was passed as str + */ +char* +lumiera_strndup (const char* str, size_t len) +{ + char* o; + if (str) + o = strndup (str, len); + else + o = strdup (""); + + if (!o) + LUMIERA_DIE (NO_MEMORY); + return o; +} + + +/** + * Compare two C strings. + * Handles NULL pointers as "", shortcut for same addresses + * @param a first string for comparsion + * @param b second string for comparsion + * @param len maximal length for the comparsion + * @return 0 if the strings are identical, -1 if smaller 1 if bigger. + */ +int +lumiera_strncmp (const char* a, const char* b, size_t len) +{ + return a == b ? 0 : strncmp (a?a:"", b?b:"", len); +} + + +/** + * check 2 strings for identity. + * @param a first string for comparsion + * @param b second string for comparsion + * @return 1 when the strings are the the same, 0 if not. + */ +int +lumiera_streq (const char* a, const char* b) +{ + return !lumiera_strncmp (a, b, SIZE_MAX); +} + + +/** + * Round robin temporary buffers. + * This provides 64 buffers per thread which are recycled with each use. + * The idea here is to have fast buffers for temporal data without need for explicit heap management. + */ +struct lumiera_tmpbuf_struct +{ + void* buffers[64]; + size_t sizes[64]; + unsigned idx; +}; + +static pthread_once_t lumiera_tmpbuf_tls_once = PTHREAD_ONCE_INIT; +static pthread_key_t lumiera_tmpbuf_tls_key; + +void +lumiera_tmpbuf_freeall (void); + +static void +lumiera_tmpbuf_destroy (void* buf) +{ + lumiera_tmpbuf_freeall (); + free (buf); +} + +static void +lumiera_tmpbuf_init (void) +{ + pthread_key_create (&lumiera_tmpbuf_tls_key, lumiera_tmpbuf_destroy); +} + + +/** + * free all buffers associated with this thread. + * This function is called automatically, usually one doesnt need to call it. + */ +void +lumiera_tmpbuf_freeall (void) +{ + pthread_once (&lumiera_tmpbuf_tls_once, lumiera_tmpbuf_init); + struct lumiera_tmpbuf_struct* buf = pthread_getspecific (lumiera_tmpbuf_tls_key); + if (!buf) + pthread_setspecific (lumiera_tmpbuf_tls_key, + buf = lumiera_malloc (sizeof (struct lumiera_tmpbuf_struct))); + + for (buf->idx = 0; buf->idx < 64; ++buf->idx) + { + free (buf->buffers[buf->idx]); + buf->buffers[buf->idx] = NULL; + buf->sizes[buf->idx] = 0; + } + buf->idx = 0; +} + + +/** + * Query a thread local buffer. + * @param size minimal needed size for the buffer + * @return the buffer + */ +void* +lumiera_tmpbuf_provide (size_t size) +{ + pthread_once (&lumiera_tmpbuf_tls_once, lumiera_tmpbuf_init); + struct lumiera_tmpbuf_struct* buf = pthread_getspecific (lumiera_tmpbuf_tls_key); + if (!buf) + pthread_setspecific (lumiera_tmpbuf_tls_key, + buf = lumiera_malloc (sizeof (struct lumiera_tmpbuf_struct))); + + buf->idx = (buf->idx + 1) & 0x3f; + + if (buf->sizes[buf->idx] < size || buf->sizes[buf->idx] > 8*size) + { + free (buf->buffers[buf->idx]); + buf->sizes[buf->idx] = (size+4*sizeof(long)) & ~(4*sizeof(long)-1); + buf->buffers[buf->idx] = lumiera_malloc (buf->sizes[buf->idx]); + } + return buf->buffers[buf->idx]; +} + + +char* +lumiera_tmpbuf_strndup (const char* src, size_t size) +{ + size_t len = strlen (src); + len = len > size ? size : len; + + char* buf = lumiera_tmpbuf_provide (len + 1); + strncpy (buf, src, len); + buf[len] = '\0'; + + return buf; +} + + +char* +lumiera_tmpbuf_sprintf (size_t size, const char* fmt, ...) +{ + va_list args; + + va_start (args, fmt); + size_t len = vsnprintf (NULL, 0, fmt, args); + va_end (args); + + len = len > size ? size : len; + char* buf = lumiera_tmpbuf_provide (len); + va_start (args, fmt); + vsnprintf (buf, len, fmt, args); + va_end (args); + + return buf; +} diff --git a/src/lib/safeclib.h b/src/lib/safeclib.h new file mode 100644 index 000000000..02d931cb7 --- /dev/null +++ b/src/lib/safeclib.h @@ -0,0 +1,114 @@ +/* + safe_clib.h - Portable and safe wrapers around some clib functions and some tools + + Copyright (C) CinelerraCV + 2008, 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 "error.h" + +#include + +/** + * @file Portable and safe wrapers around some clib functions and some tools + */ +LUMIERA_ERROR_DECLARE(NO_MEMORY); + +/** + * Allocate memory. + * always succeeds or dies + * @param size memory to be allocated + * @return pointer to the allocated memory + */ +void* +lumiera_malloc (size_t sz); + + +/** + * Duplicate a C string. + * always succeeds or dies + * @param str string to be copied + * @param len maximal length to be copied + * @return pointer to the new string, "" if NULL was passed as str + */ +char* +lumiera_strndup (const char* str, size_t len); + + +/** + * Compare two C strings. + * Handles NULL pointers as "", shortcut for same addresses + * @param a first string for comparsion + * @param b second string for comparsion + * @param len maximal length for the comparsion + * @return 0 if the strings are identical, -1 if smaller 1 if bigger. + */ +int +lumiera_strncmp (const char* a, const char* b, size_t len); + + +/** + * check 2 strings for identity. + * @param a first string for comparsion + * @param b second string for comparsion + * @return 1 when the strings are the the same, 0 if not. + */ +int +lumiera_streq (const char* a, const char* b); + + +/** + * Round robin temporary buffers. + * This provides 64 buffers per thread which are recycled with each use. + * The idea here is to have fast buffers for temporal data without need for explicit heap management. + */ + +/** + * free all buffers associated with this thread. + * This function is called automatically, usually one doesnt need to call it. + */ +void +lumiera_tmpbuf_freeall (void); + + +/** + * Query a thread local tmpbuf. + * @param size minimal needed size for the tmpbuf + * @return the tmpbuf + */ +void* +lumiera_tmpbuf_provide (size_t size); + +/** + * Duplicate string to a tmpbuf. + * @param src string to be duplicated + * @param size maximal length to be copied + * @return temporary buffer containing a copy of the string + */ +char* +lumiera_tmpbuf_strndup (const char* src, size_t size); + +/** + * Construct a string in a tmpbuf. + * @param size maximal length for the string + * @param fmt printf like formatstring + * @param ... parameters + * @return temporary buffer containing the constructed of the string + */ +char* +lumiera_tmpbuf_sprintf (size_t size, const char* fmt, ...); + diff --git a/src/lib/uuid.c b/src/lib/uuid.c new file mode 100644 index 000000000..d4eee69be --- /dev/null +++ b/src/lib/uuid.c @@ -0,0 +1,95 @@ +/* + uuid - Universal unique identifiers + + Copyright (C) CinelerraCV + 2008, 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 "lib/uuid.h" + +#include +#include +#include +#include +#include +#include + +void +lumiera_uuid_set_ptr (lumiera_uuid* uuid, void* ptr) +{ + memset (uuid, 0, 16); + *(void**)uuid = ptr; +} + + +void* +lumiera_uuid_ptr_get (lumiera_uuid* uuid) +{ + return *(void**)uuid; +} + + +void +lumiera_uuid_gen (lumiera_uuid* uuid) +{ + static int fd = -2; + if (!uuid) + return; + + if (fd == -2) + { + fd = open ("/dev/urandom", O_RDONLY); + if (fd == -1) + fd = open ("/dev/random", O_RDONLY); + if (fd >= 0) + fcntl (fd, F_SETFD, FD_CLOEXEC); + else + srand (getpid () + time (NULL)); + } + if (fd < 0) + { + for (int i = 0; i < 16; ++i) + ((unsigned char*)uuid)[i] = (unsigned char)(rand()>>7); + } + else + { + if (read (fd, uuid, 16) < 0) + abort (); + } +} + + +void +lumiera_uuid_copy (lumiera_uuid* dest, lumiera_uuid* src) +{ + memcpy (dest, src, 16); +} + + +int +lumiera_uuid_eq (lumiera_uuid* uuida, lumiera_uuid* uuidb) +{ + return !memcmp (uuida, uuidb, 16); +} + +size_t +lumiera_uuid_hash (lumiera_uuid* uuid) +{ + return *(size_t*)uuid; +} + + diff --git a/src/lib/uuid.h b/src/lib/uuid.h new file mode 100644 index 000000000..d4cf8ce9d --- /dev/null +++ b/src/lib/uuid.h @@ -0,0 +1,67 @@ +/* + uuid - Universal unique identifiers + + Copyright (C) CinelerraCV + 2008, 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 LUMIERA_UUID_H +#define LUMIERA_UUID_H + +#include + +typedef unsigned char lumiera_uuid[16]; + +/** + * Retrieve a generic pointer stored in a uuid + */ +void* +lumiera_uuid_ptr_get (lumiera_uuid* uuid); + +/** + * Generate a new uuid + */ +void +lumiera_uuid_gen (lumiera_uuid* uuid); + +/** + * Store a generic pointer in a uuid + */ +void +lumiera_uuid_set_ptr (lumiera_uuid* uuid, void* ptr); + + +/** + * Copy an uuid + */ +void +lumiera_uuid_copy (lumiera_uuid* dest, lumiera_uuid* src); + + +/** + * Test 2 uuid's for equality + */ +int +lumiera_uuid_eq (lumiera_uuid* uuida, lumiera_uuid* uuidb); + + +/** + * Generate a hashsum over an uuid + */ +size_t +lumiera_uuid_hash (lumiera_uuid* uuid); + +#endif diff --git a/tests/15safeclib.tests b/tests/15safeclib.tests new file mode 100644 index 000000000..7e4039ead --- /dev/null +++ b/tests/15safeclib.tests @@ -0,0 +1,28 @@ + +TESTING "Safe clib and tools" ./test-safeclib + +TEST "Allocating 0 bytes" allocation0 < #include "lib/llist.h" -#include "lib/error.h" +#include "tests/test.h" +TESTS_BEGIN -LUMIERA_ERROR_DEFINE(TEST, "test error"); - -int -main (int argc, char** argv) +TEST ("basic") { - NOBUG_INIT; + LLIST_AUTO (node1); - if (argc == 1) - return 0; + llist node2; + llist_init (&node2); - if (!strcmp(argv[1], "basic")) - { - LLIST_AUTO (node1); - - llist node2; - llist_init (&node2); - - printf ("%d\n", llist_is_empty (&node1)); - printf ("%d\n", llist_is_empty (&node2)); - } - else if (!strcmp(argv[1], "nodeinsert")) - { - LLIST_AUTO (list); - LLIST_AUTO (node1); - LLIST_AUTO (node2); - LLIST_AUTO (node3); - - llist_insert_next (&list, &node1); - printf ("%d\n", llist_is_empty (&list)); - printf ("%d\n", llist_is_empty (&node1)); - printf ("%d\n", llist_is_single (&node1)); - llist_insert_next (&node1, &node2); - printf ("%d\n", llist_is_single (&node1)); - llist_insert_prev (&node1, &node3); - printf ("%d\n", llist_next (&list) == &node3); - printf ("%d\n", llist_next (&node3) == &node1); - printf ("%d\n", llist_next (&node1) == &node2); - printf ("%d\n", llist_prev (&list) == &node2); - printf ("%d\n", llist_count (&list)); - } - else if (!strcmp(argv[1], "predicates")) - { - LLIST_AUTO (list); - LLIST_AUTO (node1); - LLIST_AUTO (node2); - LLIST_AUTO (node3); - LLIST_AUTO (node4); - LLIST_AUTO (nil); - - llist_insert_tail (&list, &node2); - llist_insert_tail (&list, &node3); - llist_insert_tail (&list, &node4); - llist_insert_head (&list, &node1); - - printf ("%d\n", llist_is_head (&list, &node1)); - printf ("%d\n", llist_is_tail (&list, &node4)); - printf ("%d\n", llist_is_head (&list, &node4)); - printf ("%d\n", llist_is_tail (&list, &node1)); - printf ("%d\n", llist_is_end (&list, &list)); - printf ("%d\n", llist_is_member (&list, &node3)); - printf ("%d\n", llist_is_member (&list, &nil)); - - printf ("%d\n", llist_is_before_after (&list, &node1, &node3)); - printf ("%d\n", llist_is_before_after (&list, &node3, &node1)); - printf ("%d\n", llist_is_before_after (&list, &node1, &nil)); - } - else if (!strcmp(argv[1], "unlink")) - { - LLIST_AUTO (list); - LLIST_AUTO (node1); - LLIST_AUTO (node2); - LLIST_AUTO (node3); - LLIST_AUTO (node4); - LLIST_AUTO (nil); - - llist_insert_tail (&list, &node2); - llist_insert_tail (&list, &node3); - llist_insert_tail (&list, &node4); - llist_insert_head (&list, &node1); - - LLIST_FOREACH_REV (&list, itr) - { - if(itr == &node1) printf ("node1 "); - else if(itr == &node2) printf ("node2 "); - else if(itr == &node3) printf ("node3 "); - else if(itr == &node4) printf ("node4 "); - else printf ("unknown "); - } - printf (".\n"); - - llist_unlink (&nil); - llist_unlink (&node2); - llist_unlink (&node3); - - LLIST_FOREACH (&list, itr) - { - if(itr == &node1) printf ("node1 "); - else if(itr == &node2) printf ("node2 "); - else if(itr == &node3) printf ("node3 "); - else if(itr == &node4) printf ("node4 "); - else printf ("unknown "); - } - printf (".\n"); - printf ("%d\n", llist_is_empty (&node2)); - printf ("%d\n", llist_is_empty (&node3)); - printf ("%d\n", llist_is_empty (&nil)); - } - else if (!strcmp(argv[1], "whiles")) - { - LLIST_AUTO (list); - LLIST_AUTO (node1); - LLIST_AUTO (node2); - LLIST_AUTO (node3); - LLIST_AUTO (node4); - LLIST_AUTO (nil); - - llist_insert_tail (&list, &node2); - llist_insert_tail (&list, &node3); - llist_insert_tail (&list, &node4); - llist_insert_head (&list, &node1); - - LLIST_FOREACH_REV (&list, itr) - { - if(itr == &node1) printf ("node1 "); - else if(itr == &node2) printf ("node2 "); - else if(itr == &node3) printf ("node3 "); - else if(itr == &node4) printf ("node4 "); - else printf ("unknown "); - } - printf (".\n"); - - LLIST_WHILE_HEAD (&list, head) - llist_unlink (head); - - LLIST_FOREACH (&list, itr) - { - if(itr == &node1) printf ("node1 "); - else if(itr == &node2) printf ("node2 "); - else if(itr == &node3) printf ("node3 "); - else if(itr == &node4) printf ("node4 "); - else printf ("unknown "); - } - printf (".\n"); - - llist_insert_tail (&list, &node2); - llist_insert_tail (&list, &node3); - llist_insert_tail (&list, &node4); - llist_insert_head (&list, &node1); - - LLIST_WHILE_TAIL (&list, tail) - llist_unlink (tail); - - LLIST_FOREACH (&list, itr) - { - if(itr == &node1) printf ("node1 "); - else if(itr == &node2) printf ("node2 "); - else if(itr == &node3) printf ("node3 "); - else if(itr == &node4) printf ("node4 "); - else printf ("unknown "); - } - printf (".\n"); - } - else - return 1; - - return 0; + printf ("%d\n", llist_is_empty (&node1)); + printf ("%d\n", llist_is_empty (&node2)); } + +TEST ("nodeinsert") +{ + LLIST_AUTO (list); + LLIST_AUTO (node1); + LLIST_AUTO (node2); + LLIST_AUTO (node3); + + llist_insert_next (&list, &node1); + printf ("%d\n", llist_is_empty (&list)); + printf ("%d\n", llist_is_empty (&node1)); + printf ("%d\n", llist_is_single (&node1)); + llist_insert_next (&node1, &node2); + printf ("%d\n", llist_is_single (&node1)); + llist_insert_prev (&node1, &node3); + printf ("%d\n", llist_next (&list) == &node3); + printf ("%d\n", llist_next (&node3) == &node1); + printf ("%d\n", llist_next (&node1) == &node2); + printf ("%d\n", llist_prev (&list) == &node2); + printf ("%d\n", llist_count (&list)); +} + +TEST ("predicates") +{ + LLIST_AUTO (list); + LLIST_AUTO (node1); + LLIST_AUTO (node2); + LLIST_AUTO (node3); + LLIST_AUTO (node4); + LLIST_AUTO (nil); + + llist_insert_tail (&list, &node2); + llist_insert_tail (&list, &node3); + llist_insert_tail (&list, &node4); + llist_insert_head (&list, &node1); + + printf ("%d\n", llist_is_head (&list, &node1)); + printf ("%d\n", llist_is_tail (&list, &node4)); + printf ("%d\n", llist_is_head (&list, &node4)); + printf ("%d\n", llist_is_tail (&list, &node1)); + printf ("%d\n", llist_is_end (&list, &list)); + printf ("%d\n", llist_is_member (&list, &node3)); + printf ("%d\n", llist_is_member (&list, &nil)); + + printf ("%d\n", llist_is_before_after (&list, &node1, &node3)); + printf ("%d\n", llist_is_before_after (&list, &node3, &node1)); + printf ("%d\n", llist_is_before_after (&list, &node1, &nil)); +} + +TEST ("unlink") +{ + LLIST_AUTO (list); + LLIST_AUTO (node1); + LLIST_AUTO (node2); + LLIST_AUTO (node3); + LLIST_AUTO (node4); + LLIST_AUTO (nil); + + llist_insert_tail (&list, &node2); + llist_insert_tail (&list, &node3); + llist_insert_tail (&list, &node4); + llist_insert_head (&list, &node1); + + LLIST_FOREACH_REV (&list, itr) + { + if(itr == &node1) printf ("node1 "); + else if(itr == &node2) printf ("node2 "); + else if(itr == &node3) printf ("node3 "); + else if(itr == &node4) printf ("node4 "); + else printf ("unknown "); + } + printf (".\n"); + + llist_unlink (&nil); + llist_unlink (&node2); + llist_unlink (&node3); + + LLIST_FOREACH (&list, itr) + { + if(itr == &node1) printf ("node1 "); + else if(itr == &node2) printf ("node2 "); + else if(itr == &node3) printf ("node3 "); + else if(itr == &node4) printf ("node4 "); + else printf ("unknown "); + } + printf (".\n"); + printf ("%d\n", llist_is_empty (&node2)); + printf ("%d\n", llist_is_empty (&node3)); + printf ("%d\n", llist_is_empty (&nil)); +} + +TEST ("whiles") +{ + LLIST_AUTO (list); + LLIST_AUTO (node1); + LLIST_AUTO (node2); + LLIST_AUTO (node3); + LLIST_AUTO (node4); + LLIST_AUTO (nil); + + llist_insert_tail (&list, &node2); + llist_insert_tail (&list, &node3); + llist_insert_tail (&list, &node4); + llist_insert_head (&list, &node1); + + LLIST_FOREACH_REV (&list, itr) + { + if(itr == &node1) printf ("node1 "); + else if(itr == &node2) printf ("node2 "); + else if(itr == &node3) printf ("node3 "); + else if(itr == &node4) printf ("node4 "); + else printf ("unknown "); + } + printf (".\n"); + + LLIST_WHILE_HEAD (&list, head) + llist_unlink (head); + + LLIST_FOREACH (&list, itr) + { + if(itr == &node1) printf ("node1 "); + else if(itr == &node2) printf ("node2 "); + else if(itr == &node3) printf ("node3 "); + else if(itr == &node4) printf ("node4 "); + else printf ("unknown "); + } + printf (".\n"); + + llist_insert_tail (&list, &node2); + llist_insert_tail (&list, &node3); + llist_insert_tail (&list, &node4); + llist_insert_head (&list, &node1); + + LLIST_WHILE_TAIL (&list, tail) + llist_unlink (tail); + + LLIST_FOREACH (&list, itr) + { + if(itr == &node1) printf ("node1 "); + else if(itr == &node2) printf ("node2 "); + else if(itr == &node3) printf ("node3 "); + else if(itr == &node4) printf ("node4 "); + else printf ("unknown "); + } + printf (".\n"); +} + + +TESTS_END diff --git a/tests/library/test-safeclib.c b/tests/library/test-safeclib.c new file mode 100644 index 000000000..837e3d5fd --- /dev/null +++ b/tests/library/test-safeclib.c @@ -0,0 +1,85 @@ +/* + Copyright (C) Lumiera.org + 2008, 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 "lib/safeclib.h" +#include "tests/test.h" + +#include +#include + +TESTS_BEGIN + +TEST ("allocation0") +{ + lumiera_malloc (0); + NOTREACHED; +} + +TEST ("allocation1024") +{ + void* data[1024]; + for (int i = 0; i < 1024; ++i) + { + data[i] = lumiera_malloc (1024); + ENSURE (data[i]); + } + for (int i = 0; i < 1024; ++i) + { + free (data[i]); + } +} + +TEST ("allocationtoobig") +{ + struct rlimit rl; + rl.rlim_cur = 100*1024*1024; + rl.rlim_max = 100*1024*1024; + setrlimit (RLIMIT_AS, &rl); + lumiera_malloc (200*1024*1024); + NOTREACHED; +} + +TEST ("streq") +{ + if (!lumiera_streq ("foo", "foo")) + LUMIERA_DIE (TEST); + if (!lumiera_streq (NULL, NULL)) + LUMIERA_DIE (TEST); + if (!!lumiera_streq (NULL, "foo")) + LUMIERA_DIE (TEST); + if (!!lumiera_streq ("foo", NULL)) + LUMIERA_DIE (TEST); + if (!!lumiera_streq ("foo", "bar")) + LUMIERA_DIE (TEST); +} + +TEST ("tmpbuf") +{ + for (int i = 0; i < 256; ++i) + { + char* buf = lumiera_tmpbuf_provide (1024); + + for (int j = 0; j < 1024; ++j) + { + buf[j] = i; + } + } +} + + +TESTS_END diff --git a/tests/library/test-uuid.c b/tests/library/test-uuid.c new file mode 100644 index 000000000..4a6050163 --- /dev/null +++ b/tests/library/test-uuid.c @@ -0,0 +1,73 @@ +/* + test-uuid.c - test the uuid lib + + Copyright (C) CinelerraCV + 2007, 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 "cinelerra-config.h" + +//#include + + +#include "lib/uuid.h" + +#include + +//CINELERRA_ERROR_DEFINE(TEST, "test error"); + +int +main (int argc, char** argv) +{ + NOBUG_INIT; + + if (argc == 1) + return 0; + + if (!strcmp(argv[1], "uuidgen_2")) + { + lumiera_uuid uuid1; + lumiera_uuid uuid2; + + lumiera_uuid_gen (&uuid1); + lumiera_uuid_gen (&uuid2); + + printf ("%d\n", lumiera_uuid_eq (&uuid2, &uuid1)); + } + else if (!strcmp(argv[1], "uuidgen_copy")) + { + lumiera_uuid uuid1; + lumiera_uuid uuid2; + + lumiera_uuid_gen (&uuid1); + + lumiera_uuid_copy (&uuid2, &uuid1); + + printf ("%d\n", lumiera_uuid_eq (&uuid2, &uuid1)); + } + else if (!strcmp(argv[1], "ptrs")) + { + lumiera_uuid uuid; + + lumiera_uuid_set_ptr (&uuid, &uuid); + + printf ("%d\n", lumiera_uuid_ptr_get (&uuid) == &uuid); + } + else + return 1; + + return 0; +} diff --git a/tests/locking/test-locking.c b/tests/locking/test-locking.c index 1b54d152e..b37c2c07f 100644 --- a/tests/locking/test-locking.c +++ b/tests/locking/test-locking.c @@ -21,33 +21,21 @@ #include #include - -#include "lib/error.h" - -LUMIERA_ERROR_DEFINE(TEST, "test error"); +#include "tests/test.h" int conditionforgotunlock (); int mutexforgotunlock (); +TESTS_BEGIN -int -main (int argc, char** argv) +TEST ("conditionforgotunlock") { - NOBUG_INIT; - - if (argc == 1) - return 0; - - if (!strcmp(argv[1], "conditionforgotunlock")) - { - return conditionforgotunlock (); - } - if (!strcmp(argv[1], "mutexforgotunlock")) - { - return mutexforgotunlock (); - } - else - return 1; - - return 0; + return conditionforgotunlock (); } + +TEST ("mutexforgotunlock") +{ + return mutexforgotunlock (); +} + +TESTS_END diff --git a/tests/plugin/plugin_main.c b/tests/plugin/plugin_main.c index 2efe32471..4b95ea60c 100644 --- a/tests/plugin/plugin_main.c +++ b/tests/plugin/plugin_main.c @@ -3,6 +3,7 @@ #include "lib/plugin.h" #include "hello_interface.h" +LUMIERA_ERROR_DEFINE(FAILURE, "test failure"); int main(int argc, char** argv) @@ -25,7 +26,7 @@ main(int argc, char** argv) LUMIERA_INTERFACE_TYPE(hello, 1)* hello_de = (LUMIERA_INTERFACE_TYPE(hello, 1)*) lumiera_interface_open ("example_plugin", "german_1", sizeof(LUMIERA_INTERFACE_TYPE(hello, 1))); - if (!hello_de) LUMIERA_DIE; + if (!hello_de) LUMIERA_DIE (FAILURE); hello_de->hello(); hello_de->goodbye(argv[1]); @@ -33,7 +34,7 @@ main(int argc, char** argv) LUMIERA_INTERFACE_TYPE(hello, 1)* hello_en = (LUMIERA_INTERFACE_TYPE(hello, 1)*) lumiera_interface_open ("example_plugin", "english_1", sizeof(LUMIERA_INTERFACE_TYPE(hello, 1))); - if (!hello_en) LUMIERA_DIE; + if (!hello_en) LUMIERA_DIE (FAILURE); hello_en->hello(); hello_en->goodbye(argv[1]); @@ -48,7 +49,7 @@ main(int argc, char** argv) LUMIERA_INTERFACE_TYPE(hello, 1)* hello_de = (LUMIERA_INTERFACE_TYPE(hello, 1)*) lumiera_interface_open ("example_plugin_cpp", "german_1", sizeof(LUMIERA_INTERFACE_TYPE(hello, 1))); - if (!hello_de) LUMIERA_DIE; + if (!hello_de) LUMIERA_DIE (FAILURE); hello_de->hello(); hello_de->goodbye(argv[1]); @@ -56,7 +57,7 @@ main(int argc, char** argv) LUMIERA_INTERFACE_TYPE(hello, 1)* hello_en = (LUMIERA_INTERFACE_TYPE(hello, 1)*) lumiera_interface_open ("example_plugin_cpp", "english_1", sizeof(LUMIERA_INTERFACE_TYPE(hello, 1))); - if (!hello_en) LUMIERA_DIE; + if (!hello_en) LUMIERA_DIE (FAILURE); hello_en->hello(); hello_en->goodbye(argv[1]); diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 000000000..d25fa0a59 --- /dev/null +++ b/tests/test.h @@ -0,0 +1,52 @@ +/* + test.h - macros for running tests + + Copyright (C) CinelerraCV + 2008, 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 TEST_H +#define TEST_H + +#include + +#include "lib/error.h" + +LUMIERA_ERROR_DEFINE (TEST, "test error"); + +#define TESTS_BEGIN \ +int \ +main (int argc, char** argv) \ +{ \ + NOBUG_INIT; \ + \ + if (argc == 1) \ + return 1; + +#define TEST(name) \ + else if (!strcmp(argv[1], name)) + + +#define TESTS_END \ + else \ + return 1; \ + \ + return 0; \ +} + + +#endif diff --git a/wiki/compatibility.html b/wiki/compatibility.html index 979f2301c..6eec84d85 100644 --- a/wiki/compatibility.html +++ b/wiki/compatibility.html @@ -2104,13 +2104,26 @@ if (oldText.indexOf("SplashScreen")==-1) } //}}} -
+
! Source code formatting
-http://www.gnu.org/prep/standards/html_node/Formatting.html#Formatting
+We decided to use the 'gnu-style' for indenting (using spaces and never tabs!):
+ http://www.gnu.org/prep/standards/html_node/Formatting.html#Formatting
+
+It is reasonable to be relaxed about soem formatting rules:
+ * line length might be longer when required
+ * inter expession spacing can be changed to the actual needed ex: (2*x + 2)
+
+Things we are pedantic about:
+ * use never ever tab characters in C/C++ sources
+ * be consistent
+ * source files should end with a newline
+ * no trailing or bogus whitespaces
+
+! Coding Practices
+
+! Writing Tests
 
 ! Contibuting
-
-! Tests
 
diff --git a/wiki/index.html b/wiki/index.html index 1ed553afd..c0c2e2a5f 100755 --- a/wiki/index.html +++ b/wiki/index.html @@ -930,11 +930,10 @@ git push git://git.pipapo.org/lumiera/mob lumiera/mob is an anonymous account at pipapo.org where everyone can commit changes.
-
+
We keep a protocol or short summary of each important discussion. The summaries of the monthly developer meetings are posted to the Mailinglist and can be found on pipapo.org too
 
-* [[04-08 developer meeting 3.Apr.2008|IRC_2008-04-03]]
-* [[03-08 developer meeting 6.Mar.2008|IRC_2008-03-06]]
+* [[2.official developer meeting 6.March.2008|IRC_2008-03-06]]
 * [[1.official developer meeting 1.Feb.2008|IRC_2008-02-01]]
 * [[informal developer meeting 10.Aug.2007|IRC_2007-08-10]]
 
@@ -1807,17 +1806,16 @@ Wiki works. It is simple to use and just flexible enough to handle the task. I d Please __end your tiddlers in a newline__, this makes merging in git easier since the /pre tag used in tiddlywiki will become on a single line. ---- -!Architecture and Subsystems +!Design Draft to get started, we create design drafts emphasizing different aspects and regions of Lumiera -* Cehteh works on the data backend, see [[this page|backend.html]] -* Ichthyo focuses mainly on Edit operations and Builder, [[see this separate page|renderengine.html]] -* Gmerlin is in charge of [[GAVL|http://gmerlin.sourceforge.net/gavl.html]] for processing of video data -* as of 4/08 a GuiWorkingGroup is emerging... +* Ichthyo focuses mainly on the Render Engine and its interconnection to the EDL, [[see this separate page|renderengine.html]] +* Cehteh works on the data backend draft, see [[this page|backend.html]] * Some tools which don't fit somewhere else and are used everywhere are put into a [[Support Library|support_library.html]] +* [[Description of the Test System|TestSh]] -!Coding &mdash; Building &mdash; Testing -how to organize several aspects of the practical coding... +!Coding Structures +next we should //start thinking// on how to organize several aspects of the practical coding... * what to do in BOUML? &rarr; [[more|whatInBOUML]] * how to organize packages, files, includes? &rarr; [[more|SrcTreeStructure]] * how to organize the executable to be built?