diff --git a/.gitignore b/.gitignore index 69cb7933a..cf7ebb45d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ configure config.log aclocal.m4 semantic.cache +wiki/backups/* +doc/devel/draw/*.png diff --git a/Makefile.am b/Makefile.am index 8a2bc889f..f3ae3ef41 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,9 @@ include $(top_srcdir)/src/common/Makefile.am include $(top_srcdir)/src/lib/Makefile.am include $(top_srcdir)/src/backend/Makefile.am +# tools +include $(top_srcdir)/src/tool/Makefile.am + # gui include $(top_srcdir)/src/gui/Makefile.am diff --git a/SConstruct b/SConstruct index 2b3e2cfd0..9c56cece3 100644 --- a/SConstruct +++ b/SConstruct @@ -79,7 +79,7 @@ def setupBasicEnvironment(): env.Append(CPPDEFINES = '_GNU_SOURCE') appendCppDefine(env,'DEBUG','DEBUG', 'NDEBUG') - appendCppDefine(env,'OPENGL','USE_OPENGL') +# appendCppDefine(env,'OPENGL','USE_OPENGL') appendVal(env,'ARCHFLAGS', 'CCFLAGS') # for both C and C++ appendVal(env,'OPTIMIZE', 'CCFLAGS', val=' -O3') appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') @@ -128,7 +128,7 @@ def defineCmdlineOptions(): allowed_values=('ALPHA', 'BETA', 'RELEASE')) ,BoolOption('DEBUG', 'Build with debugging information and no optimizations', False) ,BoolOption('OPTIMIZE', 'Build with strong optimization (-O3)', False) - ,BoolOption('OPENGL', 'Include support for OpenGL preview rendering', False) +# ,BoolOption('OPENGL', 'Include support for OpenGL preview rendering', False) # ,EnumOption('DIST_TARGET', 'Build target architecture', 'auto', # allowed_values=('auto', 'i386', 'i686', 'x86_64' ), ignorecase=2) ,PathOption('DESTDIR', 'Installation dir prefix', '/usr/local') @@ -337,8 +337,8 @@ def definePostBuildTargets(env, artifacts): env.Clean ('build', [ '$SRCDIR/pre.gch' ]) doxydoc = artifacts['doxydoc'] = env.Doxygen('doc/devel/Doxyfile') - env.Alias ('doxydoc', doxydoc) - env.Clean ('doxydoc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) + env.Alias ('doc', doxydoc) + env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) def defineInstallTargets(env, artifacts): diff --git a/doc/devel/Doxyfile b/doc/devel/Doxyfile index b7c59fa54..f43b69042 100644 --- a/doc/devel/Doxyfile +++ b/doc/devel/Doxyfile @@ -1,11 +1,11 @@ -# Doxyfile 1.5.5 +# Doxyfile 1.5.6 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = Lumiera -PROJECT_NUMBER = 0.1+pre +PROJECT_NUMBER = 3.0+alpha OUTPUT_DIRECTORY = CREATE_SUBDIRS = YES OUTPUT_LANGUAGE = English @@ -32,7 +32,7 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO -DETAILS_AT_TOP = YES +DETAILS_AT_TOP = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 @@ -44,6 +44,7 @@ OPTIMIZE_OUTPUT_VHDL = NO BUILTIN_STL_SUPPORT = YES CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES TYPEDEF_HIDES_STRUCT = NO @@ -65,8 +66,8 @@ CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES -SORT_MEMBER_DOCS = NO -SORT_BRIEF_DOCS = NO +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = YES SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES @@ -77,6 +78,8 @@ ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO +SHOW_FILES = YES +SHOW_NAMESPACES = YES FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages @@ -87,7 +90,7 @@ WARN_IF_UNDOCUMENTED = NO WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES WARN_FORMAT = "$file:$line: $text" -WARN_LOGFILE = warnings.txt +WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- @@ -179,18 +182,20 @@ HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO GENERATE_DOCSET = NO -DOCSET_FEEDNAME = "Lumiera Doxygen docs" +DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project HTML_DYNAMIC_SECTIONS = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO +CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = YES TREEVIEW_WIDTH = 250 +FORMULA_FONTSIZE = 10 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- @@ -268,6 +273,8 @@ CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES +DOT_FONTNAME = FreeSans +DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES diff --git a/doc/devel/draw/Lumi.Architecture-1.svg b/doc/devel/draw/Lumi.Architecture-1.svg index 9df05b7d3..40fab02c7 100644 --- a/doc/devel/draw/Lumi.Architecture-1.svg +++ b/doc/devel/draw/Lumi.Architecture-1.svg @@ -14,10 +14,10 @@ sodipodi:version="0.32" inkscape:version="0.45.1" version="1.0" - sodipodi:docbase="/home/hiv/devel/skizzen" - sodipodi:docname="Cin3.Architecture-1.svg" + sodipodi:docbase="/mnt/Lager/heim/devel/lumi/doc/devel/draw" + sodipodi:docname="Lumi.Architecture-1.svg" inkscape:output_extension="org.inkscape.output.svg.inkscape" - inkscape:export-filename="/home/hiv/devel/skizzen/Cin3.Architecture-1.png" + inkscape:export-filename="/home/hiv/devel/skizzen/Lumi.Architecture-1.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90"> + inkscape:window-y="28" + showgrid="true" + inkscape:grid-points="true" + inkscape:grid-bbox="false" + gridspacingx="2px" + gridspacingy="2px" + objecttolerance="50" + gridtolerance="10000" /> @@ -87,7 +94,7 @@ style="opacity:1;color:#000000;fill:#ececec;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" id="rect3136" width="449.55356" - height="142.91072" + height="144.89977" x="46.375008" y="369.10715" /> Defaults + y="267.42578">SessionDefaults Cache + width="119.99424" + height="29.96665" + x="159.96713" + y="480.03333" /> IO-Handling Storage Backend(s) + x="300" + y="480">Storage Backend(s) Plugins (Codecs, Effects,...) + y="355.89285" /> + y="232.85713" /> - - - - (re)-Building + + Plugin Loader + + + + + + Serializer + + + + + + + Defaults + + + + diff --git a/doc/devel/draw/LumiLogo.svg b/doc/devel/draw/LumiLogo.svg new file mode 100644 index 000000000..a4d60254d --- /dev/null +++ b/doc/devel/draw/LumiLogo.svg @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + image/svg+xml + + Lumiera Logo + 8/2008 + + + Lumiera.org + + + + + + + + + + + + + + + + diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 8ad4cce2b..abb576021 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -26,7 +26,12 @@ liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/file.c \ $(liblumibackend_a_srcdir)/filehandle.c \ $(liblumibackend_a_srcdir)/filedescriptor.c \ - $(liblumibackend_a_srcdir)/filehandlecache.c + $(liblumibackend_a_srcdir)/filehandlecache.c \ + $(liblumibackend_a_srcdir)/config.c \ + $(liblumibackend_a_srcdir)/config_typed.c \ + $(liblumibackend_a_srcdir)/configentry.c \ + $(liblumibackend_a_srcdir)/configitem.c \ + $(liblumibackend_a_srcdir)/config_lookup.c noinst_HEADERS += \ @@ -35,5 +40,9 @@ noinst_HEADERS += \ $(liblumibackend_a_srcdir)/file.h \ $(liblumibackend_a_srcdir)/filehandle.h \ $(liblumibackend_a_srcdir)/filedescriptor.h \ - $(liblumibackend_a_srcdir)/filehandlecache.h + $(liblumibackend_a_srcdir)/filehandlecache.h \ + $(liblumibackend_a_srcdir)/config.h \ + $(liblumibackend_a_srcdir)/configentry.h \ + $(liblumibackend_a_srcdir)/configitem.h \ + $(liblumibackend_a_srcdir)/config_lookup.h diff --git a/src/backend/config.c b/src/backend/config.c new file mode 100644 index 000000000..276088673 --- /dev/null +++ b/src/backend/config.c @@ -0,0 +1,355 @@ +/* + config.c - Lumiera configuration system + + 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. +*/ + +//TODO: Support library includes// +#include "lib/safeclib.h" + + +//TODO: Lumiera header includes// +#include "backend/config.h" + +//TODO: internal/static forward declarations// + + +//TODO: System includes// +#include +#include +#include + +/** + * @file + * + */ + +NOBUG_DEFINE_FLAG_PARENT (config_all, backend); +NOBUG_DEFINE_FLAG_PARENT (config, config_all); +NOBUG_DEFINE_FLAG_PARENT (config_typed, config_all); +NOBUG_DEFINE_FLAG_PARENT (config_file, config_all); +NOBUG_DEFINE_FLAG_PARENT (config_item, config_all); +NOBUG_DEFINE_FLAG_PARENT (config_lookup, config_all); + +LUMIERA_ERROR_DEFINE (CONFIG_SYNTAX, "syntax error in configfile"); +LUMIERA_ERROR_DEFINE (CONFIG_SYNTAX_KEY, "syntax error in key"); +LUMIERA_ERROR_DEFINE (CONFIG_SYNTAX_VALUE, "syntax error in value"); +LUMIERA_ERROR_DEFINE (CONFIG_NO_ENTRY, "no configuration entry"); +LUMIERA_ERROR_DEFINE (CONFIG_DEFAULT, "illegal default value"); + +/** + * defaults for the configuraton system itself + */ +const char* lumiera_config_defaults[] = + { + /* Low level formating, don't change these */ + "config.formatstr.link = '< %s'", + "config.formatstr.number.dec = '= %lld'", + "config.formatstr.number.hex = '= 0x%llX'", + "config.formatstr.number.oct = '= 0%llo'", + "config.formatstr.real = '= %Lg'", + "config.formatstr.real.dec = '= %Lf'", + "config.formatstr.real.sci = '= %Le'", + "config.formatstr.string = '=%s'", + "config.formatstr.string.dquoted = '= \"%s\"'", + "config.formatstr.string.quoted = '= ''%s'''", + "config.formatstr.word = '= %s'", + "config.formatstr.bool = '= %d'", + + /* default representations per type */ + "config.formatdef.link < config.formatstr.link", + "config.formatdef.number < config.formatstr.number.dec", + "config.formatdef.real < config.formatstr.real", + "config.formatdef.string < config.formatstr.string", + "config.formatdef.word < config.formatstr.word", + "config.formatdef.bool < config.formatstr.bool", + + /* per key formatting override stored under */ + "config.formatkey ='config.format.%s'", + + NULL + }; + + +/* singleton config */ +LumieraConfig lumiera_global_config = NULL; + + +int +lumiera_config_init (const char* path) +{ + TRACE (config); + REQUIRE (!lumiera_global_config, "Configuration subsystem already initialized"); + REQUIRE (path); + + NOBUG_INIT_FLAG (config_all); + NOBUG_INIT_FLAG (config); + NOBUG_INIT_FLAG (config_typed); + NOBUG_INIT_FLAG (config_file); + NOBUG_INIT_FLAG (config_item); + NOBUG_INIT_FLAG (config_lookup); + + lumiera_global_config = lumiera_malloc (sizeof (*lumiera_global_config)); + lumiera_config_lookup_init (&lumiera_global_config->keys); + + lumiera_configitem_init (&lumiera_global_config->defaults); + lumiera_configitem_init (&lumiera_global_config->files); + lumiera_configitem_init (&lumiera_global_config->TODO_unknown); + + lumiera_rwlock_init (&lumiera_global_config->lock, "config rwlock", &NOBUG_FLAG (config)); + + lumiera_config_setdefault (lumiera_tmpbuf_snprintf (SIZE_MAX, "config.path = %s", path)); + + for (const char** itr = lumiera_config_defaults; *itr; ++itr) + { + lumiera_config_setdefault (*itr); + } + + return 0; +} + + +void +lumiera_config_destroy () +{ + TRACE (config); + if (lumiera_global_config) + { + lumiera_rwlock_destroy (&lumiera_global_config->lock, &NOBUG_FLAG (config)); + lumiera_configitem_destroy (&lumiera_global_config->defaults, &lumiera_global_config->keys); + lumiera_configitem_destroy (&lumiera_global_config->files, &lumiera_global_config->keys); + lumiera_configitem_destroy (&lumiera_global_config->TODO_unknown, &lumiera_global_config->keys); + lumiera_config_lookup_destroy (&lumiera_global_config->keys); + lumiera_free (lumiera_global_config); + lumiera_global_config = NULL; + } + else + WARN (config, "Tried to destroy non initialized config subsystem"); +} + + +int +lumiera_config_load (const char* file) +{ + TRACE (config); + UNIMPLEMENTED(); + return -1; +} + + +int +lumiera_config_save () +{ + TRACE (config); + UNIMPLEMENTED(); + return -1; +} + + +int +lumiera_config_purge (const char* filename) +{ + TRACE (config); + + UNIMPLEMENTED(); + return -1; +} + + +int +lumiera_config_get (const char* key, const char** value) +{ + TRACE (config); + REQUIRE (key); + REQUIRE (value); + + int ret = -1; + + /* we translate the key for the env var override by making it uppercase and replace . with _, + as side effect, this also checks the key syntax */ + char* tr_key = lumiera_tmpbuf_tr (key, + LUMIERA_CONFIG_KEY_CHARS, + LUMIERA_CONFIG_ENV_CHARS, + NULL); + + if (tr_key) + { + char* env = lumiera_tmpbuf_snprintf (2048, "LUMIERA_%s", tr_key); + + *value = getenv(env); + if (*value) + { + ret = 0; + NOTICE (config, "envvar override for config %s = %s", env, *value); + } + else + { + TODO ("follow '<' delegates?"); + LumieraConfigitem item = lumiera_config_lookup_item_find (&lumiera_global_config->keys, key); + + if (item) + { + *value = item->delim+1; + ret = 0; + } + else + LUMIERA_ERROR_SET (config, CONFIG_NO_ENTRY); + } + } + else + { + LUMIERA_ERROR_SET (config, CONFIG_SYNTAX_KEY); + } + + return ret; +} + + +int +lumiera_config_get_default (const char* key, const char** value) +{ + TRACE (config); + REQUIRE (key); + REQUIRE (value); + + int ret = -1; + + TODO ("follow '<' delegates?"); + TODO ("refactor _get and get_default to iterator access (return LList or Lookupentry)"); + LumieraConfigitem item = lumiera_config_lookup_item_tail_find (&lumiera_global_config->keys, key); + + if (item && item->parent == &lumiera_global_config->defaults) + { + *value = item->delim+1; + ret = 0; + } + + return ret; +} + + +int +lumiera_config_set (const char* key, const char* delim_value) +{ + TRACE (config); + + TODO ("if does this item already exist in a user writeable file?"); + TODO (" replace delim_value"); + + TODO ("else"); + TODO (" find matching prefix"); + TODO (" find matching suffix"); + TODO (" find proper prefix indentation, else use config.indent"); + TODO (" create configitem with prefix/suffix removed"); + + + +// * set a value by key +// * handles internally everything as string:string key:value pair. +// * lowlevel function +// * tag file as dirty +// * set will create a new user configuration file if it does not exist yet or will append a line to the existing one in RAM. These files, tagged as 'dirty', will be only written if save() is called. + + + + UNIMPLEMENTED(); + return -1; +} + + +LumieraConfigitem +lumiera_config_setdefault (const char* line) +{ + TRACE (config); + REQUIRE (line); + + LumieraConfigitem item = NULL; + + LUMIERA_WRLOCK_SECTION (config, &lumiera_global_config->lock) + { + const char* key = line; + while (*key && isspace (*key)) + key++; + + key = lumiera_tmpbuf_strndup (line, strspn (line, LUMIERA_CONFIG_KEY_CHARS)); + + if (!(item = lumiera_config_lookup_item_find (&lumiera_global_config->keys, key)) || item->parent != &lumiera_global_config->defaults) + { + item = lumiera_configitem_new (line); + + if (item) + { + ENSURE (item->delim, "default must be a configentry with key=value or keydelim == '=' || *item->delim == '<', "default must be a configentry with key=value or keyline); + + llist_insert_head (&lumiera_global_config->defaults.childs, &item->link); + item->parent = &lumiera_global_config->defaults; + + lumiera_config_lookup_insert_default (&lumiera_global_config->keys, item); + } + } + } + + return item; +} + + +void +lumiera_config_dump (FILE* out) +{ + fprintf (out, "# registered defaults:\n"); + + LLIST_FOREACH (&lumiera_global_config->defaults.childs, node) + fprintf (out, "%s\n", ((LumieraConfigitem) node)->line); + + fprintf (out, "# end of defaults\n\n"); + +#if 0 /*TODO UNIMPLEMENTED */ + fprintf (out, "# files:\n"); + lumiera_configitem files; + fprintf (out, "# volatiles:") + lumiera_configitem TODO_unknown; +#endif +} + + +int +lumiera_config_reset (const char* key) +{ + TRACE (config); + UNIMPLEMENTED(); + return -1; +} + + +int +lumiera_config_info (const char* key, const char** filename, unsigned* line) +{ + TRACE (config); + UNIMPLEMENTED(); + return -1; +} + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/config.h b/src/backend/config.h new file mode 100644 index 000000000..7667f26f1 --- /dev/null +++ b/src/backend/config.h @@ -0,0 +1,262 @@ +/* + config.h - Lumiera configuration system + + 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_CONFIG_H +#define LUMIERA_CONFIG_H + +//TODO: Support library includes// +#include "lib/error.h" +#include "lib/rwlock.h" + +//TODO: Forward declarations// +struct lumiera_config_struct; + + +/* master config subsystem debug flag */ +NOBUG_DECLARE_FLAG (config_all); +/* config subsystem internals */ +NOBUG_DECLARE_FLAG (config); +/* high level typed interface operations */ +NOBUG_DECLARE_FLAG (config_typed); +/* file operations */ +NOBUG_DECLARE_FLAG (config_file); +/* single config items */ +NOBUG_DECLARE_FLAG (config_item); +/* lookup config keys */ +NOBUG_DECLARE_FLAG (config_lookup); + + +LUMIERA_ERROR_DECLARE (CONFIG_SYNTAX); +LUMIERA_ERROR_DECLARE (CONFIG_SYNTAX_KEY); +LUMIERA_ERROR_DECLARE (CONFIG_SYNTAX_VALUE); +LUMIERA_ERROR_DECLARE (CONFIG_NO_ENTRY); +LUMIERA_ERROR_DECLARE (CONFIG_DEFAULT); + +//TODO: Lumiera header includes// +#include "backend/config_lookup.h" + +//TODO: System includes// +#include +#include + +/** + * @file + * TODO documentation, http://www.pipapo.org/pipawiki/Lumiera/ConfigLoader + */ + +#define LUMIERA_CONFIG_KEY_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_." +#define LUMIERA_CONFIG_ENV_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789__" + +struct lumiera_config_struct +{ + lumiera_config_lookup keys; + + lumiera_configitem defaults; /* registered default values */ + lumiera_configitem files; /* all loaded files */ + lumiera_configitem TODO_unknown; /* all values which are not part of a file and not default TODO: this will be removed when file support is finished */ + + /* + all access is protected with rwlock's. + We use rwlocks here since concurrent reads are likely common. + + So far this is a global config lock, if this is a problem we might granularize it by locking on a file level. + config access is not planned to be transactional yet, if this is a problem we need to expose the rwlock to a config_acquire/config_release function pair + */ + lumiera_rwlock lock; +}; + +typedef struct lumiera_config_struct lumiera_config; +typedef lumiera_config* LumieraConfig; + +/** + * Supported high level types: TODO documenting + */ +/* TODO: add here as 'LUMIERA_CONFIG_TYPE(name, ctype)' the _get/_set prototypes are declared automatically below, you still have to implement them in config.c */ +#define LUMIERA_CONFIG_TYPES \ + LUMIERA_CONFIG_TYPE(link, char*) \ + LUMIERA_CONFIG_TYPE(number, signed long long) \ + LUMIERA_CONFIG_TYPE(real, long double) \ + LUMIERA_CONFIG_TYPE(string, char*) \ + LUMIERA_CONFIG_TYPE(word, char*) \ + LUMIERA_CONFIG_TYPE(bool, int) + + +// * does only initialize the variables, so that they get valid values, but does not allocate them as they will be allocated before as they are singleton. +// * lumiera_config_init (const char* searchpath) searchpath is a buildin-default, can be changed via configure and can be appended and overridden by using a flag, e.g. {{{ --config-path-append="" }}} or {{{ --config-path="" }}} + +/** + * Initialize the configuration subsystem. + * @param path search path for config files. + * Must be called only once + */ +int +lumiera_config_init (const char* path); + + +// * frees all space allocated by the ConfigLoader. + +/** + * Destroys the configuration subsystem. + * Subsequent calls are no-ops. + */ +void +lumiera_config_destroy (); + + +// * reads '''one''' single configuration file that will include all settings from other files. +// * does not read itself but give delegates reading. The actual reading and parsing will be done in configfile object. s.later. +/** + * + */ +int +lumiera_config_load (const char* file); + + +//{{{ lumiera_config_save () { LLIST_FOREACH(config_singleton.files, f) { LumieraFile file = (LumieraFile) f; if(lumiera_configfile_isdirty (file)) lumiera_configfile_save(file); } } }}} +// * saves all the changed settings to user's configuration files, but recognizes where settings came from and will write them to an appropriate named file. Example: '''changed''' values from ''/usr/local/share/lumiera/plugins/blur.conf'' will be saved into ''~/.lumiera/plugins/blur.conf'' +// * finds out which files are dirty and which settings have to be written to user's config files. +// * does initiate the actual saving procedure by delegating the save to the actual configfile objects, see below. +// * empty user configuration files in RAM will be deleted from disk on write. +// * checks whether the file has changed since last read, and will print out an error if necessary instead of overriding it without notification. +/** + * + */ +int +lumiera_config_save (); + + +// * `lumiera_config_purge(const char* filename)` removes all configs loaded from filename +/** + * + */ +int +lumiera_config_purge (const char* filename); + + +/** + * Does a diagnostic dump of the whole config database + */ +void +lumiera_config_dump (FILE* out); + + +// * {{{ lumiera_config_get(...) }}} +// * get a value by key +// * handles internally everything as string:string key:value pair. +// * lowlevel function +// * lumiera_config_integer_get (const char* key, int *value) will return integers instead of strings and return 0 if succeeded and -1 if it failed. +/** + * + */ +int +lumiera_config_get (const char* key, const char** value); + + +int +lumiera_config_get_default (const char* key, const char** value); + + +// * {{{ lumiera_config_set(...) }}} +// * set a value by key +// * handles internally everything as string:string key:value pair. +// * lowlevel function +// * tag file as dirty +// * set will create a new user configuration file if it does not exist yet or will append a line to the existing one in RAM. These files, tagged as 'dirty', will be only written if save() is called. + +/** + * + * + * @param key + * @param delim_value delimiter (= or <) followed by the value to be set + * + */ +int +lumiera_config_set (const char* key, const char* delim_value); + + +/** + * Installs a default value for a config key. + * Any key might have an associated default value which is used when + * no other configuration is available, this can be set once. + * Any subsequent call will be a no-op. + * @param line line with key, delimiter and value to store as default value + * @return NULL in case of an error, else a pointer to the default configitem + */ +LumieraConfigitem +lumiera_config_setdefault (const char* line); + + + +// * {{{int lumiera_config_TYPE_get(const char* key, TYPE* value, const char* default) }}} +// High level config interface for different types. +// if default is given (!NULL) then value is set to default in case key was not found or any other error occured. +// error code is still set and -1 (fail) is returned in case of an error, but it might be cleared with no ill effects. +// NOTE: errors are persistent in our error handler, they must still be cleared, even when ignored. +// if default is given then 'KEY_NOT_FOUND' is not a error here, if default is NULL then it is +// NOTE2: default values are given as strings, the config loader remembers a given default value and checks if it got changed +// when it is _set(). Thus a default value can be supressed when set/written +/** + * + */ +#define LUMIERA_CONFIG_TYPE(name, type) \ + int \ + lumiera_config_##name##_get (const char* key, type* value); +LUMIERA_CONFIG_TYPES +#undef LUMIERA_CONFIG_TYPE + + + +// * {{{ lumiera_config_TYPE_set (const char* key, TYPE*value, const char* fmt) }}} +// Highlevel interface for different types, fmt is a printf format specifier for the desired format, when NULL, defaults apply. +/** + * + */ +#define LUMIERA_CONFIG_TYPE(name, type) \ + int \ + lumiera_config_##name##_set (const char* key, type* value); +LUMIERA_CONFIG_TYPES +#undef LUMIERA_CONFIG_TYPE + + +// * {{{ lumiera_config_reset(...) }}} +// * reset a value by key to the system default values, thus removes a user's configuration line. +/** + * + */ +int +lumiera_config_reset (const char* key); + + +// * Find exact place of a setting. +/** + * + */ +int +lumiera_config_info (const char* key, const char** filename, unsigned* line); + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/config_lookup.c b/src/backend/config_lookup.c new file mode 100644 index 000000000..96c62c880 --- /dev/null +++ b/src/backend/config_lookup.c @@ -0,0 +1,239 @@ +/* + config_lookup.c - Lookup functions for the config subsystem + + 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 "backend/config_lookup.h" +#include "backend/config.h" + +/* we only use one fatal error for now, when allocation in the config system fail, something else is pretty wrong */ +LUMIERA_ERROR_DEFINE (CONFIG_LOOKUP, "config lookup failure"); + +/* + support functions for the splay tree +*/ +static int +cmp_fn (const void* a, const void* b); + +static void +delete_fn (PSplaynode node); + +static const void* +key_fn (const PSplaynode node); + + +/** + * @file + * Implementation of the lookup of configuration keys + */ + + +LumieraConfigLookup +lumiera_config_lookup_init (LumieraConfigLookup self) +{ + TRACE (config_lookup); + psplay_init (&self->tree, cmp_fn, key_fn, delete_fn); + return self; +} + + +LumieraConfigLookup +lumiera_config_lookup_destroy (LumieraConfigLookup self) +{ + TRACE (config_lookup); + if (self) + psplay_destroy (&self->tree); + return self; +} + + +LumieraConfigLookupentry +lumiera_config_lookup_insert (LumieraConfigLookup self, LumieraConfigitem item) +{ + TRACE (config_lookup); + REQUIRE (self); + REQUIRE (item); + REQUIRE (item->key); + REQUIRE (item->key_size); + + FIXME ("implement section prefix/suffix for the key"); + const char* key = lumiera_tmpbuf_strcat3 (NULL, 0, item->key, item->key_size, NULL, 0); + + LumieraConfigLookupentry entry = (LumieraConfigLookupentry)psplay_find (&self->tree, key, 100); + if (!entry) + entry = (LumieraConfigLookupentry)psplay_insert (&self->tree, &lumiera_config_lookupentry_new (key)->node, 100); + + llist_insert_head (&entry->configitems, &item->lookup); + return entry; +} + + +LumieraConfigLookupentry +lumiera_config_lookup_insert_default (LumieraConfigLookup self, LumieraConfigitem item) +{ + TRACE (config_lookup); + REQUIRE (self); + REQUIRE (item); + REQUIRE (item->key); + REQUIRE (item->key_size); + + const char* key = lumiera_tmpbuf_snprintf (SIZE_MAX, "%.*s", item->key_size, item->key); + LumieraConfigLookupentry entry = (LumieraConfigLookupentry)psplay_find (&self->tree, key, 100); + if (!entry) + entry = (LumieraConfigLookupentry)psplay_insert (&self->tree, &lumiera_config_lookupentry_new (key)->node, 100); + TODO ("else check that no 'default' item already exists, that is, the tail element's parent points to the 'defaults' in config"); + + llist_insert_tail (&entry->configitems, &item->lookup); + return entry; +} + + +LumieraConfigitem +lumiera_config_lookup_remove (LumieraConfigLookup self, LumieraConfigitem item) +{ + TRACE (config_lookup); + REQUIRE (!llist_is_empty (&item->lookup), "item is not in a lookup"); + + if (llist_is_single (&item->lookup)) + { + /* last item in lookup, remove it from the splay tree */ + LumieraConfigLookupentry entry = LLIST_TO_STRUCTP (llist_next (&item->lookup), lumiera_config_lookupentry, configitems); + llist_unlink (&item->lookup); + psplay_delete_node (&self->tree, (PSplaynode)entry); + } + else + { + /* more than this item present in hash, just unlink this item */ + llist_unlink (&item->lookup); + } + + return item; +} + + +LumieraConfigLookupentry +lumiera_config_lookup_find (LumieraConfigLookup self, const char* key) +{ + TRACE (config_lookup); + return (LumieraConfigLookupentry)psplay_find (&self->tree, key, 100); +} + + +LumieraConfigitem +lumiera_config_lookup_item_find (LumieraConfigLookup self, const char* key) +{ + TRACE (config_lookup); + + LumieraConfigLookupentry entry = + lumiera_config_lookup_find (self, key); + + if (entry && !llist_is_empty (&entry->configitems)) + return LLIST_TO_STRUCTP (llist_head (&entry->configitems), lumiera_configitem, lookup); + + return NULL; +} + + +LumieraConfigitem +lumiera_config_lookup_item_tail_find (LumieraConfigLookup self, const char* key) +{ + TRACE (config_lookup); + + LumieraConfigLookupentry entry = + lumiera_config_lookup_find (self, key); + + if (entry && !llist_is_empty (&entry->configitems)) + return LLIST_TO_STRUCTP (llist_tail (&entry->configitems), lumiera_configitem, lookup); + + return NULL; +} + + + +/* + Lookup entries +*/ + +LumieraConfigLookupentry +lumiera_config_lookupentry_init (LumieraConfigLookupentry self, const char* key) +{ + TRACE (config_lookup, "key = %s", key); + if (self) + { + psplaynode_init (&self->node); + llist_init (&self->configitems); + self->full_key = lumiera_strndup (key, SIZE_MAX); + } + return self; +} + + +LumieraConfigLookupentry +lumiera_config_lookupentry_new (const char* key) +{ + return lumiera_config_lookupentry_init (lumiera_malloc (sizeof (lumiera_config_lookupentry)), key); +} + + +LumieraConfigLookupentry +lumiera_config_lookupentry_destroy (LumieraConfigLookupentry self) +{ + TRACE (config_lookup); + if (self) + { + REQUIRE (llist_is_empty (&self->configitems), "lookup node still in use"); + lumiera_free (self->full_key); + } + return self; +} + + +void +lumiera_config_lookupentry_delete (LumieraConfigLookupentry self) +{ + lumiera_free (lumiera_config_lookupentry_destroy (self)); +} + +static int +cmp_fn (const void* a, const void* b) +{ + return strcmp ((const char*)a, (const char*)b); +} + +static void +delete_fn (PSplaynode node) +{ + lumiera_config_lookupentry_delete ((LumieraConfigLookupentry) node); +} + + +static const void* +key_fn (const PSplaynode node) +{ + return ((LumieraConfigLookupentry) node)->full_key; +} + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/config_lookup.h b/src/backend/config_lookup.h new file mode 100644 index 000000000..a0ec20dd5 --- /dev/null +++ b/src/backend/config_lookup.h @@ -0,0 +1,192 @@ +/* + config_lookup.h - Lookup functions for the config subsystem + + 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_CONFIG_LOOKUP_H +#define LUMIERA_CONFIG_LOOKUP_H + +#include "lib/psplay.h" +#include "lib/llist.h" +#include "lib/error.h" + +typedef struct lumiera_config_lookup_struct lumiera_config_lookup; +typedef lumiera_config_lookup* LumieraConfigLookup; + +typedef struct lumiera_config_lookupentry_struct lumiera_config_lookupentry; +typedef lumiera_config_lookupentry* LumieraConfigLookupentry; + + +#include "backend/configitem.h" + + +#include + + +/** + * @file + * Lookup of configuration keys. Configuration keys are dynamically stored in a splay tree. + * This happens for defaults, loaded config files and entries which are set explicitly. + * The system maintains no central registry of all possible keys. + * We store here the full keys of configentries as well as the keys of section prefixes. + * Section prefixes are stored with a trailing dot to disambiguate them from entry keys. + */ + + +LUMIERA_ERROR_DECLARE (CONFIG_LOOKUP); + +/** + * Just contains a hashtable to give sufficent abstraction. + */ +struct lumiera_config_lookup_struct +{ + psplay tree; +}; + +/** + * Initialize a lookup structure. + * @param self lookup structure to be initialized + * @return self on success else NULL + */ +LumieraConfigLookup +lumiera_config_lookup_init (LumieraConfigLookup self); + +/** + * Destruct a lookup structure. + * @param self lookup structure to be destructed + * @return self + */ +LumieraConfigLookup +lumiera_config_lookup_destroy (LumieraConfigLookup self); + +/** + * Add a config item to a lookup structure. + * Config items are stored under their key and stacked in insertion order. + * @param self lookup structure where the item shall be added + * @param item config item to add to the lookup structure + * @return opaque pointer to a hashtable entry + */ +LumieraConfigLookupentry +lumiera_config_lookup_insert (LumieraConfigLookup self, LumieraConfigitem item); + + +/** + * Add a default config item to a lookup structure. + * @internal + * This function is used internal. + * The item must contain a full key and not part of any 'section' + * and is inserted as tail of the lookup list. + * @param self lookup structure where the item shall be added + * @param item config item to add to the lookup structure + * @return opaque pointer to a hashtable entry + */ +LumieraConfigLookupentry +lumiera_config_lookup_insert_default (LumieraConfigLookup self, LumieraConfigitem item); + + +/** + * Remove a config item from a lookup structure. + * Config must be removed from the lookup when they are not used anymore. + * Removing a config item unlinks it from the stack of all config items with the same key. + * When this was the last config item under that key, the lookup entry is cleaned up. + * @param self lookup structure where the item shall be removed + * @param item config item to be removed from the lookup + * @return item + */ +LumieraConfigitem +lumiera_config_lookup_remove (LumieraConfigLookup self, LumieraConfigitem item); + +/** + * Find a hashtable entry in the lookup structure. + * Internal function, can be used to check if at least one item is available for a given key. + * @param self lookup structure where the key shall be searched + * @param key string to be looked up + * @return NULL if nothing is found, otherwise a opaque pointer to a hash table entry + */ +LumieraConfigLookupentry +lumiera_config_lookup_find (LumieraConfigLookup self, const char* key); + +/** + * Find a the topmost config item stored to a given key. + * @param self lookup structure where the key shall be searched + * @param key string to be looked up + * @return the config item which was last stored under the given key or NULL when nothing was found + */ +LumieraConfigitem +lumiera_config_lookup_item_find (LumieraConfigLookup self, const char* key); + +/** + * Find a the bottommost config item stored to a given key. + * defaults sits at the bottom if exists + * @param self lookup structure where the key shall be searched + * @param key string to be looked up + * @return TODO + */ +LumieraConfigitem +lumiera_config_lookup_item_tail_find (LumieraConfigLookup self, const char* key); + + + +/* + Lookup hash entries for the cuckoo hash +*/ + +/** + * Structure defining single hash table entries. + * @internal + */ +struct lumiera_config_lookupentry_struct +{ + psplaynode node; + /* stack of all configitems stored under this key */ + llist configitems; + + /* + we store a copy of the full key here + configentry keys are complete as expected + section keys are the prefix stored with a trailing dot, + suffixes will be found by iterative search + */ + char* full_key; +}; + + +/* internal */ +LumieraConfigLookupentry +lumiera_config_lookupentry_init (LumieraConfigLookupentry self, const char* key); + +LumieraConfigLookupentry +lumiera_config_lookupentry_new (const char* key); + +/* internal */ +LumieraConfigLookupentry +lumiera_config_lookupentry_destroy (LumieraConfigLookupentry self); + + +void +lumiera_config_lookupentry_delete (LumieraConfigLookupentry self); + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/config_typed.c b/src/backend/config_typed.c new file mode 100644 index 000000000..07c2bb486 --- /dev/null +++ b/src/backend/config_typed.c @@ -0,0 +1,313 @@ +/* + config_typed.c - Lumiera configuration highlevel interface + + 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. +*/ + +//TODO: Support library includes// +#include "lib/safeclib.h" + + +//TODO: Lumiera header includes// +#include "backend/config.h" + +//TODO: internal/static forward declarations// +extern LumieraConfig lumiera_global_config; + + +//TODO: System includes// +#include + +/** + * @file + * Here are the high level typed configuration interfaces defined. + */ + +int +lumiera_config_link_get (const char* key, char** value) +{ + TRACE (config_typed); + UNIMPLEMENTED(); + return 0; +} + +int +lumiera_config_link_set (const char* key, char** value) +{ + TRACE (config_typed); + UNIMPLEMENTED(); + return 0; +} + + +/** + * Number + * signed integer numbers, in different formats (decimal, hex, oct, binary(for masks)) + */ +int +lumiera_config_number_get (const char* key, long long* value) +{ + TRACE (config_typed); + + int ret = -1; + + const char* raw_value = NULL; + + LUMIERA_RDLOCK_SECTION (config_typed, &lumiera_global_config->lock) + { + if (!lumiera_config_get (key, &raw_value)) + { + if (raw_value) + { + /* got it, scan it */ + if (sscanf (raw_value, "%Li", value) == 1) + ret = 0; /* all ok */ + else + { + LUMIERA_ERROR_SET (config_typed, CONFIG_SYNTAX_VALUE); + } + } + else + LUMIERA_ERROR_SET (config, CONFIG_NO_ENTRY); + } + } + + return ret; +} + +int +lumiera_config_number_set (const char* key, long long* value) +{ + TRACE (config_typed); + UNIMPLEMENTED(); + return 0; +} + + +/** + * Real + * floating point number in standard formats (see printf/scanf) + */ +int +lumiera_config_real_get (const char* key, long double* value) +{ + TRACE (config_typed); + UNIMPLEMENTED(); + return 0; +} + +int +lumiera_config_real_set (const char* key, long double* value) +{ + TRACE (config_typed); + UNIMPLEMENTED(); + return 0; +} + + + +/** + * String + * unquoted string which covers the whole value area and gets chopped or + * quoted string which preserves leading/trailing spaces + * either single or double quotes are allowed, doubling the quote in a string escapes it + */ + + +/** + * helper function, takes a raw input string and give a tmpbuf with the string parsed back. + */ +static char* +scan_string (const char* in) +{ + /* chop leading blanks */ + in += strspn(in, " \t"); + + char quote = *in; + char* end; + char* ret = NULL; + + if (quote == '"' || quote == '\'') + { + /* quoted string */ + ++in; + end = strchr (in, quote); + while (end && end[1] == quote) + end = strchr (end + 2, quote); + + if (end) + { + ret = lumiera_tmpbuf_strndup (in, end - in); + + /* replace double quote chars with single one */ + char* wpos; + char* rpos; + for (wpos = rpos = ret; *rpos; ++rpos, ++wpos) + { + if (*rpos == quote) + ++rpos; + *wpos = *rpos; + } + *wpos = '\0'; + } + else + /* quotes doesnt match */ + LUMIERA_ERROR_SET (config_typed, CONFIG_SYNTAX_VALUE); + } + else + { + /* unquoted string */ + ret = lumiera_tmpbuf_strndup (in, SIZE_MAX); + + /* chop trailing blanks */ + end = ret + strlen (ret) - 1; + while (end > ret && (*end == ' ' || *end == '\t')) + *end-- = '\0'; + } + + return ret; +} + +int +lumiera_config_string_get (const char* key, char** value) +{ + TRACE (config_typed); + + int ret = -1; + + const char* raw_value = NULL; + + LUMIERA_RDLOCK_SECTION (config_typed, &lumiera_global_config->lock) + { + if (!lumiera_config_get (key, &raw_value)) + { + if (raw_value) + { + *value = scan_string (raw_value); + if (*value) + ret = 0; /* all ok */ + /* else error was raised by scan_string */ + } + else + LUMIERA_ERROR_SET (config, CONFIG_NO_ENTRY); + } + } + + return ret; +} + +int +lumiera_config_string_set (const char* key, char** value) +{ + TRACE (config_typed); + UNIMPLEMENTED(); + return 0; +} + + + +/** + * Word + * A single word, no quotes, chopped + */ + +/** + * helper function, takes a raw input string and give a tmpbuf with the word parsed back. + */ +static char* +scan_word (const char* in) +{ + /* chop leading blanks */ + in += strspn(in, " \t"); + + char* ret = lumiera_tmpbuf_strndup (in, SIZE_MAX); + char* end = ret; + + /* chop trailing blanks */ + while (*end != ' ' && *end != '\t') + ++end; + + *end++ = '\0'; + + return ret; +} + +int +lumiera_config_word_get (const char* key, char** value) +{ + TRACE (config_typed, "KEY %s", key); + + int ret = -1; + + const char* raw_value = NULL; + + LUMIERA_RDLOCK_SECTION (config_typed, &lumiera_global_config->lock) + { + if (!lumiera_config_get (key, &raw_value)) + { + if (raw_value) + { + *value = scan_word (raw_value); + if (*value) + ret = 0; /* all ok */ + } + else + LUMIERA_ERROR_SET (config, CONFIG_NO_ENTRY); + } + } + + return ret; +} + +int +lumiera_config_word_set (const char* key, char** value) +{ + TRACE (config_typed); + UNIMPLEMENTED(); + return 0; +} + + +/** + * Bool + * Bool in various formats, (0,1(!1), yes/no, true/false, on/off, set/clear) + */ +int +lumiera_config_bool_get (const char* key, int* value) +{ + TRACE (config_typed); + UNIMPLEMENTED(); + return 0; +} + +int +lumiera_config_bool_set (const char* key, int* value) +{ + TRACE (config_typed); + UNIMPLEMENTED(); + return 0; +} + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/configentry.c b/src/backend/configentry.c new file mode 100644 index 000000000..e2c846b12 --- /dev/null +++ b/src/backend/configentry.c @@ -0,0 +1,74 @@ +/* + configentry.c - single entries from configfiles + + 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. +*/ + +//TODO: Support library includes// +#include "lib/safeclib.h" + +//TODO: Lumiera header includes// +#include "backend/configentry.h" + +//TODO: internal/static forward declarations// + + +//TODO: System includes// + + +/** + * @file + * + */ + +//code goes here// +LumieraConfigitem +lumiera_configentry_new (LumieraConfigitem tmp) +{ + LumieraConfigentry self = lumiera_malloc (sizeof (*self)); + lumiera_configitem_move ((LumieraConfigitem)self, tmp); + + TODO ("initialize other stuff here (lookup, parent, ...)"); + + return (LumieraConfigitem)self; +} + + +LumieraConfigitem +lumiera_configentry_destroy (LumieraConfigitem self) +{ + TODO ("cleanup other stuff here (lookup, parent, ...)"); + + return self; +} + +struct lumiera_configitem_vtable lumiera_configentry_funcs = + { + .new = lumiera_configentry_new, + .destroy = lumiera_configentry_destroy + }; + + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/locking.h b/src/backend/configentry.h similarity index 51% rename from src/lib/locking.h rename to src/backend/configentry.h index 3c4d497d8..61f5f5f73 100644 --- a/src/lib/locking.h +++ b/src/backend/configentry.h @@ -1,5 +1,5 @@ /* - locking.h - shared declarations for all locking primitives + configentry.h - single entries from configfiles Copyright (C) Lumiera.org 2008, Christian Thaeter @@ -19,36 +19,49 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifndef LUMIERA_LOCKING_H -#define LUMIERA_LOCKING_H +#ifndef LUMIERA_CONFIGENTRY_H +#define LUMIERA_CONFIGENTRY_H -#include -#include +//TODO: Support library includes// + + +//TODO: Forward declarations// +typedef struct lumiera_configentry_struct lumiera_configentry; +typedef lumiera_configentry* LumieraConfigentry; + + +//TODO: Lumiera header includes// +#include "backend/configitem.h" + +//TODO: System includes// #include -#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. */ -/** - * used to store the current lock state. - * - * - */ -enum lumiera_lockstate - { - LUMIERA_UNLOCKED, - LUMIERA_LOCKED, - LUMIERA_RDLOCKED, - LUMIERA_WRLOCKED - }; +//TODO: declarations go here// +struct lumiera_configentry_struct +{ + lumiera_configitem entry; +}; + +extern struct lumiera_configitem_vtable lumiera_configentry_funcs; + + +LumieraConfigitem +lumiera_configentry_new (LumieraConfigitem tmp); + + +LumieraConfigitem +lumiera_configentry_destroy (LumieraConfigitem self); #endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/configitem.c b/src/backend/configitem.c new file mode 100644 index 000000000..96012ad14 --- /dev/null +++ b/src/backend/configitem.c @@ -0,0 +1,392 @@ +/* + configitem.c - generalized hierachy of configuration items + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + Simeon Voelkel + + 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. +*/ + +//TODO: Support library includes// +#include "lib/llist.h" +#include "lib/safeclib.h" + + +//TODO: Lumiera header includes// +#include "backend/config.h" +#include "backend/configitem.h" +#include "backend/configentry.h" + +//TODO: internal/static forward declarations// + + +//TODO: System includes// +#include +#include + +/** + * @file + * + */ + + +//code goes here// + +LumieraConfigitem +lumiera_configitem_init (LumieraConfigitem self) +{ + TRACE (config_item); + REQUIRE (self); + + llist_init (&self->link); + self->parent = NULL; + llist_init (&self->childs); + + llist_init (&self->lookup); + + self->line = NULL; + + self->key = NULL; + self->key_size = 0; + self->delim = NULL; + self->vtable = NULL; + + return self; +} + +LumieraConfigitem +lumiera_configitem_destroy (LumieraConfigitem self, LumieraConfigLookup lookup) +{ + TRACE (config_item); + + if (self) + { + LLIST_WHILE_HEAD (&self->childs, node) + lumiera_configitem_delete ((LumieraConfigitem) node, lookup); + + ENSURE (llist_is_empty (&self->childs), "destructor didn't remove childs"); + + if (self->vtable && self->vtable->destroy) + self->vtable->destroy (self); + + if (!llist_is_empty (&self->lookup)) + lumiera_config_lookup_remove (lookup, self); + + llist_unlink (&self->link); + lumiera_free (self->line); + } + + return self; +} + + +LumieraConfigitem +lumiera_configitem_new (const char* line) +{ + TRACE (config_item); + + lumiera_configitem tmp; + lumiera_configitem_init (&tmp); + + lumiera_configitem_parse (&tmp, line); + + LumieraConfigitem self = tmp.vtable && tmp.vtable->new + ? tmp.vtable->new (&tmp) + : lumiera_configitem_move (lumiera_malloc (sizeof (*self)), &tmp); + + return self; +} + + +void +lumiera_configitem_delete (LumieraConfigitem self, LumieraConfigLookup lookup) +{ + TRACE (config_item); + lumiera_free (lumiera_configitem_destroy (self, lookup)); +} + + + +LumieraConfigitem +lumiera_configitem_move (LumieraConfigitem self, LumieraConfigitem source) +{ + TRACE (config_item); + REQUIRE (self); + REQUIRE (source); + + llist_init (&self->link); + llist_insertlist_next (&self->link, &source->link); + + self->parent = source->parent; + + llist_init (&self->childs); + llist_insertlist_next (&self->childs, &source->childs); + + llist_init (&self->lookup); + llist_insertlist_next (&self->lookup, &source->lookup); + + self->line = source->line; + source->line = NULL; + + self->key = source->key; + self->key_size = source->key_size; + self->delim = source->delim; + self->vtable = source->vtable; + + return self; +} + + +LumieraConfigitem +lumiera_configitem_parse (LumieraConfigitem self, const char* line) +{ + TRACE (config_item); + + self->line = lumiera_strndup (line, SIZE_MAX); + + FIXME ("MOCKUP START"); + + TODO ("parsing here"); + /* + HOWTO parse (for simav) + in self->line liegt jetzt der 'rohe' string + + parsen setzt folgende werte in self: .key, .key_size, .delim und vtable. den rest macht dann die 'new' funktion aus der vtable + + es geht jetzt da drum rauszufinden ob diese zeile einses der folgenden sachen ist: + (ich zeig hier nur die grundsyntax, das parsen sollte auch entartete situationen behandeln, insbesondere leerzeichen/tabulatoren an allen moeglichen stellen) + auserdem sollt hier alles soweit wie moeglich validiert werden z.b. keys auf erlaubte zeichen gescheckt (siehe die _tr function) + + section: + '[prefix suffix]' + .key == prefix + .delim == das leerzeichen (oder tab) vor suffix oder aufs abschliessende ] wenn kein suffix + + kommentar: + leere zeile, zeile nur aus leerzeichen und tabulatoren, leerzeichen und tabulatoren gefolgt von # bis zum zeilenende + alles ausser vtable auf NULL + + direktive: + '@direktive argumente' + .key == @ + .delim == leerzeichen oder tab vor argumente, NULL wenn keine argumente + + configentry: + 'key = value' + .key == key begin + .delim == '=' + 'key < redirect' + .key == key begin + .delim == '>' + + */ + /* + * What should be working (for cehteh) or not yet.. + * + * die Elemente sollten bereits richtig unterschieden werden, die {} sind noch zu füllen. + * + * */ + + char* itr = self->line; + + /*skip leading whitespaces*/ + while (*itr && isspace (*itr)) + itr++; + + /*decide what this line represents*/ + if (!*itr || *itr == '#' ) + { + /*this is an empty line or a a comment*/ + } + else if (*itr == '@' ) + { + /*this is a directive*/ + + /*itr points now to @*/ + self->key = itr; + + /*check whether there are illegal whitespaces after @*/ + itr++; + if (*itr && !isspace(*itr)) + { + /*now look for the end of the directive and set the keysize*/ + self->key_size = strspn (itr, LUMIERA_CONFIG_KEY_CHARS); + + itr += self->key_size; + + /*we need a key with a length greather than zero and + * either the end of the line + * or a whitespace after the key */ + + if ( self->key_size && ( !*itr || (*itr && isspace(*itr)) )) + { + /*look for given arguments*/ + + /*skip blanks*/ + while (*itr && isspace (*itr)) + itr++; + + if (*itr) + { + /*there are arguments given, thus set delim*/ + self->delim = itr - 1; + } + else + { + /*no arguments were given*/ + self->delim = NULL; + } + } + else + { + /*malformed lines shall be treated like if they were comments*/ + self->key = NULL; + self->key_size = 0; + + LUMIERA_ERROR_SET (config_item, CONFIG_SYNTAX); + } + } + else + { + /*there occurred already an error right after the @!*/ + /*malformed lines shall be treated like if they were comments*/ + self->key = NULL; + self->key_size = 0; + + LUMIERA_ERROR_SET (config_item, CONFIG_SYNTAX); + } + } + else if (*itr == '[' ) + { + /*this is a section*/ + + /*skip blanks before prefix*/ + itr++; + while (*itr && isspace(*itr)) + itr++; + + /*itr points now to the begin of the key*/ + self->key = itr; + + /*now look for the end of the key and set the keysize*/ + self->key_size = strspn (itr, LUMIERA_CONFIG_KEY_CHARS); + + itr += self->key_size; + + /*if the line ends ends with prefix] delim points to ] + * and not the last (blank) character before the final square bracket*/ + if (self->key_size && *itr && *itr == ']') + { + self->delim = itr; + TODO("self->vtable = &lumiera_configsection_funcs;"); + } + else if (self->key_size && *itr && isspace(*itr)) + { + /* skip blanks until we reach the suffix or the final square bracket*/ + while (*itr && isspace(*itr)) + itr++; + + if (*itr && *itr == ']') + { + /*final square bracket reached, so place delim one char before the + * actual position which must be a whitespace: no extra check necessary*/ + self->delim = itr - 1; + TODO("self->vtable = &lumiera_configsection_funcs;"); + } + else if (*itr) + { + TODO("check wheter suffix is made of legal characters"); + + /*delim points to the last whitespace before the actual position; + * no extra check needed*/ + self->delim = itr - 1; + TODO("self->vtable = &lumiera_configsection_funcs;"); + } + else + { + /*malformed section line, treat this line like a comment*/ + self->key = NULL; + self->key_size = 0; + + LUMIERA_ERROR_SET (config_item, CONFIG_SYNTAX); + + } + } + else + { + /*error: either *itr is false, points neither to a blank nor to a closed square + * bracket or the key_size is zero*/ + + /*treat this line like a comment*/ + self->key = NULL; + self->key_size = 0; + + LUMIERA_ERROR_SET (config_item, CONFIG_SYNTAX); + + } + } + else + { + /*this is probably a configentry*/ + + /*itr points now to the first not-whitespace-character*/ + self->key = itr; + + /*now look for the end of the key and set the keysize*/ + self->key_size = strspn (itr, LUMIERA_CONFIG_KEY_CHARS); + + /* skip blanks */ + itr += self->key_size; + while (*itr && isspace (*itr)) + itr++; + + if (self->key_size && *itr == '=') + { + /*this configentry assigns a value to a key*/ + self->delim = itr; + self->vtable = &lumiera_configentry_funcs; + } + else if (self->key_size && *itr == '<') + { + /*this configentry is a redirect*/ + self->delim = itr; + self->vtable = &lumiera_configentry_funcs; + } + else + { + /*this is not a valid configentry; treat this line like a comment*/ + self->key = NULL; + self->key_size = 0; + + LUMIERA_ERROR_SET (config_item, CONFIG_SYNTAX); + } + + + } + + + + return self; +} + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/configitem.h b/src/backend/configitem.h new file mode 100644 index 000000000..c2395b6dc --- /dev/null +++ b/src/backend/configitem.h @@ -0,0 +1,137 @@ +/* + configitem.h - generalized hierachy of configuration items + + 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_CONFIGITEM_H +#define LUMIERA_CONFIGITEM_H + +//TODO: Support library includes// +#include "lib/llist.h" + + +//TODO: Forward declarations// +typedef struct lumiera_configitem_struct lumiera_configitem; +typedef lumiera_configitem* LumieraConfigitem; + +struct lumiera_configitem_vtable; + +//TODO: Lumiera header includes// +#include "backend/config_lookup.h" + + +//TODO: System includes// +#include + + +/** + * @file + * configitems build a 3 level hierachy: + * + * 1. file: + * contain sections + * + * 2. section: + * [prefix suffix] + * contain lines + * + * 3. lines are + * comment: + * empty line or line only containing spaces and tabs + * line starting with spaces and tabs followed by a # + * directive: + * '@include name' or '@readonly' + * directives are only valid at the toplevel section [] + * configurationentry: + * 'key = value' or 'key < redirect' + */ + + +//TODO: declarations go here// +/** + * @file + * configitems build a 3 level hierachy: + * + * 1. file: + * contain sections + * + * 2. section: + * [prefix suffix] + * contain lines + * + * 3. lines are + * comment: + * empty line or line only containing spaces and tabs + * line starting with spaces and tabs followed by a # + * directive: + * '@include name' or '@readonly' + * directives are only valid at the toplevel section [] + * configurationentry: + * 'key = value' or 'key < redirect' + * errorneous: + * any line which cant be parsed + */ + +struct lumiera_configitem_vtable +{ + LumieraConfigitem (*new)(LumieraConfigitem); + LumieraConfigitem (*destroy)(LumieraConfigitem); +}; + +struct lumiera_configitem_struct +{ + llist link; // all lines on the same hierachy level are linked here (see childs) + LumieraConfigitem parent; // parent section + llist childs; // root node for all lines below this hierachy + + llist lookup; // all lines with the same key are stacked up on the loockup + + char* line; // raw line as read in allocated here trailing \n will be replaced with \0 + char* key; // pointer into line to start of key + size_t key_size; + char* delim; // delimiter, value starts at delim+1 + struct lumiera_configitem_vtable* vtable; // functiontable for subclassing +}; + +LumieraConfigitem +lumiera_configitem_init (LumieraConfigitem self); + +LumieraConfigitem +lumiera_configitem_destroy (LumieraConfigitem self, LumieraConfigLookup lookup); + +LumieraConfigitem +lumiera_configitem_new (const char* line); + +void +lumiera_configitem_delete (LumieraConfigitem self, LumieraConfigLookup lookup); + +LumieraConfigitem +lumiera_configitem_parse (LumieraConfigitem self, const char* line); + +LumieraConfigitem +lumiera_configitem_move (LumieraConfigitem self, LumieraConfigitem dest); + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/file.c b/src/backend/file.c index 9af608567..9739e3a58 100644 --- a/src/backend/file.c +++ b/src/backend/file.c @@ -50,7 +50,7 @@ lumiera_file_destroy (LumieraFile self) { TRACE (file); lumiera_filedescriptor_release (self->descriptor); - free ((void*)self->name); + lumiera_free (self->name); return self; } @@ -67,7 +67,7 @@ void lumiera_file_delete (LumieraFile self) { TRACE (file); - free (lumiera_file_destroy (self)); + lumiera_free (lumiera_file_destroy (self)); } @@ -79,7 +79,7 @@ lumiera_file_handle_acquire (LumieraFile self) REQUIRE (self->descriptor); REQUIRE (lumiera_fhcache); - LUMIERA_MUTEX_SECTION (file, self->descriptor->rh, &self->descriptor->lock) + LUMIERA_MUTEX_SECTION (file, &self->descriptor->lock) { if (!self->descriptor->handle) /* no handle yet, get a new one */ @@ -123,7 +123,7 @@ lumiera_file_handle_release (LumieraFile self) { TRACE (file); - LUMIERA_MUTEX_SECTION (file, self->descriptor->rh, &self->descriptor->lock) + LUMIERA_MUTEX_SECTION (file, &self->descriptor->lock) { lumiera_filehandlecache_checkin (lumiera_fhcache, self->descriptor->handle); } diff --git a/src/backend/file.h b/src/backend/file.h index bba12a167..bf5f11181 100644 --- a/src/backend/file.h +++ b/src/backend/file.h @@ -60,7 +60,7 @@ typedef lumiera_file* LumieraFile; struct lumiera_file_struct { - const char* name; + char* name; LumieraFiledescriptor descriptor; }; diff --git a/src/backend/filedescriptor.c b/src/backend/filedescriptor.c index b9608984f..4daa04f3e 100644 --- a/src/backend/filedescriptor.c +++ b/src/backend/filedescriptor.c @@ -21,7 +21,6 @@ #include "lib/mutex.h" #include "lib/safeclib.h" -#include "lib/cuckoo.h" #include "backend/file.h" #include "backend/filedescriptor.h" @@ -32,6 +31,7 @@ #include #include #include +#include NOBUG_DEFINE_FLAG_PARENT (filedescriptor, file_all); @@ -41,46 +41,50 @@ NOBUG_DEFINE_FLAG_PARENT (filedescriptor, file_all); This registry stores all acquired filedescriptors for lookup, they will be freed when not referenced anymore. */ -static Cuckoo registry = NULL; +static PSplay registry = NULL; static lumiera_mutex registry_mutex = {PTHREAD_MUTEX_INITIALIZER}; -/* - * setup hashing and compare functions for cuckoo hashing - */ -static size_t -h1 (const void* item, const uint32_t r) -{ - const LumieraFiledescriptor i = *(const LumieraFiledescriptor*)item; - return i->stat.st_dev^i->stat.st_ino^(i->flags&LUMIERA_FILE_MASK) - ^((i->stat.st_dev^i->stat.st_ino^(i->flags&LUMIERA_FILE_MASK))>>7)^r; -} - -static size_t -h2 (const void* item, const uint32_t r) -{ - const LumieraFiledescriptor i = *(const LumieraFiledescriptor*)item; - return i->stat.st_dev^i->stat.st_ino^(i->flags&LUMIERA_FILE_MASK) - ^((i->stat.st_dev^i->stat.st_ino^(i->flags&LUMIERA_FILE_MASK))>>5)^r; -} - -static size_t -h3 (const void* item, const uint32_t r) -{ - const LumieraFiledescriptor i = *(const LumieraFiledescriptor*)item; - return i->stat.st_dev^i->stat.st_ino^(i->flags&LUMIERA_FILE_MASK) - ^((i->stat.st_dev^i->stat.st_ino^(i->flags&LUMIERA_FILE_MASK))>>3)^r; -} - static int -cmp (const void* keya, const void* keyb) +cmp_fn (const void* keya, const void* keyb) { - const LumieraFiledescriptor a = *(const LumieraFiledescriptor*)keya; - const LumieraFiledescriptor b = *(const LumieraFiledescriptor*)keyb; - return a->stat.st_dev == b->stat.st_dev && a->stat.st_ino == b->stat.st_ino - && (a->flags&LUMIERA_FILE_MASK) == (b->flags&LUMIERA_FILE_MASK); + const LumieraFiledescriptor a = (const LumieraFiledescriptor)keya; + const LumieraFiledescriptor b = (const LumieraFiledescriptor)keyb; + + if (a->stat.st_dev < b->stat.st_dev) + return -1; + else if (a->stat.st_dev > b->stat.st_dev) + return 1; + + if (a->stat.st_ino < b->stat.st_ino) + return -1; + else if (a->stat.st_ino > b->stat.st_ino) + return 1; + + if ((a->flags&LUMIERA_FILE_MASK) < (b->flags&LUMIERA_FILE_MASK)) + return -1; + else if ((a->flags&LUMIERA_FILE_MASK) > (b->flags&LUMIERA_FILE_MASK)) + return 1; + + return 0; } + +static void +delete_fn (PSplaynode node) +{ + lumiera_filedescriptor_delete ((LumieraFiledescriptor) node); +} + + +static const void* +key_fn (const PSplaynode node) +{ + return node; +} + + + void lumiera_filedescriptor_registry_init (void) { @@ -88,20 +92,24 @@ lumiera_filedescriptor_registry_init (void) TRACE (filedescriptor); REQUIRE (!registry); - registry = cuckoo_new (h1, h2, h3, cmp, - sizeof (LumieraFiledescriptor), - 3); + registry = psplay_new (cmp_fn, key_fn, delete_fn); if (!registry) LUMIERA_DIE (NO_MEMORY); + + RESOURCE_HANDLE_INIT (registry_mutex.rh); + RESOURCE_ANNOUNCE (filedescriptor, "mutex", "filedescriptor registry", ®istry, registry_mutex.rh); } void lumiera_filedescriptor_registry_destroy (void) { TRACE (filedescriptor); - REQUIRE (!cuckoo_nelements (registry)); + REQUIRE (!psplay_nelements (registry)); + + RESOURCE_FORGET (filedescriptor, registry_mutex.rh); + if (registry) - cuckoo_free (registry); + psplay_destroy (registry); registry = NULL; } @@ -112,82 +120,77 @@ lumiera_filedescriptor_acquire (const char* name, int flags) TRACE (filedescriptor, "%s", name); REQUIRE (registry, "not initialized"); - lumiera_mutexacquirer registry_lock; - lumiera_mutexacquirer_init_mutex (®istry_lock, ®istry_mutex, LUMIERA_LOCKED); + LumieraFiledescriptor dest = NULL; - lumiera_filedescriptor fdesc; - fdesc.flags = flags; - - if (stat (name, &fdesc.stat) != 0) + LUMIERA_MUTEX_SECTION (filedescriptor, ®istry_mutex) { - if (errno == ENOENT && flags&O_CREAT) + lumiera_filedescriptor fdesc; + fdesc.flags = flags; + + if (stat (name, &fdesc.stat) != 0) { - char* dir = lumiera_tmpbuf_strndup (name, PATH_MAX); - char* slash = dir; - while ((slash = strchr (slash+1, '/'))) + if (errno == ENOENT && flags&O_CREAT) { - *slash = '\0'; - INFO (filedescriptor, "try creating dir: %s", dir); - if (mkdir (dir, 0777) == -1 && errno != EEXIST) + char* dir = lumiera_tmpbuf_strndup (name, PATH_MAX); + char* slash = dir; + while ((slash = strchr (slash+1, '/'))) + { + *slash = '\0'; + INFO (filedescriptor, "try creating dir: %s", dir); + if (mkdir (dir, 0777) == -1 && errno != EEXIST) + { + LUMIERA_ERROR_SET (filedescriptor, ERRNO); + goto error; + } + *slash = '/'; + } + int fd; + INFO (filedescriptor, "try creating file: %s", name); + fd = creat (name, 0777); + if (fd == -1) { LUMIERA_ERROR_SET (filedescriptor, ERRNO); - goto efile; + goto error; + } + close (fd); + if (stat (name, &fdesc.stat) != 0) + { + /* finally, no luck */ + LUMIERA_ERROR_SET (filedescriptor, ERRNO); + goto error; } - *slash = '/'; - } - int fd; - INFO (filedescriptor, "try creating file: %s", name); - fd = creat (name, 0777); - if (fd == -1) - { - LUMIERA_ERROR_SET (filedescriptor, ERRNO); - goto efile; - } - close (fd); - if (stat (name, &fdesc.stat) != 0) - { - /* finally, no luck */ - LUMIERA_ERROR_SET (filedescriptor, ERRNO); - goto efile; } } - } - /* lookup/create descriptor */ - LumieraFiledescriptor dest = &fdesc; - LumieraFiledescriptor* found = cuckoo_find (registry, &dest); + /* lookup/create descriptor */ + dest = (LumieraFiledescriptor) psplay_find (registry, &fdesc, 100); - if (!found) - { - TRACE (filedescriptor, "Descriptor not found"); - - dest = lumiera_filedescriptor_new (&fdesc); if (!dest) - goto ecreate; + { + TRACE (filedescriptor, "Descriptor not found"); - cuckoo_insert (registry, &dest); - } - else - { - TRACE (filedescriptor, "Descriptor already existing"); - dest = *found; - ++dest->refcount; + dest = lumiera_filedescriptor_new (&fdesc); + if (!dest) + goto error; + + psplay_insert (registry, &dest->node, 100); + } + else + { + TRACE (filedescriptor, "Descriptor already existing"); + ++dest->refcount; + } + error: ; } - lumiera_mutexacquirer_unlock (®istry_lock); return dest; - - efile: - ecreate: - lumiera_mutexacquirer_unlock (®istry_lock); - return NULL; } void lumiera_filedescriptor_release (LumieraFiledescriptor self) { - TRACE (filedescriptor); + TRACE (filedescriptor, "%p", self); if (!--self->refcount) lumiera_filedescriptor_delete (self); } @@ -199,17 +202,14 @@ lumiera_filedescriptor_new (LumieraFiledescriptor template) LumieraFiledescriptor self = lumiera_malloc (sizeof (lumiera_filedescriptor)); TRACE (filedescriptor, "at %p", self); + psplaynode_init (&self->node); self->stat = template->stat; self->flags = template->flags; - lumiera_mutex_init (&self->lock); self->refcount = 1; self->handle = 0; - const char* type = "mutex"; - const char* name = "filedescriptor"; - - RESOURCE_ANNOUNCE (filedescriptor, type, name, self, self->rh); + lumiera_mutex_init (&self->lock, "filedescriptor", &NOBUG_FLAG (filedescriptor)); return self; } @@ -219,22 +219,19 @@ void lumiera_filedescriptor_delete (LumieraFiledescriptor self) { TRACE (filedescriptor, "%p", self); - lumiera_mutexacquirer registry_lock; - lumiera_mutexacquirer_init_mutex (®istry_lock, ®istry_mutex, LUMIERA_LOCKED); - REQUIRE (self->refcount == 0); + LUMIERA_MUTEX_SECTION (filedescriptor, ®istry_mutex) + { + REQUIRE (self->refcount == 0); - RESOURCE_FORGET (filedescriptor, self->rh); + psplay_remove (registry, &self->node); - cuckoo_remove (registry, cuckoo_find (registry, &self)); - - TODO ("destruct other members (WIP)"); + TODO ("destruct other members (WIP)"); - TODO ("release filehandle"); + TODO ("release filehandle"); - lumiera_mutex_destroy (&self->lock); - free (self); - - lumiera_mutexacquirer_unlock (®istry_lock); + lumiera_mutex_destroy (&self->lock, &NOBUG_FLAG (filedescriptor)); + lumiera_free (self); + } } diff --git a/src/backend/filedescriptor.h b/src/backend/filedescriptor.h index 528be41e8..d91296956 100644 --- a/src/backend/filedescriptor.h +++ b/src/backend/filedescriptor.h @@ -23,6 +23,7 @@ #define LUMIERA_FILEDESCRIPTOR_H #include "lib/mutex.h" +#include "lib/psplay.h" #include @@ -47,7 +48,8 @@ typedef lumiera_filedescriptor* LumieraFiledescriptor; struct lumiera_filedescriptor_struct { - struct stat stat; /* create after first open, maintained metadata, MUST BE FIRST! */ + psplaynode node; /* node for the lookup tree */ + struct stat stat; /* create after first open, maintained metadata */ int flags; /* open flags, must be masked for reopen */ lumiera_mutex lock; /* locks operations on this file descriptor */ unsigned refcount; /* reference counter, all users sans registry */ @@ -55,8 +57,6 @@ struct lumiera_filedescriptor_struct LumieraFilehandle handle; //LumieraFileMap mappings; //LumieraWriteBuffer writebuffer; - - RESOURCE_HANDLE (rh); }; /** diff --git a/src/backend/filehandlecache.c b/src/backend/filehandlecache.c index 181807661..cf8d0a0e5 100644 --- a/src/backend/filehandlecache.c +++ b/src/backend/filehandlecache.c @@ -44,8 +44,7 @@ lumiera_filehandlecache_new (int max_entries) lumiera_mrucache_init (&lumiera_fhcache->cache, lumiera_filehandle_destroy_node); lumiera_fhcache->available = max_entries; lumiera_fhcache->checked_out = 0; - lumiera_mutex_init (&lumiera_fhcache->lock); - RESOURCE_ANNOUNCE (filehandlecache, "mutex", "filehandlecache", lumiera_fhcache, lumiera_fhcache->rh); + lumiera_mutex_init (&lumiera_fhcache->lock, "filehandlecache", &NOBUG_FLAG (filehandlecache)); } @@ -55,10 +54,9 @@ lumiera_filehandlecache_delete (void) if (lumiera_fhcache) { REQUIRE (!lumiera_fhcache->checked_out, "Filehandles in use at shutdown"); - RESOURCE_FORGET (filehandlecache, lumiera_fhcache->rh); lumiera_mrucache_destroy (&lumiera_fhcache->cache); - lumiera_mutex_destroy (&lumiera_fhcache->lock); - free (lumiera_fhcache); + lumiera_mutex_destroy (&lumiera_fhcache->lock, &NOBUG_FLAG (filehandlecache)); + lumiera_free (lumiera_fhcache); lumiera_fhcache = NULL; } } @@ -69,7 +67,8 @@ lumiera_filehandlecache_handle_acquire (LumieraFilehandlecache self, LumieraFile { TRACE (filehandlecache); LumieraFilehandle ret = NULL; - LUMIERA_MUTEX_SECTION (filehandlecache, self->rh, &self->lock) + + LUMIERA_MUTEX_SECTION (filehandlecache, &self->lock) { if (self->available <= 0 && self->cache.cached) { @@ -106,7 +105,7 @@ lumiera_filehandlecache_checkout (LumieraFilehandlecache self, LumieraFilehandle if (!handle->use_cnt) { /* lock cache and checkout */ - LUMIERA_MUTEX_SECTION (filehandlecache, self->rh, &self->lock) + LUMIERA_MUTEX_SECTION (filehandlecache, &self->lock) { lumiera_mrucache_checkout (&self->cache, &handle->cachenode); } @@ -128,7 +127,7 @@ lumiera_filehandlecache_checkin (LumieraFilehandlecache self, LumieraFilehandle if (!--handle->use_cnt) { /* lock cache and checin */ - LUMIERA_MUTEX_SECTION (filehandlecache, self->rh, &self->lock) + LUMIERA_MUTEX_SECTION (filehandlecache, &self->lock) { --self->checked_out; lumiera_mrucache_checkin (&self->cache, &handle->cachenode); diff --git a/src/backend/filehandlecache.h b/src/backend/filehandlecache.h index 37c2a416b..f624caefe 100644 --- a/src/backend/filehandlecache.h +++ b/src/backend/filehandlecache.h @@ -49,7 +49,6 @@ struct lumiera_filehandlecache_struct int available; int checked_out; lumiera_mutex lock; - RESOURCE_HANDLE (rh); }; extern LumieraFilehandlecache lumiera_fhcache; diff --git a/src/common/cmdline.cpp b/src/common/cmdline.cpp index dec1a0620..eca0b18bb 100644 --- a/src/common/cmdline.cpp +++ b/src/common/cmdline.cpp @@ -52,7 +52,7 @@ namespace util /** create as a tokenized copy of the current commandline. * Note that argv[0] is allways ignored. */ - Cmdline::Cmdline (int argc, char* argv[]) + Cmdline::Cmdline (int argc, const char** argv) : vector (noneg(argc-1)) { for (int i=1; i (&cause); if (err) - if (isnil (err->cause_)) - return cause.what(); // cause is root cause - else - return err->cause_; // cause was caused by another exception + { + if (isnil (err->cause_)) + return cause.what(); // cause is root cause + else + return err->cause_; // cause was caused by another exception + } // unknown other exception type return cause.what (); diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index f7d9058ae..e7c22abdb 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -26,27 +26,26 @@ liblumi_a_SOURCES = \ $(liblumi_a_srcdir)/mutex.c \ $(liblumi_a_srcdir)/rwlock.c \ $(liblumi_a_srcdir)/condition.c \ - $(liblumi_a_srcdir)/references.c \ - $(liblumi_a_srcdir)/uuid.c \ + $(liblumi_a_srcdir)/luid.c \ $(liblumi_a_srcdir)/safeclib.c \ $(liblumi_a_srcdir)/cuckoo.c \ + $(liblumi_a_srcdir)/psplay.c \ $(liblumi_a_srcdir)/mrucache.c \ - $(liblumi_a_srcdir)/time.c \ + $(liblumi_a_srcdir)/time.c \ $(liblumi_a_srcdir)/appconfig.cpp noinst_HEADERS += \ $(liblumi_a_srcdir)/plugin.h \ $(liblumi_a_srcdir)/error.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)/uuid.h \ + $(liblumi_a_srcdir)/luid.h \ $(liblumi_a_srcdir)/safeclib.h \ $(liblumi_a_srcdir)/cuckoo.h \ + $(liblumi_a_srcdir)/psplay.h \ $(liblumi_a_srcdir)/mrucache.h \ - $(liblumi_a_srcdir)/time.h \ + $(liblumi_a_srcdir)/time.h \ $(liblumi_a_srcdir)/appconfig.hpp \ $(liblumi_a_srcdir)/lifecycleregistry.hpp diff --git a/src/lib/condition.c b/src/lib/condition.c index 60e8b6f10..a7f17e57b 100644 --- a/src/lib/condition.c +++ b/src/lib/condition.c @@ -28,36 +28,32 @@ LUMIERA_ERROR_DEFINE (CONDITION_DESTROY, "condition destroy failed"); -/** - * Initialize a condition variable - * @param self is a pointer to the condition variable to be initialized - * @return self as given - */ + LumieraCondition -lumiera_condition_init (LumieraCondition self) +lumiera_condition_init (LumieraCondition self, const char* purpose, struct nobug_flag* flag) { if (self) { pthread_cond_init (&self->cond, NULL); pthread_mutex_init (&self->mutex, NULL); + NOBUG_RESOURCE_HANDLE_INIT (self->rh); + NOBUG_RESOURCE_ANNOUNCE_RAW (flag, "cond_var", purpose, self, self->rh); } return self; } -/** - * Destroy a condition variable - * @param self is a pointer to the condition variable to be destroyed - * @return self as given - */ LumieraCondition -lumiera_condition_destroy (LumieraCondition self) +lumiera_condition_destroy (LumieraCondition self, struct nobug_flag* flag) { if (self) { + NOBUG_RESOURCE_FORGET_RAW (flag, self->rh); + if (pthread_mutex_destroy (&self->mutex)) LUMIERA_DIE (MUTEX_DESTROY); - else if (pthread_cond_destroy (&self->cond)) + + if (pthread_cond_destroy (&self->cond)) LUMIERA_DIE (CONDITION_DESTROY); } return self; diff --git a/src/lib/condition.h b/src/lib/condition.h index dd92f326b..3bdbcbedc 100644 --- a/src/lib/condition.h +++ b/src/lib/condition.h @@ -22,7 +22,9 @@ #ifndef LUMIERA_CONDITION_H #define LUMIERA_CONDITION_H -#include "lib/locking.h" +#include "lib/error.h" +#include "lib/mutex.h" + /** * @file @@ -31,6 +33,49 @@ LUMIERA_ERROR_DECLARE (CONDITION_DESTROY); + + +/** + * Condition section. + * Locks the condition mutex, one can use LUMIERA_CONDITION_WAIT to wait for signals or + * LUMIERA_CONDITION_SIGNAL or LUMIERA_CONDITION_BROADCAST to wake waiting threads + */ +#define LUMIERA_CONDITION_SECTION(nobugflag, cnd) \ + for (lumiera_conditionacquirer NOBUG_CLEANUP(lumiera_conditionacquirer_ensureunlocked) \ + lumiera_condition_section_ = {(LumieraCondition)1}; \ + lumiera_condition_section_.condition;) \ + for ( \ + ({ \ + lumiera_condition_section_.condition = (cnd); \ + NOBUG_IF(NOBUG_MODE_ALPHA, lumiera_condition_section_.flag = &NOBUG_FLAG(nobugflag)); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_condition_section_.rh); \ + RESOURCE_ENTER (nobugflag, (cnd)->rh, "acquire condition", &lumiera_condition_section_, \ + NOBUG_RESOURCE_EXCLUSIVE, lumiera_condition_section_.rh); \ + if (pthread_mutex_lock (&(cnd)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + }); \ + lumiera_condition_section_.condition; \ + ({ \ + if (lumiera_condition_section_.condition) \ + { \ + pthread_mutex_unlock (&lumiera_condition_section_.condition->mutex); \ + lumiera_condition_section_.condition = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_condition_section_.rh); \ + } \ + })) + +#define LUMIERA_CONDITION_WAIT \ + do { \ + NOBUG_RESOURCE_STATE_RAW (lumiera_condition_section_.flag, lumiera_condition_section_.rh, NOBUG_RESOURCEING); \ + pthread_cond_wait (&lumiera_condition_section_.condition->cond, &lumiera_condition_section_.condition->mutex); \ + NOBUG_RESOURCE_STATE_RAW (lumiera_condition_section_.flag, lumiera_condition_section_.rh, NOBUG_RESOURCE_EXCLUSIVE); \ + while (0) + +#define LUMIERA_CONDITION_SIGNAL (nobugflag) pthread_cond_signal (&lumiera_condition_section_.condition->cond) + +#define LUMIERA_CONDITION_BROADCAST (nobugflag) pthread_cond_broadcast (&lumiera_condition_section_.condition->cond) + + + /** * Condition variables. * @@ -39,17 +84,28 @@ struct lumiera_condition_struct { pthread_cond_t cond; pthread_mutex_t mutex; + RESOURCE_HANDLE (rh); }; typedef struct lumiera_condition_struct lumiera_condition; typedef lumiera_condition* LumieraCondition; +/** + * Initialize a condition variable + * @param self is a pointer to the condition variable to be initialized + * @return self as given + */ LumieraCondition -lumiera_condition_init (LumieraCondition self); +lumiera_condition_init (LumieraCondition self, const char* purpose, struct nobug_flag* flag); +/** + * Destroy a condition variable + * @param self is a pointer to the condition variable to be destroyed + * @return self as given + */ LumieraCondition -lumiera_condition_destroy (LumieraCondition self); +lumiera_condition_destroy (LumieraCondition self, struct nobug_flag* flag); /** @@ -91,8 +147,9 @@ lumiera_condition_broadcast (LumieraCondition self) */ struct lumiera_conditionacquirer_struct { - LumieraCondition cond; - enum lumiera_lockstate state; + LumieraCondition condition; + NOBUG_IF(NOBUG_MODE_ALPHA, struct nobug_flag* flag); + RESOURCE_HANDLE (rh); }; typedef struct lumiera_conditionacquirer_struct lumiera_conditionacquirer; typedef struct lumiera_conditionacquirer_struct* LumieraConditionacquirer; @@ -101,108 +158,15 @@ typedef struct lumiera_conditionacquirer_struct* LumieraConditionacquirer; static inline void lumiera_conditionacquirer_ensureunlocked (LumieraConditionacquirer self) { - ENSURE (self->state == LUMIERA_UNLOCKED, "forgot to unlock the condition mutex"); -} - -/* override with a macro to use the cleanup checker */ -#define lumiera_conditionacquirer \ -lumiera_conditionacquirer NOBUG_CLEANUP(lumiera_conditionacquirer_ensureunlocked) - - -/** - * initialize a conditionacquirer state - * @param self conditionacquirer to be initialized, must be an automatic variable - * @param cond associated condition variable - * @param state initial state of the mutex, either LUMIERA_LOCKED or LUMIERA_UNLOCKED - * @return self as given - * errors are fatal - */ -static inline LumieraConditionacquirer -lumiera_conditionacquirer_init (LumieraConditionacquirer self, LumieraCondition cond, enum lumiera_lockstate state) -{ - REQUIRE (self); - REQUIRE (cond); - self->cond = cond; - self->state = state; - if (state == LUMIERA_LOCKED) - if (pthread_mutex_lock (&cond->mutex)) - LUMIERA_DIE (MUTEX_LOCK); - - return self; -} - -/** - * lock the mutex. - * must not already be locked - * @param self conditionacquirer associated with a condition variable - */ -static inline void -lumiera_conditionacquirer_lock (LumieraConditionacquirer self) -{ - REQUIRE (self); - REQUIRE (self->state == LUMIERA_UNLOCKED, "mutex already locked"); - - if (pthread_mutex_lock (&self->cond->mutex)) - LUMIERA_DIE (MUTEX_LOCK); - - self->state = LUMIERA_LOCKED; -} - - -/** - * wait on a locked condition. - * Waits until the condition variable gets signaled from another thread. Must already be locked. - * @param self conditionacquirer associated with a condition variable - */ -static inline void -lumiera_conditionacquirer_wait (LumieraConditionacquirer self) -{ - REQUIRE (self); - REQUIRE (self->state == LUMIERA_LOCKED, "mutex must be locked"); - pthread_cond_wait (&self->cond->cond, &self->cond->mutex); -} - - -/** - * release mutex. - * a conditionacquirer must be unlocked before leaving scope - * @param self conditionacquirer associated with a condition variable - */ -static inline void -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 (MUTEX_UNLOCK); - self->state = LUMIERA_UNLOCKED; -} - - -/** - * signal a single waiting thread - * @param self conditionacquirer associated with the condition variable to be signaled - */ -static inline void -lumiera_conditionacquirer_signal (LumieraConditionacquirer self) -{ - REQUIRE (self); - REQUIRE (self->state == LUMIERA_LOCKED, "mutex was not locked"); - pthread_cond_signal (&self->cond->cond); -} - - -/** - * signal all waiting threads - * @param self conditionacquirer associated with the condition variable to be signaled - */ -static inline void -lumiera_conditionacquirer_broadcast (LumieraConditionacquirer self) -{ - REQUIRE (self); - REQUIRE (self->state == LUMIERA_LOCKED, "mutex was not locked"); - pthread_cond_broadcast (&self->cond->cond); + ENSURE (!self->condition, "forgot to unlock condition variable"); } #endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/cuckoo.c b/src/lib/cuckoo.c index bbcb0e71a..b2ad064c0 100644 --- a/src/lib/cuckoo.c +++ b/src/lib/cuckoo.c @@ -23,6 +23,8 @@ #include +#define CUCKOO_GRANULARITY int + enum compact_state { COMPACTING_OFF, @@ -35,14 +37,11 @@ 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; + struct cuckoo_vtable vtable; - cuckoo_cmpfunc cmp; + uint32_t r1; /* random, reset for each rehash */ + uint32_t r2; + uint32_t r3; void* t1; void* t2; @@ -61,28 +60,59 @@ static inline uint32_t cuckoo_fast_prng () return rnd = rnd<<1 ^ ((rnd>>30) & 1) ^ ((rnd>>2) & 1); } +static inline int +iszero (void* mem, size_t size) +{ + while (size && !*(CUCKOO_GRANULARITY*)mem) + { + size -= sizeof (CUCKOO_GRANULARITY); + mem += sizeof (CUCKOO_GRANULARITY); + } + return !size; +} + +static inline void +xmemmov (void* dst, void* src, size_t size) +{ + while (size) + { + size -= sizeof (CUCKOO_GRANULARITY); + *(CUCKOO_GRANULARITY*)(dst + size) = *(CUCKOO_GRANULARITY*)(src + size); + } +} + + Cuckoo -cuckoo_init (Cuckoo self, - cuckoo_hashfunc h1, - cuckoo_hashfunc h2, - cuckoo_hashfunc h3, - cuckoo_cmpfunc cmp, - size_t itemsize, - unsigned startsize) +cuckoo_init (Cuckoo self, size_t itemsize, struct cuckoo_vtable* vtable) { if (!self) return NULL; - self->size = 1<itemsize = itemsize; - self->h1 = h1; + self->size = 16; + self->itemsize = (itemsize * sizeof (CUCKOO_GRANULARITY) + + sizeof (CUCKOO_GRANULARITY) - 1) / sizeof (CUCKOO_GRANULARITY); /* round up to next CUCKOO_GRANULARITY boundary */ self->r1 = cuckoo_fast_prng (); - self->h2 = h2; self->r2 = cuckoo_fast_prng (); - self->h3 = h3; self->r3 = cuckoo_fast_prng (); - self->cmp = cmp; + if (!vtable->h1 || !vtable->h2 || !vtable->h3) + return NULL; + + self->vtable.h1 = vtable->h1; + self->vtable.h2 = vtable->h2; + self->vtable.h3 = vtable->h3; + + if (!vtable->cmp) + return NULL; + + self->vtable.cmp = vtable->cmp; + + self->vtable.dtor = vtable->dtor; + + if (vtable->mov) + self->vtable.mov = vtable->mov; + else + self->vtable.mov = xmemmov; self->t1 = calloc (self->size * 4, itemsize); self->t2 = calloc (self->size * 2, itemsize); @@ -101,19 +131,15 @@ cuckoo_init (Cuckoo self, 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_new (size_t itemsize, struct cuckoo_vtable* vtable) { Cuckoo self = malloc (sizeof (struct cuckoo_struct)); - if (!cuckoo_init (self, h1, h2, h3, cmp, itemsize, startsize)) + if (!cuckoo_init (self, itemsize, vtable)) { free (self); return NULL; @@ -126,6 +152,18 @@ cuckoo_destroy (Cuckoo self) { if (self) { + + if (self->vtable.dtor) + { + void* elem; + for (elem = self->t1; elem < self->t1 + self->size * 4; elem += self->size) + self->vtable.dtor (elem); + for (elem = self->t2; elem < self->t1 + self->size * 2; elem += self->size) + self->vtable.dtor (elem); + for (elem = self->t3; elem < self->t1 + self->size; elem += self->size) + self->vtable.dtor (elem); + } + free (self->t1); free (self->t2); free (self->t3); @@ -135,76 +173,57 @@ cuckoo_destroy (Cuckoo self) void -cuckoo_free (Cuckoo self) +cuckoo_delete (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 +static void* cuckoo_insert_internal_ (Cuckoo self, void* item) { void* pos; - char tmp[self->itemsize]; + CUCKOO_GRANULARITY tmp[self->itemsize / sizeof(CUCKOO_GRANULARITY)]; for (unsigned n = 0; n < self->maxloops; ++n) { /* find nest */ - pos = self->t1 + self->itemsize * (self->h1 (item, self->r1) % (4*self->size)); + pos = self->t1 + self->itemsize * (self->vtable.h1 (item, self->r1) % (4*self->size)); /* kick old egg out */ - xmemcpy (tmp, pos, self->itemsize); + if (iszero (pos, self->itemsize)) + memset (tmp, 0, self->itemsize); + else + self->vtable.mov (tmp, pos, self->itemsize); /* lay egg */ - xmemcpy (pos, item, self->itemsize); + self->vtable.mov (pos, item, self->itemsize); if (iszero (tmp, self->itemsize)) - return 1; + return pos; /* find nest */ - pos = self->t2 + self->itemsize * (self->h2 (tmp, self->r2) % (2*self->size)); + pos = self->t2 + self->itemsize * (self->vtable.h2 (tmp, self->r2) % (2*self->size)); /* kick old egg out */ - xmemcpy (item, pos, self->itemsize); + self->vtable.mov (item, pos, self->itemsize); /* lay egg */ - xmemcpy (pos, tmp, self->itemsize); + self->vtable.mov (pos, tmp, self->itemsize); if (iszero (item, self->itemsize)) - return 1; + return pos; /* find nest */ - pos = self->t3 + self->itemsize * (self->h3 (item, self->r3) % self->size); + pos = self->t3 + self->itemsize * (self->vtable.h3 (item, self->r3) % self->size); /* kick old egg out */ - xmemcpy (tmp, pos, self->itemsize); + self->vtable.mov (tmp, pos, self->itemsize); /* lay egg */ - xmemcpy (pos, item, self->itemsize); + self->vtable.mov (pos, item, self->itemsize); if (iszero (tmp, self->itemsize)) - return 1; + return pos; /* copy tmp to item, which will be reinserted on next interation / after rehashing */ - xmemcpy (item, tmp, self->itemsize); + self->vtable.mov (item, tmp, self->itemsize); } - return 0; + return NULL; } @@ -230,14 +249,14 @@ cuckoo_rehash (Cuckoo self) { for (n = 0; n < self->maxloops; ++n) { - unsigned hash = self->h1 (pos, self->r1) % (4*self->size); + unsigned hash = self->vtable.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); + self->vtable.mov (t, hpos, self->itemsize); + self->vtable.mov (hpos, pos, self->itemsize); + self->vtable.mov (pos, t, self->itemsize); if (iszero (t, self->itemsize)) break; } @@ -260,14 +279,14 @@ cuckoo_rehash (Cuckoo self) { for (n = 0; n < self->maxloops; ++n) { - unsigned hash = self->h2 (pos, self->r2) % (2*self->size); + unsigned hash = self->vtable.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); + self->vtable.mov (t, hpos, self->itemsize); + self->vtable.mov (hpos, pos, self->itemsize); + self->vtable.mov (pos, t, self->itemsize); if (iszero (t, self->itemsize)) break; } @@ -290,14 +309,14 @@ cuckoo_rehash (Cuckoo self) { for (n = 0; n < self->maxloops; ++n) { - unsigned hash = self->h3 (pos, self->r3) % self->size; + unsigned hash = self->vtable.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); + self->vtable.mov (t, hpos, self->itemsize); + self->vtable.mov (hpos, pos, self->itemsize); + self->vtable.mov (pos, t, self->itemsize); if (iszero (t, self->itemsize)) break; } @@ -314,10 +333,10 @@ 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; + cuckoo_hashfunc th = self->vtable.h3; + self->vtable.h3 = self->vtable.h2; + self->vtable.h2 = self->vtable.h1; + self->vtable.h1 = th; uint32_t tr = self->r3; self->r3 = self->r2; @@ -380,10 +399,10 @@ cuckoo_compact (Cuckoo self) 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; + cuckoo_hashfunc th = self->vtable.h1; + self->vtable.h1 = self->vtable.h2; + self->vtable.h2 = self->vtable.h3; + self->vtable.h3 = th; uint32_t tr = self->r1; self->r1 = self->r2; @@ -425,7 +444,7 @@ cuckoo_compact (Cuckoo self) } -int +void* cuckoo_insert (Cuckoo self, void* item) { char tmp[self->itemsize]; @@ -433,30 +452,32 @@ cuckoo_insert (Cuckoo self, void* item) void* found; if ((found = cuckoo_find (self, item))) { - xmemcpy (found, item, self->itemsize); - return 1; + if (self->vtable.dtor) + self->vtable.dtor (found); + self->vtable.mov (found, item, self->itemsize); + return found; } - xmemcpy (tmp, item, self->itemsize); + self->vtable.mov (tmp, item, self->itemsize); for (unsigned n = 6; n; --n) /* rehash/grow loop */ { - if (cuckoo_insert_internal_ (self, tmp)) + if ((found = cuckoo_insert_internal_ (self, tmp))) { ++self->elements; - return 1; + return found; } if (self->elements > n*self->size) { n = 6; if (!cuckoo_grow (self)) - return 0; + return NULL; } else cuckoo_rehash (self); } - return 0; + return NULL; } @@ -465,16 +486,16 @@ 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)) + pos = self->t1 + self->itemsize * (self->vtable.h1 (item, self->r1) % (4*self->size)); + if (!iszero (pos, self->itemsize) && self->vtable.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)) + pos = self->t2 + self->itemsize * (self->vtable.h2 (item, self->r2) % (2*self->size)); + if (!iszero (pos, self->itemsize) && self->vtable.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)) + pos = self->t3 + self->itemsize * (self->vtable.h3 (item, self->r3) % self->size); + if (!iszero (pos, self->itemsize) && self->vtable.cmp (item, pos)) return pos; return NULL; @@ -486,6 +507,9 @@ cuckoo_remove (Cuckoo self, void* item) { if (item) { + if (self->vtable.dtor) + self->vtable.dtor (item); + memset (item, 0, self->itemsize); --self->elements; diff --git a/src/lib/cuckoo.h b/src/lib/cuckoo.h index 323b5cc75..84546d002 100644 --- a/src/lib/cuckoo.h +++ b/src/lib/cuckoo.h @@ -54,41 +54,61 @@ typedef size_t (*cuckoo_hashfunc)(const void* item, const uint32_t r); */ typedef int (*cuckoo_cmpfunc)(const void* item1, const void* item2); +/** + * Item destructor function. + * User supplied destructor function. This function is called when items are removed + * from the hash or at hash detroy/delete time. Must be safe to be called on a zeroed element. + * @param item address of the item to be destroyed + */ +typedef void (*cuckoo_dtorfunc)(void* item); + +/** + * Move function. + * User supplied item move function + * @param dest target address for the move operation + * @param src source for the move operation + * @param size size of a item (requested size rounded up to the next CUCKOO_GRANULARITY) + * It might be necessary to invalidate the source in some case, cuckoo will zero it out + * after moving. + */ +typedef void (*cuckoo_movfunc)(void* dest, void* src, size_t size); + + +/** + * Function table used to specialize various funtions used by the hash. + * TODO some elements might be NULL, then defaults are used + */ +struct cuckoo_vtable +{ + cuckoo_hashfunc h1; + cuckoo_hashfunc h2; + cuckoo_hashfunc h3; + cuckoo_cmpfunc cmp; + cuckoo_dtorfunc dtor; + cuckoo_movfunc mov; +}; + + /** * 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 + * @param itemsize size for a single hash entry, will be rounded up to align CUCKOO_GRANULARITY + * @param vtable initialized vtable * @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); +cuckoo_init (Cuckoo self, size_t itemsize, struct cuckoo_vtable* vtable); /** * 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 itemsize size for a single hash entry, will be rounded up to align CUCKOO_GRANULARITY * @param startsize initial size of table t3, as 2's exponent + * @param vtable initialized vtable * @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); +cuckoo_new (size_t itemsize, struct cuckoo_vtable* vtable); + /** * Destroy a cuckoo hash. @@ -104,7 +124,7 @@ cuckoo_destroy (Cuckoo self); * @param self handle of the hash table to be freed */ void -cuckoo_free (Cuckoo self); +cuckoo_delete (Cuckoo self); /** * Get the number of elements stored in a hash. @@ -117,12 +137,14 @@ cuckoo_nelements (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. + * Inserting an element already in the hash calls the dtor for the old entry and places + * the new one into the hash. * @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 + * @return pointer to inserted element on successful insert, NULL on allocation failure * Note: at failure there is one arbitary hash cell lost! */ -int +void* cuckoo_insert (Cuckoo self, void* item); /** diff --git a/src/lib/error.c b/src/lib/error.c index b2bc390b6..1d3fb50c3 100644 --- a/src/lib/error.c +++ b/src/lib/error.c @@ -45,19 +45,13 @@ lumiera_error_tls_init (void) pthread_key_create (&lumiera_error_tls, NULL); } -/** - * Set error state for the current thread. - * If the error state of the current thread was cleared, then set it, else preserve the old state. - * @param nerr name of the error with 'LUMIERA_ERROR_' prefix (example: LUMIERA_ERROR_NO_MEMORY) - * @return old state, that is NULL for success, when the state was cleared and a pointer to a pending - * error when the error state was already set - */ -const char* -lumiera_error_set (const char * nerr) + +lumiera_err +lumiera_error_set (lumiera_err nerr) { pthread_once (&lumiera_error_initialized, lumiera_error_tls_init); - const char* err = pthread_getspecific (lumiera_error_tls); + lumiera_err err = pthread_getspecific (lumiera_error_tls); if (!err) pthread_setspecific (lumiera_error_tls, nerr); @@ -65,18 +59,12 @@ lumiera_error_set (const char * nerr) } -/** - * Get and clear current error state. - * This function clears the error state, if it needs to be reused, one has to store it in a temporary - * variable. - * @return pointer to any pending error of this thread, NULL if no error is pending - */ -const char* +lumiera_err lumiera_error () { pthread_once (&lumiera_error_initialized, lumiera_error_tls_init); - const char* err = pthread_getspecific (lumiera_error_tls); + lumiera_err err = pthread_getspecific (lumiera_error_tls); if (err) pthread_setspecific (lumiera_error_tls, NULL); return err; diff --git a/src/lib/error.h b/src/lib/error.h index 1eeb42dda..5c4a8c145 100644 --- a/src/lib/error.h +++ b/src/lib/error.h @@ -35,6 +35,7 @@ extern "C" { * C Error handling in Lumiera, header. */ +typedef const char* lumiera_err; /** * Abort unconditionally with a 'Fatal Error!' message. @@ -49,7 +50,7 @@ extern "C" { * @param err name of the error without the 'LUMIERA_ERROR_' prefix (example: NO_MEMORY) */ #define LUMIERA_ERROR_DECLARE(err) \ -extern const char* LUMIERA_ERROR_##err +extern lumiera_err LUMIERA_ERROR_##err /** * Definition and initialization of an error constant. @@ -58,7 +59,7 @@ extern const char* LUMIERA_ERROR_##err * @param msg message describing the error in plain english (example: "memory allocation failed") */ #define LUMIERA_ERROR_DEFINE(err, msg) \ -const char* LUMIERA_ERROR_##err = "LUMIERA_ERROR_" #err ":" msg +lumiera_err LUMIERA_ERROR_##err = "LUMIERA_ERROR_" #err ":" msg /** * Helper macro to raise an error for the current thread. @@ -70,10 +71,23 @@ const char* LUMIERA_ERROR_##err = "LUMIERA_ERROR_" #err ":" msg (({ERROR (flag, "%s", strchr(LUMIERA_ERROR_##err, ':')+1);}), \ lumiera_error_set(LUMIERA_ERROR_##err)) -const char* -lumiera_error_set (const char * err); +/** + * Set error state for the current thread. + * If the error state of the current thread was cleared, then set it, else preserve the old state. + * @param nerr name of the error with 'LUMIERA_ERROR_' prefix (example: LUMIERA_ERROR_NO_MEMORY) + * @return old state, that is NULL for success, when the state was cleared and a pointer to a pending + * error when the error state was already set + */ +lumiera_err +lumiera_error_set (lumiera_err err); -const char* +/** + * Get and clear current error state. + * This function clears the error state, if it needs to be reused, one has to store it in a temporary + * variable. + * @return pointer to any pending error of this thread, NULL if no error is pending + */ +lumiera_err lumiera_error (); /* diff --git a/src/lib/llist.h b/src/lib/llist.h index 128a3e826..353fe4320 100644 --- a/src/lib/llist.h +++ b/src/lib/llist.h @@ -299,6 +299,9 @@ LLIST_FUNC (LList llist_unlink (LList self), /** * Fix a node which got relocated in memory. * It is supported to realloc/move list nodes in memory but one must call 'list_relocate' after doing so. + * IMPORTANT: it is not possible to relocate nodes which are empty this way, nor can this be determined + * after the relocation, so either llist_init them afterwards or insert a bogus node before moving the node + * and relocating it and remove it afterwards. * @param self node which got relocated * @return self */ @@ -306,6 +309,7 @@ LLIST_FUNC (LList llist_relocate (LList self), return self->next->prev = self->prev->next = self; ); + /** * Insert a node after another. * @param self node after which we want to insert diff --git a/src/lib/luid.c b/src/lib/luid.c new file mode 100644 index 000000000..c8d766094 --- /dev/null +++ b/src/lib/luid.c @@ -0,0 +1,113 @@ +/* + luid - Lumiera unique identifiers + + 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/luid.h" + +#include +#include +#include +#include +#include +#include +#include + +void +lumiera_uid_set_ptr (lumiera_uid* luid, void* ptr) +{ + memset (luid, 0, 16); + *(void**)luid = ptr; +} + + +void* +lumiera_uid_ptr_get (lumiera_uid* luid) +{ + return *(void**)luid; +} + + +void +lumiera_uid_gen (lumiera_uid* luid) +{ + static int fd = -2; + if (!luid) + return; + + if (fd == -2) + { + fd = open ("/dev/urandom", O_RDONLY); + /* on linux /dev/random would be way to slow for our purpose, so we comment that out for now. + other unixiods offer a /dev/random which has the same semantics as linux /dev/urandom has, + configuration should do this right some day. + if (fd == -1) + fd = open ("/dev/random", O_RDONLY); + */ + if (fd >= 0) + fcntl (fd, F_SETFD, FD_CLOEXEC); + else + srand (getpid () + time (NULL)); + } + + do + { + if (fd < 0) + { + for (int i = 0; i < 16; ++i) + ((unsigned char*)luid)[i] = (unsigned char)(rand()>>7); + } + else + { + if (read (fd, luid, 16) < 16) + abort (); + } + } + /* we identify generic pointers by having some zeros in the luid, + * this happens very unlikely to be in a random luid, just regenerate it then */ + while (!*(((intptr_t*)luid)+1)); +} + + +void +lumiera_uid_copy (lumiera_uid* dest, lumiera_uid* src) +{ + memcpy (dest, src, 16); +} + + +int +lumiera_uid_eq (lumiera_uid* luida, lumiera_uid* luidb) +{ + return !memcmp (luida, luidb, 16); +} + +size_t +lumiera_uid_hash (lumiera_uid* luid) +{ + return *(size_t*)luid; +} + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/luid.h b/src/lib/luid.h new file mode 100644 index 000000000..a93d004a3 --- /dev/null +++ b/src/lib/luid.h @@ -0,0 +1,81 @@ +/* + luid - Lumiera unique identifiers + + 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_LUID_H +#define LUMIERA_LUID_H + +#include + +/** + * @file + * Lumiera unique identifiers are 128 byte random value. Unlike standard uuid's we + * don't tag a version within them and we may store generic pointers in the space + * occupied by an luid. + */ + +typedef unsigned char lumiera_uid[16]; + +/** + * Retrieve a generic pointer stored in a luid + */ +void* +lumiera_uid_ptr_get (lumiera_uid* luid); + +/** + * Generate a new luid + */ +void +lumiera_uid_gen (lumiera_uid* luid); + +/** + * Store a generic pointer in a luid + */ +void +lumiera_uid_set_ptr (lumiera_uid* luid, void* ptr); + + +/** + * Copy an luid + */ +void +lumiera_uid_copy (lumiera_uid* dest, lumiera_uid* src); + + +/** + * Test 2 luid's for equality + */ +int +lumiera_uid_eq (lumiera_uid* luida, lumiera_uid* luidb); + + +/** + * Generate a hashsum over an luid + */ +size_t +lumiera_uid_hash (lumiera_uid* luid); + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/mrucache.c b/src/lib/mrucache.c index 0f9bb86df..d591d5f56 100644 --- a/src/lib/mrucache.c +++ b/src/lib/mrucache.c @@ -19,6 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "lib/safeclib.h" #include "lib/mrucache.h" @@ -39,9 +40,9 @@ lumiera_mrucache_destroy (LumieraMruCache self) { llist_unlink (node); if (self->destructor_cb) - free (self->destructor_cb (node)); + lumiera_free (self->destructor_cb (node)); else - free (node); + lumiera_free (node); } self->cached = 0; return self; @@ -52,7 +53,7 @@ lumiera_mrucache_age (LumieraMruCache self, int nelem) { REQUIRE (self); while (self->cached && nelem--) - free (lumiera_mrucache_pop (self)); + lumiera_free (lumiera_mrucache_pop (self)); return nelem; } diff --git a/src/lib/mutex.c b/src/lib/mutex.c index b7c5f1844..99ef8897f 100644 --- a/src/lib/mutex.c +++ b/src/lib/mutex.c @@ -31,34 +31,35 @@ LUMIERA_ERROR_DEFINE (MUTEX_UNLOCK, "Mutex unlocking failed"); LUMIERA_ERROR_DEFINE (MUTEX_DESTROY, "Mutex destroy failed"); -/** - * Initialize a mutex variable - * @param self is a pointer to the mutex to be initialized - * @return self as given - */ LumieraMutex -lumiera_mutex_init (LumieraMutex self) +lumiera_mutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag) { if (self) { pthread_mutex_init (&self->mutex, NULL); + NOBUG_RESOURCE_HANDLE_INIT (self->rh); + NOBUG_RESOURCE_ANNOUNCE_RAW (flag, "mutex", purpose, self, self->rh); } return self; } -/** - * Destroy a mutex variable - * @param self is a pointer to the mutex to be destroyed - * @return self as given - */ + LumieraMutex -lumiera_mutex_destroy (LumieraMutex self) +lumiera_mutex_destroy (LumieraMutex self, struct nobug_flag* flag) { if (self) { + NOBUG_RESOURCE_FORGET_RAW (flag, self->rh); if (pthread_mutex_destroy (&self->mutex)) LUMIERA_DIE (MUTEX_DESTROY); } return self; } +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/mutex.h b/src/lib/mutex.h index f19058e7a..681d29432 100644 --- a/src/lib/mutex.h +++ b/src/lib/mutex.h @@ -22,21 +22,43 @@ #ifndef LUMIERA_MUTEX_H #define LUMIERA_MUTEX_H -#include "lib/locking.h" +#include "lib/error.h" + +#include +#include /** * @file * Mutual exclusion locking, header. */ -#define LUMIERA_MUTEX_SECTION(flag, handle, mutex) \ -RESOURCE_HANDLE (rh_##__LINE__##_); \ -lumiera_mutexacquirer lock_##__LINE__##_; \ -RESOURCE_ENTER (flag, handle, "acquire mutex", &lock_##__LINE__##_, \ - NOBUG_RESOURCE_EXCLUSIVE, rh_##__LINE__##_); \ -for (lumiera_mutexacquirer_init_mutex (&lock_##__LINE__##_, mutex, LUMIERA_LOCKED); \ - lock_##__LINE__##_.state == LUMIERA_LOCKED; \ - lumiera_mutexacquirer_unlock (&lock_##__LINE__##_), \ - ({RESOURCE_LEAVE(flag, rh_##__LINE__##_);})) + +LUMIERA_ERROR_DECLARE (MUTEX_LOCK); +LUMIERA_ERROR_DECLARE (MUTEX_UNLOCK); +LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); + +/** + * Mutual exclusive section. + */ +#define LUMIERA_MUTEX_SECTION(nobugflag, mtx) \ + for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1}; \ + lumiera_mutex_section_.mutex;) \ + for ( \ + ({ \ + lumiera_mutex_section_.mutex = (mtx); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + }); \ + lumiera_mutex_section_.mutex; \ + ({ \ + if (lumiera_mutex_section_.mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ + lumiera_mutex_section_.mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ + } \ + })) /** @@ -46,27 +68,37 @@ for (lumiera_mutexacquirer_init_mutex (&lock_##__LINE__##_, mutex, LUMIERA_LOCKE struct lumiera_mutex_struct { pthread_mutex_t mutex; + RESOURCE_HANDLE (rh); }; typedef struct lumiera_mutex_struct lumiera_mutex; typedef lumiera_mutex* LumieraMutex; +/** + * Initialize a mutex variable + * @param self is a pointer to the mutex to be initialized + * @return self as given + */ LumieraMutex -lumiera_mutex_init (LumieraMutex self); - - -LumieraMutex -lumiera_mutex_destroy (LumieraMutex self); - +lumiera_mutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag); /** - * mutexacquirer used to manage the state of a mutex variable. + * Destroy a mutex variable + * @param self is a pointer to the mutex to be destroyed + * @return self as given + */ +LumieraMutex +lumiera_mutex_destroy (LumieraMutex self, struct nobug_flag* flag); + + +/** + * mutexacquirer used to manage the state of a mutex. */ struct lumiera_mutexacquirer_struct { LumieraMutex mutex; - enum lumiera_lockstate state; + RESOURCE_HANDLE (rh); }; typedef struct lumiera_mutexacquirer_struct lumiera_mutexacquirer; typedef struct lumiera_mutexacquirer_struct* LumieraMutexacquirer; @@ -75,123 +107,14 @@ typedef struct lumiera_mutexacquirer_struct* LumieraMutexacquirer; static inline void lumiera_mutexacquirer_ensureunlocked (LumieraMutexacquirer self) { - ENSURE (self->state == LUMIERA_UNLOCKED, "forgot to unlock mutex"); -} - -/* override with a macro to use the cleanup checker */ -#define lumiera_mutexacquirer \ -lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) - - -/** - * initialize a mutexacquirer state without mutex. - * @param self mutexacquirer to be initialized, must be an automatic variable - * @return self as given - * This initialization is used when lumiera_mutexacquirer_try_mutex shall be used later - */ -static inline LumieraMutexacquirer -lumiera_mutexacquirer_init (LumieraMutexacquirer self) -{ - REQUIRE (self); - self->mutex = NULL; - self->state = LUMIERA_UNLOCKED; - - return self; -} - -/** - * initialize a mutexacquirer state - * @param self mutexacquirer to be initialized, must be an automatic variable - * @param mutex associated mutex - * @param state initial state of the mutex, either LUMIERA_LOCKED or LUMIERA_UNLOCKED - * @return self as given - * errors are fatal - */ -static inline LumieraMutexacquirer -lumiera_mutexacquirer_init_mutex (LumieraMutexacquirer self, LumieraMutex mutex, enum lumiera_lockstate state) -{ - REQUIRE (self); - REQUIRE (mutex); - self->mutex = mutex; - self->state = state; - if (state == LUMIERA_LOCKED) - if (pthread_mutex_lock (&mutex->mutex)) - LUMIERA_DIE (MUTEX_LOCK); - - return self; -} - - -/** - * lock the mutex. - * must not already be locked - * @param self mutexacquirer associated with a mutex variable - */ -static inline void -lumiera_mutexacquirer_lock (LumieraMutexacquirer self) -{ - REQUIRE (self); - REQUIRE (self->state == LUMIERA_UNLOCKED, "mutex already locked"); - - if (pthread_mutex_lock (&self->mutex->mutex)) - LUMIERA_DIE (MUTEX_LOCK); - - self->state = LUMIERA_LOCKED; -} - - -/** - * get the state of a lock. - * @param self mutexacquirer associated with a mutex variable - * @return LUMIERA_LOCKED when the mutex is locked by this thead - */ -static inline enum lumiera_lockstate -lumiera_mutexacquirer_state (LumieraMutexacquirer self) -{ - REQUIRE (self); - return self->state; -} - - -/** - * try to lock a mutex. - * must not already be locked - * @param self mutexacquirer associated with a mutex variable - * @param mutex pointer to a mutex which should be tried - * @return LUMIERA_LOCKED when the mutex got locked - */ -static inline enum lumiera_lockstate -lumiera_mutexacquirer_try_mutex (LumieraMutexacquirer self, LumieraMutex mutex) -{ - REQUIRE (self); - REQUIRE (self->state == LUMIERA_UNLOCKED, "mutex already locked"); - - self->mutex=mutex; - switch (pthread_mutex_trylock (&self->mutex->mutex)) - { - case 0: - return self->state = LUMIERA_LOCKED; - case EBUSY: - return LUMIERA_UNLOCKED; - default: - LUMIERA_DIE (MUTEX_LOCK); - } -} - - -/** - * release mutex. - * a mutexacquirer must be unlocked before leaving scope - * @param self mutexacquirer associated with a mutex variable - */ -static inline void -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 (MUTEX_UNLOCK); - self->state = LUMIERA_UNLOCKED; + ENSURE (!self->mutex, "forgot to unlock mutex"); } #endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/plugin.c b/src/lib/plugin.c index 1f5e8576e..44cf6abc5 100644 --- a/src/lib/plugin.c +++ b/src/lib/plugin.c @@ -111,10 +111,6 @@ lumiera_plugin_name_cmp (const void* a, const void* b) return strcmp (((struct lumiera_plugin*) a)->name, ((struct lumiera_plugin*) b)->name); } -/** - * Initialize the plugin system. - * always succeeds or aborts - */ void lumiera_init_plugin (void) { @@ -173,17 +169,6 @@ lumiera_plugin_lookup (struct lumiera_plugin* self, const char* path) } -/** - * Make an interface available. - * To use an interface provided by a plugin it must be opened first. It is allowed to open an interface - * more than once. Each open must be paired with a close. - * @param name name of the plugin to use. - * @param interface name of the interface to open. - * @param min_revision the size of the interface structure is used as measure of a minimal required - * revision (new functions are appended at the end) - * @return handle to the interface or NULL in case of a error. The application shall cast this handle to - * the actual interface type. - */ struct lumiera_interface* lumiera_interface_open (const char* name, const char* interface, size_t min_revision) { @@ -311,11 +296,6 @@ lumiera_interface_open (const char* name, const char* interface, size_t min_revi return NULL; } -/** - * Close an interface. Does not free associated resources - * Calling this function with self==NULL is legal. Every interface handle must be closed only once. - * @param ptr interface to be closed - */ void lumiera_interface_close (void* ptr) { diff --git a/src/lib/plugin.h b/src/lib/plugin.h index 54dad6372..7e17afc18 100644 --- a/src/lib/plugin.h +++ b/src/lib/plugin.h @@ -96,14 +96,34 @@ struct lumiera_interface int (*close)(void); }; +/** + * Initialize the plugin system. + * always succeeds or aborts + */ void lumiera_init_plugin (void); +/** + * Make an interface available. + * To use an interface provided by a plugin it must be opened first. It is allowed to open an interface + * more than once. Each open must be paired with a close. + * @param name name of the plugin to use. + * @param interface name of the interface to open. + * @param min_revision the size of the interface structure is used as measure of a minimal required + * revision (new functions are appended at the end) + * @return handle to the interface or NULL in case of a error. The application shall cast this handle to + * the actual interface type. + */ struct lumiera_interface* lumiera_interface_open (const char* plugin, const char* name, size_t min_revision); +/** + * Close an interface. Does not free associated resources + * Calling this function with self==NULL is legal. Every interface handle must be closed only once. + * @param ptr interface to be closed + */ void lumiera_interface_close (void* self); diff --git a/src/lib/psplay.c b/src/lib/psplay.c new file mode 100644 index 000000000..7647fd905 --- /dev/null +++ b/src/lib/psplay.c @@ -0,0 +1,522 @@ +/* + psplay.c - probabilistic splay tree + + Copyright (C) + 2004, 2005, 2006, Christian Thaeter + Copyright (C) CinelerraCV + 2007, 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/psplay.h" + +#include +#include +#include +#include + +NOBUG_DEFINE_FLAG (psplay); + +#ifndef PSPLAY_TRAIL_DEPTH +#define PSPLAY_TRAIL_DEPTH 128 +#endif + +/* + probabilistic distribution, this are the direct splay equations used to determine if to splay + or break out of the splaying algorithm. + + useable variables/functions are: + self->log2 - log2 of the tree elements, this would be the depth of a fully balanced tree + splayfactor - user defined weigth for splaying, we define '100' to be the default + depth - depth of the current node in the tree + trail->dir - dervitation from tree center + psplay_fast_prng () - returns a prng in the range 1...2^31 +*/ + +#define PSPLAY_FORMULA (self->log2*100/(depth + (psplay_fast_prng () & 63)) + trail->dir) * splayfactor + +#ifndef PSPLAY_PROB_ZIGZIG +#define PSPLAY_PROB_ZIGZIG 5000 +#endif +#ifndef PSPLAY_PROB_ZIGZAG +#define PSPLAY_PROB_ZIGZAG 2500 +#endif + + + +/* simple prng with 2^31-1 cycle */ +static inline uint32_t psplay_fast_prng () +{ + static uint32_t rnd=0xbabeface; + return rnd = rnd<<1 ^ ((rnd >> 30) & 1) ^ ((rnd>>2) & 1); +} + + +PSplay +psplay_init (PSplay self, psplay_cmp_fn cmp, psplay_key_fn key, psplay_delete_fn delete) +{ + NOBUG_INIT_FLAG (psplay); + TRACE (psplay); + REQUIRE (cmp); + REQUIRE (key); + + if (self) + { + self->tree = NULL; + self->found_parent = &self->tree; + self->cmp = cmp; + self->key = key; + self->delete = delete; + self->elem_cnt = 0; + self->log2 = 0; + } + return self; +} + + +PSplay +psplay_new (psplay_cmp_fn cmp, psplay_key_fn key, psplay_delete_fn delete) +{ + PSplay self = malloc (sizeof *self); + if (self) + { + psplay_init (self , cmp, key, delete); + } + return self; +} + + + +PSplay +psplay_destroy (PSplay self) +{ + TRACE (psplay); + if (self) while (self->tree) + { + PSplaynode n = psplay_remove (self, self->tree); + if (self->delete) + self->delete (n); + } + return self; +} + + +void +psplay_delete (PSplay self) +{ + free (psplay_destroy (self)); +} + + +PSplaynode +psplaynode_init (PSplaynode self) +{ + if (self) + self->left = self->right = NULL; + return self; +} + + + +/* + Lookup operations (lookup and insert) record the path as they decend into the tree + this will allow bottom up splaying without storing 'up' pointers in the node. + The length of this trail (PSPLAY_TRAIL_DEPTH) also define the maximal limit on how much + a node can be splayed up giving some hard bound for the splay operation. + + General wisdom tells that top down splaying is more efficent to implement than bottom + up splaying. Nevertheless we do bottom up splaying here because we can decide + randomly on each level if we want to continue splaying or not. No splaying + is certainly more efficent than top-down splaying. +*/ +struct psplaytrail +{ + int dir; + unsigned depth; + PSplaynode* trail[PSPLAY_TRAIL_DEPTH]; +}; + +static inline unsigned +trailidx (unsigned n) +{ + return n & (PSPLAY_TRAIL_DEPTH-1); +} + + +static inline void +psplay_splay (PSplay self, struct psplaytrail* trail, unsigned splayfactor) +{ + TRACE (psplay, "%p %u", self, splayfactor); + + if (trail->dir < 0) trail->dir = - trail->dir; + + for (unsigned lim = PSPLAY_TRAIL_DEPTH, depth = trail->depth; lim > 2 && depth > 2; lim-=2, depth-=2) + { + PSplaynode node = *trail->trail [trailidx (depth)]; + PSplaynode parent = *trail->trail [trailidx (depth-1)]; + PSplaynode grandparent = *trail->trail [trailidx (depth-2)]; + + unsigned r = PSPLAY_FORMULA; + TRACE (psplay, "r is %u", r); + + if (parent == grandparent->left) + { + TRACE (psplay, "ZIG.."); + if (node == parent->left) + { + TRACE (psplay, "..ZIG"); + if (r < PSPLAY_PROB_ZIGZIG) + { + TRACE (psplay, "BREAK"); + return; + } + + grandparent->left = parent->right; + parent->right = grandparent; + + parent->left = node->right; + node->right = parent; + } + else + { + TRACE (psplay, "..ZAG"); + if (r < PSPLAY_PROB_ZIGZAG) + { + TRACE (psplay, "BREAK"); + return; + } + + parent->right = node->left; + node->left = parent; + + grandparent->left = node->right; + node->right = grandparent; + } + } + else + { + TRACE (psplay, "ZAG.."); + if (node == parent->left) + { + TRACE (psplay, "..ZIG"); + if (r < PSPLAY_PROB_ZIGZAG) + { + TRACE (psplay, "BREAK"); + return; + } + + parent->left = node->right; + node->right = parent; + + grandparent->right = node->left; + node->left = grandparent; + } + else + { + TRACE (psplay, "..ZAG"); + if (r < PSPLAY_PROB_ZIGZIG) + { + TRACE (psplay, "BREAK"); + return; + } + + grandparent->right = parent->left; + parent->left = grandparent; + + parent->right = node->left; + node->left = parent; + } + } + *trail->trail [trailidx (depth-2)] = node; + } +} + + +PSplaynode +psplay_insert (PSplay self, PSplaynode node, int splayfactor) +{ + TRACE (psplay); + PSplaynode n = self->tree; + struct psplaytrail trail; + + trail.dir = 0; + trail.depth = 0; + trail.trail [0] = &self->tree; + + if (!n) + self->tree = node; + else + { + while (n != node) + { + int c; + c = self->cmp (self->key (node), self->key (n)); + ++trail.depth; + + if (c < 0) + { + --trail.dir; + if (!n->left) + n->left = node; + trail.trail [trailidx (trail.depth)] = &n->left; + n = n->left; + } + else if (c > 0) + { + ++trail.dir; + if (!n->right) + n->right = node; + trail.trail [trailidx (trail.depth)] = &n->right; + n = n->right; + } + else + { + TODO ("policy for multiple entered items (before, after, fail, replace)"); + return NULL; + } + } + } + ++self->elem_cnt; + if (self->elem_cnt >= 1<log2) ++self->log2; + if (splayfactor && trail.depth > 2) + psplay_splay (self, &trail, splayfactor); + return node; +} + + + +PSplaynode +psplay_find (PSplay self, const void* key, int splayfactor) +{ + TRACE (psplay); + PSplaynode node = self->tree; + struct psplaytrail trail; + trail.dir = 0; + trail.depth = 0; + trail.trail [0] = &self->tree; + + while (node) + { + int c; + c = self->cmp (key, self->key (node)); + ++trail.depth; + + if (c < 0) + { + --trail.dir; + trail.trail [trailidx (trail.depth)] = &node->left; + node = node->left; + } + else if (c > 0) + { + ++trail.dir; + trail.trail [trailidx (trail.depth)] = &node->right; + node = node->right; + } + else + { + self->found_parent = trail.trail [trailidx (--trail.depth)]; + break; + } + } + if (node && splayfactor && trail.depth > 2) + psplay_splay (self, &trail, splayfactor); + return node; +} + + +PSplaynode +psplay_remove (PSplay self, PSplaynode node) +{ + TRACE (psplay); + if (!node) return NULL; + + PSplaynode* r = self->found_parent; + + while (*r != node) + { + if (!psplay_find (self, self->key (node), 0)) + { + WARN (psplay, "node %p is not in splay tree %p", node, self); + return NULL; + } + r = self->found_parent; + } + + if (!node->left) + *r = node->right; + else if (!node->right) + *r = node->left; + else + { + PSplaynode i, iparent = NULL; + if (psplay_fast_prng()&1) /* 50% probability removing left or right wards */ + { + for (i = node->left; i->right; iparent = i, i = i->right); + if (iparent) + iparent->right = i->left; + if (node->left != i) + i->left = node->left; + i->right = node->right; + } + else + { + for (i = node->right; i->left; iparent = i, i = i->left); + if (iparent) + iparent->left = i->right; + if (node->right != i) + i->right = node->right; + i->left = node->left; + } + *r = i; + } + --self->elem_cnt; + if (self->elem_cnt < 1<log2) --self->log2; + return node; +} + + +PSplaynode +psplay_remove_key (PSplay self, void* key) +{ + return psplay_remove (self, psplay_find (self, key, 0)); +} + + +void +psplay_delete_node (PSplay self, PSplaynode node) +{ + if (node) + self->delete (psplay_remove (self, node)); +} + + +void +psplay_delete_key (PSplay self, void* key) +{ + PSplaynode node = psplay_find (self, key, 0); + psplay_delete_node (self, node); +} + + + +const psplay_delete_fn PSPLAY_CONT = (psplay_delete_fn)0x0; +const psplay_delete_fn PSPLAY_STOP = (psplay_delete_fn)0x1; +const psplay_delete_fn PSPLAY_REMOVE = (psplay_delete_fn)0x2; + +static int +psplay_handle (PSplay self, PSplaynode node, psplay_delete_fn res) +{ + if (res == PSPLAY_CONT) + return 1; + + if (res == PSPLAY_STOP) + ; + else if (res == PSPLAY_REMOVE) + { + psplay_remove (self, node); + if (self->delete) + self->delete (node); + } + else + { + psplay_remove (self, node); + res (node); + } + return 0; +} + + +int +psplay_walk (PSplay self, PSplaynode node, psplay_action_fn action, int level, void* data) +{ + if (!self->tree) + return 1; + + if (!node) + node = self->tree; + + psplay_delete_fn res; + + res = action (node, PSPLAY_PREORDER, level, data); + if (!psplay_handle (self, node, res)) + return 0; + + if (node->left) + if (!psplay_walk (self, node->left, action, level+1, data)) + return 0; + + res = action (node, PSPLAY_INORDER, level, data); + if (!psplay_handle (self, node, res)) + return 0; + + if (node->right) + if (!psplay_walk (self, node->right, action, level+1, data)) + return 0; + + res = action (node, PSPLAY_POSTORDER, level, data); + if (!psplay_handle (self, node, res)) + return 0; + + return 1; +} + + +static psplay_delete_fn +psplay_print_node (PSplaynode node, const enum psplay_order_enum which, int level, void* data) +{ + FILE* fh = data; + static char* sp = " "; + if (level>40) + { + if (which == PSPLAY_PREORDER) + fprintf (fh, "%s ...\n", sp); + return PSPLAY_CONT; + } + + switch (which) + { + case PSPLAY_PREORDER: + fprintf (fh, "%s%p\n", sp+40-level, node); + if (node->left) + fprintf (fh, "%sleft %p\n", sp+40-level, node->left); + break; + case PSPLAY_INORDER: + if (node->right) + fprintf (fh, "%sright %p\n", sp+40-level, node->right); + break; + case PSPLAY_POSTORDER: + break; + } + + return PSPLAY_CONT; +} + +void +psplay_dump (PSplay self, FILE* dest) +{ + fprintf (dest, "root %p\n", self->tree); + psplay_walk (self, NULL, psplay_print_node, 0, dest); +} + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/psplay.h b/src/lib/psplay.h new file mode 100644 index 000000000..26639910a --- /dev/null +++ b/src/lib/psplay.h @@ -0,0 +1,292 @@ +/* + psplay.h - probabilistic splay tree + + Copyright (C) + 2004, 2005, 2006, Christian Thaeter + Copyright (C) Lumiera.org + 2007, 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 PSPLAY_H +#define PSPLAY_H + +#include +#include + +/** + * @file + * Probabilistic splay trees + * A splay trees is self-optimizing (in contrast to self-balancing) datastructure. + * We introduce here a probabilistic bottom up approach which reduces the splay costs. + * Without affecting the performance. The randomization gives also some insurance that + * worst case situations are extremely unlikely. + * Tree nodes are very small (just 2 pointers) and are intrusively placed into the users + * datastructure. + */ + + +/** + * Type and handle for a psplay tree node + * This node have to be placed inside users data. + */ +typedef struct psplaynode_struct psplaynode; +typedef psplaynode* PSplaynode; +struct psplaynode_struct +{ + PSplaynode left; + PSplaynode right; +}; + + +/** + * Function use to compare keys + * @param a first key + * @param b second key + * @return shall return -1/0/1 when a is less than/equal to/biggier than b. + */ +typedef int (*psplay_cmp_fn)(const void* a, const void* b); + + +/** + * Destructor for user defined data + * Called when an element got removed from a splay tree. + * @param node pointer to the intrusive node inside the users datastructure + * The user is responsible for casting 'node' back to his real datastructure (maybe with OFFSET_OF()), + * free all resources associated with it and finally free the datastructure itself. + */ +typedef void (*psplay_delete_fn)(PSplaynode node); + + +/** + * Retrieve the key from a user datastructure + * @param node pointer to the intrusive node inside the users datastructure + * This functiom must return a pointer to the key under which the user stores his data. + */ +typedef const void* (*psplay_key_fn)(const PSplaynode node); + + +/** + * Type and handle for a psplay root structure + * This structure shall be treated opaque, its only defined in the header to allow + * one to integrate it directly instead referencing it. + */ +typedef struct psplay_struct psplay; +typedef psplay* PSplay; + +struct psplay_struct +{ + PSplaynode tree; /* the tree */ + PSplaynode* found_parent; /* maybe direct parent of last found node, used for fast remove */ + psplay_cmp_fn cmp; + psplay_key_fn key; + psplay_delete_fn delete; + + size_t elem_cnt; + unsigned log2; /* roughly log2 of the elem_cnt*/ +}; + + +/** + * Number of elements in tree + * @param self pointer to the tree + * @return number of elements + */ +static inline size_t +psplay_nelements (PSplay self) +{ + return self->elem_cnt; +}; + + +/** + * Initialize a splay tree + * @param self pointer to the psplay structure + * @param cmp user supplied compare function + * @param key user supplied function to retrieve a key + * @param delete user supplied destructor function or NULL if no destructor is necessary + * @return self + */ +PSplay +psplay_init (PSplay self, psplay_cmp_fn cmp, psplay_key_fn key, psplay_delete_fn delete); + + +/** + * Destroy a splay tree + * Frees all elements and associated resources of a splay tree + * @param self pointer to the psplay structure + * @return self + */ +PSplay +psplay_destroy (PSplay self); + +/** + * Allocate a splay tree + * @param cmp user supplied compare function + * @param key user supplied function to retrieve a key + * @param delete user supplied destructor function or NULL if no destructor is necessary + * @return allcoated splay tree or NULL on error + */ +PSplay +psplay_new (psplay_cmp_fn cmp, psplay_key_fn key, psplay_delete_fn delete); + + +/** + * Delete a splay tree + * Frees all elements and associated resources of a splay tree and then itseld + * @param self pointer to the psplay structure + */ +void +psplay_delete (PSplay self); + + +/** + * Initialize a splay tree node + * The user has to place this nodes within his datastructure and must + * initialize them before using them. + * @param self pointer to the node to be initialized + * @return self + */ +PSplaynode +psplaynode_init (PSplaynode self); + + +/** + * Insert a element into a splay tree + * @param self pointer to the splay tree + * @param node pointer to the node to be inserted + * @param splayfactor weight for the probabilistic splaying, + * 0 disables the splaying, 100 is the expected normal value + * use 100 when in doubt + * @return self or NULL when a node with same key already in the tree + */ +PSplaynode +psplay_insert (PSplay self, PSplaynode node, int splayfactor); + + +/** + * Find a element in a splay tree + * @param self pointer to the splay tree + * @param key pointer to the key to be searched + * @param splayfactor weight for the probabilistic splaying, + * 0 disables the splaying, 100 is the expected normal value + * @return found node or NULL if the key was not found in the tree + */ +PSplaynode +psplay_find (PSplay self, const void* key, int splayfactor); + + +/** + * Remove a node from a splay tree + * @param self pointer to the splay tree + * @param node node to be removed + * @return pointer to the removed node + * removal is optimized for the case where one call it immediately after one did a + * psplay_find() as last operation on that tree + */ +PSplaynode +psplay_remove (PSplay self, PSplaynode node); + + +/** + * Remove a node by key from a splay tree + * @param self pointer to the splay tree + * @param key key of the node to be removed + * @return pointer to the removed node + */ +PSplaynode +psplay_remove_key (PSplay self, void* key); + + +/** + * Delete a node from a splay tree + * @param self pointer to the splay tree + * @param node node to be removed + * Calls the registered delete handler, frees all resources. + */ +void +psplay_delete_node (PSplay self, PSplaynode node); + + +/** + * Delete a node by key from a splay tree + * @param self pointer to the splay tree + * @param key key of the node to be removed + * Calls the registered delete handler, frees all resources. + */ +void +psplay_delete_key (PSplay self, void* key); + + +enum psplay_order_enum + { + PSPLAY_PREORDER, + PSPLAY_INORDER, + PSPLAY_POSTORDER + }; + +/** + * Traverse a splay tree + * Traversing a tree calls a user supplied action three times + * An 'action' must not alter the tree itself but it can indicate aborting the tree traversal and + * how the current node is handled by its return value. + * @param node pointer to the currently traversed node + * @param which state of the traversal: + * PSPLAY_PREORDER before visiting the left subtree, + * PSPLAY_INORDER after visiting the left subtree and before the right subtree + * PSPLAY_POSTORDER finally after visiting the right subtree. + * Example: For to traverse the tree in order action would only handle PSPLAY_INORDER. + * This action shall return PSPLAY_CONT when the traversal of the tree shall continue. + * @param level depth of the node in the tree + * @param data user supplied data which is transparently passed around + * @return a pointer to a function which indicates how to proceed, there are three special + * return values predefined: + * PSPLAY_CONT - continue with the traversal + * PSPLAY_STOP - stop the traversal + * PSPLAY_REMOVE - stops the traversal and removes the current node, calling the delete handler + * any other psplay_delete_fn - stops the traversal and removes the current node, calling the returned delete handler with it + */ +typedef psplay_delete_fn (*psplay_action_fn)(PSplaynode node, const enum psplay_order_enum which, int level, void* data); + +extern const psplay_delete_fn PSPLAY_CONT; +extern const psplay_delete_fn PSPLAY_STOP; +extern const psplay_delete_fn PSPLAY_REMOVE; + +/** + * Start a tree traversal + * @param self the tree to be traversed + * @param node pointer to root node where traversal shall start, use NULL for the whole tree + * @param action handler function as defined above + * @param level initial value for the level + * @param data user supplied data which is transparently passed to the action + * @return 0 when the tree traversal got aborted (by anything but PSPLAY_CONT as action handler return) + * 1 when the whole tree was traversed successfully + */ +int +psplay_walk (PSplay self, PSplaynode node, psplay_action_fn action, int level, void* data); + + +void +psplay_dump (PSplay self, FILE* dest); + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/references.c b/src/lib/references.c deleted file mode 100644 index 925258377..000000000 --- a/src/lib/references.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - references.c - strong and weak references - - 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/references.h" -#include "lib/safeclib.h" - -/** - * @file - * Strong and Weak references - * Strong references keep some object alive while they existing - * Weak references become invalidated when the referenced object gets destroyed - * - * Thread safety: references protect their internal operations with a mutex (optimization using futex may come in future) - * while operations on the reference itself (initialization, destruction, strengthen, weaken) and on the referenced object (get) - * should be protected from elsewhere. - */ - - -/** - * Construct an initial strong reference from an object. - * For every object which should be managed with references you need to construct an initial strong reference - * which then will be used to initialize all further references. - * @param self pointer to the reference to be initialized - * @param obj pointer to the object being referenced - * @param dtor destructor function which will be called on obj when the last strong reference gets deleted - * @return self as given - */ -LumieraReference -lumiera_reference_strong_init_once (LumieraReference self, void* obj, void (*dtor)(void*)) -{ - LumieraReftarget target = lumiera_malloc (sizeof(lumiera_reftarget)); - - target->object = obj; - target->dtor = dtor; - target->strong_cnt = 1; - target->weak_cnt = 0; - lumiera_mutex_init (&target->lock); - - self->object = obj; - self->target = target; - return self; -} - -/** - * Destroy a reference. - * All references need to be destroyed when not used any more. - * When the last strong reference gets destroyed, the object's destructor is called. - * Remaining weak references stay invalidated then until they get destroyed too. - * @param self reference to be destroyed - * @return self as given - * destroying a reference is not thread safe as far as 2 threads try to concurrently destroy it! - */ -LumieraReference -lumiera_reference_destroy (LumieraReference self) -{ - LumieraReftarget target = self->target; - - /* defensive, lets detect errors if anything still tries to use this reference */ - self->target = NULL; - - lumiera_mutexacquirer lock; - lumiera_mutexacquirer_init_mutex (&lock, &target->lock, LUMIERA_LOCKED); - - if (self->object) - { - /* strong reference */ - if (!--target->strong_cnt) - { - /* was last strong reference */ - if (target->dtor) - target->dtor (target->object); - target->object = NULL; - if (!target->weak_cnt) - { - /* no weak refs either, destroy it */ - lumiera_mutexacquirer_unlock (&lock); - lumiera_mutex_destroy (&target->lock); - free (target); - return self; - } - } - } - else - { - /* weak reference */ - if (!--target->weak_cnt && !target->strong_cnt) - { - /* was last weak reference, and no strong refs left */ - lumiera_mutexacquirer_unlock (&lock); - lumiera_mutex_destroy (&target->lock); - free (target); - return self; - } - } - lumiera_mutexacquirer_unlock (&lock); - return self; -} - -/** - * Copy construct a reference as strong reference - * @param source reference to copy - * @return self as strong reference (always for strong references) or NULL if source is an invalidated weak reference, - * in the later case the reference is constructed as weak reference barely to allow it be destroyed - */ -LumieraReference -lumiera_reference_strong_init (LumieraReference self, LumieraReference source) -{ - lumiera_mutexacquirer lock; - lumiera_mutexacquirer_init_mutex (&lock, &source->target->lock, LUMIERA_LOCKED); - - self->object = source->target->object; - self->target = source->target; - - if (self->object) - { - ++self->target->strong_cnt; - } - else - { - ++self->target->weak_cnt; - self = NULL; - } - lumiera_mutexacquirer_unlock (&lock); - return self; -} - -/** - * Copy construct a reference as weak reference - * @param source reference to copy - * @return self (always for strong references) or NULL if self is an invalidated weak reference - */ -LumieraReference -lumiera_reference_weak_init (LumieraReference self, LumieraReference source) -{ - lumiera_mutexacquirer lock; - lumiera_mutexacquirer_init_mutex (&lock, &source->target->lock, LUMIERA_LOCKED); - - self->object = NULL; - self->target = source->target; - - ++self->target->weak_cnt; - if (!self->target->object) - /* already invalidated */ - self = NULL; - - lumiera_mutexacquirer_unlock (&lock); - return self; -} - -/** - * turn a (strong) reference into a weak reference - * Weaken a reference may remove its last strong reference and thus destroy the object - * do nothing if the referene is already weak - * @return self or NULL if the final strong reference got removed, - */ -LumieraReference -lumiera_reference_weaken (restrict LumieraReference self) -{ - /* is this a strong reference? */ - if (self->object) - { - lumiera_mutexacquirer lock; - lumiera_mutexacquirer_init_mutex (&lock, &self->target->lock, LUMIERA_LOCKED); - - self->object = NULL; - ++self->target->weak_cnt; - if (!--self->target->strong_cnt) - { - if (self->target->dtor) - self->target->dtor (self->target->object); - self->target->object = NULL; - self = NULL; - } - lumiera_mutexacquirer_unlock (&lock); - } - return self; -} - - -/** - * turn a (weak) reference into a strong reference - * only references of object which are not already destroyed can be strengthened - * @return self when successful, NULL when the object was already destroyed, 'self' stays a dead weak reference in that case - */ -LumieraReference -lumiera_reference_strengthen (LumieraReference self) -{ - /* is this a weak reference? */ - if (!self->object) - { - lumiera_mutexacquirer lock; - lumiera_mutexacquirer_init_mutex (&lock, &self->target->lock, LUMIERA_LOCKED); - - if (self->target->object) - { - self->object = self->target->object; - --self->target->weak_cnt; - ++self->target->strong_cnt; - } - else - self = NULL; - lumiera_mutexacquirer_unlock (&lock); - } - return self; -} - diff --git a/src/lib/references.h b/src/lib/references.h deleted file mode 100644 index 6ad65d62f..000000000 --- a/src/lib/references.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - references.h - strong and weak references - - 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_REFERENCES_H -#define LUMIERA_REFERENCES_H - -#include - -/** - * @file - * Strong and Weak references, header. - */ - - -typedef struct lumiera_reference_struct lumiera_reference; -typedef lumiera_reference* LumieraReference; - -#include "lib/error.h" -#include "lib/mutex.h" - -/* Implementation detail */ -struct lumiera_reftarget_struct -{ - void* object; - void (*dtor)(void*); - unsigned strong_cnt; /*when strong becomes 0 obj is destroyed, if weak is 0 destroy target too*/ - unsigned weak_cnt; /*when weak becomes 0 and !obj and lock strong is 0, destroy target */ - lumiera_mutex lock; -}; -typedef struct lumiera_reftarget_struct lumiera_reftarget; -typedef lumiera_reftarget* LumieraReftarget; - - -/** - * A reference pointing to some other object - */ -struct lumiera_reference_struct -{ - void* object; /*set for strong, NULL for weak*/ - LumieraReftarget target; -}; - -#define lumiera_reference \ -lumiera_reference NOBUG_CLEANUP(lumiera_reference_ensuredestroyed) - -/* helper function for nobug */ -static inline void -lumiera_reference_ensuredestroyed (LumieraReference self) -{ - ENSURE (!self->target, "forgot to destroy reference"); -} - - -LumieraReference -lumiera_reference_strong_init_once (LumieraReference self, void* obj, void (*dtor)(void*)); - - -LumieraReference -lumiera_reference_destroy (LumieraReference self); - -/** - * Get object from a strong reference. - * @return pointer to object, NULL if applied to a weak reference - */ -static inline void* -lumiera_reference_get (LumieraReference self) -{ - ENSURE (self->target, "illegal reference (not initialized or already destroyed?)"); - return self->object; -} - - -LumieraReference -lumiera_reference_strong_init (LumieraReference self, LumieraReference source); - - -LumieraReference -lumiera_reference_weak_init (LumieraReference self, LumieraReference source); - - -LumieraReference -lumiera_reference_weaken (restrict LumieraReference self); - - -LumieraReference -lumiera_reference_strengthen (LumieraReference self); - -#endif diff --git a/src/lib/rwlock.c b/src/lib/rwlock.c index 077ff9766..c5287d535 100644 --- a/src/lib/rwlock.c +++ b/src/lib/rwlock.c @@ -18,161 +18,52 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include + +#include "lib/error.h" #include "lib/rwlock.h" + 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"); +LUMIERA_ERROR_DEFINE(RWLOCK_DESTROY, "destroying rwlock"); +LUMIERA_ERROR_DEFINE(RWLOCK_UNLOCK, "unlock rwlock failed"); +LUMIERA_ERROR_DEFINE(RWLOCK_RDLOCK, "locking rwlock for reading failed"); +LUMIERA_ERROR_DEFINE(RWLOCK_WRLOCK, "locking rwlock for writing failed"); /** * @file * Read/write locks. */ - -/** - * Initialize a rwlock - * @param self is a pointer to the rwlock to be initialized - * @return self as given - */ LumieraRWLock -lumiera_rwlock_init (LumieraRWLock self) +lumiera_rwlock_init (LumieraRWLock self, const char* purpose, struct nobug_flag* flag) { if (self) { pthread_rwlock_init (&self->rwlock, NULL); + NOBUG_RESOURCE_HANDLE_INIT (self->rh); + NOBUG_RESOURCE_ANNOUNCE_RAW (flag, "rwlock", purpose, self, self->rh); } return self; } -/** - * destroy a rwlock - * @param self is a pointer to the rwlock to be initialized - * @return self on success or NULL at error - */ + LumieraRWLock -lumiera_rwlock_destroy (LumieraRWLock self) +lumiera_rwlock_destroy (LumieraRWLock self, struct nobug_flag* flag) { if (self) { + NOBUG_RESOURCE_FORGET_RAW (flag, self->rh); if (pthread_rwlock_destroy (&self->rwlock)) LUMIERA_DIE (RWLOCK_DESTROY); } return self; } - - - -/** - * initialize a rwlockacquirer state - * @param self rwlockacquirer to be initialized, must be an automatic variable - * @param rwlock associated rwlock - * @param state initial state of the mutex, either LUMIERA_RDLOCKED, LUMIERA_WRLOCKED or LUMIERA_UNLOCKED - * @return self as given or NULL on error - */ -LumieraRWLockacquirer -lumiera_rwlockacquirer_init (LumieraRWLockacquirer self, LumieraRWLock rwlock, enum lumiera_lockstate state) -{ - REQUIRE (self); - REQUIRE (rwlock); - REQUIRE (state != LUMIERA_LOCKED, "illegal state for rwlock"); - self->rwlock = rwlock; - self->state = state; - - switch (state) - { - case LUMIERA_RDLOCKED: - switch (pthread_rwlock_rdlock (&rwlock->rwlock)) - { - case 0: - break; - case EAGAIN: - lumiera_error_set (LUMIERA_ERROR_RWLOCK_AGAIN); - return NULL; - case EDEADLK: - lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK); - return NULL; - default: - LUMIERA_DIE (RWLOCK_RLOCK); - } - case LUMIERA_WRLOCKED: - switch (pthread_rwlock_wrlock (&rwlock->rwlock)) - { - case 0: - break; - case EDEADLK: - lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK); - return NULL; - default: - LUMIERA_DIE (RWLOCK_WLOCK); - } - default: - break; - } - return self; -} - - -/** - * readlock the rwlock. - * must not already be locked - * @param self rwlockacquirer associated with a rwlock - * @return self as given or NULL on error - */ -LumieraRWLockacquirer -lumiera_rwlockacquirer_rdlock (LumieraRWLockacquirer self) -{ - REQUIRE (self); - REQUIRE (self->state == LUMIERA_UNLOCKED, "rwlock already locked"); - - switch (pthread_rwlock_rdlock (&self->rwlock->rwlock)) - { - case 0: - break; - case EAGAIN: - lumiera_error_set (LUMIERA_ERROR_RWLOCK_AGAIN); - return NULL; - case EDEADLK: - lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK); - return NULL; - default: - LUMIERA_DIE (RWLOCK_RLOCK); - } - - self->state = LUMIERA_RDLOCKED; - return self; -} - - -/** - * writelock the rwlock. - * must not already be locked - * @param self rwlockacquirer associated with a rwlock - * @return self as given or NULL on error - */ -LumieraRWLockacquirer -lumiera_rwlockacquirer_wrlock (LumieraRWLockacquirer self) -{ - REQUIRE (self); - REQUIRE (self->state == LUMIERA_UNLOCKED, "rwlock already locked"); - - switch (pthread_rwlock_wrlock (&self->rwlock->rwlock)) - { - case 0: - break; - case EDEADLK: - lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK); - return NULL; - default: - LUMIERA_DIE (RWLOCK_WLOCK); - } - - self->state = LUMIERA_WRLOCKED; - return self; -} - +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/rwlock.h b/src/lib/rwlock.h index d58627dda..23376979b 100644 --- a/src/lib/rwlock.h +++ b/src/lib/rwlock.h @@ -29,14 +29,12 @@ #include #include -#include "lib/locking.h" - 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); +LUMIERA_ERROR_DECLARE(RWLOCK_RDLOCK); +LUMIERA_ERROR_DECLARE(RWLOCK_WRLOCK); /** * @file @@ -44,6 +42,56 @@ LUMIERA_ERROR_DECLARE(RWLOCK_WLOCK); */ +/** + * Read locked section. + */ +#define LUMIERA_RDLOCK_SECTION(nobugflag, rwlck) \ + for (lumiera_rwlockacquirer NOBUG_CLEANUP(lumiera_rwlockacquirer_ensureunlocked) lumiera_rwlock_section_ = {(LumieraRWLock)1}; \ + lumiera_rwlock_section_.rwlock;) \ + for ( \ + ({ \ + lumiera_rwlock_section_.rwlock = (rwlck); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_rwlock_section_.rh); \ + RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire rwlock for reading", &lumiera_rwlock_section_, \ + NOBUG_RESOURCE_EXCLUSIVE, lumiera_rwlock_section_.rh); \ + if (pthread_rwlock_rdlock (&(rwlck)->rwlock)) LUMIERA_DIE (RWLOCK_RDLOCK); \ + }); \ + lumiera_rwlock_section_.rwlock; \ + ({ \ + if (lumiera_rwlock_section_.rwlock) \ + { \ + pthread_rwlock_unlock (&lumiera_rwlock_section_.rwlock->rwlock); \ + lumiera_rwlock_section_.rwlock = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_rwlock_section_.rh); \ + } \ + })) + + +/** + * Write locked section. + */ +#define LUMIERA_WRLOCK_SECTION(nobugflag, rwlck) \ + for (lumiera_rwlockacquirer NOBUG_CLEANUP(lumiera_rwlockacquirer_ensureunlocked) lumiera_rwlock_section_ = {(LumieraRWLock)1}; \ + lumiera_rwlock_section_.rwlock;) \ + for ( \ + ({ \ + lumiera_rwlock_section_.rwlock = (rwlck); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_rwlock_section_.rh); \ + RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire rwlock for reading", &lumiera_rwlock_section_, \ + NOBUG_RESOURCE_EXCLUSIVE, lumiera_rwlock_section_.rh); \ + if (pthread_rwlock_wrlock (&(rwlck)->rwlock)) LUMIERA_DIE (RWLOCK_WRLOCK); \ + }); \ + lumiera_rwlock_section_.rwlock; \ + ({ \ + if (lumiera_rwlock_section_.rwlock) \ + { \ + pthread_rwlock_unlock (&lumiera_rwlock_section_.rwlock->rwlock); \ + lumiera_rwlock_section_.rwlock = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_rwlock_section_.rh); \ + } \ + })) + + /** * RWLock. * @@ -51,17 +99,27 @@ LUMIERA_ERROR_DECLARE(RWLOCK_WLOCK); struct lumiera_rwlock_struct { pthread_rwlock_t rwlock; + RESOURCE_HANDLE (rh); }; typedef struct lumiera_rwlock_struct lumiera_rwlock; typedef lumiera_rwlock* LumieraRWLock; - +/** + * Initialize a rwlock + * @param self is a pointer to the rwlock to be initialized + * @return self as given + */ LumieraRWLock -lumiera_rwlock_init (LumieraRWLock self); - +lumiera_rwlock_init (LumieraRWLock self, const char* purpose, struct nobug_flag* flag); +/** + * destroy a rwlock + * @param self is a pointer to the rwlock to be initialized + * @return self on success or NULL at error + */ LumieraRWLock -lumiera_rwlock_destroy (LumieraRWLock self); +lumiera_rwlock_destroy (LumieraRWLock self, struct nobug_flag* flag); + @@ -72,7 +130,7 @@ lumiera_rwlock_destroy (LumieraRWLock self); struct lumiera_rwlockacquirer_struct { LumieraRWLock rwlock; - enum lumiera_lockstate state; + RESOURCE_HANDLE (rh); }; typedef struct lumiera_rwlockacquirer_struct lumiera_rwlockacquirer; typedef struct lumiera_rwlockacquirer_struct* LumieraRWLockacquirer; @@ -81,39 +139,15 @@ typedef struct lumiera_rwlockacquirer_struct* LumieraRWLockacquirer; static inline void lumiera_rwlockacquirer_ensureunlocked (LumieraRWLockacquirer self) { - ENSURE (self->state == LUMIERA_UNLOCKED, "forgot to unlock the rwlock mutex"); -} - -/* override with a macro to use the cleanup checker */ -#define lumiera_rwlockacquirer \ -lumiera_rwlockacquirer NOBUG_CLEANUP(lumiera_rwlockacquirer_ensureunlocked) - - -LumieraRWLockacquirer -lumiera_rwlockacquirer_init (LumieraRWLockacquirer self, LumieraRWLock rwlock, enum lumiera_lockstate state); - -LumieraRWLockacquirer -lumiera_rwlockacquirer_rdlock (LumieraRWLockacquirer self); - - -LumieraRWLockacquirer -lumiera_rwlockacquirer_wrlock (LumieraRWLockacquirer self); - - -/** - * release rwlock. - * a rwlockacquirer must be unlocked before leaving scope - * @param self rwlockacquirer associated with a rwlock variable - */ -static inline void -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 (RWLOCK_UNLOCK); - self->state = LUMIERA_UNLOCKED; + ENSURE (!self->rwlock, "forgot to unlock rwlock"); } #endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/safeclib.c b/src/lib/safeclib.c index f500ea71c..1c5cf28c6 100644 --- a/src/lib/safeclib.c +++ b/src/lib/safeclib.c @@ -18,27 +18,19 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "error.h" +#include "lib/error.h" +#include "lib/safeclib.h" #include #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 size) { @@ -49,13 +41,6 @@ lumiera_malloc (size_t size) } -/** - * Allocate cleared memory for an array. - * always succeeds or dies - * @param n number of elements - * @param size memory to be allocated - * @return pointer to the allocated memory - */ void* lumiera_calloc (size_t n, size_t size) { @@ -66,13 +51,6 @@ lumiera_calloc (size_t n, size_t size) } -/** - * 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) { @@ -88,14 +66,6 @@ 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) { @@ -103,12 +73,6 @@ 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) { @@ -116,11 +80,6 @@ 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. - */ struct lumiera_tmpbuf_struct { void* buffers[64]; @@ -144,13 +103,10 @@ static void lumiera_tmpbuf_init (void) { pthread_key_create (&lumiera_tmpbuf_tls_key, lumiera_tmpbuf_destroy); + TODO ("register an atexit() handler to free tmpbufs"); } -/** - * free all buffers associated with this thread. - * This function is called automatically, usually one doesnt need to call it. - */ void lumiera_tmpbuf_freeall (void) { @@ -160,17 +116,12 @@ lumiera_tmpbuf_freeall (void) { pthread_setspecific (lumiera_tmpbuf_tls_key, NULL); for (int idx = 0; idx < 64; ++idx) - free (buf->buffers[idx]); - free (buf); + lumiera_free (buf->buffers[idx]); + lumiera_free (buf); } } -/** - * Query a thread local buffer. - * @param size minimal needed size for the buffer - * @return the buffer - */ void* lumiera_tmpbuf_provide (size_t size) { @@ -184,7 +135,7 @@ lumiera_tmpbuf_provide (size_t size) if (buf->sizes[buf->idx] < size || buf->sizes[buf->idx] > 8*size) { - free (buf->buffers[buf->idx]); + 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]); } @@ -223,3 +174,49 @@ lumiera_tmpbuf_snprintf (size_t size, const char* fmt, ...) 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 6fc532239..79f10d024 100644 --- a/src/lib/safeclib.h +++ b/src/lib/safeclib.h @@ -50,6 +50,18 @@ void* lumiera_calloc (size_t n, size_t size); +/** + * Free previously allocated memory. + * @param mem pointer to the memory block obtained by lumiera_malloc or lumiera_calloc + */ +static inline void +lumiera_free (void* mem) +{ + /* for now only a alias, might change in future */ + free (mem); +} + + /** * Duplicate a C string. * always succeeds or dies @@ -123,3 +135,33 @@ lumiera_tmpbuf_strndup (const char* src, size_t size); 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); + diff --git a/src/lib/uuid.c b/src/lib/uuid.c deleted file mode 100644 index d4eee69be..000000000 --- a/src/lib/uuid.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - 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 deleted file mode 100644 index d4cf8ce9d..000000000 --- a/src/lib/uuid.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - 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/src/proc/asset/category.cpp b/src/proc/asset/category.cpp index dcb767ccf..3d178e549 100644 --- a/src/proc/asset/category.cpp +++ b/src/proc/asset/category.cpp @@ -38,7 +38,9 @@ namespace asset */ Category::operator string () const { - char *kinds[6] = { "AUDIO" + typedef const char * const SymID; + + SymID kinds[6] = { "AUDIO" , "VIDEO" , "EFFECT" , "CODEC" diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index 64f84ab32..73925e11a 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -53,6 +53,7 @@ namespace mobject using boost::lambda::var; namespace // Implementation details //////////////////TODO better a named implementation namespace (avoids warnings on gcc 4.3) +//////////////////////////////////////////////////////////FIXME this is a *real* problem, because this namespace create storage, which it shouldn't { struct TableEntry { diff --git a/src/tool/Makefile.am b/src/tool/Makefile.am new file mode 100644 index 000000000..69c4a7f25 --- /dev/null +++ b/src/tool/Makefile.am @@ -0,0 +1,28 @@ +# 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. + +lumitool_srcdir = $(top_srcdir)/src/tool + +noinst_PROGRAMS += luidgen + +luidgen_CFLAGS = $(CFLAGS) -std=gnu99 -Wall -Werror +luidgen_CPPFLAGS = -I$(top_srcdir)/src/ +luidgen_SOURCES = \ + $(lumitool_srcdir)/luidgen.c + +luidgen_LDADD = liblumi.a + diff --git a/tests/locking/test-locking.c b/src/tool/luidgen.c similarity index 60% rename from tests/locking/test-locking.c rename to src/tool/luidgen.c index b37c2c07f..41081ca97 100644 --- a/tests/locking/test-locking.c +++ b/src/tool/luidgen.c @@ -1,8 +1,8 @@ /* - test-locking.c - test locking functions + luidgen.c - generate a lumiera uuid Copyright (C) Lumiera.org - 2008, Christian Thaeter + 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 @@ -19,23 +19,33 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "lib/luid.h" + #include -#include -#include "tests/test.h" -int conditionforgotunlock (); -int mutexforgotunlock (); +/** + * @file + * Generate amd print a Lumiera uid as octal escaped string + */ -TESTS_BEGIN - -TEST ("conditionforgotunlock") +int +main (int argc, char** argv) { - return conditionforgotunlock (); + lumiera_uid luid; + lumiera_uid_gen (&luid); + + printf ("\""); + for (int i = 0; i < 16; ++i) + printf ("\\%.3hho", *(((char*)&luid)+i)); + printf ("\"\n"); + + return 0; } -TEST ("mutexforgotunlock") -{ - return mutexforgotunlock (); -} - -TESTS_END +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/tests/15list.tests b/tests/15list.tests index 88aef3c49..e04044f27 100644 --- a/tests/15list.tests +++ b/tests/15list.tests @@ -44,9 +44,11 @@ out: . out: . END +TEST "llist_relocate" relocate < ,tmp_testfile1 +echo testdata > ,tmp_testfile2 +echo testdata > ,tmp_testfile3 + +TEST "acquire 3 files" acquire_existing_3files <line = ' #comment bla' +END + +TEST "check content of configitem with section" configitem_simple_content_check $'[ key.foo suffix.bar ] ' << END +out: item->line = '[ key.foo suffix.bar ] ' +out: item->key_size = '7' +out: item->key = 'key.foo suffix.bar ] ' +out: item->delim = ' suffix.bar ] ' +END + +TEST "check content of configitem with directive (without argument)" configitem_simple_content_check $'\t @directive ' << END +out: item->line = ' @directive ' +out: item->key_size = '9' +out: item->key = '@directive ' +END + +TEST "check content of configitem with directive (with argument)" configitem_simple_content_check $'\t @directive \targument' << END +out: item->line = ' @directive argument' +out: item->key_size = '9' +out: item->key = '@directive argument' +out: item->delim = ' argument' +END + +TEST "check content of configitem with configentry" configitem_simple_content_check $' \t\t key.foo \t\t=\tbar' << END +out: item->line = ' key.foo = bar' +out: item->key_size = '7' +out: item->key = 'key.foo = bar' +out: item->delim = '= bar' +END + +TEST "check content of configitem with configentry (redirect)" configitem_simple_content_check $' \t\t key.foo \t\t<\tkey.bar' << END +out: item->line = ' key.foo < key.bar' +out: item->key_size = '7' +out: item->key = 'key.foo < key.bar' +out: item->delim = '< key.bar' +END diff --git a/tests/Makefile.am b/tests/Makefile.am index f128693c1..fa28a115f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,10 +24,7 @@ test_error_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror test_error_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl check_PROGRAMS += test-locking -test_locking_SOURCES = \ - $(tests_srcdir)/locking/test-locking.c \ - $(tests_srcdir)/locking/mutex.c \ - $(tests_srcdir)/locking/condition.c +test_locking_SOURCES = $(tests_srcdir)/library/test-locking.c test_locking_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror test_locking_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm @@ -36,20 +33,20 @@ test_llist_SOURCES = $(tests_srcdir)/library/test-llist.c test_llist_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror test_llist_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm +check_PROGRAMS += test-psplay +test_psplay_SOURCES = $(tests_srcdir)/library/test-psplay.c +test_psplay_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/ +test_psplay_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm + check_PROGRAMS += test-safeclib test_safeclib_SOURCES = $(tests_srcdir)/library/test-safeclib.c test_safeclib_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror test_safeclib_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm -check_PROGRAMS += test-uuid -test_uuid_SOURCES = $(tests_srcdir)/library/test-uuid.c -test_uuid_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -test_uuid_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm - -check_PROGRAMS += test-references -test_references_SOURCES = $(tests_srcdir)/library/test-references.c -test_references_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -test_references_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm -lrt +check_PROGRAMS += test-luid +test_luid_SOURCES = $(tests_srcdir)/library/test-luid.c +test_luid_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror +test_luid_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm check_PROGRAMS += test-filedescriptors test_filedescriptors_SOURCES = $(tests_srcdir)/backend/test-filedescriptors.c @@ -61,4 +58,9 @@ test_filehandles_SOURCES = $(tests_srcdir)/backend/test-filehandles.c test_filehandles_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror test_filehandles_LDADD = liblumibackend.a liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm +check_PROGRAMS += test-config +test_config_SOURCES = $(tests_srcdir)/backend/test-config.c +test_config_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/ +test_config_LDADD = liblumibackend.a liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm + TESTS = $(tests_srcdir)/test.sh diff --git a/tests/backend/test-config.c b/tests/backend/test-config.c new file mode 100644 index 000000000..c9b2aeca3 --- /dev/null +++ b/tests/backend/test-config.c @@ -0,0 +1,222 @@ +/* + test-config.c - test the config system + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + Simeon Voelkel + + 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 "backend/config.h" +#include "backend/configitem.h" + +#include "tests/test.h" + +TESTS_BEGIN + +TEST ("init") +{ + lumiera_config_init ("./"); + printf ("initialized\n"); + lumiera_config_destroy (); + printf ("destroyed\n"); +} + + +TEST ("configitem_simple") +{ + REQUIRE (argv[2]); + lumiera_config_init ("./"); + + LumieraConfigitem item; + + item = lumiera_configitem_new (argv[2]); + ENSURE (item); + + printf ("line = '%s'\n", item->line); + if (item->key) + printf ("key = '%.*s'\n", (int)item->key_size, item->key); + if (item->delim) + { + printf ("delim = '%c'\n", *item->delim); + printf ("value = '%s'\n", item->delim+1); + } + + lumiera_configitem_delete (item, NULL); + + lumiera_config_destroy (); +} + + +TEST ("lookup") +{ + lumiera_config_init ("./"); + + lumiera_config_lookup lookup; + lumiera_config_lookup_init (&lookup); + + LumieraConfigitem item = lumiera_configitem_new ("foo.bar = test"); + lumiera_config_lookup_insert (&lookup, item); + + LumieraConfigitem found = lumiera_config_lookup_item_find (&lookup, "foo.bar"); + ENSURE (found == item); + + lumiera_config_lookup_remove (&lookup, found); + ENSURE (!lumiera_config_lookup_item_find (&lookup, "foo.bar")); + + lumiera_config_lookup_destroy (&lookup); + lumiera_config_destroy (); +} + + +TEST ("number_get") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + + lumiera_config_init ("./"); + + long long number = 0; + + lumiera_config_setdefault (lumiera_tmpbuf_snprintf (SIZE_MAX, "%s = %s", argv[2], argv[3])); + + if (!lumiera_config_number_get (argv[2], &number)) + printf ("%lld\n", number); + else + printf ("%s, %lld\n", lumiera_error (), number); + + lumiera_config_destroy (); +} + + +TEST ("number_get_nodefault") +{ + REQUIRE (argv[2]); + + lumiera_config_init ("./"); + + long long number = 0; + + if (!lumiera_config_number_get (argv[2], &number)) + printf ("%lld\n", number); + else + printf ("%s\n", lumiera_error ()); + + lumiera_config_destroy (); +} + + +TEST ("string_get") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + + lumiera_config_init ("./"); + + char* string; + + lumiera_config_setdefault (lumiera_tmpbuf_snprintf (SIZE_MAX, "%s = %s", argv[2], argv[3])); + + if (!lumiera_config_string_get (argv[2], &string)) + printf ("'%s'\n", string); + else + printf ("%s, '%s'\n", lumiera_error (), string); + + lumiera_config_destroy (); +} + + +TEST ("string_set") +{ + REQUIRE (argv[2]); + + lumiera_config_init ("./"); + + lumiera_config_string_set (argv[2], &argv[3]); + FIXME ("handle error"); + + const char* string; + if (!lumiera_config_get (argv[2], &string)) + printf ("'%s'\n", string); + else + printf ("%s, '%s'\n", lumiera_error (), string); + + lumiera_config_destroy (); +} + + +TEST ("word_get") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + + lumiera_config_init ("./"); + + char* word; + + lumiera_config_setdefault (lumiera_tmpbuf_snprintf (SIZE_MAX, "%s = %s", argv[2], argv[3])); + + if (!lumiera_config_word_get (argv[2], &word)) + printf ("'%s'\n", word); + else + printf ("%s, '%s'\n", lumiera_error (), word); + + lumiera_config_destroy (); +} + +TEST ("configitem_simple_ctor_dtor") +{ + REQUIRE (argv[2]); + lumiera_config_init ("./"); + + LumieraConfigitem item; + + item = lumiera_configitem_new (argv[2]); + + lumiera_config_destroy (); +} + +TEST ("configitem_simple_content_check") +{ + REQUIRE (argv[2]); + lumiera_config_init ("./"); + + LumieraConfigitem item; + + item = lumiera_configitem_new (argv[2]); + + if ( item->line ) + { + printf("item->line = '%s'\n", item->line); + } + if ( item->key_size ) + { + printf("item->key_size = '%zi'\n", item->key_size); + } + if ( item->key ) + { + printf("item->key = '%s'\n", item->key); + } + if ( item->delim ) + { + printf("item->delim = '%s'\n", item->delim); + } + + lumiera_config_destroy (); +} + +TESTS_END diff --git a/tests/backend/test-filedescriptors.c b/tests/backend/test-filedescriptors.c index 3e14ead1d..745c4087b 100644 --- a/tests/backend/test-filedescriptors.c +++ b/tests/backend/test-filedescriptors.c @@ -57,6 +57,32 @@ TEST ("acquire_existing_again") return 1; } +TEST ("acquire_existing_3files") +{ + lumiera_backend_init (); + LumieraFiledescriptor descriptor1 = lumiera_filedescriptor_acquire (",tmp_testfile1", LUMIERA_FILE_READONLY); + + LumieraFiledescriptor descriptor2 = lumiera_filedescriptor_acquire (",tmp_testfile2", LUMIERA_FILE_READONLY); + + LumieraFiledescriptor descriptor3 = lumiera_filedescriptor_acquire (",tmp_testfile3", LUMIERA_FILE_READONLY); + if (descriptor1) + lumiera_filedescriptor_release (descriptor1); + + if (descriptor2) + lumiera_filedescriptor_release (descriptor2); + + if (descriptor3) + lumiera_filedescriptor_release (descriptor3); + + if (descriptor1 && descriptor2 && descriptor3) + { + lumiera_backend_destroy (); + return 0; + } + else + return 1; +} + TEST ("acquire_create") { lumiera_backend_init (); diff --git a/tests/common/mainsuite.cpp b/tests/common/mainsuite.cpp index 3ef740744..455bfd98e 100644 --- a/tests/common/mainsuite.cpp +++ b/tests/common/mainsuite.cpp @@ -34,7 +34,7 @@ using lumiera::ON_GLOBAL_SHUTDOWN; * cmd line argument. * Note: to ease debugging, we don't catch any exceptions. */ -int main (int argc, char* argv[]) +int main (int argc, const char* argv[]) { util::Cmdline args (argc,argv); test::TestOption optparser (args); diff --git a/tests/common/singletontestmocktest.cpp b/tests/common/singletontestmocktest.cpp index ea5e1d70e..2be296bb1 100644 --- a/tests/common/singletontestmocktest.cpp +++ b/tests/common/singletontestmocktest.cpp @@ -50,11 +50,11 @@ namespace lumiera class TestSingletonO { int callCnt; - char* typid; + Symbol typid; format msg; public: - TestSingletonO(char* ty="TestSingletonO") + TestSingletonO(Symbol ty="TestSingletonO") : callCnt (0), typid(ty), msg ("%s::doIt() call=%d\n") { TRACE (singleton, "ctor %s", typid); diff --git a/tests/common/test/cmdlinewrappertest.cpp b/tests/common/test/cmdlinewrappertest.cpp index 668ce8121..f5d1eaf25 100644 --- a/tests/common/test/cmdlinewrappertest.cpp +++ b/tests/common/test/cmdlinewrappertest.cpp @@ -85,7 +85,7 @@ namespace util */ void testStandardCmdlineformat() { - char* fakeArg[3] = {"CMD", "one ", "two"}; + const char* fakeArg[3] = {"CMD", "one ", "two"}; Cmdline theCmdline(3, fakeArg); cout << "Standard Cmdlineformat:" << theCmdline << "\n"; } diff --git a/tests/library/test-llist.c b/tests/library/test-llist.c index 66bfa614d..520cf9012 100644 --- a/tests/library/test-llist.c +++ b/tests/library/test-llist.c @@ -1,5 +1,5 @@ /* - test-llist.c - test the linked lis lib + test-llist.c - test the linked list lib Copyright (C) Lumiera.org 2008, Christian Thaeter @@ -185,4 +185,23 @@ TEST ("whiles") } +TEST ("relocate") +{ + llist source; + llist_init (&source); + + llist something; + llist_init (&something); + + llist_insert_head (&source, &something); + + llist target = {NULL,NULL}; + + target = source; + + llist_relocate (&target); + ENSURE (llist_is_head (&target, &something)); +} + + TESTS_END diff --git a/tests/library/test-locking.c b/tests/library/test-locking.c new file mode 100644 index 000000000..55ea91cdb --- /dev/null +++ b/tests/library/test-locking.c @@ -0,0 +1,154 @@ +/* + test-locking.c - test locking functions + + 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 "tests/test.h" +#include "lib/mutex.h" +#include "lib/condition.h" +#include "lib/rwlock.h" + +#include +#include + +TESTS_BEGIN + + +TEST ("mutexsection") +{ + lumiera_mutex m; + lumiera_mutex_init (&m, "mutexsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_MUTEX_SECTION (NOBUG_ON, &m) + { + printf ("mutex locked section 1\n"); + } + + LUMIERA_MUTEX_SECTION (NOBUG_ON, &m) + { + printf ("mutex locked section 2\n"); + } + + lumiera_mutex_destroy (&m, &NOBUG_FLAG(NOBUG_ON)); +} + + +TEST ("mutexforgotunlock") +{ + lumiera_mutex m; + lumiera_mutex_init (&m, "mutexforgotunlock", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_MUTEX_SECTION (NOBUG_ON, &m) + { + break; // MUTEX_SECTIONS must not be left by a jump + } + + lumiera_mutex_destroy (&m, &NOBUG_FLAG(NOBUG_ON)); +} + + +TEST ("nestedmutexsection") +{ + lumiera_mutex m; + lumiera_mutex_init (&m, "m_mutexsection", &NOBUG_FLAG(NOBUG_ON)); + + lumiera_mutex n; + lumiera_mutex_init (&n, "n_mutexsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_MUTEX_SECTION (NOBUG_ON, &m) + { + printf ("outer mutex locked section\n"); + + LUMIERA_MUTEX_SECTION (NOBUG_ON, &n) + { + printf ("inner mutex locked section\n"); + } + } + + lumiera_mutex_destroy (&n, &NOBUG_FLAG(NOBUG_ON)); + lumiera_mutex_destroy (&m, &NOBUG_FLAG(NOBUG_ON)); +} + + + +TEST ("rwlocksection") +{ + lumiera_rwlock rwlock; + lumiera_rwlock_init (&rwlock, "rwsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_WRLOCK_SECTION (NOBUG_ON, &rwlock) + { + printf ("write locked section 1\n"); + } + + LUMIERA_RDLOCK_SECTION (NOBUG_ON, &rwlock) + { + printf ("read locked section 2\n"); + } + + lumiera_rwlock_destroy (&rwlock, &NOBUG_FLAG(NOBUG_ON)); +} + +TEST ("rwlockforgotunlock") +{ + lumiera_rwlock rwlock; + lumiera_rwlock_init (&rwlock, "rwlockforgotunlock", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_RDLOCK_SECTION (NOBUG_ON, &rwlock) + { + break; // LOCK_SECTIONS must not be left by a jump + } + + lumiera_rwlock_destroy (&rwlock, &NOBUG_FLAG(NOBUG_ON)); +} + + +TEST ("conditionsection") +{ + lumiera_condition cond; + lumiera_condition_init (&cond, "conditionsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_CONDITION_SECTION (NOBUG_ON, &cond) + { + printf ("condition locked section 1\n"); + } + + LUMIERA_CONDITION_SECTION (NOBUG_ON, &cond) + { + printf ("condition locked section 2\n"); + } + + lumiera_condition_destroy (&cond, &NOBUG_FLAG(NOBUG_ON)); +} + + +TEST ("conditionforgotunlock") +{ + lumiera_condition cond; + lumiera_condition_init (&cond, "conditionforgotunlock", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_CONDITION_SECTION (NOBUG_ON, &cond) + { + break; // CONDITION_SECTIONS must not be left by a jump + } + + lumiera_condition_destroy (&cond, &NOBUG_FLAG(NOBUG_ON)); +} + +TESTS_END diff --git a/tests/library/test-uuid.c b/tests/library/test-luid.c similarity index 60% rename from tests/library/test-uuid.c rename to tests/library/test-luid.c index 4a6050163..1695bbec6 100644 --- a/tests/library/test-uuid.c +++ b/tests/library/test-luid.c @@ -1,8 +1,8 @@ /* - test-uuid.c - test the uuid lib + test-luid.c - test the luid lib - Copyright (C) CinelerraCV - 2007, Christian Thaeter + Copyright (C) Lumiera.org + 2007, 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 @@ -23,7 +23,7 @@ //#include -#include "lib/uuid.h" +#include "lib/luid.h" #include @@ -39,35 +39,43 @@ main (int argc, char** argv) if (!strcmp(argv[1], "uuidgen_2")) { - lumiera_uuid uuid1; - lumiera_uuid uuid2; + lumiera_uid luid1; + lumiera_uid luid2; - lumiera_uuid_gen (&uuid1); - lumiera_uuid_gen (&uuid2); + lumiera_uid_gen (&luid1); + lumiera_uid_gen (&luid2); - printf ("%d\n", lumiera_uuid_eq (&uuid2, &uuid1)); + printf ("%d\n", lumiera_uid_eq (&luid2, &luid1)); } else if (!strcmp(argv[1], "uuidgen_copy")) { - lumiera_uuid uuid1; - lumiera_uuid uuid2; + lumiera_uid luid1; + lumiera_uid luid2; - lumiera_uuid_gen (&uuid1); + lumiera_uid_gen (&luid1); - lumiera_uuid_copy (&uuid2, &uuid1); + lumiera_uid_copy (&luid2, &luid1); - printf ("%d\n", lumiera_uuid_eq (&uuid2, &uuid1)); + printf ("%d\n", lumiera_uid_eq (&luid2, &luid1)); } else if (!strcmp(argv[1], "ptrs")) { - lumiera_uuid uuid; + lumiera_uid luid; - lumiera_uuid_set_ptr (&uuid, &uuid); + lumiera_uid_set_ptr (&luid, &luid); - printf ("%d\n", lumiera_uuid_ptr_get (&uuid) == &uuid); + printf ("%d\n", lumiera_uid_ptr_get (&luid) == &luid); } else return 1; return 0; } + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/tests/library/test-psplay.c b/tests/library/test-psplay.c new file mode 100644 index 000000000..f7989bae7 --- /dev/null +++ b/tests/library/test-psplay.c @@ -0,0 +1,471 @@ +/* + test-psplay.c - test the probanilistic splay tree + + 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 +#include + +#include "lib/psplay.h" +#include "tests/test.h" + +struct testitem +{ + psplaynode node; + char* key; +}; +typedef struct testitem* TestItem; + +TestItem +testitem_new (const char* str) +{ + TestItem self = malloc (sizeof *self); + psplaynode_init (&self->node); + self->key = strdup (str); + return self; +} + +void +testitem_delete (TestItem self) +{ + free (self->key); + free (self); +} + + + +static int +cmp_fn (const void*, const void*); + +static const void* +key_fn (const PSplaynode); + +static void +delete_fn (PSplaynode); + +//static psplay_delete_fn +//action_fn (PSplaynode node, const enum psplay_order_e which, int level, void* data); + +static int +fcmp_fn (const void*, const void*); + +static const void* +fkey_fn (const PSplaynode); + +static void +fdelete_fn (PSplaynode); + +//static psplay_delete_fn +//action_fn (PSplaynode node, const enum psplay_order_e which, int level, void* data); + + +psplay_delete_fn +testitem_print_node (PSplaynode node, const enum psplay_order_enum which, int level, void* data) +{ + FILE* fh = data; + static char* sp = " "; + if (level>40) + { + if (which == PSPLAY_PREORDER) + fprintf (fh, "%s ...\n", sp); + return PSPLAY_CONT; + } + + switch (which) + { + case PSPLAY_PREORDER: + fprintf (fh, "%s%p '%s'\n", sp+40-level, node, ((TestItem)node)->key); + if (node->left) fprintf (fh, "%sleft %p '%s'\n", sp+40-level, node->left, ((TestItem)node->left)->key); + break; + case PSPLAY_INORDER: + if (node->right) fprintf (fh, "%sright %p '%s'\n", sp+40-level, node->right, ((TestItem)node->right)->key); + break; + case PSPLAY_POSTORDER: + break; + } + + return PSPLAY_CONT; +} + +void +testitem_dump (PSplay self, FILE* dest) +{ + fprintf (dest, "root %p '%s'\n", self->tree, self->tree?((TestItem)self->tree)->key:"EMPTY"); + psplay_walk (self, NULL, testitem_print_node, 0, dest); + fprintf (dest, "\n"); +} + +psplay_delete_fn +testitem_graphvizprint_node (PSplaynode node, const enum psplay_order_enum which, int level, void* data) +{ + FILE* fh = data; + + switch (which) + { + case PSPLAY_PREORDER: + if (node->left) + fprintf (fh, "\t\"%p:%s\":sw -> \"%p:%s\":ne;\n", + node, + ((TestItem)node)->key, + node->left, + ((TestItem)node->left)->key); + break; + case PSPLAY_INORDER: + if (node->right) + fprintf (fh, "\t\"%p:%s\":se -> \"%p:%s\":nw;\n", + node, + ((TestItem)node)->key, + node->right, + ((TestItem)node->right)->key); + break; + case PSPLAY_POSTORDER: + break; + } + + return PSPLAY_CONT; +} + + + +void +testitem_graphvizdump (PSplay self, FILE* dest) +{ + static int cnt = 0; + if (!cnt) cnt = (time(NULL) % 1000) * 100; + char cmd[256]; + + sprintf(cmd,"dot -Tps >/var/tmp/dbg%d.ps; gv /var/tmp/dbg%d.ps",cnt,cnt); + FILE * graph = popen(cmd, "w"); + + fprintf(graph, "digraph \"%s\" { center=true; size=\"6,6\"; node [color=lightblue2, style=filled];", "psplay"); + ++cnt; + + fprintf(graph, "\t\"root\":s -> \"%p:%s\":n;\n", + self->tree, self->tree?((TestItem)self->tree)->key:"EMPTY"); + + psplay_walk (self, NULL, testitem_graphvizprint_node, 0, graph); + + fprintf(graph, "}"); + + pclose(graph); +} + + + +TESTS_BEGIN + +TEST ("basic") +{ + psplay splay_tree; + psplay_init (&splay_tree, cmp_fn, key_fn, delete_fn); + + psplay_dump (&splay_tree, stdout); + + psplay_destroy (&splay_tree); +} + + +TEST ("basic_insert_dump") +{ + REQUIRE (argv[2]); + + psplay splay_tree; + psplay_init (&splay_tree, cmp_fn, key_fn, delete_fn); + + int end = atoi (argv[2]); + + char key[1024]; + + for (int i = 1; i <= end; ++i) + { + sprintf (key, "%d", i); + TRACE (tests, "insert %s", key); + psplay_insert (&splay_tree, (PSplaynode)testitem_new (key), 100); + } + + psplay_dump (&splay_tree, stderr); + +#if 0 + for (int i = 1; i <= end; ++i) + { + sprintf (key, "%d", i); + TRACE (tests, "insert %s", key); + psplay_remove_key (&splay_tree, key); + psplay_dump (&splay_tree, stderr); + } + for (int i = end; i; --i) + { + sprintf (key, "%d", i); + TRACE (tests, "insert %s", key); + psplay_remove_key (&splay_tree, key); + psplay_dump (&splay_tree, stderr); + } +#endif + + psplay_destroy (&splay_tree); + printf ("done\n"); +} + + +TEST ("insert_find") +{ + psplay splay_tree; + psplay_init (&splay_tree, cmp_fn, key_fn, delete_fn); + + psplay_insert (&splay_tree, (PSplaynode)testitem_new ("foo"), 100); + psplay_insert (&splay_tree, (PSplaynode)testitem_new ("bar"), 100); + psplay_insert (&splay_tree, (PSplaynode)testitem_new ("baz"), 100); + psplay_insert (&splay_tree, (PSplaynode)testitem_new ("test"), 100); + psplay_insert (&splay_tree, (PSplaynode)testitem_new ("pap"), 100); + psplay_insert (&splay_tree, (PSplaynode)testitem_new ("qux"), 100); + + testitem_graphvizdump (&splay_tree, stdout); + psplay_dump (&splay_tree, stdout); + + //TestItem f = (TestItem) psplay_find (&splay_tree, "baz", 100); + TestItem f = (TestItem) psplay_find (&splay_tree, "baz", 100); + ENSURE (f); + printf ("found %p (%.4s)\n", &f->node, f->key); + psplay_dump (&splay_tree, stdout); + + f = (TestItem) psplay_find (&splay_tree, "test", 100); + ENSURE (f); + printf ("found %p (%.4s)\n", &f->node, f->key); + psplay_dump (&splay_tree, stdout); + + f = (TestItem) psplay_find (&splay_tree, "test", 100); + ENSURE (f); + printf ("found %p (%.4s)\n", &f->node, f->key); + psplay_dump (&splay_tree, stdout); + + f = (TestItem) psplay_find (&splay_tree, "foo", 100); + ENSURE (f); + printf ("found %p (%.4s)\n", &f->node, f->key); + psplay_dump (&splay_tree, stdout); + +#if 0 + psplay_delete (psplay_remove (&splay_tree, root.tree)); + psplay_dump (&splay_tree, stdout); + + psplay_delete (psplay_remove (&splay_tree, root.tree)); + psplay_dump (&splay_tree, stdout); + + psplay_delete (psplay_remove (&splay_tree, root.tree)); + psplay_dump (&splay_tree, stdout); +#endif + printf ("destroying\n"); + psplay_destroy (&splay_tree); + psplay_dump (&splay_tree, stdout); +#if 0 + psplay_delete (psplay_remove (&splay_tree, root.tree)); + psplay_dump (&splay_tree, stdout); + + psplay_delete (psplay_remove (&splay_tree, root.tree)); + psplay_dump (&splay_tree, stdout); + + psplay_delete (psplay_remove (&splay_tree, root.tree)); + psplay_dump (&splay_tree, stdout); +#endif + return 0; +} + +TEST ("basic_insert_splay") +{ + REQUIRE (argv[2]); + + psplay splay_tree; + psplay_init (&splay_tree, cmp_fn, key_fn, delete_fn); + + int end = atoi (argv[2]); + + char key[1024]; + + for (int i = 1; i <= end; ++i) + { + sprintf (key, "%d", i); + TRACE (tests, "insert %s", key); + psplay_insert (&splay_tree, (PSplaynode)testitem_new (key), 100); + } + + for (int i = end/2; i <= end; ++i) + { + psplay_dump (&splay_tree, stderr); + sprintf (key, "%d", i); + psplay_find (&splay_tree, key, 100); + } + psplay_destroy (&splay_tree); + printf ("done\n"); +} + + +TEST ("basic_rand_insert_dump") +{ + REQUIRE (argv[2]); + + psplay splay_tree; + psplay_init (&splay_tree, cmp_fn, key_fn, delete_fn); + + int end = atoi (argv[2]); + + char key[1024]; + + for (int i = 1; i <= end; ++i) + { + sprintf (key, "%d", i /*rand()*/); + psplay_insert (&splay_tree, (PSplaynode)testitem_new (key), 100); + } + + testitem_graphvizdump (&splay_tree, stdout); + //testitem_dump (&splay_tree, stdout); + + psplay_destroy (&splay_tree); + printf ("done\n"); +} + + +TEST ("fast_insert") +{ + REQUIRE (argv[2]); + + psplay splay_tree; + psplay_init (&splay_tree, fcmp_fn, fkey_fn, fdelete_fn); + + int end = atoi (argv[2]); + + char key[1024]; + + for (int i = 1; i <= end; ++i) + { + sprintf (key, "%d", i); + psplay_insert (&splay_tree, (PSplaynode)testitem_new (key), 100); + } + + psplay_destroy (&splay_tree); + printf ("done\n"); +} + + + + +TEST ("nonexistant") +{ + REQUIRE (argv[2]); + +} + + +TEST ("insert") +{ + REQUIRE (argv[2]); + +} + + + +TEST ("insert_rand") +{ + REQUIRE (argv[2]); + +} + + + +TEST ("insert_fastcheck") +{ + REQUIRE (argv[2]); + +} + + + +TESTS_END + + + + +/* + cuckoo support functions +*/ + +static int +cmp_fn (const void* a, const void* b) +{ + REQUIRE (a); + REQUIRE (b); + return strcmp (a, b); +} + +static const void* +key_fn (const PSplaynode node) +{ + REQUIRE (node); + REQUIRE (((TestItem)node)->key); + + return ((TestItem)node)->key; +} + +static void +delete_fn (PSplaynode node) +{ + REQUIRE (node); + testitem_delete ((TestItem) node); +} + + +//static psplay_delete_fn +//action_fn (PSplaynode node, const enum psplay_order_e which, int level, void* data) +//{ +//} + + +static int +fcmp_fn (const void* a, const void* b) +{ + return strcmp (a, b); +} + +static const void* +fkey_fn (const PSplaynode node) +{ + return ((TestItem)node)->key; +} + +static void +fdelete_fn (PSplaynode node) +{ + testitem_delete ((TestItem) node); +} + +//static psplay_delete_fn +//action_fn (PSplaynode node, const enum psplay_order_e which, int level, void* data) +//{ +//} + + + + + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/tests/library/test-references.c b/tests/library/test-references.c deleted file mode 100644 index c1509c341..000000000 --- a/tests/library/test-references.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - test-references.c - test strong and weak references - - 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 -//#include - -#include "lib/references.h" - - -LUMIERA_ERROR_DEFINE(TEST, "test error"); - - -struct example -{ - int foo; -}; - -void -example_dtor(void* o) -{ - printf ("destruct: %d\n", ((struct example*)o)->foo); - ((struct example*)o)->foo = 0; - - // free(o) -} - -int -main (int argc, char** argv) -{ - NOBUG_INIT; - - if (argc == 1) - return 0; - - if (!strcmp(argv[1], "basic")) - { - struct example test; - test.foo = 123; - - lumiera_reference hold; - - lumiera_reference_strong_init_once (&hold, &test, example_dtor); - - - struct example* r = lumiera_reference_get (&hold); - - printf ("got: %d\n", r->foo); - - lumiera_reference_destroy (&hold); - } - else if (!strcmp(argv[1], "nodeinsert")) - { - - } - else - return 1; - - return 0; -} diff --git a/tests/library/test-safeclib.c b/tests/library/test-safeclib.c index 837e3d5fd..a5dfa47a9 100644 --- a/tests/library/test-safeclib.c +++ b/tests/library/test-safeclib.c @@ -82,4 +82,24 @@ TEST ("tmpbuf") } +TEST ("tr0") +{ + char* r = lumiera_tmpbuf_tr (argv[2], "abcdeABCDE0123456789", "ABCDEABCDE0123456789", NULL); + printf("%s\n", r?r:"failed"); +} + + +TEST ("tr") +{ + char* r = lumiera_tmpbuf_tr (argv[2], "abcdeABCDE0123456789", "ABCDEABCDE0123456789", ""); + printf("%s\n", r?r:"failed"); +} + + +TEST ("tr_") +{ + printf("%s\n", lumiera_tmpbuf_tr (argv[2], "abcdeABCDE0123456789", "ABCDEABCDE0123456789", "_")); +} + + TESTS_END diff --git a/tests/locking/condition.c b/tests/locking/condition.c deleted file mode 100644 index 7d79e9a7e..000000000 --- a/tests/locking/condition.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - test condition functions - - 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/condition.h" - -#if 0 -waiting_thread() -{ - lock; - wait; - unlock; -} - - -signaling_thread() -{ - signal(); -} -#endif - - -int -conditionforgotunlock () -{ - lumiera_condition c; - lumiera_condition_init (&c); - - lumiera_conditionacquirer l; - lumiera_conditionacquirer_init (&l, &c, LUMIERA_LOCKED); - return 0; -} diff --git a/tests/locking/mutex.c b/tests/locking/mutex.c deleted file mode 100644 index f4f8c18f6..000000000 --- a/tests/locking/mutex.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - test mutex functions - - 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/mutex.h" - -int mutexforgotunlock() -{ - lumiera_mutex m; - lumiera_mutex_init (&m); - - lumiera_mutexacquirer l; - lumiera_mutexacquirer_init_mutex (&l, &m, LUMIERA_LOCKED); - return 0; -} diff --git a/tests/test.h b/tests/test.h index d25fa0a59..060d007fc 100644 --- a/tests/test.h +++ b/tests/test.h @@ -26,6 +26,9 @@ #include "lib/error.h" +#include + +NOBUG_DEFINE_FLAG (tests); LUMIERA_ERROR_DEFINE (TEST, "test error"); #define TESTS_BEGIN \ @@ -33,20 +36,35 @@ int \ main (int argc, char** argv) \ { \ NOBUG_INIT; \ + NOBUG_INIT_FLAG (tests); \ \ if (argc == 1) \ - return 1; + { \ + fprintf (stderr, "missing argument\n"); \ + return 1; \ + } -#define TEST(name) \ +#define TEST(name) \ else if (!strcmp(argv[1], name)) #define TESTS_END \ else \ - return 1; \ + { \ + fprintf (stderr, "unknown test\n"); \ + return 1; \ + } \ \ return 0; \ } #endif + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/