diff --git a/.gitignore b/.gitignore index f0af9d8..f9de9cf 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,7 @@ tests/lv2ftpd tests/lv2ftpc tests/*.cfg debian/libsntl.substvars -lib/libsxmp.pc +*.pc compile test-driver *.pem diff --git a/Makefile.am b/Makefile.am index 2b1aa22..f2ea9f4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,7 @@ else EXAMPLES = endif -SUBDIRS = include lib man $(EXAMPLES) +SUBDIRS = include tdata sexpr sxmp man $(EXAMPLES) libsxmpdocdir = ${prefix}/doc/libsxmp libsxmpdoc_DATA = \ diff --git a/configure.ac b/configure.ac index 035f93f..e263827 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,8 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(libsxmp, m4_esyscmd([tr -d '\n' < VERSION.sxmp])) +AC_SUBST([LIBTDATA_VERSION], m4_esyscmd([tr -d '\n' < tdata/VERSION])) +AC_SUBST([LIBSEXPR_VERSION], m4_esyscmd([tr -d '\n' < sexpr/VERSION])) AC_CONFIG_HEADERS([config.h]) @@ -12,6 +14,36 @@ AC_PROG_CC LT_INIT +# Checks for pointer size. +# TODO: Later this is irrelevant, and we should just bail on 32-bit platforms always +AC_CHECK_SIZEOF([uintptr_t]) +if test "x$ac_cv_sizeof_uintptr_t" == "x"; then + AC_ERROR([Cannot determine size of uintptr_t]) +fi +AC_SUBST(ac_cv_sizeof_uintptr_t) + +if test "${ac_cv_sizeof_uintptr_t}" = "4"; then + AC_DEFINE([BUILD_HOST_32BIT], 1, [Define to 1 if host is 32bit]) +fi + +dnl ************************************** +dnl ***** tests for compiler built-ins ***** +dnl ************************************** + +AC_CACHE_CHECK([for __sync_bool_compare_and_swap_8], +[ctrie_cv_func___sync_bool_compare_and_swap_8], +[AC_LINK_IFELSE([ +typedef unsigned int uint64 __attribute__ ((mode (DI))); +uint64 i; +int main() { return __sync_bool_compare_and_swap (&i, 0, 1); } +], +[ctrie_cv_func___sync_bool_compare_and_swap_8=yes], +[ctrie_cv_func___sync_bool_compare_and_swap_8=no])]) +if test "$ctrie_cv_func___sync_bool_compare_and_swap_8" = "yes"; then + AC_DEFINE([HAVE__SYNC_BOOL_COMPARE_AND_SWAP_8], 1, + [Define to 1 if the compiler provides the __sync_bool_compare_and_swap function for uint64]) +fi + dnl ***************** dnl ***** options ***** dnl ***************** @@ -41,9 +73,6 @@ dnl checking fpr dependencies PKG_CHECK_MODULES(OPENSSL, [openssl]) -PKG_CHECK_MODULES(LIBTDATA, [libtdata >= 0.2.2]) -PKG_CHECK_MODULES(LIBSEXPR, [libsexpr >= 1.3.1]) - case $host_os in linux*) WIN32=no LINUX=yes @@ -72,8 +101,12 @@ dnl AM_CONDITIONAL(BUILD_WIN32, test "x$enable_win32_build" = "xyes") AC_OUTPUT([ Makefile -lib/libsxmp.pc -lib/Makefile +sxmp/libsxmp.pc +sxmp/Makefile +sexpr/Makefile +sexpr/libsexpr.pc +tdata/Makefile +tdata/libtdata.pc include/Makefile man/Makefile examples/Makefile]) diff --git a/examples/Makefile.am b/examples/Makefile.am index d9ce209..62122a1 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -10,8 +10,8 @@ AM_CPPFLAGS = \ AM_CFLAGS = -Wall -g -# where to find libsxmp -libsxmp = ../lib/.libs/libsxmp.la +# where to find libsxmp libraries +libsxmp = ../sexpr/.libs/libsexpr.la ../tdata/.libs/libtdata.la ../sxmp/.libs/libsxmp.la if BUILD_SMPF_EXAMPLE smpf_programs = smpfd smpfc diff --git a/include/Makefile.am b/include/Makefile.am index 2b23563..2f2d75c 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,2 +1,6 @@ nobase_include_HEADERS = sxmp/sxmp.h sxmp/errno.h sxmp/limits.h sxmp/version.h \ - sxmp/base64.h + sxmp/base64.h \ + sexpr/cstring.h sexpr/faststack.h sexpr/sexp_errors.h sexpr/sexp.h \ + sexpr/sexp_memory.h sexpr/sexp_ops.h sexpr/sexp_vis.h \ + tdata/bitwise.h tdata/idx_allocator.h tdata/macro.h tdata/tree.h \ + tdata/usrtc.h tdata/list.h tdata/ctrie.h diff --git a/include/sexpr/cstring.h b/include/sexpr/cstring.h new file mode 100644 index 0000000..79e7458 --- /dev/null +++ b/include/sexpr/cstring.h @@ -0,0 +1,142 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +/** + * cstring.h : c string library to make Ron happy. Wrapper around plain + * C strings that handles automatically growing the string as data is + * concattenated to the end. (note: this is an improved version of cstring + * from supermon. Migrate it into that library eventually... ) + * + * -matt sottile + */ +#ifndef __CSTRING_H__ +#define __CSTRING_H__ + +#include + +/** + * Structure wrapping the character pointer and size counters (allocated vs. + * actual used). + */ +typedef struct __cstring { + /** + * Base address of the string. + */ + char *base; + + /** + * Size of the memory allocated and pointed to by the base pointer. + */ + size_t len; + + /** + * Current size of the string stored in the buffer. len >= curlen + * always, and when len < curlen would be true after a concat operation, + * we realloc bigger space to keep len >= curlen. + */ + size_t curlen; +} CSTRING; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /** + * Set the growth size. Values less than one are ignored. + */ + void sgrowsize(size_t s); + + /** + * Allocate a new CSTRING of the given size. + * A NULL return value indicates that something went wrong and that + * sexp_errno should be checked for the cause. + */ + CSTRING *snew(size_t s); + + /** + * Concatenate the second argument to the CSTRING passed in the first. + * The second argument must be a pointer to a null terminated string. + * A NULL return value indicates that something went wrong and that + * sexp_errno should be checked for the cause. The contents of s are + * left alone. As such, the caller should check the pointer returned + * before overwriting the value of s, as this may result in a memory + * leak if an error condition occurs. + */ + CSTRING *sadd(CSTRING *s, char *a); + + /** + * Append a character to the end of the CSTRING. + * A NULL return value indicates that something went wrong and that + * sexp_errno should be checked for the cause. The contents of s are + * left alone. As such, the caller should check the pointer returned + * before overwriting the value of s, as this may result in a memory + * leak if an error condition occurs. + */ + CSTRING *saddch(CSTRING *s, char a); + + /** + * Trim the allocated memory to precisely the string length plus one char + * to hold the null terminator + * A NULL return value indicates that something went wrong and that + * sexp_errno should be checked for the cause. The contents of s are + * left alone. As such, the caller should check the pointer returned + * before overwriting the value of s, as this may result in a memory + * leak if an error condition occurs. + */ + CSTRING *strim(CSTRING *s); + + /** + * Return the base pointer of the CSTRING. NULL either means the base + * pointer was null, or the CSTRING itself was NULL. + */ + char *toCharPtr(CSTRING *s); + + /** + * Set the current length to zero, effectively dumping the string without + * deallocating it so we can use it later without reallocating any memory. + */ + void sempty(CSTRING *s); + + /** + * Destroy the CSTRING struct and the data it points at. + */ + void sdestroy(CSTRING *s); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CSTRING_H__ */ diff --git a/include/sexpr/faststack.h b/include/sexpr/faststack.h new file mode 100644 index 0000000..195895d --- /dev/null +++ b/include/sexpr/faststack.h @@ -0,0 +1,152 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +/** + * \file faststack.h + * + * \brief Implementation of a fast stack with smart memory management. + */ +#ifndef __FASTSTACK_H__ +#define __FASTSTACK_H__ + +/** + * Structure representing a single level in the stack. Has a pointer to the + * level above and below itself and a pointer to a generic blob of data + * associated with this level. + */ +typedef struct stack_level { + /** + * Pointer to the level above this one. If NULL, then this level is the + * top of the stack. If above is non-NULL, this level *may* be the top, + * but all that can be guaranteed is that there are other allocated + * but potentially unused levels above this one. + */ + struct stack_level *above; + + /** + * Pointer to the level below this one. If NULL, then this level is the + * bottom. + */ + struct stack_level *below; + + /** + * Pointer to some data associated with this level. User is responsible + * for knowing what to cast the \c void \c * pointer into. + */ + void *data; +} stack_lvl_t; + +/** + * Wrapper around the stack levels - keeps a pointer to the current top and + * bottom of the stack and a count of the current height. This allows the top + * to have non-null above pointer resulting from previously allocated stack + * levels that may be recycled later without \c malloc overhead. + */ +typedef struct stack_wrapper { + /** + * The top of the stack. If this is NULL, the stack is empty. + */ + stack_lvl_t *top; + + /** + * The bottom of the stack. If this is NULL, the stack is empty. + */ + stack_lvl_t *bottom; + + /** + * The current height of the stack, in terms of allocated and used levels. + */ + int height; +} faststack_t; + +/** functions **/ + +/* this is for C++ */ +#ifdef __cplusplus +extern "C" { +#endif + + /** + * Return a pointer to an empty stack structure. If the return value is + * NULL, one should check sexp_errno to determine why. + */ + faststack_t *make_stack(); + + /** + * Given a stack structure, destroy it and free all of the stack levels. + * Important note : This function does not free the data + * pointed to from each level of the stack - the user is responsible + * for freeing this data themselves before calling this function to + * prevent memory leakage. + */ + void destroy_stack(faststack_t *s); + + /** + * Given a stack, push a new level on referring to the data pointer. + * If a new level cannot be allocated, NULL is returned and sexp_errno + * is set with the appropriate error condition. Memory allocation errors + * will result in SEXP_ERR_MEMORY, while a null stack will result in + * SEXP_ERR_BAD_STACK. + */ + faststack_t *push(faststack_t *cur_stack, void *data); + + /** + * Given a stack, pop a level off and return a pointer to that level. + * The user is responsible for extracting the data, but the stack_lvl_t + * structures pointed to from the level (above and below) should be left + * alone. If NULL is returned, either the stack contained nothing, or + * the incoming stack s was NULL. Consult sexp_errno to determine which + * was the case -- SEXP_ERR_BAD_STACK indicates a null stack was passed in. + */ + stack_lvl_t *pop(faststack_t *s); + +/* this is for C++ */ +#ifdef __cplusplus +} +#endif + +/** + * Given a stack \a s, examine the data pointer at the top. + */ +#define top_data(s) (s->top->data) + +/** + * Given a stack \a s, check to see if the stack is empty or not. Value + * is boolean true or false. + */ +#define empty_stack(s) (s->top == NULL) + +#endif /* __FASTSTACK_H__ */ diff --git a/include/sexpr/sexp.h b/include/sexpr/sexp.h new file mode 100644 index 0000000..891a0cd --- /dev/null +++ b/include/sexpr/sexp.h @@ -0,0 +1,806 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +#ifndef __SEXP_H__ +#define __SEXP_H__ + +#include +#include /* for BUFSIZ only */ +#include "faststack.h" +#include "cstring.h" +#include "sexp_memory.h" +#include "sexp_errors.h" + +/* doxygen documentation groups defined here */ + +/** + * \defgroup IO I/O routines + */ + +/** + * \defgroup parser Parser routines + */ + +/** + * \mainpage A small and quick S-expression parsing library. + * + * \section intro Introduction + * + * This library was created to provide s-expression parsing and manipulation + * facilities to C and C++ programs. The primary goals were speed and + * efficiency - low memory impact, and the highest speed we could achieve in + * parsing. Suprisingly, no other libraries on the net were found that were + * not bloated with features or involved embedding a full-fledged LISP or + * Scheme interpreter into our programs. So, this library evolved to fill + * this gap. As such, it does not guarantee that every valid LISP + * expression is parseable, and many features that are not required aren't + * implemented. See Rivest's S-expression library for an example of a much + * more featureful library. + * + * What features does this library include? At the heart of the code is a + * continuation-based parser implementing a basic parser state machine. + * Continuations allow users to accumulate multiple streams of characters, + * and parse each stream simultaneously. A continuation allows the parser + * to stop midstream, start working on a new expression, and return to the + * first without corruption of complex state management in the users code. + * No threads, no forking, nothing more than a data structure that must be + * passed in and captured as data becomes available to parse. Once an + * expression has been parsed, a simple structure is returned that + * represents the "abstract syntax tree" of the parsed expression. For the + * majority of users, the parser and this data structure will be all that + * they will ever need to see. For convenience reasons, other functions + * such as I/O wrappers and AST traversal routines have been included, but + * they are not required if users don't wish to use them. + * + * \section credits Credits + * + * SFSEXP: Small, Fast S-Expression Library version 1.2, October 2007 \n + * Written by Matthew Sottile (mjsottile@gmail.com) + * + * \section license License Information + * + * Copyright (2003-2006). The Regents of the University of California. This + * material was produced under U.S. Government contract W-7405-ENG-36 for Los + * Alamos National Laboratory, which is operated by the University of + * California for the U.S. Department of Energy. The U.S. Government has rights + * to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR + * THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY + * LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce + * derivative works, such modified software should be clearly marked, so as not + * to confuse it with the version available from LANL. + * + * Additionally, this library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + * + * LA-CC-04-094 + * + */ + +/** + * \file sexp.h + * + * \brief API for a small, fast and portable s-expression parser library. + */ + +/*==============*/ +/* ENUMERATIONS */ +/*==============*/ + +/** + * An element in an s-expression can be one of three types: a value + * represents an atom with an associated text value. A list + * represents an s-expression, and the element contains a pointer to + * the head element of the associated s-expression. + */ +typedef enum { + /** + * An atom of some type. See atom type (aty) field of element structure + * for details as to which atom type this is. + */ + SEXP_VALUE, + + /** + * A list. This means the element points to an element representing the + * head of a list. + */ + SEXP_LIST +} elt_t; + +/** + * For an element that represents a value, the value can be interpreted + * as a more specific type. A basic value is a simple string with + * no whitespace (and therefore no quotes required). A double quote + * value, or dquote, is one that contains characters (such as + * whitespace) that requires quotation marks to contain the string. A + * single quote value, or squote, represents an element that is + * prefaced with a single tick-mark. This can be either an atom or + * s-expression, and the result is that the parser does not attempt to parse + * the element following the tick mark. It is simply stored as text. This + * is similar to the meaning of a tick mark in the Scheme or LISP family + * of programming languages. Finally, binary allows raw binary to + * be stored within an atom. Note that if the binary type is used, the data + * is stored in bindata with the length in binlength. Otherwise, the data + * us stored in the val field with val_used and val_allocated tracking the + * size of the value string and the total memory allocated for it. + */ +typedef enum { + /** + * Basic, unquoted value. + */ + SEXP_BASIC, + + /** + * Single quote (tick-mark) value - contains a string representing + * a non-parsed portion of the s-expression. + */ + SEXP_SQUOTE, + + /** + * Double-quoted string. Similar to a basic value, but potentially + * containing white-space. + */ + SEXP_DQUOTE, + + /** + * Binary data. This is used when the specialized parser is active + * and supports inlining of binary blobs of data inside an expression. + */ + SEXP_BINARY +} atom_t; + +/*============*/ +/* STRUCTURES */ +/*============*/ + +/** + * An s-expression is represented as a linked structure of elements, + * where each element is either an atom or list. An + * atom corresponds to a string, while a list corresponds to an + * s-expression. The following grammar represents our definition of + * an s-expression:

+ * + *

+ * sexpr  ::= ( sx )
+ * sx     ::= atom sxtail | sexpr sxtail | 'sexpr sxtail | 'atom sxtail | NULL
+ * sxtail ::= sx | NULL
+ * atom   ::= quoted | value
+ * quoted ::= "ws_string"
+ * value  ::= nws_string
+ * 
+ *

