diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index a89dc137e..74911c158 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -61,6 +61,7 @@ liblumiera_la_SOURCES = \ $(liblumiera_la_srcdir)/test/test-helper.cpp \ $(liblumiera_la_srcdir)/test/testoption.cpp \ $(liblumiera_la_srcdir)/time.c \ + $(liblumiera_la_srcdir)/tmpbuf.c \ $(liblumiera_la_srcdir)/util.cpp @@ -69,6 +70,7 @@ noinst_HEADERS += \ $(liblumiera_la_srcdir)/advice/binding.hpp \ $(liblumiera_la_srcdir)/advice/index.hpp \ $(liblumiera_la_srcdir)/allocationcluster.hpp \ + $(liblumiera_la_srcdir)/allocationcluster.hpp \ $(liblumiera_la_srcdir)/condition.h \ $(liblumiera_la_srcdir)/error.h \ $(liblumiera_la_srcdir)/error.hpp \ @@ -84,11 +86,14 @@ noinst_HEADERS += \ $(liblumiera_la_srcdir)/meta/typelist.hpp \ $(liblumiera_la_srcdir)/mpool.h \ $(liblumiera_la_srcdir)/mrucache.h \ + $(liblumiera_la_srcdir)/mrucache.h \ $(liblumiera_la_srcdir)/mutex.h \ $(liblumiera_la_srcdir)/nobug-init.hpp \ $(liblumiera_la_srcdir)/observable-list.hpp \ $(liblumiera_la_srcdir)/p.hpp \ $(liblumiera_la_srcdir)/ppmpl.h \ + $(liblumiera_la_srcdir)/ppmpl.h \ + $(liblumiera_la_srcdir)/psplay.h \ $(liblumiera_la_srcdir)/psplay.h \ $(liblumiera_la_srcdir)/query.hpp \ $(liblumiera_la_srcdir)/reccondition.h \ @@ -110,6 +115,8 @@ noinst_HEADERS += \ $(liblumiera_la_srcdir)/test/test-helper.hpp \ $(liblumiera_la_srcdir)/test/testoption.hpp \ $(liblumiera_la_srcdir)/time.h \ + $(liblumiera_la_srcdir)/time.h \ + $(liblumiera_la_srcdir)/tmpbuf.h \ $(liblumiera_la_srcdir)/tree.hpp \ $(liblumiera_la_srcdir)/util.hpp \ $(liblumiera_la_srcdir)/visitor-dispatcher.hpp \ diff --git a/src/lib/safeclib.c b/src/lib/safeclib.c index 989648812..ae621501d 100644 --- a/src/lib/safeclib.c +++ b/src/lib/safeclib.c @@ -27,7 +27,6 @@ #include #include #include -#include #include @@ -134,149 +133,6 @@ lumiera_streq (const char* a, const char* b) } -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) -{ - (void) buf; - lumiera_tmpbuf_freeall (); -} - -static void -lumiera_tmpbuf_init (void) -{ - pthread_key_create (&lumiera_tmpbuf_tls_key, lumiera_tmpbuf_destroy); - TODO ("register an atexit() handler to free tmpbufs"); -} - - -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, NULL); - for (int idx = 0; idx < 64; ++idx) - lumiera_free (buf->buffers[idx]); - lumiera_free (buf); - } -} - - -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_calloc (1, sizeof (struct lumiera_tmpbuf_struct))); - - buf->idx = (buf->idx + 1) & 0x3f; - - if (buf->sizes[buf->idx] < size || buf->sizes[buf->idx] > 8*size) - { - lumiera_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_snprintf (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+1); - va_start (args, fmt); - vsnprintf (buf, len+1, fmt, args); - va_end (args); - - return buf; -} - - -char* -lumiera_tmpbuf_strcat3 (const char* str1, size_t str1_len, - const char* str2, size_t str2_len, - const char* str3, size_t str3_len) -{ - return lumiera_tmpbuf_snprintf (SIZE_MAX, "%.*s%s%.*s%s%.*s", - str1?str1_len:0, str1?str1:"", str1?".":"", - str2?str2_len:0, str2?str2:"", - str3?".":"", str3?str3_len:0, str3?str3:""); -} - - -char* -lumiera_tmpbuf_tr (const char* in, const char* from, const char* to, const char* def) -{ - REQUIRE (strlen (from) == strlen (to), "from and to character set must have equal length"); - - char* ret = lumiera_tmpbuf_strndup (in, SIZE_MAX); - - char* wpos; - char* rpos; - for (wpos = rpos = ret; *rpos; ++rpos, ++wpos) - { - char* found = strchr (from, *rpos); - if (found) - *wpos = to[found-from]; - else if (def) - { - if (*def) - *wpos = *def; - else - { - ++rpos; - if (!*rpos) - break; - } - } - else - return NULL; - } - *wpos = '\0'; - - return ret; -} /* diff --git a/src/lib/safeclib.h b/src/lib/safeclib.h index be322aa0b..7a79b5021 100644 --- a/src/lib/safeclib.h +++ b/src/lib/safeclib.h @@ -116,74 +116,10 @@ lumiera_strncmp (const char* a, const char* b, size_t len); 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_snprintf (size_t size, const char* fmt, ...); - - -/** - * Concat up to 3 strings in a tmpbuf. - * @param str1 first string to concat or NULL - * @param str1_len how much of the first string shall be used - * @param str2 second string to concat or NULL - * @param str2_len how much of the second string shall be used - * @param str3 third string to concat or NULL - * @param str3_len how much of the third string shall be used - * @return temporary buffer containing the constructed of the string - */ -char* -lumiera_tmpbuf_strcat3 (const char* str1, size_t str1_len, - const char* str2, size_t str2_len, - const char* str3, size_t str3_len); - -/** - * Translates characters in a string, similar to the shell 'tr' utility - * @param in input string to be translated - * @param from source character set - * @param to destination character set - * @param def default destination character when a character is not in the source set, - * when NULL then translation will abort on unknown characters and return NULL, - * when "" then unknown characters will be removed - * when set to a single character string, unknown characters will be replaced with this string - * @return temporary buffer containing the constructed of the string - */ -char* -lumiera_tmpbuf_tr (const char* in, const char* from, const char* to, const char* def); - +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/tmpbuf.c b/src/lib/tmpbuf.c new file mode 100644 index 000000000..6dd7ba114 --- /dev/null +++ b/src/lib/tmpbuf.c @@ -0,0 +1,202 @@ +/* + tmpbuf.c - Round Robin Temporary buffers + + Copyright (C) Lumiera.org + 2008, 2010 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 "lib/tmpbuf.h" + + +#include +#include +#include +#include + + +struct lumiera_tmpbuf_struct +{ + /* tiny buffers are not aligned, anything which is smaller than sizeof(void*) */ + char tiny_buffers[LUMIERA_TMPBUF_NUM*(sizeof(void*)-1)]; + unsigned tiny_idx; + /* first 16 static LUMIERA_TMPBUF_SMALL byte arrays */ + char small_buffers[LUMIERA_TMPBUF_NUM][LUMIERA_TMPBUF_SMALL]; + unsigned small_idx; + /* next 16 dynamic arrays for biggier buffers */ + void* big_buffers[LUMIERA_TMPBUF_NUM]; + size_t big_sizes[LUMIERA_TMPBUF_NUM]; + unsigned big_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) +{ + (void) buf; + lumiera_tmpbuf_freeall (); +} + +static void +lumiera_tmpbuf_init (void) +{ + pthread_key_create (&lumiera_tmpbuf_tls_key, lumiera_tmpbuf_destroy); + atexit (lumiera_tmpbuf_freeall); +} + + +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, NULL); + for (int idx = 0; idx < LUMIERA_TMPBUF_NUM; ++idx) + lumiera_free (buf->big_buffers[idx]); + lumiera_free (buf); + } +} + + +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_calloc (1, sizeof (struct lumiera_tmpbuf_struct))); + + if (size < sizeof(void*)) + { + buf->tiny_idx = (buf->tiny_idx + size) & ~(LUMIERA_TMPBUF_NUM*sizeof(void*)-1); + return &buf->tiny_buffers[buf->tiny_idx-size]; + } + else if (size <= LUMIERA_TMPBUF_SMALL) + { + buf->small_idx = (buf->small_idx + 1) % LUMIERA_TMPBUF_NUM; + return buf->small_buffers[buf->small_idx]; + } + else + { + buf->big_idx = (buf->big_idx + 1) & ~(LUMIERA_TMPBUF_NUM-1); + + if (buf->big_sizes[buf->big_idx] < size || buf->big_sizes[buf->big_idx] > 8*size) + { + lumiera_free (buf->big_buffers[buf->big_idx]); + buf->big_sizes[buf->big_idx] = (size + LUMIERA_TMPBUF_SMALL-1) & ~(LUMIERA_TMPBUF_SMALL-1); + buf->big_buffers[buf->big_idx] = lumiera_malloc (buf->big_sizes[buf->big_idx]); + } + return buf->big_buffers[buf->big_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_snprintf (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+1); + va_start (args, fmt); + vsnprintf (buf, len+1, fmt, args); + va_end (args); + + return buf; +} + + +char* +lumiera_tmpbuf_strcat3 (const char* str1, size_t str1_len, + const char* str2, size_t str2_len, + const char* str3, size_t str3_len) +{ + return lumiera_tmpbuf_snprintf (SIZE_MAX, "%.*s%s%.*s%s%.*s", + str1?str1_len:0, str1?str1:"", str1?".":"", + str2?str2_len:0, str2?str2:"", + str3?".":"", str3?str3_len:0, str3?str3:""); +} + + +char* +lumiera_tmpbuf_tr (const char* in, const char* from, const char* to, const char* def) +{ + REQUIRE (strlen (from) == strlen (to), "from and to character set must have equal length"); + + char* ret = lumiera_tmpbuf_strndup (in, SIZE_MAX); + + char* wpos; + char* rpos; + for (wpos = rpos = ret; *rpos; ++rpos, ++wpos) + { + char* found = strchr (from, *rpos); + if (found) + *wpos = to[found-from]; + else if (def) + { + if (*def) + *wpos = *def; + else + { + ++rpos; + if (!*rpos) + break; + } + } + else + return NULL; + } + *wpos = '\0'; + + return ret; +} + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/tmpbuf.h b/src/lib/tmpbuf.h new file mode 100644 index 000000000..da1f0f09a --- /dev/null +++ b/src/lib/tmpbuf.h @@ -0,0 +1,123 @@ +/* + tmpbuf.c - Round Robin Temporary buffers + + Copyright (C) Lumiera.org + 2008, 2010 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_TMPBUF_H +#define LUMIERA_TMPBUF_H + +#include + +/** + * @file + * Round robin temporary buffers. + * This provides some buffers per thread which are round-robin recycled with each use. + * The idea is to have fast buffers for temporal data without need for explicit heap management. + */ + + +/* following 2 values must be exponent of 2 */ +/** + * Number of buffers in the ring + * This also defines how many concurent buffers can be in use in one thread (including nested calls) + * tmpbufs are only suitable for nested calls where one knows in advance how much tmpbufs might be used + */ +#define LUMIERA_TMPBUF_NUM 16 + +/** + * Size of a small block + * tmpbuf's knows small and big blocks, anything bigger than LUMIERA_TMPBUF_SMALL will be + * allocated dynamically as 'big' buffer while for anything smaller static buffers are allocated. + */ +#define LUMIERA_TMPBUF_SMALL 256 + + +/** + * 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_snprintf (size_t size, const char* fmt, ...); + + +/** + * Concat up to 3 strings in a tmpbuf. + * @param str1 first string to concat or NULL + * @param str1_len how much of the first string shall be used + * @param str2 second string to concat or NULL + * @param str2_len how much of the second string shall be used + * @param str3 third string to concat or NULL + * @param str3_len how much of the third string shall be used + * @return temporary buffer containing the constructed of the string + */ +char* +lumiera_tmpbuf_strcat3 (const char* str1, size_t str1_len, + const char* str2, size_t str2_len, + const char* str3, size_t str3_len); + +/** + * Translates characters in a string, similar to the shell 'tr' utility + * @param in input string to be translated + * @param from source character set + * @param to destination character set + * @param def default destination character when a character is not in the source set, + * when NULL then translation will abort on unknown characters and return NULL, + * when "" then unknown characters will be removed + * when set to a single character string, unknown characters will be replaced with this character + * @return temporary buffer containing the constructed of the string + */ +char* +lumiera_tmpbuf_tr (const char* in, const char* from, const char* to, const char* def); + + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/tests/library/test-safeclib.c b/tests/library/test-safeclib.c index 5a91aba9f..a14a73c33 100644 --- a/tests/library/test-safeclib.c +++ b/tests/library/test-safeclib.c @@ -17,6 +17,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "lib/safeclib.h" +#include "lib/tmpbuf.h" /* not factored out yet */ #include "tests/test.h" #include