+ * + * An atom can either be a quoted string, which is a string containing + * whitespace (possibly) surrounded by double quotes, or a non-whitespace + * string that does not require surrounding quotes. An element representing + * an atom will have a type of value and data stored in the val + * field. An element of type list represents an s-expression + * corresponding to sexpr in the grammar, and will have a pointer to + * the head of the appropriate s-expression. Details regarding these fields + * and their values given with the fields themselves. Notice that a single + * quote can appear directly before an s-expression or atom, similar to the + * use in LISP. + */ +typedef struct elt { + /** + * The element has a type that determines how the structure is used. + * If the type is SEXP_VALUE, then a programmer knows that + * either the val field or bindata field is meaningful dependin on + * the value of the aty field, and contains the data associated with + * this element of the s-expression. If the type is + * SEXP_LIST, then the list field contains a pointer to the + * s-expression element representing the head of the list. For each + * case, the field for the opposite case contains no meaningful data + * and using them in any way is likely to cause an error. + */ + elt_t ty; + + /** + * If the type of the element is SEXP_VALUE and the aty field + * is not SEXP_BINARY, this field will contain the actual data + * represented by this element. + */ + char *val; + + /** + * Number of bytes allocated for val. + */ + size_t val_allocated; + + /** + * Number of bytes used in val (<= val_allocated). + */ + size_t val_used; + + /** + * If the type of the element is SEXP_LIST, this field will contain + * a pointer to the head element of the list. + */ + struct elt *list; + + /** + * The next field is a pointer to the next element in the current + * expression. If this element is the last element in the s-expression, + * this field will be NULL. + */ + struct elt *next; + + /** + * For elements that represent values, this field will specify the + * specific type of value that it represents. This can be used by + * functions to determine how this value should be printed (ie: how it + * should be quoted) or interpreted (ie: interpreting s-expressions that + * are prefixed with a tick-mark.). This value also indicates whether or + * not the programmer should look in the val field or bindata field for + * the atom data. + */ + atom_t aty; + + /** + * For elements that represent binary blobs, this field will + * point to a memory location where the data resides. The length + * of this memory blob is the next field. char* implies byte sized + * elements. This is only used in INLINE_BINARY parser mode. + * IMPORTANT NOTE: The data in this field is freed on a + * destroy_sexp() call, so users should copy it to memory they allocate + * if they wish it to persist after the sexp_t has been freed. + */ + char *bindata; + + /** + * The length of the data pointed at by bindata in bytes. + */ + size_t binlength; +} sexp_t; + +/** + * parser mode flag used by continuation to toggle special parser + * behaviour. + */ +typedef enum { + /** + * normal (LISP-style) s-expression parser behaviour. + */ + PARSER_NORMAL, + + /** + * treat atoms beginning with \#b\# as inlined binary data. everything + * else is treated the same as in PARSER_NORMAL mode. + */ + PARSER_INLINE_BINARY, + + /** + * if the event_handlers field in the continuation contains a non-null + * value, the handlers specified in the parser_event_handlers_t struct + * will be called as appropriate, but the parser will not allocate a + * structure composed of sexp_t structs. Note that if the event_handlers + * is set to null and this mode is selected, the user would be better off + * not calling anything in the first place, as they are telling the parser + * to walk the string, but do nothing productive in the process. + */ + PARSER_EVENTS_ONLY +} parsermode_t; + +/** + * Some users would prefer to, instead of parsing a full string and walking + * a potentially huge sexp_t structure, use an XML SAX-style parser where + * events are triggered as certain parts of the s-expression are encountered. + * This structure contains a set of function pointers that are called by + * the parser as it hits expression start and end, and completes reading + * atoms and binary data. NOTE: The parser_event_handler struct that is + * a field in the continuation data structure is NOT freed by + * destroy_continuation since structs for callbacks are ALWAYS malloc'd + * by the user, not the library. + */ +typedef struct parser_event_handlers { + /** + * The start_sexpr function pointer is called when an open parenthesis + * is encountered starting an expression. + */ + void (* start_sexpr)(); + + /** + * The end_sexpr function pointer is called when an close parenthesis + * is encountered ending an expression. + */ + void (* end_sexpr)(); + + /** + * The characters function pointer is called when an atom is completely + * parsed. The function must take three arguments: a pointer to the + * atom data, the number of elements the atom contains, and the + * specific type of atom that the data represents. + */ + void (* characters)(const char *data, size_t len, atom_t aty); + + /** + * The binary function pointer is called when the parser is functioning + * in INLINE_BINARY mode and binary data is encountered. The function + * must take two arguments: a pointer to the beginning of the binary data + * and the number of bytes of data present. + */ + void (* binary)(const char *data, size_t len); +} parser_event_handlers_t; + +/** + * A continuation is used by the parser to save and restore state between + * invocations to support partial parsing of strings. For example, if we + * pass the string "(foo bar)(goo car)" to the parser, we want to be able + * to retrieve each s-expression one at a time - it would be difficult to + * return all s-expressions at once without knowing how many there are in + * advance (this would require more memory management than we want...). + * So, by using a continuation-based parser, we can call it with this string + * and have it return a continuation when it has parsed the first + * s-expression. Once we have processed the s-expression (accessible + * through the last_sexpr field of the continuation), we can call + * the parser again with the same string and continuation, and it will be + * able to pick up where it left off.

+ * + * We use continuations instead of a state-ful parser to allow multiple + * concurrent strings to be parsed by simply maintaining a set of + * continuations. Manipulating continuations by hand is required if the + * continuation-based parser is called directly. This is not + * recommended unless you are willing to deal with potential errors and + * are willing to learn exactly how the continuation relates to the + * internals of the parser. A simpler approach is to use either the + * parse_sexp function that simply returns an s-expression without + * exposing the continuations, or the iparse_sexp function that + * allows iteratively popping one s-expression at a time from a string + * containing one or more s-expressions. Refer to the documentation for + * each parsing function for further details on behavior and usage. + */ +typedef struct pcont { + /** + * The parser stack used for iterative parsing. + */ + faststack_t *stack; + + /** + * The last full s-expression encountered by the parser. If this is + * NULL, the parser has not encountered a full s-expression and more + * data is required for the current s-expression being parsed. If this + * is non-NULL, then the parser has encountered one s-expression and may + * be partially through parsing the next s-expression. + */ + sexp_t *last_sexp; + + /** + * Pointer to a temporary buffer used to store atom values during parsing. + */ + char *val; + + /** + * Current number of bytes allocated for val. + */ + size_t val_allocated; + + /** + * Current number of used bytes in val. + */ + size_t val_used; + + /** + * Pointer to the character following the last character in the current + * atom value being parsed. + */ + char *vcur; + + /** + * Pointer to the last character to examine in the string being parsed. + * When the parser is called with the continuation, this is the first + * character that will be processed. If this is NULL, the parser will + * start parsing at the beginning of the string passed into the parser. + */ + char *lastPos; + + /** + * This is a pointer to the beginning of the current string being + * processed. lastPos is a pointer to some value inside the string + * that this points to. + */ + char *sbuffer; + + /** + * This is the depth of parenthesis (the number of left parens encountered) + * that the parser is currently working with. + */ + unsigned int depth; + + /** + * This is the depth of parenthesis encountered after a single quote (tick) + * if the character immediately following the tick was a left paren. + */ + unsigned int qdepth; + + /** + * This is the state ID of the current state of the parser in the + * DFA representing the parser. The current parser is a DFA based parser + * to simplify restoring the proper state from a continuation. + */ + unsigned int state; + + /** + * This is a flag indicating whether the next character to be processed + * should be assumed to have been prefaced with a '\' character to escape + * it. + */ + unsigned int esc; + + /** + * Flag whether or not we are processing an atom that was preceeded by + * a single quote. + */ + unsigned int squoted; + + /** + * Error code. Used to indicate that the continuation being returned does + * not represent a successful parsing and thus the contents aren't of much + * value. + */ + sexp_errcode_t error; + + /** + * Mode. The parsers' specialized behaviours can be activated by + * tweaking the mode setting. There are currently two available: + * normal and inline_binary. Inline_binary treats atoms that start + * with \#b\# specially, assuming that they have the structure: + * + * \#b\#s\#data + * + * Where s is a positive (greater than 0) integer representing the length + * of the data, and data is s bytes of binary data following the \# + * sign. After the s bytes, it is assumed normal s-expression data + * continues. + */ + parsermode_t mode; + + /* ----------------------------------------------------------------- + * These fields below are related to dealing with INLINE_BINARY mode + * ----------------------------------------------------------------- */ + + /** + * Length to expect of the current binary data being read in. + * this also corresponds to the size of the memory allocated for + * reading this binary data into. + */ + size_t binexpected; + + /** + * Number of bytes of the binary blob that have already been read in. + */ + size_t binread; + + /** + * Pointer to the memory containing the binary data being read in. + */ + char *bindata; + + /** + * Pointer to a structure holding handlers for sexpr events. NULL for + * normal parser operation. This field is NOT freed by + * destroy_continuation and must be free'd by the user. This is because + * these are malloc'd outside the library ALWAYS, so they are the user's + * responsibility. + */ + parser_event_handlers_t *event_handlers; +} pcont_t; + +/** + * \ingroup IO + * This structure is a wrapper around a standard I/O file descriptor and + * the parsing infrastructure (continuation and a buffer) required to + * parse off of it. This is used so that routines can hide the loops and + * details required to accumulate up data read off of the file descriptor + * and parse expressions individually out of it. + */ +typedef struct sexp_iowrap { + /** + * Continuation used to parse off of the file descriptor. + */ + pcont_t *cc; + + /** + * The file descriptor. Currently CANNOT be a socket since implementation + * uses read(), not recv(). + */ + int fd; + + /** + * Buffer to read data into before parsing. + */ + char buf[BUFSIZ]; + + /** + * Byte count for last read. If it is -1, there was an error. Otherwise, + * it will be a value from 0 to BUFSIZ. + */ + size_t cnt; +} sexp_iowrap_t; + +/*========*/ +/* GLOBAL */ +/*========*/ + +/** + * Global value indicating the most recent error condition encountered. + * This value can be reset to SEXP_ERR_OK by calling sexp_errno_reset(). + */ +extern sexp_errcode_t sexp_errno; + +/*===========*/ +/* FUNCTIONS */ +/*===========*/ + +/* this is for C++ users */ +#ifdef __cplusplus +extern "C" { +#endif + /** + * \ingroup parser + * Set the parameters on atom value buffer allocation and growth sizes. + * This is an important point for performance tuning, as many factors in + * the expected expression structure must be taken into account such as: + * + * - Average size of atom values + * - Variance in sizes of atom values + * - Amount of memory that is tolerably ''wasted'' (allocated but not + * used) + * + * The \a ss parameter specifies the initial size of all atom buffers. + * Ideally, this should be sufficiently large to capture MOST atom values, + * or at least close enough such that one growth is required. The + * \a gs parameter specifies the number of bytes to increase the buffer size + * by when space is exhausted. A safe choice for parameter sizes would + * be on the order of the average size for \a ss, and one standard + * deviation for \a gs. This ensures that 50% of all expressions are + * guaranteed to fit in the initial buffer, and rougly 80-90% will fit in + * one growth. If memory is not an issue, choosing ss to be the mean plus + * one standard deviation will capture 80-90% of expressions in the initial + * buffer, and a gs of one standard deviation will capture nearly all + * expressions. + * + * Note: These parameters can be tuned at runtime as needs change, and they + * will be applied to all expressions and expression elements parsed after + * they are modified. They will not be applied retroactively to expressions + * that have already been parsed. + */ + sexp_errcode_t set_parser_buffer_params(size_t ss, size_t gs); + + /** + * return an allocated sexp_t. This structure may be an already allocated + * one from the stack or a new one if none are available. Use this instead + * of manually mallocing if you want to avoid excessive mallocs. Note: + * Mallocing your own expressions is fine - you can even use + * sexp_t_deallocate to deallocate them and put them in the pool. + * Also, if the stack has not been initialized yet, this does so. + */ + sexp_t *sexp_t_allocate(void); + + /** + * given a malloc'd sexp_t element, put it back into the already-allocated + * element stack. This method will allocate a stack if one has not been + * allocated already. + */ + void sexp_t_deallocate(sexp_t *s); + + /** + * In the event that someone wants us to release ALL of the memory used + * between calls by the library, they can free it. If you don't call + * this, the caches will be persistent for the lifetime of the library + * user. Note that in the event of an error condition resulting in + * sexp_errno being set, the user might consider calling this to clean up + * any memory that may be lingering around that should be cleaned up. + */ + void sexp_cleanup(void); + + /** + * print a sexp_t struct as a string in the LISP style. If the buffer + * is large enough and the conversion is successful, the return value + * represents the length of the string contained in the buffer. If the + * buffer was too small, or some other error occurred, the return + * value is -1 and the contents of the buffer should not be assumed to + * contain any useful information. When the return value is -1, the + * caller should check the contents of sexp_errno for details on what + * error may have occurred. + */ + int print_sexp(char *loc, size_t size, const sexp_t *e); + + /** + * print a sexp_t structure to a buffer, growing it as necessary instead + * of relying on fixed size buffers like print_sexp. Important argument + * to tune for performance reasons is ss - the + * buffer start size. The growsize used by the CSTRING routines also should + * be considered for tuning via the sgrowsize() function. This routine no + * longer requires the user to specify the growsize, and uses the current + * setting without changing it. + */ + int print_sexp_cstr(CSTRING **s, const sexp_t *e, size_t ss); + + /** + * Allocate a new sexp_t element representing a list. + */ + sexp_t *new_sexp_list(sexp_t *l); + + /** + * Allocate a new sexp_t element representing a raw binary atom. + * This element will contain a pointer to the raw binary data + * provided, as well as the binary data length. The character + * atom fields will be NULL and the corresponding val + * length and allocation size will be set to zero since this + * element is carrying a binary pointer only. + */ + sexp_t *new_sexp_binary_atom(char *data, size_t binlength); + + /** + * Allocate a new sexp_t element representing a value. The user must + * specify the precise type of the atom. This used to default to + * SEXP_BASIC, but this can lead to errors if the user did not expect this + * assumption. By explicitly passing in the atom type, the caller should + * ensure that the data in the buffer is valid given the requested atom + * type. For performance reasons, such checks are left to the caller if + * they are desired, and not performed in the library if they are not + * wanted. + */ + sexp_t *new_sexp_atom(const char *buf, size_t bs, atom_t aty); + + /** + * create an initial continuation for parsing the given string + */ + pcont_t *init_continuation(char *str); + + /** + * destroy a continuation. This involves cleaning up what it contains, + * and cleaning up the continuation itself. + */ + void destroy_continuation (pcont_t * pc); + + /** + * \ingroup IO + * create an IO wrapper structure around a file descriptor. A NULL return + * value indicates some problem occurred allocating the wrapper, so the + * user should check the value of sexp_errno for further information. + */ + sexp_iowrap_t *init_iowrap(int fd); + + /** + * \ingroup IO + * destroy an IO wrapper structure. The file descriptor wrapped in the + * wrapper will not be closed, so the caller is responsible + * for manually calling close on the file descriptor. + */ + void destroy_iowrap(sexp_iowrap_t *iow); + + /** + * \ingroup IO + * given and IO wrapper handle, read one s-expression + * off of it. this expression may be contained in a continuation, + * so there is no guarantee that under the covers an IO read + * actually is occuring. A return value of NULL can either indicate + * a parser error or no more data on the input IO handle. In the + * event that NULL is returned, the user should check to see if + * sexp_errno contains SEXP_ERR_IO_EMPTY (no more data) or a more + * problematic error. + */ + sexp_t *read_one_sexp(sexp_iowrap_t *iow); + + /** + * \ingroup parser + * wrapper around parser for compatibility. + */ + sexp_t *parse_sexp(char *s, size_t len); + + /** + * \ingroup parser + * wrapper around parser for friendlier continuation use + * pre-condition : continuation (cc) is NON-NULL! + */ + sexp_t *iparse_sexp(char *s, size_t len, pcont_t *cc); + + /** + * \ingroup parser + * given a LISP style s-expression string, parse it into a set of + * connected sexp_t structures. + */ + pcont_t *cparse_sexp(char *s, size_t len, pcont_t *pc); + + /** + * given a sexp_t structure, free the memory it uses (and recursively free + * the memory used by all sexp_t structures that it references). Note + * that this will call the deallocation routine for sexp_t elements. + * This means that memory isn't freed, but stored away in a cache of + * pre-allocated elements. This is an optimization to speed up the + * parser to eliminate wasteful free and re-malloc calls. Note: If using + * inlined binary mode, this will free the data pointed to by the bindata + * field. So, if you care about the data after the lifetime of the + * s-expression, make sure to make a copy before cleaning up the sexpr. + */ + void destroy_sexp(sexp_t *s); + + /** + * reset the value of sexp_errno to SEXP_ERR_OK. + */ + void reset_sexp_errno(); + + /** + * print the contents of the parser continuation stack to a buffer. + * this is useful if an expression is partially parsed and the caller + * realizes that something is wrong with it. with this routine, + * the caller can reconstruct the expression parsed so far and use it + * for error reporting. this works with fixed size buffers allocated + * by the caller. there is not a CSTRING-based version currently. + */ + void print_pcont(pcont_t * pc, char * buf, size_t buflen); + +/* this is for C++ users */ +#ifdef __cplusplus +} +#endif + +/* sexp helpers */ +#define SEXP_IS_LIST(sx) \ + ((sx)->ty == SEXP_LIST) ? 1 : 0 + +#define SEXP_IS_TYPE(sx,type) \ + ((sx)->ty == SEXP_VALUE && (sx)->aty == (type)) ? 1 : 0 + +#define SEXP_ITERATE_LIST(lst, iter, ind) \ + for((ind) = 0, (iter) = (lst)->list; (ind) < sexp_list_length(lst); \ + (ind)++, (iter) = (iter)->next) + +/* additional functions to work with sexp */ +#ifdef __cplusplus +extern "C" { +#endif +int sexp_list_cdr(sexp_t *expr, sexp_t **sx); +int sexp_list_car(sexp_t *expr, sexp_t **sx); +#ifdef __cplusplus +} +#endif + +#include "sexp_ops.h" + +#endif /* __SEXP_H__ */ + diff --git a/include/sexpr/sexp_errors.h b/include/sexpr/sexp_errors.h new file mode 100644 index 0000000..87c63f0 --- /dev/null +++ b/include/sexpr/sexp_errors.h @@ -0,0 +1,148 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +#ifndef __SEXP_ERRORS_H__ +#define __SEXP_ERRORS_H__ + +/** + * \file sexp_errors.h + * + * \brief Error conditions are enumerated here along with any routines for + * translating error codes to human readable messages. + */ + +/** + * Error codes used by the library are defined in this enumeration. They + * are either used as values for the error field within the continuation + * structures, or as return values for functions with a return type of + * sexp_errcode_t. + */ +typedef enum { + /** + * no error. + */ + SEXP_ERR_OK = 0, + + /** + * memory error. malloc/realloc/calloc failures may result in this error + * code. this can either result from the system calls failing, or in the + * limited memory mode of the library, the memory limit being exceeded. + * In limited memory mode, if this error condition is present, one should + * check the memory limits that were in place during the erroneous call. + */ + SEXP_ERR_MEMORY, + + /** + * badly formed expression. Missing, misplaced, or mismatched parenthesis + * will result in this error. + */ + SEXP_ERR_BADFORM, + + /** + * a sexp_t that is inconsistent will result in this error code. An example + * is a SEXP_BASIC sexp_t with a null val field but a non-zero val_used + * value. Similar cases exist for SEXP_DQUOTE, SQUOTE, and BINARY types. + * This value is also used in the parser to flag a case where an inlined + * binary block is given a negative length. + */ + SEXP_ERR_BADCONTENT, + + /** + * if a null string is passed into the parser, this error occurs. + */ + SEXP_ERR_NULLSTRING, + + /** + * general IO related errors, such as failure of fopen(). these are + * basically non-starters with respect to getting the IO routines going. + */ + SEXP_ERR_IO, + + /** + * I/O routines that return NULL may simply have nothing to read. This is + * sometimes an error condition if the io wrapper continuation contains a + * partially complete s-expression, but nothing more is present (yet) on the + * file descriptor. + */ + SEXP_ERR_IO_EMPTY, + + /** + * when running the library under limited memory (ie, _SEXP_LIMIT_MEMORY_ + * defined at build time), this error will be produced when the memory + * limit is exceeded. + */ + SEXP_ERR_MEM_LIMIT, + + /** + * buffer for unparsing is full. + */ + SEXP_ERR_BUFFER_FULL, + + /** + * routines that take parameters such as memory limits, growth sizes, or + * default sizes, can produce this error if a bad value has been passed in. + * this error usually will indicate that the parameters were bad and the + * default values were used instead (ie, it is non-fatal.). + */ + SEXP_ERR_BAD_PARAM, + + /** + * bad stack state encountered. + */ + SEXP_ERR_BAD_STACK, + + /** + * unknown parser state + */ + SEXP_ERR_UNKNOWN_STATE, + + /** + * parsing is incomplete and need more data to complete it. + */ + SEXP_ERR_INCOMPLETE, + + /** + * this error code indicates that an atom was created with + * the incorrect constructor. For example, attempting to + * create a binary mode atom with the new_sexp_atom + * constructor intended for text atoms will cause this to + * be set. + */ + SEXP_ERR_BAD_CONSTRUCTOR + +} sexp_errcode_t; + +#endif /* __SEXP_ERRORS_H__ */ diff --git a/include/sexpr/sexp_memory.h b/include/sexpr/sexp_memory.h new file mode 100644 index 0000000..5d902bf --- /dev/null +++ b/include/sexpr/sexp_memory.h @@ -0,0 +1,160 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +#ifndef __SEXP_MEMORY_H__ +#define __SEXP_MEMORY_H__ + +/** + * \file sexp_memory.h + * + * \brief Wrappers around basic memory allocation/deallocation routines to + * allow memory usage limiting. Only enabled if _SEXP_LIMIT_MEMORY_ + * is defined when building the library, otherwise the routines + * are defined to be the standard malloc/calloc/realloc/free + * functions. + */ + + +#ifdef _SEXP_LIMIT_MEMORY_ + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * \defgroup memory Memory management routines. + */ + + /** + * \ingroup memory + * Wrapper around malloc, will check and increment memory usage + * counters if space is available. Returns NULL if no memory left + * to use, otherwise returns whatever malloc returns. + * Due to the fact that NULL could mean either the memory limit was exceeded + * or the system call returned NULL, the user must check sexp_errno to + * determine the root cause. + */ + void *sexp_malloc(size_t size); + + /** + * \ingroup memory + * Wrapper around calloc, will check and increment memory usage + * counters if space is available. Returns NULL if no memory left + * to use, otherwise returns whatever calloc returns + * Due to the fact that NULL could mean either the memory limit was exceeded + * or the system call returned NULL, the user must check sexp_errno to + * determine the root cause. + */ + void *sexp_calloc(size_t count, size_t size); + + /** + * \ingroup memory + * Wrapper around free. Instead of trusting sizeof(ptr) to return the + * proper value, we explicitly pass the size of memory associated with + * ptr. Decrements memory usage counter and frees ptr. + */ + void sexp_free(void *ptr, size_t size); + + /** + * \ingroup memory + * Wrapper around realloc. Instead of trusting sizeof(ptr) to return the + * proper value, we explicitly pass the size of memory associated with + * ptr as the oldsize. Increments the memory usage counter by + * (size-oldsize) if enough space available for realloc. + * Returns NULL if no memory left to use, otherwise returns whatever + * realloc returns. + * Due to the fact that NULL could mean either the memory limit was exceeded + * or the system call returned NULL, the user must check sexp_errno to + * determine the root cause. + */ + void *sexp_realloc(void *ptr, size_t size, size_t oldsize); + + /** + * \ingroup memory + * Return the memory limit imposed by the library if memory limiting was + * enabled at compile time. + */ + size_t get_sexp_max_memory(); + + /** + * \ingroup memory + * Return the amount of memory used. + */ + size_t get_sexp_used_memory(); + + /** + * \ingroup memory + * Set the memory limit if memory limiting was enabled. If the new value + * is zero or less, -1 is returned and sexp_errno is set. Similarly, if + * the new value is less than the current amount used by the library, + * -1 is returned and sexp_errno is set. If the new value is valid, the + * new value is returned. + */ + int set_sexp_max_memory(size_t newsize); + +#ifdef __cplusplus +} +#endif + +#else + +/** + * \ingroup memory + * _SEXP_LIMIT_MEMORY_ not defined. This is a macro that maps to calloc(). + */ +#define sexp_calloc(count,size) calloc(count,size) + +/** + * \ingroup memory + * _SEXP_LIMIT_MEMORY_ not defined. This is a macro that maps to malloc(). + */ +#define sexp_malloc(size) malloc(size) + +/** + * \ingroup memory + * _SEXP_LIMIT_MEMORY_ not defined. This is a macro that maps to free(). + */ +#define sexp_free(ptr,size) free(ptr) + +/** + * \ingroup memory + * _SEXP_LIMIT_MEMORY_ not defined. This is a macro that maps to realloc(). + */ +#define sexp_realloc(ptr,size,oldsize) realloc((ptr),(size)) + +#endif + +#endif /* __SEXP_MEMORY_H__ */ diff --git a/include/sexpr/sexp_ops.h b/include/sexpr/sexp_ops.h new file mode 100644 index 0000000..18ef492 --- /dev/null +++ b/include/sexpr/sexp_ops.h @@ -0,0 +1,133 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +#ifndef __SEXP_OPS_H__ +#define __SEXP_OPS_H__ + +/** + * \file sexp_ops.h + * + * \brief A collection of useful operations to perform on s-expressions. + * + * A set of routines for operating on s-expressions. + */ + +#include "sexp.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /*========*/ + /* MACROS */ + /*========*/ + + /** + * Return the head of a list \a s by reference, not copy. + */ +#define hd_sexp(s) ((s)->list) + + /** + * Return the tail of a list \a s by reference, not copy. + */ +#define tl_sexp(s) ((s)->list->next) + + /** + * Return the element following the argument \a s. + */ +#define next_sexp(s) ((s)->next) + + /** + * Reset the continuation \a c by setting the \c lastPos pointer to + * \c NULL. + */ +#define reset_pcont(c) ((c)->lastPos = NULL) + + /** + * Find an atom in a sexpression data structure and return a pointer to + * it. Return NULL if the string doesn't occur anywhere as an atom. + * This is a depth-first search algorithm. + * + * \param name Value to search for. + * \param start Root element of the s-expression to search from. + * \return If the value is found, return a pointer to the first + * occurrence in a depth-first traversal. NULL if not found. + */ + sexp_t *find_sexp(const char *name, sexp_t *start); + + /** + * Breadth first search for s-expressions. Depth first search will find + * the first occurance of a string in an s-expression by basically finding + * the earliest occurance in the string representation of the expression + * itself. Breadth first search will find the first occurance of a string + * in relation to the structure of the expression itself (IE: the instance + * with the lowest depth will be found). + * + * \param name Value to search for. + * \param start Root element of the s-expression to search from. + * \return If the value is found, return a pointer to the first + * occurrence in a breadth-first traversal. NULL if not found. + */ + sexp_t *bfs_find_sexp(const char *name, sexp_t *start); + + /** + * Given an s-expression, determine the length of the list that it encodes. + * A null expression has length 0. An atom has length 1. A list has + * length equal to the number of sexp_t elements from the list head + * to the end of the ->next linked list from that point. + * + * \param sx S-expression input. + * \return Number of sexp_t elements at the same level as sx, 0 for + * NULL, 1 for an atom. + */ + int sexp_list_length(const sexp_t *sx); + + /** + * Copy an s-expression. This is a deep copy - so the resulting s-expression + * shares no pointers with the original. The new one can be changed without + * damaging the contents of the original. + * + * \param sx S-expression to copy. + * \return A pointer to a copy of sx. This is a deep copy, so no memory + * is shared between the original and the returned copy. + */ + sexp_t *copy_sexp(const sexp_t *sx); + +#ifdef __cplusplus +} +#endif + +#endif /* __SEXP_OPS_H__ */ diff --git a/include/sexpr/sexp_vis.h b/include/sexpr/sexp_vis.h new file mode 100644 index 0000000..8ef642f --- /dev/null +++ b/include/sexpr/sexp_vis.h @@ -0,0 +1,65 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +/** + * \defgroup viz Visualization and debugging routines + */ + +/** + * \file sexp_vis.h + * + * \brief API for emitting graphviz data structure visualizations. + */ +#ifndef __SEXP_VIS_H__ +#define __SEXP_VIS_H__ + +/** + * \ingroup viz + * + * Given a s-expression and a filename, this routine creates a DOT-file that + * can be used to generate a visualization of the s-expression data structure. + * This is useful for debugging to ensure that the structure is correct and + * follows what was expected by the programmer. Non-trivial s-expressions + * can yield very large visualizations though. Sometimes it is more + * practical to visualize a portion of the structure if one knows where a bug + * is likely to occur. + * + * \param sx S-expression data structure to create a DOT file based on. + * \param fname Filename of the DOT file to emit. + */ +sexp_errcode_t sexp_to_dotfile(const sexp_t *sx, const char *fname); + +#endif /* __SEXP_VIS_H__ */ diff --git a/include/tdata/bitwise.h b/include/tdata/bitwise.h new file mode 100644 index 0000000..ada6a04 --- /dev/null +++ b/include/tdata/bitwise.h @@ -0,0 +1,429 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * (c) Copyright 2006,2007,2008 Jari OS Core Team + * (c) Copyright 2008 Dmitry Gromada + * (c) Copyright 2010 Alexander Vdolainen + * + * (c) Copyright 2012 - 2013 Askele Oy + * + * Implements bitwise operations with multiword bitmaps + */ + +#ifndef __BITWISE_H__ +#define __BITWISE_H__ + +/* TODO: add arch deps */ +//#include + +/* TODO: add linux headers tests */ +#include + +#ifdef __BITS_PER_LONG +#define BITS_PER_LONG __BITS_PER_LONG +#endif + +#if BITS_PER_LONG == 64 +#define TYPE_LONG_SHIFT 6 +#endif + +#if BITS_PER_LONG == 32 +#define TYPE_LONG_SHIFT 5 +#endif + +typedef struct __bitmap { + int nwords; + unsigned long *map; +} bitmap_t; + +/** + * @fn static inline void bit_set(void *bitmap, int bit) + * Set bit number @a bit in the bitmap @a bitmap + * @note The limit of @a bitmap = sizeof(unsigned long) + * + * @param[out] bitmap - A pointer to the bitmap + * @param bit - Number of bit to set + */ +#ifndef ARCH_BIT_SET +static inline void bit_set(void *bitmap, int bit) +{ + *(unsigned long *)bitmap |= (1 << bit); +} +#else +#define bit_set(bitmap, bit) arch_bit_set(bitmap, bit) +#endif /* ARCH_BIT_SET */ + +/** + * @fn static inline void bit_clear(void *bitmap, int bit) + * Clear bit number @a bit in the bitmap @a bitmap + * @note The limit of @a bitmap = sizeof(unsigned long) + * + * @param[out] bitmap - A pointer to the bitmap + * @param bit - Number of bit to clear + */ +#ifndef ARCH_BIT_CLEAR +static inline void bit_clear(void *bitmap, int bit) +{ + *(unsigned long *)bitmap &= ~(1 << bit); +} +#else +#define bit_clear(bitmap, bit) arch_bit_clear(bitmap, bit) +#endif /* ARCH_BIT_CLEAR */ + +/** + * @fn static inline void bit_toggle(void *bitmap, int bit) + * Toggle bit @a bit in the bitmap @a bitmap. + * @note The limit of @a bitmap = sizeof(unsigned long) + * + * @param[out] bitmap - A pointer to the bitmap. + * @param bit - Number of bit to toggle + */ +#ifndef ARCH_BIT_TOGGLE +static inline void bit_toggle(void *bitmap, int bit) +{ + *(unsigned long *)bitmap ^= (1 << bit); +} +#else +#define bit_toggle(bitmap, bit) arch_bit_toggle(bitmap, bit) +#endif /* ARCH_BIT_TOGGLE */ + +/** + * @fn static inline int bit_test(void *bitmap, int bitno) + * Test if bit with number @a bitno is set in the bitmap @a bitmap. + * @note The limit of @a bitmap = sizeof(unsigned long) + * + * @param bitmap - A pointer to the bitmap. + * @param bitno - Number of bit to test + * @return 1 if bit is set and 0 otherwise + */ +#ifndef ARCH_BIT_TEST +static inline int bit_test(void *bitmap, int bitno) +{ + return ((*(unsigned long *)bitmap & (1 << bitno)) >> bitno); +} +#else +#define bit_test(bitmap, bitno) arch_bit_test(bitmap, bitno) +#endif /* ARCH_BIT_TEST */ + +/** + * @fn static inline int bit_test_and_set(void *bitmap, int bitno) + * @brief Get old value of bit with number @a bitno and set @a bitno bit in the bitmap + * + * This function is similar to bit_set() except it copies old bit value before + * setting it to 1 and return that value after needed bit was setted. + * @note The limit of @a bitmap = sizeof(unsigned long) + * + * @param bitmap - A pointer to the bitmap + * @param bitno - The number of bit to test and set + * @return Old value of bit with number @a bitno + */ +#ifndef ARCH_BIT_TEST_AND_SET +static inline int bit_test_and_set(void *bitmap, int bitno) +{ + int val = (*(unsigned long *)bitmap & (1 << bitno)); + *(unsigned long *)bitmap |= (1 << bitno); + + return val; +} +#else +#define bit_test_and_set(bitmap, bitno) arch_bit_test_and_set(bitmap, bitno) +#endif /* ARCH_BIT_TEST_AND_SET */ + +/** + * @fn static inline int bit_test_and_reset(void *bitmap, int bitno) + * @brief Get old value of bit with number @a bitno and clear @a bitno bit in the bitmap + * + * This function is similar to bit_clear() except it copies old bit value before + * setting it to 1 and return that value after needed bit was setted. + * @note The limit of @a bitmap = sizeof(unsigned long) + * + * @param bitmap - A pointer to the bitmap + * @param bitno - The number of bit to test and set + * @return Old value of bit with number @a bitno + */ +#ifndef ARCH_BIT_TEST_AND_RESET +static inline int bit_test_and_reset(void *bitmap, int bitno) +{ + int val = (*(unsigned long *)bitmap & (1 << bitno)); + *(unsigned long *)bitmap &= ~(1 << bitno); + + return val; +} +#else +#define bit_test_and_reset(bitmap, bitno) arch_bit_test_and_reset(bitmap, bitno) +#endif /* ARCH_BIT_TEST_AND_RESET */ + +/** + * @fn static inline long bit_find_lsf(unsigned long word) + * Find first set least significant bit. + * + * @param word - Where to search + * @return Found bit number on success, negative value on failure. + */ +#ifndef ARCH_BIT_FIND_LSF +static inline long bit_find_lsf(unsigned long word) +{ + long c = -1; + + for (; word; c++, word >>= 1) { + if ((word & 0x01)) { + c++; + break; + } + } + + return c; +} +#else +#define bit_find_lsf(word) arch_bit_find_lsf(word) +#endif /* ARCH_BIT_FIND_LSF */ + +#ifndef ARCH_ZERO_BIT_FIND_LSF +#define zero_bit_find_lsf(word) bit_find_lsf(~(word)) +#else +#define zero_bit_find_lsf(word) arch_zero_bit_find_lsf(word) +#endif + +/** + * @fn static inline long bit_find_msf(unsigned long word) + * Find most significant set bit in the @a word. + * + * @param word - Where to search. + * @return Found bit number on success, negative value on failure. + */ +#ifndef ARCH_BIT_FIND_MSF +static inline long bit_find_msf(unsigned long word) +{ + long c = -1; + + while (word) { + c++; + word >>= 1; + } + + return c; +} +#else +#define bit_find_msf(word) arch_bit_find_msf(word) +#endif /* ARCH_BIT_FIND_MSF */ + +/** + * @fn static inline long bit_find_lsfz(unsigned long word) + * Find first zero least significant bit. + * + * @param word - Where to search + * @return Found bit number on success, negative value on failure. + */ +#ifndef ARCH_BIT_FIND_LSFZ +static inline long bit_find_lsfz(unsigned long word) +{ + long c = -1; + + word = ~word; + + for (; word; c++, word >>= 1) { + if ((word & 0x01)) { + c++; + break; + } + } + + return c; +} +#else +#define bit_find_lsfz(word) arch_bit_find_lsfz(word) +#endif /* ARCH_BIT_FIND_LSFZ */ + +/** + * @fn static inline long bit_find_msfz(unsigned long word) + * Find most significant zero bit in the @a word. + * + * @param word - Where to search. + * @return Found bit number on success, negative value on failure. + */ +#ifndef ARCH_BIT_FIND_MSFZ +static inline long bit_find_msfz(unsigned long word) +{ + long c = -1; + + word = ~word; + + while (word) { + c++; + word >>= 1; + } + + return c; +} +#else +#define bit_find_msfz(word) arch_bit_find_msfz(word) +#endif /* ARCH_BIT_FIND_MSFZ */ + +/** + * @fn static inline void bits_or(void *word, unsigned long flags) + * Executes logical OR with @a word and @a flags and writes result to @a word + * @note The limit of @a word = sizeof(unsigned long) + * + * @param[out] word - A pointer to memory results will be written to + * @param flsgs - Flags that will be OR'ed with @a word + */ +#ifndef ARCH_BITS_OR +static inline void bits_or(void *word, unsigned long flags) +{ + *(unsigned long *)word |= flags; +} +#define bits_or(word, flags) panic("bits_or uniplemented") +#else +#define bits_or(word, flags) arch_bits_or(word, flags) +#endif /* ARCH_BITS_OR */ + +/** + * @fn static inline void bits_and(void *word, unsigned long mask) + * Executes logical AND with @a word and @a mask and writes result to @a word. + * @note The limit of @a word = sizeof(unsigned long) + * + * @param[out] word - A pointer to memory results will be written to + * @param mask - A mask that will be AND'ed with @a word + */ +#ifndef ARCH_BITS_AND +static inline void bits_and(void *word, unsigned long mask) +{ + *(unsigned long *)word &= mask; +} +#else +#define bits_and(word, mask) arch_bits_and(word, mask) +#endif /* ARCH_BITS_AND */ + + +/* + * Initialize multiword bitmap + * + * @param map - pointer to the multiword bitmap + * @param bitno - memory size in bits to allocate + * + * @return 0 on success, -1 if can't allocate memory + */ +int init_bitmap(bitmap_t *bitmap, int nbits); + +/* + * Free memory taken by multiword bitmap + * + * @param bitmap - bitmap to free + */ +void free_bitmap(bitmap_t *map); + +/** + * Set bit number @a bit in the bitmap @a bitmap + * + * @param bitmap - A pointer to the bitmap + * @param bit - Number of bit to set + */ +void bit_set_multi(bitmap_t *bitmap, int bitno); + +/** + * test bit of a multiword bitmap + * + * @param bitmap - A pointer to the bitmap + * @param bitno - The number of bit to test + * + * @return value of the specified bit + */ +int bit_test_multi(bitmap_t *bitmap, int bitno); + +/** + * @brief Get old value of bit with number @a bitno and set @a bitno bit in the bitmap + * + * This function is similar to bit_set() except it copies old bit value before + * setting it to 1 and return that value after needed bit was setted. + * + * @param bitmap - A pointer to the bitmap + * @param bitno - The number of bit to test and set + * + * @return Old value of bit with number @a bitno or -1 if bitno is illegal + */ +int bit_test_and_set_multi(bitmap_t *bitmap, int bitno); + +/** + * @brief Get old value of bit with number @a bitno and clear @a bitno bit in the bitmap + * + * This function is similar to bit_set() except it copies old bit value before + * setting it to 1 and return that value after needed bit was setted. + * + * @param bitmap - A pointer to the bitmap + * @param bitno - The number of bit to test and set + * + * @return Old value of bit with number @a bitno or -1 if bitno is illegal + */ +int bit_test_and_reset_multi(bitmap_t *bitmap, int bitno); + + +/** + * Clear bit number @a bit in the bitmap @a bitmap + * + * @param bitmap - A pointer to the bitmap + * @param bit - Number of bit to clear + */ +void bit_clear_multi(bitmap_t *bitmap, int bitno); + +/* + * Set multiple bits in the multiword bitmap + * + * @param bitmap - pointer to the multimap bitmap + * @param start_bit - bit to set from + * int end_bit - bit to set upto + */ +void bitrange_set_multi(bitmap_t *bitmap, int start_bit, int end_bit); + +/* + * Clear multiple bits in the multiword bitmap + * + * @param bitmap - pointer to the multimap bitmap + * @param start_bit - bit to clear from + * int end_bit - bit to clear upto + */ +void bitrange_clear_multi(bitmap_t *bitmap, int start_bit, int end_bit); + +/** + * Find most significant set bit in multimap word which from the 0 bit + * upto @end_bit + * + * @param word - Where to search. + * @param end_bit - most bit to search upto + * @return Found bit number on success, negative value on failure. + */ +int bit_find_msf_multi(bitmap_t *bitmap, int mbit); + +/** + * Find first set least significant bit. + * + * @param word - Where to search + * @param sbit - least bit to search from + * @return Found bit number on success, negative value on failure. + */ +int bit_find_lsf_multi(bitmap_t *bitmap, int lbit); + +#ifndef ARCH_BIT_TEST_AND_CLEAR +static inline int bit_test_and_clear(void *bitmap, int bitno) +{ + int val = bit_test(bitmap, bitno); + bit_clear(bitmap, bitno); + return val; +} +#else +#define bit_test_and_clear(bitmap, bitno) arch_bit_test_and_clear(bitmap, bitno) +#endif + +#endif /* __BITWISE_H__ */ diff --git a/include/tdata/ctrie.h b/include/tdata/ctrie.h new file mode 100644 index 0000000..ae23c69 --- /dev/null +++ b/include/tdata/ctrie.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * cas.c + * Copyright (C) 2015 Alexander Vdolainen + * + * libtdata is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtdata 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see ."; + */ + +#ifndef __TDATA_CTRIE_H__ +#define __TDATA_CTRIE_H__ + +#include "../config.h" + +/* flags */ +#define CTRIE_FLAG_RO (1 << 1) + +/* structure Ctrie (flags is also underlying node type) */ +typedef struct __ctrie_type { + int flags; + void **root; +}__attribute__((packed)) ctrie_t; + +/* structure SNode (key-value node) */ +typedef struct __ctrie_snode { + ct_key_t key; + void *value; +}__attribute__((packed)) ct_snode_t; + + + +#endif /* __TDATA_CTRIE_H__ */ diff --git a/include/tdata/idx_allocator.h b/include/tdata/idx_allocator.h new file mode 100644 index 0000000..da64b8b --- /dev/null +++ b/include/tdata/idx_allocator.h @@ -0,0 +1,130 @@ +/* + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * (c) Copyright 2006,2007,2008 MString Core Team + * (c) Copyright 2009 Dan Kruchinin + * (c) Copyright 2009 Alfeiks Kaanoken (libc adaptation) + * (c) Copyright 2009 Dmitry Gromada (locking added) + * (c) Copyright 2013 Alexander Vdolainen (verios changes to make it run on linux) + * + * (c) Copyright 2012 - 2013 Askele Oy + * + * Index allocator + * + */ + +/** + * @file include/libdata/idx_allocator.h + * @brief Index allocator API and definitions + * + * Index allocator is a quite simple bitmaps based data + * structure allowing to allocate non-negative integers + * from an ordered continuous numbers set. + * It may be useful for dynamic allocation of pids, uids and + * other identifiers that must be unique. + * Any identifier from a given set may be allocated, deallocated and reserved + * (which prevents its allocation). + * Note, there is no any sence to use index allocator for relatively + * small(for example for sets containing less than 1024 items) identifier sets. + * Allocator uses *at least* BYTES_PER_ITEM + sizeof(ulong_t) bytes for its internal bitmaps. + */ + +#ifndef __IDX_ALLOCATOR_H__ +#define __IDX_ALLOCATOR_H__ + +#include +#include + +typedef unsigned long ulong_t; + +#define BYTES_PER_ITEM 64 /**< Number of bytes per one entry of a second-level bitmap */ +#define WORDS_PER_ITEM (BYTES_PER_ITEM / sizeof(ulong_t)) /**< Number of machine words(ulong_t) per second-level bitmap item */ +#define IDX_INVAL ~0UL /**< Invalid index value */ + +#define ida_lock_init(lock) pthread_mutex_init(lock, NULL) +#define ida_lock_destroy(lock) pthread_mutex_destroy(lock); +#define ida_lock(a) pthread_mutex_lock(&a->lock) +#define ida_unlock(a) pthread_mutex_unlock(&a->lock) +#define ida_lockable(a) (a->lck) + +typedef pthread_mutex_t ida_lock_t; + +/** + * @struct idx_allocator_t + * @brief Index allocator structure + */ +typedef struct __idx_allocator { + int size; /**< Total number of words used for second-level bitmap */ + ulong_t max_id; /**< Maximum index value(exclusive) */ + ulong_t *main_bmap; /**< First-level(main) bitmap that splits second-level bitmap on several parts */ + ulong_t *ids_bmap; /**< Second-level bitmap whose each bit corresponds to particular unique identifier */ + ida_lock_t lock; + int lck; +} idx_allocator_t; + +#define idx_allocator_initialized(ida) (((idx_allocator_t*)(ida))->max_id) + +/** + * @brief Initialize an index allocator. + * @param ida - A pointer to particular index allocator + * @param idx_max - Maximum index value. + * @lockable - flag to indicate the allocator must support locking itself + */ +int idx_allocator_init(idx_allocator_t *ida, ulong_t idx_max, int lockable); + +/** + * @brief Destroy index allocator. + * + * This function frees space used by allocator's internal + * bitmaps. After it is called, allocator can not be used. + * + * @param ida - A pointer to praticular index allocator. + */ +void idx_allocator_destroy(idx_allocator_t *ida); + +/** + * @brief Allocate new index from an allocator's set. + * @param ida - A pointer to target index allocator. + * @return Valid index value on success, IDX_INVAL on error. + * @see IDX_INVAL + * @see idx_reserve + * @see idx_free + */ +ulong_t idx_allocate(idx_allocator_t *ida); + +/** + * @brief Reserves particular index number. + * + * This action prevents named index number from + * allocation. Index allocator skips reserved indices until + * they are fried with idx_free + * + * @param ida - A pointer to particular index allocator. + * @param idx - Number of index to reserve. + * @see idx_free + * @see idx_allocate + */ +void idx_reserve(idx_allocator_t *ida, ulong_t idx); + +/** + * @brief Free back index @a idx to named allocator @a ida. + * @param ida - Allocator index will be freed back to. + * @param idx - An index to free. + * @see idx_allocate + * @see idx_reserve + */ +void idx_free(idx_allocator_t *ida, ulong_t idx); + +#endif /* __IDX_ALLOCATOR_H__ */ diff --git a/include/tdata/list.h b/include/tdata/list.h new file mode 100644 index 0000000..12d5905 --- /dev/null +++ b/include/tdata/list.h @@ -0,0 +1,428 @@ +/* + * Typical data structures used. + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * (c) 2013-2014, 2015 Copyright Askele, inc. + * (c) 2013-2014, 2015 Copyright Askele Ingria, inc. + */ + +#ifndef __TDATA_LIST_H__ +#define __TDATA_LIST_H__ + +#include +#include + +#define __MILESTONE ((unsigned long)0x123) + +/** + * @def MILESTONES_SEQ(num) ((void *)__MILESTONE##num) + * @brief Easy way to define next item in milestones sequence + * @pararm num - number of milestone in sequence + */ +#define MILESTONES_SEQ(num) ((void *)(__MILESTONE + (num))) + +/* milestones for list's next and prev pointers respectively */ +#define MLST_LIST_NEXT MILESTONES_SEQ(1) +#define MLST_LIST_PREV MILESTONES_SEQ(2) + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#ifndef container_of +#define container_of(PTR, TYPE, MEMBER) \ + ((TYPE *)((char *)(PTR) - offsetof(TYPE, MEMBER))) +#endif + +/* init root node of non containerized double linked non cyclic list */ +#define nclist_init_root(root) \ +do { \ + root->next = NULL; \ + root->prev = root; \ +} while (0) + +/* add to tail of non containerized double linked non cyclic list */ +#define nclist_add2tail(root,node) \ +do { \ + typeof(root) __tail = (root)->prev; \ + \ + __tail->next = node; \ + node->prev = __tail; \ + node->next = NULL; \ + (root)->prev = node; \ +} while (0) + +#define nclist_for_each(root, node) \ + for (node = root; node != NULL; node = node->next) + +/** + * @typedef struct __list_node list_node_t + * @struct __list_node + * @brief List node + */ +typedef struct __list_node { + struct __list_node *next; + struct __list_node *prev; +} list_node_t; + +/** + * @typedef struct __list_head list_head_t + * @struct __list_head + * @brief List head + * Actually list_head_t is the same as list_node_t, but + * they have different types though. That was done to prevent + * potentional errors(notice that list head is a stub, it's never + * tied with any real data and it's used only to determine where list + * starts and where it ends) + */ +typedef struct __list_head { + list_node_t head; /**< Head element of the list */ +} list_head_t; + +/** + * @def LIST_DEFINE(name) + * @brief Define and initialize list head with name @a name + * @param name - name of variable + */ +#define LIST_DEFINE(name) \ + list_head_t name = { .head = { &(name).head, &(name).head } } + +/** + * @fn static inline void list_init_head(list_head_t *lst) + * @brief Initialize list head + * @param lst - a pointer to list head. + */ +static inline void list_init_head(list_head_t *lst) +{ + lst->head.next = lst->head.prev = &lst->head; +} + +/** + * @fn static inline void list_init_node(list_node_t *node) + * @brief Initialize list node + * @param node - a pointer to free(unattached from list) node. + */ +static inline void list_init_node(list_node_t *node) +{ + node->next = MLST_LIST_NEXT; + node->prev = MLST_LIST_PREV; +} + +static inline bool list_node_next_isbound(list_node_t *node) +{ + return (node->next == MLST_LIST_NEXT); +} + +static inline bool list_node_prev_isbound(list_node_t *node) +{ + return (node->prev == MLST_LIST_PREV); +} + +/** + * @def list_entry(lst, nptr) + * @brief Get item that holds @a nptr node + * @param list - A pointer to the list + * @param nptr - A pointer to the node + * @return A pointer to the object given node contains + */ +#define list_entry(node, type, member) \ + container_of(node, type, member) + + + +/** + * @def list_head(lst) + * @brief Get head of the list + * @param lst - a pointer to list_head_t + * @return A pointer to header list_node_t + */ +#define list_head(lst) \ + (&(lst)->head) + +/** + * @def list_node_first(lst) + * @brief Get list's first node + * @param list - A pointer to the list_head_t + * @return A pointer to the list first node + */ +#define list_node_first(lst) \ + ((lst)->head.next) + +/** + * list_first_entry - get the first element from a list + * @lst: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(lst, type, member) \ + list_entry((lst)->head.next, type, member) + +/** + * @def list_node_last(lst) + * @brief Get list's last node + * @param list - A pointer to the list_head_t + * @return A pointer to the list last node + */ +#define list_node_last(lst) \ + ((lst)->head.prev) + +/** + * list_last_entry - get the last element from a list + * @lst: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_last_entry(lst, type, member) \ + list_entry((lst)->head.prev, type, member) + +/** + * @def list_add2head(lst, new) + * @brief Add a node @a new to the head of the list + * @param lst - A pointer to the list + * @param new - A pointer to the list node + */ +#define list_add2head(lst, new) \ + list_add(list_node_first(lst), new) + +/** + * @def list_add2tail(lst, new) + * @brief Add a node @a new to the tail of the list + * @param lst - A pointer to the list + * @param new - A pointer to node to add + */ +#define list_add2tail(lst, new) \ + list_add(list_head(lst), new) + +/** + * @def list_delfromhead(lst) + * @brief Remove first element of the list + * @param lst - A pointer to the list + */ +#define list_delfromhead(lst) \ + list_del(list_node_first(lst)) + +/** + * @def list_delfromtail(lst) + * @brief Remove the last element of the list + * @param list - A pointer to the list + */ +#define list_delfromtail(lst) \ + list_del(list_node_last(lst)) + +/** + * @def list_del(del) + * @brief Remove node @a del from the list + * @param del - A node to remove + */ +#define list_del(del) \ + (list_del_range(del, del)) + +/** + * @def list_add(before, new, after) + * @param after - will be the next node after @a new + * @param new - node to insert + */ +#define list_add(after, new) \ + (list_add_range(new, new, (after)->prev, after)) + +/** + * @def list_move2head(to, from) + * @brief Move all nodes from list @a from to the head of list @a to + * @param to - destination list + * @param from - source list + */ +#define list_move2head(to, from) \ + (list_move(list_head(to), list_node_first(to), from)) + +/** + * @def list_move2tail(to, from) + * @brief Move all nodes from list @a from to the tail of list @a to + * @param to - destination list + * @param from - source list + */ +#define list_move2tail(to, from) \ + (list_move(list_node_last(to), list_head(to), from)) + + +/** + * @def list_for_each(lst, liter) + * @brief Iterate through each element of the list + * @param lst - A pointer to list head + * @param liter - A pointer to list which will be used for iteration + */ +#define list_for_each(lst, liter) \ + for (liter = list_node_first(lst); \ + (liter) != list_head(lst); (liter) = (liter)->next) + +/** + * @def list_for_each_safe(lst, liter, save) + * @brief Safe iteration through the list @a lst + * + * This iteration wouldn't be broken even if @a liter will be removed + * from the list + * + * @param lst - A pointer to the list head + * @param liter - A pointer to list node which will be used for iteration + * @param save - The same + */ +#define list_for_each_safe(lst, liter, save) \ + for (liter = list_node_first(lst), save = (liter)->next; \ + (liter) != list_head(lst); (liter) = (save), \ + (save) = (liter)->next) + +/** + * @def list_for_each_entry(lst, iter, member) + * @brief Iterate through each list node member + * @param lst - a pointer list head + * @param iter - a pointer to list entry using as iterator + * @param member - name of list node member in the parent structure + */ +#define list_for_each_entry(lst, iter, member) \ + for (iter = list_entry(list_node_first(lst), typeof(*iter), member); \ + &iter->member != list_head(lst); \ + iter = list_entry(iter->member.next, typeof(*iter), member)) + +/** + * @def list_for_each_entry(lst, iter, member) + * @brief Iterate through each list node member and calls specified function with argument + * pointing to a list entry + * + * @param lst - a pointer list head + * @param iter - a pointer to list entry using as iterator + * @param member - name of list node member in the parent structure + * @param fn - function doing some actins against each entry of the list + */ +#define list_do_for_each_entry(lst, type, member, fn) \ +{ \ + type *t; \ + list_node_t *node, *save; \ + \ + list_for_each_safe(lst, node, save) { \ + t = container_of(node, type, member); \ + fn(t); \ + } \ +} + +/** + * @fn static inline int list_is_empty(list_t *list) + * @brief Determines if list @a list is empty + * @param list - A pointer to list to test + * @return True if list is empty, false otherwise + */ +static inline int list_is_empty(list_head_t *list) +{ + return (list_node_first(list) == list_head(list)); +} + +/** + * @fn static inline void list_add_range(list_node_t *first, list_node_t *last, + * list_node_t *prev, list_node_t *next) + * @brief Insert a range of nodes from @a frist to @a last after @a prev and before @a last + * @param first - first node of range + * @param last - last node of range + * @param prev - after this node a range will be inserted + * @param next - before this node a range will be inserted + */ +static inline void list_add_range(list_node_t *first, list_node_t *last, + list_node_t *prev, list_node_t *next) +{ + next->prev = last; + last->next = next; + prev->next = first; + first->prev = prev; +} + +/* for internal usage */ +static inline void __list_del_range(list_node_t *first, list_node_t *last) +{ + first->prev->next = last->next; + last->next->prev = first->prev; +} + +/** + * @fn static inline list_del_range(list_node_t *first, list_node_t *last) + * @brief Delete nodes from @a first to @a last from list. + * @param fist - first node to delete + * @param last - last node to delete + */ +static inline void list_del_range(list_node_t *first, list_node_t *last) +{ + __list_del_range(first, last); + first->prev = MLST_LIST_PREV; + last->next = MLST_LIST_NEXT; +} + +/** + * @fn static inline void list_cut_sublist(list_node_t *first, list_node_t *last) + * @brief Cut a "sublist" started from @a first and ended with @a last + * + * A @e "sublist" is similar to ordinary list except it hasn't a head. + * In other words it's a cyclic list in which all nodes are equitable. + * + * @param first - From this node sublist will be cutted + * @param last - The last node in the cutted sequence + */ +static inline void list_cut_sublist(list_node_t *first, list_node_t *last) +{ + __list_del_range(first, last); + first->prev = last; + last->next = first; +} + +/** + * @fn static inline void list_cut_head(list_head_t *head) + * @brief Cut a head from the list and make a "sublist" + * @param head - List's head that will be cutted. + * @see list_cut_sublist + * @see list_set_head + */ +static inline void list_cut_head(list_head_t *head) +{ + list_cut_sublist(list_node_first(head), list_node_last(head)); +} + +/** + * @fn static inline void list_cut_head(list_head_t *head) + * @brief Attach a head to the sublist @a cyclist + * @param new_head - A head that will be attached + * @param cyclist - "sublist" + * @see list_cut_sublist + * @see list_set_head + */ +static inline void list_set_head(list_head_t *new_head, list_node_t *cyclist) +{ + list_add_range(cyclist, cyclist->prev, + list_node_first(new_head), list_node_last(new_head)); +} + +/** + * @fn static inline void list_move(list_node_t *prev, list_node_t *next, list_head_t *from) + * @brief Insert nodes of list @a from after @a prev and before @a next + * @param prev - a node after which nodes of @a from will be inserted + * @param next - a node before which nodes of @a from will be inserted + */ +static inline void list_move(list_node_t *prev, list_node_t *next, list_head_t *from) +{ + list_add_range(list_node_first(from), list_node_last(from), + prev, next); + list_init_head(from); +} + +#endif /* __TDATA_LIST_H__ */ + diff --git a/include/tdata/macro.h b/include/tdata/macro.h new file mode 100644 index 0000000..b8cc2ab --- /dev/null +++ b/include/tdata/macro.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * macro.h + * Copyright (C) 2006-2013 Askele inc. + * + * libtdata is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtdata 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see ."; + */ + +#ifndef __MACRO_H__ +#define __MACRO_H__ + +/*this macros just for better source code look and feel*/ + +#define left usrtc_left +#define right usrtc_right +#define parent usrtc_parent +#define next usrtc_right +#define prev usrtc_left +#define data usrtc_data +#define key usrtc_node_key +#define rb_color usrtc_rb_color +#define impl_specific usrtc_impldata + +#define futable usrtc_futable +#define nodecount usrtc_nodecount +#define maxcount usrtc_maxcount +#define dupes_allowed usrtc_dupes_allowed +#define sentinel usrtc_sentinel +#define compare usrtc_compare +#define node_alloc usrtc_node_alloc +#define node_free usrtc_node_free +#define context usrtc_context + +#endif /*__MACRO_H__*/ diff --git a/include/tdata/tree.h b/include/tdata/tree.h new file mode 100644 index 0000000..1e8d897 --- /dev/null +++ b/include/tdata/tree.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * tree.h + * Copyright (C) 2006-2013 Askele inc. + * + * libtdata is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtdata 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see ."; + */ + +#ifndef __TREE_H__ +#define __TREE_H__ + +#include + +#define tree_root_priv(T) ((T)->sentinel.left) +#define tree_null_priv(L) (&(L)->sentinel) +#define TREE_DEPTH_MAX 64 + +/*tree functions*/ +void usrtc_tree_init(usrtc_t *us); +void usrtc_tree_insert(usrtc_t *us, usrtc_node_t *node, const void *key); +void usrtc_tree_delete(usrtc_t *us, usrtc_node_t *node, usrtc_node_t **pswap, + usrtc_node_t **pchild); +usrtc_node_t *usrtc_tree_lookup(usrtc_t *us, const void *key); +usrtc_node_t *usrtc_tree_lower_bound(usrtc_t *us, const void *key); +usrtc_node_t *usrtc_tree_upper_bound(usrtc_t *us, const void *key); +usrtc_node_t *usrtc_tree_first(usrtc_t *us); +usrtc_node_t *usrtc_tree_last(usrtc_t *us); +usrtc_node_t *usrtc_tree_next(usrtc_t *us, usrtc_node_t *curr); +usrtc_node_t *usrtc_tree_prev(usrtc_t *us, usrtc_node_t *curr); +void usrtc_tree_convert_to_list(usrtc_t *us); +void usrtc_tree_convert_from_list(usrtc_t *us); +void usrtc_tree_rotate_left(usrtc_node_t *child, usrtc_node_t *parent); +void usrtc_tree_rotate_right(usrtc_node_t *child, usrtc_node_t *parent); + + +#endif /*__TREE_H__*/ diff --git a/include/tdata/usrtc.h b/include/tdata/usrtc.h new file mode 100644 index 0000000..9d2a35c --- /dev/null +++ b/include/tdata/usrtc.h @@ -0,0 +1,136 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * usrtc.h + * Copyright (C) 2006-2013 Askele inc. + * + * libtdata is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtdata 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see ."; + */ + +#ifndef __USRTC_H__ +#define __USRTC_H__ + +#define USRTC_COUNT_T_MAX ((1 << (sizeof(unsigned long)*(sizeof(char) << 1)))-1) + +/*count and some misc typedefs*/ +typedef unsigned long usrtc_count_t; +typedef unsigned int usrtc_impl_id_t; + +/* implementations ids, + * if you want to add some + * add the id here, don't forget to do it. + */ +#define USRTC_LIST 0 +#define USRTC_BST 1 +#define USRTC_REDBLACK 2 +#define USRTC_SPLAY 3 +#define USRTC_AVL 4 + +typedef enum { + usrtc_lst, + usrtc_bst, + usrtc_other +} usrtc_impltype_t; + +typedef enum { + usrtc_red, + usrtc_black +} usrtc_rb_color_t; +typedef enum { + usrtc_balanced, + usrtc_leftheavy, + usrtc_rightheavy +} usrtc_avl_balance_t; + +/*used for the specific implementation features*/ +typedef union { + int usrtc_dummy; + usrtc_rb_color_t usrtc_rb_color; + usrtc_avl_balance_t usrtc_avl_balance; +} usrtc_impldata_t; + +/*universal node*/ +typedef struct __usrtc_node_t { + struct __usrtc_node_t *usrtc_left; + struct __usrtc_node_t *usrtc_right; + struct __usrtc_node_t *usrtc_parent; + void *usrtc_data; + const void *usrtc_node_key; + usrtc_impldata_t usrtc_impldata; +} usrtc_node_t; + +typedef long (*usrtc_compare_t)(const void*, const void*); +typedef usrtc_node_t *(*usrtc_node_alloc_t)(void *); +typedef void (*usrtc_node_free_t)(void *, usrtc_node_t *); + +typedef struct __usrtc_t { + struct __usrtc_functions_t *usrtc_futable; + usrtc_count_t usrtc_nodecount; + usrtc_count_t usrtc_maxcount; + int usrtc_dupes_allowed; + usrtc_node_t usrtc_sentinel; + usrtc_compare_t usrtc_compare; + usrtc_node_alloc_t usrtc_node_alloc; + usrtc_node_free_t usrtc_node_free; + void *usrtc_context; +} usrtc_t; + +typedef struct __usrtc_functions_t { + void (*usrtc_init)(usrtc_t *); + void (*usrtc_insert)(usrtc_t *, usrtc_node_t *, const void *); + void (*usrtc_delete)(usrtc_t *, usrtc_node_t *); + usrtc_node_t *(*usrtc_lookup)(usrtc_t *, const void *); + usrtc_node_t *(*usrtc_lower_bound)(usrtc_t *, const void *); + usrtc_node_t *(*usrtc_upper_bound)(usrtc_t *, const void *); + usrtc_node_t *(*usrtc_first)(usrtc_t *); + usrtc_node_t *(*usrtc_last)(usrtc_t *); + usrtc_node_t *(*usrtc_next)(usrtc_t *, usrtc_node_t *); + usrtc_node_t *(*usrtc_prev)(usrtc_t *, usrtc_node_t *); + void (*usrtc_convert_to_list)(usrtc_t *); + void (*usrtc_convert_from_list)(usrtc_t *); + usrtc_impltype_t usrtc_type; +} usrtc_functions_t; + +/*basic rtc functions*/ +void usrtc_init(usrtc_t *us,int impl,usrtc_count_t maxcount,usrtc_compare_t compare); +usrtc_t *usrtc_create(int impl,usrtc_count_t maxcount,usrtc_compare_t compare); +void usrtc_destroy(usrtc_t *us); +void usrtc_convert_to(usrtc_t *us,int impl); +usrtc_count_t usrtc_count(usrtc_t *us); +int usrtc_isempty(usrtc_t *us); +int usrtc_isfull(usrtc_t *us); +int usrtc_alloc_insert(usrtc_t *us,const void *key,void *data); +void usrtc_delete_free(usrtc_t *us,usrtc_node_t *node); +void usrtc_set_allocator(usrtc_t *us,usrtc_node_alloc_t alloc,usrtc_node_free_t n_free,void *context); +void usrtc_allow_dupes(usrtc_t *ud); + +/*basic node functions*/ +void usrtc_node_init(usrtc_node_t *node,void *data); +usrtc_node_t *usrtc_node_create(void *data); +void usrtc_node_destroy(usrtc_node_t *node); +void *usrtc_node_getdata(usrtc_node_t *node); +void usrtc_node_setdata(usrtc_node_t *node,void *data); +const void *usrtc_node_getkey(usrtc_node_t *node); + +/*rtc wrappers for the specific data structure functions*/ +void usrtc_insert(usrtc_t *us,usrtc_node_t *node,const void *key); +void usrtc_delete(usrtc_t *us,usrtc_node_t *node); +usrtc_node_t *usrtc_lookup(usrtc_t *us,const void *key); +usrtc_node_t *usrtc_lower_bound(usrtc_t *us,const void *key); +usrtc_node_t *usrtc_upper_bound(usrtc_t *us,const void *key); +usrtc_node_t *usrtc_first(usrtc_t *us); +usrtc_node_t *usrtc_last(usrtc_t *us); +usrtc_node_t *usrtc_next(usrtc_t *us,usrtc_node_t *node); +usrtc_node_t *usrtc_prev(usrtc_t *us,usrtc_node_t *node); + +#endif /*__USRTC_H__*/ diff --git a/sexpr/Makefile.am b/sexpr/Makefile.am new file mode 100644 index 0000000..2d06dda --- /dev/null +++ b/sexpr/Makefile.am @@ -0,0 +1,38 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DPACKAGE_LOCALE_DIR=\""$(localedir)"\" \ + -DPACKAGE_SRC_DIR=\""$(srcdir)"\" \ + -DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \ + -I../include + +#if WIN32 +#AM_CPPFLAGS + = -I../../include +#endif + +AM_CFLAGS =\ + -Wall\ + -g + +lib_LTLIBRARIES = libsexpr.la + + +libsexpr_la_SOURCES = \ + cstring.c event_temp.c faststack.c io.c \ + parser.c sexp.c sexp_memory.c sexp_ops.c sexp_vis.c + +libsexpr_la_LDFLAGS = + +libsexpr_la_LIBADD = + +##include_HEADERS = \ +## ../include/sexpr/cstring.h ../include/sexpr/faststack.h ../include/sexpr/sexp_errors.h \ +## ../include/sexpr/sexp.h ../include/sexpr/sexp_memory.h ../include/sexpr/sexp_ops.h \ +## ../include/sexpr/sexp_vis.h + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libsexpr.pc + +EXTRA_DIST = \ + libsexpr.pc.in + diff --git a/sexpr/VERSION b/sexpr/VERSION new file mode 100644 index 0000000..6f685a4 --- /dev/null +++ b/sexpr/VERSION @@ -0,0 +1,3 @@ +1.3.1 + + diff --git a/sexpr/cstring.c b/sexpr/cstring.c new file mode 100644 index 0000000..62a72be --- /dev/null +++ b/sexpr/cstring.c @@ -0,0 +1,220 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +/** + * Implementation of stuff in cstring.h to make ron happy + */ +#include +#include +#include +#include +#include +#include + +/** + * growth size for cstrings -- default is 8k. use sgrowsize() to adjust. + */ +static size_t cstring_growsize = 8192; + +void sgrowsize(size_t s) { + if (s < 1) + return; + + cstring_growsize = s; +} + +CSTRING *snew(size_t s) { + CSTRING *cs; + +#ifdef __cplusplus + cs = (CSTRING *)sexp_malloc(sizeof(CSTRING)); +#else + cs = sexp_malloc(sizeof(CSTRING)); +#endif + if (cs == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + cs->len = s; + cs->curlen = 0; + +#ifdef __cplusplus + cs->base = (char *)sexp_calloc(sizeof(char),s); +#else + cs->base = sexp_calloc(sizeof(char),s); +#endif + if (cs->base == NULL) { + sexp_free(cs,sizeof(CSTRING)); + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + return cs; +} + +CSTRING *sadd(CSTRING *s, char *a) { + size_t alen; + char *newbase; + + /* no string, so bail */ + if (s == NULL) { + return NULL; + } + + /* nothing to add, return s */ + if (a == NULL) { + return s; + } + + alen = strlen(a); + + if (s->curlen + alen >= s->len) { +#ifdef __cplusplus + newbase = (char *)sexp_realloc(s->base, + s->len+cstring_growsize+alen, + s->len); +#else + newbase = sexp_realloc(s->base, + s->len+cstring_growsize+alen, + s->len); +#endif + + /* do NOT destroy s anymore. if realloc fails, the original data is + still valid, so just report the error to sexp_errno and return NULL. + */ + if (newbase == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + s->len += cstring_growsize + alen; + s->base = newbase; + } + + memcpy(&s->base[s->curlen],a,alen); + s->curlen += alen; + s->base[s->curlen] = 0; + return s; +} + +CSTRING *saddch(CSTRING *s, char a) { + char *newbase; + + if (s == NULL) { + return NULL; + } + + if (s->curlen + 1 >= s->len) { +#ifdef __cplusplus + newbase = (char *)sexp_realloc(s->base, + s->len+cstring_growsize+1, + s->len); +#else + newbase = sexp_realloc(s->base, + s->len+cstring_growsize+1, + s->len); +#endif + + /* do NOT destroy s anymore. if realloc fails, the original data is + still valid, so just report the error to sexp_errno and return NULL. + */ + if (newbase == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + s->len += cstring_growsize+1; + s->base = newbase; + } + + s->base[s->curlen] = a; + s->curlen++; + s->base[s->curlen] = 0; + return s; +} + +CSTRING *strim(CSTRING *s) { + char *newbase; + + if (s == NULL) { + return NULL; + } + + /* no trimming necessary? */ + if (s->len == s->curlen+1) { + return s; + } + +#ifdef __cplusplus + newbase = (char *)sexp_realloc(s->base, + s->curlen+1, + s->len); +#else + newbase = sexp_realloc(s->base, + s->curlen+1, + s->len); +#endif + + /* do NOT destroy s anymore. if realloc fails, the original data is + still valid, so just report the error to sexp_errno and return NULL. + */ + if (newbase == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + s->len = s->curlen+1; + s->base = newbase; + + return s; +} + +char *toCharPtr(CSTRING *s) { + if (s == NULL) return NULL; + return s->base; +} + +void sempty(CSTRING *s) { + if (s == NULL) return; + s->curlen = 0; +} + +void sdestroy(CSTRING *s) { + if (s == NULL) return; + sexp_free(s->base,s->len); + sexp_free(s,sizeof(CSTRING)); +} diff --git a/sexpr/event_temp.c b/sexpr/event_temp.c new file mode 100644 index 0000000..0ac27b7 --- /dev/null +++ b/sexpr/event_temp.c @@ -0,0 +1,891 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +#include +#include + +static size_t sexp_val_start_size = 256; +static size_t sexp_val_grow_size = 64; + +/*************************************************************************/ + + +/** + * event parser : based on cparse_sexp from v1.91 of this file. separate out + * in the event that the parser mode is PARSER_EVENTS_ONLY. + */ +pcont_t * +eparse_sexp (char *str, size_t len, pcont_t *lc) +{ + char *t, *s; + register size_t binexpected = 0; + register size_t binread = 0; + register parsermode_t mode = PARSER_NORMAL; + register size_t val_allocated = 0; + register unsigned int squoted = 0; + register size_t val_used = 0; + register unsigned int state = 1; + register unsigned int depth = 0; + register unsigned int qdepth = 0; + register unsigned int elts = 0; + register unsigned int esc = 0; + pcont_t *cc; + char *val, *vcur, *bindata = NULL; + faststack_t *stack; + char *bufEnd; + int keepgoing = 1; + parser_event_handlers_t *event_handlers = NULL; + + /* make sure non-null string */ + if (str == NULL) { + lc->error = SEXP_ERR_NULLSTRING; + return lc; + } + + /* first, if we have a non null continuation passed in, restore state. */ + if (lc != NULL) { + cc = lc; + binexpected = cc->binexpected; + binread = cc->binread; + bindata = cc->bindata; + val_used = cc->val_used; + val_allocated = cc->val_allocated; + squoted = cc->squoted; + val = cc->val; + vcur = cc->vcur; + state = cc->state; + depth = cc->depth; + qdepth = cc->qdepth; + stack = cc->stack; + esc = cc->esc; + mode = cc->mode; + event_handlers = cc->event_handlers; + s = str; + if (cc->lastPos != NULL) + t = cc->lastPos; + else { + t = s; + cc->sbuffer = str; + } + } else { + /* new continuation... */ +#ifdef __cplusplus + cc = (pcont_t *)sexp_malloc(sizeof(pcont_t)); +#else + cc = sexp_malloc(sizeof(pcont_t)); +#endif + assert(cc != NULL); + + cc->mode = mode; + + /* allocate atom buffer */ +#ifdef __cplusplus + cc->val = val = (char *)sexp_malloc(sizeof(char)*sexp_val_start_size); +#else + cc->val = val = sexp_malloc(sizeof(char)*sexp_val_start_size); +#endif + assert(val != NULL); + + cc->val_used = val_used = 0; + cc->val_allocated = val_allocated = sexp_val_start_size; + + vcur = val; + + /* allocate stack */ + cc->stack = stack = make_stack(); + cc->bindata = NULL; + cc->binread = cc->binexpected = 0; + + /* event handlers are null */ + cc->event_handlers = NULL; + + /* t is temp pointer into s for current position */ + s = str; + t = s; + cc->sbuffer = str; + } + + bufEnd = cc->sbuffer+len; + + /* guard for loop - see end of loop for info. Put it out here in the + event that we're restoring state from a continuation and need to + check before we start up. */ + if (state != 15 && t[0] == '\0') keepgoing = 0; + + /*==================*/ + /* main parser loop */ + /*==================*/ + while (keepgoing == 1 && t != bufEnd) + { + + /* based on the current state in the FSM, do something */ + switch (state) + { + case 1: + switch (t[0]) + { + /* space,tab,CR,LF considered white space */ + case '\n': + case ' ': + case '\t': + case '\r': + t++; + break; + /* semicolon starts a comment that extends until a \n is + encountered. */ + case ';': + t++; + state = 11; + break; + /* enter state 2 for open paren */ + case '(': + state = 2; + t++; + if (event_handlers != NULL && + event_handlers->start_sexpr != NULL) + event_handlers->start_sexpr(); + break; + /* enter state 3 for close paren */ + case ')': + state = 3; + break; + /* begin quoted string - enter state 5 */ + case '\"': + state = 5; + /* set cur pointer to beginning of val buffer */ + vcur = val; + t++; + break; + /* single quote - enter state 7 */ + case '\'': + state = 7; + t++; + break; + /* other characters are assumed to be atom parts */ + default: + /* set cur pointer to beginning of val buffer */ + vcur = val; + + /** NOTE: the following code originally required a transition + to state 4 before processing the first atom character -- + this required two iterations for the first character + of each atom. merging this into here allows us to process + what we already know to be a valid atom character before + entering state 4. **/ + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { +#ifdef __cplusplus + val = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + val = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + assert(val != NULL); + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + /* if the atom starts with # and we're in inline + binary mode, we need to go to state 12 to start + checking for the #b# prefix. otherwise, + if it's not a # or we're just in normal mode, + proceed to state 4 as usual. */ + if (t[0] == '#' && mode == PARSER_INLINE_BINARY) { + state = 12; + } else { + state = 4; + } + + t++; + break; + } + break; + case 2: + /* open paren */ + depth++; + + elts++; + + if (stack->height < 1) + stack->height++; + + stack->height++; + + state = 1; + break; + case 3: + /** close paren **/ + + /* check for close parens that were never opened. */ + if (depth == 0) { + cc->bindata = bindata; + cc->binread = binread; + cc->binexpected = binexpected; + cc->val = val; + cc->mode = mode; + cc->val_used = val_used; + cc->val_allocated = val_allocated; + cc->vcur = vcur; + cc->lastPos = t; + cc->depth = depth; + cc->qdepth = qdepth; + cc->state = 1; + cc->stack = stack; + cc->esc = 0; + cc->last_sexp = NULL; + cc->error = SEXP_ERR_BADFORM; + cc->event_handlers = event_handlers; + + return cc; + } + + t++; + depth--; + + stack->height--; + + if (event_handlers != NULL && + event_handlers->end_sexpr != NULL) + event_handlers->end_sexpr(); + + state = 1; + + /** if depth = 0 then we finished a sexpr, and we return **/ + if (depth == 0) { + cc->bindata = bindata; + cc->binread = binread; + cc->binexpected = binexpected; + cc->error = SEXP_ERR_OK; + cc->mode = mode; + cc->val = val; + cc->val_allocated = val_allocated; + cc->val_used = val_used; + cc->vcur = vcur; + cc->lastPos = t; + cc->depth = depth; + cc->qdepth = qdepth; + cc->state = 1; + cc->stack = stack; + cc->esc = 0; + cc->event_handlers = event_handlers; + cc->last_sexp = NULL; + stack->height = 0; + + return cc; + } + break; + case 4: /** parsing atom **/ + if (esc == 1 && (t[0] == '\"' || t[0] == '(' || + t[0] == ')' || t[0] == '\'' || + t[0] == '\\')) { + vcur--; /* back up to overwrite the \ */ + vcur[0] = t[0]; + vcur++; + t++; + esc = 0; + break; + } + + /* look at an ascii table - these ranges are the non-whitespace, non + paren and quote characters that are legal in atoms */ + if (!((t[0] >= '*' && t[0] <= '~') || + ((unsigned char)(t[0]) > 127) || + (t[0] == '!') || + (t[0] >= '#' && t[0] <= '&'))) + { + vcur[0] = '\0'; + val_used++; + + elts++; + + if (event_handlers != NULL && + event_handlers->characters != NULL) { + if (squoted != 0) + event_handlers->characters(val,val_used,SEXP_SQUOTE); + else + event_handlers->characters(val,val_used,SEXP_BASIC); + } + + vcur = val; + val_used = 0; + + if (stack->height < 1) { + /* looks like this expression was just a basic atom - so + return it. */ + cc->bindata = bindata; + cc->binread = binread; + cc->binexpected = binexpected; + cc->mode = mode; + cc->error = SEXP_ERR_OK; + cc->val = val; + cc->val_used = val_used; + cc->val_allocated = val_allocated; + cc->vcur = vcur; + cc->squoted = 0; + cc->lastPos = t; + cc->depth = depth; + cc->qdepth = qdepth; + cc->state = 1; + cc->stack = stack; + cc->esc = 0; + cc->last_sexp = NULL; + cc->event_handlers = event_handlers; + + return cc; + } + + switch (t[0]) { + case ' ': + case '\t': + case '\n': + case '\r': + /** NOTE: we know whitespace following atom, so spin ahead + one and let state 1 do what it needs to for the next + character. **/ + state = 1; + t++; + squoted = 0; + break; + case ')': + squoted = 0; + state = 3; + break; + default: + squoted = 0; + state = 1; + } + } + else + { + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { +#ifdef __cplusplus + val = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + val = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + assert(val != NULL); + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + t++; + } + break; + case 5: + if (esc == 1 && (t[0] == '\"' || + t[0] == '\'' || + t[0] == '(' || + t[0] == ')' || + t[0] == '\\')) { + vcur--; + vcur[0] = t[0]; + vcur++; + /** NO NEED TO UPDATE VAL COUNTS **/ + t++; + esc = 0; + } + + if (t[0] == '\"') + { + state = 6; + + if (squoted == 1) { + vcur[0] = '\"'; + val_used++; + + if (val_used == val_allocated) { +#ifdef __cplusplus + val = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + val = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + assert(val != NULL); + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + } + + vcur[0] = '\0'; + + val_used++; + elts++; + + if (event_handlers != NULL && + event_handlers->characters != NULL) { + if (squoted == 1) + event_handlers->characters(val,val_used,SEXP_SQUOTE); + else + event_handlers->characters(val,val_used,SEXP_DQUOTE); + } + + vcur = val; + val_used = 0; + + if (stack->height < 1) { + /* looks like this expression was just a basic double + quoted atom - so return it. */ + t++; /* spin past the quote */ + + cc->bindata = bindata; + cc->binread = binread; + cc->binexpected = binexpected; + cc->mode = mode; + cc->squoted = 0; + cc->error = SEXP_ERR_OK; + cc->val = val; + cc->val_used = val_used; + cc->val_allocated = val_allocated; + cc->vcur = vcur; + cc->lastPos = t++; + cc->depth = depth; + cc->qdepth = qdepth; + cc->state = 1; + cc->stack = stack; + cc->esc = 0; + cc->last_sexp = NULL; + cc->event_handlers = event_handlers; + + return cc; + } + } + else + { + vcur[0] = t[0]; + val_used++; + + if (val_used == val_allocated) { +#ifdef __cplusplus + val = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + val = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + assert(val != NULL); + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + if (t[0] == '\\') { + esc = 1; + } else + esc = 0; + } + + t++; + break; + case 6: + vcur = val; + state = 1; + break; + case 7: + if (t[0] == '\"') + { + state = 5; + vcur = val; + t++; + + vcur[0] = '\"'; + val_used++; + + if (val_used == val_allocated) { +#ifdef __cplusplus + val = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + val = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + assert(val != NULL); + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + squoted = 1; + } + else if (t[0] == '(') + { + vcur = val; + state = 8; + } + else + { + vcur = val; + state = 4; + squoted = 1; + } + break; + case 8: + if (esc == 0) { + if (t[0] == '(') + { + qdepth++; + } + else if (t[0] == ')') + { + qdepth--; + state = 9; + } + else if (t[0] == '\"') + { + state = 10; + } + } else { + esc = 0; + } + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { +#ifdef __cplusplus + val = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + val = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + assert(val != NULL); + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + t++; + /* let it fall through to state 9 if we know we're transitioning + into that state */ + if (state != 9) + break; + case 9: + if (qdepth == 0) + { + state = 1; + vcur[0] = '\0'; + + elts++; + + if (event_handlers != NULL && + event_handlers->characters != NULL) + event_handlers->characters(val,val_used,SEXP_SQUOTE); + + vcur = val; + val_used = 0; + + if (stack->height < 1) { + /* looks like the whole expression was a single + quoted value! So return it. */ + cc->bindata = bindata; + cc->binread = binread; + cc->binexpected = binexpected; + cc->mode = mode; + cc->error = SEXP_ERR_OK; + cc->squoted = 0; + cc->val = val; + cc->val_used = val_used; + cc->val_allocated = val_allocated; + cc->vcur = vcur; + cc->lastPos = t; + cc->depth = depth; + cc->qdepth = qdepth; + cc->state = 1; + cc->stack = stack; + cc->esc = 0; + cc->last_sexp = NULL; + cc->event_handlers = event_handlers; + + return cc; + } + } + else + state = 8; + break; + case 10: + if (t[0] == '\"' && esc == 0) + { + state = 8; + } + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { +#ifdef __cplusplus + val = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + val = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + assert(val != NULL); + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + t++; + break; + case 11: + if (t[0] == '\n') { + state = 1; + } + t++; + break; + case 12: /* pre: we saw a # and we're in inline binary mode */ + if (t[0] == 'b') { + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { +#ifdef __cplusplus + val = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + val = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + assert(val != NULL); + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + state = 13; /* so far, #b */ + t++; + } else { + state = 4; /* not #b, so plain ol' atom */ + } + + break; + + case 13: /* pre: we saw a #b and we're in inline binary mode */ + if (t[0] == '#') { + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { +#ifdef __cplusplus + val = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + val = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + assert(val != NULL); + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + state = 14; /* so far, #b# - we're definitely in binary + land now. */ + /* reset vcur to val, overwrite #b# with the size string. */ + vcur = val; + val_used = 0; + t++; + } else { + state = 4; /* not #b#, so plain ol' atom */ + } + + break; + + case 14: + /** + * so far we've read #b#. Now, the steps of the process become: + * proceed to read bytes in until we see # again. This will be + * an ASCII representation of the size. At this point, we want + * to read as many bytes as specified by this size string after + * the #. + */ + if (t[0] == '#') { /* done with size string */ + t++; + state = 15; + vcur[0] = '\0'; + + binexpected = (size_t) atoi(val); + assert(binexpected > 0); + binread = 0; +#ifdef __cplusplus + bindata = (char *)sexp_malloc(sizeof(char)*binexpected); +#else + bindata = sexp_malloc(sizeof(char)*binexpected); +#endif + assert(bindata != NULL); + } else { /* still reading size string */ + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { +#ifdef __cplusplus + val = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + val = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + assert(val != NULL); + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + t++; + } + + break; + + case 15: /* reading binary blob */ + bindata[binread] = t[0]; + binread++; + t++; + + if (binread == binexpected) { + /* state = 1 -- create a sexp_t and head back */ + + elts++; + + if (event_handlers != NULL && + event_handlers->binary != NULL) + event_handlers->binary(bindata, binread); + + sexp_free(bindata,binread); + bindata = NULL; + binread = binexpected = 0; + + state = 1; + + val_used = 0; + vcur = val; + + } + + break; + + default: + fprintf (stderr, "eparse_sexp: unknown parser state %d.\n", state); + break; + } + + /* the null check used to be part of the guard on the while loop. + unfortunately, if we're in state 15, null is considered a + perfectly valid byte. This means the length passed in better + be accurate for the parser to not walk off the end of the + string! */ + if (state != 15 && t[0] == '\0') keepgoing = 0; + } + + if (depth == 0 && elts > 0) { + cc->bindata = bindata; + cc->binread = binread; + cc->binexpected = binexpected; + cc->mode = mode; + cc->error = SEXP_ERR_OK; + cc->val = val; + cc->squoted = squoted; + cc->val_used = val_used; + cc->val_allocated = val_allocated; + cc->vcur = vcur; + cc->lastPos = t; + cc->depth = depth; + cc->qdepth = qdepth; + cc->state = 1; + cc->stack = stack; + cc->esc = 0; + cc->event_handlers = event_handlers; + stack->height = 0; + cc->last_sexp = NULL; + } else { + cc->bindata = bindata; + cc->binread = binread; + cc->binexpected = binexpected; + cc->mode = mode; + cc->val = val; + cc->esc = esc; + cc->squoted = squoted; + cc->vcur = vcur; + cc->val_allocated = val_allocated; + cc->val_used = val_used; + if (t[0] == '\0' || t == bufEnd) + cc->lastPos = NULL; + else + cc->lastPos = t; + cc->depth = depth; + cc->qdepth = qdepth; + cc->state = state; + cc->stack = stack; + cc->last_sexp = NULL; + cc->event_handlers = event_handlers; + cc->error = SEXP_ERR_OK; + } + + return cc; +} diff --git a/sexpr/faststack.c b/sexpr/faststack.c new file mode 100644 index 0000000..9d4ccd5 --- /dev/null +++ b/sexpr/faststack.c @@ -0,0 +1,221 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +/** + * faststack.c : implementation of fast stack. + * + * matt sottile / matt@lanl.gov + */ +#include +#include +#include +#include + +/** + * create an empty stack. + */ +faststack_t * +make_stack () +{ + faststack_t *s; + +#ifdef __cplusplus + s = (faststack_t *)sexp_malloc (sizeof (faststack_t)); +#else + s = sexp_malloc (sizeof (faststack_t)); +#endif + + if (s == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + s->top = s->bottom = NULL; + s->height = 0; + + return s; +} + +/** + * free all levels of a stack + */ +void +destroy_stack (faststack_t * s) +{ + stack_lvl_t *sl; + + /* return if stack is null. no error condition - just return. */ + if (s == NULL) + return; + + /* start at the bottom */ + sl = s->bottom; + + /* if bottom is null, no levels to free. just free stack itself then. */ + if (sl == NULL) { + sexp_free(s, sizeof(faststack_t)); + return; + } + + /* go up to the top of the allocated stack */ + while (sl->above != NULL) + sl = sl->above; + + /* until we get to the bottom (where below is null), free the data + at each level and the level itself. */ + while (sl->below != NULL) + { + sl = sl->below; + sexp_free (sl->above, sizeof(stack_lvl_t)); + } + + /* free the bottom level */ + sexp_free (sl, sizeof(stack_lvl_t)); + + /* free the stack wrapper itself. */ + sexp_free (s, sizeof(faststack_t)); +} + +/** + * push a level onto the cur_stack. reuse levels that have + * been previously allocated, allocate a new one if none + * are available. + */ +faststack_t * +push (faststack_t * cur_stack, void *data) +{ + stack_lvl_t *top, *tmp; + + if (cur_stack == NULL) { + sexp_errno = SEXP_ERR_BAD_STACK; + return NULL; + } + + top = cur_stack->top; + + /* if top isn't null, try to push above it. */ + if (top != NULL) + { + /* if above isn't null, set the stack top to it and set the + data */ + if (top->above != NULL) + { + top = cur_stack->top = cur_stack->top->above; + top->data = data; + } + else + { + /* otherwise, allocate a new level. */ + +#ifdef __cplusplus + tmp = top->above = (stack_level *)sexp_malloc (sizeof (stack_lvl_t)); +#else + tmp = top->above = sexp_malloc (sizeof (stack_lvl_t)); +#endif + + if (tmp == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + tmp->below = cur_stack->top; + tmp->above = NULL; + cur_stack->top = tmp; + tmp->data = data; + } + } + else + { + if (cur_stack->bottom != NULL) + { + cur_stack->top = cur_stack->bottom; + cur_stack->top->data = data; + } + else + { +#ifdef __cplusplus + tmp = cur_stack->top = + (stack_lvl_t *)sexp_malloc (sizeof (stack_lvl_t)); +#else + tmp = cur_stack->top = sexp_malloc (sizeof (stack_lvl_t)); +#endif + if (tmp == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + cur_stack->bottom = tmp; + tmp->above = NULL; + tmp->below = NULL; + tmp->data = data; + } + } + + cur_stack->height++; + + return cur_stack; +} + +/** + * pop the top of the stack, return the stack level that was + * popped of. + */ +stack_lvl_t * +pop (faststack_t * s) +{ + stack_lvl_t *top; + + if (s == NULL) { + sexp_errno = SEXP_ERR_BAD_STACK; + return NULL; + } + + top = s->top; + + /* if stack top isn't null, set the top pointer to the next + level down and return the old top. */ + if (top != NULL && s->height > 0) + { + s->top = s->top->below; + s->height--; + } + else + { + if (s->height < 1) return NULL; + } + + return top; +} diff --git a/sexpr/io.c b/sexpr/io.c new file mode 100644 index 0000000..8cb9524 --- /dev/null +++ b/sexpr/io.c @@ -0,0 +1,139 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +#include +#ifndef WIN32 +# include +#else +# define ssize_t int +# include +# include +#endif +#include +#include +#include +#include + +/** + * initialize an io-wrapper + */ +sexp_iowrap_t *init_iowrap(int fd) { + sexp_iowrap_t *iow; + +#ifdef __cplusplus + iow = (sexp_iowrap_t *)sexp_calloc(1,sizeof(sexp_iowrap_t)); +#else + iow = sexp_calloc(1,sizeof(sexp_iowrap_t)); +#endif + + if (iow == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + iow->cc = NULL; + iow->fd = fd; + iow->cnt = 0; + iow->buf[0] = '\0'; + + return iow; +} + +/** + * + */ +void destroy_iowrap(sexp_iowrap_t *iow) { + if (iow == NULL) return; /* idiot */ + + destroy_continuation(iow->cc); + sexp_free(iow, sizeof(sexp_iowrap_t)); +} + +/** + * + */ +sexp_t *read_one_sexp(sexp_iowrap_t *iow) { + sexp_t *sx = NULL; + + if (iow == NULL) + return NULL; + + /* check if we have more to parse from the continuation. */ + if (iow->cc != NULL && iow->cc->lastPos != NULL) { + iow->cc = cparse_sexp(iow->buf, iow->cnt, iow->cc); + + if (iow->cc == NULL) return NULL; /* cparse_sexp set sexp_errno */ + if (iow->cc->last_sexp != NULL) { + sx = iow->cc->last_sexp; + iow->cc->last_sexp = NULL; + return sx; + } + iow->cnt = 0; + } + + if (iow->cnt == 0) { + iow->cnt = (size_t) read(iow->fd,iow->buf,BUFSIZ); + + if (iow->cnt == 0) { + sexp_errno = SEXP_ERR_IO_EMPTY; + return NULL; + } + } + + iow->cc = cparse_sexp(iow->buf,iow->cnt,iow->cc); + + while (iow->cc->last_sexp == NULL) { + if (iow->cc->error != SEXP_ERR_OK) { + sexp_errno = iow->cc->error; + return NULL; + } + + iow->cnt = (size_t) read(iow->fd,iow->buf,BUFSIZ); + + if (iow->cnt == 0) { + sexp_errno = SEXP_ERR_IO_EMPTY; + return NULL; + } + + iow->cc = cparse_sexp(iow->buf,iow->cnt,iow->cc); + iow->cnt = 0; + } + + sx = iow->cc->last_sexp; + iow->cc->last_sexp = NULL; + + return sx; +} diff --git a/sexpr/libsexpr.pc.in b/sexpr/libsexpr.pc.in new file mode 100644 index 0000000..c511c57 --- /dev/null +++ b/sexpr/libsexpr.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +datarootdir=@datarootdir@ +datadir=@datadir@ +includedir=@includedir@ + +Name: libsexpr +Description: Small, fast s-expression handling library (so called sexpr). +Version: @LIBSEXPR_VERSION@ +Requires: +Libs: -L${libdir} -lsexpr +Cflags: -I${includedir} diff --git a/sexpr/parser.c b/sexpr/parser.c new file mode 100644 index 0000000..5bb837c --- /dev/null +++ b/sexpr/parser.c @@ -0,0 +1,1719 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +#include +#include +#include +#include +#include + +/* we need a locks in this case and we don't have ability to change API */ +#ifndef _NO_MEMORY_MANAGEMENT_ +#include +#endif + +/* + * constants related to atom buffer sizes and growth. + */ +static size_t sexp_val_start_size = 256; +static size_t sexp_val_grow_size = 128; + +/* + * Function for tuning growth parameters. + */ +sexp_errcode_t set_parser_buffer_params(size_t ss, size_t gs) { + if (ss > 0) + sexp_val_start_size = ss; + else + return SEXP_ERR_BAD_PARAM; + + if (gs > 0) + sexp_val_grow_size = gs; + else + return SEXP_ERR_BAD_PARAM; + + return SEXP_ERR_OK; +} + +/** + * this structure is pushed onto the stack so we can keep track of the + * first and last elements in a list. + * !!!!DON'T USE THESE OUTSIDE THIS FILE!!!! + */ +typedef struct parse_stack_data +{ + sexp_t *fst, *lst; +} +parse_data_t; + +/** + * parse_data_t stack - similar malloc prevention to sexp_t_cache. + */ +#ifndef _NO_MEMORY_MANAGEMENT_ +faststack_t *pd_cache; +pthread_mutex_t pd_cache_lock; +#endif + +/** + * The global sexp_t_cache is a faststack implementing a cache of + * pre-alloced s-expression element entities. Odds are a user should never + * touch this. If you do, you're on your own. This is used internally by + * the parser and related code to store unused but allocated sexp_t elements. + * This should be left alone and manipulated only by the sexp_t_allocate and + * sexp_t_deallocate functions. Touching the stack is bad. + */ +#ifndef _NO_MEMORY_MANAGEMENT_ +faststack_t *sexp_t_cache; +pthread_mutex_t sexp_cache_lock; +#endif + +/** + * sexp_t allocation + */ +#ifdef _NO_MEMORY_MANAGEMENT_ +sexp_t * +sexp_t_allocate(void) { + sexp_t *sx = sexp_calloc(1, sizeof(sexp_t)); + if (sx == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + return(sx); +} +#else +sexp_t * +sexp_t_allocate(void) { + sexp_t *sx; + stack_lvl_t *l; + + if (sexp_t_cache == NULL) { + sexp_t_cache = make_stack(); + pthread_mutex_init(&sexp_cache_lock, NULL); + if (sexp_t_cache == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + pthread_mutex_lock(&sexp_cache_lock); +#ifdef __cplusplus + sx = (sexp_t *)sexp_malloc(sizeof(sexp_t)); +#else + sx = sexp_malloc(sizeof(sexp_t)); +#endif + if (sx == NULL) { + pthread_mutex_unlock(&sexp_cache_lock); + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + sx->next = sx->list = NULL; + pthread_mutex_unlock(&sexp_cache_lock); + } else { + pthread_mutex_lock(&sexp_cache_lock); + if (empty_stack(sexp_t_cache)) { +#ifdef __cplusplus + sx = (sexp_t *)sexp_malloc(sizeof(sexp_t)); +#else + sx = sexp_malloc(sizeof(sexp_t)); +#endif + if (sx == NULL) { + pthread_mutex_unlock(&sexp_cache_lock); + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + sx->next = sx->list = NULL; + } else { + l = pop(sexp_t_cache); + sx = (sexp_t *)l->data; + } + pthread_mutex_unlock(&sexp_cache_lock); + } + + return sx; +} +#endif + +/** + * sexp_t de-allocation + */ +#ifdef _NO_MEMORY_MANAGEMENT_ +void +sexp_t_deallocate(sexp_t *s) { + if (s->ty == SEXP_VALUE && s->val != NULL) { + sexp_free(s->val,s->val_allocated); + } + + sexp_free(s,sizeof(sexp_t)); +} +#else +void +sexp_t_deallocate(sexp_t *s) { + if (s == NULL) return; + + if (sexp_t_cache == NULL) { + sexp_t_cache = make_stack(); + pthread_mutex_init(&sexp_cache_lock, NULL); + if (sexp_t_cache == NULL) { + + /**** HOW DO WE GET THE USER TO KNOW SOMETHING HAPPENED? ****/ + + sexp_errno = SEXP_ERR_MEMORY; + if (s->ty == SEXP_VALUE && s->val != NULL) { + sexp_free(s->val,s->val_allocated); + } + sexp_free(s,sizeof(sexp_t)); + return; + } + } + pthread_mutex_lock(&sexp_cache_lock); + + s->list = s->next = NULL; + + if (s->ty == SEXP_VALUE && s->val != NULL) { + sexp_free(s->val,s->val_allocated); + } + + s->val = NULL; + memset(s, 0, sizeof(sexp_t)); + + sexp_t_cache = push(sexp_t_cache, s); + pthread_mutex_unlock(&sexp_cache_lock); +} +#endif + +/** + * cleanup the sexp library. Note this is implemented HERE since we need + * to know about pd_cache, which is local to this file. + */ +#ifdef _NO_MEMORY_MANAGEMENT_ +void sexp_cleanup(void) { +} +#else +void sexp_cleanup(void) { + stack_lvl_t *l; + + if (pd_cache != NULL) { + pthread_mutex_lock(&pd_cache_lock); + l = pd_cache->top; + while (l != NULL) { + sexp_free(l->data,sizeof(parse_data_t)); + l = l->below; + } + destroy_stack(pd_cache); + pd_cache = NULL; + pthread_mutex_unlock(&pd_cache_lock); + pthread_mutex_destroy(&pd_cache_lock); + } + + if (sexp_t_cache != NULL) { + pthread_mutex_lock(&sexp_cache_lock); + l = sexp_t_cache->top; + while (l != NULL) { + sexp_free(l->data,sizeof(sexp_t)); + l = l->below; + } + destroy_stack(sexp_t_cache); + pthread_mutex_lock(&sexp_cache_lock); + pthread_mutex_destroy(&sexp_cache_lock); + sexp_t_cache = NULL; + } +} +#endif + +/** + * allocation + */ +#ifdef _NO_MEMORY_MANAGEMENT_ +parse_data_t * +pd_allocate(void) { + parse_data_t *p = NULL; + p = sexp_malloc(sizeof(parse_data_t)); + return p; +} +#else +parse_data_t * +pd_allocate(void) { + parse_data_t *p; + stack_lvl_t *l; + + if (pd_cache == NULL) { + pthread_mutex_init(&pd_cache_lock, NULL); + pd_cache = make_stack(); + + if (pd_cache == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + pthread_mutex_lock(&pd_cache_lock); +#ifdef __cplusplus + p = (parse_data_t *)sexp_malloc(sizeof(parse_data_t)); +#else + p = sexp_malloc(sizeof(parse_data_t)); +#endif + + if (p == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + pthread_mutex_unlock(&pd_cache_lock); + } else { + pthread_mutex_lock(&pd_cache_lock); + if (empty_stack(pd_cache)) { +#ifdef __cplusplus + p = (parse_data_t *)sexp_malloc(sizeof(parse_data_t)); +#else + p = sexp_malloc(sizeof(parse_data_t)); +#endif + + if (p == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + pthread_mutex_unlock(&pd_cache_lock); + return NULL; + } + } else { + l = pop(pd_cache); + p = (parse_data_t *)l->data; + } + pthread_mutex_unlock(&pd_cache_lock); + } + + return p; +} +#endif /* _NO_MEMORY_MANAGEMENT_ */ + +/** + * de-allocation + */ +#ifdef _NO_MEMORY_MANAGEMENT_ +void +pd_deallocate(parse_data_t *p) { + sexp_free(p, sizeof(parse_data_t)); +} +#else +void +pd_deallocate(parse_data_t *p) { + if (pd_cache == NULL) { + pd_cache = make_stack(); + pthread_mutex_init(&pd_cache_lock, NULL); + if (pd_cache == NULL) { + sexp_free(p, sizeof(parse_data_t)); + sexp_errno = SEXP_ERR_MEMORY; + return; + } + } + pthread_mutex_lock(&pd_cache_lock); + + memset(p, 0, sizeof(parse_data_t)); + pd_cache = push(pd_cache, p); + + pthread_mutex_unlock(&pd_cache_lock); +} +#endif /* _NO_MEMORY_MANAGEMENT_ */ + +/** + * print the current parsing state based on the contents of the parser + * continuation. Useful for error reporting if an error is detected + * while the current expression being parsed is incomplete. + */ +void print_pcont(pcont_t * pc, char * buf, size_t buflen) { + char *cur = buf; + int loc = 0; + int n; + stack_lvl_t *lvl; + parse_data_t *pdata; + sexp_t *sx; + + /* return if either the buffer or continuation are null */ + if (buf == NULL) return; + if (pc == NULL) return; + + /* if continuation has no stack, return */ + if (pc->stack == NULL) return; + + /* start at the bottom of the stack */ + lvl = pc->stack->bottom; + + /* go until we either run out of buffer space or we hit the + top of the stack */ + while (loc < buflen-1 && lvl != NULL) { + /* get the data at the current stack level */ + pdata = (parse_data_t *)lvl->data; + + /* if this is null, we're at a level with nothing added yet */ + if (pdata == NULL) break; + + /* get first fully parsed sexpr for this level. this could be + any sub-expression, like an atom or a full s-expression */ + sx = pdata->fst; + + /* spin through all of the s-expressions at this level */ + while (sx != NULL) { + + /* if we have a list that has no contents, just add the open + paren. this means we haven't finished this expression and the + stack contains it's partial contents. Just print the open paren + and break out so we can pop up the stack. */ + if (sx->ty == SEXP_LIST && sx->list == NULL) { + cur[0] = '('; + cur++; + loc++; + break; + } else { + /* print the fully parsed sub-expression */ + n = print_sexp(cur,buflen-loc,sx); + + /* add a space between this and the next expression. note that + this may induce spaces that were not part of the original + expression. */ + cur[n] = ' '; + + /* increment n to compensate for the space we added */ + n++; + + /* push the pointer into the output buffer forward by n */ + cur += n; + + /* increment counter for location in buffer by n */ + loc += n; + } + + /* go to next s-expr */ + sx = sx->next; + } + + /* go up to next level in stack */ + lvl = lvl->above; + } + + /* at this point, all that may remain is a partially parsed string + that hasn't been turned into a sexpr yet. attach it to the + output string. */ + if (pc->val_used < (buflen-loc)-1) { + strncpy(cur, pc->val, pc->val_used); + cur += pc->val_used; + } else { + /* don't bother if we're so close to the end of the buffer that + we can't attach our null terminator. */ + if (buflen-loc > 2) { + strncpy(cur, pc->val, (buflen-loc)-2); + cur += (buflen-loc)-2; + } + } + + /* add null terminator */ + cur[0] = '\0'; +} + +/** + * Destroy a continuation by freeing all of its fields that it is responsible + * for managing, and then free the continuation itself. This includes internal + * buffers, stacks, etc.. + */ +void +destroy_continuation (pcont_t * pc) +{ + stack_lvl_t *lvl; + parse_data_t *lvl_data; + + if (pc == NULL) return; /* return if null passed in */ + + if (pc->stack != NULL) { + lvl = pc->stack->top; + + /* + * note that destroy_stack() does not free the data hanging off of the + * stack. we have to walk down the stack and do that here. + */ + + while (lvl != NULL) { + lvl_data = (parse_data_t *)lvl->data; + + /** + * Seems to have fixed bug with destroying partially parsed + * expression continuations with the short three lines below. + */ + if (lvl_data != NULL) { + lvl_data->lst = NULL; + destroy_sexp(lvl_data->fst); + lvl_data->fst = NULL; + + pd_deallocate(lvl_data); + lvl->data = lvl_data = NULL; + } + + lvl = lvl->below; + } + + /* + * stack has no data on it anymore, so we can free it. + */ + destroy_stack(pc->stack); + pc->stack = NULL; + } + + /* + * free up data used for INLINE_BINARY mode + */ + if (pc->bindata != NULL) { + sexp_free(pc->bindata,pc->binexpected); + pc->bindata = NULL; + } + + if (pc->val != NULL) { + sexp_free (pc->val,pc->val_allocated); + pc->val = NULL; + } + + sexp_free (pc,sizeof(pcont_t)); +} + +/* + * wrapper around cparse_sexp. assumes s contains a single, complete, + * null terminated s-expression. partial sexps or strings containing more + * than one will act up. + */ +sexp_t * +parse_sexp (char *s, size_t len) +{ + pcont_t *pc = NULL; + sexp_t *sx = NULL; + + if (len < 1 || s == NULL) return NULL; /* empty string - return */ + + pc = cparse_sexp (s, len, pc); + if (pc == NULL) return NULL; /* assume that cparse_sexp set sexp_errno */ + sx = pc->last_sexp; + + destroy_continuation(pc); + + return sx; +} + +pcont_t * +init_continuation(char *str) +{ + pcont_t *cc; + /* new continuation... */ +#ifdef __cplusplus + cc = (pcont_t *)sexp_malloc(sizeof(pcont_t)); +#else + cc = sexp_malloc(sizeof(pcont_t)); +#endif + + if (cc == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + /* allocate atom buffer */ +#ifdef __cplusplus + cc->val = (char *)sexp_calloc(1, sizeof(char)*sexp_val_start_size); +#else + cc->val = sexp_calloc(1, sizeof(char)*sexp_val_start_size); +#endif + + if (cc->val == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + sexp_free(cc,sizeof(pcont_t)); + return NULL; + } + + /* by default we assume a normal parser */ + cc->mode = PARSER_NORMAL; + + cc->val_allocated = sexp_val_start_size; + cc->val_used = 0; + + cc->bindata = NULL; + cc->binread = cc->binexpected = 0; + + /* allocate stack */ + cc->esc = 0; + cc->stack = make_stack(); + + if (cc->stack == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + sexp_free(cc->val,sizeof(char)*sexp_val_start_size); + sexp_free(cc,sizeof(pcont_t)); + return NULL; + } + + cc->sbuffer = str; + cc->lastPos = NULL; + cc->state = 1; + cc->vcur = cc->val; + cc->depth = 0; + cc->qdepth = 0; + cc->squoted = 0; + cc->event_handlers = NULL; + + return cc; +} + +/** + * Iterative parser. Wrapper around parse_sexp that is slightly more + * intelligent and allows users to iteratively "pop" the expressions + * out of a string that contains a bunch of expressions. + * Useful if you have a string like "(foo bar)(goo har)(moo mar)" and + * want to get "(foo bar)", "(goo har)", and "(moo mar)" individually on + * repeated calls. + */ +sexp_t * +iparse_sexp (char *s, size_t len, pcont_t *cc) { + pcont_t *pc; + sexp_t *sx = NULL; + + /* + * error check. note that cc must be non-null, as this routine returns + * a sexp_t . If cc is null and a new one gets allocated, there is no + * way to return it. Thus this call requires cc to be allocated outside + * the routine. A null return value should cause sexp_errno to be checked. + */ + if (cc == NULL) { + sexp_errno = SEXP_ERR_BAD_PARAM; + return NULL; + } + + /* call the parser */ + pc = cparse_sexp(s,len,cc); + + if (pc == NULL) return NULL; /* assume cparse_sexp set sexp_errno */ + + if (cc->last_sexp != NULL) { + sx = cc->last_sexp; + cc->last_sexp = NULL; + } + + return sx; +} + +/*************************************************************************/ +/*************************************************************************/ +/*************************************************************************/ +/*************************************************************************/ +/*************************************************************************/ + +/* TEMPORARY -- THIS WILL GO AWAY WHEN eparse_sexp GETS ROLLED BACK INTO + * cparse_sexp */ +pcont_t *eparse_sexp (char *str, size_t len, pcont_t *lc); + +#ifdef REGISTEROPTIMIZAITON +#define REGISTER register +#else +#define REGISTER +#endif + +/** + * Continuation based parser - the guts of the package. + */ +pcont_t * +cparse_sexp (char *str, size_t len, pcont_t *lc) +{ + char *t = NULL; + char *s = NULL; + REGISTER size_t binexpected = 0; + REGISTER size_t binread = 0; + REGISTER parsermode_t mode = PARSER_NORMAL; + REGISTER size_t val_allocated = 0; + REGISTER unsigned int squoted = 0; + REGISTER size_t val_used = 0; + REGISTER unsigned int state = 1; + REGISTER unsigned int depth = 0; + REGISTER unsigned int qdepth = 0; + REGISTER unsigned int elts = 0; + REGISTER unsigned int esc = 0; + pcont_t *cc = NULL; + char *val = NULL; + char *vcur = NULL; + char *bindata = NULL; + sexp_t *sx = NULL; + faststack_t *stack = NULL; + parse_data_t *data = NULL; + stack_lvl_t *lvl = NULL; + char *bufEnd = NULL; + int keepgoing = 1; + parser_event_handlers_t *event_handlers = NULL; + + /*** define a macro used for stashing continuation state away ***/ + /** NOTE1: sbuffer is set manually as appropriate. **/ + /** NOTE2: this also sets sexp_errno to the same value as the + error field in the continuation. This used to be + done in iparse_sexp and parse_sexp, but that meant that + direct callers of cparse_sexp would see inconsistent errors. + sexp_errno could say one thing, but cc would say the other. + This has been fixed. **/ +#define SAVE_CONT_STATE(err,ls) { \ + cc->bindata = bindata; \ + cc->binread = binread; \ + cc->binexpected = binexpected; \ + cc->val = val; \ + cc->mode = mode; \ + cc->squoted = squoted; \ + cc->val_used = val_used; \ + cc->val_allocated = val_allocated; \ + cc->vcur = vcur; \ + cc->lastPos = t; \ + cc->depth = depth; \ + cc->qdepth = qdepth; \ + cc->state = state; \ + cc->stack = stack; \ + cc->esc = esc; \ + cc->last_sexp = (ls); \ + cc->error = (err); \ + cc->event_handlers = event_handlers; \ + sexp_errno = (err); \ +} + /*** end continuation state saving macro ***/ + + /* make sure non-null string */ + if (str == NULL) { + cc = lc; + + if (cc == NULL) { + cc = init_continuation(str); + if (cc == NULL) return NULL; /* sexp_errno was set in call */ + } + cc->error = SEXP_ERR_NULLSTRING; + cc->last_sexp = NULL; + + return cc; + } + + /* first, if we have a non null continuation passed in, restore state. */ + if (lc != NULL) { + /* if the parser mode is events only, call the parser that doesn't + allocate any elements or stack parts */ + if (lc->mode == PARSER_EVENTS_ONLY) + return eparse_sexp(str,len,lc); + + cc = lc; + binexpected = cc->binexpected; + binread = cc->binread; + bindata = cc->bindata; + val_used = cc->val_used; + val_allocated = cc->val_allocated; + squoted = cc->squoted; + val = cc->val; + vcur = cc->vcur; + state = cc->state; + depth = cc->depth; + qdepth = cc->qdepth; + stack = cc->stack; + esc = cc->esc; + mode = cc->mode; + event_handlers = cc->event_handlers; + s = str; + if (cc->lastPos != NULL) + t = cc->lastPos; + else { + t = s; + cc->sbuffer = str; + } + } else { + /* new continuation... */ + cc = init_continuation(str); + if (cc == NULL) return NULL; + + /* explicitly set mode -- init continuation defaults to PARSER_NORMAL */ + cc->mode = mode; + val = cc->val; + + val_used = cc->val_used; + val_allocated = cc->val_allocated; + + vcur = val; + + /* allocate stack */ + stack = cc->stack; + + /* t is temp pointer into s for current position */ + s = str; + t = s; + } + + bufEnd = cc->sbuffer+len; + + /* guard for loop - see end of loop for info. Put it out here in the + event that we're restoring state from a continuation and need to + check before we start up. */ + if (state != 15 && t[0] == '\0') keepgoing = 0; + + /*==================*/ + /* main parser loop */ + /*==================*/ + while (keepgoing == 1 && t != bufEnd) + { + /* based on the current state in the FSM, do something */ + switch (state) + { + case 1: + switch (t[0]) + { + /* space,tab,CR,LF considered white space */ + case '\n': + case ' ': + case '\t': + case '\r': + t++; + break; + /* semicolon starts a comment that extends until a \n is + encountered. */ + case ';': + t++; + state = 11; + break; + /* enter state 2 for open paren */ + case '(': + state = 2; + t++; + if (event_handlers != NULL && + event_handlers->start_sexpr != NULL) + event_handlers->start_sexpr(); + break; + /* enter state 3 for close paren */ + case ')': + state = 3; + break; + /* begin quoted string - enter state 5 */ + case '\"': + state = 5; + /* set cur pointer to beginning of val buffer */ + vcur = val; + t++; + break; + /* single quote - enter state 7 */ + case '\'': + state = 7; + t++; + break; + /* other characters are assumed to be atom parts */ + default: + /* set cur pointer to beginning of val buffer */ + vcur = val; + + /** NOTE: the following code originally required a transition + to state 4 before processing the first atom character -- + this required two iterations for the first character + of each atom. merging this into here allows us to process + what we already know to be a valid atom character before + entering state 4. **/ + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { +#ifdef __cplusplus + val = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + val = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + + if (val == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY,NULL); + return cc; + } + + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + /* if the atom starts with # and we're in inline + binary mode, we need to go to state 12 to start + checking for the #b# prefix. otherwise, + if it's not a # or we're just in normal mode, + proceed to state 4 as usual. */ + if (t[0] == '#' && mode == PARSER_INLINE_BINARY) { + state = 12; + } else { + state = 4; + } + + t++; + break; + } + break; + case 2: + /* open paren */ + depth++; + + sx = sexp_t_allocate(); + + if (sx == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY,NULL); + return cc; + } + + elts++; + sx->ty = SEXP_LIST; + sx->next = NULL; + sx->list = NULL; + + if (stack->height < 1) + { + data = pd_allocate(); + + if (data == NULL) { + sexp_t_deallocate(sx); + SAVE_CONT_STATE(SEXP_ERR_MEMORY,NULL); + return cc; + } + + data->fst = data->lst = sx; + push (stack, data); + } + else + { + data = (parse_data_t *) top_data (stack); + if (data->lst != NULL) + data->lst->next = sx; + else + data->fst = sx; + data->lst = sx; + } + + data = pd_allocate(); + if (data == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY,NULL); + return cc; + } + data->fst = data->lst = NULL; + push (stack, data); + + state = 1; + break; + case 3: + /** close paren **/ + + /* check for close parens that were never opened. */ + if (depth == 0) { + esc = 0; + state = 1; + SAVE_CONT_STATE(SEXP_ERR_BADFORM,NULL); + return cc; + } + + t++; + depth--; + + lvl = pop (stack); + data = (parse_data_t *) lvl->data; + sx = data->fst; + pd_deallocate(data); + lvl->data = NULL; + + if (stack->top != NULL) + { + data = (parse_data_t *) top_data (stack); + data->lst->list = sx; + } + else + { + SAVE_CONT_STATE(SEXP_ERR_BAD_STACK, NULL); + return cc; + } + + if (event_handlers != NULL && + event_handlers->end_sexpr != NULL) + event_handlers->end_sexpr(); + + state = 1; + + /** if depth = 0 then we finished a sexpr, and we return **/ + if (depth == 0) { + while (stack->top != NULL) + { + lvl = pop (stack); + data = (parse_data_t *) lvl->data; + sx = data->fst; + pd_deallocate(data); + lvl->data = NULL; + } + + esc = 0; + state = 1; + SAVE_CONT_STATE(SEXP_ERR_OK, sx); + + return cc; + } + break; + case 4: /** parsing atom **/ + if (esc == 1 && (t[0] == '\"' || t[0] == '(' || + t[0] == ')' || t[0] == '\'' || + t[0] == '\\')) { + vcur--; /* back up to overwrite the \ */ + vcur[0] = t[0]; + vcur++; + t++; + esc = 0; + break; + } + + /* look at an ascii table - these ranges are the non-whitespace, non + paren and quote characters that are legal in atoms */ + if (!((t[0] >= '*' && t[0] <= '~') || + ((unsigned char)(t[0]) > 127) || + (t[0] == '!') || + (t[0] >= '#' && t[0] <= '&'))) + { + vcur[0] = '\0'; + val_used++; + + sx = sexp_t_allocate(); + + if (sx == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + elts++; + sx->ty = SEXP_VALUE; + sx->val = val; + sx->val_allocated = val_allocated; + sx->val_used = val_used; + sx->next = NULL; + if (squoted != 0) + sx->aty = SEXP_SQUOTE; + else + sx->aty = SEXP_BASIC; + + if (event_handlers != NULL && + event_handlers->characters != NULL) + event_handlers->characters(sx->val,sx->val_used,sx->aty); + +#ifdef __cplusplus + val = (char *)sexp_malloc(sizeof(char)*sexp_val_start_size); +#else + val = sexp_malloc(sizeof(char)*sexp_val_start_size); +#endif + + if (val == NULL) { + sexp_t_deallocate(sx); + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val_allocated = sexp_val_start_size; + val_used = 0; + vcur = val; + + if (!empty_stack (stack)) + { + data = (parse_data_t *) top_data (stack); + if (data->fst == NULL) + { + data->fst = data->lst = sx; + } + else + { + data->lst->next = sx; + data->lst = sx; + } + } + else + { + /* looks like this expression was just a basic atom - so + return it. */ + squoted = 0; + state = 1; + esc = 0; + SAVE_CONT_STATE(SEXP_ERR_OK, sx); + return cc; + } + + switch (t[0]) { + case ' ': + case '\t': + case '\n': + case '\r': + /** NOTE: we know whitespace following atom, so spin ahead + one and let state 1 do what it needs to for the next + character. **/ + state = 1; + t++; + squoted = 0; + break; + case ')': + squoted = 0; + state = 3; + break; + default: + squoted = 0; + state = 1; + } + } + else + { + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { + char *valnew = NULL; +#ifdef __cplusplus + valnew = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + valnew = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + + if (valnew == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val = valnew; + + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + t++; + } + break; + case 5: + if (esc == 1 && (t[0] == '\"' || + t[0] == '\'' || + t[0] == '(' || + t[0] == ')' || + t[0] == '\\')) { + vcur--; + vcur[0] = t[0]; + vcur++; + /** NO NEED TO UPDATE VAL COUNTS **/ + t++; + esc = 0; + } + + if (t[0] == '\"') + { + state = 6; + + if (squoted == 1) { + vcur[0] = '\"'; + val_used++; + + if (val_used == val_allocated) { + char *valnew = NULL; + +#ifdef __cplusplus + valnew = (char *)sexp_realloc(val, + val_allocated+ + sexp_val_grow_size, + val_allocated); +#else + valnew = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + + if (valnew == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val = valnew; + + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + } + + vcur[0] = '\0'; + + val_used++; + sx = sexp_t_allocate(); + + if (sx == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + elts++; + sx->ty = SEXP_VALUE; + sx->val = val; + sx->val_used = val_used; + sx->val_allocated = val_allocated; + sx->next = NULL; + + if (squoted == 1) { + sx->aty = SEXP_SQUOTE; + squoted = 0; + } else + sx->aty = SEXP_DQUOTE; + + if (event_handlers != NULL && + event_handlers->characters != NULL) + event_handlers->characters(sx->val,sx->val_used,sx->aty); + +#ifdef __cplusplus + val = (char *)sexp_malloc(sizeof(char)*sexp_val_start_size); +#else + val = sexp_malloc(sizeof(char)*sexp_val_start_size); +#endif + + if (val == NULL) { + sexp_t_deallocate(sx); + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val_allocated = sexp_val_start_size; + val_used = 0; + vcur = val; + + if (!empty_stack (stack)) + { + data = (parse_data_t *) top_data (stack); + if (data->fst == NULL) + { + data->fst = data->lst = sx; + } + else + { + data->lst->next = sx; + data->lst = sx; + } + } + else + { + /* looks like this expression was just a basic double + quoted atom - so return it. */ + t++; /* spin past the quote */ + + squoted = 0; + esc = 0; + state = 1; + SAVE_CONT_STATE(SEXP_ERR_OK, sx); + + return cc; + } + } + else + { + vcur[0] = t[0]; + val_used++; + + if (val_used == val_allocated) { + char *valnew = NULL; + +#ifdef __cplusplus + valnew = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + valnew = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + + if (valnew == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val = valnew; + + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + if (t[0] == '\\') { + esc = 1; + } else + esc = 0; + } + + t++; + break; + case 6: + vcur = val; + state = 1; + break; + case 7: + if (t[0] == '\"') + { + state = 5; + vcur = val; + t++; + + vcur[0] = '\"'; + val_used++; + + if (val_used == val_allocated) { + char *valnew = NULL; + +#ifdef __cplusplus + valnew = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + valnew = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + if (valnew == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val = valnew; + + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + squoted = 1; + } + else if (t[0] == '(') + { + vcur = val; + state = 8; + } + else + { + vcur = val; + state = 4; + squoted = 1; + } + break; + case 8: + if (esc == 0) { + if (t[0] == '(') + { + qdepth++; + } + else if (t[0] == ')') + { + qdepth--; + state = 9; + } + else if (t[0] == '\"') + { + state = 10; + } + } else { + esc = 0; + } + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { + char *valnew = NULL; + +#ifdef __cplusplus + valnew = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + valnew = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + if (valnew == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val = valnew; + + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + t++; + /* let it fall through to state 9 if we know we're transitioning + into that state */ + if (state != 9) + break; + case 9: + if (qdepth == 0) + { + state = 1; + vcur[0] = '\0'; + sx = sexp_t_allocate(); + + if (sx == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + elts++; + sx->ty = SEXP_VALUE; + sx->val = val; + sx->val_allocated = val_allocated; + sx->val_used = val_used; + sx->next = NULL; + sx->aty = SEXP_SQUOTE; + + if (event_handlers != NULL && + event_handlers->characters != NULL) + event_handlers->characters(sx->val,sx->val_used,sx->aty); + +#ifdef __cplusplus + val = (char *)sexp_malloc(sizeof(char)*sexp_val_start_size); +#else + val = sexp_malloc(sizeof(char)*sexp_val_start_size); +#endif + + if (val == NULL) { + sexp_t_deallocate(sx); + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val_allocated = sexp_val_start_size; + val_used = 0; + vcur = val; + + if (!empty_stack (stack)) + { + data = (parse_data_t *) top_data (stack); + if (data->fst == NULL) + { + data->fst = data->lst = sx; + } + else + { + data->lst->next = sx; + data->lst = sx; + } + } + else + { + /* looks like the whole expression was a single + quoted value! So return it. */ + squoted = 0; + esc = 0; + state = 1; + SAVE_CONT_STATE(SEXP_ERR_OK, sx); + return cc; + } + } + else + state = 8; + break; + case 10: + if (t[0] == '\"' && esc == 0) + { + state = 8; + } + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { + char *valnew = NULL; + +#ifdef __cplusplus + valnew = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + valnew = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + + if (valnew == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val = valnew; + + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + t++; + break; + case 11: + if (t[0] == '\n') { + state = 1; + } + t++; + break; + case 12: /* pre: we saw a # and we're in inline binary mode */ + if (t[0] == 'b') { + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { + char *valnew = NULL; + +#ifdef __cplusplus + valnew = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + valnew = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + + if (valnew == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val = valnew; + + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + state = 13; /* so far, #b */ + t++; + } else { + state = 4; /* not #b, so plain ol' atom */ + } + + break; + + case 13: /* pre: we saw a #b and we're in inline binary mode */ + if (t[0] == '#') { + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { + char *valnew = NULL; + +#ifdef __cplusplus + valnew = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + valnew = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + + if (valnew == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val = valnew; + + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + state = 14; /* so far, #b# - we're definitely in binary + land now. */ + /* reset vcur to val, overwrite #b# with the size string. */ + vcur = val; + val_used = 0; + t++; + } else { + state = 4; /* not #b#, so plain ol' atom */ + } + + break; + + case 14: + /** + * so far we've read #b#. Now, the steps of the process become: + * proceed to read bytes in until we see # again. This will be + * an ASCII representation of the size. At this point, we want + * to read as many bytes as specified by this size string after + * the #. + */ + if (t[0] == '#') { /* done with size string */ + t++; + state = 15; + vcur[0] = '\0'; + + binexpected = (size_t) atoi(val); + + binread = 0; + if (binexpected > 0) { +#ifdef __cplusplus + bindata = (char *)sexp_malloc(sizeof(char)*binexpected); +#else + bindata = sexp_malloc(sizeof(char)*binexpected); +#endif + + if (bindata == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + } else { + bindata = NULL; + } + } else { /* still reading size string */ + vcur[0] = t[0]; + if (t[0] == '\\') esc = 1; + else esc = 0; + val_used++; + + if (val_used == val_allocated) { + char *valnew = NULL; + +#ifdef __cplusplus + valnew = (char *)sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#else + valnew = sexp_realloc(val, + val_allocated+sexp_val_grow_size, + val_allocated); +#endif + + if (valnew == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + val = valnew; + + vcur = val + val_used; + val_allocated += sexp_val_grow_size; + } else vcur++; + + t++; + } + + break; + + case 15: /* reading binary blob */ + if (binread < binexpected) { + bindata[binread] = t[0]; + binread++; + t++; + } + + if (binread == binexpected) { + /* state = 1 -- create a sexp_t and head back */ + sx = sexp_t_allocate(); + + if (sx == NULL) { + SAVE_CONT_STATE(SEXP_ERR_MEMORY, NULL); + return cc; + } + + elts++; + sx->ty = SEXP_VALUE; + sx->bindata = bindata; + sx->binlength = binread; + sx->next = NULL; + sx->aty = SEXP_BINARY; + + if (event_handlers != NULL && + event_handlers->binary != NULL) + event_handlers->binary(sx->bindata, sx->binlength); + + bindata = NULL; + binread = binexpected = 0; + + state = 1; + + val_used = 0; + vcur = val; + + if (!empty_stack (stack)) + { + data = (parse_data_t *) top_data (stack); + if (data->fst == NULL) + { + data->fst = data->lst = sx; + } + else + { + data->lst->next = sx; + data->lst = sx; + } + } + + } + + break; + + default: + SAVE_CONT_STATE(SEXP_ERR_UNKNOWN_STATE, NULL); + return cc; + } + + /* the null check used to be part of the guard on the while loop. + unfortunately, if we're in state 15, null is considered a + perfectly valid byte. This means the length passed in better + be accurate for the parser to not walk off the end of the + string! */ + if (state != 15 && t[0] == '\0') keepgoing = 0; + } + + if (depth == 0 && elts > 0) { + while (stack->top != NULL) + { + lvl = pop (stack); + data = (parse_data_t *) lvl->data; + sx = data->fst; + pd_deallocate(data); + lvl->data = NULL; + } + + esc = 0; + state = 1; + SAVE_CONT_STATE(SEXP_ERR_OK, sx); + } else { + SAVE_CONT_STATE(SEXP_ERR_INCOMPLETE, NULL); + if (t[0] == '\0' || t == bufEnd) + cc->lastPos = NULL; + else + cc->lastPos = t; + } + + return cc; +} diff --git a/sexpr/sexp.c b/sexpr/sexp.c new file mode 100644 index 0000000..8232fc8 --- /dev/null +++ b/sexpr/sexp.c @@ -0,0 +1,589 @@ +/** +@Cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +#include +#include +#include +#include +#include + +/* + * global error code that can be set by sexp library calls. default + * is SEXP_ERR_OK. + */ +sexp_errcode_t sexp_errno = SEXP_ERR_OK; + +void reset_sexp_errno() { + sexp_errno = SEXP_ERR_OK; +} + +/** + * Recursively walk an s-expression and free it. + */ +void +destroy_sexp (sexp_t * s) +{ + if (s == NULL) + return; + + if (s->ty == SEXP_LIST) { + destroy_sexp (s->list); + } else if (s->ty == SEXP_VALUE) { + if (s->aty == SEXP_BINARY && s->bindata != NULL) { + sexp_free(s->bindata, s->binlength); + } else if (s->val != NULL) { + sexp_free(s->val, s->val_allocated); + } + } + + s->val = NULL; + s->bindata = NULL; + + destroy_sexp (s->next); + + s->next = s->list = NULL; + + sexp_t_deallocate(s); +} + +/** + * Iterative method to walk sx and turn it back into the string + * representation of the s-expression. Fills the buffer. + */ +int +print_sexp (char *buf, size_t size, const sexp_t * sx) +{ + int retval; + size_t sz; + char *b = buf, *tc; + size_t left = size; + int depth = 0; + faststack_t *stack; + stack_lvl_t *top; + sexp_t *tdata; + sexp_t *fakehead; + sexp_t tmp; + + if (sx == NULL) { + buf[0] = '\0'; + return 0; + } + + tmp = *sx; + tmp.next = tmp.list = NULL; + + fakehead = copy_sexp(&tmp); + + if (fakehead == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return -1; + } + + fakehead->list = sx->list; + fakehead->next = NULL; /* this is the important part of fakehead */ + + stack = make_stack (); + if (stack == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + sexp_t_deallocate(fakehead); + return -1; + } + + push (stack, fakehead); + + while (stack->top != NULL) + { + top = stack->top; + tdata = (sexp_t *) top->data; + + if (tdata == NULL) + { + pop (stack); + + if (depth > 0) + { + b[0] = ')'; + b++; + left--; + depth--; + if (left == 0) + { + sexp_errno = SEXP_ERR_BUFFER_FULL; + break; + } + } + + if (stack->top == NULL) + break; + + top = stack->top; + top->data = ((sexp_t *) top->data)->next; + if (top->data != NULL) + { + b[0] = ' '; + b++; + left--; + if (left == 0) + { + sexp_errno = SEXP_ERR_BUFFER_FULL; + break; + } + } + } + else if (tdata->ty == SEXP_VALUE) + { + if (tdata->aty == SEXP_DQUOTE) + { + b[0] = '\"'; + b++; + left--; + } + else if (tdata->aty == SEXP_SQUOTE) + { + b[0] = '\''; + b++; + left--; + } + + if (tdata->aty != SEXP_BINARY && tdata->val_used > 0) { + tc = tdata->val; + /* copy value into string */ + while (tc[0] != 0 && left > 0) + { + /* escape characters that need escaping. */ + if ((tc[0] == '\"' || tc[0] == '\\') && + tdata->aty == SEXP_DQUOTE) + { + b[0] = '\\'; + b++; + left--; + if (left == 0) break; + } + + b[0] = tc[0]; + b++; + tc++; + left--; + if (left == 0) + break; + } + } else { + if (left > 3) { + b[0] = '#'; b[1] = 'b'; b[2] = '#'; + b+=3; + left-=3; + +#ifndef WIN32 + if ((size_t)(sz = snprintf(b,left,"%lu#",(unsigned long)tdata->binlength)) >= left) { +#else + if ((sz = _snprintf(b,left,"%lu#",tdata->binlength)) >= left) { +#endif + left = 0; + break; + } + b += sz; + left -= sz; + + if (left < tdata->binlength) { + left = 0; + break; + } + + if (tdata->binlength > 0) { + memcpy(b,tdata->bindata,tdata->binlength); + left -= tdata->binlength; + b+=tdata->binlength; + } + + b[0] = ' '; + left--; + + } else { + left = 0; + break; + } + } + + if (tdata->aty == SEXP_DQUOTE && left > 0) + { + b[0] = '\"'; + b++; + left--; + } + + if (left == 0) + { + sexp_errno = SEXP_ERR_BUFFER_FULL; + break; + } + + top->data = ((sexp_t *) top->data)->next; + + if (top->data != NULL) + { + b[0] = ' '; + b++; + left--; + if (left == 0) + { + sexp_errno = SEXP_ERR_BUFFER_FULL; + break; + } + } + } + else if (tdata->ty == SEXP_LIST) + { + depth++; + b[0] = '('; + b++; + left--; + if (left == 0) + { + sexp_errno = SEXP_ERR_BUFFER_FULL; + break; + } + + push (stack, tdata->list); + } + else + { + sexp_errno = SEXP_ERR_BADCONTENT; + destroy_stack (stack); + sexp_t_deallocate(fakehead); + return -1; + } + + } + while (depth != 0) + { + b[0] = ')'; + b++; + left--; + depth--; + if (left == 0) + { + sexp_errno = SEXP_ERR_BUFFER_FULL; + break; + } + } + + if (left != 0) { + b[0] = 0; + retval = (int) (size-left); + } else { + b--; + b[0] = 0; + retval = -1; + } + + destroy_stack (stack); + sexp_t_deallocate(fakehead); + + return retval; +} + +/** + * Iterative method to walk sx and turn it back into the string + * representation of the s-expression. Fills the CSTRING that is + * passed in. If *s == NULL (new CSTRING, never used), snew() is called + * and passed back. If *s != NULL, *s is used as the CSTRING to print + * into. In the last case, the recycled CSTRING must have sempty() called + * to reset the allocated vs. used counters to make it appear to be empty. + * the code will assume that sempty() was called by the user! + */ +int +print_sexp_cstr (CSTRING **s, const sexp_t *sx, size_t ss) +{ + int retval; + char *tc; + int depth = 0; + faststack_t *stack; + stack_lvl_t *top; + sexp_t *tdata; + sexp_t *fakehead; + CSTRING *_s = NULL; + char sbuf[32]; + unsigned int i; + sexp_t tmp; + + if (sx == NULL) { + return -1; + } + + if (*s == NULL) + _s = snew(ss); + else + _s = *s; + + tmp = *sx; + tmp.next = tmp.list = NULL; + + fakehead = copy_sexp(&tmp); + + if (fakehead == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return -1; + } + + fakehead->list = sx->list; + fakehead->next = NULL; /* this is the important part of fakehead */ + + stack = make_stack (); + if (stack == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + sexp_t_deallocate(fakehead); + return -1; + } + + push (stack, fakehead); + + while (stack->top != NULL) + { + top = stack->top; + tdata = (sexp_t *) top->data; + + if (tdata == NULL) + { + pop (stack); + + if (depth > 0) + { + _s = saddch(_s, ')'); + depth--; + } + + if (stack->top == NULL) + break; + + top = stack->top; + top->data = ((sexp_t *) top->data)->next; + if (top->data != NULL) + { + _s = saddch(_s, ' '); + } + } + else if (tdata->ty == SEXP_VALUE) + { + if (tdata->aty == SEXP_DQUOTE) + { + _s = saddch(_s,'\"'); + } + else if (tdata->aty == SEXP_SQUOTE) + { + _s = saddch(_s,'\''); + } + + if (tdata->aty == SEXP_BINARY) { + sprintf(sbuf,"#b#%lu#",(unsigned long)tdata->binlength); + + _s = sadd(_s,sbuf); + + for (i=0;ibinlength;i++) + _s = saddch(_s,tdata->bindata[i]); + _s = saddch(_s,' '); + } else { + if (tdata->val_used > 0) { + tc = tdata->val; + + /* copy value into string */ + while (tc[0] != 0) + { + /* escape characters that need escaping. */ + if ((tc[0] == '\"' || + tc[0] == '\\') && tdata->aty == SEXP_DQUOTE) + { + _s = saddch(_s,'\\'); + } + + _s = saddch(_s,tc[0]); + tc++; + } + } + } + + if (tdata->aty == SEXP_DQUOTE) + { + _s = saddch(_s,'\"'); + } + + top->data = ((sexp_t *) top->data)->next; + + if (top->data != NULL) + { + _s = saddch(_s,' '); + } + } + else if (tdata->ty == SEXP_LIST) + { + depth++; + _s = saddch(_s,'('); + push (stack, tdata->list); + } + else + { + sexp_errno = SEXP_ERR_BADCONTENT; + destroy_stack (stack); + sexp_t_deallocate(fakehead); + return -1; + } + + } + while (depth != 0) + { + _s = saddch(_s,')'); + depth--; + } + + *s = _s; + if (_s == NULL) + retval = 0; + else + retval = (int) _s->curlen; + + destroy_stack (stack); + sexp_t_deallocate(fakehead); + + return retval; +} + +/** + * Allocate a new sexp_t element representing a list. + */ +sexp_t *new_sexp_list(sexp_t *l) { + sexp_t *sx = sexp_t_allocate(); + + if (sx == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + sx->ty = SEXP_LIST; + + sx->list = l; + sx->next = NULL; + + sx->val = NULL; + sx->val_used = sx->val_allocated = 0; + + return sx; +} + +/** + * allocate a new sexp_t element representing a raw binary value + */ +sexp_t *new_sexp_binary_atom(char *data, size_t binlength) { + sexp_t *sx = sexp_t_allocate(); + + if (sx == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + sx->ty = SEXP_VALUE; + sx->next = sx->list = NULL; + sx->aty = SEXP_BINARY; + sx->bindata = data; + sx->binlength = binlength; + sx->val = NULL; + sx->val_used = sx->val_allocated = 0; + + return sx; +} + +/** + * allocate a new sexp_t element representing a value + */ +sexp_t *new_sexp_atom(const char *buf, size_t bs, atom_t aty) { + sexp_t *sx = NULL; + + if (aty == SEXP_BINARY) { + sexp_errno = SEXP_ERR_BAD_CONSTRUCTOR; + return NULL; + } + + sx = sexp_t_allocate(); + + if (sx == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + sx->ty = SEXP_VALUE; + sx->aty = aty; + +#ifdef __cplusplus + sx->val = (char *)sexp_malloc(sizeof(char)*(bs+1)); +#else + sx->val = sexp_malloc(sizeof(char)*(bs+1)); +#endif + + if (sx->val == NULL) { + sexp_t_deallocate(sx); + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + sx->val_used = sx->val_allocated = bs+1; + + strcpy(sx->val,buf); + + sx->list = sx->next = NULL; + + return sx; +} + +/* sexp helpers */ +int sexp_list_car(sexp_t *expr, sexp_t **sx) +{ + if (!SEXP_IS_LIST(expr) || expr->list->ty != SEXP_VALUE) return 1; + + *sx = expr->list; + + return 0; +} + +int sexp_list_cdr(sexp_t *expr, sexp_t **sx) +{ + /* Dummy function. Can we do cdr properly? */ + if (!SEXP_IS_LIST(expr) || expr->list->ty != SEXP_VALUE) return 1; + + if (!expr->list->next) *sx = NULL; + else *sx = expr->list->next; + + return 0; +} + + diff --git a/sexpr/sexp_memory.c b/sexpr/sexp_memory.c new file mode 100644 index 0000000..2961e64 --- /dev/null +++ b/sexpr/sexp_memory.c @@ -0,0 +1,125 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +#include +#include +#include +#include +#include + +#ifdef _SEXP_LIMIT_MEMORY_ + +static size_t sexp_max_memory = 32*1024*1024; /* default: 32MB */ +static size_t sexp_used_memory = 0; + +size_t get_sexp_max_memory() { + return sexp_max_memory; +} + +size_t get_sexp_used_memory() { + return sexp_used_memory; +} + +int set_sexp_max_memory(size_t newsize) { + if (newsize > 0) { + if (newsize < sexp_used_memory) { + sexp_errno = SEXP_ERR_BAD_PARAM; + return -1; + } else { + sexp_max_memory = newsize; + } + } else { + sexp_errno = SEXP_ERR_BAD_PARAM; + return -1; + } + + return sexp_max_memory; +} + +void *sexp_malloc(size_t size) { + void *ptr; + + if (sexp_used_memory+size > sexp_max_memory) { + sexp_errno = SEXP_ERR_MEM_LIMIT; + return NULL; + } + + ptr = malloc(size); + if (ptr != NULL) sexp_used_memory += size; + + return ptr; +} + +void *sexp_calloc(size_t count, size_t size) { + void *ptr; + + if (sexp_used_memory+(size*count) > sexp_max_memory) { + sexp_errno = SEXP_ERR_MEM_LIMIT; + return NULL; + } + + ptr = calloc(count, size); + if (ptr != NULL) sexp_used_memory += size*count; + + return ptr; +} + + +void sexp_free(void *ptr, size_t size) { + if (sexp_used_memory < size) { + fprintf(stderr,"ERROR: sexp_free called too many times!\n"); + } else { + sexp_used_memory -= size; + } + + free(ptr); +} + +void *sexp_realloc(void *ptr, size_t size, size_t oldsize) { + void *p; + + if (sexp_used_memory+(size-oldsize) > sexp_max_memory) { + sexp_errno = SEXP_ERR_MEM_LIMIT; + return NULL; + } + + p = realloc(ptr,size); + if (p != NULL) sexp_used_memory += size-oldsize; + + return p; +} + +#endif /* _SEXP_LIMIT_MEMORY_ */ diff --git a/sexpr/sexp_ops.c b/sexpr/sexp_ops.c new file mode 100644 index 0000000..ca15436 --- /dev/null +++ b/sexpr/sexp_ops.c @@ -0,0 +1,224 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +#include +#include +#include +#include + +/** + * Given an s-expression, find the atom inside of it with the + * value matchine name, and return a reference to it. If the atom + * doesn't occur inside start, return NULL. + */ +sexp_t * +find_sexp (const char *name, sexp_t * start) +{ + sexp_t *temp; + + if (start == NULL) + return NULL; + + if (start->ty == SEXP_LIST) + { + temp = find_sexp (name, start->list); + if (temp == NULL) + return find_sexp (name, start->next); + else + return temp; + } + else + { + if (start->val != NULL && strcmp (start->val, name) == 0) + return start; + else + return find_sexp (name, start->next); + } + + return NULL; /* shouldn't get here */ +} + +/** + * Breadth first search - look at ->next before ->list when seeing list + * elements of an expression. + */ +sexp_t *bfs_find_sexp(const char *str, sexp_t *sx) { + sexp_t *t = sx; + sexp_t *rt; + + if (sx == NULL) return NULL; + + while (t != NULL) { + if (t->ty == SEXP_VALUE) { + if (t->val != NULL) { + if (strcmp(t->val,str) == 0) { + return t; + } + } + } + + t = t->next; + } + + t = sx; + while (t != NULL) { + if (t->ty == SEXP_LIST) { + rt = bfs_find_sexp(str,t->list); + if (rt != NULL) return rt; + } + + t = t->next; + } + + return NULL; +} + +/** + * Give the length of a s-expression list. + */ +int sexp_list_length(const sexp_t *sx) { + int len = 0; + const sexp_t *t; + + if (sx == NULL) return 0; + + if (sx->ty == SEXP_VALUE) return 1; + + t = sx->list; + + while (t != NULL) { + len++; + t = t->next; + } + return len; +} + +/** + * Copy an s-expression. + */ +sexp_t *copy_sexp(const sexp_t *s) { + sexp_t *s_new; + + if (s == NULL) return NULL; + + s_new = sexp_t_allocate(); + if (s_new == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + return NULL; + } + + /* initialize fields to null and zero, and fill in only those necessary. */ + s_new->val_allocated = s_new->val_used = 0; + s_new->val = NULL; + s_new->list = s_new->next = NULL; + s_new->bindata = NULL; + s_new->binlength = 0; + + /* now start copying in data and setting appropriate fields. */ + s_new->ty = s->ty; + + /* values */ + if (s_new->ty == SEXP_VALUE) { + s_new->aty = s->aty; + + /* binary */ + if (s_new->aty == SEXP_BINARY) { + if (s->bindata == NULL && s->binlength > 0) { + sexp_errno = SEXP_ERR_BADCONTENT; + sexp_t_deallocate(s_new); + return NULL; + } + + s_new->binlength = s->binlength; + + if (s->bindata == NULL) { + s_new->bindata = NULL; + } else { + /** allocate space **/ +#ifdef __cplusplus + s_new->bindata = (char *)sexp_malloc(sizeof(char)*s->binlength); +#else + s_new->bindata = sexp_malloc(sizeof(char)*s->binlength); +#endif + } + + if (s_new->bindata == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + sexp_t_deallocate(s_new); + return NULL; + } + + memcpy(s_new->bindata,s->bindata,s->binlength*sizeof(char)); + + /* non-binary */ + } else { + if (s->val == NULL && (s->val_used > 0 || s->val_allocated > 0)) { + sexp_errno = SEXP_ERR_BADCONTENT; + sexp_t_deallocate(s_new); + return NULL; + } + + s_new->val_used = s->val_used; + s_new->val_allocated = s->val_allocated; + + if (s->val == NULL) { + s_new->val = NULL; + } else { + /** allocate space **/ +#ifdef __cplusplus + s_new->val = (char *)sexp_calloc(1,sizeof(char)*s->val_allocated); +#else + s_new->val = sexp_calloc(1,sizeof(char)*s->val_allocated); +#endif + + if (s_new->val == NULL) { + sexp_errno = SEXP_ERR_MEMORY; + sexp_t_deallocate(s_new); + return NULL; + } + + memcpy(s_new->val, s->val, sizeof(char)*s->val_used); + } + } + } else { + s_new->list = copy_sexp(s->list); + } + + s_new->next = copy_sexp(s->next); + + return s_new; +} + diff --git a/sexpr/sexp_vis.c b/sexpr/sexp_vis.c new file mode 100644 index 0000000..c8ca07a --- /dev/null +++ b/sexpr/sexp_vis.c @@ -0,0 +1,124 @@ +/** +@cond IGNORE + +====================================================== + SFSEXP: Small, Fast S-Expression Library version 1.2 + Written by Matthew Sottile (mjsottile@gmail.com) +====================================================== + +Copyright (2003-2006). The Regents of the University of California. This +material was produced under U.S. Government contract W-7405-ENG-36 for Los +Alamos National Laboratory, which is operated by the University of +California for the U.S. Department of Energy. The U.S. Government has rights +to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR +THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY +LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce +derivative works, such modified software should be clearly marked, so as not +to confuse it with the version available from LANL. + +Additionally, this library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +This library 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 Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA + +LA-CC-04-094 + +@endcond +**/ +#include +#include + +void _sexp_to_dotfile(const sexp_t *sx, FILE *fp) { + const sexp_t *tmp; + + tmp = sx; + + while (tmp != NULL) { + fprintf(fp," sx%lu [shape=record,label=\"",(unsigned long)tmp); + if (tmp->ty == SEXP_VALUE) { + fprintf(fp,"{ SEXP_VALUE | "); + switch (tmp->aty) { + case SEXP_BASIC: + fprintf(fp,"SEXP_BASIC }"); + break; + case SEXP_SQUOTE: + fprintf(fp,"SEXP_SQUOTE }"); + break; + case SEXP_DQUOTE: + fprintf(fp,"SEXP_DQUOTE }"); + break; + case SEXP_BINARY: + fprintf(fp,"SEXP_BINARY }"); + break; + default: + fprintf(fp,"ATY Unknown }"); + break; + } + } else + fprintf(fp," SEXP_LIST"); + + if (tmp->ty == SEXP_LIST) { + fprintf(fp,"| list | next\"];\n"); + + if (tmp->list != NULL) { + fprintf(fp," sx%lu:list -> sx%lu:type;\n", + (unsigned long)tmp, + (unsigned long)tmp->list); + _sexp_to_dotfile(tmp->list,fp); + if (tmp->next != NULL) + fprintf(fp," sx%lu:next -> sx%lu:type;\n", + (unsigned long)tmp, + (unsigned long)tmp->next); + tmp = tmp->next; + } + } else { + if (tmp->aty == SEXP_BINARY) + fprintf(fp,"| binlength=%lu | next\"];\n", + (unsigned long)tmp->binlength); + else + fprintf(fp,"| { va=%lu | vu=%lu } | val=%s | next\"];\n", + (unsigned long)tmp->val_allocated, + (unsigned long)tmp->val_used, + tmp->val); + + if (tmp->next != NULL) + fprintf(fp," sx%lu:next -> sx%lu:type;\n", + (unsigned long)tmp, + (unsigned long)tmp->next); + tmp = tmp->next; + + } + } +} + +sexp_errcode_t sexp_to_dotfile(const sexp_t *sx, const char *fname) { + FILE *fp; + + if (sx == NULL || fname == NULL) { + return SEXP_ERR_NULLSTRING; + } + + fp = fopen(fname,"w+"); + if (fp == NULL) { + return SEXP_ERR_IO; + } + + fprintf(fp,"digraph sexp {\n"); + + _sexp_to_dotfile(sx,fp); + + fprintf(fp,"}\n"); + + fclose(fp); + + return SEXP_ERR_OK; +} diff --git a/lib/Makefile.am b/sxmp/Makefile.am similarity index 89% rename from lib/Makefile.am rename to sxmp/Makefile.am index 6c1c204..127d277 100644 --- a/lib/Makefile.am +++ b/sxmp/Makefile.am @@ -29,7 +29,7 @@ libsxmp_la_LIBADD += -lmman -luuid /mingw64/lib/libsexpr.a /mingw64/lib/libtdata else -libsxmp_la_LIBADD += $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(LIBUUID_LIBS) +libsxmp_la_LIBADD += $(LIBUUID_LIBS) ../tdata/.libs/libtdata.la ../sexpr/.libs/libsexpr.la endif !COND_WIN32 diff --git a/lib/base64.c b/sxmp/base64.c similarity index 100% rename from lib/base64.c rename to sxmp/base64.c diff --git a/lib/channel.c b/sxmp/channel.c similarity index 100% rename from lib/channel.c rename to sxmp/channel.c diff --git a/lib/error.c b/sxmp/error.c similarity index 100% rename from lib/error.c rename to sxmp/error.c diff --git a/lib/hub.c b/sxmp/hub.c similarity index 100% rename from lib/hub.c rename to sxmp/hub.c diff --git a/lib/internal.h b/sxmp/internal.h similarity index 100% rename from lib/internal.h rename to sxmp/internal.h diff --git a/lib/libsxmp.pc.in b/sxmp/libsxmp.pc.in similarity index 100% rename from lib/libsxmp.pc.in rename to sxmp/libsxmp.pc.in diff --git a/lib/message.c b/sxmp/message.c similarity index 100% rename from lib/message.c rename to sxmp/message.c diff --git a/lib/rpc.c b/sxmp/rpc.c similarity index 100% rename from lib/rpc.c rename to sxmp/rpc.c diff --git a/lib/stream.c b/sxmp/stream.c similarity index 100% rename from lib/stream.c rename to sxmp/stream.c diff --git a/lib/stream_generic_listops.c b/sxmp/stream_generic_listops.c similarity index 100% rename from lib/stream_generic_listops.c rename to sxmp/stream_generic_listops.c diff --git a/lib/sxmplv2.c b/sxmp/sxmplv2.c similarity index 100% rename from lib/sxmplv2.c rename to sxmp/sxmplv2.c diff --git a/lib/uuid.c b/sxmp/uuid.c similarity index 100% rename from lib/uuid.c rename to sxmp/uuid.c diff --git a/tdata/Makefile.am b/tdata/Makefile.am new file mode 100644 index 0000000..1e8f02b --- /dev/null +++ b/tdata/Makefile.am @@ -0,0 +1,34 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DPACKAGE_LOCALE_DIR=\""$(localedir)"\" \ + -DPACKAGE_SRC_DIR=\""$(srcdir)"\" \ + -DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \ + $(LIBTDATA_CFLAGS) -I../include + +AM_CFLAGS =\ + -Wall\ + -g + +lib_LTLIBRARIES = libtdata.la + + +libtdata_la_SOURCES = \ + avl.c lslist.c redblack.c splay.c \ + tree.c usrtc.c bitwise.c idx_allocator.c \ + cas.c + +libtdata_la_LDFLAGS = + +libtdata_la_LIBADD = + +##include_HEADERS = \ +## ../include/tdata/macro.h ../include/tdata/tree.h ../include/tdata/usrtc.h \ +## ../include/tdata/bitwise.h ../include/tdata/idx_allocator.h + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libtdata.pc + +EXTRA_DIST = \ + libtdata.pc.in + diff --git a/tdata/VERSION b/tdata/VERSION new file mode 100644 index 0000000..e7b6f4c --- /dev/null +++ b/tdata/VERSION @@ -0,0 +1,3 @@ +0.2.3 + + diff --git a/tdata/avl.c b/tdata/avl.c new file mode 100644 index 0000000..6fe1f5b --- /dev/null +++ b/tdata/avl.c @@ -0,0 +1,328 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * avl.c + * Copyright (C) 2006, 2013 Alexander Vdolainen + * Copyright (C) 2006-2013, 2014 Askele inc. + * + * libtdata is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtdata 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see ."; + */ + +#include +#include +#include + +#define balance usrtc_impldata.usrtc_avl_balance +#define BALANCED usrtc_balanced +#define LEFTHEAVY usrtc_leftheavy +#define RIGHTHEAVY usrtc_rightheavy + +/*local functions prototypes*/ +static void avl_init(usrtc_t *); +static void avl_insert(usrtc_t *,usrtc_node_t *,const void *); +static void avl_delete(usrtc_t *,usrtc_node_t *); +static void avl_convert_from_list(usrtc_t *); + +usrtc_functions_t usrtc_avl_fu = { + avl_init, + avl_insert, + avl_delete, + usrtc_tree_lookup, + usrtc_tree_lower_bound, + usrtc_tree_upper_bound, + usrtc_tree_first, + usrtc_tree_last, + usrtc_tree_next, + usrtc_tree_prev, + usrtc_tree_convert_to_list, + avl_convert_from_list, + usrtc_bst +}; + +/*internal use*/ +static void rotate_left(usrtc_node_t **); +static void rotate_right(usrtc_node_t **); +static void fix_balance(usrtc_node_t **,usrtc_avl_balance_t ); +static int insert(usrtc_t *,usrtc_node_t *,usrtc_node_t **,usrtc_node_t *); +static usrtc_node_t *make_tree(usrtc_node_t **,usrtc_count_t ,int *,usrtc_node_t *); + +/*implementation*/ +static void avl_init(usrtc_t *us) +{ + usrtc_tree_init(us); + us->sentinel.balance=BALANCED; +} + +static void avl_insert(usrtc_t *us,usrtc_node_t *node,const void *key) +{ + usrtc_node_t *nil=tree_null_priv(us); + + node->key=key; + node->left=nil; + node->right=nil; + node->balance=BALANCED; + + if(insert(us,node,&nil->left,nil)) { + nil->balance=LEFTHEAVY; + } + + us->nodecount++; +} + +static void avl_delete(usrtc_t *us,usrtc_node_t *node) +{ + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *swap; + usrtc_node_t *child; + usrtc_node_t *parent; + + /*basic deletion*/ + usrtc_tree_delete(us,node,&swap,&child); + + /*implementation specific*/ + swap->balance=node->balance; + parent=child->parent; + + if(parent==nil) { + if(child==nil) { + parent->balance=BALANCED; + } + } + + while(parent!=nil) { + if((parent->left==nil) && (parent->right==nil)) { + if(child!=nil) + return; + if(parent==BALANCED) + return; + parent->balance=BALANCED; + } + else { + usrtc_node_t **pparent; + if(parent==parent->parent->left) + pparent=&parent->parent->left; + else + pparent=&parent->parent->right; + + if(child==parent->left) + fix_balance(pparent,RIGHTHEAVY); + else { + if(child!=parent->right) + return; + + fix_balance(pparent,LEFTHEAVY); + } + + parent=*pparent; + } + + if(parent->balance==BALANCED) { + child=parent; + parent=child->parent; + } + else + break; + } +} + +static void avl_convert_from_list(usrtc_t *us) +{ + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *root; + int height; + + if(us->nodecount==0) { + nil->balance=BALANCED; + return; + } + + root=make_tree(&nil->next,us->nodecount,&height,nil); + + if(nil->next!=nil) + return; + + nil->left=root; + root->parent=nil; + nil->balance=LEFTHEAVY; +} + +/*---*/ +static void rotate_left(usrtc_node_t **top) +{ + usrtc_node_t *parent=*top; + usrtc_node_t *child=parent->right; + + child->parent=parent->parent; + parent->right=child->left; + parent->right->parent=parent; + child->left=parent; + parent->parent=child; + *top=child; +} + +static void rotate_right(usrtc_node_t **top) +{ + usrtc_node_t *parent=*top; + usrtc_node_t *child=parent->left; + + child->parent=parent->parent; + parent->left=child->right; + parent->left->parent=parent; + child->right=parent; + parent->parent=child; + *top=child; +} + +static void fix_balance(usrtc_node_t **pnode,usrtc_avl_balance_t bal) +{ + usrtc_node_t *node=*pnode; + usrtc_node_t *child; + usrtc_node_t *grandchild; + + if(node->balance==BALANCED) + node->balance=bal; + else if(node->balance!=bal) + node->balance=BALANCED; + else { + if(node->balance!=bal) + return; + + if(bal==LEFTHEAVY) { + child=node->left; + if(child->balance==LEFTHEAVY) { + node->balance=BALANCED; + child->balance=BALANCED; + rotate_right(pnode); + } + else if(child->balance==BALANCED) { + node->balance=LEFTHEAVY; + child->balance=RIGHTHEAVY; + rotate_right(pnode); + } + else { + grandchild=child->right; + if(grandchild->balance==LEFTHEAVY) { + node->balance=RIGHTHEAVY; + child->balance=BALANCED; + } + else if(grandchild->balance==RIGHTHEAVY) { + node->balance=BALANCED; + child->balance=LEFTHEAVY; + } + else { + node->balance=BALANCED; + child->balance=BALANCED; + } + grandchild->balance=BALANCED; + rotate_left(&node->left); + rotate_right(pnode); + } + } + else { + child=node->right; + if(child->balance==RIGHTHEAVY) { + node->balance=BALANCED; + child->balance=BALANCED; + rotate_left(pnode); + } + else if(child->balance==BALANCED) { + node->balance=RIGHTHEAVY; + child->balance=LEFTHEAVY; + rotate_left(pnode); + } + else { + grandchild=child->left; + if(grandchild->balance==RIGHTHEAVY) { + node->balance=LEFTHEAVY; + child->balance=BALANCED; + } + else if(grandchild->balance==LEFTHEAVY) { + node->balance=BALANCED; + child->balance=RIGHTHEAVY; + } + else { + node->balance=BALANCED; + child->balance=BALANCED; + } + grandchild->balance=BALANCED; + rotate_right(&node->right); + rotate_left(pnode); + } + } + } +} + +static int insert(usrtc_t *us,usrtc_node_t *what,usrtc_node_t **where,usrtc_node_t *parent) +{ + usrtc_node_t *here=*where; + int result; + + if(here==tree_null_priv(us)) { + *where=what; + what->parent=parent; + return 1; + } + else { + result=us->compare(what->key,here->key); + + if(result < 0) { + if(insert(us,what,&here->left,here)) { + fix_balance(where,LEFTHEAVY); + return ((*where)->balance!=BALANCED); + } + } + else { + if(insert(us,what,&here->right,here)) { + fix_balance(where,RIGHTHEAVY); + return ((*where)->balance!=BALANCED); + } + } + } + return 0; +} + +static usrtc_node_t *make_tree(usrtc_node_t **pnode,usrtc_count_t count, + int *pheight,usrtc_node_t *nil) +{ + usrtc_count_t leftcount; + int leftheight, rightheight; + usrtc_node_t *root; + usrtc_node_t *leftroot; + + if(count==0) { + *pheight=0; + return nil; + } + + leftcount=(count-1)/2; + leftroot=make_tree(pnode,leftcount,&leftheight,nil); + count-=leftcount; + + root=*pnode; + *pnode=root->next; + --count; + + root->left=leftroot; + leftroot->parent=root; + root->right=make_tree(pnode,count,&rightheight,nil); + root->right->parent=root; + + if(leftheight>rightheight) + return (void *)0; + + *pheight=rightheight+1; + root->balance=(leftheight==rightheight) ? BALANCED : RIGHTHEAVY; + + return root; +} + diff --git a/tdata/bitwise.c b/tdata/bitwise.c new file mode 100644 index 0000000..2966b50 --- /dev/null +++ b/tdata/bitwise.c @@ -0,0 +1,201 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * (c) Copyright 2006,2007,2008 Jari OS Core Team + * (c) Copyright 2008 Dmitry Gromada + * (c) Copyright 2010 Alexander Vdolainen + * + * (c) Copyright 2012 - 2013, 2014 Askele Oy + * + * Implements bitwise operations with multiword bitmaps + */ + +#include +#include +#include +#include +#include + +/* operation equivalent to division modulo number of power 2 */ +#define MOD_POW2(var, pow) ((var) & ((pow) - 1)) + +int init_bitmap(bitmap_t *bitmap, int nbits) +{ + int ret = 0; + int size = nbits >> TYPE_LONG_SHIFT; + + if (nbits < 0) + return -1; + + if (size << TYPE_LONG_SHIFT != nbits) + size++; + + bitmap->map = (unsigned long*)malloc(size * sizeof(unsigned long)); + if (bitmap->map == NULL) + ret = -1; + else + bitmap->nwords = size; + + memset(bitmap->map, 0, size * sizeof(unsigned long)); + + return ret; +} + + +void free_bitmap(bitmap_t *bitmap) +{ + if (bitmap->map) { + free(bitmap->map); + bitmap->map = NULL; + } + bitmap->nwords = 0; +} + + +void bit_set_multi(bitmap_t *bitmap, int bitno) +{ + int i = bitno >> TYPE_LONG_SHIFT; + + return bit_set( bitmap->map + i, MOD_POW2(bitno, BITS_PER_LONG) ); +} + +int bit_test_and_set_multi(bitmap_t *bitmap, int bitno) +{ + int i = bitno >> TYPE_LONG_SHIFT; + + return bit_test_and_set( bitmap->map + i, MOD_POW2(bitno, BITS_PER_LONG) ); +} + +int bit_test_and_reset_multi(bitmap_t *bitmap, int bitno) +{ + int i = bitno >> TYPE_LONG_SHIFT; + + return bit_test_and_reset( bitmap->map + i, MOD_POW2(bitno, BITS_PER_LONG) ); +} + +void bit_clear_multi(bitmap_t *bitmap, int bitno) +{ + int i = bitno >> TYPE_LONG_SHIFT; + + return bit_clear( bitmap->map + i, MOD_POW2(bitno, BITS_PER_LONG) ); +} + +int bit_find_msf_multi(bitmap_t *bitmap, int mbit) +{ + int i, r; + unsigned long word; + + if (mbit < 0) + return -1; + else if (mbit >= bitmap->nwords * BITS_PER_LONG) + mbit = bitmap->nwords * BITS_PER_LONG - 1; + + i = mbit >> TYPE_LONG_SHIFT; + word = bitmap->map[i] & (~0UL >> ( BITS_PER_LONG - 1 - MOD_POW2(mbit, BITS_PER_LONG) )); + r = bit_find_msf(word); + + while ((r == -1) && (--i >= 0)) { + if (bitmap->map[i]) + r = bit_find_msf(bitmap->map[i]); + } + + r = (r == -1) ? r : (i * BITS_PER_LONG + r); + + return r; +} + +int bit_find_lsf_multi(bitmap_t *bitmap, int lbit) +{ + int i, r; + unsigned long word; + + if (lbit >= bitmap->nwords * BITS_PER_LONG) + return -1; + else if (lbit < 0) + lbit = 0; + + i = lbit >> TYPE_LONG_SHIFT; + word = bitmap->map[i] & (~0UL << MOD_POW2(lbit, BITS_PER_LONG)); + r = bit_find_lsf(word); + + while ((r == -1) && (++i < bitmap->nwords)) { + if (bitmap->map[i]) + r = bit_find_lsf(bitmap->map[i]); + } + + r = (r == -1) ? r : (i * BITS_PER_LONG + r); + + return r; +} + +int bit_test_multi(bitmap_t *bitmap, int bitno) +{ + int i = bitno >> TYPE_LONG_SHIFT; + + return bit_test( bitmap->map + i, MOD_POW2(bitno, BITS_PER_LONG) ); +} + +void bitrange_set_multi(bitmap_t *bitmap, int start_bit, int end_bit) +{ + int i1, i2; + unsigned long mask1, mask2, *p = bitmap->map; + + if (start_bit > end_bit) + return; + + i1 = start_bit >> TYPE_LONG_SHIFT; + mask1 = ~0UL << MOD_POW2(start_bit, BITS_PER_LONG); + + i2 = end_bit >> TYPE_LONG_SHIFT; + mask2 = ~0UL >> (BITS_PER_LONG - 1 - MOD_POW2(end_bit, BITS_PER_LONG)); + + if (i1 == i2) + p[i1] |= mask1 & mask2; + else { + p[i1++] |= mask1; + + if (i2 - i1 > 0) + memset( p + i1, 0xFF, (i2 - i1) * sizeof(unsigned long) ); + + p[i2] |= mask2; + } +} + +void bitrange_clear_multi(bitmap_t *bitmap, int start_bit, int end_bit) +{ + int i1, i2; + unsigned long mask1, mask2, *p = bitmap->map; + + if (start_bit > end_bit) + return; + + i1 = start_bit >> TYPE_LONG_SHIFT; + mask1 = ( 1UL << (start_bit & MOD_POW2(start_bit, BITS_PER_LONG)) ) - 1; + + i2 = end_bit >> TYPE_LONG_SHIFT; + mask2 = ~0UL << (MOD_POW2(end_bit, BITS_PER_LONG) + 1); + + if (i1 == i2) + p[i1] &= mask1 | mask2; + else { + p[i1++] &= mask1; + + if (i2 - i1 > 0) + memset(p + i1, 0, (i2 - i1) * sizeof(unsigned long)); + + p[i2] &= mask2; + } +} diff --git a/tdata/cas.c b/tdata/cas.c new file mode 100644 index 0000000..2fbe1c7 --- /dev/null +++ b/tdata/cas.c @@ -0,0 +1,51 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * cas.c + * Copyright (C) 2015 Alexander Vdolainen + * + * libtdata is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtdata 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see ."; + */ + +#include +#include + +#include "../config.h" + +#if !defined (HAVE__SYNC_BOOL_COMPARE_AND_SWAP_8) +#include +static pthread_mutex_t __sync_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +#ifndef HAVE__SYNC_BOOL_COMPARE_AND_SWAP_8 +_Bool __sync_bool_compare_and_swap_8 (uint64_t*, uint64_t, uint64_t) +__attribute__ ((visibility ("hidden"))); + +_Bool __sync_bool_compare_and_swap_8 (uint64_t* ptr, uint64_t old, uint64_t new) +{ + int i; + _Bool ret; + + i = pthread_mutex_lock(&__sync_lock); + + if(*ptr != old) ret = 0; + else { + *ptr = new; + ret = 1; + } + + i = pthread_mutex_unlock(&__sync_lock); + + return ret; +} +#endif diff --git a/tdata/idx_allocator.c b/tdata/idx_allocator.c new file mode 100644 index 0000000..0a57894 --- /dev/null +++ b/tdata/idx_allocator.c @@ -0,0 +1,254 @@ +/* + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * (c) Copyright 2006,2007,2008 MString Core Team + * (c) Copyright 2009 Dan Kruchinin + * (c) Copyright 2009 Alexander Vdolainen (libc adaptation) + * (c) Copyright 2009 Dmitry Gromada (locking added) + * (c) Copyright 2013 Alexander Vdolainen (verios changes to make it run on linux) + * + * (c) Copyright 2012 - 2013, 2014 Askele Oy + * + * Index allocator + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Main idea of index allocator is quite simple: + * There is a group of tasks that requires dynamic allocation + * of unique non-negative integer identifiers. Where a set of such + * integers is relatively big (thousands of numbers), index allocator + * becomes a nifty solution. + * The core of allocator is two bitmaps. One of them called "indices bitmap" + * or "second level bitmap". Each bit of that bitmap corresponds to particular unique + * index. Since second-level bitmap is a large portion of continuous memory, searching + * of set/zero bit in it becomes relatively slow. For speeding-up search process we invented + * a first-level bitmap. Second-level array is splitted on groups each of them contains BYTES_PER_ITEM bytes. + * First-level(or main) bitmap establishes corresponding between particular group and its + * state(group may have free indices or not). + * So index allocation assumes at first searching of group index in a the first-level bitmap and only + * then searching an index in the second level bitmap starting from a group index. + */ + +#define MIN_IDA_SIZE (WORDS_PER_ITEM * sizeof(ulong_t)) +#define MAX_UNLOCKED_ATTEMPTS 3 + +/* there macros defined only on new glibc (from 12-jul-2013), let's use our own */ +#ifndef is_powerof2 +#define is_powerof2(num) (((num) & ~((num) - 1)) == (num)) +#endif +#ifndef round_up_pow2 +#define round_up_pow2(x) (!is_powerof2(x) ? (1 << (bit_find_msf(x) + 1)) : (x) ) +#endif + +static inline size_t __get_main_bmap_size(idx_allocator_t *ida) +{ + if (ida->ids_bmap != NULL) + return (round_up_pow2((ida->size * sizeof(ulong_t)) / BYTES_PER_ITEM)); + + return ida->size; +} + +ulong_t idx_allocate(idx_allocator_t *ida) +{ + ulong_t id = IDX_INVAL; + long fnfi = 0; + size_t i, main_offs = 0, main_sz; + int natm = 0; /* attempt number */ + + main_sz = __get_main_bmap_size(ida) / sizeof(ulong_t); + i = 0; + + if (ida->ids_bmap != NULL) { + for (;;) { + while (i < main_sz) { + fnfi = zero_bit_find_lsf(ida->main_bmap[i]); + if (fnfi >= 0) { + fnfi = (fnfi * WORDS_PER_ITEM) + i * WORDS_PER_ITEM * BITS_PER_LONG; + main_offs = i; + break; + } + + i++; + } + if ((fnfi >= 0) && (fnfi < ida->size)) { + int res_id, j, total_sz; + + total_sz = fnfi + WORDS_PER_ITEM; + j = fnfi; + while (j < total_sz) { + res_id = zero_bit_find_lsf(ida->ids_bmap[j]); + if (res_id < 0) { + j++; + continue; + } + + if (ida_lockable(ida) /*&& atomic_test_and_set_bit(ida->ids_bmap + j, res_id)*/) { + natm++; + if (natm == MAX_UNLOCKED_ATTEMPTS) + ida_lock(ida); + } else { + bit_set(ida->ids_bmap + j, res_id); + } + + id = res_id + j * BITS_PER_LONG; + if (id >= ida->max_id) { + bit_clear(ida->ids_bmap + j, res_id); + id = IDX_INVAL; + } + + goto out; + } + + bit_set(ida->main_bmap + main_offs, + (fnfi - (main_offs * WORDS_PER_ITEM * BITS_PER_LONG)) / WORDS_PER_ITEM); + if ((ida->main_bmap[i] & ~0UL) == ~0UL) + i++; + } + else + break; + } + } + else { + while (i < main_sz) { + fnfi = zero_bit_find_lsf(ida->main_bmap[i]); + if (fnfi >= 0) { + bit_set(ida->main_bmap + i, fnfi); + id = fnfi + i * BITS_PER_LONG; + if (id >= ida->max_id) { + bit_clear(ida->main_bmap + i, fnfi); + id = IDX_INVAL; + } + + break; + } + + i++; + } + } + + out: + if (natm == MAX_UNLOCKED_ATTEMPTS) + ida_unlock(ida); + + return id; +} + +void idx_reserve(idx_allocator_t *ida, ulong_t idx) +{ + int start_id, bitno; + ulong_t *ptr; + + start_id = idx / BITS_PER_LONG; + bitno = idx - start_id * BITS_PER_LONG; + if (ida->ids_bmap != NULL) + ptr = ida->ids_bmap + start_id; + else + ptr = ida->main_bmap + start_id; + + if (bit_test_and_set(ptr, bitno)) { + printf("[libc warning] Detected an attempt to reserve already busy index %ld " + "[function: %s]!\n", idx, __FUNCTION__); + } +} + +void idx_free(idx_allocator_t *ida, ulong_t idx) +{ + int start_id, bitno, main_id, main_bitno; + ulong_t *ptr; + + start_id = idx / BITS_PER_LONG; + bitno = idx - start_id * BITS_PER_LONG; + if (ida->ids_bmap != NULL) { + ptr = ida->ids_bmap + start_id; + main_id = start_id / WORDS_PER_ITEM; + main_bitno = start_id - main_id * WORDS_PER_ITEM; + bit_clear(ida->main_bmap + main_id, main_bitno); + } + else + ptr = ida->main_bmap + start_id; + + if (!bit_test_and_clear(ptr, bitno)) { + printf("[libc warning] Detected an attempt to free already fried index %ld " + "[function: %s]!\n", idx, __FUNCTION__); + } +} + +int idx_allocator_init(idx_allocator_t *ida, ulong_t idx_max, int lockable) +{ + size_t bmap_sz; + int err = -ENOMEM; + + bmap_sz = (round_up_pow2(idx_max) >> 3); + memset(ida, 0, sizeof(*ida)); + ida->size = bmap_sz / sizeof(ulong_t); + if (ida->size >= MIN_IDA_SIZE) { + ida->ids_bmap = malloc(bmap_sz); + if (!ida->ids_bmap) { + goto error; + } + + memset(ida->ids_bmap, 0, bmap_sz); + } + else + ida->size = (ida->size > 0) ? (ida->size * sizeof(ulong_t)) : sizeof(ulong_t); + + ida->main_bmap = malloc(__get_main_bmap_size(ida)); + if (!ida->main_bmap) { + goto error; + } + +#if 0 + if (lockable && ida_lock_init(&ida->lock)) + goto error; +#endif + ida->lck = 0; /* cutty, we don't use the lockable due to the very arch specifics */ + + memset(ida->main_bmap, 0, __get_main_bmap_size(ida)); + ida->max_id = idx_max; + return 0; + + error: + if (ida->ids_bmap) { + free(ida->ids_bmap); + } + if (ida->main_bmap) { + free(ida->main_bmap); + } + + return err; +} + +void idx_allocator_destroy(idx_allocator_t *ida) +{ + if (ida->ids_bmap != NULL) + free(ida->ids_bmap); + + free(ida->main_bmap); + + if (ida_lockable(ida)) + ida_lock_destroy(&ida->lock); +} + diff --git a/tdata/libtdata.pc.in b/tdata/libtdata.pc.in new file mode 100644 index 0000000..68c711d --- /dev/null +++ b/tdata/libtdata.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +datarootdir=@datarootdir@ +datadir=@datadir@ +includedir=@includedir@ + +Name: libtdata +Description: Data structures C implementation. +Version: @LIBTDATA_VERSION@ +Requires: +Libs: -L${libdir} -ltdata +Cflags: -I${includedir} diff --git a/tdata/lslist.c b/tdata/lslist.c new file mode 100644 index 0000000..67a1ef4 --- /dev/null +++ b/tdata/lslist.c @@ -0,0 +1,174 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * lslist.c + * Copyright (C) 2006, 2013 Alexander Vdolainen + * Copyright (C) 2006-2013, 2014 Askele inc. + * + * libtdata is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtdata 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see ."; + */ + +#include + +#include +#include + +#define list_first_priv(L) ((L)->sentinel.next) +#define list_last_priv(L) ((L)->sentinel.prev) +#define list_null_priv(L) (&(L)->sentinel) + +/*function prototypes*/ +static void list_init(usrtc_t *); +static void list_insert(usrtc_t *,usrtc_node_t *,const void *); +static void list_delete(usrtc_t *,usrtc_node_t *); +static usrtc_node_t *list_lookup(usrtc_t *,const void *); +static usrtc_node_t *list_lower_bound(usrtc_t *,const void *); +static usrtc_node_t *list_upper_bound(usrtc_t *,const void *); +static usrtc_node_t *list_first(usrtc_t *); +static usrtc_node_t *list_last(usrtc_t *); +static usrtc_node_t *list_next(usrtc_t *, usrtc_node_t *); +static usrtc_node_t *list_prev(usrtc_t *, usrtc_node_t *); +static void list_convert_to_list(usrtc_t *); +static void list_convert_from_list(usrtc_t *); + +usrtc_functions_t usrtc_list_fu = { + list_init, + list_insert, + list_delete, + list_lookup, + list_lower_bound, + list_upper_bound, + list_first, + list_last, + list_next, + list_prev, + list_convert_to_list, + list_convert_from_list, + usrtc_lst +}; + +static void insert_before(usrtc_t *,usrtc_node_t *,usrtc_node_t *); + +/*implementation*/ +static void list_init(usrtc_t *us) +{ + us->sentinel.next=&us->sentinel; + us->sentinel.prev=&us->sentinel; +} + +static void list_insert(usrtc_t *us,usrtc_node_t *newnode,const void *key) +{ + usrtc_node_t *succ; + + newnode->key=key; + + for(succ=list_first_priv(us);succ!=list_null_priv(us);succ=succ->next) { + if(us->compare(succ->key,key)>0) + break; + } + + insert_before(us,newnode,succ); +} + +static void list_delete(usrtc_t *us,usrtc_node_t *node) +{ + usrtc_node_t *pred=node->prev; + usrtc_node_t *succ=node->next; + + pred->next=succ; + succ->prev=pred; + /*check node count*/ + us->nodecount--; +} + +static usrtc_node_t *list_lookup(usrtc_t *us,const void *key) +{ + usrtc_node_t *node; + + for(node=list_first_priv(us);node!=list_null_priv(us);node=node->next) { + if(us->compare(node->key,key)==0) + return node; + } + + return NULL; +} + +static usrtc_node_t *list_lower_bound(usrtc_t *us,const void *key) +{ + usrtc_node_t *node; + + for(node=list_first_priv(us);node!=list_null_priv(us);node=node->next) { + if(us->compare(node->key,key) >= 0) + return node; + } + + return NULL; +} + +static usrtc_node_t *list_upper_bound(usrtc_t *us,const void *key) +{ + usrtc_node_t *node; + + for(node=list_first_priv(us);node!=list_null_priv(us);node=node->prev) { + if(us->compare(node->key,key) >= 0) + return node; + } + + return NULL; +} + +static usrtc_node_t *list_first(usrtc_t *us) +{ + return (list_first_priv(us) == list_null_priv(us)) ? 0 : list_first_priv(us); +} + +static usrtc_node_t *list_last(usrtc_t *us) +{ + return (list_last_priv(us) == list_null_priv(us)) ? 0 : list_last_priv(us); +} + +static usrtc_node_t *list_next(usrtc_t *us, usrtc_node_t *node) +{ + return (node->next == list_null_priv(us)) ? 0 : node->next; +} + +static usrtc_node_t *list_prev(usrtc_t *us, usrtc_node_t *node) +{ + return (node->prev == list_null_priv(us)) ? 0 : node->prev; +} + +static void list_convert_to_list(usrtc_t *us) +{ + /*dummy*/ +} + +static void list_convert_from_list(usrtc_t *us) +{ + /*dummy*/ +} + +/*internal function*/ +static void insert_before(usrtc_t *us,usrtc_node_t *newnode,usrtc_node_t *succ) +{ + usrtc_node_t *pred=succ->prev; + + newnode->prev=pred; + newnode->next=succ; + + pred->next=newnode; + succ->prev=newnode; + + /*TODO: check for maxcount*/ + us->nodecount++; +} + diff --git a/tdata/redblack.c b/tdata/redblack.c new file mode 100644 index 0000000..e5f8cf4 --- /dev/null +++ b/tdata/redblack.c @@ -0,0 +1,310 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * redblack.c + * Copyright (C) 2006, 2013 Alexander Vdolainen + * Copyright (C) 2006-2013, 2014 Askele inc. + * + * libtdata is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtdata 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see ."; + */ + +#include + +#include +#include +#include + +#define color usrtc_impldata.usrtc_rb_color + +/*function prototypes*/ +static void redblack_init(usrtc_t *); +static void redblack_insert(usrtc_t *, usrtc_node_t *, const void *); +static void redblack_delete(usrtc_t *, usrtc_node_t *); +static void redblack_convert_from_list(usrtc_t *); + +usrtc_functions_t usrtc_redblack_fu = { + redblack_init, + redblack_insert, + redblack_delete, + usrtc_tree_lookup, + usrtc_tree_lower_bound, + usrtc_tree_upper_bound, + usrtc_tree_first, + usrtc_tree_last, + usrtc_tree_next, + usrtc_tree_prev, + usrtc_tree_convert_to_list, + redblack_convert_from_list, + usrtc_bst +}; + +/*implementation*/ +static void redblack_init(usrtc_t *us) +{ + usrtc_tree_init(us); + us->sentinel.color = usrtc_black; +} + +static void redblack_insert(usrtc_t *us, usrtc_node_t *node, const void *key) +{ + usrtc_node_t *parent; + + /* simple bt insert */ + usrtc_tree_insert(us,node,key); + + /* implementation specific insert */ + node->color = usrtc_red; + parent=node->parent; + + while(parent->color==usrtc_red) { + usrtc_node_t *grandpa=parent->parent; + + if(parent==grandpa->left) { + usrtc_node_t *uncle=grandpa->right; + if(uncle->color==usrtc_red) { /*red parent->red uncle*/ + parent->color=usrtc_black; + uncle->color=usrtc_black; + grandpa->color=usrtc_red; + node=grandpa; + parent=grandpa->parent; + } else { /*red parent->black uncle */ + if(node==parent->right) { + usrtc_tree_rotate_left(node,parent); + parent=node; + if(grandpa!=parent->parent) + return; + } + parent->color=usrtc_black; + grandpa->color=usrtc_red; + usrtc_tree_rotate_right(parent,grandpa); + break; + } + } else { /*ooh,parent == parent->parent->right*/ + usrtc_node_t *uncle=grandpa->left; + if(uncle->color==usrtc_red) { + parent->color=usrtc_black; + uncle->color=usrtc_black; + grandpa->color=usrtc_red; + node=grandpa; + parent=grandpa->parent; + } else { + if(node==parent->left) { + usrtc_tree_rotate_right(node,parent); + parent=node; + + if(grandpa!=parent->parent) + return; + } + parent->color=usrtc_black; + grandpa->color=usrtc_red; + usrtc_tree_rotate_left(parent,grandpa); + break; + } + } + } + + tree_root_priv(us)->color=usrtc_black; +} + +static void redblack_delete(usrtc_t *us,usrtc_node_t *node) +{ + usrtc_node_t *swap; + usrtc_node_t *child; + usrtc_rb_color_t savecolor; + + /*basic bt delete*/ + usrtc_tree_delete(us,node,&swap,&child); + + /*implementation specific deletion*/ + savecolor=node->color; + node->color=swap->color; + swap->color=savecolor; + + if(node->color==usrtc_black) { /*black*/ + usrtc_node_t *parent; + usrtc_node_t *sister; + + tree_root_priv(us)->color=usrtc_red; + + while(child->color==usrtc_black) { + parent=child->parent; + if(child==parent->left) { + sister=parent->right; + if(sister==tree_null_priv(us)) + return; + + if(sister->color==usrtc_red) { + sister->color=usrtc_black; + parent->color=usrtc_red; + usrtc_tree_rotate_left(sister,parent); + sister=parent->right; + + if(sister==tree_null_priv(us)) + return; + } + if(sister->left->color==usrtc_black && sister->right->color==usrtc_black) { + sister->color=usrtc_red; + child=parent; + } else { + if(sister->right->color==usrtc_black) { + if(sister->left->color!=usrtc_red) + return; + + sister->left->color=usrtc_black; + sister->color=usrtc_red; + usrtc_tree_rotate_right(sister->left,sister); + sister=parent->right; + + if(sister==tree_null_priv(us)) + return; + } + sister->color=parent->color; + sister->right->color=usrtc_black; + parent->color=usrtc_black; + usrtc_tree_rotate_left(sister,parent); + break; + } + } else { /*!child == child->parent->right*/ + if(child!=parent->right) + return; + + sister=parent->left; + + if(sister==tree_null_priv(us)) + return; + + if(sister->color==usrtc_red) { + sister->color=usrtc_black; + parent->color=usrtc_red; + usrtc_tree_rotate_right(sister,parent); + sister = parent->left; + + if(sister==tree_null_priv(us)) + return; + } + if(sister->right->color==usrtc_black && sister->left->color==usrtc_black) { + sister->color=usrtc_red; + child=parent; + } else { + if(sister->left->color == usrtc_black) { + if(sister->right->color!=usrtc_red) + return; + + sister->right->color=usrtc_black; + sister->color=usrtc_red; + usrtc_tree_rotate_left(sister->right, sister); + sister=parent->left; + + if(sister==tree_null_priv(us)) + return; + } + sister->color=parent->color; + sister->left->color=usrtc_black; + parent->color=usrtc_black; + usrtc_tree_rotate_right(sister,parent); + break; + } + } + } + + child->color=usrtc_black; + tree_root_priv(us)->color=usrtc_black; + } + +} + +static void redblack_convert_from_list(usrtc_t *us) +{ + usrtc_node_t *tree[TREE_DEPTH_MAX] = { NULL }; + usrtc_node_t *curr; + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *next; + usrtc_node_t *complete=NULL; + usrtc_count_t fullcount=(usrtc_count_t)USRTC_COUNT_T_MAX; + usrtc_count_t nodecount=us->nodecount; + usrtc_count_t botrowcount; + unsigned int baselevel=0; + unsigned int level=0; + unsigned int i; + + if(usrtc_red!=0 && usrtc_black!=1) + return; + + while(fullcount>=nodecount && fullcount) /*calc*/ + fullcount >>= 1; + + botrowcount=nodecount-fullcount; + + for(curr=nil->next;curr!=nil;curr=next) { + next=curr->next; + + if(complete==NULL && botrowcount--==0) { + baselevel=level=1; + complete=tree[0]; + + if(complete!=NULL) { + tree[0]=NULL; + complete->right=nil; + while(tree[level]!=0) { + tree[level]->right=complete; + complete->parent=tree[level]; + complete=tree[level]; + tree[level++]=NULL; + } + } + } + + if(complete==NULL) { + curr->left=nil; + curr->right=nil; + curr->color=level%2; + complete=curr; + + if(level!=baselevel) + return; + + while(tree[level]!=NULL) { + tree[level]->right=complete; + complete->parent=tree[level]; + complete=tree[level]; + tree[level++]=NULL; + } + } else { + curr->left=complete; + curr->color=(level+1)%2; + complete->parent=curr; + tree[level]=curr; + complete=NULL; + level=baselevel; + } + } + + if(complete==NULL) + complete=nil; + + for (i=0;iright=complete; + complete->parent=tree[i]; + complete=tree[i]; + } + } + + nil->right=nil; + nil->left=complete; + nil->color=usrtc_black; + complete->parent=nil; + complete->color=usrtc_black; + +} + diff --git a/tdata/splay.c b/tdata/splay.c new file mode 100644 index 0000000..5006eff --- /dev/null +++ b/tdata/splay.c @@ -0,0 +1,143 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * splay.c + * Copyright (C) 2006, 2013 Alexander Vdolainen + * Copyright (C) 2006-2013, 2014 Askele inc. + * + * libtdata is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtdata 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see ."; + */ + +#include +#include +#include + +/*prototypes*/ +static void splay_insert(usrtc_t *,usrtc_node_t *,const void *); +static void splay_delete(usrtc_t *,usrtc_node_t *); +static usrtc_node_t *splay_lookup(usrtc_t *,const void *); + +usrtc_functions_t usrtc_splay_fu = { + usrtc_tree_init, + splay_insert, + splay_delete, + splay_lookup, + usrtc_tree_lower_bound, + usrtc_tree_upper_bound, + usrtc_tree_first, + usrtc_tree_last, + usrtc_tree_next, + usrtc_tree_prev, + usrtc_tree_convert_to_list, + usrtc_tree_convert_from_list, + usrtc_bst +}; + +static void right_zig_zig(usrtc_node_t *,usrtc_node_t *,usrtc_node_t *); +static void left_zig_zig(usrtc_node_t *,usrtc_node_t *,usrtc_node_t *); +static void right_zig_zag(usrtc_node_t *,usrtc_node_t *,usrtc_node_t *); +static void left_zig_zag(usrtc_node_t *,usrtc_node_t *,usrtc_node_t *); +static void splay_node(usrtc_t *,usrtc_node_t *); + +/*implementation*/ + +static void splay_insert(usrtc_t *us,usrtc_node_t *node,const void *key) +{ + usrtc_tree_insert(us,node,key); + + while(node!=tree_root_priv(us)) + splay_node(us,node); +} + +static void splay_delete(usrtc_t *us,usrtc_node_t *node) +{ + usrtc_node_t *dummy; + usrtc_tree_delete(us,node,&dummy,&dummy); +} + +static usrtc_node_t *splay_lookup(usrtc_t *us,const void *key) +{ + usrtc_node_t *node=usrtc_tree_lookup(us,key); + + if(node) + while(node!=tree_root_priv(us)) + splay_node(us,node); + + return node; +} + +static void right_zig_zig(usrtc_node_t *child,usrtc_node_t *parent,usrtc_node_t *grandpa) +{ + usrtc_tree_rotate_right(parent,grandpa); + usrtc_tree_rotate_right(child,parent); +} + +static void left_zig_zig(usrtc_node_t *child,usrtc_node_t *parent,usrtc_node_t *grandpa) +{ + usrtc_tree_rotate_left(parent,grandpa); + usrtc_tree_rotate_left(child,parent); +} + +static void right_zig_zag(usrtc_node_t *child,usrtc_node_t *parent,usrtc_node_t *grandpa) +{ + usrtc_tree_rotate_right(child,parent); + usrtc_tree_rotate_left(child,grandpa); +} + +static void left_zig_zag(usrtc_node_t *child,usrtc_node_t *parent,usrtc_node_t *grandpa) +{ + usrtc_tree_rotate_left(child,parent); + usrtc_tree_rotate_right(child,grandpa); +} + +static void splay_node(usrtc_t *us,usrtc_node_t *node) +{ + usrtc_node_t *root=tree_root_priv(us); + usrtc_node_t *parent=node->parent; + + if(parent->left==node) { + if(parent==root) { + usrtc_tree_rotate_right(node,parent); + } else { + usrtc_node_t *grandpa=parent->parent; + + if(grandpa->left==parent) + right_zig_zig(node,parent,grandpa); + else { + if(grandpa->right!=parent) + return; + + right_zig_zag(node,parent,grandpa); + } + } + } else { + if(parent->right!=node) + return; + + if(parent==root) { + usrtc_tree_rotate_left(node,parent); + } else { + usrtc_node_t *grandpa=parent->parent; + + if(grandpa->right==parent) { + left_zig_zig(node,parent,grandpa); + } else { + if(grandpa->left!=parent) + return; + + left_zig_zag(node,parent,grandpa); + } + } + + } +} diff --git a/tdata/tree.c b/tdata/tree.c new file mode 100644 index 0000000..4c52e3b --- /dev/null +++ b/tdata/tree.c @@ -0,0 +1,492 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * tree.c + * Copyright (C) 2006, 2013 Alexander Vdolainen + * Copyright (C) 2006-2013, 2014 Askele inc. + * + * libtdata is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtdata 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see ."; + */ + +#include + +#include +#include +#include + +/*local functions prototypes*/ +static void tree_delete(usrtc_t *, usrtc_node_t *); + +usrtc_functions_t usrtc_tree_fu = { + usrtc_tree_init, + usrtc_tree_insert, + tree_delete, + usrtc_tree_lookup, + usrtc_tree_lower_bound, + usrtc_tree_upper_bound, + usrtc_tree_first, + usrtc_tree_last, + usrtc_tree_next, + usrtc_tree_prev, + usrtc_tree_convert_to_list, + usrtc_tree_convert_from_list, + usrtc_bst +}; + +/*implementation*/ + +void usrtc_tree_init(usrtc_t *us) +{ + us->sentinel.left=&us->sentinel; + us->sentinel.right=&us->sentinel; + us->sentinel.parent=&us->sentinel; + + us->sentinel.impl_specific.usrtc_dummy=0; + us->sentinel.data=0; + us->sentinel.key=0; +} + +void usrtc_tree_insert(usrtc_t *us,usrtc_node_t *node,const void *key) +{ + usrtc_node_t *where=tree_root_priv(us); + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *parent=nil; + long res=-1; + + node->key=key; + + while(where!=nil) { + parent=where; + res=us->compare(key,where->key); + + if(us->dupes_allowed && !res) /*trying to put duplicate to the disabled dupe tree*/ + return; + + if(res<0) + where=where->left; + else + where=where->right; + } + + /*assert(where==nil);*/ + + if(res<0) + parent->left=node; + else + parent->right=node; + + node->parent=parent; + node->left=nil; + node->right=nil; + + us->nodecount++; +} + +void usrtc_tree_delete(usrtc_t *us,usrtc_node_t *node,usrtc_node_t **pswap,usrtc_node_t **pchild) +{ + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *child; + usrtc_node_t *delparent=node->parent; + usrtc_node_t *next=node; + usrtc_node_t *nextparent; + + if(node->left!=nil && node->right!=nil) { + next=usrtc_tree_next(us,node); + nextparent=next->parent; + + /*if(next!=nil && next->parent!=nil && next->parent==nil) + return;*/ + + child=next->right; + child->parent=nextparent; + + if(nextparent->left==next) + nextparent->left=child; + else { + //if(nextparent->right!=next) + //return; + nextparent->right=child; + } + + next->parent=delparent; + next->left=node->left; + next->right=node->right; + next->left->parent=next; + next->right->parent=next; + + if(delparent->left==node) { + delparent->left=next; + } else { + //if(delparent->right!=node) + // return; + delparent->right = next; + } + + } else { + /*if(node==nil) + return; + if(node->left!=nil && node->right!=nil) + return;*/ + + child=(node->left!=nil) ? node->left : node->right; + + child->parent=delparent=node->parent; + + if(node==delparent->left) { + delparent->left=child; + } else { + /*if(node!=delparent->right) + return;*/ + delparent->right=child; + } + } + + node->parent=0; + node->right=0; + node->left=0; + + us->nodecount--; + + *pswap = next; + *pchild = child; +} + +usrtc_node_t *usrtc_tree_lookup(usrtc_t *us,const void *key) +{ + usrtc_node_t *root=tree_root_priv(us); + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *saved; + long res; + + while (root!=nil) { + res=us->compare(key,root->key); + if(res<0) + root=root->left; + else if(res>0) + root=root->right; + else { + if(!us->dupes_allowed) + return root; /*no duplicates*/ + else { /*duplicate, then find left occurence*/ + do { + saved=root; + root=root->left; + while(root!=nil && us->compare(key,root->key)) + root=root->right; + } while(root!=nil); + return saved; + } + } + } + + return NULL; +} + +usrtc_node_t *usrtc_tree_lower_bound(usrtc_t *us,const void *key) +{ + usrtc_node_t *root=tree_root_priv(us); + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *tentative=NULL; + long res; + + while(root!=nil) { + res=us->compare(key,root->key); + + if(res>0) { + root=root->right; + } else if(res<0) { + tentative=root; + root=root->left; + } else { + if (!us->dupes_allowed) + return root; + else { + tentative=root; + root=root->left; + } + } + } + + return tentative; +} + +usrtc_node_t *usrtc_tree_upper_bound(usrtc_t *us,const void *key) +{ + usrtc_node_t *root=tree_root_priv(us); + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *tentative=NULL; + long res; + + while(root!=nil) { + res=us->compare(key,root->key); + + if(res>0) { + root=root->left; + } else if(res<0) { + tentative=root; + root=root->right; + } else { + if (!us->dupes_allowed) + return root; + else { + tentative=root; + root=root->right; + } + } + } + + return tentative; +} + +usrtc_node_t *usrtc_tree_first(usrtc_t *us) +{ + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *root=tree_root_priv(us); + usrtc_node_t *left; + + if(root!=nil) + while((left=root->left)!=nil) + root=left; + + return (root==nil) ? NULL : root; +} + +usrtc_node_t *usrtc_tree_last(usrtc_t *us) +{ + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *root=tree_root_priv(us); + usrtc_node_t *right; + + if(root!=nil) + while((right=root->right)!=nil) + root=right; + + return (root==nil) ? NULL : root; +} + +usrtc_node_t *usrtc_tree_next(usrtc_t *us, usrtc_node_t *curr) +{ + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *parent; + usrtc_node_t *left; + + if(curr->right!=nil) { + curr=curr->right; + while((left=curr->left)!=nil) + curr=left; + return curr; + } + + parent=curr->parent; + + while(parent!=nil && curr==parent->right) { + curr=parent; + parent=curr->parent; + } + + return (parent==nil) ? NULL : parent; +} + +usrtc_node_t *usrtc_tree_prev(usrtc_t *us, usrtc_node_t *curr) +{ + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *parent; + usrtc_node_t *right; + + if(curr->left!=nil) { + curr=curr->left; + while((right=curr->right)!=nil) + curr=right; + return curr; + } + + parent=curr->parent; + + while(parent!=nil && curr==parent->left) { + curr=parent; + parent=curr->parent; + } + + return (parent==nil) ? NULL : parent; +} + +/*uff, convertation between trees and lists*/ +void usrtc_tree_convert_to_list(usrtc_t *us) +{ + usrtc_node_t *node; + usrtc_node_t tempsentinel; + usrtc_node_t *nil=&tempsentinel; + usrtc_node_t *tail,*next; + usrtc_node_t *treenil=tree_null_priv(us); + + if(us->nodecount==0) /* no nodes */ + return; + + tempsentinel.next=nil; + tempsentinel.prev=nil; + + /* two passes */ + for(tail=nil,node=usrtc_tree_first(us);node!=0;tail=node,node=next) { + next=usrtc_tree_next(us,node); + node->prev=tail; + } + + nil->prev=tail; + + for(tail=nil,node=nil->prev;node!=nil;tail=node,node=node->prev) + node->next=tail; + + nil->next=tail; + + us->sentinel.next=tempsentinel.next; + us->sentinel.prev=tempsentinel.prev; + us->sentinel.next->prev=treenil; + us->sentinel.prev->next=treenil; +} + +void usrtc_tree_convert_from_list(usrtc_t *us) +{ + usrtc_node_t *tree[TREE_DEPTH_MAX]={ 0 }; + usrtc_node_t *curr; + usrtc_node_t *nil=tree_null_priv(us); + usrtc_node_t *next; + usrtc_node_t *complete=NULL; + usrtc_count_t fullcount=(usrtc_count_t)USRTC_COUNT_T_MAX; + usrtc_count_t nodecount=us->nodecount; + usrtc_count_t botrowcount; + int baselevel=0; + int level=0; + int i=0; + + while (fullcount>=nodecount && fullcount) /* calc */ + fullcount>>=1; + + botrowcount=nodecount-fullcount; + + for(curr=nil->next;curr!=nil;curr=next) { + next=curr->next; + + if(complete==NULL && botrowcount-- ==0) { + baselevel=level=1; + complete=tree[0]; + + if(complete!=NULL) { + tree[0]=0; + complete->right=nil; + while(tree[level]!=NULL) { + tree[level]->right=complete; + complete->parent=tree[level]; + complete=tree[level]; + tree[level++]=0; + } + } + } + + if(complete==NULL) { + curr->left=nil; + curr->right=nil; + complete=curr; + + if(level!=baselevel) + return; + while(tree[level]!=NULL) { + tree[level]->right=complete; + complete->parent=tree[level]; + complete=tree[level]; + tree[level++]=0; + } + } else { + curr->left=complete; + complete->parent=curr; + tree[level]=curr; + complete=NULL; + level=baselevel; + } + } + + if(complete==NULL) + complete=nil; + + for(i=0;iright=complete; + complete->parent=tree[i]; + complete=tree[i]; + } + } + + nil->right=nil; + nil->left=complete; + complete->parent=nil; + +} + +void usrtc_tree_rotate_left(usrtc_node_t *child,usrtc_node_t *parent) +{ + usrtc_node_t *leftgrandchild; + usrtc_node_t *grandpa; + + if(parent->right!=child) + return; + + child=parent->right; + parent->right=leftgrandchild=child->left; + leftgrandchild->parent=parent; + + child->parent=grandpa=parent->parent; + + if(parent==grandpa->left) { + grandpa->left=child; + } else { + if(parent!=grandpa->right) + return; + grandpa->right=child; + } + + child->left=parent; + parent->parent=child; +} + +void usrtc_tree_rotate_right(usrtc_node_t *child,usrtc_node_t *parent) +{ + usrtc_node_t *rightgrandchild; + usrtc_node_t *grandpa; + + if(parent->left!=child) + return; + + parent->left=rightgrandchild=child->right; + rightgrandchild->parent=parent; + + child->parent=grandpa=parent->parent; + + if(parent==grandpa->right) { + grandpa->right=child; + } else { + if(parent!=grandpa->left) + return; + grandpa->left=child; + } + + child->right=parent; + parent->parent=child; +} + +/* local functions */ +static void tree_delete(usrtc_t *us, usrtc_node_t *node) +{ + usrtc_node_t *dummy; + + usrtc_tree_delete(us, node, &dummy, &dummy); +} + diff --git a/tdata/usrtc.c b/tdata/usrtc.c new file mode 100644 index 0000000..aded662 --- /dev/null +++ b/tdata/usrtc.c @@ -0,0 +1,244 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * usrtc.c + * Copyright (C) 2006, 2013 Alexander Vdolainen + * Copyright (C) 2006-2013, 2014 Askele inc. + * + * libtdata is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtdata 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see ."; + */ + +#include +#include +#include + +#include +#include + +/* so, if you want to add your implementation of the data + * structure please add this here, like the following. + */ + +#define MAX_IMPL 4 + +extern usrtc_functions_t usrtc_list_fu; +extern usrtc_functions_t usrtc_tree_fu; +extern usrtc_functions_t usrtc_redblack_fu; +extern usrtc_functions_t usrtc_splay_fu; +extern usrtc_functions_t usrtc_avl_fu; + +static usrtc_functions_t *impl_table[] = { + &usrtc_list_fu,&usrtc_tree_fu, + &usrtc_redblack_fu,&usrtc_splay_fu, + &usrtc_avl_fu }; + +static usrtc_node_t *default_node_alloc(void *context) +{ + return malloc(sizeof *default_node_alloc(context)); +} + +static void default_node_free(void *context,usrtc_node_t *node) +{ + free(node); +} + +void usrtc_init(usrtc_t *us,int impl,usrtc_count_t maxcount,usrtc_compare_t compare) +{ + if(!us) + return; + if(impl>MAX_IMPL) + return; + + us->nodecount=0; + us->maxcount=maxcount; + us->dupes_allowed=0; + us->compare=compare; + us->node_alloc=default_node_alloc; + us->node_free=default_node_free; + us->context=0; + us->futable=impl_table[impl]; + us->futable->usrtc_init(us); +} + +usrtc_t *usrtc_create(int impl,usrtc_count_t maxcount,usrtc_compare_t compare) +{ + usrtc_t *newrtc=(usrtc_t*)malloc(sizeof *newrtc); + + if(newrtc) + usrtc_init(newrtc,impl,maxcount,compare); + + return newrtc; +} + +void usrtc_destroy(usrtc_t *us) +{ + /*assert(usrtc_isempty(us));*/ + if(!us) + return; + + free(us); +} + +void usrtc_convert_to(usrtc_t *us,int impl) +{ + if(impl_table[impl]==us->futable) + return; + + if(us->futable->usrtc_type==usrtc_bst && (impl==USRTC_BST || impl==USRTC_SPLAY)) { + us->futable=impl_table[impl]; + return; + } + + us->futable->usrtc_convert_to_list(us); + us->futable=impl_table[impl]; + + if(impl_table[impl]->usrtc_type==usrtc_lst) + return; + + us->futable->usrtc_convert_from_list(us); +} + +usrtc_count_t usrtc_count(usrtc_t *us) +{ + return us->nodecount; +} + +int usrtc_isempty(usrtc_t *us) +{ + return us->nodecount == 0; +} + +int usrtc_isfull(usrtc_t *us) +{ + return us->nodecount == us->maxcount; +} + +int usrtc_alloc_insert(usrtc_t *us,const void *key,void *data) +{ + usrtc_node_t *newnode=us->node_alloc(us->context); + + if(newnode!=NULL) { + newnode->data=data; + us->futable->usrtc_insert(us,newnode,key); + } + + return newnode!=NULL; +} + +void usrtc_delete_free(usrtc_t *us,usrtc_node_t *node) +{ + us->futable->usrtc_delete(us,node); + us->node_free(us->context,node); +} + +void usrtc_set_allocator(usrtc_t *us,usrtc_node_alloc_t alloc,usrtc_node_free_t n_free,void *context) +{ + us->node_alloc=alloc; + us->node_free=n_free; + us->context=context; +} + +void usrtc_allow_dupes(usrtc_t *us) +{ + /*assert(us->nodecount == 0);*/ + if(!us->nodecount) + return; + + us->dupes_allowed=1; +} + +void usrtc_node_init(usrtc_node_t *node,void *data) +{ + node->data=data; + node->impl_specific.usrtc_dummy=0; + node->left=NULL; + node->right=NULL; + node->parent=NULL; +} + +usrtc_node_t *usrtc_node_create(void *data) +{ + usrtc_node_t *newnode=(usrtc_node_t*)malloc(sizeof *newnode); + + if(newnode!=NULL) + newnode->data=data; + + return newnode; +} + +void usrtc_node_destroy(usrtc_node_t *node) +{ + free(node); +} + +void *usrtc_node_getdata(usrtc_node_t *node) +{ + return node->data; +} + +void usrtc_node_setdata(usrtc_node_t *node,void *data) +{ + node->data=data; +} + +const void *usrtc_node_getkey(usrtc_node_t *node) +{ + return node->key; +} + +void usrtc_insert(usrtc_t *us,usrtc_node_t *node,const void *key) +{ + us->futable->usrtc_insert(us,node,key); +} + +void usrtc_delete(usrtc_t *us,usrtc_node_t *node) +{ + us->futable->usrtc_delete(us,node); +} + +usrtc_node_t *usrtc_lookup(usrtc_t *us,const void *key) +{ + return us->futable->usrtc_lookup(us,key); +} + +usrtc_node_t *usrtc_lower_bound(usrtc_t *us,const void *key) +{ + return us->futable->usrtc_lower_bound(us,key); +} + +usrtc_node_t *usrtc_upper_bound(usrtc_t *us,const void *key) +{ + return us->futable->usrtc_upper_bound(us,key); +} + +usrtc_node_t *usrtc_first(usrtc_t *us) +{ + return us->futable->usrtc_first(us); +} + +usrtc_node_t *usrtc_last(usrtc_t *us) +{ + return us->futable->usrtc_last(us); +} + +usrtc_node_t *usrtc_next(usrtc_t *us,usrtc_node_t *node) +{ + return us->futable->usrtc_next(us,node); +} + +usrtc_node_t *usrtc_prev(usrtc_t *us,usrtc_node_t *node) +{ + return us->futable->usrtc_prev(us,node); +} + +