diff --git a/libs/libks/Makefile b/libs/libks/Makefile new file mode 100644 index 0000000000..250280766f --- /dev/null +++ b/libs/libks/Makefile @@ -0,0 +1,26 @@ +PWD=$(shell pwd) +INCS=-I$(PWD)/src/include +DEBUG=-g -ggdb +BASE_FLAGS=$(INCS) $(DEBUG) -I$(LIBEDIT_DIR)/src/ -fPIC +PICKY=-O2 +CFLAGS=$(BASE_FLAGS) $(PICKY) +CXXFLAGS=$(BASE_FLAGS) +MYLIB=libks.a +LIBS=-lncurses -lks -lpthread -lm +LDFLAGS=-L. +OBJS=src/ks.o src/ks_threadmutex.o src/ks_config.o src/ks_json.o src/ks_buffer.o src/mpool.o src/table.o src/table_util.o src/simclist.o +SRC=src/ks.c src/ks_json.c src/ks_threadmutex.c src/ks_config.c src/ks_json.c src/ks_buffer.c src/mpool.c src/table.c src/table_util.c src/simclist.c +HEADERS=src/include/ks_config.h src/include/ks.h src/include/ks_threadmutex.h src/include/ks_json.h src/include/ks_buffer.h src/include/mpool.h src/include/mpool_loc.h src/include/table.h src/include/table_loc.h src/include/simclist.h +SOLINK=-shared -Xlinker -x + +all: $(MYLIB) + +$(MYLIB): $(OBJS) $(HEADERS) $(SRC) + ar rcs $(MYLIB) $(OBJS) + ranlib $(MYLIB) + +%.o: %.c $(HEADERS) + $(CC) $(CC_CFLAGS) $(CFLAGS) -c $< -o $@ + +clean: + rm -f *.o src/*.o libks.a *~ src/*~ src/include/*~ diff --git a/libs/libks/src/include/cc.h b/libs/libks/src/include/cc.h new file mode 100644 index 0000000000..44666193c6 --- /dev/null +++ b/libs/libks/src/include/cc.h @@ -0,0 +1,2 @@ +const char *cc = ".========================================================================================================.\n| ____ _____ ____ _ ____ _ _ _____ |\n| / ___|___ _ __ ___ ___ |_ _|__ / ___| |_ _ ___ / ___|___ _ __ ( ) |___ / |\n| | | / _ \\| '_ ` _ \\ / _ \\ | |/ _ \\ | | | | | | |/ _ \\ | / _ \\| '_ \\ |/| | |_ \\ |\n| | |__| (_) | | | | | | __/ | | (_) | | |___| | |_| | __/ |__| (_) | | | | | |___) | |\n| \\____\\___/|_| |_| |_|\\___| |_|\\___/ \\____|_|\\__,_|\\___|\\____\\___/|_| |_| |_|____/ |\n| |\n| ____ _ _ _ _ ____ _ |\n| / ___| |__ (_) ___ __ _ __ _ ___ | | | / ___| / \\ |\n| | | | '_ \\| |/ __/ _` |/ _` |/ _ \\ | | | \\___ \\ / _ \\ |\n| | |___| | | | | (_| (_| | (_| | (_) | | |_| |___) / ___ \\ |\n| \\____|_| |_|_|\\___\\__,_|\\__, |\\___( ) \\___/|____/_/ \\_\\ |\n| |___/ |/ |\n| _ _ __ _ _ ___ _ _ ____ ___ _ _____ |\n| / \\ _ _ __ _ _ _ ___| |_ / /_ | |_| |__ ( _ )| |_| |__ |___ \\ / _ \\/ |___ / |\n| / _ \\| | | |/ _` | | | / __| __| | '_ \\| __| '_ \\ _____ / _ \\| __| '_ \\ __) | | | | | |_ \\ |\n| / ___ \\ |_| | (_| | |_| \\__ \\ |_ | (_) | |_| | | | |_____| | (_) | |_| | | | / __/| |_| | |___) | |\n| /_/ \\_\\__,_|\\__, |\\__,_|___/\\__| \\___/ \\__|_| |_| \\___/ \\__|_| |_| |_____|\\___/|_|____/ |\n| |___/ |\n| _ |\n| __ ____ ____ __ ___| |_ _ ___ ___ ___ _ __ ___ ___ _ __ ___ |\n| \\ \\ /\\ / /\\ \\ /\\ / /\\ \\ /\\ / / / __| | | | |/ _ \\/ __/ _ \\| '_ \\ / __/ _ \\| '_ ` _ \\ |\n| \\ V V / \\ V V / \\ V V / _ | (__| | |_| | __/ (_| (_) | | | | _ | (_| (_) | | | | | | |\n| \\_/\\_/ \\_/\\_/ \\_/\\_/ (_) \\___|_|\\__,_|\\___|\\___\\___/|_| |_| (_) \\___\\___/|_| |_| |_| |\n| |\n.========================================================================================================.\n"; + diff --git a/libs/libks/src/include/ks.h b/libs/libks/src/include/ks.h new file mode 100644 index 0000000000..c581544d85 --- /dev/null +++ b/libs/libks/src/include/ks.h @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2007-2012, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KS_H_ +#define _KS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +#define ks_copy_string(_x, _y, _z) strncpy(_x, _y, _z - 1) +#define ks_set_string(_x, _y) ks_copy_string(_x, _y, sizeof(_x)) +#define KS_VA_NONE "%s", "" + + +typedef enum { + KS_POLL_READ = (1 << 0), + KS_POLL_WRITE = (1 << 1), + KS_POLL_ERROR = (1 << 2) +} ks_poll_t; + +#ifdef WIN32 +#define KS_SEQ_FWHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY +#define KS_SEQ_BWHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE +#define KS_SEQ_FRED FOREGROUND_RED | FOREGROUND_INTENSITY +#define KS_SEQ_BRED FOREGROUND_RED +#define KS_SEQ_FMAGEN FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY +#define KS_SEQ_BMAGEN FOREGROUND_BLUE | FOREGROUND_RED +#define KS_SEQ_FCYAN FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY +#define KS_SEQ_BCYAN FOREGROUND_GREEN | FOREGROUND_BLUE +#define KS_SEQ_FGREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY +#define KS_SEQ_BGREEN FOREGROUND_GREEN +#define KS_SEQ_FYELLOW FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY +#define KS_SEQ_BYELLOW FOREGROUND_RED | FOREGROUND_GREEN +#define KS_SEQ_DEFAULT_COLOR KS_SEQ_FWHITE +#define KS_SEQ_FBLUE FOREGROUND_BLUE | FOREGROUND_INTENSITY +#define KS_SEQ_BBLUE FOREGROUND_BLUE +#define KS_SEQ_FBLACK 0 | FOREGROUND_INTENSITY +#define KS_SEQ_BBLACK 0 +#else +#define KS_SEQ_ESC "\033[" +/* Ansi Control character suffixes */ +#define KS_SEQ_HOME_CHAR 'H' +#define KS_SEQ_HOME_CHAR_STR "H" +#define KS_SEQ_CLEARLINE_CHAR '1' +#define KS_SEQ_CLEARLINE_CHAR_STR "1" +#define KS_SEQ_CLEARLINEEND_CHAR "K" +#define KS_SEQ_CLEARSCR_CHAR0 '2' +#define KS_SEQ_CLEARSCR_CHAR1 'J' +#define KS_SEQ_CLEARSCR_CHAR "2J" +#define KS_SEQ_DEFAULT_COLOR KS_SEQ_ESC KS_SEQ_END_COLOR /* Reset to Default fg/bg color */ +#define KS_SEQ_AND_COLOR ";" /* To add multiple color definitions */ +#define KS_SEQ_END_COLOR "m" /* To end color definitions */ +/* Foreground colors values */ +#define KS_SEQ_F_BLACK "30" +#define KS_SEQ_F_RED "31" +#define KS_SEQ_F_GREEN "32" +#define KS_SEQ_F_YELLOW "33" +#define KS_SEQ_F_BLUE "34" +#define KS_SEQ_F_MAGEN "35" +#define KS_SEQ_F_CYAN "36" +#define KS_SEQ_F_WHITE "37" +/* Background colors values */ +#define KS_SEQ_B_BLACK "40" +#define KS_SEQ_B_RED "41" +#define KS_SEQ_B_GREEN "42" +#define KS_SEQ_B_YELLOW "43" +#define KS_SEQ_B_BLUE "44" +#define KS_SEQ_B_MAGEN "45" +#define KS_SEQ_B_CYAN "46" +#define KS_SEQ_B_WHITE "47" +/* Preset escape sequences - Change foreground colors only */ +#define KS_SEQ_FBLACK KS_SEQ_ESC KS_SEQ_F_BLACK KS_SEQ_END_COLOR +#define KS_SEQ_FRED KS_SEQ_ESC KS_SEQ_F_RED KS_SEQ_END_COLOR +#define KS_SEQ_FGREEN KS_SEQ_ESC KS_SEQ_F_GREEN KS_SEQ_END_COLOR +#define KS_SEQ_FYELLOW KS_SEQ_ESC KS_SEQ_F_YELLOW KS_SEQ_END_COLOR +#define KS_SEQ_FBLUE KS_SEQ_ESC KS_SEQ_F_BLUE KS_SEQ_END_COLOR +#define KS_SEQ_FMAGEN KS_SEQ_ESC KS_SEQ_F_MAGEN KS_SEQ_END_COLOR +#define KS_SEQ_FCYAN KS_SEQ_ESC KS_SEQ_F_CYAN KS_SEQ_END_COLOR +#define KS_SEQ_FWHITE KS_SEQ_ESC KS_SEQ_F_WHITE KS_SEQ_END_COLOR +#define KS_SEQ_BBLACK KS_SEQ_ESC KS_SEQ_B_BLACK KS_SEQ_END_COLOR +#define KS_SEQ_BRED KS_SEQ_ESC KS_SEQ_B_RED KS_SEQ_END_COLOR +#define KS_SEQ_BGREEN KS_SEQ_ESC KS_SEQ_B_GREEN KS_SEQ_END_COLOR +#define KS_SEQ_BYELLOW KS_SEQ_ESC KS_SEQ_B_YELLOW KS_SEQ_END_COLOR +#define KS_SEQ_BBLUE KS_SEQ_ESC KS_SEQ_B_BLUE KS_SEQ_END_COLOR +#define KS_SEQ_BMAGEN KS_SEQ_ESC KS_SEQ_B_MAGEN KS_SEQ_END_COLOR +#define KS_SEQ_BCYAN KS_SEQ_ESC KS_SEQ_B_CYAN KS_SEQ_END_COLOR +#define KS_SEQ_BWHITE KS_SEQ_ESC KS_SEQ_B_WHITE KS_SEQ_END_COLOR +/* Preset escape sequences */ +#define KS_SEQ_HOME KS_SEQ_ESC KS_SEQ_HOME_CHAR_STR +#define KS_SEQ_CLEARLINE KS_SEQ_ESC KS_SEQ_CLEARLINE_CHAR_STR +#define KS_SEQ_CLEARLINEEND KS_SEQ_ESC KS_SEQ_CLEARLINEEND_CHAR +#define KS_SEQ_CLEARSCR KS_SEQ_ESC KS_SEQ_CLEARSCR_CHAR KS_SEQ_HOME +#endif + +#if !defined(_XOPEN_SOURCE) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) +#define _XOPEN_SOURCE 600 +#endif + +#ifndef HAVE_STRINGS_H +#define HAVE_STRINGS_H 1 +#endif +#ifndef HAVE_SYS_SOCKET_H +#define HAVE_SYS_SOCKET_H 1 +#endif + +#ifndef __WINDOWS__ +#if defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32) +#define __WINDOWS__ +#endif +#endif + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +#if (_MSC_VER >= 1400) /* VC8+ */ +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif +#endif +#ifndef strcasecmp +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#endif +#ifndef strncasecmp +#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) +#endif +#ifndef snprintf +#define snprintf _snprintf +#endif +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#undef HAVE_STRINGS_H +#undef HAVE_SYS_SOCKET_H +#endif + +#include +#ifndef WIN32 +#include +#endif + +#include +#include +#include +#include +#ifndef WIN32 +#include +#include +#include +#include +#include +#include +#endif + +#ifdef HAVE_STRINGS_H +#include +#endif +#include + +#if (_MSC_VER >= 1400) // VC8+ +#define ks_assert(expr) assert(expr);__analysis_assume( expr ) +#endif + +#ifndef ks_assert +#define ks_assert(_x) assert(_x) +#endif + +#define ks_safe_free(_x) if (_x) free(_x); _x = NULL +#define ks_strlen_zero(s) (!s || *(s) == '\0') +#define ks_strlen_zero_buf(s) (*(s) == '\0') +#define end_of(_s) *(*_s == '\0' ? _s : _s + strlen(_s) - 1) + +#ifdef WIN32 +#include +#include +typedef SOCKET ks_socket_t; +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +typedef intptr_t ks_ssize_t; +typedef int ks_filehandle_t; +#define KS_SOCK_INVALID INVALID_SOCKET +#define strerror_r(num, buf, size) strerror_s(buf, size, num) +#if defined(KS_DECLARE_STATIC) +#define KS_DECLARE(type) type __stdcall +#define KS_DECLARE_NONSTD(type) type __cdecl +#define KS_DECLARE_DATA +#elif defined(KS_EXPORTS) +#define KS_DECLARE(type) __declspec(dllexport) type __stdcall +#define KS_DECLARE_NONSTD(type) __declspec(dllexport) type __cdecl +#define KS_DECLARE_DATA __declspec(dllexport) +#else +#define KS_DECLARE(type) __declspec(dllimport) type __stdcall +#define KS_DECLARE_NONSTD(type) __declspec(dllimport) type __cdecl +#define KS_DECLARE_DATA __declspec(dllimport) +#endif +#else +#define KS_DECLARE(type) type +#define KS_DECLARE_NONSTD(type) type +#define KS_DECLARE_DATA +#include +#include +#include +#include +#include +#include +#include +#define KS_SOCK_INVALID -1 +typedef int ks_socket_t; +typedef ssize_t ks_ssize_t; +typedef int ks_filehandle_t; +#endif + +#include "math.h" +#include "ks_json.h" + +typedef int16_t ks_port_t; +typedef size_t ks_size_t; + +typedef enum { + KS_SUCCESS, + KS_FAIL, + KS_BREAK, + KS_DISCONNECTED, + KS_GENERR +} ks_status_t; + +#define BUF_CHUNK 65536 * 50 +#define BUF_START 65536 * 100 + +#include +#include + +#define ks_test_flag(obj, flag) ((obj)->flags & flag) +#define ks_set_flag(obj, flag) (obj)->flags |= (flag) +#define ks_clear_flag(obj, flag) (obj)->flags &= ~(flag) + +/*! \brief Used internally for truth test */ +typedef enum { + KS_TRUE = 1, + KS_FALSE = 0 +} ks_bool_t; + +#ifndef __FUNCTION__ +#define __FUNCTION__ (const char *)__func__ +#endif + +#define KS_PRE __FILE__, __FUNCTION__, __LINE__ +#define KS_LOG_LEVEL_DEBUG 7 +#define KS_LOG_LEVEL_INFO 6 +#define KS_LOG_LEVEL_NOTICE 5 +#define KS_LOG_LEVEL_WARNING 4 +#define KS_LOG_LEVEL_ERROR 3 +#define KS_LOG_LEVEL_CRIT 2 +#define KS_LOG_LEVEL_ALERT 1 +#define KS_LOG_LEVEL_EMERG 0 + +#define KS_LOG_DEBUG KS_PRE, KS_LOG_LEVEL_DEBUG +#define KS_LOG_INFO KS_PRE, KS_LOG_LEVEL_INFO +#define KS_LOG_NOTICE KS_PRE, KS_LOG_LEVEL_NOTICE +#define KS_LOG_WARNING KS_PRE, KS_LOG_LEVEL_WARNING +#define KS_LOG_ERROR KS_PRE, KS_LOG_LEVEL_ERROR +#define KS_LOG_CRIT KS_PRE, KS_LOG_LEVEL_CRIT +#define KS_LOG_ALERT KS_PRE, KS_LOG_LEVEL_ALERT +#define KS_LOG_EMERG KS_PRE, KS_LOG_LEVEL_EMERG +typedef void (*ks_logger_t)(const char *file, const char *func, int line, int level, const char *fmt, ...); +typedef void (*ks_listen_callback_t)(ks_socket_t server_sock, ks_socket_t client_sock, struct sockaddr_in *addr); + + +KS_DECLARE(int) ks_vasprintf(char **ret, const char *fmt, va_list ap); + +KS_DECLARE_DATA extern ks_logger_t ks_log; + +/*! Sets the logger for libks. Default is the null_logger */ +KS_DECLARE(void) ks_global_set_logger(ks_logger_t logger); +/*! Sets the default log level for libks */ +KS_DECLARE(void) ks_global_set_default_logger(int level); + + +#include "ks_threadmutex.h" +#include "ks_config.h" +#include "ks_buffer.h" +#include "mpool.h" +#include "simclist.h" +#include "table.h" + +KS_DECLARE(size_t) ks_url_encode(const char *url, char *buf, size_t len); +KS_DECLARE(char *)ks_url_decode(char *s); +KS_DECLARE(const char *)ks_stristr(const char *instr, const char *str); +KS_DECLARE(int) ks_toupper(int c); +KS_DECLARE(int) ks_tolower(int c); +KS_DECLARE(int) ks_snprintf(char *buffer, size_t count, const char *fmt, ...); + + +KS_DECLARE(int) ks_wait_sock(ks_socket_t sock, uint32_t ms, ks_poll_t flags); + +KS_DECLARE(unsigned int) ks_separate_string_string(char *buf, const char *delim, char **array, unsigned int arraylen); + +#define ks_recv(_h) ks_recv_event(_h, 0, NULL) +#define ks_recv_timed(_h, _ms) ks_recv_event_timed(_h, _ms, 0, NULL) + +static __inline__ int ks_safe_strcasecmp(const char *s1, const char *s2) +{ + if (!(s1 && s2)) { + return 1; + } + + return strcasecmp(s1, s2); +} + +#ifdef __cplusplus +} +#endif /* defined(__cplusplus) */ + + +#endif /* defined(_KS_H_) */ + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/libks/src/include/ks_buffer.h b/libs/libks/src/include/ks_buffer.h new file mode 100644 index 0000000000..83426c9885 --- /dev/null +++ b/libs/libks/src/include/ks_buffer.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2010-2012, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "ks.h" +#ifndef KS_BUFFER_H +#define KS_BUFFER_H +/** + * @defgroup ks_buffer Buffer Routines + * @ingroup buffer + * The purpose of this module is to make a plain buffering interface that can be used for read/write buffers + * throughout the application. + * @{ + */ +struct ks_buffer; +typedef struct ks_buffer ks_buffer_t; + +/*! \brief Allocate a new dynamic ks_buffer + * \param buffer returned pointer to the new buffer + * \param blocksize length to realloc by as data is added + * \param start_len ammount of memory to reserve initially + * \param max_len length the buffer is allowed to grow to + * \return status + */ +KS_DECLARE(ks_status_t) ks_buffer_create(ks_buffer_t **buffer, ks_size_t blocksize, ks_size_t start_len, ks_size_t max_len); + +/*! \brief Get the length of a ks_buffer_t + * \param buffer any buffer of type ks_buffer_t + * \return int size of the buffer. + */ +KS_DECLARE(ks_size_t) ks_buffer_len(ks_buffer_t *buffer); + +/*! \brief Get the freespace of a ks_buffer_t + * \param buffer any buffer of type ks_buffer_t + * \return int freespace in the buffer. + */ +KS_DECLARE(ks_size_t) ks_buffer_freespace(ks_buffer_t *buffer); + +/*! \brief Get the in use amount of a ks_buffer_t + * \param buffer any buffer of type ks_buffer_t + * \return int ammount of buffer curently in use + */ +KS_DECLARE(ks_size_t) ks_buffer_inuse(ks_buffer_t *buffer); + +/*! \brief Read data from a ks_buffer_t up to the ammount of datalen if it is available. Remove read data from buffer. + * \param buffer any buffer of type ks_buffer_t + * \param data pointer to the read data to be returned + * \param datalen amount of data to be returned + * \return int ammount of data actually read + */ +KS_DECLARE(ks_size_t) ks_buffer_read(ks_buffer_t *buffer, void *data, ks_size_t datalen); + +KS_DECLARE(ks_size_t) ks_buffer_read_packet(ks_buffer_t *buffer, void *data, ks_size_t maxlen); +KS_DECLARE(ks_size_t) ks_buffer_packet_count(ks_buffer_t *buffer); + +/*! \brief Read data endlessly from a ks_buffer_t + * \param buffer any buffer of type ks_buffer_t + * \param data pointer to the read data to be returned + * \param datalen amount of data to be returned + * \return int ammount of data actually read + * \note Once you have read all the data from the buffer it will loop around. + */ +KS_DECLARE(ks_size_t) ks_buffer_read_loop(ks_buffer_t *buffer, void *data, ks_size_t datalen); + +/*! \brief Assign a number of loops to read + * \param buffer any buffer of type ks_buffer_t + * \param loops the number of loops (-1 for infinite) + */ +KS_DECLARE(void) ks_buffer_set_loops(ks_buffer_t *buffer, int32_t loops); + +/*! \brief Write data into a ks_buffer_t up to the length of datalen + * \param buffer any buffer of type ks_buffer_t + * \param data pointer to the data to be written + * \param datalen amount of data to be written + * \return int amount of buffer used after the write, or 0 if no space available + */ +KS_DECLARE(ks_size_t) ks_buffer_write(ks_buffer_t *buffer, const void *data, ks_size_t datalen); + +/*! \brief Remove data from the buffer + * \param buffer any buffer of type ks_buffer_t + * \param datalen amount of data to be removed + * \return int size of buffer, or 0 if unable to toss that much data + */ +KS_DECLARE(ks_size_t) ks_buffer_toss(ks_buffer_t *buffer, ks_size_t datalen); + +/*! \brief Remove all data from the buffer + * \param buffer any buffer of type ks_buffer_t + */ +KS_DECLARE(void) ks_buffer_zero(ks_buffer_t *buffer); + +/*! \brief Destroy the buffer + * \param buffer buffer to destroy + * \note only neccessary on dynamic buffers (noop on pooled ones) + */ +KS_DECLARE(void) ks_buffer_destroy(ks_buffer_t **buffer); + +/*! \brief Seek to offset from the beginning of the buffer + * \param buffer buffer to seek + * \param datalen offset in bytes + * \return new position + */ +KS_DECLARE(ks_size_t) ks_buffer_seek(ks_buffer_t *buffer, ks_size_t datalen); + +/** @} */ + +KS_DECLARE(ks_size_t) ks_buffer_zwrite(ks_buffer_t *buffer, const void *data, ks_size_t datalen); + +#endif +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/libks/src/include/ks_config.h b/libs/libks/src/include/ks_config.h new file mode 100644 index 0000000000..0a90bde078 --- /dev/null +++ b/libs/libks/src/include/ks_config.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2007-2012, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @defgroup config Config File Parser + * @ingroup config + * This module implements a basic interface and file format parser + * + *
+ *
+ * EXAMPLE 
+ * 
+ * [category1]
+ * var1 => val1
+ * var2 => val2
+ * \# lines that begin with \# are comments
+ * \#var3 => val3
+ * 
+ * @{ + */ + +#ifndef KS_CONFIG_H +#define KS_CONFIG_H + +#include "ks.h" + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + + +#define KS_URL_SEPARATOR "://" + + +#ifdef WIN32 +#define KS_PATH_SEPARATOR "\\" +#ifndef KS_CONFIG_DIR +#define KS_CONFIG_DIR "c:\\openks" +#endif +#define ks_is_file_path(file) (*(file +1) == ':' || *file == '/' || strstr(file, SWITCH_URL_SEPARATOR)) +#else +#define KS_PATH_SEPARATOR "/" +#ifndef KS_CONFIG_DIR +#define KS_CONFIG_DIR "/etc/openks" +#endif +#define ks_is_file_path(file) ((*file == '/') || strstr(file, SWITCH_URL_SEPARATOR)) +#endif + +/*! + \brief Evaluate the truthfullness of a string expression + \param expr a string expression + \return true or false +*/ +#define ks_true(expr)\ +(expr && ( !strcasecmp(expr, "yes") ||\ +!strcasecmp(expr, "on") ||\ +!strcasecmp(expr, "true") ||\ +!strcasecmp(expr, "enabled") ||\ +!strcasecmp(expr, "active") ||\ +!strcasecmp(expr, "allow") ||\ +atoi(expr))) ? 1 : 0 + +/*! + \brief Evaluate the falsefullness of a string expression + \param expr a string expression + \return true or false +*/ +#define ks_false(expr)\ +(expr && ( !strcasecmp(expr, "no") ||\ +!strcasecmp(expr, "off") ||\ +!strcasecmp(expr, "false") ||\ +!strcasecmp(expr, "disabled") ||\ +!strcasecmp(expr, "inactive") ||\ +!strcasecmp(expr, "disallow") ||\ +!atoi(expr))) ? 1 : 0 + +typedef struct ks_config ks_config_t; + +/*! \brief A simple file handle representing an open configuration file **/ +struct ks_config { + /*! FILE stream buffer to the opened file */ + FILE *file; + /*! path to the file */ + char path[512]; + /*! current category */ + char category[256]; + /*! current section */ + char section[256]; + /*! buffer of current line being read */ + char buf[1024]; + /*! current line number in file */ + int lineno; + /*! current category number in file */ + int catno; + /*! current section number in file */ + int sectno; + + int lockto; +}; + +/*! + \brief Open a configuration file + \param cfg (ks_config_t *) config handle to use + \param file_path path to the file + \return 1 (true) on success 0 (false) on failure +*/ +KS_DECLARE(int) ks_config_open_file(ks_config_t * cfg, const char *file_path); + +/*! + \brief Close a previously opened configuration file + \param cfg (ks_config_t *) config handle to use +*/ +KS_DECLARE(void) ks_config_close_file(ks_config_t * cfg); + +/*! + \brief Retrieve next name/value pair from configuration file + \param cfg (ks_config_t *) config handle to use + \param var pointer to aim at the new variable name + \param val pointer to aim at the new value +*/ +KS_DECLARE(int) ks_config_next_pair(ks_config_t * cfg, char **var, char **val); + +/*! + \brief Retrieve the CAS bits from a configuration string value + \param strvalue pointer to the configuration string value (expected to be in format whatever:xxxx) + \param outbits pointer to aim at the CAS bits +*/ +KS_DECLARE(int) ks_config_get_cas_bits(char *strvalue, unsigned char *outbits); + + +/** @} */ + +#ifdef __cplusplus +} +#endif /* defined(__cplusplus) */ + +#endif /* defined(KS_CONFIG_H) */ + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/libks/src/include/ks_json.h b/libs/libks/src/include/ks_json.h new file mode 100755 index 0000000000..1ad116e980 --- /dev/null +++ b/libs/libks/src/include/ks_json.h @@ -0,0 +1,127 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +#include "ks.h" +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 + +/* The cJSON structure: */ +typedef struct cJSON { + struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +KS_DECLARE(void) cJSON_InitHooks(cJSON_Hooks* hooks); + + +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ +KS_DECLARE(cJSON *)cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ +KS_DECLARE(char *)cJSON_Print(cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ +KS_DECLARE(char *)cJSON_PrintUnformatted(cJSON *item); +/* Delete a cJSON entity and all subentities. */ +KS_DECLARE(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +KS_DECLARE(int) cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +KS_DECLARE(cJSON *)cJSON_GetArrayItem(cJSON *array,int item); +/* Get item "string" from object. Case insensitive. */ +KS_DECLARE(cJSON *)cJSON_GetObjectItem(cJSON *object,const char *string); + +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +KS_DECLARE(const char *)cJSON_GetErrorPtr(); + +/* These calls create a cJSON item of the appropriate type. */ +KS_DECLARE(cJSON *)cJSON_CreateNull(); +KS_DECLARE(cJSON *)cJSON_CreateTrue(); +KS_DECLARE(cJSON *)cJSON_CreateFalse(); +KS_DECLARE(cJSON *)cJSON_CreateBool(int b); +KS_DECLARE(cJSON *)cJSON_CreateNumber(double num); +KS_DECLARE(cJSON *)cJSON_CreateString(const char *string); +KS_DECLARE(cJSON *)cJSON_CreateArray(); +KS_DECLARE(cJSON *)cJSON_CreateObject(); + +/* These utilities create an Array of count items. */ +KS_DECLARE(cJSON *)cJSON_CreateIntArray(int *numbers,int count); +KS_DECLARE(cJSON *)cJSON_CreateFloatArray(float *numbers,int count); +KS_DECLARE(cJSON *)cJSON_CreateDoubleArray(double *numbers,int count); +KS_DECLARE(cJSON *)cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array/object. */ +KS_DECLARE(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +KS_DECLARE(void) cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +KS_DECLARE(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +KS_DECLARE(void) cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +KS_DECLARE(cJSON *)cJSON_DetachItemFromArray(cJSON *array,int which); +KS_DECLARE(void) cJSON_DeleteItemFromArray(cJSON *array,int which); +KS_DECLARE(cJSON *)cJSON_DetachItemFromObject(cJSON *object,const char *string); +KS_DECLARE(void) cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +KS_DECLARE(void) cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +KS_DECLARE(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/libks/src/include/ks_threadmutex.h b/libs/libks/src/include/ks_threadmutex.h new file mode 100644 index 0000000000..2b12acaccd --- /dev/null +++ b/libs/libks/src/include/ks_threadmutex.h @@ -0,0 +1,58 @@ +/* + * Cross Platform Thread/Mutex abstraction + * Copyright(C) 2007 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + */ + + +#ifndef _KS_THREADMUTEX_H +#define _KS_THREADMUTEX_H + +#include "ks.h" + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +typedef struct ks_mutex ks_mutex_t; +typedef struct ks_thread ks_thread_t; +typedef void *(*ks_thread_function_t) (ks_thread_t *, void *); + +KS_DECLARE(ks_status_t) ks_thread_create_detached(ks_thread_function_t func, void *data); +ks_status_t ks_thread_create_detached_ex(ks_thread_function_t func, void *data, size_t stack_size); +void ks_thread_override_default_stacksize(size_t size); +KS_DECLARE(ks_status_t) ks_mutex_create(ks_mutex_t **mutex); +KS_DECLARE(ks_status_t) ks_mutex_destroy(ks_mutex_t **mutex); +KS_DECLARE(ks_status_t) ks_mutex_lock(ks_mutex_t *mutex); +KS_DECLARE(ks_status_t) ks_mutex_trylock(ks_mutex_t *mutex); +KS_DECLARE(ks_status_t) ks_mutex_unlock(ks_mutex_t *mutex); + +#ifdef __cplusplus +} +#endif /* defined(__cplusplus) */ + +#endif /* defined(_KS_THREADMUTEX_H) */ + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/libks/src/include/mpool.h b/libs/libks/src/include/mpool.h new file mode 100644 index 0000000000..23ac93fb91 --- /dev/null +++ b/libs/libks/src/include/mpool.h @@ -0,0 +1,463 @@ +/* + * Memory pool defines. + * + * Copyright 1996 by Gray Watson. + * + * This file is part of the mpool package. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies, and that the name of Gray Watson not be used in advertising + * or publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be reached via http://256.com/gray/ + * + * $Id: mpool.h,v 1.4 2006/05/31 20:26:11 gray Exp $ + */ + +#ifndef __MPOOL_H__ +#define __MPOOL_H__ + +#include + +/* + * mpool flags to mpool_alloc or mpool_set_attr + */ + +/* + * Choose a best fit algorithm not first fit. This takes more CPU + * time but will result in a tighter heap. + */ +#define MPOOL_FLAG_BEST_FIT (1<<0) + +/* + * By default the library adds 2 bytes onto all allocations to insert + * a magic number that it can look for to determine how large a freed + * memory chunk is. This flag indicates that few if any frees are + * going to be performed on the pool and to not waste memory on these + * bytes. + */ +#define MPOOL_FLAG_NO_FREE (1<<1) + +/* + * This enables very heavy packing at the possible expense of CPU. + * This affects a number of parts of the library. + * + * By default the 1st page of memory is reserved for the main mpool + * structure. This flag will cause the rest of the 1st block to be + * available for use as user memory. + * + * By default the library looks through the memory when freed looking + * for a magic value. There is an internal max size that it will look + * and then it will give up. This flag forces it to look until it + * finds it. + */ +#define MPOOL_FLAG_HEAVY_PACKING (1<<2) + +/* + * Use sbrk not mmap to allocate pages. This is not recommended for + * normal use. + */ +#define MPOOL_FLAG_USE_SBRK (1<<3) + +/* + * Mpool error codes + */ +#define MPOOL_ERROR_NONE 1 /* no error */ +#define MPOOL_ERROR_ARG_NULL 2 /* function argument is null */ +#define MPOOL_ERROR_ARG_INVALID 3 /* function argument is invalid */ +#define MPOOL_ERROR_PNT 4 /* invalid mpool pointer */ +#define MPOOL_ERROR_POOL_OVER 5 /* mpool structure was overwritten */ +#define MPOOL_ERROR_PAGE_SIZE 6 /* could not get system page-size */ +#define MPOOL_ERROR_OPEN_ZERO 7 /* could not open /dev/zero */ +#define MPOOL_ERROR_NO_MEM 8 /* no memory available */ +#define MPOOL_ERROR_MMAP 9 /* problems with mmap */ +#define MPOOL_ERROR_SIZE 10 /* error processing requested size */ +#define MPOOL_ERROR_TOO_BIG 11 /* allocation exceeded max size */ +#define MPOOL_ERROR_MEM 12 /* invalid memory address */ +#define MPOOL_ERROR_MEM_OVER 13 /* memory lower bounds overwritten */ +#define MPOOL_ERROR_NOT_FOUND 14 /* memory block not found in pool */ +#define MPOOL_ERROR_IS_FREE 15 /* memory block already free */ +#define MPOOL_ERROR_BLOCK_STAT 16 /* invalid internal block status */ +#define MPOOL_ERROR_FREE_ADDR 17 /* invalid internal free address */ +#define MPOOL_ERROR_SBRK_CONTIG 18 /* sbrk did not return contiguous mem*/ +#define MPOOL_ERROR_NO_PAGES 19 /* ran out of pages in pool */ +#define MPOOL_ERROR_ALLOC 20 /* calloc,malloc,free,realloc failed */ +#define MPOOL_ERROR_PNT_OVER 21 /* pointer structure was overwritten */ + +/* + * Mpool function IDs for the mpool_log_func callback function. + */ +#define MPOOL_FUNC_CLOSE 1 /* mpool_close function called */ +#define MPOOL_FUNC_CLEAR 2 /* mpool_clear function called */ +#define MPOOL_FUNC_ALLOC 3 /* mpool_alloc function called */ +#define MPOOL_FUNC_CALLOC 4 /* mpool_calloc function called */ +#define MPOOL_FUNC_FREE 5 /* mpool_free function called */ +#define MPOOL_FUNC_RESIZE 6 /* mpool_resize function called */ + +/* + * void mpool_log_func_t + * + * DESCRIPTION: + * + * Mpool transaction log function. + * + * RETURNS: + * + * None. + * + * ARGUMENT: + * + * mp_p -> Associated mpool address. + * + * func_id -> Integer function ID which identifies which mpool + * function is being called. + * + * byte_size -> Optionally specified byte size. + * + * ele_n -> Optionally specified element number. For mpool_calloc + * only. + * + * new_addr -> Optionally specified new address. For mpool_alloc, + * mpool_calloc, and mpool_resize only. + * + * old_addr -> Optionally specified old address. For mpool_resize and + * mpool_free only. + * + * old_byte_size -> Optionally specified old byte size. For + * mpool_resize only. + */ +typedef void (*mpool_log_func_t)(const void *mp_p, + const int func_id, + const unsigned long byte_size, + const unsigned long ele_n, + const void *old_addr, const void *new_addr, + const unsigned long old_byte_size); + +#ifdef MPOOL_MAIN + +#include "mpool_loc.h" + +#else + +/* generic mpool type */ +typedef void mpool_t; + +#endif + +/*<<<<<<<<<< The below prototypes are auto-generated by fillproto */ + +/* + * mpool_t *mpool_open + * + * DESCRIPTION: + * + * Open/allocate a new memory pool. + * + * RETURNS: + * + * Success - Pool pointer which must be passed to mpool_close to + * deallocate. + * + * Failure - NULL + * + * ARGUMENTS: + * + * flags -> Flags to set attributes of the memory pool. See the top + * of mpool.h. + * + * page_size -> Set the internal memory page-size. This must be a + * multiple of the getpagesize() value. Set to 0 for the default. + * + * start_addr -> Starting address to try and allocate memory pools. + * This is ignored if the MPOOL_FLAG_USE_SBRK is enabled. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +extern +mpool_t *mpool_open(const unsigned int flags, const unsigned int page_size, + void *start_addr, int *error_p); + +/* + * int mpool_close + * + * DESCRIPTION: + * + * Close/free a memory allocation pool previously opened with + * mpool_open. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to our memory pool. + */ +extern +int mpool_close(mpool_t *mp_p); + +/* + * int mpool_clear + * + * DESCRIPTION: + * + * Wipe an opened memory pool clean so we can start again. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to our memory pool. + */ +extern +int mpool_clear(mpool_t *mp_p); + +/* + * void *mpool_alloc + * + * DESCRIPTION: + * + * Allocate space for bytes inside of an already open memory pool. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal malloc. + * + * byte_size -> Number of bytes to allocate in the pool. Must be >0. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +extern +void *mpool_alloc(mpool_t *mp_p, const unsigned long byte_size, + int *error_p); + +/* + * void *mpool_calloc + * + * DESCRIPTION: + * + * Allocate space for elements of bytes in the memory pool and zero + * the space afterwards. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal calloc. + * + * ele_n -> Number of elements to allocate. + * + * ele_size -> Number of bytes per element being allocated. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +extern +void *mpool_calloc(mpool_t *mp_p, const unsigned long ele_n, + const unsigned long ele_size, int *error_p); + +/* + * int mpool_free + * + * DESCRIPTION: + * + * Free an address from a memory pool. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal free. + * + * addr <-> Address to free. + * + * size -> Size of the address being freed. + */ +extern +int mpool_free(mpool_t *mp_p, void *addr, const unsigned long size); + +/* + * void *mpool_resize + * + * DESCRIPTION: + * + * Reallocate an address in a mmeory pool to a new size. This is + * different from realloc in that it needs the old address' size. If + * you don't have it then you need to allocate new space, copy the + * data, and free the old pointer yourself. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal realloc. + * + * old_addr -> Previously allocated address. + * + * old_byte_size -> Size of the old address. Must be known, cannot be + * 0. + * + * new_byte_size -> New size of the allocation. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +extern +void *mpool_resize(mpool_t *mp_p, void *old_addr, + const unsigned long old_byte_size, + const unsigned long new_byte_size, + int *error_p); + +/* + * int mpool_stats + * + * DESCRIPTION: + * + * Return stats from the memory pool. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p -> Pointer to the memory pool. + * + * page_size_p <- Pointer to an unsigned integer which, if not NULL, + * will be set to the page-size of the pool. + * + * num_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the number of pointers currently allocated in pool. + * + * user_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the number of user bytes allocated in this pool. + * + * max_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the maximum number of user bytes that have been + * allocated in this pool. + * + * tot_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the total amount of space (including administrative + * overhead) used by the pool. + */ +extern +int mpool_stats(const mpool_t *mp_p, unsigned int *page_size_p, + unsigned long *num_alloced_p, + unsigned long *user_alloced_p, + unsigned long *max_alloced_p, + unsigned long *tot_alloced_p); + +/* + * int mpool_set_log_func + * + * DESCRIPTION: + * + * Set a logging callback function to be called whenever there was a + * memory transaction. See mpool_log_func_t. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * log_func -> Log function (defined in mpool.h) which will be called + * with each mpool transaction. + */ +extern +int mpool_set_log_func(mpool_t *mp_p, mpool_log_func_t log_func); + +/* + * int mpool_set_max_pages + * + * DESCRIPTION: + * + * Set the maximum number of pages that the library will use. Once it + * hits the limit it will return MPOOL_ERROR_NO_PAGES. + * + * NOTE: if the MPOOL_FLAG_HEAVY_PACKING is set then this max-pages + * value will include the page with the mpool header structure in it. + * If the flag is _not_ set then the max-pages will not include this + * first page. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * max_pages -> Maximum number of pages used by the library. + */ +extern +int mpool_set_max_pages(mpool_t *mp_p, const unsigned int max_pages); + +/* + * const char *mpool_strerror + * + * DESCRIPTION: + * + * Return the corresponding string for the error number. + * + * RETURNS: + * + * Success - String equivalient of the error. + * + * Failure - String "invalid error code" + * + * ARGUMENTS: + * + * error -> Error number that we are converting. + */ +extern +const char *mpool_strerror(const int error); + +/*<<<<<<<<<< This is end of the auto-generated output from fillproto. */ + +#endif /* ! __MPOOL_H__ */ diff --git a/libs/libks/src/include/mpool_loc.h b/libs/libks/src/include/mpool_loc.h new file mode 100644 index 0000000000..3d33f5d927 --- /dev/null +++ b/libs/libks/src/include/mpool_loc.h @@ -0,0 +1,116 @@ +/* + * Memory pool local defines. + * + * Copyright 1996 by Gray Watson. + * + * This file is part of the mpool package. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies, and that the name of Gray Watson not be used in advertising + * or publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be reached via http://256.com/gray/ + * + * $Id: mpool_loc.h,v 1.2 2005/05/20 20:08:54 gray Exp $ + */ + +#ifndef __MPOOL_LOC_H__ +#define __MPOOL_LOC_H__ + +#define MPOOL_MAGIC 0xABACABA /* magic for struct */ +#define BLOCK_MAGIC 0xB1B1007 /* magic for blocks */ +#define FENCE_MAGIC0 (unsigned char)(0xFAU) /* 1st magic mem byte */ +#define FENCE_MAGIC1 (unsigned char)(0xD3U) /* 2nd magic mem byte */ + +#define FENCE_SIZE 2 /* fence space */ +#define MIN_ALLOCATION (sizeof(mpool_free_t)) /* min alloc */ +#define MAX_FREE_SEARCH 10240 /* max size to search */ +#define MAX_FREE_LIST_SEARCH 100 /* max looking for free mem */ + +/* + * bitflag tools for Variable and a Flag + */ +#define BIT_FLAG(x) (1 << (x)) +#define BIT_SET(v,f) (v) |= (f) +#define BIT_CLEAR(v,f) (v) &= ~(f) +#define BIT_IS_SET(v,f) ((v) & (f)) +#define BIT_TOGGLE(v,f) (v) ^= (f) + +#define SET_POINTER(pnt, val) \ + do { \ + if ((pnt) != NULL) { \ + (*(pnt)) = (val); \ + } \ + } while(0) + +#define BLOCK_FLAG_USED BIT_FLAG(0) /* block is used */ +#define BLOCK_FLAG_FREE BIT_FLAG(1) /* block is free */ + +#define DEFAULT_PAGE_MULT 16 /* pagesize = this * getpagesize*/ + +/* How many pages SIZE bytes resides in. We add in the block header. */ +#define PAGES_IN_SIZE(mp_p, size) (((size) + sizeof(mpool_block_t) + \ + (mp_p)->mp_page_size - 1) / \ + (mp_p)->mp_page_size) +#define SIZE_OF_PAGES(mp_p, page_n) ((page_n) * (mp_p)->mp_page_size) +#define MAX_BITS 30 /* we only can allocate 1gb chunks */ + +#define MAX_BLOCK_USER_MEMORY(mp_p) ((mp_p)->mp_page_size - \ + sizeof(mpool_block_t)) +#define FIRST_ADDR_IN_BLOCK(block_p) (void *)((char *)(block_p) + \ + sizeof(mpool_block_t)) +#define MEMORY_IN_BLOCK(block_p) ((char *)(block_p)->mb_bounds_p - \ + ((char *)(block_p) + \ + sizeof(mpool_block_t))) + +typedef struct { + unsigned int mp_magic; /* magic number for struct */ + unsigned int mp_flags; /* flags for the struct */ + unsigned long mp_alloc_c; /* number of allocations */ + unsigned long mp_user_alloc; /* user bytes allocated */ + unsigned long mp_max_alloc; /* maximum user bytes allocated */ + unsigned int mp_page_c; /* number of pages allocated */ + unsigned int mp_max_pages; /* maximum number of pages to use */ + unsigned int mp_page_size; /* page-size of our system */ + int mp_fd; /* fd for /dev/zero if mmap-ing */ + off_t mp_top; /* top of our allocations in fd */ + mpool_log_func_t mp_log_func; /* log callback function */ + void *mp_addr; /* current address for mmaping */ + void *mp_min_p; /* min address in pool for checks */ + void *mp_bounds_p; /* max address in pool for checks */ + struct mpool_block_st *mp_first_p; /* first memory block we are using */ + struct mpool_block_st *mp_last_p; /* last memory block we are using */ + struct mpool_block_st *mp_free[MAX_BITS + 1]; /* free lists based on size */ + unsigned int mp_magic2; /* upper magic for overwrite sanity */ +} mpool_t; + +/* for debuggers to be able to interrogate the generic type in the .h file */ +typedef mpool_t mpool_ext_t; + +/* + * Block header structure. This structure *MUST* be long-word + * aligned. + */ +typedef struct mpool_block_st { + unsigned int mb_magic; /* magic number for block header */ + void *mb_bounds_p; /* block boundary location */ + struct mpool_block_st *mb_next_p; /* linked list next pointer */ + unsigned int mb_magic2; /* upper magic for overwrite sanity */ +} mpool_block_t; + +/* + * Free list structure. + */ +typedef struct { + void *mf_next_p; /* pointer to the next free address */ + unsigned long mf_size; /* size of the free block */ +} mpool_free_t; + +#endif /* ! __MPOOL_LOC_H__ */ diff --git a/libs/libks/src/include/simclist.h b/libs/libks/src/include/simclist.h new file mode 100755 index 0000000000..2ce9d491ad --- /dev/null +++ b/libs/libks/src/include/simclist.h @@ -0,0 +1,980 @@ +/* + * Copyright (c) 2007,2008 Mij + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +/* + * SimCList library. See http://mij.oltrelinux.com/devel/simclist + */ + + +#ifndef SIMCLIST_H +#define SIMCLIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#ifndef SIMCLIST_NO_DUMPRESTORE +# ifndef _WIN32 +# include /* list_dump_info_t's struct timeval */ +# else +# include +# endif +#endif + + +/* Be friend of both C90 and C99 compilers */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + /* "inline" and "restrict" are keywords */ +#else +# define inline /* inline */ +# define restrict /* restrict */ +#endif + + +/** + * Type representing list hashes. + * + * This is a signed integer value. + */ +typedef int32_t list_hash_t; + +#ifndef SIMCLIST_NO_DUMPRESTORE +typedef struct { + uint16_t version; /* dump version */ + struct timeval timestamp; /* when the list has been dumped, seconds since UNIX epoch */ + uint32_t list_size; + uint32_t list_numels; + list_hash_t list_hash; /* hash of the list when dumped, or 0 if invalid */ + uint32_t dumpsize; + int consistent; /* 1 if the dump is verified complete/consistent; 0 otherwise */ +} list_dump_info_t; +#endif + +/** + * a comparator of elements. + * + * A comparator of elements is a function that: + * -# receives two references to elements a and b + * -# returns {<0, 0, >0} if (a > b), (a == b), (a < b) respectively + * + * It is responsability of the function to handle possible NULL values. + */ +typedef int (*element_comparator)(const void *a, const void *b); + +/** + * a seeker of elements. + * + * An element seeker is a function that: + * -# receives a reference to an element el + * -# receives a reference to some indicator data + * -# returns non-0 if the element matches the indicator, 0 otherwise + * + * It is responsability of the function to handle possible NULL values in any + * argument. + */ +typedef int (*element_seeker)(const void *el, const void *indicator); + +/** + * an element lenght meter. + * + * An element meter is a function that: + * -# receives the reference to an element el + * -# returns its size in bytes + * + * It is responsability of the function to handle possible NULL values. + */ +typedef size_t (*element_meter)(const void *el); + +/** + * a function computing the hash of elements. + * + * An hash computing function is a function that: + * -# receives the reference to an element el + * -# returns a hash value for el + * + * It is responsability of the function to handle possible NULL values. + */ +typedef list_hash_t (*element_hash_computer)(const void *el); + +/** + * a function for serializing an element. + * + * A serializer function is one that gets a reference to an element, + * and returns a reference to a buffer that contains its serialization + * along with the length of this buffer. + * It is responsability of the function to handle possible NULL values, + * returning a NULL buffer and a 0 buffer length. + * + * These functions have 3 goals: + * -# "freeze" and "flatten" the memory representation of the element + * -# provide a portable (wrt byte order, or type size) representation of the element, if the dump can be used on different sw/hw combinations + * -# possibly extract a compressed representation of the element + * + * @param el reference to the element data + * @param serialize_buffer reference to fill with the length of the buffer + * @return reference to the buffer with the serialized data + */ +typedef void *(*element_serializer)(const void *restrict el, uint32_t *restrict serializ_len); + +/** + * a function for un-serializing an element. + * + * An unserializer function accomplishes the inverse operation of the + * serializer function. An unserializer function is one that gets a + * serialized representation of an element and turns it backe to the original + * element. The serialized representation is passed as a reference to a buffer + * with its data, and the function allocates and returns the buffer containing + * the original element, and it sets the length of this buffer into the + * integer passed by reference. + * + * @param data reference to the buffer with the serialized representation of the element + * @param data_len reference to the location where to store the length of the data in the buffer returned + * @return reference to a buffer with the original, unserialized representation of the element + */ +typedef void *(*element_unserializer)(const void *restrict data, uint32_t *restrict data_len); + +/* [private-use] list entry -- olds actual user datum */ +struct list_entry_s { + void *data; + + /* doubly-linked list service references */ + struct list_entry_s *next; + struct list_entry_s *prev; +}; + +/* [private-use] list attributes */ +struct list_attributes_s { + /* user-set routine for comparing list elements */ + element_comparator comparator; + /* user-set routing for seeking elements */ + element_seeker seeker; + /* user-set routine for determining the length of an element */ + element_meter meter; + int copy_data; + /* user-set routine for computing the hash of an element */ + element_hash_computer hasher; + /* user-set routine for serializing an element */ + element_serializer serializer; + /* user-set routine for unserializing an element */ + element_unserializer unserializer; +}; + +/** list object */ +typedef struct { + struct list_entry_s *head_sentinel; + struct list_entry_s *tail_sentinel; + struct list_entry_s *mid; + + unsigned int numels; + + /* array of spare elements */ + struct list_entry_s **spareels; + unsigned int spareelsnum; + +#ifdef SIMCLIST_WITH_THREADS + /* how many threads are currently running */ + unsigned int threadcount; +#endif + + /* service variables for list iteration */ + int iter_active; + unsigned int iter_pos; + struct list_entry_s *iter_curentry; + + /* list attributes */ + struct list_attributes_s attrs; +} list_t; + +/** + * initialize a list object for use. + * + * @param l must point to a user-provided memory location + * @return 0 for success. -1 for failure + */ +int list_init(list_t *restrict l); + +/** + * completely remove the list from memory. + * + * This function is the inverse of list_init(). It is meant to be called when + * the list is no longer going to be used. Elements and possible memory taken + * for internal use are freed. + * + * @param l list to destroy + */ +void list_destroy(list_t *restrict l); + +/** + * set the comparator function for list elements. + * + * Comparator functions are used for searching and sorting. If NULL is passed + * as reference to the function, the comparator is disabled. + * + * @param l list to operate + * @param comparator_fun pointer to the actual comparator function + * @return 0 if the attribute was successfully set; -1 otherwise + * + * @see element_comparator() + */ +int list_attributes_comparator(list_t *restrict l, element_comparator comparator_fun); + +/** + * set a seeker function for list elements. + * + * Seeker functions are used for finding elements. If NULL is passed as reference + * to the function, the seeker is disabled. + * + * @param l list to operate + * @param seeker_fun pointer to the actual seeker function + * @return 0 if the attribute was successfully set; -1 otherwise + * + * @see element_seeker() + */ +int list_attributes_seeker(list_t *restrict l, element_seeker seeker_fun); + +/** + * require to free element data when list entry is removed (default: don't free). + * + * [ advanced preference ] + * + * By default, when an element is removed from the list, it disappears from + * the list by its actual data is not free()d. With this option, every + * deletion causes element data to be freed. + * + * It is responsability of this function to correctly handle NULL values, if + * NULL elements are inserted into the list. + * + * @param l list to operate + * @param metric_fun pointer to the actual metric function + * @param copy_data 0: do not free element data (default); non-0: do free + * @return 0 if the attribute was successfully set; -1 otherwise + * + * @see element_meter() + * @see list_meter_int8_t() + * @see list_meter_int16_t() + * @see list_meter_int32_t() + * @see list_meter_int64_t() + * @see list_meter_uint8_t() + * @see list_meter_uint16_t() + * @see list_meter_uint32_t() + * @see list_meter_uint64_t() + * @see list_meter_float() + * @see list_meter_double() + * @see list_meter_string() + */ +int list_attributes_copy(list_t *restrict l, element_meter metric_fun, int copy_data); + +/** + * set the element hash computing function for the list elements. + * + * [ advanced preference ] + * + * An hash can be requested depicting the list status at a given time. An hash + * only depends on the elements and their order. By default, the hash of an + * element is only computed on its reference. With this function, the user can + * set a custom function computing the hash of an element. If such function is + * provided, the list_hash() function automatically computes the list hash using + * the custom function instead of simply referring to element references. + * + * @param l list to operate + * @param hash_computer_fun pointer to the actual hash computing function + * @return 0 if the attribute was successfully set; -1 otherwise + * + * @see element_hash_computer() + */ +int list_attributes_hash_computer(list_t *restrict l, element_hash_computer hash_computer_fun); + +/** + * set the element serializer function for the list elements. + * + * [ advanced preference ] + * + * Serialize functions are used for dumping the list to some persistent + * storage. The serializer function is called for each element; it is passed + * a reference to the element and a reference to a size_t object. It will + * provide (and return) the buffer with the serialization of the element and + * fill the size_t object with the length of this serialization data. + * + * @param l list to operate + * @param serializer_fun pointer to the actual serializer function + * @return 0 if the attribute was successfully set; -1 otherwise + * + * @see element_serializer() + * @see list_dump_filedescriptor() + * @see list_restore_filedescriptor() + */ +int list_attributes_serializer(list_t *restrict l, element_serializer serializer_fun); + +/** + * set the element unserializer function for the list elements. + * + * [ advanced preference ] + * + * Unserialize functions are used for restoring the list from some persistent + * storage. The unserializer function is called for each element segment read + * from the storage; it is passed the segment and a reference to an integer. + * It shall allocate and return a buffer compiled with the resumed memory + * representation of the element, and set the integer value to the length of + * this buffer. + * + * @param l list to operate + * @param unserializer_fun pointer to the actual unserializer function + * @return 0 if the attribute was successfully set; -1 otherwise + * + * @see element_unserializer() + * @see list_dump_filedescriptor() + * @see list_restore_filedescriptor() + */ +int list_attributes_unserializer(list_t *restrict l, element_unserializer unserializer_fun); + +/** + * append data at the end of the list. + * + * This function is useful for adding elements with a FIFO/queue policy. + * + * @param l list to operate + * @param data pointer to user data to append + * + * @return 1 for success. < 0 for failure + */ +int list_append(list_t *restrict l, const void *data); + +/** + * insert data in the head of the list. + * + * This function is useful for adding elements with a LIFO/Stack policy. + * + * @param l list to operate + * @param data pointer to user data to append + * + * @return 1 for success. < 0 for failure + */ +int list_prepend(list_t *restrict l, const void *restrict data); + +/** + * extract the element in the top of the list. + * + * This function is for using a list with a FIFO/queue policy. + * + * @param l list to operate + * @return reference to user datum, or NULL on errors + */ +void *list_fetch(list_t *restrict l); + +/** + * retrieve an element at a given position. + * + * @param l list to operate + * @param pos [0,size-1] position index of the element wanted + * @return reference to user datum, or NULL on errors + */ +void *list_get_at(const list_t *restrict l, unsigned int pos); + +/** + * return the maximum element of the list. + * + * @warning Requires a comparator function to be set for the list. + * + * Returns the maximum element with respect to the comparator function output. + * + * @see list_attributes_comparator() + * + * @param l list to operate + * @return the reference to the element, or NULL + */ +void *list_get_max(const list_t *restrict l); + +/** + * return the minimum element of the list. + * + * @warning Requires a comparator function to be set for the list. + * + * Returns the minimum element with respect to the comparator function output. + * + * @see list_attributes_comparator() + * + * @param l list to operate + * @return the reference to the element, or NULL + */ +void *list_get_min(const list_t *restrict l); + +/** + * retrieve and remove from list an element at a given position. + * + * @param l list to operate + * @param pos [0,size-1] position index of the element wanted + * @return reference to user datum, or NULL on errors + */ +void *list_extract_at(list_t *restrict l, unsigned int pos); + +/** + * insert an element at a given position. + * + * @param l list to operate + * @param data reference to data to be inserted + * @param pos [0,size-1] position index to insert the element at + * @return positive value on success. Negative on failure + */ +int list_insert_at(list_t *restrict l, const void *data, unsigned int pos); + +/** + * expunge the first found given element from the list. + * + * Inspects the given list looking for the given element; if the element + * is found, it is removed. Only the first occurence is removed. + * If a comparator function was not set, elements are compared by reference. + * Otherwise, the comparator is used to match the element. + * + * @param l list to operate + * @param data reference of the element to search for + * @return 0 on success. Negative value on failure + * + * @see list_attributes_comparator() + * @see list_delete_at() + */ +int list_delete(list_t *restrict l, const void *data); + +/** + * expunge an element at a given position from the list. + * + * @param l list to operate + * @param pos [0,size-1] position index of the element to be deleted + * @return 0 on success. Negative value on failure + */ +int list_delete_at(list_t *restrict l, unsigned int pos); + +/** + * expunge an array of elements from the list, given their position range. + * + * @param l list to operate + * @param posstart [0,size-1] position index of the first element to be deleted + * @param posend [posstart,size-1] position of the last element to be deleted + * @return the number of elements successfully removed on success, <0 on error + */ +int list_delete_range(list_t *restrict l, unsigned int posstart, unsigned int posend); + +/** + * clear all the elements off of the list. + * + * The element datums will not be freed. + * + * @see list_delete_range() + * @see list_size() + * + * @param l list to operate + * @return the number of elements removed on success, <0 on error + */ +int list_clear(list_t *restrict l); + +/** + * inspect the number of elements in the list. + * + * @param l list to operate + * @return number of elements currently held by the list + */ +unsigned int list_size(const list_t *restrict l); + +/** + * inspect whether the list is empty. + * + * @param l list to operate + * @return 0 iff the list is not empty + * + * @see list_size() + */ +int list_empty(const list_t *restrict l); + +/** + * find the position of an element in a list. + * + * @warning Requires a comparator function to be set for the list. + * + * Inspects the given list looking for the given element; if the element + * is found, its position into the list is returned. + * Elements are inspected comparing references if a comparator has not been + * set. Otherwise, the comparator is used to find the element. + * + * @param l list to operate + * @param data reference of the element to search for + * @return position of element in the list, or <0 if not found + * + * @see list_attributes_comparator() + * @see list_get_at() + */ +int list_locate(const list_t *restrict l, const void *data); + +/** + * returns an element given an indicator. + * + * @warning Requires a seeker function to be set for the list. + * + * Inspect the given list looking with the seeker if an element matches + * an indicator. If such element is found, the reference to the element + * is returned. + * + * @param l list to operate + * @param indicator indicator data to pass to the seeker along with elements + * @return reference to the element accepted by the seeker, or NULL if none found + */ +void *list_seek(list_t *restrict l, const void *indicator); + +/** + * inspect whether some data is member of the list. + * + * @warning Requires a comparator function to be set for the list. + * + * By default, a per-reference comparison is accomplished. That is, + * the data is in list if any element of the list points to the same + * location of data. + * A "semantic" comparison is accomplished, otherwise, if a comparator + * function has been set previously, with list_attributes_comparator(); + * in which case, the given data reference is believed to be in list iff + * comparator_fun(elementdata, userdata) == 0 for any element in the list. + * + * @param l list to operate + * @param data reference to the data to search + * @return 0 iff the list does not contain data as an element + * + * @see list_attributes_comparator() + */ +int list_contains(const list_t *restrict l, const void *data); + +/** + * concatenate two lists + * + * Concatenates one list with another, and stores the result into a + * user-provided list object, which must be different from both the + * lists to concatenate. Attributes from the original lists are not + * cloned. + * The destination list referred is threated as virgin room: if it + * is an existing list containing elements, memory leaks will happen. + * It is OK to specify the same list twice as source, for "doubling" + * it in the destination. + * + * @param l1 base list + * @param l2 list to append to the base + * @param dest reference to the destination list + * @return 0 for success, -1 for errors + */ +int list_concat(const list_t *l1, const list_t *l2, list_t *restrict dest); + +/** + * sort list elements. + * + * @warning Requires a comparator function to be set for the list. + * + * Sorts the list in ascending or descending order as specified by the versus + * flag. The algorithm chooses autonomously what algorithm is best suited for + * sorting the list wrt its current status. + * + * @param l list to operate + * @param versus positive: order small to big; negative: order big to small + * @return 0 iff sorting was successful + * + * @see list_attributes_comparator() + */ +int list_sort(list_t *restrict l, int versus); + +/** + * start an iteration session. + * + * This function prepares the list to be iterated. + * + * @param l list to operate + * @return 0 if the list cannot be currently iterated. >0 otherwise + * + * @see list_iterator_stop() + */ +int list_iterator_start(list_t *restrict l); + +/** + * return the next element in the iteration session. + * + * @param l list to operate + * @return element datum, or NULL on errors + */ +void *list_iterator_next(list_t *restrict l); + +/** + * inspect whether more elements are available in the iteration session. + * + * @param l list to operate + * @return 0 iff no more elements are available. + */ +int list_iterator_hasnext(const list_t *restrict l); + +/** + * end an iteration session. + * + * @param l list to operate + * @return 0 iff the iteration session cannot be stopped + */ +int list_iterator_stop(list_t *restrict l); + +/** + * return the hash of the current status of the list. + * + * @param l list to operate + * @param hash where the resulting hash is put + * + * @return 0 for success; <0 for failure + */ +int list_hash(const list_t *restrict l, list_hash_t *restrict hash); + +#ifndef SIMCLIST_NO_DUMPRESTORE +/** + * get meta informations on a list dump on filedescriptor. + * + * [ advanced function ] + * + * Extracts the meta information from a SimCList dump located in a file + * descriptor. The file descriptor must be open and positioned at the + * beginning of the SimCList dump block. + * + * @param fd file descriptor to get metadata from + * @param info reference to a dump metainformation structure to fill + * @return 0 for success; <0 for failure + * + * @see list_dump_filedescriptor() + */ +int list_dump_getinfo_filedescriptor(int fd, list_dump_info_t *restrict info); + +/** + * get meta informations on a list dump on file. + * + * [ advanced function ] + * + * Extracts the meta information from a SimCList dump located in a file. + * + * @param filename filename of the file to fetch from + * @param info reference to a dump metainformation structure to fill + * @return 0 for success; <0 for failure + * + * @see list_dump_filedescriptor() + */ +int list_dump_getinfo_file(const char *restrict filename, list_dump_info_t *restrict info); + +/** + * dump the list into an open, writable file descriptor. + * + * This function "dumps" the list to a persistent storage so it can be + * preserved across process terminations. + * When called, the file descriptor must be open for writing and positioned + * where the serialized data must begin. It writes its serialization of the + * list in a form which is portable across different architectures. Dump can + * be safely performed on stream-only (non seekable) descriptors. The file + * descriptor is not closed at the end of the operations. + * + * To use dump functions, either of these conditions must be satisfied: + * -# a metric function has been specified with list_attributes_copy() + * -# a serializer function has been specified with list_attributes_serializer() + * + * If a metric function has been specified, each element of the list is dumped + * as-is from memory, copying it from its pointer for its length down to the + * file descriptor. This might have impacts on portability of the dump to + * different architectures. + * + * If a serializer function has been specified, its result for each element is + * dumped to the file descriptor. + * + * + * @param l list to operate + * @param fd file descriptor to write to + * @param len location to store the resulting length of the dump (bytes), or NULL + * + * @return 0 if successful; -1 otherwise + * + * @see element_serializer() + * @see list_attributes_copy() + * @see list_attributes_serializer() + */ +int list_dump_filedescriptor(const list_t *restrict l, int fd, size_t *restrict len); + +/** + * dump the list to a file name. + * + * This function creates a filename and dumps the current content of the list + * to it. If the file exists it is overwritten. The number of bytes written to + * the file can be returned in a specified argument. + * + * @param l list to operate + * @param filename filename to write to + * @param len location to store the resulting length of the dump (bytes), or NULL + * + * @return 0 if successful; -1 otherwise + * + * @see list_attributes_copy() + * @see element_serializer() + * @see list_attributes_serializer() + * @see list_dump_filedescriptor() + * @see list_restore_file() + * + * This function stores a representation of the list + */ +int list_dump_file(const list_t *restrict l, const char *restrict filename, size_t *restrict len); + +/** + * restore the list from an open, readable file descriptor to memory. + * + * This function is the "inverse" of list_dump_filedescriptor(). It restores + * the list content from a (open, read-ready) file descriptor to memory. An + * unserializer might be needed to restore elements from the persistent + * representation back into memory-consistent format. List attributes can not + * be restored and must be set manually. + * + * @see list_dump_filedescriptor() + * @see list_attributes_serializer() + * @see list_attributes_unserializer() + * + * @param l list to restore to + * @param fd file descriptor to read from. + * @param len location to store the length of the dump read (bytes), or NULL + * @return 0 if successful; -1 otherwise + */ +int list_restore_filedescriptor(list_t *restrict l, int fd, size_t *restrict len); + +/** + * restore the list from a file name. + * + * This function restores the content of a list from a file into memory. It is + * the inverse of list_dump_file(). + * + * @see element_unserializer() + * @see list_attributes_unserializer() + * @see list_dump_file() + * @see list_restore_filedescriptor() + * + * @param l list to restore to + * @param filename filename to read data from + * @param len location to store the length of the dump read (bytes), or NULL + * @return 0 if successful; -1 otherwise + */ +int list_restore_file(list_t *restrict l, const char *restrict filename, size_t *len); +#endif + +/* ready-made comparators, meters and hash computers */ + /* comparator functions */ +/** + * ready-made comparator for int8_t elements. + * @see list_attributes_comparator() + */ +int list_comparator_int8_t(const void *a, const void *b); + +/** + * ready-made comparator for int16_t elements. + * @see list_attributes_comparator() + */ +int list_comparator_int16_t(const void *a, const void *b); + +/** + * ready-made comparator for int32_t elements. + * @see list_attributes_comparator() + */ +int list_comparator_int32_t(const void *a, const void *b); + +/** + * ready-made comparator for int64_t elements. + * @see list_attributes_comparator() + */ +int list_comparator_int64_t(const void *a, const void *b); + +/** + * ready-made comparator for uint8_t elements. + * @see list_attributes_comparator() + */ +int list_comparator_uint8_t(const void *a, const void *b); + +/** + * ready-made comparator for uint16_t elements. + * @see list_attributes_comparator() + */ +int list_comparator_uint16_t(const void *a, const void *b); + +/** + * ready-made comparator for uint32_t elements. + * @see list_attributes_comparator() + */ +int list_comparator_uint32_t(const void *a, const void *b); + +/** + * ready-made comparator for uint64_t elements. + * @see list_attributes_comparator() + */ +int list_comparator_uint64_t(const void *a, const void *b); + +/** + * ready-made comparator for float elements. + * @see list_attributes_comparator() + */ +int list_comparator_float(const void *a, const void *b); + +/** + * ready-made comparator for double elements. + * @see list_attributes_comparator() + */ +int list_comparator_double(const void *a, const void *b); + +/** + * ready-made comparator for string elements. + * @see list_attributes_comparator() + */ +int list_comparator_string(const void *a, const void *b); + + /* metric functions */ +/** + * ready-made metric function for int8_t elements. + * @see list_attributes_copy() + */ +size_t list_meter_int8_t(const void *el); + +/** + * ready-made metric function for int16_t elements. + * @see list_attributes_copy() + */ +size_t list_meter_int16_t(const void *el); + +/** + * ready-made metric function for int32_t elements. + * @see list_attributes_copy() + */ +size_t list_meter_int32_t(const void *el); + +/** + * ready-made metric function for int64_t elements. + * @see list_attributes_copy() + */ +size_t list_meter_int64_t(const void *el); + +/** + * ready-made metric function for uint8_t elements. + * @see list_attributes_copy() + */ +size_t list_meter_uint8_t(const void *el); + +/** + * ready-made metric function for uint16_t elements. + * @see list_attributes_copy() + */ +size_t list_meter_uint16_t(const void *el); + +/** + * ready-made metric function for uint32_t elements. + * @see list_attributes_copy() + */ +size_t list_meter_uint32_t(const void *el); + +/** + * ready-made metric function for uint64_t elements. + * @see list_attributes_copy() + */ +size_t list_meter_uint64_t(const void *el); + +/** + * ready-made metric function for float elements. + * @see list_attributes_copy() + */ +size_t list_meter_float(const void *el); + +/** + * ready-made metric function for double elements. + * @see list_attributes_copy() + */ +size_t list_meter_double(const void *el); + +/** + * ready-made metric function for string elements. + * @see list_attributes_copy() + */ +size_t list_meter_string(const void *el); + + /* hash functions */ +/** + * ready-made hash function for int8_t elements. + * @see list_attributes_hash_computer() + */ +list_hash_t list_hashcomputer_int8_t(const void *el); + +/** + * ready-made hash function for int16_t elements. + * @see list_attributes_hash_computer() + */ +list_hash_t list_hashcomputer_int16_t(const void *el); + +/** + * ready-made hash function for int32_t elements. + * @see list_attributes_hash_computer() + */ +list_hash_t list_hashcomputer_int32_t(const void *el); + +/** + * ready-made hash function for int64_t elements. + * @see list_attributes_hash_computer() + */ +list_hash_t list_hashcomputer_int64_t(const void *el); + +/** + * ready-made hash function for uint8_t elements. + * @see list_attributes_hash_computer() + */ +list_hash_t list_hashcomputer_uint8_t(const void *el); + +/** + * ready-made hash function for uint16_t elements. + * @see list_attributes_hash_computer() + */ +list_hash_t list_hashcomputer_uint16_t(const void *el); + +/** + * ready-made hash function for uint32_t elements. + * @see list_attributes_hash_computer() + */ +list_hash_t list_hashcomputer_uint32_t(const void *el); + +/** + * ready-made hash function for uint64_t elements. + * @see list_attributes_hash_computer() + */ +list_hash_t list_hashcomputer_uint64_t(const void *el); + +/** + * ready-made hash function for float elements. + * @see list_attributes_hash_computer() + */ +list_hash_t list_hashcomputer_float(const void *el); + +/** + * ready-made hash function for double elements. + * @see list_attributes_hash_computer() + */ +list_hash_t list_hashcomputer_double(const void *el); + +/** + * ready-made hash function for string elements. + * @see list_attributes_hash_computer() + */ +list_hash_t list_hashcomputer_string(const void *el); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libs/libks/src/include/table.h b/libs/libks/src/include/table.h new file mode 100644 index 0000000000..687358683d --- /dev/null +++ b/libs/libks/src/include/table.h @@ -0,0 +1,1372 @@ +/* + * Generic table defines... + * + * Copyright 2000 by Gray Watson. + * + * This file is part of the table package. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, + * and that the name of Gray Watson not be used in advertising or + * publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be reached via http://256.com/gray/ + * + * $Id: table.h,v 1.11 2000/03/09 03:30:42 gray Exp $ + */ + +#ifndef __TABLE_H__ +#define __TABLE_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * To build a "key" in any of the below routines, pass in a pointer to + * the key and its size [i.e. sizeof(int), etc]. With any of the + * "key" or "data" arguments, if their size is < 0, it will do an + * internal strlen of the item and add 1 for the \0. + * + * If you are using firstkey() and nextkey() functions, be careful if, + * after starting your firstkey loop, you use delete or insert, it + * will not crash but may produce interesting results. If you are + * deleting from firstkey to NULL it will work fine. + */ + +/* return types for table functions */ +#define TABLE_ERROR_NONE 1 /* no error from function */ +#define TABLE_ERROR_PNT 2 /* bad table pointer */ +#define TABLE_ERROR_ARG_NULL 3 /* buffer args were null */ +#define TABLE_ERROR_SIZE 4 /* size of data was bad */ +#define TABLE_ERROR_OVERWRITE 5 /* key exists and we cant overwrite */ +#define TABLE_ERROR_NOT_FOUND 6 /* key does not exist */ +#define TABLE_ERROR_ALLOC 7 /* memory allocation error */ +#define TABLE_ERROR_LINEAR 8 /* no linear access started */ +#define TABLE_ERROR_OPEN 9 /* could not open file */ +#define TABLE_ERROR_SEEK 10 /* could not seek to pos in file */ +#define TABLE_ERROR_READ 11 /* could not read from file */ +#define TABLE_ERROR_WRITE 12 /* could not write to file */ +#define TABLE_ERROR_MMAP_NONE 13 /* no mmap support */ +#define TABLE_ERROR_MMAP 14 /* could not mmap file */ +#define TABLE_ERROR_MMAP_OP 15 /* can't perform operation on mmap */ +#define TABLE_ERROR_EMPTY 16 /* table is empty */ +#define TABLE_ERROR_NOT_EMPTY 17 /* table contains data */ +#define TABLE_ERROR_ALIGNMENT 18 /* invalid alignment value */ +#define TABLE_ERROR_COMPARE 19 /* problems with internal comparison */ +#define TABLE_ERROR_FREE 20 /* memory free error */ + +/* + * Table flags set with table_attr. + */ + +/* + * Automatically adjust the number of table buckets on the fly. + * Whenever the number of entries gets above some threshold, the + * number of buckets is realloced to a new size and each entry is + * re-hashed. Although this may take some time when it re-hashes, the + * table will perform better over time. + */ +#define TABLE_FLAG_AUTO_ADJUST (1<<0) + +/* + * If the above auto-adjust flag is set, also adjust the number of + * table buckets down as we delete entries. + */ +#define TABLE_FLAG_ADJUST_DOWN (1<<1) + +/* structure to walk through the fields in a linear order */ +typedef struct { + unsigned int tl_magic; /* magic structure to ensure correct init */ + unsigned int tl_bucket_c; /* where in the table buck array we are */ + unsigned int tl_entry_c; /* in the bucket, which entry we are on */ +} table_linear_t; + +/* + * int (*table_compare_t) + * + * DESCRIPTION + * + * Comparison function which compares two key/data pairs for table + * order. + * + * RETURNS: + * + * -1, 0, or 1 if key1 is <, ==, or > than key2. + * + * ARGUMENTS: + * + * key1 - Pointer to the first key entry. + * + * key1_size - Pointer to the size of the first key entry. + * + * data1 - Pointer to the first data entry. + * + * data1_size - Pointer to the size of the first data entry. + * + * key2 - Pointer to the second key entry. + * + * key2_size - Pointer to the size of the second key entry. + * + * data2 - Pointer to the second data entry. + * + * data2_size - Pointer to the size of the second data entry. + */ +typedef int (*table_compare_t)(const void *key1, const int key1_size, + const void *data1, const int data1_size, + const void *key2, const int key2_size, + const void *data2, const int data2_size); + +/* + * int (*table_mem_alloc_t) + * + * DESCRIPTION + * + * Function to override the table's allocation function. + * + * RETURNS: + * + * Success - Newly allocated pointer. + * + * Failure - NULL + * + * ARGUMENTS: + * + * pool_p <-> Pointer to our memory pool. If no pool is set then this + * will be NULL. + * + * size -> Number of bytes that needs to be allocated. + */ +typedef void *(*table_mem_alloc_t)(void *pool_p, const unsigned long size); + +/* + * int (*table_mem_resize_t) + * + * DESCRIPTION + * + * Function to override the table's memory resize function. The + * difference between this and realloc is that this provides the + * previous allocation size. You can specify NULL for this function + * in which cause the library will allocate, copy, and free itself. + * + * RETURNS: + * + * Success - Newly allocated pointer. + * + * Failure - NULL + * + * ARGUMENTS: + * + * pool_p <-> Pointer to our memory pool. If no pool is set then this + * will be NULL. + * + * old_addr -> Previously allocated address. + * + * old_size -> Size of the old address. Since the system is + * lightweight, it does not store size information on the pointer. + * + * new_size -> New size of the allocation. + */ +typedef void *(*table_mem_resize_t)(void *pool_p, void *old_addr, + const unsigned long old_size, + const unsigned long new_size); + +/* + * int (*table_mem_free_t) + * + * DESCRIPTION + * + * Function to override the table's free function. + * + * RETURNS: + * + * Success - 1 + * + * Failure - 0 + * + * ARGUMENTS: + * + * pool_p <-> Pointer to our memory pool. If no pool is set then this + * will be NULL. + * + * addr -> Address that we are freeing. + * + * min_size -> Minimum size of the address being freed or 0 if not + * known. This can also be the exact size if known. + */ +typedef int (*table_mem_free_t)(void *pool_p, void *addr, + const unsigned long min_size); + +#ifdef TABLE_MAIN + +#include "table_loc.h" + +#else + +/* generic table type */ +typedef void table_t; + +/* generic table entry type */ +typedef void table_entry_t; + +#endif + +/*<<<<<<<<<< The below prototypes are auto-generated by fillproto */ + +/* + * table_t *table_alloc + * + * DESCRIPTION: + * + * Allocate a new table structure. + * + * RETURNS: + * + * A pointer to the new table structure which must be passed to + * table_free to be deallocated. On error a NULL is returned. + * + * ARGUMENTS: + * + * bucket_n - Number of buckets for the hash table. Our current hash + * value works best with base two numbers. Set to 0 to take the + * library default of 1024. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +extern +table_t *table_alloc(const unsigned int bucket_n, int *error_p); + +/* + * table_t *table_alloc_in_pool + * + * DESCRIPTION: + * + * Allocate a new table structure in a memory pool or using + * alternative allocation and free functions. + * + * RETURNS: + * + * A pointer to the new table structure which must be passed to + * table_free to be deallocated. On error a NULL is returned. + * + * ARGUMENTS: + * + * bucket_n - Number of buckets for the hash table. Our current hash + * value works best with base two numbers. Set to 0 to take the + * library default of 1024. + * + * mem_pool <-> Memory pool to associate with the table. Can be NULL. + * + * alloc_func -> Allocate function we are overriding malloc() with. + * + * resize_func -> Resize function we are overriding the standard + * memory resize/realloc with. This can be NULL in which cause the + * library will allocate, copy, and free itself. + * + * free_func -> Free function we are overriding free() with. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +extern +table_t *table_alloc_in_pool(const unsigned int bucket_n, + void *mem_pool, + table_mem_alloc_t alloc_func, + table_mem_resize_t resize_func, + table_mem_free_t free_func, int *error_p); + +/* + * int table_attr + * + * DESCRIPTION: + * + * Set the attributes for the table. The available attributes are + * specified at the top of table.h. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to a table structure which we will be altering. + * + * attr - Attribute(s) that we will be applying to the table. + */ +extern +int table_attr(table_t *table_p, const int attr); + +/* + * int table_set_data_alignment + * + * DESCRIPTION: + * + * Set the alignment for the data in the table. This is used when you + * want to store binary data types and refer to them directly out of + * the table storage. For instance if you are storing integers as + * data in the table and want to be able to retrieve the location of + * the interger and then increment it as (*loc_p)++. Otherwise you + * would have to memcpy it out to an integer, increment it, and memcpy + * it back. If you are storing character data, no alignment is + * necessary. + * + * For most data elements, sizeof(long) is recommended unless you use + * smaller data types exclusively. + * + * WARNING: If necessary, you must set the data alignment before any + * data gets put into the table. Otherwise a TABLE_ERROR_NOT_EMPTY + * error will be returned. + * + * NOTE: there is no way to set the key data alignment although it + * should automatically be long aligned. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to a table structure which we will be altering. + * + * alignment - Alignment requested for the data. Must be a power of + * 2. Set to 0 for none. + */ +extern +int table_set_data_alignment(table_t *table_p, const int alignment); + +/* + * int table_clear + * + * DESCRIPTION: + * + * Clear out and free all elements in a table structure. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer that we will be clearing. + */ +extern +int table_clear(table_t *table_p); + +/* + * int table_free + * + * DESCRIPTION: + * + * Deallocates a table structure. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer that we will be freeing. + */ +extern +int table_free(table_t *table_p); + +/* + * int table_insert_kd + * + * DESCRIPTION: + * + * Like table_insert except it passes back a pointer to the key and + * the data buffers after they have been inserted into the table + * structure. + * + * This routine adds a key/data pair both of which are made up of a + * buffer of bytes and an associated size. Both the key and the data + * will be copied into buffers allocated inside the table. If the key + * exists already, the associated data will be replaced if the + * overwrite flag is set, otherwise an error is returned. + * + * NOTE: be very careful changing the values since the table library + * provides the pointers to its memory. The key can _never_ be + * changed otherwise you will not find it again. The data can be + * changed but its length can never be altered unless you delete and + * re-insert it into the table. + * + * WARNING: The pointers to the key and data are not in any specific + * alignment. Accessing the key and/or data as an short, integer, or + * long pointer directly can cause problems. + * + * WARNING: Replacing a data cell (not inserting) will cause the table + * linked list to be temporarily invalid. Care must be taken with + * multiple threaded programs which are relying on the first/next + * linked list to be always valid. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer into which we will be inserting a + * new key/data pair. + * + * key_buf - Buffer of bytes of the key that we are inserting. If you + * are storing an (int) as the key (for example) then key_buf should + * be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are storing an (int) as the key (for example) then key_size should + * be sizeof(int). + * + * data_buf - Buffer of bytes of the data that we are inserting. If + * it is NULL then the library will allocate space for the data in the + * table without copying in any information. If data_buf is NULL and + * data_size is 0 then the library will associate a NULL data pointer + * with the key. If you are storing a (long) as the data (for + * example) then data_buf should be a (long *). + * + * data_size - Size of the data_buf buffer. If set to < 0 then the + * library will do a strlen of data_buf and add 1 for the '\0'. If + * you are storing an (long) as the key (for example) then key_size + * should be sizeof(long). + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the key storage that was allocated in the table. If you are + * storing an (int) as the key (for example) then key_buf_p should be + * (int **) i.e. the address of a (int *). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table. If you are + * storing an (long) as the data (for example) then data_buf_p should + * be (long **) i.e. the address of a (long *). + * + * overwrite - Flag which, if set to 1, will allow the overwriting of + * the data in the table with the new data if the key already exists + * in the table. + */ +extern +int table_insert_kd(table_t *table_p, + const void *key_buf, const int key_size, + const void *data_buf, const int data_size, + void **key_buf_p, void **data_buf_p, + const char overwrite_b); + +/* + * int table_insert + * + * DESCRIPTION: + * + * Exactly the same as table_insert_kd except it does not pass back a + * pointer to the key after they have been inserted into the table + * structure. This is still here for backwards compatibility. + * + * See table_insert_kd for more information. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer into which we will be inserting a + * new key/data pair. + * + * key_buf - Buffer of bytes of the key that we are inserting. If you + * are storing an (int) as the key (for example) then key_buf should + * be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are storing an (int) as the key (for example) then key_size should + * be sizeof(int). + * + * data_buf - Buffer of bytes of the data that we are inserting. If + * it is NULL then the library will allocate space for the data in the + * table without copying in any information. If data_buf is NULL and + * data_size is 0 then the library will associate a NULL data pointer + * with the key. If you are storing a (long) as the data (for + * example) then data_buf should be a (long *). + * + * data_size - Size of the data_buf buffer. If set to < 0 then the + * library will do a strlen of data_buf and add 1 for the '\0'. If + * you are storing an (long) as the key (for example) then key_size + * should be sizeof(long). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table. If you are + * storing an (long) as the data (for example) then data_buf_p should + * be (long **) i.e. the address of a (long *). + * + * overwrite - Flag which, if set to 1, will allow the overwriting of + * the data in the table with the new data if the key already exists + * in the table. + */ +extern +int table_insert(table_t *table_p, + const void *key_buf, const int key_size, + const void *data_buf, const int data_size, + void **data_buf_p, const char overwrite_b); + +/* + * int table_retrieve + * + * DESCRIPTION: + * + * This routine looks up a key made up of a buffer of bytes and an + * associated size in the table. If found then it returns the + * associated data information. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer into which we will be searching + * for the key. + * + * key_buf - Buffer of bytes of the key that we are searching for. If + * you are looking for an (int) as the key (for example) then key_buf + * should be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are looking for an (int) as the key (for example) then key_size + * should be sizeof(int). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table and that is + * associated with the key. If a (long) was stored as the data (for + * example) then data_buf_p should be (long **) i.e. the address of a + * (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data stored in the table that is associated with + * the key. + */ +extern +int table_retrieve(table_t *table_p, + const void *key_buf, const int key_size, + void **data_buf_p, int *data_size_p); + +/* + * int table_delete + * + * DESCRIPTION: + * + * This routine looks up a key made up of a buffer of bytes and an + * associated size in the table. If found then it will be removed + * from the table. The associated data can be passed back to the user + * if requested. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * NOTE: this could be an allocation error if the library is to return + * the data to the user. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we will be deleteing + * the key. + * + * key_buf - Buffer of bytes of the key that we are searching for to + * delete. If you are deleting an (int) key (for example) then + * key_buf should be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are deleting an (int) key (for example) then key_size should be + * sizeof(int). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table and that was + * associated with the key. If a (long) was stored as the data (for + * example) then data_buf_p should be (long **) i.e. the address of a + * (long *). If a pointer is passed in, the caller is responsible for + * freeing it after use. If data_buf_p is NULL then the library will + * free up the data allocation itself. + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that was stored in the table and that was + * associated with the key. + */ +extern +int table_delete(table_t *table_p, + const void *key_buf, const int key_size, + void **data_buf_p, int *data_size_p); + +/* + * int table_delete_first + * + * DESCRIPTION: + * + * This is like the table_delete routines except it deletes the first + * key/data pair in the table instead of an entry corresponding to a + * particular key. The associated key and data information can be + * passed back to the user if requested. This routines is handy to + * clear out a table. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * NOTE: this could be an allocation error if the library is to return + * the data to the user. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we will be deleteing + * the first key. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the first key that was allocated in the table. + * If an (int) was stored as the first key (for example) then + * key_buf_p should be (int **) i.e. the address of a (int *). If a + * pointer is passed in, the caller is responsible for freeing it + * after use. If key_buf_p is NULL then the library will free up the + * key allocation itself. + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that was stored in the table and that was + * associated with the key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table and that was + * associated with the key. If a (long) was stored as the data (for + * example) then data_buf_p should be (long **) i.e. the address of a + * (long *). If a pointer is passed in, the caller is responsible for + * freeing it after use. If data_buf_p is NULL then the library will + * free up the data allocation itself. + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that was stored in the table and that was + * associated with the key. + */ +extern +int table_delete_first(table_t *table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p); + +/* + * int table_info + * + * DESCRIPTION: + * + * Get some information about a table_p structure. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting + * information. + * + * num_buckets_p - Pointer to an integer which, if not NULL, will + * contain the number of buckets in the table. + * + * num_entries_p - Pointer to an integer which, if not NULL, will + * contain the number of entries stored in the table. + */ +extern +int table_info(table_t *table_p, int *num_buckets_p, int *num_entries_p); + +/* + * int table_adjust + * + * DESCRIPTION: + * + * Set the number of buckets in a table to a certain value. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer of which we are adjusting. + * + * bucket_n - Number buckets to adjust the table to. Set to 0 to + * adjust the table to its number of entries. + */ +extern +int table_adjust(table_t *table_p, const int bucket_n); + +/* + * int table_type_size + * + * DESCRIPTION: + * + * Return the size of the internal table type. + * + * RETURNS: + * + * The size of the table_t type. + * + * ARGUMENTS: + * + * None. + */ +extern +int table_type_size(void); + +/* + * int table_first + * + * DESCRIPTION: + * + * Find first element in a table and pass back information about the + * key/data pair. If any of the key/data pointers are NULL then they + * are ignored. + * + * NOTE: This function is not reentrant. More than one thread cannot + * be doing a first and next on the same table at the same time. Use + * the table_first_r version below for this. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * first element. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the first key that is allocated in the table. If + * an (int) is stored as the first key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the first key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the first key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the first key. + */ +extern +int table_first(table_t *table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p); + +/* + * int table_next + * + * DESCRIPTION: + * + * Find the next element in a table and pass back information about + * the key/data pair. If any of the key/data pointers are NULL then + * they are ignored. + * + * NOTE: This function is not reentrant. More than one thread cannot + * be doing a first and next on the same table at the same time. Use + * the table_next_r version below for this. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * next element. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the next key that is allocated in the table. If + * an (int) is stored as the next key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the next key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the next key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the next key. + */ +extern +int table_next(table_t *table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p); + +/* + * int table_this + * + * DESCRIPTION: + * + * Find the current element in a table and pass back information about + * the key/data pair. If any of the key/data pointers are NULL then + * they are ignored. + * + * NOTE: This function is not reentrant. Use the table_current_r + * version below. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * current element. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the current key that is allocated in the table. + * If an (int) is stored as the current key (for example) then + * key_buf_p should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the current key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the current key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the current key. + */ +extern +int table_this(table_t *table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p); + +/* + * int table_first_r + * + * DESCRIPTION: + * + * Reetrant version of the table_first routine above. Find first + * element in a table and pass back information about the key/data + * pair. If any of the key/data pointers are NULL then they are + * ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * first element. + * + * linear_p - Pointer to a table linear structure which is initialized + * here. The same pointer should then be passed to table_next_r + * below. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the first key that is allocated in the table. If + * an (int) is stored as the first key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the first key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the first key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the first key. + */ +extern +int table_first_r(table_t *table_p, table_linear_t *linear_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p); + +/* + * int table_next_r + * + * DESCRIPTION: + * + * Reetrant version of the table_next routine above. Find next + * element in a table and pass back information about the key/data + * pair. If any of the key/data pointers are NULL then they are + * ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * next element. + * + * linear_p - Pointer to a table linear structure which is incremented + * here. The same pointer must have been passed to table_first_r + * first so that it can be initialized. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the next key that is allocated in the table. If + * an (int) is stored as the next key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL will be set + * to the size of the key that is stored in the table and that is + * associated with the next key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the next key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the next key. + */ +extern +int table_next_r(table_t *table_p, table_linear_t *linear_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p); + +/* + * int table_this_r + * + * DESCRIPTION: + * + * Reetrant version of the table_this routine above. Find current + * element in a table and pass back information about the key/data + * pair. If any of the key/data pointers are NULL then they are + * ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * current element. + * + * linear_p - Pointer to a table linear structure which is accessed + * here. The same pointer must have been passed to table_first_r + * first so that it can be initialized. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the current key that is allocated in the table. + * If an (int) is stored as the current key (for example) then + * key_buf_p should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the current key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the current key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the current key. + */ +extern +int table_this_r(table_t *table_p, table_linear_t *linear_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p); + +/* + * table_t *table_mmap + * + * DESCRIPTION: + * + * Mmap a table from a file that had been written to disk earlier via + * table_write. + * + * RETURNS: + * + * A pointer to the new table structure which must be passed to + * table_munmap to be deallocated. On error a NULL is returned. + * + * ARGUMENTS: + * + * path - Table file to mmap in. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +extern +table_t *table_mmap(const char *path, int *error_p); + +/* + * int table_munmap + * + * DESCRIPTION: + * + * Unmmap a table that was previously mmapped using table_mmap. + * + * RETURNS: + * + * Returns table error codes. + * + * ARGUMENTS: + * + * table_p - Mmaped table pointer to unmap. + */ +extern +int table_munmap(table_t *table_p); + +/* + * int table_read + * + * DESCRIPTION: + * + * Read in a table from a file that had been written to disk earlier + * via table_write. + * + * RETURNS: + * + * Success - Pointer to the new table structure which must be passed + * to table_free to be deallocated. + * + * Failure - NULL + * + * ARGUMENTS: + * + * path - Table file to read in. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +extern +table_t *table_read(const char *path, int *error_p); + +/* + * int table_write + * + * DESCRIPTION: + * + * Write a table from memory to file. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to the table that we are writing to the file. + * + * path - Table file to write out to. + * + * mode - Mode of the file. This argument is passed on to open when + * the file is created. + */ +extern +int table_write(const table_t *table_p, const char *path, const int mode); + +/* + * table_entry_t *table_order + * + * DESCRIPTION: + * + * Order a table by building an array of table entry pointers and then + * sorting this array using the qsort function. To retrieve the + * sorted entries, you can then use the table_entry routine to access + * each entry in order. + * + * NOTE: This routine is thread safe and makes use of an internal + * status qsort function. + * + * RETURNS: + * + * Success - An allocated list of table-linear structures which must + * be freed by table_order_free later. + * + * Failure - NULL + * + * ARGUMENTS: + * + * table_p - Pointer to the table that we are ordering. + * + * compare - Comparison function defined by the user. Its definition + * is at the top of the table.h file. If this is NULL then it will + * order the table my memcmp-ing the keys. + * + * num_entries_p - Pointer to an integer which, if not NULL, will + * contain the number of entries in the returned entry pointer array. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +extern +table_entry_t **table_order(table_t *table_p, table_compare_t compare, + int *num_entries_p, int *error_p); + +/* + * int table_order_free + * + * DESCRIPTION: + * + * Free the pointer returned by the table_order or table_order_pos + * routines. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to the table. + * + * table_entries - Allocated list of entry pointers returned by + * table_order. + * + * entry_n - Number of entries in the array as passed back by + * table_order or table_order_pos in num_entries_p. + */ +extern +int table_order_free(table_t *table_p, table_entry_t **table_entries, + const int entry_n); + +/* + * int table_entry + * + * DESCRIPTION: + * + * Get information about an element. The element is one from the + * array returned by the table_order function. If any of the key/data + * pointers are NULL then they are ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * element. + * + * entry_p - Pointer to a table entry from the array returned by the + * table_order function. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of this entry that is allocated in the table. If an + * (int) is stored as this entry (for example) then key_buf_p should + * be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage of this entry that is allocated in the table. + * If a (long) is stored as this entry data (for example) then + * data_buf_p should be (long **) i.e. the address of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table. + */ +extern +int table_entry(table_t *table_p, table_entry_t *entry_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p); + +/* + * table_linear_t *table_order_pos + * + * DESCRIPTION: + * + * Order a table by building an array of table linear structures and + * then sorting this array using the qsort function. To retrieve the + * sorted entries, you can then use the table_entry_pos routine to + * access each entry in order. + * + * NOTE: This routine is thread safe and makes use of an internal + * status qsort function. + * + * RETURNS: + * + * Success - An allocated list of table-linear structures which must + * be freed by table_order_pos_free later. + * + * Failure - NULL + * + * ARGUMENTS: + * + * table_p - Pointer to the table that we are ordering. + * + * compare - Comparison function defined by the user. Its definition + * is at the top of the table.h file. If this is NULL then it will + * order the table my memcmp-ing the keys. + * + * num_entries_p - Pointer to an integer which, if not NULL, will + * contain the number of entries in the returned entry pointer array. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +extern +table_linear_t *table_order_pos(table_t *table_p, table_compare_t compare, + int *num_entries_p, int *error_p); + +/* + * int table_order_pos_free + * + * DESCRIPTION: + * + * Free the pointer returned by the table_order or table_order_pos + * routines. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to the table. + * + * table_entries - Allocated list of entry pointers returned by + * table_order_pos. + * + * entry_n - Number of entries in the array as passed back by + * table_order or table_order_pos in num_entries_p. + */ +extern +int table_order_pos_free(table_t *table_p, table_linear_t *table_entries, + const int entry_n); + +/* + * int table_entry_pos + * + * DESCRIPTION: + * + * Get information about an element. The element is one from the + * array returned by the table_order function. If any of the key/data + * pointers are NULL then they are ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * element. + * + * linear_p - Pointer to a table linear structure from the array + * returned by the table_order function. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of this entry that is allocated in the table. If an + * (int) is stored as this entry (for example) then key_buf_p should + * be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage of this entry that is allocated in the table. + * If a (long) is stored as this entry data (for example) then + * data_buf_p should be (long **) i.e. the address of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table. + */ +extern +int table_entry_pos(table_t *table_p, table_linear_t *linear_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p); + +/* + * const char *table_strerror + * + * DESCRIPTION: + * + * Return the corresponding string for the error number. + * + * RETURNS: + * + * Success - String equivalient of the error. + * + * Failure - String "invalid error code" + * + * ARGUMENTS: + * + * error - Error number that we are converting. + */ +extern +const char *table_strerror(const int error); + +/*<<<<<<<<<< This is end of the auto-generated output from fillproto. */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ! __TABLE_H__ */ diff --git a/libs/libks/src/include/table_loc.h b/libs/libks/src/include/table_loc.h new file mode 100644 index 0000000000..02514c8211 --- /dev/null +++ b/libs/libks/src/include/table_loc.h @@ -0,0 +1,229 @@ +/* + * local defines for the table module + * + * Copyright 2000 by Gray Watson. + * + * This file is part of the table package. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, + * and that the name of Gray Watson not be used in advertising or + * publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be reached via http://256.com/gray/ + * + * $Id: table_loc.h,v 1.11 2000/03/09 03:30:42 gray Exp $ + */ + +#ifndef __TABLE_LOC_H__ +#define __TABLE_LOC_H__ + +#ifndef unix +#define NO_MMAP +#endif + +#ifndef BITSPERBYTE +#define BITSPERBYTE 8 +#endif +#ifndef BITS +#define BITS(type) (BITSPERBYTE * (int)sizeof(type)) +#endif + +#define TABLE_MAGIC 0xBADF00D /* very magic magicness */ +#define LINEAR_MAGIC 0xAD00D00 /* magic value for linear struct */ +#define DEFAULT_SIZE 1024 /* default table size */ +#define MAX_ALIGNMENT 128 /* max alignment value */ + +/* + * Maximum number of splits. This should mean that these routines can + * handle at least 2^128 different values (that's _quite_ a few). And + * then you can always increase the value. + */ +#define MAX_QSORT_SPLITS 128 + +/* + * Maximum number of entries that must be in list for it to be + * partitioned. If there are fewer elements then just do our + * insertion sort. + */ +#define MAX_QSORT_MANY 8 + +/* + * Macros. + */ + +/* returns 1 when we should grow or shrink the table */ +#define SHOULD_TABLE_GROW(tab) ((tab)->ta_entry_n > (tab)->ta_bucket_n * 2) +#define SHOULD_TABLE_SHRINK(tab) ((tab)->ta_entry_n < (tab)->ta_bucket_n / 2) + +/* + * void HASH_MIX + * + * DESCRIPTION: + * + * Mix 3 32-bit values reversibly. For every delta with one or two + * bits set, and the deltas of all three high bits or all three low + * bits, whether the original value of a,b,c is almost all zero or is + * uniformly distributed. + * + * If HASH_MIX() is run forward or backward, at least 32 bits in a,b,c + * have at least 1/4 probability of changing. If mix() is run + * forward, every bit of c will change between 1/3 and 2/3 of the + * time. (Well, 22/100 and 78/100 for some 2-bit deltas.) + * + * HASH_MIX() takes 36 machine instructions, but only 18 cycles on a + * superscalar machine (like a Pentium or a Sparc). No faster mixer + * seems to work, that's the result of my brute-force search. There + * were about 2^68 hashes to choose from. I only tested about a + * billion of those. + */ +#define HASH_MIX(a, b, c) \ + do { \ + a -= b; a -= c; a ^= (c >> 13); \ + b -= c; b -= a; b ^= (a << 8); \ + c -= a; c -= b; c ^= (b >> 13); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 16); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 3); \ + b -= c; b -= a; b ^= (a << 10); \ + c -= a; c -= b; c ^= (b >> 15); \ + } while(0) + +#define SET_POINTER(pnt, val) \ + do { \ + if ((pnt) != NULL) { \ + (*(pnt)) = (val); \ + } \ + } while(0) + +/* + * The following macros take care of the mmap case. When we are + * mmaping a table from a disk file, all of the pointers in the table + * structures are replaced with offsets into the file. The following + * macro, for each pointer, adds the starting address of the mmaped + * section onto each pointer/offset turning it back into a legitimate + * pointer. + */ +#ifdef NO_MMAP + +#define TABLE_POINTER(table, type, pnt) (pnt) + +#else + +#define TABLE_POINTER(tab_p, type, pnt) \ + ((tab_p)->ta_mmap == NULL || (pnt) == NULL ? (pnt) : \ + (type)((char *)((tab_p)->ta_mmap) + (long)(pnt))) + +#endif + +/* + * Macros to get at the key and the data pointers + */ +#define ENTRY_KEY_BUF(entry_p) ((entry_p)->te_key_buf) +#define ENTRY_DATA_BUF(tab_p, entry_p) \ + (ENTRY_KEY_BUF(entry_p) + (entry_p)->te_key_size) + +/* + * Table structures... + */ + +/* + * HACK: this should be equiv as the table_entry_t without the key_buf + * char. We use this with the ENTRY_SIZE() macro above which solves + * the problem with the lack of the [0] GNU hack. We use the + * table_entry_t structure to better map the memory and make things + * faster. + */ +typedef struct table_shell_st { + unsigned int te_key_size; /* size of data */ + unsigned int te_data_size; /* size of data */ + struct table_shell_st *te_next_p; /* pointer to next in the list */ + /* NOTE: this does not have the te_key_buf field here */ +} table_shell_t; + +/* + * Elements in the bucket linked-lists. The key[1] is the start of + * the key with the rest of the key and all of the data information + * packed in memory directly after the end of this structure. + * + * NOTE: if this structure is changed, the table_shell_t must be + * changed to match. + */ +typedef struct table_entry_st { + unsigned int te_key_size; /* size of data */ + unsigned int te_data_size; /* size of data */ + struct table_entry_st *te_next_p; /* pointer to next in the list */ + unsigned char te_key_buf[1]; /* 1st byte of key buf */ +} table_entry_t; + +/* external structure for debuggers be able to see void */ +typedef table_entry_t table_entry_ext_t; + +/* main table structure */ +typedef struct table_st { + unsigned int ta_magic; /* magic number */ + unsigned int ta_flags; /* table's flags defined in table.h */ + unsigned int ta_bucket_n; /* num of buckets, should be 2^X */ + unsigned int ta_entry_n; /* num of entries in all buckets */ + unsigned int ta_data_align; /* data alignment value */ + table_entry_t **ta_buckets; /* array of linked lists */ + table_linear_t ta_linear; /* linear tracking */ + struct table_st *ta_mmap; /* mmaped table */ + unsigned long ta_file_size; /* size of on-disk space */ + + void *ta_mem_pool; /* pointer to some memory pool */ + table_mem_alloc_t ta_alloc_func; /* memory allocation function */ + table_mem_resize_t ta_resize_func; /* memory resize function */ + table_mem_free_t ta_free_func; /* memory free function */ +} table_t; + +/* external table structure for debuggers */ +typedef table_t table_ext_t; + +/* local comparison functions */ +typedef int (*compare_t)(const void *element1_p, const void *element2_p, + table_compare_t user_compare, + const table_t *table_p, int *err_bp); + +/* + * to map error to string + */ +typedef struct { + int es_error; /* error number */ + char *es_string; /* assocaited string */ +} error_str_t; + +static error_str_t errors[] = { + { TABLE_ERROR_NONE, "no error" }, + { TABLE_ERROR_PNT, "invalid table pointer" }, + { TABLE_ERROR_ARG_NULL, "buffer argument is null" }, + { TABLE_ERROR_SIZE, "incorrect size argument" }, + { TABLE_ERROR_OVERWRITE, "key exists and no overwrite" }, + { TABLE_ERROR_NOT_FOUND, "key does not exist" }, + { TABLE_ERROR_ALLOC, "error allocating memory" }, + { TABLE_ERROR_LINEAR, "linear access not in progress" }, + { TABLE_ERROR_OPEN, "could not open file" }, + { TABLE_ERROR_SEEK, "could not seek to position in file" }, + { TABLE_ERROR_READ, "could not read from file" }, + { TABLE_ERROR_WRITE, "could not write to file" }, + { TABLE_ERROR_MMAP_NONE, "no mmap support compiled in library" }, + { TABLE_ERROR_MMAP, "could not mmap the file" }, + { TABLE_ERROR_MMAP_OP, "operation not valid on mmap files" }, + { TABLE_ERROR_EMPTY, "table is empty" }, + { TABLE_ERROR_NOT_EMPTY, "table contains data" }, + { TABLE_ERROR_ALIGNMENT, "invalid alignment value" }, + { TABLE_ERROR_COMPARE, "problems with internal comparison" }, + { TABLE_ERROR_FREE, "memory free error" }, + { 0 } +}; + +#define INVALID_ERROR "invalid error code" + +#endif /* ! __TABLE_LOC_H__ */ diff --git a/libs/libks/src/ks.c b/libs/libks/src/ks.c new file mode 100644 index 0000000000..b328f36798 --- /dev/null +++ b/libs/libks/src/ks.c @@ -0,0 +1,739 @@ +/* + * Copyright (c) 2007-2012, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* Use select on windows and poll everywhere else. + Select is the devil. Especially if you are doing a lot of small socket connections. + If your FD number is bigger than 1024 you will silently create memory corruption. + + If you have build errors on your platform because you don't have poll find a way to detect it and #define KS_USE_SELECT and #undef KS_USE_POLL + All of this will be upgraded to autoheadache eventually. +*/ + +/* TBD for win32 figure out how to tell if you have WSAPoll (vista or higher) and use it when available by #defining KS_USE_WSAPOLL (see below) */ + +#ifdef _MSC_VER +#define FD_SETSIZE 8192 +#define KS_USE_SELECT +#else +#define KS_USE_POLL +#endif + +#include +#ifndef WIN32 +#define closesocket(x) shutdown(x, 2); close(x) +#include +#include +#else +#pragma warning (disable:6386) +/* These warnings need to be ignored warning in sdk header */ +#include +#include +#ifndef errno +#define errno WSAGetLastError() +#endif +#ifndef EINTR +#define EINTR WSAEINTR +#endif +#pragma warning (default:6386) +#endif + +#ifdef KS_USE_POLL +#include +#endif + +#ifndef KS_MIN +#define KS_MIN(x,y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef KS_MAX +#define KS_MAX(x,y) ((x) > (y) ? (x) : (y)) +#endif +#ifndef KS_CLAMP +#define KS_CLAMP(min,max,val) (KS_MIN(max,KS_MAX(val,min))) +#endif + + +/* Written by Marc Espie, public domain */ +#define KS_CTYPE_NUM_CHARS 256 + +const short _ks_C_toupper_[1 + KS_CTYPE_NUM_CHARS] = { + EOF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +const short *_ks_toupper_tab_ = _ks_C_toupper_; + +KS_DECLARE(int) ks_toupper(int c) +{ + if ((unsigned int)c > 255) + return(c); + if (c < -1) + return EOF; + return((_ks_toupper_tab_ + 1)[c]); +} + +const short _ks_C_tolower_[1 + KS_CTYPE_NUM_CHARS] = { + EOF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +const short *_ks_tolower_tab_ = _ks_C_tolower_; + +KS_DECLARE(int) ks_tolower(int c) +{ + if ((unsigned int)c > 255) + return(c); + if (c < -1) + return EOF; + return((_ks_tolower_tab_ + 1)[c]); +} + +KS_DECLARE(const char *)ks_stristr(const char *instr, const char *str) +{ +/* +** Rev History: 16/07/97 Greg Thayer Optimized +** 07/04/95 Bob Stout ANSI-fy +** 02/03/94 Fred Cole Original +** 09/01/03 Bob Stout Bug fix (lines 40-41) per Fred Bulback +** +** Hereby donated to public domain. +*/ + const char *pptr, *sptr, *start; + + if (!str || !instr) + return NULL; + + for (start = str; *start; start++) { + /* find start of pattern in string */ + for (; ((*start) && (ks_toupper(*start) != ks_toupper(*instr))); start++); + + if (!*start) + return NULL; + + pptr = instr; + sptr = start; + + while (ks_toupper(*sptr) == ks_toupper(*pptr)) { + sptr++; + pptr++; + + /* if end of pattern then pattern was found */ + if (!*pptr) + return (start); + + if (!*sptr) + return NULL; + } + } + return NULL; +} + +#ifdef WIN32 +#ifndef vsnprintf +#define vsnprintf _vsnprintf +#endif +#endif + + +int vasprintf(char **ret, const char *format, va_list ap); + +KS_DECLARE(int) ks_vasprintf(char **ret, const char *fmt, va_list ap) +{ +#if !defined(WIN32) && !defined(__sun) + return vasprintf(ret, fmt, ap); +#else + char *buf; + int len; + size_t buflen; + va_list ap2; + char *tmp = NULL; + +#ifdef _MSC_VER +#if _MSC_VER >= 1500 + /* hack for incorrect assumption in msvc header files for code analysis */ + __analysis_assume(tmp); +#endif + ap2 = ap; +#else + va_copy(ap2, ap); +#endif + + len = vsnprintf(tmp, 0, fmt, ap2); + + if (len > 0 && (buf = malloc((buflen = (size_t) (len + 1)))) != NULL) { + len = vsnprintf(buf, buflen, fmt, ap); + *ret = buf; + } else { + *ret = NULL; + len = -1; + } + + va_end(ap2); + return len; +#endif +} + + + + +KS_DECLARE(int) ks_snprintf(char *buffer, size_t count, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vsnprintf(buffer, count-1, fmt, ap); + if (ret < 0) + buffer[count-1] = '\0'; + va_end(ap); + return ret; +} + +static void null_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) +{ + if (file && func && line && level && fmt) { + return; + } + return; +} + + +static const char *LEVEL_NAMES[] = { + "EMERG", + "ALERT", + "CRIT", + "ERROR", + "WARNING", + "NOTICE", + "INFO", + "DEBUG", + NULL +}; + +static int ks_log_level = 7; + +static const char *cut_path(const char *in) +{ + const char *p, *ret = in; + char delims[] = "/\\"; + char *i; + + for (i = delims; *i; i++) { + p = in; + while ((p = strchr(p, *i)) != 0) { + ret = ++p; + } + } + return ret; +} + + +static void default_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) +{ + const char *fp; + char *data; + va_list ap; + int ret; + + if (level < 0 || level > 7) { + level = 7; + } + if (level > ks_log_level) { + return; + } + + fp = cut_path(file); + + va_start(ap, fmt); + + ret = ks_vasprintf(&data, fmt, ap); + + if (ret != -1) { + fprintf(stderr, "[%s] %s:%d %s() %s", LEVEL_NAMES[level], fp, line, func, data); + free(data); + } + + va_end(ap); + +} + +ks_logger_t ks_log = null_logger; + +KS_DECLARE(void) ks_global_set_logger(ks_logger_t logger) +{ + if (logger) { + ks_log = logger; + } else { + ks_log = null_logger; + } +} + +KS_DECLARE(void) ks_global_set_default_logger(int level) +{ + if (level < 0 || level > 7) { + level = 7; + } + + ks_log = default_logger; + ks_log_level = level; +} + +KS_DECLARE(size_t) ks_url_encode(const char *url, char *buf, size_t len) +{ + const char *p; + size_t x = 0; + const char urlunsafe[] = "\r\n \"#%&+:;<=>?@[\\]^`{|}"; + const char hex[] = "0123456789ABCDEF"; + + if (!buf) { + return 0; + } + + if (!url) { + return 0; + } + + len--; + + for (p = url; *p; p++) { + if (x >= len) { + break; + } + if (*p < ' ' || *p > '~' || strchr(urlunsafe, *p)) { + if ((x + 3) >= len) { + break; + } + buf[x++] = '%'; + buf[x++] = hex[*p >> 4]; + buf[x++] = hex[*p & 0x0f]; + } else { + buf[x++] = *p; + } + } + buf[x] = '\0'; + + return x; +} + +KS_DECLARE(char *)ks_url_decode(char *s) +{ + char *o; + unsigned int tmp; + + for (o = s; *s; s++, o++) { + if (*s == '%' && strlen(s) > 2 && sscanf(s + 1, "%2x", &tmp) == 1) { + *o = (char) tmp; + s += 2; + } else { + *o = *s; + } + } + *o = '\0'; + return s; +} + + +static int ks_socket_reuseaddr(ks_socket_t socket) +{ +#ifdef WIN32 + BOOL reuse_addr = TRUE; + return setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse_addr, sizeof(reuse_addr)); +#else + int reuse_addr = 1; + return setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); +#endif +} + + +struct thread_handler { + ks_listen_callback_t callback; + ks_socket_t server_sock; + ks_socket_t client_sock; + struct sockaddr_in addr; +}; + +static void *client_thread(ks_thread_t *me, void *obj) +{ + struct thread_handler *handler = (struct thread_handler *) obj; + + handler->callback(handler->server_sock, handler->client_sock, &handler->addr); + free(handler); + + return NULL; + +} + +KS_DECLARE(ks_status_t) ks_listen(const char *host, ks_port_t port, ks_listen_callback_t callback) +{ + ks_socket_t server_sock = KS_SOCK_INVALID; + struct sockaddr_in addr; + ks_status_t status = KS_SUCCESS; + + if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + return KS_FAIL; + } + + ks_socket_reuseaddr(server_sock); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + + if (bind(server_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + status = KS_FAIL; + goto end; + } + + if (listen(server_sock, 10000) < 0) { + status = KS_FAIL; + goto end; + } + + for (;;) { + int client_sock; + struct sockaddr_in echoClntAddr; +#ifdef WIN32 + int clntLen; +#else + unsigned int clntLen; +#endif + + clntLen = sizeof(echoClntAddr); + + if ((client_sock = accept(server_sock, (struct sockaddr *) &echoClntAddr, &clntLen)) == KS_SOCK_INVALID) { + status = KS_FAIL; + goto end; + } + + callback(server_sock, client_sock, &echoClntAddr); + } + + end: + + if (server_sock != KS_SOCK_INVALID) { + closesocket(server_sock); + server_sock = KS_SOCK_INVALID; + } + + return status; + +} + +KS_DECLARE(ks_status_t) ks_listen_threaded(const char *host, ks_port_t port, ks_listen_callback_t callback, int max) +{ + ks_socket_t server_sock = KS_SOCK_INVALID; + struct sockaddr_in addr; + ks_status_t status = KS_SUCCESS; + struct thread_handler *handler = NULL; + + if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + return KS_FAIL; + } + + ks_socket_reuseaddr(server_sock); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + + if (bind(server_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + status = KS_FAIL; + goto end; + } + + if (listen(server_sock, max) < 0) { + status = KS_FAIL; + goto end; + } + + for (;;) { + int client_sock; + struct sockaddr_in echoClntAddr; +#ifdef WIN32 + int clntLen; +#else + unsigned int clntLen; +#endif + + clntLen = sizeof(echoClntAddr); + + if ((client_sock = accept(server_sock, (struct sockaddr *) &echoClntAddr, &clntLen)) == KS_SOCK_INVALID) { + status = KS_FAIL; + goto end; + } + + handler = malloc(sizeof(*handler)); + ks_assert(handler); + + memset(handler, 0, sizeof(*handler)); + handler->callback = callback; + handler->server_sock = server_sock; + handler->client_sock = client_sock; + handler->addr = echoClntAddr; + + ks_thread_create_detached(client_thread, handler); + } + + end: + + if (server_sock != KS_SOCK_INVALID) { + closesocket(server_sock); + server_sock = KS_SOCK_INVALID; + } + + return status; + +} + + +/* USE WSAPoll on vista or higher */ +#ifdef KS_USE_WSAPOLL +KS_DECLARE(int) ks_wait_sock(ks_socket_t sock, uint32_t ms, ks_poll_t flags) +{ +} +#endif + + +#ifdef KS_USE_SELECT +#ifdef WIN32 +#pragma warning( push ) +#pragma warning( disable : 6262 ) /* warning C6262: Function uses '98348' bytes of stack: exceeds /analyze:stacksize'16384'. Consider moving some data to heap */ +#endif +KS_DECLARE(int) ks_wait_sock(ks_socket_t sock, uint32_t ms, ks_poll_t flags) +{ + int s = 0, r = 0; + fd_set rfds; + fd_set wfds; + fd_set efds; + struct timeval tv; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + +#ifndef WIN32 + /* Wouldn't you rather know?? */ + assert(sock <= FD_SETSIZE); +#endif + + if ((flags & KS_POLL_READ)) { + +#ifdef WIN32 +#pragma warning( push ) +#pragma warning( disable : 4127 ) + FD_SET(sock, &rfds); +#pragma warning( pop ) +#else + FD_SET(sock, &rfds); +#endif + } + + if ((flags & KS_POLL_WRITE)) { + +#ifdef WIN32 +#pragma warning( push ) +#pragma warning( disable : 4127 ) + FD_SET(sock, &wfds); +#pragma warning( pop ) +#else + FD_SET(sock, &wfds); +#endif + } + + if ((flags & KS_POLL_ERROR)) { + +#ifdef WIN32 +#pragma warning( push ) +#pragma warning( disable : 4127 ) + FD_SET(sock, &efds); +#pragma warning( pop ) +#else + FD_SET(sock, &efds); +#endif + } + + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * ms; + + s = select(sock + 1, (flags & KS_POLL_READ) ? &rfds : NULL, (flags & KS_POLL_WRITE) ? &wfds : NULL, (flags & KS_POLL_ERROR) ? &efds : NULL, &tv); + + if (s < 0) { + r = s; + } else if (s > 0) { + if ((flags & KS_POLL_READ) && FD_ISSET(sock, &rfds)) { + r |= KS_POLL_READ; + } + + if ((flags & KS_POLL_WRITE) && FD_ISSET(sock, &wfds)) { + r |= KS_POLL_WRITE; + } + + if ((flags & KS_POLL_ERROR) && FD_ISSET(sock, &efds)) { + r |= KS_POLL_ERROR; + } + } + + return r; + +} +#ifdef WIN32 +#pragma warning( pop ) +#endif +#endif + +#ifdef KS_USE_POLL +KS_DECLARE(int) ks_wait_sock(ks_socket_t sock, uint32_t ms, ks_poll_t flags) +{ + struct pollfd pfds[2] = { { 0 } }; + int s = 0, r = 0; + + pfds[0].fd = sock; + + if ((flags & KS_POLL_READ)) { + pfds[0].events |= POLLIN; + } + + if ((flags & KS_POLL_WRITE)) { + pfds[0].events |= POLLOUT; + } + + if ((flags & KS_POLL_ERROR)) { + pfds[0].events |= POLLERR; + } + + s = poll(pfds, 1, ms); + + if (s < 0) { + r = s; + } else if (s > 0) { + if ((pfds[0].revents & POLLIN)) { + r |= KS_POLL_READ; + } + if ((pfds[0].revents & POLLOUT)) { + r |= KS_POLL_WRITE; + } + if ((pfds[0].revents & POLLERR)) { + r |= KS_POLL_ERROR; + } + } + + return r; + +} +#endif + + +KS_DECLARE(unsigned int) ks_separate_string_string(char *buf, const char *delim, char **array, unsigned int arraylen) +{ + unsigned int count = 0; + char *d; + size_t dlen = strlen(delim); + + array[count++] = buf; + + while (count < arraylen && array[count - 1]) { + if ((d = strstr(array[count - 1], delim))) { + *d = '\0'; + d += dlen; + array[count++] = d; + } else + break; + } + + return count; +} + diff --git a/libs/libks/src/ks_buffer.c b/libs/libks/src/ks_buffer.c new file mode 100644 index 0000000000..469296a3a9 --- /dev/null +++ b/libs/libks/src/ks_buffer.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2010-2012, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "ks_buffer.h" + +static unsigned buffer_id = 0; + +struct ks_buffer { + unsigned char *data; + unsigned char *head; + ks_size_t used; + ks_size_t actually_used; + ks_size_t datalen; + ks_size_t max_len; + ks_size_t blocksize; + unsigned id; + int loops; +}; + + +KS_DECLARE(ks_status_t) ks_buffer_create(ks_buffer_t **buffer, ks_size_t blocksize, ks_size_t start_len, ks_size_t max_len) +{ + ks_buffer_t *new_buffer; + + new_buffer = malloc(sizeof(*new_buffer)); + if (new_buffer) { + memset(new_buffer, 0, sizeof(*new_buffer)); + + if (start_len) { + new_buffer->data = malloc(start_len); + if (!new_buffer->data) { + free(new_buffer); + return KS_FAIL; + } + memset(new_buffer->data, 0, start_len); + } + + new_buffer->max_len = max_len; + new_buffer->datalen = start_len; + new_buffer->id = buffer_id++; + new_buffer->blocksize = blocksize; + new_buffer->head = new_buffer->data; + + *buffer = new_buffer; + return KS_SUCCESS; + } + + return KS_FAIL; +} + +KS_DECLARE(ks_size_t) ks_buffer_len(ks_buffer_t *buffer) +{ + + ks_assert(buffer != NULL); + + return buffer->datalen; + +} + + +KS_DECLARE(ks_size_t) ks_buffer_freespace(ks_buffer_t *buffer) +{ + ks_assert(buffer != NULL); + + if (buffer->max_len) { + return (ks_size_t) (buffer->max_len - buffer->used); + } + return 1000000; + +} + +KS_DECLARE(ks_size_t) ks_buffer_inuse(ks_buffer_t *buffer) +{ + ks_assert(buffer != NULL); + + return buffer->used; +} + +KS_DECLARE(ks_size_t) ks_buffer_seek(ks_buffer_t *buffer, ks_size_t datalen) +{ + ks_size_t reading = 0; + + ks_assert(buffer != NULL); + + if (buffer->used < 1) { + buffer->used = 0; + return 0; + } else if (buffer->used >= datalen) { + reading = datalen; + } else { + reading = buffer->used; + } + + buffer->used = buffer->actually_used - reading; + buffer->head = buffer->data + reading; + + return reading; +} + +KS_DECLARE(ks_size_t) ks_buffer_toss(ks_buffer_t *buffer, ks_size_t datalen) +{ + ks_size_t reading = 0; + + ks_assert(buffer != NULL); + + if (buffer->used < 1) { + buffer->used = 0; + return 0; + } else if (buffer->used >= datalen) { + reading = datalen; + } else { + reading = buffer->used; + } + + buffer->used -= reading; + buffer->head += reading; + + return buffer->used; +} + +KS_DECLARE(void) ks_buffer_set_loops(ks_buffer_t *buffer, int loops) +{ + buffer->loops = loops; +} + +KS_DECLARE(ks_size_t) ks_buffer_read_loop(ks_buffer_t *buffer, void *data, ks_size_t datalen) +{ + ks_size_t len; + if ((len = ks_buffer_read(buffer, data, datalen)) < datalen) { + if (buffer->loops == 0) { + return len; + } + buffer->head = buffer->data; + buffer->used = buffer->actually_used; + len = ks_buffer_read(buffer, (char*)data + len, datalen - len); + buffer->loops--; + } + return len; +} + +KS_DECLARE(ks_size_t) ks_buffer_read(ks_buffer_t *buffer, void *data, ks_size_t datalen) +{ + ks_size_t reading = 0; + + ks_assert(buffer != NULL); + ks_assert(data != NULL); + + + if (buffer->used < 1) { + buffer->used = 0; + return 0; + } else if (buffer->used >= datalen) { + reading = datalen; + } else { + reading = buffer->used; + } + + memcpy(data, buffer->head, reading); + buffer->used -= reading; + buffer->head += reading; + + /* if (buffer->id == 4) printf("%u o %d = %d\n", buffer->id, (unsigned)reading, (unsigned)buffer->used); */ + return reading; +} + + +KS_DECLARE(ks_size_t) ks_buffer_packet_count(ks_buffer_t *buffer) +{ + char *pe, *p, *e, *head = (char *) buffer->head; + ks_size_t x = 0; + + ks_assert(buffer != NULL); + + e = (head + buffer->used); + + for (p = head; p && *p && p < e; p++) { + if (*p == '\n') { + pe = p+1; + if (*pe == '\r') pe++; + if (pe <= e && *pe == '\n') { + p = pe++; + x++; + } + } + } + + return x; +} + +KS_DECLARE(ks_size_t) ks_buffer_read_packet(ks_buffer_t *buffer, void *data, ks_size_t maxlen) +{ + char *pe, *p, *e, *head = (char *) buffer->head; + ks_size_t datalen = 0; + + ks_assert(buffer != NULL); + ks_assert(data != NULL); + + e = (head + buffer->used); + + for (p = head; p && *p && p < e; p++) { + if (*p == '\n') { + pe = p+1; + if (*pe == '\r') pe++; + if (pe <= e && *pe == '\n') { + pe++; + datalen = pe - head; + if (datalen > maxlen) { + datalen = maxlen; + } + break; + } + } + } + + return ks_buffer_read(buffer, data, datalen); +} + +KS_DECLARE(ks_size_t) ks_buffer_write(ks_buffer_t *buffer, const void *data, ks_size_t datalen) +{ + ks_size_t freespace, actual_freespace; + + ks_assert(buffer != NULL); + ks_assert(data != NULL); + ks_assert(buffer->data != NULL); + + if (!datalen) { + return buffer->used; + } + + actual_freespace = buffer->datalen - buffer->actually_used; + if (actual_freespace < datalen && (!buffer->max_len || (buffer->used + datalen <= buffer->max_len))) { + memmove(buffer->data, buffer->head, buffer->used); + buffer->head = buffer->data; + buffer->actually_used = buffer->used; + } + + freespace = buffer->datalen - buffer->used; + + /* + if (buffer->data != buffer->head) { + memmove(buffer->data, buffer->head, buffer->used); + buffer->head = buffer->data; + } + */ + + if (freespace < datalen) { + ks_size_t new_size, new_block_size; + void *data1; + + new_size = buffer->datalen + datalen; + new_block_size = buffer->datalen + buffer->blocksize; + + if (new_block_size > new_size) { + new_size = new_block_size; + } + buffer->head = buffer->data; + data1 = realloc(buffer->data, new_size); + if (!data1) { + return 0; + } + buffer->data = data1; + buffer->head = buffer->data; + buffer->datalen = new_size; + } + + + freespace = buffer->datalen - buffer->used; + + if (freespace < datalen) { + return 0; + } else { + memcpy(buffer->head + buffer->used, data, datalen); + buffer->used += datalen; + buffer->actually_used += datalen; + } + /* if (buffer->id == 4) printf("%u i %d = %d\n", buffer->id, (unsigned)datalen, (unsigned)buffer->used); */ + + return buffer->used; +} + +KS_DECLARE(void) ks_buffer_zero(ks_buffer_t *buffer) +{ + ks_assert(buffer != NULL); + ks_assert(buffer->data != NULL); + + buffer->used = 0; + buffer->actually_used = 0; + buffer->head = buffer->data; +} + +KS_DECLARE(ks_size_t) ks_buffer_zwrite(ks_buffer_t *buffer, const void *data, ks_size_t datalen) +{ + ks_size_t w; + + if (!(w = ks_buffer_write(buffer, data, datalen))) { + ks_buffer_zero(buffer); + return ks_buffer_write(buffer, data, datalen); + } + + return w; +} + +KS_DECLARE(void) ks_buffer_destroy(ks_buffer_t **buffer) +{ + if (*buffer) { + free((*buffer)->data); + free(*buffer); + } + + *buffer = NULL; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/libks/src/ks_config.c b/libs/libks/src/ks_config.c new file mode 100644 index 0000000000..efe410ce2c --- /dev/null +++ b/libs/libks/src/ks_config.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2007-2012, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ks.h" +#include "ks_config.h" + +KS_DECLARE(int) ks_config_open_file(ks_config_t *cfg, const char *file_path) +{ + FILE *f; + const char *path = NULL; + char path_buf[1024]; + + if (file_path[0] == '/') { + path = file_path; + } else { + ks_snprintf(path_buf, sizeof(path_buf), "%s%s%s", KS_CONFIG_DIR, KS_PATH_SEPARATOR, file_path); + path = path_buf; + } + + if (!path) { + return 0; + } + + memset(cfg, 0, sizeof(*cfg)); + cfg->lockto = -1; + ks_log(KS_LOG_DEBUG, "Configuration file is %s.\n", path); + f = fopen(path, "r"); + + if (!f) { + if (file_path[0] != '/') { + int last = -1; + char *var, *val; + + ks_snprintf(path_buf, sizeof(path_buf), "%s%sopenks.conf", KS_CONFIG_DIR, KS_PATH_SEPARATOR); + path = path_buf; + + if ((f = fopen(path, "r")) == 0) { + return 0; + } + + cfg->file = f; + ks_set_string(cfg->path, path); + + while (ks_config_next_pair(cfg, &var, &val)) { + if ((cfg->sectno != last) && !strcmp(cfg->section, file_path)) { + cfg->lockto = cfg->sectno; + return 1; + } + } + + ks_config_close_file(cfg); + memset(cfg, 0, sizeof(*cfg)); + return 0; + } + + return 0; + } else { + cfg->file = f; + ks_set_string(cfg->path, path); + return 1; + } +} + +KS_DECLARE(void) ks_config_close_file(ks_config_t *cfg) +{ + + if (cfg->file) { + fclose(cfg->file); + } + + memset(cfg, 0, sizeof(*cfg)); +} + + + +KS_DECLARE(int) ks_config_next_pair(ks_config_t *cfg, char **var, char **val) +{ + int ret = 0; + char *p, *end; + + *var = *val = NULL; + + if (!cfg || !cfg->file) { + return 0; + } + + for (;;) { + cfg->lineno++; + + if (!fgets(cfg->buf, sizeof(cfg->buf), cfg->file)) { + ret = 0; + break; + } + *var = cfg->buf; + + if (**var == '[' && (end = strchr(*var, ']')) != 0) { + *end = '\0'; + (*var)++; + if (**var == '+') { + (*var)++; + ks_copy_string(cfg->section, *var, sizeof(cfg->section)); + cfg->sectno++; + + if (cfg->lockto > -1 && cfg->sectno != cfg->lockto) { + break; + } + cfg->catno = 0; + cfg->lineno = 0; + *var = (char *) ""; + *val = (char *) ""; + return 1; + } else { + ks_copy_string(cfg->category, *var, sizeof(cfg->category)); + cfg->catno++; + } + continue; + } + + + + if (**var == '#' || **var == ';' || **var == '\n' || **var == '\r') { + continue; + } + + if (!strncmp(*var, "__END__", 7)) { + break; + } + + + if ((end = strchr(*var, ';')) && *(end+1) == *end) { + *end = '\0'; + end--; + } else if ((end = strchr(*var, '\n')) != 0) { + if (*(end - 1) == '\r') { + end--; + } + *end = '\0'; + } + + p = *var; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *var = p; + + + if ((*val = strchr(*var, '=')) == 0) { + ret = -1; + /* log_printf(0, server.log, "Invalid syntax on %s: line %d\n", cfg->path, cfg->lineno); */ + continue; + } else { + p = *val - 1; + *(*val) = '\0'; + (*val)++; + if (*(*val) == '>') { + *(*val) = '\0'; + (*val)++; + } + + while ((*p == ' ' || *p == '\t') && p != *var) { + *p = '\0'; + p--; + } + + p = *val; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *val = p; + ret = 1; + break; + } + } + + + return ret; + +} + +KS_DECLARE(int) ks_config_get_cas_bits(char *strvalue, unsigned char *outbits) +{ + char cas_bits[5]; + unsigned char bit = 0x8; + char *double_colon = strchr(strvalue, ':'); + int x = 0; + + if (!double_colon) { + ks_log(KS_LOG_ERROR, "No CAS bits specified: %s, :xxxx definition expected, where x is 1 or 0\n", double_colon); + return -1; + } + + double_colon++; + *outbits = 0; + cas_bits[4] = 0; + + if (sscanf(double_colon, "%c%c%c%c", &cas_bits[0], &cas_bits[1], &cas_bits[2], &cas_bits[3]) != 4) { + ks_log(KS_LOG_ERROR, "Invalid CAS bits specified: %s, :xxxx definition expected, where x is 1 or 0\n", double_colon); + return -1; + } + + ks_log(KS_LOG_DEBUG, "CAS bits specification found: %s\n", cas_bits); + + for (; cas_bits[x]; x++) { + if ('1' == cas_bits[x]) { + *outbits |= bit; + } else if ('0' != cas_bits[x]) { + ks_log(KS_LOG_ERROR, "Invalid CAS pattern specified: %s, just 0 or 1 allowed for each bit\n"); + return -1; + } + bit >>= 1; + } + return 0; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/libks/src/ks_json.c b/libs/libks/src/ks_json.c new file mode 100644 index 0000000000..ffa965a609 --- /dev/null +++ b/libs/libks/src/ks_json.c @@ -0,0 +1,529 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "ks_json.h" +#include "ks.h" + +static const char *ep; + +KS_DECLARE(const char *)cJSON_GetErrorPtr() {return ep;} + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +static void *glue_malloc(size_t theSize) +{ + return(malloc(theSize)); +} + +static void glue_free(void *thePtr) +{ + free(thePtr); +} + +static void *(*cJSON_malloc)(size_t sz) = glue_malloc; +static void (*cJSON_free)(void *ptr) = glue_free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + const char *s = str ? str : ""; + + len = strlen(s) + 1; + if (!(copy = (char*)cJSON_malloc(len))) return 0; + memcpy(copy,s,len); + return copy; +} + +KS_DECLARE(void)cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item() +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +KS_DECLARE(void)cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + /* Could use sscanf for this? */ + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ + if (*num=='e' || *num=='E') /* Exponent? */ + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item) +{ + char *str; + double d=item->valuedouble; + if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ + if (str) sprintf(str,"%d",item->valueint); + } + else + { + str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + } + return str; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; + if (*str!='\"') {ep=str;return 0;} /* not a string! */ + + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && *ptr) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': /* transcode utf16 to utf8. */ + if (sscanf(ptr+1,"%4x",&uc) < 1) break; + + ptr+=4; /* get the unicode char. */ + + if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid. + + if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs. + { + if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate. + if (sscanf(ptr+3,"%4x",&uc2) < 1) break; + ptr+=6; + if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate. + uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF); + } + + len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; + + switch (len) { + case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(char)(uc | firstByteMark[len]); + } + ptr2+=len; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str) +{ + const char *ptr;char *ptr2,*out;int len=0;unsigned char token; + + if (!str) return cJSON_strdup(""); + ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} + + out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (token=*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} + +/* Parse an object - create a new root, and populate. */ +KS_DECLARE(cJSON *)cJSON_Parse(const char *value) +{ + cJSON *c=cJSON_New_Item(); + ep=0; + if (!c) return 0; /* memory fail */ + + if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;} + return c; +} + +/* Render a cJSON item/entity/structure to text. */ +KS_DECLARE(char *) cJSON_Print(cJSON *item) {return print_value(item,0,1);} +KS_DECLARE(char *) cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + ep=value;return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item,int depth,int fmt) +{ + char *out=0; + if (!item) return 0; + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item);break; + case cJSON_String: out=print_string(item);break; + case cJSON_Array: out=print_array(item,depth,fmt);break; + case cJSON_Object: out=print_object(item,depth,fmt);break; + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') {ep=value;return 0;} /* not an array! */ + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; /* memory fail */ + value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; /* memory fail */ + } + + if (*value==']') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item,int depth,int fmt) +{ + char **entries; + char *out=0,*ptr,*ret;int len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + + /* How many entries in the array? */ + while (child) numentries++,child=child->next; + /* Allocate an array to hold the values for each */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + /* Retrieve all the results: */ + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt); + entries[i++]=ret; + if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) fail=1; + + /* Handle failure. */ + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value=='}') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item,int depth,int fmt) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;int len=7,i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + /* Count the number of entries. */ + while (child) numentries++,child=child->next; + /* Allocate space for the names and the objects */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + names=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!names) {cJSON_free(entries);return 0;} + memset(entries,0,sizeof(char*)*numentries); + memset(names,0,sizeof(char*)*numentries); + + /* Collect all the results into our arrays: */ + child=item->child;depth++;if (fmt) len+=depth; + while (child) + { + names[i]=str=print_string_ptr(child->string); + entries[i++]=ret=print_value(child,depth,fmt); + if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + /* Try to allocate the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + /* Handle failure */ + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +KS_DECLARE(cJSON *)cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +KS_DECLARE(cJSON *)cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +/* Add item to array/object. */ +KS_DECLARE(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +KS_DECLARE(void) cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +KS_DECLARE(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +KS_DECLARE(void) cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +KS_DECLARE(cJSON *)cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +KS_DECLARE(void) cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +KS_DECLARE(cJSON *)cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +KS_DECLARE(void) cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +/* Replace array/object items with new ones. */ +KS_DECLARE(void) cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +KS_DECLARE(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +/* Create basic types: */ +KS_DECLARE(cJSON *)cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} +KS_DECLARE(cJSON *)cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} +KS_DECLARE(cJSON *)cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} +KS_DECLARE(cJSON *)cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} +KS_DECLARE(cJSON *)cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +KS_DECLARE(cJSON *)cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} +KS_DECLARE(cJSON *)cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} +KS_DECLARE(cJSON *)cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} + +/* Create Arrays: */ +KS_DECLARE(cJSON *)cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a!=0 && ichild=n;else suffix_object(p,n);p=n;}return a;} +KS_DECLARE(cJSON *)cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a!=0 && ichild=n;else suffix_object(p,n);p=n;}return a;} +KS_DECLARE(cJSON *)cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a!=0 && ichild=n;else suffix_object(p,n);p=n;}return a;} +KS_DECLARE(cJSON *)cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a!=0 && ichild=n;else suffix_object(p,n);p=n;}return a;} diff --git a/libs/libks/src/ks_threadmutex.c b/libs/libks/src/ks_threadmutex.c new file mode 100644 index 0000000000..a23c02f762 --- /dev/null +++ b/libs/libks/src/ks_threadmutex.c @@ -0,0 +1,239 @@ +/* + * Cross Platform Thread/Mutex abstraction + * Copyright(C) 2007 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + */ + +#ifdef WIN32 +/* required for TryEnterCriticalSection definition. Must be defined before windows.h include */ +#define _WIN32_WINNT 0x0400 +#endif + +#include "ks.h" +#include "ks_threadmutex.h" + +#ifdef WIN32 +#include + +#define KS_THREAD_CALLING_CONVENTION __stdcall + +struct ks_mutex { + CRITICAL_SECTION mutex; +}; + +#else + +#include + +#define KS_THREAD_CALLING_CONVENTION + +struct ks_mutex { + pthread_mutex_t mutex; +}; + +#endif + +struct ks_thread { +#ifdef WIN32 + void *handle; +#else + pthread_t handle; +#endif + void *private_data; + ks_thread_function_t function; + size_t stack_size; +#ifndef WIN32 + pthread_attr_t attribute; +#endif +}; + +size_t thread_default_stacksize = 240 * 1024; + +void ks_thread_override_default_stacksize(size_t size) +{ + thread_default_stacksize = size; +} + +static void * KS_THREAD_CALLING_CONVENTION thread_launch(void *args) +{ + void *exit_val; + ks_thread_t *thread = (ks_thread_t *)args; + exit_val = thread->function(thread, thread->private_data); +#ifndef WIN32 + pthread_attr_destroy(&thread->attribute); +#endif + free(thread); + + return exit_val; +} + +KS_DECLARE(ks_status_t) ks_thread_create_detached(ks_thread_function_t func, void *data) +{ + return ks_thread_create_detached_ex(func, data, thread_default_stacksize); +} + +ks_status_t ks_thread_create_detached_ex(ks_thread_function_t func, void *data, size_t stack_size) +{ + ks_thread_t *thread = NULL; + ks_status_t status = KS_FAIL; + + if (!func || !(thread = (ks_thread_t *)malloc(sizeof(ks_thread_t)))) { + goto done; + } + + thread->private_data = data; + thread->function = func; + thread->stack_size = stack_size; + +#if defined(WIN32) + thread->handle = (void *)_beginthreadex(NULL, (unsigned)thread->stack_size, (unsigned int (__stdcall *)(void *))thread_launch, thread, 0, NULL); + if (!thread->handle) { + goto fail; + } + CloseHandle(thread->handle); + + status = KS_SUCCESS; + goto done; +#else + + if (pthread_attr_init(&thread->attribute) != 0) goto fail; + + if (pthread_attr_setdetachstate(&thread->attribute, PTHREAD_CREATE_DETACHED) != 0) goto failpthread; + + if (thread->stack_size && pthread_attr_setstacksize(&thread->attribute, thread->stack_size) != 0) goto failpthread; + + if (pthread_create(&thread->handle, &thread->attribute, thread_launch, thread) != 0) goto failpthread; + + status = KS_SUCCESS; + goto done; + + failpthread: + + pthread_attr_destroy(&thread->attribute); +#endif + + fail: + if (thread) { + free(thread); + } + done: + return status; +} + + +KS_DECLARE(ks_status_t) ks_mutex_create(ks_mutex_t **mutex) +{ + ks_status_t status = KS_FAIL; +#ifndef WIN32 + pthread_mutexattr_t attr; +#endif + ks_mutex_t *check = NULL; + + check = (ks_mutex_t *)malloc(sizeof(**mutex)); + if (!check) + goto done; +#ifdef WIN32 + InitializeCriticalSection(&check->mutex); +#else + if (pthread_mutexattr_init(&attr)) + goto done; + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) + goto fail; + + if (pthread_mutex_init(&check->mutex, &attr)) + goto fail; + + goto success; + + fail: + pthread_mutexattr_destroy(&attr); + goto done; + + success: +#endif + *mutex = check; + status = KS_SUCCESS; + + done: + return status; +} + +KS_DECLARE(ks_status_t) ks_mutex_destroy(ks_mutex_t **mutex) +{ + ks_mutex_t *mp = *mutex; + *mutex = NULL; + if (!mp) { + return KS_FAIL; + } +#ifdef WIN32 + DeleteCriticalSection(&mp->mutex); +#else + if (pthread_mutex_destroy(&mp->mutex)) + return KS_FAIL; +#endif + free(mp); + return KS_SUCCESS; +} + +KS_DECLARE(ks_status_t) ks_mutex_lock(ks_mutex_t *mutex) +{ +#ifdef WIN32 + EnterCriticalSection(&mutex->mutex); +#else + if (pthread_mutex_lock(&mutex->mutex)) + return KS_FAIL; +#endif + return KS_SUCCESS; +} + +KS_DECLARE(ks_status_t) ks_mutex_trylock(ks_mutex_t *mutex) +{ +#ifdef WIN32 + if (!TryEnterCriticalSection(&mutex->mutex)) + return KS_FAIL; +#else + if (pthread_mutex_trylock(&mutex->mutex)) + return KS_FAIL; +#endif + return KS_SUCCESS; +} + +KS_DECLARE(ks_status_t) ks_mutex_unlock(ks_mutex_t *mutex) +{ +#ifdef WIN32 + LeaveCriticalSection(&mutex->mutex); +#else + if (pthread_mutex_unlock(&mutex->mutex)) + return KS_FAIL; +#endif + return KS_SUCCESS; +} + + + + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/libks/src/mpool.c b/libs/libks/src/mpool.c new file mode 100644 index 0000000000..72c5b6b108 --- /dev/null +++ b/libs/libks/src/mpool.c @@ -0,0 +1,1768 @@ +/* + * Memory pool routines. + * + * Copyright 1996 by Gray Watson. + * + * This file is part of the mpool package. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies, and that the name of Gray Watson not be used in advertising + * or publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be reached via http://256.com/gray/ + * + * $Id: mpool.c,v 1.5 2006/05/31 20:28:31 gray Exp $ + */ + +/* + * Memory-pool allocation routines. I got sick of the GNU mmalloc + * library which was close to what we needed but did not exactly do + * what I wanted. + * + * The following uses mmap from /dev/zero. It allows a number of + * allocations to be made inside of a memory pool then with a clear or + * close the pool can be reset without any memory fragmentation and + * growth problems. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define MPOOL_MAIN + +#include "mpool.h" +#include "mpool_loc.h" + +#ifdef __GNUC__ +#ident "$Id: mpool.c,v 1.5 2006/05/31 20:28:31 gray Exp $" +#else +static char *rcs_id = "$Id: mpool.c,v 1.5 2006/05/31 20:28:31 gray Exp $"; +#endif + +/* version */ +static char *version = "mpool library version 2.1.0"; + +/* local variables */ +static int enabled_b = 0; /* lib initialized? */ +static unsigned int min_bit_free_next = 0; /* min size of next pnt */ +static unsigned int min_bit_free_size = 0; /* min size of next + size */ +static unsigned long bit_array[MAX_BITS + 1]; /* size -> bit */ + +/****************************** local utilities ******************************/ + +/* + * static void startup + * + * DESCRIPTION: + * + * Perform any library level initialization. + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * None. + */ +static void startup(void) +{ + int bit_c; + unsigned long size = 1; + + if (enabled_b) { + return; + } + + /* allocate our free bit array list */ + for (bit_c = 0; bit_c <= MAX_BITS; bit_c++) { + bit_array[bit_c] = size; + + /* + * Note our minimum number of bits that can store a pointer. This + * is smallest address that we can have a linked list for. + */ + if (min_bit_free_next == 0 && size >= sizeof(void *)) { + min_bit_free_next = bit_c; + } + /* + * Note our minimum number of bits that can store a pointer and + * the size of the block. + */ + if (min_bit_free_size == 0 && size >= sizeof(mpool_free_t)) { + min_bit_free_size = bit_c; + } + + size *= 2; + } + + enabled_b = 1; +} + +/* + * static int size_to_bits + * + * DESCRIPTION: + * + * Calculate the number of bits in a size. + * + * RETURNS: + * + * Number of bits. + * + * ARGUMENTS: + * + * size -> Size of memory of which to calculate the number of bits. + */ +static int size_to_bits(const unsigned long size) +{ + int bit_c = 0; + + for (bit_c = 0; bit_c <= MAX_BITS; bit_c++) { + if (size <= bit_array[bit_c]) { + break; + } + } + + return bit_c; +} + +/* + * static int size_to_free_bits + * + * DESCRIPTION: + * + * Calculate the number of bits in a size going on the free list. + * + * RETURNS: + * + * Number of bits. + * + * ARGUMENTS: + * + * size -> Size of memory of which to calculate the number of bits. + */ +static int size_to_free_bits(const unsigned long size) +{ + int bit_c = 0; + + if (size == 0) { + return 0; + } + + for (bit_c = 0; bit_c <= MAX_BITS; bit_c++) { + if (size < bit_array[bit_c]) { + break; + } + } + + return bit_c - 1; +} + +/* + * static int bits_to_size + * + * DESCRIPTION: + * + * Calculate the size represented by a number of bits. + * + * RETURNS: + * + * Number of bits. + * + * ARGUMENTS: + * + * bit_n -> Number of bits + */ +static unsigned long bits_to_size(const int bit_n) +{ + if (bit_n > MAX_BITS) { + return bit_array[MAX_BITS]; + } + else { + return bit_array[bit_n]; + } +} + +/* + * static void *alloc_pages + * + * DESCRIPTION: + * + * Allocate space for a number of memory pages in the memory pool. + * + * RETURNS: + * + * Success - New pages of memory + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to our memory pool. + * + * page_n -> Number of pages to alloc. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +static void *alloc_pages(mpool_t *mp_p, const unsigned int page_n, + int *error_p) +{ + void *mem, *fill_mem; + unsigned long size, fill; + int state; + + /* are we over our max-pages? */ + if (mp_p->mp_max_pages > 0 && mp_p->mp_page_c >= mp_p->mp_max_pages) { + SET_POINTER(error_p, MPOOL_ERROR_NO_PAGES); + return NULL; + } + + size = SIZE_OF_PAGES(mp_p, page_n); + +#ifdef DEBUG + (void)printf("allocating %u pages or %lu bytes\n", page_n, size); +#endif + + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_USE_SBRK)) { + mem = sbrk(size); + if (mem == (void *)-1) { + SET_POINTER(error_p, MPOOL_ERROR_NO_MEM); + return NULL; + } + fill = (unsigned long)mem % mp_p->mp_page_size; + + if (fill > 0) { + fill = mp_p->mp_page_size - fill; + fill_mem = sbrk(fill); + if (fill_mem == (void *)-1) { + SET_POINTER(error_p, MPOOL_ERROR_NO_MEM); + return NULL; + } + if ((char *)fill_mem != (char *)mem + size) { + SET_POINTER(error_p, MPOOL_ERROR_SBRK_CONTIG); + return NULL; + } + mem = (char *)mem + fill; + } + } + else { + state = MAP_PRIVATE; +#ifdef MAP_FILE + state |= MAP_FILE; +#endif +#ifdef MAP_VARIABLE + state |= MAP_VARIABLE; +#endif + + /* mmap from /dev/zero */ + mem = mmap((caddr_t)mp_p->mp_addr, size, PROT_READ | PROT_WRITE, state, + mp_p->mp_fd, mp_p->mp_top); + if (mem == (void *)MAP_FAILED) { + if (errno == ENOMEM) { + SET_POINTER(error_p, MPOOL_ERROR_NO_MEM); + } + else { + SET_POINTER(error_p, MPOOL_ERROR_MMAP); + } + return NULL; + } + mp_p->mp_top += size; + if (mp_p->mp_addr != NULL) { + mp_p->mp_addr = (char *)mp_p->mp_addr + size; + } + } + + mp_p->mp_page_c += page_n; + + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return mem; +} + +/* + * static int free_pages + * + * DESCRIPTION: + * + * Free previously allocated pages of memory. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * pages <-> Pointer to memory pages that we are freeing. + * + * size -> Size of the block that we are freeing. + * + * sbrk_b -> Set to one if the pages were allocated with sbrk else mmap. + */ +static int free_pages(void *pages, const unsigned long size, + const int sbrk_b) +{ + if (! sbrk_b) { + (void)munmap((caddr_t)pages, size); + } + + return MPOOL_ERROR_NONE; +} + +/* + * static int check_magic + * + * DESCRIPTION: + * + * Check for the existance of the magic ID in a memory pointer. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * addr -> Address inside of the block that we are tryign to locate. + * + * size -> Size of the block. + */ +static int check_magic(const void *addr, const unsigned long size) +{ + const unsigned char *mem_p; + + /* set our starting point */ + mem_p = (unsigned char *)addr + size; + + if (*mem_p == FENCE_MAGIC0 && *(mem_p + 1) == FENCE_MAGIC1) { + return MPOOL_ERROR_NONE; + } + else { + return MPOOL_ERROR_PNT_OVER; + } +} + +/* + * static void write_magic + * + * DESCRIPTION: + * + * Write the magic ID to the address. + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * addr -> Address where to write the magic. + */ +static void write_magic(const void *addr) +{ + *(unsigned char *)addr = FENCE_MAGIC0; + *((unsigned char *)addr + 1) = FENCE_MAGIC1; +} + +/* + * static void free_pointer + * + * DESCRIPTION: + * + * Moved a pointer into our free lists. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * addr <-> Address where to write the magic. We may write a next + * pointer to it. + * + * size -> Size of the address space. + */ +static int free_pointer(mpool_t *mp_p, void *addr, + const unsigned long size) +{ + unsigned int bit_n; + unsigned long real_size; + mpool_free_t free_pnt; + +#ifdef DEBUG + (void)printf("freeing a block at %lx of %lu bytes\n", (long)addr, size); +#endif + + if (size == 0) { + return MPOOL_ERROR_NONE; + } + + /* + * if the user size is larger then can fit in an entire block then + * we change the size + */ + if (size > MAX_BLOCK_USER_MEMORY(mp_p)) { + real_size = SIZE_OF_PAGES(mp_p, PAGES_IN_SIZE(mp_p, size)) - + sizeof(mpool_block_t); + } + else { + real_size = size; + } + + /* + * We use a specific free bits calculation here because if we are + * freeing 10 bytes then we will be putting it into the 8-byte free + * list and not the 16 byte list. size_to_bits(10) will return 4 + * instead of 3. + */ + bit_n = size_to_free_bits(real_size); + + /* + * Minimal error checking. We could go all the way through the + * list however this might be prohibitive. + */ + if (mp_p->mp_free[bit_n] == addr) { + return MPOOL_ERROR_IS_FREE; + } + + /* add the freed pointer to the free list */ + if (bit_n < min_bit_free_next) { + /* + * Yes we know this will lose 99% of the allocations but what else + * can we do? No space for a next pointer. + */ + if (mp_p->mp_free[bit_n] == NULL) { + mp_p->mp_free[bit_n] = addr; + } + } + else if (bit_n < min_bit_free_size) { + /* we copy, not assign, to maintain the free list */ + memcpy(addr, mp_p->mp_free + bit_n, sizeof(void *)); + mp_p->mp_free[bit_n] = addr; + } + else { + + /* setup our free list structure */ + free_pnt.mf_next_p = mp_p->mp_free[bit_n]; + free_pnt.mf_size = real_size; + + /* we copy the structure in since we don't know about alignment */ + memcpy(addr, &free_pnt, sizeof(free_pnt)); + mp_p->mp_free[bit_n] = addr; + } + + return MPOOL_ERROR_NONE; +} + +/* + * static int split_block + * + * DESCRIPTION: + * + * When freeing space in a multi-block chunk we have to create new + * blocks out of the upper areas being freed. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * free_addr -> Address that we are freeing. + * + * size -> Size of the space that we are taking from address. + */ +static int split_block(mpool_t *mp_p, void *free_addr, + const unsigned long size) +{ + mpool_block_t *block_p, *new_block_p; + int ret, page_n; + void *end_p; + + /* + * 1st we find the block pointer from our free addr. At this point + * the pointer must be the 1st one in the block if it is spans + * multiple blocks. + */ + block_p = (mpool_block_t *)((char *)free_addr - sizeof(mpool_block_t)); + if (block_p->mb_magic != BLOCK_MAGIC + || block_p->mb_magic2 != BLOCK_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + page_n = PAGES_IN_SIZE(mp_p, size); + + /* we are creating a new block structure for the 2nd ... */ + new_block_p = (mpool_block_t *)((char *)block_p + + SIZE_OF_PAGES(mp_p, page_n)); + new_block_p->mb_magic = BLOCK_MAGIC; + /* New bounds is 1st block bounds. The 1st block's is reset below. */ + new_block_p->mb_bounds_p = block_p->mb_bounds_p; + /* Continue the linked list. The 1st block will point to us below. */ + new_block_p->mb_next_p = block_p->mb_next_p; + new_block_p->mb_magic2 = BLOCK_MAGIC; + + /* bounds for the 1st block are reset to the 1st page only */ + block_p->mb_bounds_p = (char *)new_block_p; + /* the next block pointer for the 1st block is now the new one */ + block_p->mb_next_p = new_block_p; + + /* only free the space in the 1st block if it is only 1 block in size */ + if (page_n == 1) { + /* now free the rest of the 1st block block */ + end_p = (char *)free_addr + size; + ret = free_pointer(mp_p, end_p, + (char *)block_p->mb_bounds_p - (char *)end_p); + if (ret != MPOOL_ERROR_NONE) { + return ret; + } + } + + /* now free the rest of the block */ + ret = free_pointer(mp_p, FIRST_ADDR_IN_BLOCK(new_block_p), + MEMORY_IN_BLOCK(new_block_p)); + if (ret != MPOOL_ERROR_NONE) { + return ret; + } + + return MPOOL_ERROR_NONE; +} + +/* + * static void *get_space + * + * DESCRIPTION: + * + * Moved a pointer into our free lists. + * + * RETURNS: + * + * Success - New address that we can use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * byte_size -> Size of the address space that we need. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +static void *get_space(mpool_t *mp_p, const unsigned long byte_size, + int *error_p) +{ + mpool_block_t *block_p; + mpool_free_t free_pnt; + int ret; + unsigned long size; + unsigned int bit_c, page_n, left; + void *free_addr = NULL, *free_end; + + size = byte_size; + while ((size & (sizeof(void *) - 1)) > 0) { + size++; + } + + /* + * First we check the free lists looking for something with enough + * pages. Maybe we should only look X bits higher in the list. + * + * XXX: this is where we'd do the best fit. We'd look for the + * closest match. We then could put the rest of the allocation that + * we did not use in a lower free list. Have a define which states + * how deep in the free list to go to find the closest match. + */ + for (bit_c = size_to_bits(size); bit_c <= MAX_BITS; bit_c++) { + if (mp_p->mp_free[bit_c] != NULL) { + free_addr = mp_p->mp_free[bit_c]; + break; + } + } + + /* + * If we haven't allocated any blocks or if the last block doesn't + * have enough memory then we need a new block. + */ + if (bit_c > MAX_BITS) { + + /* we need to allocate more space */ + + page_n = PAGES_IN_SIZE(mp_p, size); + + /* now we try and get the pages we need/want */ + block_p = alloc_pages(mp_p, page_n, error_p); + if (block_p == NULL) { + /* error_p set in alloc_pages */ + return NULL; + } + + /* init the block header */ + block_p->mb_magic = BLOCK_MAGIC; + block_p->mb_bounds_p = (char *)block_p + SIZE_OF_PAGES(mp_p, page_n); + block_p->mb_next_p = mp_p->mp_first_p; + block_p->mb_magic2 = BLOCK_MAGIC; + + /* + * We insert it into the front of the queue. We could add it to + * the end but there is not much use. + */ + mp_p->mp_first_p = block_p; + if (mp_p->mp_last_p == NULL) { + mp_p->mp_last_p = block_p; + } + + free_addr = FIRST_ADDR_IN_BLOCK(block_p); + +#ifdef DEBUG + (void)printf("had to allocate space for %lx of %lu bytes\n", + (long)free_addr, size); +#endif + + free_end = (char *)free_addr + size; + left = (char *)block_p->mb_bounds_p - (char *)free_end; + } + else { + + if (bit_c < min_bit_free_next) { + mp_p->mp_free[bit_c] = NULL; + /* calculate the number of left over bytes */ + left = bits_to_size(bit_c) - size; + } + else if (bit_c < min_bit_free_next) { + /* grab the next pointer from the freed address into our list */ + memcpy(mp_p->mp_free + bit_c, free_addr, sizeof(void *)); + /* calculate the number of left over bytes */ + left = bits_to_size(bit_c) - size; + } + else { + /* grab the free structure from the address */ + memcpy(&free_pnt, free_addr, sizeof(free_pnt)); + mp_p->mp_free[bit_c] = free_pnt.mf_next_p; + + /* are we are splitting up a multiblock chunk into fewer blocks? */ + if (PAGES_IN_SIZE(mp_p, free_pnt.mf_size) > PAGES_IN_SIZE(mp_p, size)) { + ret = split_block(mp_p, free_addr, size); + if (ret != MPOOL_ERROR_NONE) { + SET_POINTER(error_p, ret); + return NULL; + } + /* left over memory was taken care of in split_block */ + left = 0; + } + else { + /* calculate the number of left over bytes */ + left = free_pnt.mf_size - size; + } + } + +#ifdef DEBUG + (void)printf("found a free block at %lx of %lu bytes\n", + (long)free_addr, left + size); +#endif + + free_end = (char *)free_addr + size; + } + + /* + * If we have memory left over then we free it so someone else can + * use it. We do not free the space if we just allocated a + * multi-block chunk because we need to have every allocation easily + * find the start of the block. Every user address % page-size + * should take us to the start of the block. + */ + if (left > 0 && size <= MAX_BLOCK_USER_MEMORY(mp_p)) { + /* free the rest of the block */ + ret = free_pointer(mp_p, free_end, left); + if (ret != MPOOL_ERROR_NONE) { + SET_POINTER(error_p, ret); + return NULL; + } + } + + /* update our bounds */ + if (free_addr > mp_p->mp_bounds_p) { + mp_p->mp_bounds_p = free_addr; + } + else if (free_addr < mp_p->mp_min_p) { + mp_p->mp_min_p = free_addr; + } + + return free_addr; +} + +/* + * static void *alloc_mem + * + * DESCRIPTION: + * + * Allocate space for bytes inside of an already open memory pool. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal malloc. + * + * byte_size -> Number of bytes to allocate in the pool. Must be >0. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +static void *alloc_mem(mpool_t *mp_p, const unsigned long byte_size, + int *error_p) +{ + unsigned long size, fence; + void *addr; + + /* make sure we have enough bytes */ + if (byte_size < MIN_ALLOCATION) { + size = MIN_ALLOCATION; + } + else { + size = byte_size; + } + + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) { + fence = 0; + } + else { + fence = FENCE_SIZE; + } + + /* get our free space + the space for the fence post */ + addr = get_space(mp_p, size + fence, error_p); + if (addr == NULL) { + /* error_p set in get_space */ + return NULL; + } + + if (! BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) { + write_magic((char *)addr + size); + } + + /* maintain our stats */ + mp_p->mp_alloc_c++; + mp_p->mp_user_alloc += size; + if (mp_p->mp_user_alloc > mp_p->mp_max_alloc) { + mp_p->mp_max_alloc = mp_p->mp_user_alloc; + } + + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return addr; +} + +/* + * static int free_mem + * + * DESCRIPTION: + * + * Free an address from a memory pool. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal free. + * + * addr <-> Address to free. + * + * size -> Size of the address being freed. + */ +static int free_mem(mpool_t *mp_p, void *addr, const unsigned long size) +{ + unsigned long old_size, fence; + int ret; + mpool_block_t *block_p; + + /* + * If the size is larger than a block then the allocation must be at + * the front of the block. + */ + if (size > MAX_BLOCK_USER_MEMORY(mp_p)) { + block_p = (mpool_block_t *)((char *)addr - sizeof(mpool_block_t)); + if (block_p->mb_magic != BLOCK_MAGIC + || block_p->mb_magic2 != BLOCK_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + } + + /* make sure we have enough bytes */ + if (size < MIN_ALLOCATION) { + old_size = MIN_ALLOCATION; + } + else { + old_size = size; + } + + /* if we are packing the pool smaller */ + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) { + fence = 0; + } + else { + /* find the user's magic numbers if they were written */ + ret = check_magic(addr, old_size); + if (ret != MPOOL_ERROR_NONE) { + return ret; + } + fence = FENCE_SIZE; + } + + /* now we free the pointer */ + ret = free_pointer(mp_p, addr, old_size + fence); + if (ret != MPOOL_ERROR_NONE) { + return ret; + } + mp_p->mp_user_alloc -= old_size; + + /* adjust our stats */ + mp_p->mp_alloc_c--; + + return MPOOL_ERROR_NONE; +} + +/***************************** exported routines *****************************/ + +/* + * mpool_t *mpool_open + * + * DESCRIPTION: + * + * Open/allocate a new memory pool. + * + * RETURNS: + * + * Success - Pool pointer which must be passed to mpool_close to + * deallocate. + * + * Failure - NULL + * + * ARGUMENTS: + * + * flags -> Flags to set attributes of the memory pool. See the top + * of mpool.h. + * + * page_size -> Set the internal memory page-size. This must be a + * multiple of the getpagesize() value. Set to 0 for the default. + * + * start_addr -> Starting address to try and allocate memory pools. + * This is ignored if the MPOOL_FLAG_USE_SBRK is enabled. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +mpool_t *mpool_open(const unsigned int flags, const unsigned int page_size, + void *start_addr, int *error_p) +{ + mpool_block_t *block_p; + int page_n, ret; + mpool_t mp, *mp_p; + void *free_addr; + + if (! enabled_b) { + startup(); + } + + /* zero our temp struct */ + memset(&mp, 0, sizeof(mp)); + + mp.mp_magic = MPOOL_MAGIC; + mp.mp_flags = flags; + mp.mp_alloc_c = 0; + mp.mp_user_alloc = 0; + mp.mp_max_alloc = 0; + mp.mp_page_c = 0; + /* mp.mp_page_size set below */ + /* mp.mp_blocks_bit_n set below */ + /* mp.mp_fd set below */ + /* mp.mp_top set below */ + /* mp.mp_addr set below */ + mp.mp_log_func = NULL; + mp.mp_min_p = NULL; + mp.mp_bounds_p = NULL; + mp.mp_first_p = NULL; + mp.mp_last_p = NULL; + mp.mp_magic2 = MPOOL_MAGIC; + + /* get and sanity check our page size */ + if (page_size > 0) { + mp.mp_page_size = page_size; + if (mp.mp_page_size % getpagesize() != 0) { + SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID); + return NULL; + } + } + else { + mp.mp_page_size = getpagesize() * DEFAULT_PAGE_MULT; + if (mp.mp_page_size % 1024 != 0) { + SET_POINTER(error_p, MPOOL_ERROR_PAGE_SIZE); + return NULL; + } + } + + if (BIT_IS_SET(flags, MPOOL_FLAG_USE_SBRK)) { + mp.mp_fd = -1; + mp.mp_addr = NULL; + mp.mp_top = 0; + } + else { + /* open dev-zero for our mmaping */ + mp.mp_fd = open("/dev/zero", O_RDWR, 0); + if (mp.mp_fd < 0) { + SET_POINTER(error_p, MPOOL_ERROR_OPEN_ZERO); + return NULL; + } + mp.mp_addr = start_addr; + /* we start at the front of the file */ + mp.mp_top = 0; + } + + /* + * Find out how many pages we need for our mpool structure. + * + * NOTE: this adds possibly unneeded space for mpool_block_t which + * may not be in this block. + */ + page_n = PAGES_IN_SIZE(&mp, sizeof(mpool_t)); + + /* now allocate us space for the actual struct */ + mp_p = alloc_pages(&mp, page_n, error_p); + if (mp_p == NULL) { + if (mp.mp_fd >= 0) { + (void)close(mp.mp_fd); + mp.mp_fd = -1; + } + return NULL; + } + + /* + * NOTE: we do not normally free the rest of the block here because + * we want to lesson the chance of an allocation overwriting the + * main structure. + */ + if (BIT_IS_SET(flags, MPOOL_FLAG_HEAVY_PACKING)) { + + /* we add a block header to the front of the block */ + block_p = (mpool_block_t *)mp_p; + + /* init the block header */ + block_p->mb_magic = BLOCK_MAGIC; + block_p->mb_bounds_p = (char *)block_p + SIZE_OF_PAGES(&mp, page_n); + block_p->mb_next_p = NULL; + block_p->mb_magic2 = BLOCK_MAGIC; + + /* the mpool pointer is then the 2nd thing in the block */ + mp_p = FIRST_ADDR_IN_BLOCK(block_p); + free_addr = (char *)mp_p + sizeof(mpool_t); + + /* free the rest of the block */ + ret = free_pointer(&mp, free_addr, + (char *)block_p->mb_bounds_p - (char *)free_addr); + if (ret != MPOOL_ERROR_NONE) { + if (mp.mp_fd >= 0) { + (void)close(mp.mp_fd); + mp.mp_fd = -1; + } + /* NOTE: after this line mp_p will be invalid */ + (void)free_pages(block_p, SIZE_OF_PAGES(&mp, page_n), + BIT_IS_SET(flags, MPOOL_FLAG_USE_SBRK)); + SET_POINTER(error_p, ret); + return NULL; + } + + /* + * NOTE: if we are HEAVY_PACKING then the 1st block with the mpool + * header is not on the block linked list. + */ + + /* now copy our tmp structure into our new memory area */ + memcpy(mp_p, &mp, sizeof(mpool_t)); + + /* we setup min/max to our current address which is as good as any */ + mp_p->mp_min_p = block_p; + mp_p->mp_bounds_p = block_p->mb_bounds_p; + } + else { + /* now copy our tmp structure into our new memory area */ + memcpy(mp_p, &mp, sizeof(mpool_t)); + + /* we setup min/max to our current address which is as good as any */ + mp_p->mp_min_p = mp_p; + mp_p->mp_bounds_p = (char *)mp_p + SIZE_OF_PAGES(mp_p, page_n); + } + + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return mp_p; +} + +/* + * int mpool_close + * + * DESCRIPTION: + * + * Close/free a memory allocation pool previously opened with + * mpool_open. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to our memory pool. + */ +int mpool_close(mpool_t *mp_p) +{ + mpool_block_t *block_p, *next_p; + void *addr; + unsigned long size; + int ret, final = MPOOL_ERROR_NONE; + + /* special case, just return no-error */ + if (mp_p == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_CLOSE, 0, 0, NULL, NULL, 0); + } + + /* + * NOTE: if we are HEAVY_PACKING then the 1st block with the mpool + * header is not on the linked list. + */ + + /* free/invalidate the blocks */ + for (block_p = mp_p->mp_first_p; block_p != NULL; block_p = next_p) { + if (block_p->mb_magic != BLOCK_MAGIC + || block_p->mb_magic2 != BLOCK_MAGIC) { + final = MPOOL_ERROR_POOL_OVER; + break; + } + block_p->mb_magic = 0; + block_p->mb_magic2 = 0; + /* record the next pointer because it might be invalidated below */ + next_p = block_p->mb_next_p; + ret = free_pages(block_p, (char *)block_p->mb_bounds_p - (char *)block_p, + BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_USE_SBRK)); + if (ret != MPOOL_ERROR_NONE) { + final = ret; + } + } + + /* close /dev/zero if necessary */ + if (mp_p->mp_fd >= 0) { + (void)close(mp_p->mp_fd); + mp_p->mp_fd = -1; + } + + /* invalidate the mpool before we ditch it */ + mp_p->mp_magic = 0; + mp_p->mp_magic2 = 0; + + /* last we munmap the mpool pointer itself */ + if (! BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_USE_SBRK)) { + + /* if we are heavy packing then we need to free the 1st block later */ + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_HEAVY_PACKING)) { + addr = (char *)mp_p - sizeof(mpool_block_t); + } + else { + addr = mp_p; + } + size = SIZE_OF_PAGES(mp_p, PAGES_IN_SIZE(mp_p, sizeof(mpool_t))); + + (void)munmap((caddr_t)addr, size); + } + + return final; +} + +/* + * int mpool_clear + * + * DESCRIPTION: + * + * Wipe an opened memory pool clean so we can start again. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to our memory pool. + */ +int mpool_clear(mpool_t *mp_p) +{ + mpool_block_t *block_p; + int final = MPOOL_ERROR_NONE, bit_n, ret; + void *first_p; + + /* special case, just return no-error */ + if (mp_p == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_CLEAR, 0, 0, NULL, NULL, 0); + } + + /* reset all of our free lists */ + for (bit_n = 0; bit_n <= MAX_BITS; bit_n++) { + mp_p->mp_free[bit_n] = NULL; + } + + /* free the blocks */ + for (block_p = mp_p->mp_first_p; + block_p != NULL; + block_p = block_p->mb_next_p) { + if (block_p->mb_magic != BLOCK_MAGIC + || block_p->mb_magic2 != BLOCK_MAGIC) { + final = MPOOL_ERROR_POOL_OVER; + break; + } + + first_p = FIRST_ADDR_IN_BLOCK(block_p); + + /* free the memory */ + ret = free_pointer(mp_p, first_p, MEMORY_IN_BLOCK(block_p)); + if (ret != MPOOL_ERROR_NONE) { + final = ret; + } + } + + return final; +} + +/* + * void *mpool_alloc + * + * DESCRIPTION: + * + * Allocate space for bytes inside of an already open memory pool. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal malloc. + * + * byte_size -> Number of bytes to allocate in the pool. Must be >0. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +void *mpool_alloc(mpool_t *mp_p, const unsigned long byte_size, + int *error_p) +{ + void *addr; + + if (mp_p == NULL) { + /* special case -- do a normal malloc */ + addr = (void *)malloc(byte_size); + if (addr == NULL) { + SET_POINTER(error_p, MPOOL_ERROR_ALLOC); + return NULL; + } + else { + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return addr; + } + } + + if (mp_p->mp_magic != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_PNT); + return NULL; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER); + return NULL; + } + + if (byte_size == 0) { + SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID); + return NULL; + } + + addr = alloc_mem(mp_p, byte_size, error_p); + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_ALLOC, byte_size, 0, addr, NULL, 0); + } + + return addr; +} + +/* + * void *mpool_calloc + * + * DESCRIPTION: + * + * Allocate space for elements of bytes in the memory pool and zero + * the space afterwards. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal calloc. + * + * ele_n -> Number of elements to allocate. + * + * ele_size -> Number of bytes per element being allocated. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +void *mpool_calloc(mpool_t *mp_p, const unsigned long ele_n, + const unsigned long ele_size, int *error_p) +{ + void *addr; + unsigned long byte_size; + + if (mp_p == NULL) { + /* special case -- do a normal calloc */ + addr = (void *)calloc(ele_n, ele_size); + if (addr == NULL) { + SET_POINTER(error_p, MPOOL_ERROR_ALLOC); + return NULL; + } + else { + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return addr; + } + + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_PNT); + return NULL; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER); + return NULL; + } + + if (ele_n == 0 || ele_size == 0) { + SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID); + return NULL; + } + + byte_size = ele_n * ele_size; + addr = alloc_mem(mp_p, byte_size, error_p); + if (addr != NULL) { + memset(addr, 0, byte_size); + } + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_CALLOC, ele_size, ele_n, addr, NULL, 0); + } + + /* NOTE: error_p set above */ + return addr; +} + +/* + * int mpool_free + * + * DESCRIPTION: + * + * Free an address from a memory pool. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal free. + * + * addr <-> Address to free. + * + * size -> Size of the address being freed. + */ +int mpool_free(mpool_t *mp_p, void *addr, const unsigned long size) +{ + if (mp_p == NULL) { + /* special case -- do a normal free */ + free(addr); + return MPOOL_ERROR_NONE; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_FREE, size, 0, NULL, addr, 0); + } + + if (addr == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (size == 0) { + return MPOOL_ERROR_ARG_INVALID; + } + + return free_mem(mp_p, addr, size); +} + +/* + * void *mpool_resize + * + * DESCRIPTION: + * + * Reallocate an address in a mmeory pool to a new size. This is + * different from realloc in that it needs the old address' size. If + * you don't have it then you need to allocate new space, copy the + * data, and free the old pointer yourself. + * + * RETURNS: + * + * Success - Pointer to the address to use. + * + * Failure - NULL + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. If NULL then it will do a + * normal realloc. + * + * old_addr -> Previously allocated address. + * + * old_byte_size -> Size of the old address. Must be known, cannot be + * 0. + * + * new_byte_size -> New size of the allocation. + * + * error_p <- Pointer to integer which, if not NULL, will be set with + * a mpool error code. + */ +void *mpool_resize(mpool_t *mp_p, void *old_addr, + const unsigned long old_byte_size, + const unsigned long new_byte_size, + int *error_p) +{ + unsigned long copy_size, new_size, old_size, fence; + void *new_addr; + mpool_block_t *block_p; + int ret; + + if (mp_p == NULL) { + /* special case -- do a normal realloc */ + new_addr = (void *)realloc(old_addr, new_byte_size); + if (new_addr == NULL) { + SET_POINTER(error_p, MPOOL_ERROR_ALLOC); + return NULL; + } + else { + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return new_addr; + } + } + + if (mp_p->mp_magic != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_PNT); + return NULL; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER); + return NULL; + } + + if (old_addr == NULL) { + SET_POINTER(error_p, MPOOL_ERROR_ARG_NULL); + return NULL; + } + if (old_byte_size == 0) { + SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID); + return NULL; + } + + /* + * If the size is larger than a block then the allocation must be at + * the front of the block. + */ + if (old_byte_size > MAX_BLOCK_USER_MEMORY(mp_p)) { + block_p = (mpool_block_t *)((char *)old_addr - sizeof(mpool_block_t)); + if (block_p->mb_magic != BLOCK_MAGIC + || block_p->mb_magic2 != BLOCK_MAGIC) { + SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER); + return NULL; + } + } + + /* make sure we have enough bytes */ + if (old_byte_size < MIN_ALLOCATION) { + old_size = MIN_ALLOCATION; + } + else { + old_size = old_byte_size; + } + + /* verify that the size matches exactly if we can */ + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) { + fence = 0; + } + else if (old_size > 0) { + ret = check_magic(old_addr, old_size); + if (ret != MPOOL_ERROR_NONE) { + SET_POINTER(error_p, ret); + return NULL; + } + fence = FENCE_SIZE; + } + + /* make sure we have enough bytes */ + if (new_byte_size < MIN_ALLOCATION) { + new_size = MIN_ALLOCATION; + } + else { + new_size = new_byte_size; + } + + /* + * NOTE: we could here see if the size is the same or less and then + * use the current memory and free the space above. This is harder + * than it sounds if we are changing the block size of the + * allocation. + */ + + /* we need to get another address */ + new_addr = alloc_mem(mp_p, new_byte_size, error_p); + if (new_addr == NULL) { + /* error_p set in mpool_alloc */ + return NULL; + } + + if (new_byte_size > old_byte_size) { + copy_size = old_byte_size; + } + else { + copy_size = new_byte_size; + } + memcpy(new_addr, old_addr, copy_size); + + /* free the old address */ + ret = free_mem(mp_p, old_addr, old_byte_size); + if (ret != MPOOL_ERROR_NONE) { + /* if the old free failed, try and free the new address */ + (void)free_mem(mp_p, new_addr, new_byte_size); + SET_POINTER(error_p, ret); + return NULL; + } + + if (mp_p->mp_log_func != NULL) { + mp_p->mp_log_func(mp_p, MPOOL_FUNC_RESIZE, new_byte_size, + 0, new_addr, old_addr, old_byte_size); + } + + SET_POINTER(error_p, MPOOL_ERROR_NONE); + return new_addr; +} + +/* + * int mpool_stats + * + * DESCRIPTION: + * + * Return stats from the memory pool. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p -> Pointer to the memory pool. + * + * page_size_p <- Pointer to an unsigned integer which, if not NULL, + * will be set to the page-size of the pool. + * + * num_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the number of pointers currently allocated in pool. + * + * user_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the number of user bytes allocated in this pool. + * + * max_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the maximum number of user bytes that have been + * allocated in this pool. + * + * tot_alloced_p <- Pointer to an unsigned long which, if not NULL, + * will be set to the total amount of space (including administrative + * overhead) used by the pool. + */ +int mpool_stats(const mpool_t *mp_p, unsigned int *page_size_p, + unsigned long *num_alloced_p, + unsigned long *user_alloced_p, + unsigned long *max_alloced_p, + unsigned long *tot_alloced_p) +{ + if (mp_p == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + SET_POINTER(page_size_p, mp_p->mp_page_size); + SET_POINTER(num_alloced_p, mp_p->mp_alloc_c); + SET_POINTER(user_alloced_p, mp_p->mp_user_alloc); + SET_POINTER(max_alloced_p, mp_p->mp_max_alloc); + SET_POINTER(tot_alloced_p, SIZE_OF_PAGES(mp_p, mp_p->mp_page_c)); + + return MPOOL_ERROR_NONE; +} + +/* + * int mpool_set_log_func + * + * DESCRIPTION: + * + * Set a logging callback function to be called whenever there was a + * memory transaction. See mpool_log_func_t. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * log_func -> Log function (defined in mpool.h) which will be called + * with each mpool transaction. + */ +int mpool_set_log_func(mpool_t *mp_p, mpool_log_func_t log_func) +{ + if (mp_p == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + mp_p->mp_log_func = log_func; + + return MPOOL_ERROR_NONE; +} + +/* + * int mpool_set_max_pages + * + * DESCRIPTION: + * + * Set the maximum number of pages that the library will use. Once it + * hits the limit it will return MPOOL_ERROR_NO_PAGES. + * + * NOTE: if the MPOOL_FLAG_HEAVY_PACKING is set then this max-pages + * value will include the page with the mpool header structure in it. + * If the flag is _not_ set then the max-pages will not include this + * first page. + * + * RETURNS: + * + * Success - MPOOL_ERROR_NONE + * + * Failure - Mpool error code + * + * ARGUMENTS: + * + * mp_p <-> Pointer to the memory pool. + * + * max_pages -> Maximum number of pages used by the library. + */ +int mpool_set_max_pages(mpool_t *mp_p, const unsigned int max_pages) +{ + if (mp_p == NULL) { + return MPOOL_ERROR_ARG_NULL; + } + if (mp_p->mp_magic != MPOOL_MAGIC) { + return MPOOL_ERROR_PNT; + } + if (mp_p->mp_magic2 != MPOOL_MAGIC) { + return MPOOL_ERROR_POOL_OVER; + } + + if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_HEAVY_PACKING)) { + mp_p->mp_max_pages = max_pages; + } + else { + /* + * If we are not heavy-packing the pool then we don't count the + * 1st page allocated which holds the mpool header structure. + */ + mp_p->mp_max_pages = max_pages + 1; + } + + return MPOOL_ERROR_NONE; +} + +/* + * const char *mpool_strerror + * + * DESCRIPTION: + * + * Return the corresponding string for the error number. + * + * RETURNS: + * + * Success - String equivalient of the error. + * + * Failure - String "invalid error code" + * + * ARGUMENTS: + * + * error -> Error number that we are converting. + */ +const char *mpool_strerror(const int error) +{ + switch (error) { + case MPOOL_ERROR_NONE: + return "no error"; + break; + case MPOOL_ERROR_ARG_NULL: + return "function argument is null"; + break; + case MPOOL_ERROR_ARG_INVALID: + return "function argument is invalid"; + break; + case MPOOL_ERROR_PNT: + return "invalid mpool pointer"; + break; + case MPOOL_ERROR_POOL_OVER: + return "mpool structure was overwritten"; + break; + case MPOOL_ERROR_PAGE_SIZE: + return "could not get system page-size"; + break; + case MPOOL_ERROR_OPEN_ZERO: + return "could not open /dev/zero"; + break; + case MPOOL_ERROR_NO_MEM: + return "no memory available"; + break; + case MPOOL_ERROR_MMAP: + return "problems with mmap"; + break; + case MPOOL_ERROR_SIZE: + return "error processing requested size"; + break; + case MPOOL_ERROR_TOO_BIG: + return "allocation exceeds pool max size"; + break; + case MPOOL_ERROR_MEM: + return "invalid memory address"; + break; + case MPOOL_ERROR_MEM_OVER: + return "memory lower bounds overwritten"; + break; + case MPOOL_ERROR_NOT_FOUND: + return "memory block not found in pool"; + break; + case MPOOL_ERROR_IS_FREE: + return "memory address has already been freed"; + break; + case MPOOL_ERROR_BLOCK_STAT: + return "invalid internal block status"; + break; + case MPOOL_ERROR_FREE_ADDR: + return "invalid internal free address"; + break; + case MPOOL_ERROR_SBRK_CONTIG: + return "sbrk did not return contiguous memory"; + break; + case MPOOL_ERROR_NO_PAGES: + return "no available pages left in pool"; + break; + case MPOOL_ERROR_ALLOC: + return "system alloc function failed"; + break; + case MPOOL_ERROR_PNT_OVER: + return "user pointer admin space overwritten"; + break; + default: + break; + } + + return "invalid error code"; +} diff --git a/libs/libks/src/simclist.c b/libs/libks/src/simclist.c new file mode 100755 index 0000000000..c8d36f5ff4 --- /dev/null +++ b/libs/libks/src/simclist.c @@ -0,0 +1,1525 @@ +/* + * Copyright (c) 2007,2008,2009,2010,2011 Mij + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +/* + * SimCList library. See http://mij.oltrelinux.com/devel/simclist + */ + +/* SimCList implementation, version 1.6 */ + +#include +#include +#include /* for setting errno */ +#include +#ifndef _WIN32 + /* not in Windows! */ +# include +# include +#endif +#ifndef SIMCLIST_NO_DUMPRESTORE + /* includes for dump/restore */ +# include +# include /* for READ_ERRCHECK() and write() */ +# include /* for open() etc */ +# ifndef _WIN32 +# include /* for htons() on UNIX */ +# else +# include /* for htons() on Windows */ +# endif +#endif + +/* disable asserts */ +#ifndef SIMCLIST_DEBUG +#define NDEBUG +#endif + +#include + + +#include /* for open()'s access modes S_IRUSR etc */ +#include + +#if defined(_MSC_VER) || defined(__MINGW32__) +/* provide gettimeofday() missing in Windows */ +int gettimeofday(struct timeval *tp, void *tzp) { + DWORD t; + + /* XSI says: "If tzp is not a null pointer, the behavior is unspecified" */ + assert(tzp == NULL); + + t = timeGetTime(); + tp->tv_sec = t / 1000; + tp->tv_usec = t % 1000; + return 0; +} +#endif + + +/* work around lack of inttypes.h support in broken Microsoft Visual Studio compilers */ +#if !defined(_WIN32) || !defined(_MSC_VER) +# include /* (u)int*_t */ +#else +# include +typedef UINT8 uint8_t; +typedef UINT16 uint16_t; +typedef ULONG32 uint32_t; +typedef UINT64 uint64_t; +typedef INT8 int8_t; +typedef INT16 int16_t; +typedef LONG32 int32_t; +typedef INT64 int64_t; +#endif + + +/* define some commodity macros for Dump/Restore functionality */ +#ifndef SIMCLIST_NO_DUMPRESTORE +/* write() decorated with error checking logic */ +#define WRITE_ERRCHECK(fd, msgbuf, msglen) do { \ + if (write(fd, msgbuf, msglen) < 0) return -1; \ + } while (0); +/* READ_ERRCHECK() decorated with error checking logic */ +#define READ_ERRCHECK(fd, msgbuf, msglen) do { \ + if (read(fd, msgbuf, msglen) != msglen) { \ + /*errno = EPROTO;*/ \ + return -1; \ + } \ + } while (0); + +/* convert 64bit integers from host to network format */ +#define hton64(x) (\ + htons(1) == 1 ? \ + (uint64_t)x /* big endian */ \ + : /* little endian */ \ + ((uint64_t)((((uint64_t)(x) & 0xff00000000000000ULL) >> 56) | \ + (((uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \ + (((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \ + (((uint64_t)(x) & 0x000000ff00000000ULL) >> 8) | \ + (((uint64_t)(x) & 0x00000000ff000000ULL) << 8) | \ + (((uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \ + (((uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \ + (((uint64_t)(x) & 0x00000000000000ffULL) << 56))) \ + ) + +/* convert 64bit integers from network to host format */ +#define ntoh64(x) (hton64(x)) +#endif + +/* some OSes don't have EPROTO (eg OpenBSD) */ +#ifndef EPROTO +#define EPROTO EIO +#endif + +#ifdef SIMCLIST_WITH_THREADS +/* limit (approx) to the number of threads running + * for threaded operations. Only meant when + * SIMCLIST_WITH_THREADS is defined */ +#define SIMCLIST_MAXTHREADS 2 +#endif + +/* + * how many elems to keep as spare. During a deletion, an element + * can be saved in a "free-list", not free()d immediately. When + * latter insertions are performed, spare elems can be used instead + * of malloc()ing new elems. + * + * about this param, some values for appending + * 10 million elems into an empty list: + * (#, time[sec], gain[%], gain/no[%]) + * 0 2,164 0,00 0,00 <-- feature disabled + * 1 1,815 34,9 34,9 + * 2 1,446 71,8 35,9 <-- MAX gain/no + * 3 1,347 81,7 27,23 + * 5 1,213 95,1 19,02 + * 8 1,064 110,0 13,75 + * 10 1,015 114,9 11,49 <-- MAX gain w/ likely sol + * 15 1,019 114,5 7,63 + * 25 0,985 117,9 4,72 + * 50 1,088 107,6 2,15 + * 75 1,016 114,8 1,53 + * 100 0,988 117,6 1,18 + * 150 1,022 114,2 0,76 + * 200 0,939 122,5 0,61 <-- MIN time + */ +#ifndef SIMCLIST_MAX_SPARE_ELEMS +#define SIMCLIST_MAX_SPARE_ELEMS 5 +#endif + + +#ifdef SIMCLIST_WITH_THREADS +#include +#endif + +#include "simclist.h" + + +/* minumum number of elements for sorting with quicksort instead of insertion */ +#define SIMCLIST_MINQUICKSORTELS 24 + + +/* list dump declarations */ +#define SIMCLIST_DUMPFORMAT_VERSION 1 /* (short integer) version of fileformat managed by _dump* and _restore* functions */ + +#define SIMCLIST_DUMPFORMAT_HEADERLEN 30 /* length of the header */ + +/* header for a list dump */ +struct list_dump_header_s { + uint16_t ver; /* version */ + int32_t timestamp_sec; /* dump timestamp, seconds since UNIX Epoch */ + int32_t timestamp_usec; /* dump timestamp, microseconds since timestamp_sec */ + int32_t rndterm; /* random value terminator -- terminates the data sequence */ + + uint32_t totlistlen; /* sum of every element' size, bytes */ + uint32_t numels; /* number of elements */ + uint32_t elemlen; /* bytes length of an element, for constant-size lists, <= 0 otherwise */ + int32_t listhash; /* hash of the list at the time of dumping, or 0 if to be ignored */ +}; + + + +/* deletes tmp from list, with care wrt its position (head, tail, middle) */ +static int list_drop_elem(list_t *restrict l, struct list_entry_s *tmp, unsigned int pos); + +/* set default values for initialized lists */ +static int list_attributes_setdefaults(list_t *restrict l); + +#ifndef NDEBUG +/* check whether the list internal REPresentation is valid -- Costs O(n) */ +static int list_repOk(const list_t *restrict l); + +/* check whether the list attribute set is valid -- Costs O(1) */ +static int list_attrOk(const list_t *restrict l); +#endif + +/* do not inline, this is recursive */ +static void list_sort_quicksort(list_t *restrict l, int versus, + unsigned int first, struct list_entry_s *fel, + unsigned int last, struct list_entry_s *lel); + +static inline void list_sort_selectionsort(list_t *restrict l, int versus, + unsigned int first, struct list_entry_s *fel, + unsigned int last, struct list_entry_s *lel); + +static void *list_get_minmax(const list_t *restrict l, int versus); + +static inline struct list_entry_s *list_findpos(const list_t *restrict l, int posstart); + +/* + * Random Number Generator + * + * The user is expected to seed the RNG (ie call srand()) if + * SIMCLIST_SYSTEM_RNG is defined. + * + * Otherwise, a self-contained RNG based on LCG is used; see + * http://en.wikipedia.org/wiki/Linear_congruential_generator . + * + * Facts pro local RNG: + * 1. no need for the user to call srand() on his own + * 2. very fast, possibly faster than OS + * 3. avoid interference with user's RNG + * + * Facts pro system RNG: + * 1. may be more accurate (irrelevant for SimCList randno purposes) + * 2. why reinvent the wheel + * + * Default to local RNG for user's ease of use. + */ + +#ifdef SIMCLIST_SYSTEM_RNG +/* keep track whether we initialized already (non-0) or not (0) */ +static unsigned random_seed = 0; + +/* use local RNG */ +static inline void seed_random(void) { + if (random_seed == 0) + random_seed = (unsigned)getpid() ^ (unsigned)time(NULL); +} + +static inline long get_random(void) { + random_seed = (1664525 * random_seed + 1013904223); + return random_seed; +} + +#else +/* use OS's random generator */ +# define seed_random() +# define get_random() (rand()) +#endif + + +/* list initialization */ +int list_init(list_t *restrict l) { + if (l == NULL) return -1; + + seed_random(); + + l->numels = 0; + + /* head/tail sentinels and mid pointer */ + l->head_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s)); + l->tail_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s)); + l->head_sentinel->next = l->tail_sentinel; + l->tail_sentinel->prev = l->head_sentinel; + l->head_sentinel->prev = l->tail_sentinel->next = l->mid = NULL; + l->head_sentinel->data = l->tail_sentinel->data = NULL; + + /* iteration attributes */ + l->iter_active = 0; + l->iter_pos = 0; + l->iter_curentry = NULL; + + /* free-list attributes */ + l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS * sizeof(struct list_entry_s *)); + l->spareelsnum = 0; + +#ifdef SIMCLIST_WITH_THREADS + l->threadcount = 0; +#endif + + list_attributes_setdefaults(l); + + assert(list_repOk(l)); + assert(list_attrOk(l)); + + return 0; +} + +void list_destroy(list_t *restrict l) { + unsigned int i; + + list_clear(l); + for (i = 0; i < l->spareelsnum; i++) { + free(l->spareels[i]); + } + free(l->spareels); + free(l->head_sentinel); + free(l->tail_sentinel); +} + +int list_attributes_setdefaults(list_t *restrict l) { + l->attrs.comparator = NULL; + l->attrs.seeker = NULL; + + /* also free() element data when removing and element from the list */ + l->attrs.meter = NULL; + l->attrs.copy_data = 0; + + l->attrs.hasher = NULL; + + /* serializer/unserializer */ + l->attrs.serializer = NULL; + l->attrs.unserializer = NULL; + + assert(list_attrOk(l)); + + return 0; +} + +/* setting list properties */ +int list_attributes_comparator(list_t *restrict l, element_comparator comparator_fun) { + if (l == NULL) return -1; + + l->attrs.comparator = comparator_fun; + + assert(list_attrOk(l)); + + return 0; +} + +int list_attributes_seeker(list_t *restrict l, element_seeker seeker_fun) { + if (l == NULL) return -1; + + l->attrs.seeker = seeker_fun; + assert(list_attrOk(l)); + + return 0; +} + +int list_attributes_copy(list_t *restrict l, element_meter metric_fun, int copy_data) { + if (l == NULL || (metric_fun == NULL && copy_data != 0)) return -1; + + l->attrs.meter = metric_fun; + l->attrs.copy_data = copy_data; + + assert(list_attrOk(l)); + + return 0; +} + +int list_attributes_hash_computer(list_t *restrict l, element_hash_computer hash_computer_fun) { + if (l == NULL) return -1; + + l->attrs.hasher = hash_computer_fun; + assert(list_attrOk(l)); + return 0; +} + +int list_attributes_serializer(list_t *restrict l, element_serializer serializer_fun) { + if (l == NULL) return -1; + + l->attrs.serializer = serializer_fun; + assert(list_attrOk(l)); + return 0; +} + +int list_attributes_unserializer(list_t *restrict l, element_unserializer unserializer_fun) { + if (l == NULL) return -1; + + l->attrs.unserializer = unserializer_fun; + assert(list_attrOk(l)); + return 0; +} + +int list_append(list_t *restrict l, const void *data) { + return list_insert_at(l, data, l->numels); +} + +int list_prepend(list_t *restrict l, const void *data) { + return list_insert_at(l, data, 0); +} + +void *list_fetch(list_t *restrict l) { + return list_extract_at(l, 0); +} + +void *list_get_at(const list_t *restrict l, unsigned int pos) { + struct list_entry_s *tmp; + + tmp = list_findpos(l, pos); + + return (tmp != NULL ? tmp->data : NULL); +} + +void *list_get_max(const list_t *restrict l) { + return list_get_minmax(l, +1); +} + +void *list_get_min(const list_t *restrict l) { + return list_get_minmax(l, -1); +} + +/* REQUIRES {list->numels >= 1} + * return the min (versus < 0) or max value (v > 0) in l */ +static void *list_get_minmax(const list_t *restrict l, int versus) { + void *curminmax; + struct list_entry_s *s; + + if (l->attrs.comparator == NULL || l->numels == 0) + return NULL; + + curminmax = l->head_sentinel->next->data; + for (s = l->head_sentinel->next->next; s != l->tail_sentinel; s = s->next) { + if (l->attrs.comparator(curminmax, s->data) * versus > 0) + curminmax = s->data; + } + + return curminmax; +} + +/* set tmp to point to element at index posstart in l */ +static inline struct list_entry_s *list_findpos(const list_t *restrict l, int posstart) { + struct list_entry_s *ptr; + float x; + int i; + + /* accept 1 slot overflow for fetching head and tail sentinels */ + if (posstart < -1 || posstart > (int)l->numels) return NULL; + + x = (float)(posstart+1) / l->numels; + if (x <= 0.25) { + /* first quarter: get to posstart from head */ + for (i = -1, ptr = l->head_sentinel; i < posstart; ptr = ptr->next, i++); + } else if (x < 0.5) { + /* second quarter: get to posstart from mid */ + for (i = (l->numels-1)/2, ptr = l->mid; i > posstart; ptr = ptr->prev, i--); + } else if (x <= 0.75) { + /* third quarter: get to posstart from mid */ + for (i = (l->numels-1)/2, ptr = l->mid; i < posstart; ptr = ptr->next, i++); + } else { + /* fourth quarter: get to posstart from tail */ + for (i = l->numels, ptr = l->tail_sentinel; i > posstart; ptr = ptr->prev, i--); + } + + return ptr; +} + +void *list_extract_at(list_t *restrict l, unsigned int pos) { + struct list_entry_s *tmp; + void *data; + + if (l->iter_active || pos >= l->numels) return NULL; + + tmp = list_findpos(l, pos); + data = tmp->data; + + tmp->data = NULL; /* save data from list_drop_elem() free() */ + list_drop_elem(l, tmp, pos); + l->numels--; + + assert(list_repOk(l)); + + return data; +} + +int list_insert_at(list_t *restrict l, const void *data, unsigned int pos) { + struct list_entry_s *lent, *succ, *prec; + + if (l->iter_active || pos > l->numels) return -1; + + /* this code optimizes malloc() with a free-list */ + if (l->spareelsnum > 0) { + lent = l->spareels[l->spareelsnum-1]; + l->spareelsnum--; + } else { + lent = (struct list_entry_s *)malloc(sizeof(struct list_entry_s)); + if (lent == NULL) + return -1; + } + + if (l->attrs.copy_data) { + /* make room for user' data (has to be copied) */ + size_t datalen = l->attrs.meter(data); + lent->data = (struct list_entry_s *)malloc(datalen); + memcpy(lent->data, data, datalen); + } else { + lent->data = (void*)data; + } + + /* actually append element */ + prec = list_findpos(l, pos-1); + succ = prec->next; + + prec->next = lent; + lent->prev = prec; + lent->next = succ; + succ->prev = lent; + + l->numels++; + + /* fix mid pointer */ + if (l->numels == 1) { /* first element, set pointer */ + l->mid = lent; + } else if (l->numels % 2) { /* now odd */ + if (pos >= (l->numels-1)/2) l->mid = l->mid->next; + } else { /* now even */ + if (pos <= (l->numels-1)/2) l->mid = l->mid->prev; + } + + assert(list_repOk(l)); + + return 1; +} + +int list_delete(list_t *restrict l, const void *data) { + int pos, r; + + pos = list_locate(l, data); + if (pos < 0) + return -1; + + r = list_delete_at(l, pos); + if (r < 0) + return -1; + + assert(list_repOk(l)); + + return 0; +} + +int list_delete_at(list_t *restrict l, unsigned int pos) { + struct list_entry_s *delendo; + + + if (l->iter_active || pos >= l->numels) return -1; + + delendo = list_findpos(l, pos); + + list_drop_elem(l, delendo, pos); + + l->numels--; + + + assert(list_repOk(l)); + + return 0; +} + +int list_delete_range(list_t *restrict l, unsigned int posstart, unsigned int posend) { + struct list_entry_s *lastvalid, *tmp, *tmp2; + unsigned int numdel, midposafter, i; + int movedx; + + if (l->iter_active || posend < posstart || posend >= l->numels) return -1; + + numdel = posend - posstart + 1; + if (numdel == l->numels) return list_clear(l); + + tmp = list_findpos(l, posstart); /* first el to be deleted */ + lastvalid = tmp->prev; /* last valid element */ + + midposafter = (l->numels-1-numdel)/2; + + midposafter = midposafter < posstart ? midposafter : midposafter+numdel; + movedx = midposafter - (l->numels-1)/2; + + if (movedx > 0) { /* move right */ + for (i = 0; i < (unsigned int)movedx; l->mid = l->mid->next, i++); + } else { /* move left */ + movedx = -movedx; + for (i = 0; i < (unsigned int)movedx; l->mid = l->mid->prev, i++); + } + + assert(posstart == 0 || lastvalid != l->head_sentinel); + i = posstart; + if (l->attrs.copy_data) { + /* also free element data */ + for (; i <= posend; i++) { + tmp2 = tmp; + tmp = tmp->next; + if (tmp2->data != NULL) free(tmp2->data); + if (l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS) { + l->spareels[l->spareelsnum++] = tmp2; + } else { + free(tmp2); + } + } + } else { + /* only free containers */ + for (; i <= posend; i++) { + tmp2 = tmp; + tmp = tmp->next; + if (l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS) { + l->spareels[l->spareelsnum++] = tmp2; + } else { + free(tmp2); + } + } + } + assert(i == posend+1 && (posend != l->numels || tmp == l->tail_sentinel)); + + lastvalid->next = tmp; + tmp->prev = lastvalid; + + l->numels -= posend - posstart + 1; + + assert(list_repOk(l)); + + return numdel; +} + +int list_clear(list_t *restrict l) { + struct list_entry_s *s; + unsigned int numels; + + /* will be returned */ + numels = l->numels; + + if (l->iter_active) return -1; + + if (l->attrs.copy_data) { /* also free user data */ + /* spare a loop conditional with two loops: spareing elems and freeing elems */ + for (s = l->head_sentinel->next; l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS && s != l->tail_sentinel; s = s->next) { + /* move elements as spares as long as there is room */ + if (s->data != NULL) free(s->data); + l->spareels[l->spareelsnum++] = s; + } + while (s != l->tail_sentinel) { + /* free the remaining elems */ + if (s->data != NULL) free(s->data); + s = s->next; + free(s->prev); + } + l->head_sentinel->next = l->tail_sentinel; + l->tail_sentinel->prev = l->head_sentinel; + } else { /* only free element containers */ + /* spare a loop conditional with two loops: spareing elems and freeing elems */ + for (s = l->head_sentinel->next; l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS && s != l->tail_sentinel; s = s->next) { + /* move elements as spares as long as there is room */ + l->spareels[l->spareelsnum++] = s; + } + while (s != l->tail_sentinel) { + /* free the remaining elems */ + s = s->next; + free(s->prev); + } + l->head_sentinel->next = l->tail_sentinel; + l->tail_sentinel->prev = l->head_sentinel; + } + l->numels = 0; + l->mid = NULL; + + assert(list_repOk(l)); + + return numels; +} + +unsigned int list_size(const list_t *restrict l) { + return l->numels; +} + +int list_empty(const list_t *restrict l) { + return (l->numels == 0); +} + +int list_locate(const list_t *restrict l, const void *data) { + struct list_entry_s *el; + int pos = 0; + + if (l->attrs.comparator != NULL) { + /* use comparator */ + for (el = l->head_sentinel->next; el != l->tail_sentinel; el = el->next, pos++) { + if (l->attrs.comparator(data, el->data) == 0) break; + } + } else { + /* compare references */ + for (el = l->head_sentinel->next; el != l->tail_sentinel; el = el->next, pos++) { + if (el->data == data) break; + } + } + if (el == l->tail_sentinel) return -1; + + return pos; +} + +void *list_seek(list_t *restrict l, const void *indicator) { + const struct list_entry_s *iter; + + if (l->attrs.seeker == NULL) return NULL; + + for (iter = l->head_sentinel->next; iter != l->tail_sentinel; iter = iter->next) { + if (l->attrs.seeker(iter->data, indicator) != 0) return iter->data; + } + + return NULL; +} + +int list_contains(const list_t *restrict l, const void *data) { + return (list_locate(l, data) >= 0); +} + +int list_concat(const list_t *l1, const list_t *l2, list_t *restrict dest) { + struct list_entry_s *el, *srcel; + unsigned int cnt; + int err; + + + if (l1 == NULL || l2 == NULL || dest == NULL || l1 == dest || l2 == dest) + return -1; + + list_init(dest); + + dest->numels = l1->numels + l2->numels; + if (dest->numels == 0) + return 0; + + /* copy list1 */ + srcel = l1->head_sentinel->next; + el = dest->head_sentinel; + while (srcel != l1->tail_sentinel) { + el->next = (struct list_entry_s *)malloc(sizeof(struct list_entry_s)); + el->next->prev = el; + el = el->next; + el->data = srcel->data; + srcel = srcel->next; + } + dest->mid = el; /* approximate position (adjust later) */ + /* copy list 2 */ + srcel = l2->head_sentinel->next; + while (srcel != l2->tail_sentinel) { + el->next = (struct list_entry_s *)malloc(sizeof(struct list_entry_s)); + el->next->prev = el; + el = el->next; + el->data = srcel->data; + srcel = srcel->next; + } + el->next = dest->tail_sentinel; + dest->tail_sentinel->prev = el; + + /* fix mid pointer */ + err = l2->numels - l1->numels; + if ((err+1)/2 > 0) { /* correct pos RIGHT (err-1)/2 moves */ + err = (err+1)/2; + for (cnt = 0; cnt < (unsigned int)err; cnt++) dest->mid = dest->mid->next; + } else if (err/2 < 0) { /* correct pos LEFT (err/2)-1 moves */ + err = -err/2; + for (cnt = 0; cnt < (unsigned int)err; cnt++) dest->mid = dest->mid->prev; + } + + assert(!(list_repOk(l1) && list_repOk(l2)) || list_repOk(dest)); + + return 0; +} + +int list_sort(list_t *restrict l, int versus) { + if (l->iter_active || l->attrs.comparator == NULL) /* cannot modify list in the middle of an iteration */ + return -1; + + if (l->numels <= 1) + return 0; + list_sort_quicksort(l, versus, 0, l->head_sentinel->next, l->numels-1, l->tail_sentinel->prev); + assert(list_repOk(l)); + return 0; +} + +#ifdef SIMCLIST_WITH_THREADS +struct list_sort_wrappedparams { + list_t *restrict l; + int versus; + unsigned int first, last; + struct list_entry_s *fel, *lel; +}; + +static void *list_sort_quicksort_threadwrapper(void *wrapped_params) { + struct list_sort_wrappedparams *wp = (struct list_sort_wrappedparams *)wrapped_params; + list_sort_quicksort(wp->l, wp->versus, wp->first, wp->fel, wp->last, wp->lel); + free(wp); + pthread_exit(NULL); + return NULL; +} +#endif + +static inline void list_sort_selectionsort(list_t *restrict l, int versus, + unsigned int first, struct list_entry_s *fel, + unsigned int last, struct list_entry_s *lel) { + struct list_entry_s *cursor, *toswap, *firstunsorted; + void *tmpdata; + + if (last <= first) /* <= 1-element lists are always sorted */ + return; + + for (firstunsorted = fel; firstunsorted != lel; firstunsorted = firstunsorted->next) { + /* find min or max in the remainder of the list */ + for (toswap = firstunsorted, cursor = firstunsorted->next; cursor != lel->next; cursor = cursor->next) + if (l->attrs.comparator(toswap->data, cursor->data) * -versus > 0) toswap = cursor; + if (toswap != firstunsorted) { /* swap firstunsorted with toswap */ + tmpdata = firstunsorted->data; + firstunsorted->data = toswap->data; + toswap->data = tmpdata; + } + } +} + +static void list_sort_quicksort(list_t *restrict l, int versus, + unsigned int first, struct list_entry_s *fel, + unsigned int last, struct list_entry_s *lel) { + unsigned int pivotid; + unsigned int i; + register struct list_entry_s *pivot; + struct list_entry_s *left, *right; + void *tmpdata; +#ifdef SIMCLIST_WITH_THREADS + pthread_t tid; + int traised; +#endif + + + if (last <= first) /* <= 1-element lists are always sorted */ + return; + + if (last - first+1 <= SIMCLIST_MINQUICKSORTELS) { + list_sort_selectionsort(l, versus, first, fel, last, lel); + return; + } + + /* base of iteration: one element list */ + if (! (last > first)) return; + + pivotid = (get_random() % (last - first + 1)); + /* pivotid = (last - first + 1) / 2; */ + + /* find pivot */ + if (pivotid < (last - first + 1)/2) { + for (i = 0, pivot = fel; i < pivotid; pivot = pivot->next, i++); + } else { + for (i = last - first, pivot = lel; i > pivotid; pivot = pivot->prev, i--); + } + + /* smaller PIVOT bigger */ + left = fel; + right = lel; + /* iterate --- left ---> PIV <--- right --- */ + while (left != pivot && right != pivot) { + for (; left != pivot && (l->attrs.comparator(left->data, pivot->data) * -versus <= 0); left = left->next); + /* left points to a smaller element, or to pivot */ + for (; right != pivot && (l->attrs.comparator(right->data, pivot->data) * -versus >= 0); right = right->prev); + /* right points to a bigger element, or to pivot */ + if (left != pivot && right != pivot) { + /* swap, then move iterators */ + tmpdata = left->data; + left->data = right->data; + right->data = tmpdata; + + left = left->next; + right = right->prev; + } + } + + /* now either left points to pivot (end run), or right */ + if (right == pivot) { /* left part longer */ + while (left != pivot) { + if (l->attrs.comparator(left->data, pivot->data) * -versus > 0) { + tmpdata = left->data; + left->data = pivot->prev->data; + pivot->prev->data = pivot->data; + pivot->data = tmpdata; + pivot = pivot->prev; + pivotid--; + if (pivot == left) break; + } else { + left = left->next; + } + } + } else { /* right part longer */ + while (right != pivot) { + if (l->attrs.comparator(right->data, pivot->data) * -versus < 0) { + /* move current right before pivot */ + tmpdata = right->data; + right->data = pivot->next->data; + pivot->next->data = pivot->data; + pivot->data = tmpdata; + pivot = pivot->next; + pivotid++; + if (pivot == right) break; + } else { + right = right->prev; + } + } + } + + /* sort sublists A and B : |---A---| pivot |---B---| */ + +#ifdef SIMCLIST_WITH_THREADS + traised = 0; + if (pivotid > 0) { + /* prepare wrapped args, then start thread */ + if (l->threadcount < SIMCLIST_MAXTHREADS-1) { + struct list_sort_wrappedparams *wp = (struct list_sort_wrappedparams *)malloc(sizeof(struct list_sort_wrappedparams)); + l->threadcount++; + traised = 1; + wp->l = l; + wp->versus = versus; + wp->first = first; + wp->fel = fel; + wp->last = first+pivotid-1; + wp->lel = pivot->prev; + if (pthread_create(&tid, NULL, list_sort_quicksort_threadwrapper, wp) != 0) { + free(wp); + traised = 0; + list_sort_quicksort(l, versus, first, fel, first+pivotid-1, pivot->prev); + } + } else { + list_sort_quicksort(l, versus, first, fel, first+pivotid-1, pivot->prev); + } + } + if (first + pivotid < last) list_sort_quicksort(l, versus, first+pivotid+1, pivot->next, last, lel); + if (traised) { + pthread_join(tid, (void **)NULL); + l->threadcount--; + } +#else + if (pivotid > 0) list_sort_quicksort(l, versus, first, fel, first+pivotid-1, pivot->prev); + if (first + pivotid < last) list_sort_quicksort(l, versus, first+pivotid+1, pivot->next, last, lel); +#endif +} + +int list_iterator_start(list_t *restrict l) { + if (l->iter_active) return 0; + l->iter_pos = 0; + l->iter_active = 1; + l->iter_curentry = l->head_sentinel->next; + return 1; +} + +void *list_iterator_next(list_t *restrict l) { + void *toret; + + if (! l->iter_active) return NULL; + + toret = l->iter_curentry->data; + l->iter_curentry = l->iter_curentry->next; + l->iter_pos++; + + return toret; +} + +int list_iterator_hasnext(const list_t *restrict l) { + if (! l->iter_active) return 0; + return (l->iter_pos < l->numels); +} + +int list_iterator_stop(list_t *restrict l) { + if (! l->iter_active) return 0; + l->iter_pos = 0; + l->iter_active = 0; + return 1; +} + +int list_hash(const list_t *restrict l, list_hash_t *restrict hash) { + struct list_entry_s *x; + list_hash_t tmphash; + + assert(hash != NULL); + + tmphash = l->numels * 2 + 100; + if (l->attrs.hasher == NULL) { +#ifdef SIMCLIST_ALLOW_LOCATIONBASED_HASHES + /* ENABLE WITH CARE !! */ +#warning "Memlocation-based hash is consistent only for testing modification in the same program run." + int i; + + /* only use element references */ + for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) { + for (i = 0; i < sizeof(x->data); i++) { + tmphash += (tmphash ^ (uintptr_t)x->data); + } + tmphash += tmphash % l->numels; + } +#else + return -1; +#endif + } else { + /* hash each element with the user-given function */ + for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) { + tmphash += tmphash ^ l->attrs.hasher(x->data); + tmphash += tmphash % l->numels; + } + } + + *hash = tmphash; + + return 0; +} + +#ifndef SIMCLIST_NO_DUMPRESTORE +int list_dump_getinfo_filedescriptor(int fd, list_dump_info_t *restrict info) { + int32_t terminator_head, terminator_tail; + uint32_t elemlen; + off_t hop; + + + /* version */ + READ_ERRCHECK(fd, & info->version, sizeof(info->version)); + info->version = ntohs(info->version); + if (info->version > SIMCLIST_DUMPFORMAT_VERSION) { + errno = EILSEQ; + return -1; + } + + /* timestamp.tv_sec and timestamp.tv_usec */ + READ_ERRCHECK(fd, & info->timestamp.tv_sec, sizeof(info->timestamp.tv_sec)); + info->timestamp.tv_sec = ntohl(info->timestamp.tv_sec); + READ_ERRCHECK(fd, & info->timestamp.tv_usec, sizeof(info->timestamp.tv_usec)); + info->timestamp.tv_usec = ntohl(info->timestamp.tv_usec); + + /* list terminator (to check thereafter) */ + READ_ERRCHECK(fd, & terminator_head, sizeof(terminator_head)); + terminator_head = ntohl(terminator_head); + + /* list size */ + READ_ERRCHECK(fd, & info->list_size, sizeof(info->list_size)); + info->list_size = ntohl(info->list_size); + + /* number of elements */ + READ_ERRCHECK(fd, & info->list_numels, sizeof(info->list_numels)); + info->list_numels = ntohl(info->list_numels); + + /* length of each element (for checking for consistency) */ + READ_ERRCHECK(fd, & elemlen, sizeof(elemlen)); + elemlen = ntohl(elemlen); + + /* list hash */ + READ_ERRCHECK(fd, & info->list_hash, sizeof(info->list_hash)); + info->list_hash = ntohl(info->list_hash); + + /* check consistency */ + if (elemlen > 0) { + /* constant length, hop by size only */ + hop = info->list_size; + } else { + /* non-constant length, hop by size + all element length blocks */ + hop = info->list_size + elemlen*info->list_numels; + } + if (lseek(fd, hop, SEEK_CUR) == -1) { + return -1; + } + + /* read the trailing value and compare with terminator_head */ + READ_ERRCHECK(fd, & terminator_tail, sizeof(terminator_tail)); + terminator_tail = ntohl(terminator_tail); + + if (terminator_head == terminator_tail) + info->consistent = 1; + else + info->consistent = 0; + + return 0; +} + +int list_dump_getinfo_file(const char *restrict filename, list_dump_info_t *restrict info) { + int fd, ret; + + fd = open(filename, O_RDONLY, 0); + if (fd < 0) return -1; + + ret = list_dump_getinfo_filedescriptor(fd, info); + close(fd); + + return ret; +} + +int list_dump_filedescriptor(const list_t *restrict l, int fd, size_t *restrict len) { + struct list_entry_s *x; + void *ser_buf; + uint32_t bufsize; + struct timeval timeofday; + struct list_dump_header_s header; + + if (l->attrs.meter == NULL && l->attrs.serializer == NULL) { + errno = ENOTTY; + return -1; + } + + /**** DUMP FORMAT **** + + [ ver timestamp | totlen numels elemlen hash | DATA ] + + where DATA can be: + @ for constant-size list (element size is constant; elemlen > 0) + [ elem elem ... elem ] + @ for other lists (element size dictated by element_meter each time; elemlen <= 0) + [ size elem size elem ... size elem ] + + all integers are encoded in NETWORK BYTE FORMAT + *****/ + + + /* prepare HEADER */ + /* version */ + header.ver = htons( SIMCLIST_DUMPFORMAT_VERSION ); + + /* timestamp */ + gettimeofday(&timeofday, NULL); + header.timestamp_sec = htonl(timeofday.tv_sec); + header.timestamp_usec = htonl(timeofday.tv_usec); + + header.rndterm = htonl((int32_t)get_random()); + + /* total list size is postprocessed afterwards */ + + /* number of elements */ + header.numels = htonl(l->numels); + + /* include an hash, if possible */ + if (l->attrs.hasher != NULL) { + if (htonl(list_hash(l, & header.listhash)) != 0) { + /* could not compute list hash! */ + return -1; + } + } else { + header.listhash = htonl(0); + } + + header.totlistlen = header.elemlen = 0; + + /* leave room for the header at the beginning of the file */ + if (lseek(fd, SIMCLIST_DUMPFORMAT_HEADERLEN, SEEK_SET) < 0) { + /* errno set by lseek() */ + return -1; + } + + /* write CONTENT */ + if (l->numels > 0) { + /* SPECULATE that the list has constant element size */ + + if (l->attrs.serializer != NULL) { /* user user-specified serializer */ + /* get preliminary length of serialized element in header.elemlen */ + ser_buf = l->attrs.serializer(l->head_sentinel->next->data, & header.elemlen); + free(ser_buf); + /* request custom serialization of each element */ + for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) { + ser_buf = l->attrs.serializer(x->data, &bufsize); + header.totlistlen += bufsize; + if (header.elemlen != 0) { /* continue on speculation */ + if (header.elemlen != bufsize) { + free(ser_buf); + /* constant element length speculation broken! */ + header.elemlen = 0; + header.totlistlen = 0; + x = l->head_sentinel; + if (lseek(fd, SIMCLIST_DUMPFORMAT_HEADERLEN, SEEK_SET) < 0) { + /* errno set by lseek() */ + return -1; + } + /* restart from the beginning */ + continue; + } + /* speculation confirmed */ + WRITE_ERRCHECK(fd, ser_buf, bufsize); + } else { /* speculation found broken */ + WRITE_ERRCHECK(fd, & bufsize, sizeof(size_t)); + WRITE_ERRCHECK(fd, ser_buf, bufsize); + } + free(ser_buf); + } + } else if (l->attrs.meter != NULL) { + header.elemlen = (uint32_t)l->attrs.meter(l->head_sentinel->next->data); + + /* serialize the element straight from its data */ + for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) { + bufsize = l->attrs.meter(x->data); + header.totlistlen += bufsize; + if (header.elemlen != 0) { + if (header.elemlen != bufsize) { + /* constant element length speculation broken! */ + header.elemlen = 0; + header.totlistlen = 0; + x = l->head_sentinel; + /* restart from the beginning */ + continue; + } + WRITE_ERRCHECK(fd, x->data, bufsize); + } else { + WRITE_ERRCHECK(fd, &bufsize, sizeof(size_t)); + WRITE_ERRCHECK(fd, x->data, bufsize); + } + } + } + /* adjust endianness */ + header.elemlen = htonl(header.elemlen); + header.totlistlen = htonl(header.totlistlen); + } + + /* write random terminator */ + WRITE_ERRCHECK(fd, & header.rndterm, sizeof(header.rndterm)); /* list terminator */ + + + /* write header */ + lseek(fd, 0, SEEK_SET); + + WRITE_ERRCHECK(fd, & header.ver, sizeof(header.ver)); /* version */ + WRITE_ERRCHECK(fd, & header.timestamp_sec, sizeof(header.timestamp_sec)); /* timestamp seconds */ + WRITE_ERRCHECK(fd, & header.timestamp_usec, sizeof(header.timestamp_usec)); /* timestamp microseconds */ + WRITE_ERRCHECK(fd, & header.rndterm, sizeof(header.rndterm)); /* random terminator */ + + WRITE_ERRCHECK(fd, & header.totlistlen, sizeof(header.totlistlen)); /* total length of elements */ + WRITE_ERRCHECK(fd, & header.numels, sizeof(header.numels)); /* number of elements */ + WRITE_ERRCHECK(fd, & header.elemlen, sizeof(header.elemlen)); /* size of each element, or 0 for independent */ + WRITE_ERRCHECK(fd, & header.listhash, sizeof(header.listhash)); /* list hash, or 0 for "ignore" */ + + + /* possibly store total written length in "len" */ + if (len != NULL) { + *len = sizeof(header) + ntohl(header.totlistlen); + } + + return 0; +} + +int list_restore_filedescriptor(list_t *restrict l, int fd, size_t *restrict len) { + struct list_dump_header_s header; + unsigned long cnt; + void *buf; + uint32_t elsize, totreadlen, totmemorylen; + + memset(& header, 0, sizeof(header)); + + /* read header */ + + /* version */ + READ_ERRCHECK(fd, &header.ver, sizeof(header.ver)); + header.ver = ntohs(header.ver); + if (header.ver != SIMCLIST_DUMPFORMAT_VERSION) { + errno = EILSEQ; + return -1; + } + + /* timestamp */ + READ_ERRCHECK(fd, & header.timestamp_sec, sizeof(header.timestamp_sec)); + header.timestamp_sec = ntohl(header.timestamp_sec); + READ_ERRCHECK(fd, & header.timestamp_usec, sizeof(header.timestamp_usec)); + header.timestamp_usec = ntohl(header.timestamp_usec); + + /* list terminator */ + READ_ERRCHECK(fd, & header.rndterm, sizeof(header.rndterm)); + + header.rndterm = ntohl(header.rndterm); + + /* total list size */ + READ_ERRCHECK(fd, & header.totlistlen, sizeof(header.totlistlen)); + header.totlistlen = ntohl(header.totlistlen); + + /* number of elements */ + READ_ERRCHECK(fd, & header.numels, sizeof(header.numels)); + header.numels = ntohl(header.numels); + + /* length of every element, or '0' = variable */ + READ_ERRCHECK(fd, & header.elemlen, sizeof(header.elemlen)); + header.elemlen = ntohl(header.elemlen); + + /* list hash, or 0 = 'ignore' */ + READ_ERRCHECK(fd, & header.listhash, sizeof(header.listhash)); + header.listhash = ntohl(header.listhash); + + + /* read content */ + totreadlen = totmemorylen = 0; + if (header.elemlen > 0) { + /* elements have constant size = header.elemlen */ + if (l->attrs.unserializer != NULL) { + /* use unserializer */ + buf = malloc(header.elemlen); + for (cnt = 0; cnt < header.numels; cnt++) { + READ_ERRCHECK(fd, buf, header.elemlen); + list_append(l, l->attrs.unserializer(buf, & elsize)); + totmemorylen += elsize; + } + } else { + /* copy verbatim into memory */ + for (cnt = 0; cnt < header.numels; cnt++) { + buf = malloc(header.elemlen); + READ_ERRCHECK(fd, buf, header.elemlen); + list_append(l, buf); + } + totmemorylen = header.numels * header.elemlen; + } + totreadlen = header.numels * header.elemlen; + } else { + /* elements have variable size. Each element is preceded by its size */ + if (l->attrs.unserializer != NULL) { + /* use unserializer */ + for (cnt = 0; cnt < header.numels; cnt++) { + READ_ERRCHECK(fd, & elsize, sizeof(elsize)); + buf = malloc((size_t)elsize); + READ_ERRCHECK(fd, buf, elsize); + totreadlen += elsize; + list_append(l, l->attrs.unserializer(buf, & elsize)); + totmemorylen += elsize; + } + } else { + /* copy verbatim into memory */ + for (cnt = 0; cnt < header.numels; cnt++) { + READ_ERRCHECK(fd, & elsize, sizeof(elsize)); + buf = malloc(elsize); + READ_ERRCHECK(fd, buf, elsize); + totreadlen += elsize; + list_append(l, buf); + } + totmemorylen = totreadlen; + } + } + + READ_ERRCHECK(fd, &elsize, sizeof(elsize)); /* read list terminator */ + elsize = ntohl(elsize); + + /* possibly verify the list consistency */ + /* wrt hash */ + /* don't do that + if (header.listhash != 0 && header.listhash != list_hash(l)) { + errno = ECANCELED; + return -1; + } + */ + + /* wrt header */ + if (totreadlen != header.totlistlen && (int32_t)elsize == header.rndterm) { + errno = EPROTO; + return -1; + } + + /* wrt file */ + if (lseek(fd, 0, SEEK_CUR) != lseek(fd, 0, SEEK_END)) { + errno = EPROTO; + return -1; + } + + if (len != NULL) { + *len = totmemorylen; + } + + return 0; +} + +int list_dump_file(const list_t *restrict l, const char *restrict filename, size_t *restrict len) { + int fd, oflag, mode; + +#ifndef _WIN32 + oflag = O_RDWR | O_CREAT | O_TRUNC; + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; +#else + oflag = _O_RDWR | _O_CREAT | _O_TRUNC; + mode = _S_IRUSR | _S_IWUSR | _S_IRGRP | _S_IROTH; +#endif + fd = open(filename, oflag, mode); + if (fd < 0) return -1; + + list_dump_filedescriptor(l, fd, len); + close(fd); + + return 0; +} + +int list_restore_file(list_t *restrict l, const char *restrict filename, size_t *restrict len) { + int fd; + + fd = open(filename, O_RDONLY, 0); + if (fd < 0) return -1; + + list_restore_filedescriptor(l, fd, len); + close(fd); + + return 0; +} +#endif /* ifndef SIMCLIST_NO_DUMPRESTORE */ + + +static int list_drop_elem(list_t *restrict l, struct list_entry_s *tmp, unsigned int pos) { + if (tmp == NULL) return -1; + + /* fix mid pointer. This is wrt the PRE situation */ + if (l->numels % 2) { /* now odd */ + /* sort out the base case by hand */ + if (l->numels == 1) l->mid = NULL; + else if (pos >= l->numels/2) l->mid = l->mid->prev; + } else { /* now even */ + if (pos < l->numels/2) l->mid = l->mid->next; + } + + tmp->prev->next = tmp->next; + tmp->next->prev = tmp->prev; + + /* free what's to be freed */ + if (l->attrs.copy_data && tmp->data != NULL) + free(tmp->data); + + if (l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS) { + l->spareels[l->spareelsnum++] = tmp; + } else { + free(tmp); + } + + return 0; +} + +/* ready-made comparators and meters */ +#define SIMCLIST_NUMBER_COMPARATOR(type) int list_comparator_##type(const void *a, const void *b) { return( *(type *)a < *(type *)b) - (*(type *)a > *(type *)b); } + +SIMCLIST_NUMBER_COMPARATOR(int8_t) +SIMCLIST_NUMBER_COMPARATOR(int16_t) +SIMCLIST_NUMBER_COMPARATOR(int32_t) +SIMCLIST_NUMBER_COMPARATOR(int64_t) + +SIMCLIST_NUMBER_COMPARATOR(uint8_t) +SIMCLIST_NUMBER_COMPARATOR(uint16_t) +SIMCLIST_NUMBER_COMPARATOR(uint32_t) +SIMCLIST_NUMBER_COMPARATOR(uint64_t) + +SIMCLIST_NUMBER_COMPARATOR(float) +SIMCLIST_NUMBER_COMPARATOR(double) + +int list_comparator_string(const void *a, const void *b) { return strcmp((const char *)b, (const char *)a); } + +/* ready-made metric functions */ +#define SIMCLIST_METER(type) size_t list_meter_##type(const void *el) { if (el) { /* kill compiler whinge */ } return sizeof(type); } + +SIMCLIST_METER(int8_t) +SIMCLIST_METER(int16_t) +SIMCLIST_METER(int32_t) +SIMCLIST_METER(int64_t) + +SIMCLIST_METER(uint8_t) +SIMCLIST_METER(uint16_t) +SIMCLIST_METER(uint32_t) +SIMCLIST_METER(uint64_t) + +SIMCLIST_METER(float) +SIMCLIST_METER(double) + +size_t list_meter_string(const void *el) { return strlen((const char *)el) + 1; } + +/* ready-made hashing functions */ +#define SIMCLIST_HASHCOMPUTER(type) list_hash_t list_hashcomputer_##type(const void *el) { return (list_hash_t)(*(type *)el); } + +SIMCLIST_HASHCOMPUTER(int8_t) +SIMCLIST_HASHCOMPUTER(int16_t) +SIMCLIST_HASHCOMPUTER(int32_t) +SIMCLIST_HASHCOMPUTER(int64_t) + +SIMCLIST_HASHCOMPUTER(uint8_t) +SIMCLIST_HASHCOMPUTER(uint16_t) +SIMCLIST_HASHCOMPUTER(uint32_t) +SIMCLIST_HASHCOMPUTER(uint64_t) + +SIMCLIST_HASHCOMPUTER(float) +SIMCLIST_HASHCOMPUTER(double) + +list_hash_t list_hashcomputer_string(const void *el) { + size_t l; + list_hash_t hash = 123; + const char *str = (const char *)el; + char plus; + + for (l = 0; str[l] != '\0'; l++) { + if (l) plus = hash ^ str[l]; + else plus = hash ^ (str[l] - str[0]); + hash += (plus << (CHAR_BIT * (l % sizeof(list_hash_t)))); + } + + return hash; +} + + +#ifndef NDEBUG +static int list_repOk(const list_t *restrict l) { + int ok, i; + struct list_entry_s *s; + + ok = (l != NULL) && ( + /* head/tail checks */ + (l->head_sentinel != NULL && l->tail_sentinel != NULL) && + (l->head_sentinel != l->tail_sentinel) && (l->head_sentinel->prev == NULL && l->tail_sentinel->next == NULL) && + /* empty list */ + (l->numels > 0 || (l->mid == NULL && l->head_sentinel->next == l->tail_sentinel && l->tail_sentinel->prev == l->head_sentinel)) && + /* spare elements checks */ + l->spareelsnum <= SIMCLIST_MAX_SPARE_ELEMS + ); + + if (!ok) return 0; + + if (l->numels >= 1) { + /* correct referencing */ + for (i = -1, s = l->head_sentinel; i < (int)(l->numels-1)/2 && s->next != NULL; i++, s = s->next) { + if (s->next->prev != s) break; + } + ok = (i == (int)(l->numels-1)/2 && l->mid == s); + if (!ok) return 0; + for (; s->next != NULL; i++, s = s->next) { + if (s->next->prev != s) break; + } + ok = (i == (int)l->numels && s == l->tail_sentinel); + } + + return ok; +} + +static int list_attrOk(const list_t *restrict l) { + int ok; + + ok = (l->attrs.copy_data == 0 || l->attrs.meter != NULL); + return ok; +} + +#endif + diff --git a/libs/libks/src/table.c b/libs/libks/src/table.c new file mode 100644 index 0000000000..4e28556eb9 --- /dev/null +++ b/libs/libks/src/table.c @@ -0,0 +1,4079 @@ +/* + * Generic hash table handler... + * + * Copyright 2000 by Gray Watson. + * + * This file is part of the table package. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies, and that the name of Gray Watson not be used in advertising + * or publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be reached via http://256.com/gray/ + * + * $Id: table.c,v 1.19 2000/03/09 03:30:41 gray Exp $ + */ + +/* + * Handles basic hash-table manipulations. This is an implementation + * of open hashing with an array of buckets holding linked lists of + * elements. Each element has a key and a data. The user indexes on + * the key to find the data. See the typedefs in table_loc.h for more + * information. + */ + +#include +#include +#include +#include + +#ifdef unix + +#include + +#else + +#include +#include +#define NO_MMAP +#define open _open + +#endif + +#ifndef NO_MMAP + +#include +#include + +#ifndef MAP_FAILED +#define MAP_FAILED (caddr_t)0L +#endif + +#endif + +#define TABLE_MAIN + +#include "table.h" +#include "table_loc.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +static char *rcs_id = + "$Id: table.c,v 1.19 2000/03/09 03:30:41 gray Exp $"; + +/* + * Version id for the library. You also need to add an entry to the + * NEWS and ChangeLog files. + */ +static char *version_id = "$TableVersion: 4.3.0 March 8, 2000 $"; + +/****************************** local functions ******************************/ + +/* + * static table_entry_t *first_entry + * + * DESCRIPTION: + * + * Return the first entry in the table. It will set the linear + * structure counter to the position of the first entry. + * + * RETURNS: + * + * Success: A pointer to the first entry in the table. + * + * Failure: NULL if there is no first entry. + * + * ARGUMENTS: + * + * table_p -> Table whose next entry we are finding. + * + * linear_p <-> Pointer to a linear structure which we will advance + * and then find the corresponding entry. + */ +static table_entry_t *first_entry(const table_t *table_p, + table_linear_t *linear_p) +{ + table_entry_t *entry_p; + unsigned int bucket_c = 0; + + /* look for the first non-empty bucket */ + for (bucket_c = 0; bucket_c < table_p->ta_bucket_n; bucket_c++) { + entry_p = table_p->ta_buckets[bucket_c]; + if (entry_p != NULL) { + if (linear_p != NULL) { + linear_p->tl_bucket_c = bucket_c; + linear_p->tl_entry_c = 0; + } + return TABLE_POINTER(table_p, table_entry_t *, entry_p); + } + } + + return NULL; +} + +/* + * static table_entry_t *next_entry + * + * DESCRIPTION: + * + * Return the next entry in the table which is past the position in + * our linear pointer. It will advance the linear structure counters. + * + * RETURNS: + * + * Success: A pointer to the next entry in the table. + * + * Failure: NULL. + * + * ARGUMENTS: + * + * table_p -> Table whose next entry we are finding. + * + * linear_p <-> Pointer to a linear structure which we will advance + * and then find the corresponding entry. + * + * error_p <- Pointer to an integer which when the routine returns + * will contain a table error code. + */ +static table_entry_t *next_entry(const table_t *table_p, + table_linear_t *linear_p, int *error_p) +{ + table_entry_t *entry_p; + int entry_c; + + /* can't next if we haven't first-ed */ + if (linear_p == NULL) { + SET_POINTER(error_p, TABLE_ERROR_LINEAR); + return NULL; + } + + if (linear_p->tl_bucket_c >= table_p->ta_bucket_n) { + /* + * NOTE: this might happen if we delete an item which shortens the + * table bucket numbers. + */ + SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); + return NULL; + } + + linear_p->tl_entry_c++; + + /* find the entry which is the nth in the list */ + entry_p = table_p->ta_buckets[linear_p->tl_bucket_c]; + /* NOTE: we swap the order here to be more efficient */ + for (entry_c = linear_p->tl_entry_c; entry_c > 0; entry_c--) { + /* did we reach the end of the list? */ + if (entry_p == NULL) { + break; + } + entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p)->te_next_p; + } + + /* did we find an entry in the current bucket? */ + if (entry_p != NULL) { + SET_POINTER(error_p, TABLE_ERROR_NONE); + return TABLE_POINTER(table_p, table_entry_t *, entry_p); + } + + /* find the first entry in the next non-empty bucket */ + + linear_p->tl_entry_c = 0; + for (linear_p->tl_bucket_c++; linear_p->tl_bucket_c < table_p->ta_bucket_n; + linear_p->tl_bucket_c++) { + entry_p = table_p->ta_buckets[linear_p->tl_bucket_c]; + if (entry_p != NULL) { + SET_POINTER(error_p, TABLE_ERROR_NONE); + return TABLE_POINTER(table_p, table_entry_t *, entry_p); + } + } + + SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); + return NULL; +} + +/* + * static table_entry_t *this_entry + * + * DESCRIPTION: + * + * Return the entry pointer in the table which is currently being + * indicated by our linear pointer. + * + * RETURNS: + * + * Success: A pointer to the next entry in the table. + * + * Failure: NULL. + * + * ARGUMENTS: + * + * table_p -> Table whose next entry we are finding. + * + * linear_p -> Pointer to a linear structure which we will find the + * corresponding entry. + * + * error_p <- Pointer to an integer which when the routine returns + * will contain a table error code. + */ +static table_entry_t *this_entry(const table_t *table_p, + const table_linear_t *linear_p, + int *error_p) +{ + table_entry_t *entry_p; + int entry_c; + + /* can't next if we haven't first-ed */ + if (linear_p == NULL) { + SET_POINTER(error_p, TABLE_ERROR_LINEAR); + return NULL; + } + + if (linear_p->tl_bucket_c >= table_p->ta_bucket_n) { + /* + * NOTE: this might happen if we delete an item which shortens the + * table bucket numbers. + */ + SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); + return NULL; + } + + /* find the entry which is the nth in the list */ + entry_p = table_p->ta_buckets[linear_p->tl_bucket_c]; + + /* NOTE: we swap the order here to be more efficient */ + for (entry_c = linear_p->tl_entry_c; entry_c > 0; entry_c--) { + /* did we reach the end of the list? */ + if (entry_p == NULL) { + break; + } + entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p)->te_next_p; + } + + /* did we find an entry in the current bucket? */ + if (entry_p == NULL) { + SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); + return NULL; + } + else { + SET_POINTER(error_p, TABLE_ERROR_NONE); + return TABLE_POINTER(table_p, table_entry_t *, entry_p); + } +} + +/* + * static unsigned int hash + * + * DESCRIPTION: + * + * Hash a variable-length key into a 32-bit value. Every bit of the + * key affects every bit of the return value. Every 1-bit and 2-bit + * delta achieves avalanche. About (6 * len + 35) instructions. The + * best hash table sizes are powers of 2. There is no need to use mod + * (sooo slow!). If you need less than 32 bits, use a bitmask. For + * example, if you need only 10 bits, do h = (h & hashmask(10)); In + * which case, the hash table should have hashsize(10) elements. + * + * By Bob Jenkins, 1996. bob_jenkins@compuserve.com. You may use + * this code any way you wish, private, educational, or commercial. + * It's free. See + * http://ourworld.compuserve.com/homepages/bob_jenkins/evahash.htm + * Use for hash table lookup, or anything where one collision in 2^^32 + * is acceptable. Do NOT use for cryptographic purposes. + * + * RETURNS: + * + * Returns a 32-bit hash value. + * + * ARGUMENTS: + * + * key - Key (the unaligned variable-length array of bytes) that we + * are hashing. + * + * length - Length of the key in bytes. + * + * init_val - Initialization value of the hash if you need to hash a + * number of strings together. For instance, if you are hashing N + * strings (unsigned char **)keys, do it like this: + * + * for (i=0, h=0; i= 12; len -= 12) { + a += (key_p[0] + + ((unsigned int)key_p[1] << 8) + + ((unsigned int)key_p[2] << 16) + + ((unsigned int)key_p[3] << 24)); + b += (key_p[4] + + ((unsigned int)key_p[5] << 8) + + ((unsigned int)key_p[6] << 16) + + ((unsigned int)key_p[7] << 24)); + c += (key_p[8] + + ((unsigned int)key_p[9] << 8) + + ((unsigned int)key_p[10] << 16) + + ((unsigned int)key_p[11] << 24)); + HASH_MIX(a,b,c); + key_p += 12; + } + + c += length; + + /* all the case statements fall through to the next */ + switch(len) { + case 11: + c += ((unsigned int)key_p[10] << 24); + case 10: + c += ((unsigned int)key_p[9] << 16); + case 9: + c += ((unsigned int)key_p[8] << 8); + /* the first byte of c is reserved for the length */ + case 8: + b += ((unsigned int)key_p[7] << 24); + case 7: + b += ((unsigned int)key_p[6] << 16); + case 6: + b += ((unsigned int)key_p[5] << 8); + case 5: + b += key_p[4]; + case 4: + a += ((unsigned int)key_p[3] << 24); + case 3: + a += ((unsigned int)key_p[2] << 16); + case 2: + a += ((unsigned int)key_p[1] << 8); + case 1: + a += key_p[0]; + /* case 0: nothing left to add */ + } + HASH_MIX(a, b, c); + + return c; +} + +/* + * static int entry_size + * + * DESCRIPTION: + * + * Calculates the appropriate size of an entry to include the key and + * data sizes as well as any associated alignment to the data. + * + * RETURNS: + * + * The associated size of the entry. + * + * ARGUMENTS: + * + * table_p - Table associated with the entries whose size we are + * determining. + * + * key_size - Size of the entry key. + * + * data - Size of the entry data. + */ +static int entry_size(const table_t *table_p, const unsigned int key_size, + const unsigned int data_size) +{ + int size, left; + + /* initial size -- key is already aligned if right after struct */ + size = sizeof(struct table_shell_st) + key_size; + + /* if there is no alignment then it is easy */ + if (table_p->ta_data_align == 0) { + return size + data_size; + } + + /* add in our alignement */ + left = size & (table_p->ta_data_align - 1); + if (left > 0) { + size += table_p->ta_data_align - left; + } + + /* we add the data size here after the alignment */ + size += data_size; + + return size; +} + +/* + * static unsigned char *entry_data_buf + * + * DESCRIPTION: + * + * Companion to the ENTRY_DATA_BUF macro but this handles any + * associated alignment to the data in the entry. + * + * NOTE: we assume here that the data-alignment is > 0. + * + * RETURNS: + * + * Pointer to the data segment of the entry. + * + * ARGUMENTS: + * + * table_p - Table associated with the entry. + * + * entry_p - Entry whose data pointer we are determining. + */ +static unsigned char *entry_data_buf(const table_t *table_p, + const table_entry_t *entry_p) +{ + const unsigned char *buf_p; + unsigned int size, pad; + + buf_p = entry_p->te_key_buf + entry_p->te_key_size; + + /* we need the size of the space before the data */ + size = sizeof(struct table_shell_st) + entry_p->te_key_size; + + /* add in our alignment */ + pad = size & (table_p->ta_data_align - 1); + if (pad > 0) { + pad = table_p->ta_data_align - pad; + } + + return (unsigned char *)buf_p + pad; +} + +/******************************* sort routines *******************************/ + +/* + * static int local_compare + * + * DESCRIPTION: + * + * Compare two entries by calling user's compare program or by using + * memcmp. + * + * RETURNS: + * + * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. + * + * ARGUMENTS: + * + * p1 - First entry pointer to compare. + * + * p2 - Second entry pointer to compare. + * + * compare - User comparison function. Ignored. + * + * table_p - Associated table being ordered. Ignored. + * + * err_bp - Pointer to an integer which will be set with 1 if an error + * has occurred. It cannot be NULL. + */ +static int local_compare(const void *p1, const void *p2, + table_compare_t compare, const table_t *table_p, + int *err_bp) +{ + const table_entry_t * const *ent1_p = p1, * const *ent2_p = p2; + int cmp; + unsigned int size; + + /* compare as many bytes as we can */ + size = (*ent1_p)->te_key_size; + if ((*ent2_p)->te_key_size < size) { + size = (*ent2_p)->te_key_size; + } + cmp = memcmp(ENTRY_KEY_BUF(*ent1_p), ENTRY_KEY_BUF(*ent2_p), size); + /* if common-size equal, then if next more bytes, it is larger */ + if (cmp == 0) { + cmp = (*ent1_p)->te_key_size - (*ent2_p)->te_key_size; + } + + *err_bp = 0; + return cmp; +} + +/* + * static int local_compare_pos + * + * DESCRIPTION: + * + * Compare two entries by calling user's compare program or by using + * memcmp. + * + * RETURNS: + * + * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. + * + * ARGUMENTS: + * + * p1 - First entry pointer to compare. + * + * p2 - Second entry pointer to compare. + * + * compare - User comparison function. Ignored. + * + * table_p - Associated table being ordered. + * + * err_bp - Pointer to an integer which will be set with 1 if an error + * has occurred. It cannot be NULL. + */ +static int local_compare_pos(const void *p1, const void *p2, + table_compare_t compare, + const table_t *table_p, int *err_bp) +{ + const table_linear_t *lin1_p = p1, *lin2_p = p2; + const table_entry_t *ent1_p, *ent2_p; + int cmp, ret; + unsigned int size; + + /* get entry pointers */ + ent1_p = this_entry(table_p, lin1_p, &ret); + ent2_p = this_entry(table_p, lin2_p, &ret); + if (ent1_p == NULL || ent2_p == NULL) { + *err_bp = 1; + return 0; + } + + /* compare as many bytes as we can */ + size = ent1_p->te_key_size; + if (ent2_p->te_key_size < size) { + size = ent2_p->te_key_size; + } + cmp = memcmp(ENTRY_KEY_BUF(ent1_p), ENTRY_KEY_BUF(ent2_p), size); + /* if common-size equal, then if next more bytes, it is larger */ + if (cmp == 0) { + cmp = ent1_p->te_key_size - ent2_p->te_key_size; + } + + *err_bp = 0; + return cmp; +} + +/* + * static int external_compare + * + * DESCRIPTION: + * + * Compare two entries by calling user's compare program or by using + * memcmp. + * + * RETURNS: + * + * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. + * + * ARGUMENTS: + * + * p1 - First entry pointer to compare. + * + * p2 - Second entry pointer to compare. + * + * user_compare - User comparison function. + * + * table_p - Associated table being ordered. + * + * err_bp - Pointer to an integer which will be set with 1 if an error + * has occurred. It cannot be NULL. + */ +static int external_compare(const void *p1, const void *p2, + table_compare_t user_compare, + const table_t *table_p, int *err_bp) +{ + const table_entry_t * const *ent1_p = p1, * const *ent2_p = p2; + /* since we know we are not aligned we can use the EXTRY_DATA_BUF macro */ + *err_bp = 0; + return user_compare(ENTRY_KEY_BUF(*ent1_p), (*ent1_p)->te_key_size, + ENTRY_DATA_BUF(table_p, *ent1_p), + (*ent1_p)->te_data_size, + ENTRY_KEY_BUF(*ent2_p), (*ent2_p)->te_key_size, + ENTRY_DATA_BUF(table_p, *ent2_p), + (*ent2_p)->te_data_size); +} + +/* + * static int external_compare_pos + * + * DESCRIPTION: + * + * Compare two entries by calling user's compare program or by using + * memcmp. + * + * RETURNS: + * + * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. + * + * ARGUMENTS: + * + * p1 - First entry pointer to compare. + * + * p2 - Second entry pointer to compare. + * + * user_compare - User comparison function. + * + * table_p - Associated table being ordered. + * + * err_bp - Pointer to an integer which will be set with 1 if an error + * has occurred. It cannot be NULL. +*/ +static int external_compare_pos(const void *p1, const void *p2, + table_compare_t user_compare, + const table_t *table_p, int *err_bp) +{ + const table_linear_t *lin1_p = p1, *lin2_p = p2; + const table_entry_t *ent1_p, *ent2_p; + int ret; + + /* get entry pointers */ + ent1_p = this_entry(table_p, lin1_p, &ret); + ent2_p = this_entry(table_p, lin2_p, &ret); + if (ent1_p == NULL || ent2_p == NULL) { + *err_bp = 1; + return 0; + } + + /* since we know we are not aligned we can use the EXTRY_DATA_BUF macro */ + *err_bp = 0; + return user_compare(ENTRY_KEY_BUF(ent1_p), (ent1_p)->te_key_size, + ENTRY_DATA_BUF(table_p, ent1_p), ent1_p->te_data_size, + ENTRY_KEY_BUF(ent2_p), ent2_p->te_key_size, + ENTRY_DATA_BUF(table_p, ent2_p), ent2_p->te_data_size); +} + +/* + * static int external_compare_align + * + * DESCRIPTION: + * + * Compare two entries by calling user's compare program or by using + * memcmp. Alignment information is necessary. + * + * RETURNS: + * + * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. + * + * ARGUMENTS: + * + * p1 - First entry pointer to compare. + * + * p2 - Second entry pointer to compare. + * + * user_compare - User comparison function. + * + * table_p - Associated table being ordered. + * + * err_bp - Pointer to an integer which will be set with 1 if an error + * has occurred. It cannot be NULL. + */ +static int external_compare_align(const void *p1, const void *p2, + table_compare_t user_compare, + const table_t *table_p, int *err_bp) +{ + const table_entry_t * const *ent1_p = p1, * const *ent2_p = p2; + /* since we are aligned we have to use the entry_data_buf function */ + *err_bp = 0; + return user_compare(ENTRY_KEY_BUF(*ent1_p), (*ent1_p)->te_key_size, + entry_data_buf(table_p, *ent1_p), + (*ent1_p)->te_data_size, + ENTRY_KEY_BUF(*ent2_p), (*ent2_p)->te_key_size, + entry_data_buf(table_p, *ent2_p), + (*ent2_p)->te_data_size); +} + +/* + * static int external_compare_align_pos + * + * DESCRIPTION: + * + * Compare two entries by calling user's compare program or by using + * memcmp. Alignment information is necessary. + * + * RETURNS: + * + * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. + * + * ARGUMENTS: + * + * p1 - First entry pointer to compare. + * + * p2 - Second entry pointer to compare. + * + * user_compare - User comparison function. + * + * table_p - Associated table being ordered. + * + * err_bp - Pointer to an integer which will be set with 1 if an error + * has occurred. It cannot be NULL. + */ +static int external_compare_align_pos(const void *p1, const void *p2, + table_compare_t user_compare, + const table_t *table_p, int *err_bp) +{ + const table_linear_t *lin1_p = p1, *lin2_p = p2; + const table_entry_t *ent1_p, *ent2_p; + int ret; + + /* get entry pointers */ + ent1_p = this_entry(table_p, lin1_p, &ret); + ent2_p = this_entry(table_p, lin2_p, &ret); + if (ent1_p == NULL || ent2_p == NULL) { + *err_bp = 1; + return 0; + } + + /* since we are aligned we have to use the entry_data_buf function */ + *err_bp = 0; + return user_compare(ENTRY_KEY_BUF(ent1_p), ent1_p->te_key_size, + entry_data_buf(table_p, ent1_p), ent1_p->te_data_size, + ENTRY_KEY_BUF(ent2_p), ent2_p->te_key_size, + entry_data_buf(table_p, ent2_p), ent2_p->te_data_size); +} + +/* + * static void swap_bytes + * + * DESCRIPTION: + * + * Swap the values between two items of a specified size. + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * item1_p -> Pointer to the first item. + * + * item2_p -> Pointer to the first item. + * + * ele_size -> Size of the two items. + */ +static void swap_bytes(unsigned char *item1_p, unsigned char *item2_p, + int ele_size) +{ + unsigned char char_temp; + + for (; ele_size > 0; ele_size--) { + char_temp = *item1_p; + *item1_p = *item2_p; + *item2_p = char_temp; + item1_p++; + item2_p++; + } +} + +/* + * static void insert_sort + * + * DESCRIPTION: + * + * Do an insertion sort which is faster for small numbers of items and + * better if the items are already sorted. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * first_p <-> Start of the list that we are splitting. + * + * last_p <-> Last entry in the list that we are splitting. + * + * holder_p <-> Location of hold area we can store an entry. + * + * ele_size -> Size of the each element in the list. + * + * compare -> Our comparison function. + * + * user_compare -> User comparison function. Could be NULL if we are + * just using a local comparison function. + * + * table_p -> Associated table being sorted. + */ +static int insert_sort(unsigned char *first_p, unsigned char *last_p, + unsigned char *holder_p, + const unsigned int ele_size, compare_t compare, + table_compare_t user_compare, table_t *table_p) +{ + unsigned char *inner_p, *outer_p; + int ret, err_b; + + for (outer_p = first_p + ele_size; outer_p <= last_p; ) { + + /* look for the place to insert the entry */ + for (inner_p = outer_p - ele_size; + inner_p >= first_p; + inner_p -= ele_size) { + ret = compare(outer_p, inner_p, user_compare, table_p, &err_b); + if (err_b) { + return TABLE_ERROR_COMPARE; + } + if (ret >= 0) { + break; + } + } + inner_p += ele_size; + + /* do we need to insert the entry in? */ + if (outer_p != inner_p) { + /* + * Now we shift the entry down into its place in the already + * sorted list. + */ + memcpy(holder_p, outer_p, ele_size); + memmove(inner_p + ele_size, inner_p, outer_p - inner_p); + memcpy(inner_p, holder_p, ele_size); + } + + outer_p += ele_size; + } + + return TABLE_ERROR_NONE; +} + +/* + * static int split + * + * DESCRIPTION: + * + * This sorts an array of longs via the quick sort algorithm (it's + * pretty quick) + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * first_p -> Start of the list that we are splitting. + * + * last_p -> Last entry in the list that we are splitting. + * + * ele_size -> Size of the each element in the list. + * + * compare -> Our comparison function. + * + * user_compare -> User comparison function. Could be NULL if we are + * just using a local comparison function. + * + * table_p -> Associated table being sorted. + */ +static int split(unsigned char *first_p, unsigned char *last_p, + const unsigned int ele_size, compare_t compare, + table_compare_t user_compare, table_t *table_p) +{ + unsigned char *left_p, *right_p, *pivot_p, *left_last_p, *right_first_p; + unsigned char *firsts[MAX_QSORT_SPLITS], *lasts[MAX_QSORT_SPLITS], *pivot; + unsigned int width, split_c = 0; + int size1, size2, min_qsort_size; + int ret, err_b; + + /* + * Allocate some space for our pivot value. We also use this as + * holder space for our insert sort. + */ + pivot = alloca(ele_size); + if (pivot == NULL) { + /* what else can we do? */ + abort(); + } + + min_qsort_size = MAX_QSORT_MANY * ele_size; + + while (1) { + + /* find the left, right, and mid point */ + left_p = first_p; + right_p = last_p; + /* is there a faster way to find this? */ + width = (last_p - first_p) / ele_size; + pivot_p = first_p + ele_size * (width >> 1); + + /* + * Find which of the left, middle, and right elements is the + * median (Knuth vol3 p123). + */ + ret = compare(first_p, pivot_p, user_compare, table_p, &err_b); + if (err_b) { + return TABLE_ERROR_COMPARE; + } + if (ret > 0) { + swap_bytes(first_p, pivot_p, ele_size); + } + ret = compare(pivot_p, last_p, user_compare, table_p, &err_b); + if (err_b) { + return TABLE_ERROR_COMPARE; + } + if (ret > 0) { + swap_bytes(pivot_p, last_p, ele_size); + ret = compare(first_p, pivot_p, user_compare, table_p, &err_b); + if (err_b) { + return TABLE_ERROR_COMPARE; + } + if (ret > 0) { + swap_bytes(first_p, pivot_p, ele_size); + } + } + + /* + * save our pivot so we don't have to worry about hitting and + * swapping it elsewhere while we iterate across the list below. + */ + memcpy(pivot, pivot_p, ele_size); + + do { + + /* shift the left side up until we reach the pivot value */ + while (1) { + ret = compare(left_p, pivot, user_compare, table_p, &err_b); + if (err_b) { + return TABLE_ERROR_COMPARE; + } + if (ret >= 0) { + break; + } + left_p += ele_size; + } + /* shift the right side down until we reach the pivot value */ + while (1) { + ret = compare(pivot, right_p, user_compare, table_p, &err_b); + if (err_b) { + return TABLE_ERROR_COMPARE; + } + if (ret >= 0) { + break; + } + right_p -= ele_size; + } + + /* if we met in the middle then we are done */ + if (left_p == right_p) { + left_p += ele_size; + right_p -= ele_size; + break; + } + else if (left_p < right_p) { + /* + * swap the left and right since they both were on the wrong + * size of the pivot and continue + */ + swap_bytes(left_p, right_p, ele_size); + left_p += ele_size; + right_p -= ele_size; + } + } while (left_p <= right_p); + + /* Rename variables to make more sense. This will get optimized out. */ + right_first_p = left_p; + left_last_p = right_p; + + /* determine the size of the left and right hand parts */ + size1 = left_last_p - first_p; + size2 = last_p - right_first_p; + + /* is the 1st half small enough to just insert-sort? */ + if (size1 < min_qsort_size) { + + /* use the pivot as our temporary space */ + ret = insert_sort(first_p, left_last_p, pivot, ele_size, compare, + user_compare, table_p); + if (ret != TABLE_ERROR_NONE) { + return ret; + } + + /* is the 2nd part small as well? */ + if (size2 < min_qsort_size) { + + /* use the pivot as our temporary space */ + ret = insert_sort(right_first_p, last_p, pivot, ele_size, compare, + user_compare, table_p); + if (ret != TABLE_ERROR_NONE) { + return ret; + } + + /* pop a partition off our stack */ + if (split_c == 0) { + /* we are done */ + return TABLE_ERROR_NONE; + } + split_c--; + first_p = firsts[split_c]; + last_p = lasts[split_c]; + } + else { + /* we can just handle the right side immediately */ + first_p = right_first_p; + /* last_p = last_p */ + } + } + else if (size2 < min_qsort_size) { + + /* use the pivot as our temporary space */ + ret = insert_sort(right_first_p, last_p, pivot, ele_size, compare, + user_compare, table_p); + if (ret != TABLE_ERROR_NONE) { + return ret; + } + + /* we can just handle the left side immediately */ + /* first_p = first_p */ + last_p = left_last_p; + } + else { + /* + * neither partition is small, we'll have to push the larger one + * of them on the stack + */ + if (split_c >= MAX_QSORT_SPLITS) { + /* sanity check here -- we should never get here */ + abort(); + } + if (size1 > size2) { + /* push the left partition on the stack */ + firsts[split_c] = first_p; + lasts[split_c] = left_last_p; + split_c++; + /* continue handling the right side */ + first_p = right_first_p; + /* last_p = last_p */ + } + else { + /* push the right partition on the stack */ + firsts[split_c] = right_first_p; + lasts[split_c] = last_p; + split_c++; + /* continue handling the left side */ + /* first_p = first_p */ + last_p = left_last_p; + } + } + } + + return TABLE_ERROR_NONE; +} + +/*************************** exported routines *******************************/ + +/* + * table_t *table_alloc + * + * DESCRIPTION: + * + * Allocate a new table structure. + * + * RETURNS: + * + * A pointer to the new table structure which must be passed to + * table_free to be deallocated. On error a NULL is returned. + * + * ARGUMENTS: + * + * bucket_n - Number of buckets for the hash table. Our current hash + * value works best with base two numbers. Set to 0 to take the + * library default of 1024. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +table_t *table_alloc(const unsigned int bucket_n, int *error_p) +{ + table_t *table_p = NULL; + unsigned int buck_n; + + /* allocate a table structure */ + table_p = malloc(sizeof(table_t)); + if (table_p == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ALLOC); + return NULL; + } + + if (bucket_n > 0) { + buck_n = bucket_n; + } + else { + buck_n = DEFAULT_SIZE; + } + + /* allocate the buckets which are NULLed */ + table_p->ta_buckets = (table_entry_t **)calloc(buck_n, + sizeof(table_entry_t *)); + if (table_p->ta_buckets == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ALLOC); + free(table_p); + return NULL; + } + + /* initialize structure */ + table_p->ta_magic = TABLE_MAGIC; + table_p->ta_flags = 0; + table_p->ta_bucket_n = buck_n; + table_p->ta_entry_n = 0; + table_p->ta_data_align = 0; + table_p->ta_linear.tl_magic = 0; + table_p->ta_linear.tl_bucket_c = 0; + table_p->ta_linear.tl_entry_c = 0; + table_p->ta_mmap = NULL; + table_p->ta_file_size = 0; + table_p->ta_mem_pool = NULL; + table_p->ta_alloc_func = NULL; + table_p->ta_resize_func = NULL; + table_p->ta_free_func = NULL; + + SET_POINTER(error_p, TABLE_ERROR_NONE); + return table_p; +} + +/* + * table_t *table_alloc_in_pool + * + * DESCRIPTION: + * + * Allocate a new table structure in a memory pool or using + * alternative allocation and free functions. + * + * RETURNS: + * + * A pointer to the new table structure which must be passed to + * table_free to be deallocated. On error a NULL is returned. + * + * ARGUMENTS: + * + * bucket_n - Number of buckets for the hash table. Our current hash + * value works best with base two numbers. Set to 0 to take the + * library default of 1024. + * + * mem_pool <-> Memory pool to associate with the table. Can be NULL. + * + * alloc_func -> Allocate function we are overriding malloc() with. + * + * resize_func -> Resize function we are overriding the standard + * memory resize/realloc with. This can be NULL in which cause the + * library will allocate, copy, and free itself. + * + * free_func -> Free function we are overriding free() with. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +table_t *table_alloc_in_pool(const unsigned int bucket_n, + void *mem_pool, + table_mem_alloc_t alloc_func, + table_mem_resize_t resize_func, + table_mem_free_t free_func, int *error_p) +{ + table_t *table_p = NULL; + unsigned int buck_n, size; + + /* make sure we have real functions, mem_pool and resize_func can be NULL */ + if (alloc_func == NULL || free_func == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ARG_NULL); + return NULL; + } + + /* allocate a table structure */ + table_p = alloc_func(mem_pool, sizeof(table_t)); + if (table_p == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ALLOC); + return NULL; + } + + if (bucket_n > 0) { + buck_n = bucket_n; + } + else { + buck_n = DEFAULT_SIZE; + } + + /* allocate the buckets which are NULLed */ + size = buck_n * sizeof(table_entry_t *); + table_p->ta_buckets = (table_entry_t **)alloc_func(mem_pool, size); + if (table_p->ta_buckets == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ALLOC); + (void)free_func(mem_pool, table_p, sizeof(table_t)); + return NULL; + } + /* + * We zero it ourselves to save the necessity of having a + * table_mem_calloc_t memory override function. + */ + memset(table_p->ta_buckets, 0, size); + + /* initialize structure */ + table_p->ta_magic = TABLE_MAGIC; + table_p->ta_flags = 0; + table_p->ta_bucket_n = buck_n; + table_p->ta_entry_n = 0; + table_p->ta_data_align = 0; + table_p->ta_linear.tl_magic = 0; + table_p->ta_linear.tl_bucket_c = 0; + table_p->ta_linear.tl_entry_c = 0; + table_p->ta_mmap = NULL; + table_p->ta_file_size = 0; + table_p->ta_mem_pool = mem_pool; + table_p->ta_alloc_func = alloc_func; + table_p->ta_resize_func = resize_func; + table_p->ta_free_func = free_func; + + SET_POINTER(error_p, TABLE_ERROR_NONE); + return table_p; +} + +/* + * int table_attr + * + * DESCRIPTION: + * + * Set the attributes for the table. The available attributes are + * specified at the top of table.h. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to a table structure which we will be altering. + * + * attr - Attribute(s) that we will be applying to the table. + */ +int table_attr(table_t *table_p, const int attr) +{ + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + + table_p->ta_flags = attr; + + return TABLE_ERROR_NONE; +} + +/* + * int table_set_data_alignment + * + * DESCRIPTION: + * + * Set the alignment for the data in the table. This is used when you + * want to store binary data types and refer to them directly out of + * the table storage. For instance if you are storing integers as + * data in the table and want to be able to retrieve the location of + * the interger and then increment it as (*loc_p)++. Otherwise you + * would have to memcpy it out to an integer, increment it, and memcpy + * it back. If you are storing character data, no alignment is + * necessary. + * + * For most data elements, sizeof(long) is recommended unless you use + * smaller data types exclusively. + * + * WARNING: If necessary, you must set the data alignment before any + * data gets put into the table. Otherwise a TABLE_ERROR_NOT_EMPTY + * error will be returned. + * + * NOTE: there is no way to set the key data alignment although it + * should automatically be long aligned. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to a table structure which we will be altering. + * + * alignment - Alignment requested for the data. Must be a power of + * 2. Set to 0 for none. + */ +int table_set_data_alignment(table_t *table_p, const int alignment) +{ + int val; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (table_p->ta_entry_n > 0) { + return TABLE_ERROR_NOT_EMPTY; + } + + /* defaults */ + if (alignment < 2) { + table_p->ta_data_align = 0; + } + else { + /* verify we have a base 2 number */ + for (val = 2; val < MAX_ALIGNMENT; val *= 2) { + if (val == alignment) { + break; + } + } + if (val >= MAX_ALIGNMENT) { + return TABLE_ERROR_ALIGNMENT; + } + table_p->ta_data_align = alignment; + } + + return TABLE_ERROR_NONE; +} + +/* + * int table_clear + * + * DESCRIPTION: + * + * Clear out and free all elements in a table structure. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer that we will be clearing. + */ +int table_clear(table_t *table_p) +{ + int final = TABLE_ERROR_NONE; + table_entry_t *entry_p, *next_p; + table_entry_t **bucket_p, **bounds_p; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + +#ifndef NO_MMAP + /* no mmap support so immediate error */ + if (table_p->ta_mmap != NULL) { + return TABLE_ERROR_MMAP_OP; + } +#endif + + /* free the table allocation and table structure */ + bounds_p = table_p->ta_buckets + table_p->ta_bucket_n; + for (bucket_p = table_p->ta_buckets; bucket_p < bounds_p; bucket_p++) { + for (entry_p = *bucket_p; entry_p != NULL; entry_p = next_p) { + /* record the next pointer before we free */ + next_p = entry_p->te_next_p; + if (table_p->ta_free_func == NULL) { + free(entry_p); + } + else if (! table_p->ta_free_func(table_p->ta_mem_pool, entry_p, + entry_size(table_p, + entry_p->te_key_size, + entry_p->te_data_size))) { + final = TABLE_ERROR_FREE; + } + } + + /* clear the bucket entry after we free its entries */ + *bucket_p = NULL; + } + + /* reset table state info */ + table_p->ta_entry_n = 0; + table_p->ta_linear.tl_magic = 0; + table_p->ta_linear.tl_bucket_c = 0; + table_p->ta_linear.tl_entry_c = 0; + + return final; +} + +/* + * int table_free + * + * DESCRIPTION: + * + * Deallocates a table structure. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer that we will be freeing. + */ +int table_free(table_t *table_p) +{ + int ret; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + +#ifndef NO_MMAP + /* no mmap support so immediate error */ + if (table_p->ta_mmap != NULL) { + return TABLE_ERROR_MMAP_OP; + } +#endif + + ret = table_clear(table_p); + + if (table_p->ta_buckets != NULL) { + if (table_p->ta_free_func == NULL) { + free(table_p->ta_buckets); + } + else if (! table_p->ta_free_func(table_p->ta_mem_pool, + table_p->ta_buckets, + table_p->ta_bucket_n * + sizeof(table_entry_t *))) { + return TABLE_ERROR_FREE; + } + } + table_p->ta_magic = 0; + if (table_p->ta_free_func == NULL) { + free(table_p); + } + else if (! table_p->ta_free_func(table_p->ta_mem_pool, table_p, + sizeof(table_t))) { + if (ret == TABLE_ERROR_NONE) { + ret = TABLE_ERROR_FREE; + } + } + + return ret; +} + +/* + * int table_insert_kd + * + * DESCRIPTION: + * + * Like table_insert except it passes back a pointer to the key and + * the data buffers after they have been inserted into the table + * structure. + * + * This routine adds a key/data pair both of which are made up of a + * buffer of bytes and an associated size. Both the key and the data + * will be copied into buffers allocated inside the table. If the key + * exists already, the associated data will be replaced if the + * overwrite flag is set, otherwise an error is returned. + * + * NOTE: be very careful changing the values since the table library + * provides the pointers to its memory. The key can _never_ be + * changed otherwise you will not find it again. The data can be + * changed but its length can never be altered unless you delete and + * re-insert it into the table. + * + * WARNING: The pointers to the key and data are not in any specific + * alignment. Accessing the key and/or data as an short, integer, or + * long pointer directly can cause problems. + * + * WARNING: Replacing a data cell (not inserting) will cause the table + * linked list to be temporarily invalid. Care must be taken with + * multiple threaded programs which are relying on the first/next + * linked list to be always valid. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer into which we will be inserting a + * new key/data pair. + * + * key_buf - Buffer of bytes of the key that we are inserting. If you + * are storing an (int) as the key (for example) then key_buf should + * be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are storing an (int) as the key (for example) then key_size should + * be sizeof(int). + * + * data_buf - Buffer of bytes of the data that we are inserting. If + * it is NULL then the library will allocate space for the data in the + * table without copying in any information. If data_buf is NULL and + * data_size is 0 then the library will associate a NULL data pointer + * with the key. If you are storing a (long) as the data (for + * example) then data_buf should be a (long *). + * + * data_size - Size of the data_buf buffer. If set to < 0 then the + * library will do a strlen of data_buf and add 1 for the '\0'. If + * you are storing an (long) as the key (for example) then key_size + * should be sizeof(long). + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the key storage that was allocated in the table. If you are + * storing an (int) as the key (for example) then key_buf_p should be + * (int **) i.e. the address of a (int *). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table. If you are + * storing an (long) as the data (for example) then data_buf_p should + * be (long **) i.e. the address of a (long *). + * + * overwrite - Flag which, if set to 1, will allow the overwriting of + * the data in the table with the new data if the key already exists + * in the table. + */ +int table_insert_kd(table_t *table_p, + const void *key_buf, const int key_size, + const void *data_buf, const int data_size, + void **key_buf_p, void **data_buf_p, + const char overwrite_b) +{ + int bucket; + unsigned int ksize, dsize, new_size, old_size, copy_size; + table_entry_t *entry_p, *last_p, *new_entry_p; + void *key_copy_p, *data_copy_p; + + /* check the arguments */ + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (key_buf == NULL) { + return TABLE_ERROR_ARG_NULL; + } + /* data_buf can be null but size must be >= 0, if it isn't null size != 0 */ + if ((data_buf == NULL && data_size < 0) + || (data_buf != NULL && data_size == 0)) { + return TABLE_ERROR_SIZE; + } + +#ifndef NO_MMAP + /* no mmap support so immediate error */ + if (table_p->ta_mmap != NULL) { + return TABLE_ERROR_MMAP_OP; + } +#endif + + /* determine sizes of key and data */ + if (key_size < 0) { + ksize = strlen((char *)key_buf) + sizeof(char); + } + else { + ksize = key_size; + } + if (data_size < 0) { + dsize = strlen((char *)data_buf) + sizeof(char); + } + else { + dsize = data_size; + } + + /* get the bucket number via a hash function */ + bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n; + + /* look for the entry in this bucket, only check keys of the same size */ + last_p = NULL; + for (entry_p = table_p->ta_buckets[bucket]; + entry_p != NULL; + last_p = entry_p, entry_p = entry_p->te_next_p) { + if (entry_p->te_key_size == ksize + && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0) { + break; + } + } + + /* did we find it? then we are in replace mode. */ + if (entry_p != NULL) { + + /* can we not overwrite existing data? */ + if (! overwrite_b) { + SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + if (table_p->ta_data_align == 0) { + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + } + return TABLE_ERROR_OVERWRITE; + } + + /* re-alloc entry's data if the new size != the old */ + if (dsize != entry_p->te_data_size) { + + /* + * First we delete it from the list to keep the list whole. + * This properly preserves the linked list in case we have a + * thread marching through the linked list while we are + * inserting. Maybe this is an unnecessary protection but it + * should not harm that much. + */ + if (last_p == NULL) { + table_p->ta_buckets[bucket] = entry_p->te_next_p; + } + else { + last_p->te_next_p = entry_p->te_next_p; + } + + /* + * Realloc the structure which may change its pointer. NOTE: + * this may change any previous data_key_p and data_copy_p + * pointers. + */ + new_size = entry_size(table_p, entry_p->te_key_size, dsize); + if (table_p->ta_resize_func == NULL) { + /* if the alloc function has not been overriden do realloc */ + if (table_p->ta_alloc_func == NULL) { + entry_p = (table_entry_t *)realloc(entry_p, new_size); + if (entry_p == NULL) { + return TABLE_ERROR_ALLOC; + } + } + else { + old_size = new_size - dsize + entry_p->te_data_size; + /* + * if the user did override alloc but not resize, assume + * that the user's allocation functions can't grok realloc + * and do it ourselves the hard way. + */ + new_entry_p = + (table_entry_t *)table_p->ta_alloc_func(table_p->ta_mem_pool, + new_size); + if (new_entry_p == NULL) { + return TABLE_ERROR_ALLOC; + } + if (new_size > old_size) { + copy_size = old_size; + } + else { + copy_size = new_size; + } + memcpy(new_entry_p, entry_p, copy_size); + if (! table_p->ta_free_func(table_p->ta_mem_pool, entry_p, + old_size)) { + return TABLE_ERROR_FREE; + } + entry_p = new_entry_p; + } + } + else { + old_size = new_size - dsize + entry_p->te_data_size; + entry_p = (table_entry_t *) + table_p->ta_resize_func(table_p->ta_mem_pool, entry_p, + old_size, new_size); + if (entry_p == NULL) { + return TABLE_ERROR_ALLOC; + } + } + + /* add it back to the front of the list */ + entry_p->te_data_size = dsize; + entry_p->te_next_p = table_p->ta_buckets[bucket]; + table_p->ta_buckets[bucket] = entry_p; + } + + /* copy or replace data in storage */ + if (dsize > 0) { + if (table_p->ta_data_align == 0) { + data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + data_copy_p = entry_data_buf(table_p, entry_p); + } + if (data_buf != NULL) { + memcpy(data_copy_p, data_buf, dsize); + } + } + else { + data_copy_p = NULL; + } + + SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); + SET_POINTER(data_buf_p, data_copy_p); + + /* returning from the section where we were overwriting table data */ + return TABLE_ERROR_NONE; + } + + /* + * It is a new entry. + */ + + /* allocate a new entry */ + new_size = entry_size(table_p, ksize, dsize); + if (table_p->ta_alloc_func == NULL) { + entry_p = (table_entry_t *)malloc(new_size); + } + else { + entry_p = + (table_entry_t *)table_p->ta_alloc_func(table_p->ta_mem_pool, new_size); + } + if (entry_p == NULL) { + return TABLE_ERROR_ALLOC; + } + + /* copy key into storage */ + entry_p->te_key_size = ksize; + key_copy_p = ENTRY_KEY_BUF(entry_p); + memcpy(key_copy_p, key_buf, ksize); + + /* copy data in */ + entry_p->te_data_size = dsize; + if (dsize > 0) { + if (table_p->ta_data_align == 0) { + data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + data_copy_p = entry_data_buf(table_p, entry_p); + } + if (data_buf != NULL) { + memcpy(data_copy_p, data_buf, dsize); + } + } + else { + data_copy_p = NULL; + } + + SET_POINTER(key_buf_p, key_copy_p); + SET_POINTER(data_buf_p, data_copy_p); + + /* insert into list, no need to append */ + entry_p->te_next_p = table_p->ta_buckets[bucket]; + table_p->ta_buckets[bucket] = entry_p; + + table_p->ta_entry_n++; + + /* do we need auto-adjust? */ + if ((table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST) + && SHOULD_TABLE_GROW(table_p)) { + return table_adjust(table_p, table_p->ta_entry_n); + } + + return TABLE_ERROR_NONE; +} + +/* + * int table_insert + * + * DESCRIPTION: + * + * Exactly the same as table_insert_kd except it does not pass back a + * pointer to the key after they have been inserted into the table + * structure. This is still here for backwards compatibility. + * + * See table_insert_kd for more information. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer into which we will be inserting a + * new key/data pair. + * + * key_buf - Buffer of bytes of the key that we are inserting. If you + * are storing an (int) as the key (for example) then key_buf should + * be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are storing an (int) as the key (for example) then key_size should + * be sizeof(int). + * + * data_buf - Buffer of bytes of the data that we are inserting. If + * it is NULL then the library will allocate space for the data in the + * table without copying in any information. If data_buf is NULL and + * data_size is 0 then the library will associate a NULL data pointer + * with the key. If you are storing a (long) as the data (for + * example) then data_buf should be a (long *). + * + * data_size - Size of the data_buf buffer. If set to < 0 then the + * library will do a strlen of data_buf and add 1 for the '\0'. If + * you are storing an (long) as the key (for example) then key_size + * should be sizeof(long). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table. If you are + * storing an (long) as the data (for example) then data_buf_p should + * be (long **) i.e. the address of a (long *). + * + * overwrite - Flag which, if set to 1, will allow the overwriting of + * the data in the table with the new data if the key already exists + * in the table. + */ +int table_insert(table_t *table_p, + const void *key_buf, const int key_size, + const void *data_buf, const int data_size, + void **data_buf_p, const char overwrite_b) +{ + return table_insert_kd(table_p, key_buf, key_size, data_buf, data_size, + NULL, data_buf_p, overwrite_b); +} + +/* + * int table_retrieve + * + * DESCRIPTION: + * + * This routine looks up a key made up of a buffer of bytes and an + * associated size in the table. If found then it returns the + * associated data information. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer into which we will be searching + * for the key. + * + * key_buf - Buffer of bytes of the key that we are searching for. If + * you are looking for an (int) as the key (for example) then key_buf + * should be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are looking for an (int) as the key (for example) then key_size + * should be sizeof(int). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table and that is + * associated with the key. If a (long) was stored as the data (for + * example) then data_buf_p should be (long **) i.e. the address of a + * (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data stored in the table that is associated with + * the key. + */ +int table_retrieve(table_t *table_p, + const void *key_buf, const int key_size, + void **data_buf_p, int *data_size_p) +{ + int bucket; + unsigned int ksize; + table_entry_t *entry_p, **buckets; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (key_buf == NULL) { + return TABLE_ERROR_ARG_NULL; + } + + /* find key size */ + if (key_size < 0) { + ksize = strlen((char *)key_buf) + sizeof(char); + } + else { + ksize = key_size; + } + + /* get the bucket number via a has function */ + bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n; + + /* look for the entry in this bucket, only check keys of the same size */ + buckets = table_p->ta_buckets; + for (entry_p = buckets[bucket]; + entry_p != NULL; + entry_p = entry_p->te_next_p) { + entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p); + if (entry_p->te_key_size == ksize + && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0) { + break; + } + } + + /* not found? */ + if (entry_p == NULL) { + return TABLE_ERROR_NOT_FOUND; + } + + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + if (table_p->ta_data_align == 0) { + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + } + SET_POINTER(data_size_p, entry_p->te_data_size); + + return TABLE_ERROR_NONE; +} + +/* + * int table_delete + * + * DESCRIPTION: + * + * This routine looks up a key made up of a buffer of bytes and an + * associated size in the table. If found then it will be removed + * from the table. The associated data can be passed back to the user + * if requested. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * NOTE: this could be an allocation error if the library is to return + * the data to the user. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we will be deleteing + * the key. + * + * key_buf - Buffer of bytes of the key that we are searching for to + * delete. If you are deleting an (int) key (for example) then + * key_buf should be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are deleting an (int) key (for example) then key_size should be + * sizeof(int). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table and that was + * associated with the key. If a (long) was stored as the data (for + * example) then data_buf_p should be (long **) i.e. the address of a + * (long *). If a pointer is passed in, the caller is responsible for + * freeing it after use. If data_buf_p is NULL then the library will + * free up the data allocation itself. + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that was stored in the table and that was + * associated with the key. + */ +int table_delete(table_t *table_p, + const void *key_buf, const int key_size, + void **data_buf_p, int *data_size_p) +{ + int bucket; + unsigned int ksize; + unsigned char *data_copy_p; + table_entry_t *entry_p, *last_p; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (key_buf == NULL) { + return TABLE_ERROR_ARG_NULL; + } + +#ifndef NO_MMAP + /* no mmap support so immediate error */ + if (table_p->ta_mmap != NULL) { + return TABLE_ERROR_MMAP_OP; + } +#endif + + /* get the key size */ + if (key_size < 0) { + ksize = strlen((char *)key_buf) + sizeof(char); + } + else { + ksize = key_size; + } + + /* find our bucket */ + bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n; + + /* look for the entry in this bucket, only check keys of the same size */ + for (last_p = NULL, entry_p = table_p->ta_buckets[bucket]; + entry_p != NULL; + last_p = entry_p, entry_p = entry_p->te_next_p) { + if (entry_p->te_key_size == ksize + && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0) { + break; + } + } + + /* did we find it? */ + if (entry_p == NULL) { + return TABLE_ERROR_NOT_FOUND; + } + + /* + * NOTE: we may want to adjust the linear counters here if the entry + * we are deleting is the one we are pointing on or is ahead of the + * one in the bucket list + */ + + /* remove entry from the linked list */ + if (last_p == NULL) { + table_p->ta_buckets[bucket] = entry_p->te_next_p; + } + else { + last_p->te_next_p = entry_p->te_next_p; + } + + /* free entry */ + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + /* + * if we were storing it compacted, we now need to malloc some + * space if the user wants the value after the delete. + */ + if (table_p->ta_alloc_func == NULL) { + *data_buf_p = malloc(entry_p->te_data_size); + } + else { + *data_buf_p = table_p->ta_alloc_func(table_p->ta_mem_pool, + entry_p->te_data_size); + } + if (*data_buf_p == NULL) { + return TABLE_ERROR_ALLOC; + } + if (table_p->ta_data_align == 0) { + data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + data_copy_p = entry_data_buf(table_p, entry_p); + } + memcpy(*data_buf_p, data_copy_p, entry_p->te_data_size); + } + } + SET_POINTER(data_size_p, entry_p->te_data_size); + if (table_p->ta_free_func == NULL) { + free(entry_p); + } + else if (! table_p->ta_free_func(table_p->ta_mem_pool, entry_p, + entry_size(table_p, + entry_p->te_key_size, + entry_p->te_data_size))) { + return TABLE_ERROR_FREE; + } + + table_p->ta_entry_n--; + + /* do we need auto-adjust down? */ + if ((table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST) + && (table_p->ta_flags & TABLE_FLAG_ADJUST_DOWN) + && SHOULD_TABLE_SHRINK(table_p)) { + return table_adjust(table_p, table_p->ta_entry_n); + } + + return TABLE_ERROR_NONE; +} + +/* + * int table_delete_first + * + * DESCRIPTION: + * + * This is like the table_delete routines except it deletes the first + * key/data pair in the table instead of an entry corresponding to a + * particular key. The associated key and data information can be + * passed back to the user if requested. This routines is handy to + * clear out a table. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * NOTE: this could be an allocation error if the library is to return + * the data to the user. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we will be deleteing + * the first key. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the first key that was allocated in the table. + * If an (int) was stored as the first key (for example) then + * key_buf_p should be (int **) i.e. the address of a (int *). If a + * pointer is passed in, the caller is responsible for freeing it + * after use. If key_buf_p is NULL then the library will free up the + * key allocation itself. + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that was stored in the table and that was + * associated with the key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table and that was + * associated with the key. If a (long) was stored as the data (for + * example) then data_buf_p should be (long **) i.e. the address of a + * (long *). If a pointer is passed in, the caller is responsible for + * freeing it after use. If data_buf_p is NULL then the library will + * free up the data allocation itself. + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that was stored in the table and that was + * associated with the key. + */ +int table_delete_first(table_t *table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + unsigned char *data_copy_p; + table_entry_t *entry_p; + table_linear_t linear; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + +#ifndef NO_MMAP + /* no mmap support so immediate error */ + if (table_p->ta_mmap != NULL) { + return TABLE_ERROR_MMAP_OP; + } +#endif + + /* take the first entry */ + entry_p = first_entry(table_p, &linear); + if (entry_p == NULL) { + return TABLE_ERROR_NOT_FOUND; + } + + /* + * NOTE: we may want to adjust the linear counters here if the entry + * we are deleting is the one we are pointing on or is ahead of the + * one in the bucket list + */ + + /* remove entry from the linked list */ + table_p->ta_buckets[linear.tl_bucket_c] = entry_p->te_next_p; + + /* free entry */ + if (key_buf_p != NULL) { + if (entry_p->te_key_size == 0) { + *key_buf_p = NULL; + } + else { + /* + * if we were storing it compacted, we now need to malloc some + * space if the user wants the value after the delete. + */ + if (table_p->ta_alloc_func == NULL) { + *key_buf_p = malloc(entry_p->te_key_size); + } + else { + *key_buf_p = table_p->ta_alloc_func(table_p->ta_mem_pool, + entry_p->te_key_size); + } + if (*key_buf_p == NULL) { + return TABLE_ERROR_ALLOC; + } + memcpy(*key_buf_p, ENTRY_KEY_BUF(entry_p), entry_p->te_key_size); + } + } + SET_POINTER(key_size_p, entry_p->te_key_size); + + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + /* + * if we were storing it compacted, we now need to malloc some + * space if the user wants the value after the delete. + */ + if (table_p->ta_alloc_func == NULL) { + *data_buf_p = malloc(entry_p->te_data_size); + } + else { + *data_buf_p = table_p->ta_alloc_func(table_p->ta_mem_pool, + entry_p->te_data_size); + } + if (*data_buf_p == NULL) { + return TABLE_ERROR_ALLOC; + } + if (table_p->ta_data_align == 0) { + data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + data_copy_p = entry_data_buf(table_p, entry_p); + } + memcpy(*data_buf_p, data_copy_p, entry_p->te_data_size); + } + } + SET_POINTER(data_size_p, entry_p->te_data_size); + if (table_p->ta_free_func == NULL) { + free(entry_p); + } + else if (! table_p->ta_free_func(table_p->ta_mem_pool, entry_p, + entry_size(table_p, + entry_p->te_key_size, + entry_p->te_data_size))) { + return TABLE_ERROR_FREE; + } + + table_p->ta_entry_n--; + + /* do we need auto-adjust down? */ + if ((table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST) + && (table_p->ta_flags & TABLE_FLAG_ADJUST_DOWN) + && SHOULD_TABLE_SHRINK(table_p)) { + return table_adjust(table_p, table_p->ta_entry_n); + } + + return TABLE_ERROR_NONE; +} + +/* + * int table_info + * + * DESCRIPTION: + * + * Get some information about a table_p structure. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting + * information. + * + * num_buckets_p - Pointer to an integer which, if not NULL, will + * contain the number of buckets in the table. + * + * num_entries_p - Pointer to an integer which, if not NULL, will + * contain the number of entries stored in the table. + */ +int table_info(table_t *table_p, int *num_buckets_p, int *num_entries_p) +{ + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + + SET_POINTER(num_buckets_p, table_p->ta_bucket_n); + SET_POINTER(num_entries_p, table_p->ta_entry_n); + + return TABLE_ERROR_NONE; +} + +/* + * int table_adjust + * + * DESCRIPTION: + * + * Set the number of buckets in a table to a certain value. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer of which we are adjusting. + * + * bucket_n - Number buckets to adjust the table to. Set to 0 to + * adjust the table to its number of entries. + */ +int table_adjust(table_t *table_p, const int bucket_n) +{ + table_entry_t *entry_p, *next_p; + table_entry_t **buckets, **bucket_p, **bounds_p; + int bucket; + unsigned int buck_n, bucket_size; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + +#ifndef NO_MMAP + /* no mmap support so immediate error */ + if (table_p->ta_mmap != NULL) { + return TABLE_ERROR_MMAP_OP; + } +#endif + + /* + * NOTE: we walk through the entries and rehash them. If we stored + * the hash value as a full int in the table-entry, all we would + * have to do is remod it. + */ + + /* normalize to the number of entries */ + if (bucket_n == 0) { + buck_n = table_p->ta_entry_n; + } + else { + buck_n = bucket_n; + } + + /* we must have at least 1 bucket */ + if (buck_n == 0) { + buck_n = 1; + } + + (void)printf("growing table to %d\n", buck_n); + + /* make sure we have something to do */ + if (buck_n == table_p->ta_bucket_n) { + return TABLE_ERROR_NONE; + } + + /* allocate a new bucket list */ + bucket_size = buck_n * sizeof(table_entry_t *); + if (table_p->ta_alloc_func == NULL) { + buckets = (table_entry_t **)malloc(bucket_size); + } + else { + buckets = + (table_entry_t **)table_p->ta_alloc_func(table_p->ta_mem_pool, + bucket_size); + } + if (buckets == NULL) { + return TABLE_ERROR_ALLOC; + } + /* + * We zero it ourselves to save the necessity of having a + * table_mem_calloc_t memory override function. + */ + memset(buckets, 0, bucket_size); + + /* + * run through each of the items in the current table and rehash + * them into the newest bucket sizes + */ + bounds_p = table_p->ta_buckets + table_p->ta_bucket_n; + for (bucket_p = table_p->ta_buckets; bucket_p < bounds_p; bucket_p++) { + for (entry_p = *bucket_p; entry_p != NULL; entry_p = next_p) { + + /* hash the old data into the new table size */ + bucket = hash(ENTRY_KEY_BUF(entry_p), entry_p->te_key_size, 0) % buck_n; + + /* record the next one now since we overwrite next below */ + next_p = entry_p->te_next_p; + + /* insert into new list, no need to append */ + entry_p->te_next_p = buckets[bucket]; + buckets[bucket] = entry_p; + + /* + * NOTE: we may want to adjust the bucket_c linear entry here to + * keep it current + */ + } + /* remove the old table pointers as we go by */ + *bucket_p = NULL; + } + + /* replace the table buckets with the new ones */ + if (table_p->ta_free_func == NULL) { + free(table_p->ta_buckets); + } + else if (! table_p->ta_free_func(table_p->ta_mem_pool, + table_p->ta_buckets, + table_p->ta_bucket_n * + sizeof(table_entry_t *))) { + return TABLE_ERROR_FREE; + } + table_p->ta_buckets = buckets; + table_p->ta_bucket_n = buck_n; + + return TABLE_ERROR_NONE; +} + +/* + * int table_type_size + * + * DESCRIPTION: + * + * Return the size of the internal table type. + * + * RETURNS: + * + * The size of the table_t type. + * + * ARGUMENTS: + * + * None. + */ +int table_type_size(void) +{ + return sizeof(table_t); +} + +/************************* linear access routines ****************************/ + +/* + * int table_first + * + * DESCRIPTION: + * + * Find first element in a table and pass back information about the + * key/data pair. If any of the key/data pointers are NULL then they + * are ignored. + * + * NOTE: This function is not reentrant. More than one thread cannot + * be doing a first and next on the same table at the same time. Use + * the table_first_r version below for this. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * first element. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the first key that is allocated in the table. If + * an (int) is stored as the first key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the first key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the first key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the first key. + */ +int table_first(table_t *table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + + /* initialize our linear magic number */ + table_p->ta_linear.tl_magic = LINEAR_MAGIC; + + entry_p = first_entry(table_p, &table_p->ta_linear); + if (entry_p == NULL) { + return TABLE_ERROR_NOT_FOUND; + } + + SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); + SET_POINTER(key_size_p, entry_p->te_key_size); + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + if (table_p->ta_data_align == 0) { + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + } + SET_POINTER(data_size_p, entry_p->te_data_size); + + return TABLE_ERROR_NONE; +} + +/* + * int table_next + * + * DESCRIPTION: + * + * Find the next element in a table and pass back information about + * the key/data pair. If any of the key/data pointers are NULL then + * they are ignored. + * + * NOTE: This function is not reentrant. More than one thread cannot + * be doing a first and next on the same table at the same time. Use + * the table_next_r version below for this. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * next element. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the next key that is allocated in the table. If + * an (int) is stored as the next key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the next key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the next key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the next key. + */ +int table_next(table_t *table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p; + int error; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (table_p->ta_linear.tl_magic != LINEAR_MAGIC) { + return TABLE_ERROR_LINEAR; + } + + /* move to the next entry */ + entry_p = next_entry(table_p, &table_p->ta_linear, &error); + if (entry_p == NULL) { + return error; + } + + SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); + SET_POINTER(key_size_p, entry_p->te_key_size); + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + if (table_p->ta_data_align == 0) { + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + } + SET_POINTER(data_size_p, entry_p->te_data_size); + + return TABLE_ERROR_NONE; +} + +/* + * int table_this + * + * DESCRIPTION: + * + * Find the current element in a table and pass back information about + * the key/data pair. If any of the key/data pointers are NULL then + * they are ignored. + * + * NOTE: This function is not reentrant. Use the table_current_r + * version below. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * current element. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the current key that is allocated in the table. + * If an (int) is stored as the current key (for example) then + * key_buf_p should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the current key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the current key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the current key. + */ +int table_this(table_t *table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p = NULL; + int entry_c; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (table_p->ta_linear.tl_magic != LINEAR_MAGIC) { + return TABLE_ERROR_LINEAR; + } + + /* if we removed an item that shorted the bucket list, we may get this */ + if (table_p->ta_linear.tl_bucket_c >= table_p->ta_bucket_n) { + /* + * NOTE: this might happen if we delete an item which shortens the + * table bucket numbers. + */ + return TABLE_ERROR_NOT_FOUND; + } + + /* find the entry which is the nth in the list */ + entry_p = table_p->ta_buckets[table_p->ta_linear.tl_bucket_c]; + /* NOTE: we swap the order here to be more efficient */ + for (entry_c = table_p->ta_linear.tl_entry_c; entry_c > 0; entry_c--) { + /* did we reach the end of the list? */ + if (entry_p == NULL) { + break; + } + entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p)->te_next_p; + } + + /* is this a NOT_FOUND or a LINEAR error */ + if (entry_p == NULL) { + return TABLE_ERROR_NOT_FOUND; + } + + SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); + SET_POINTER(key_size_p, entry_p->te_key_size); + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + if (table_p->ta_data_align == 0) { + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + } + SET_POINTER(data_size_p, entry_p->te_data_size); + + return TABLE_ERROR_NONE; +} + +/* + * int table_first_r + * + * DESCRIPTION: + * + * Reetrant version of the table_first routine above. Find first + * element in a table and pass back information about the key/data + * pair. If any of the key/data pointers are NULL then they are + * ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * first element. + * + * linear_p - Pointer to a table linear structure which is initialized + * here. The same pointer should then be passed to table_next_r + * below. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the first key that is allocated in the table. If + * an (int) is stored as the first key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the first key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the first key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the first key. + */ +int table_first_r(table_t *table_p, table_linear_t *linear_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (linear_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + + /* initialize our linear magic number */ + linear_p->tl_magic = LINEAR_MAGIC; + + entry_p = first_entry(table_p, linear_p); + if (entry_p == NULL) { + return TABLE_ERROR_NOT_FOUND; + } + + SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); + SET_POINTER(key_size_p, entry_p->te_key_size); + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + if (table_p->ta_data_align == 0) { + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + } + SET_POINTER(data_size_p, entry_p->te_data_size); + + return TABLE_ERROR_NONE; +} + +/* + * int table_next_r + * + * DESCRIPTION: + * + * Reetrant version of the table_next routine above. Find next + * element in a table and pass back information about the key/data + * pair. If any of the key/data pointers are NULL then they are + * ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * next element. + * + * linear_p - Pointer to a table linear structure which is incremented + * here. The same pointer must have been passed to table_first_r + * first so that it can be initialized. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the next key that is allocated in the table. If + * an (int) is stored as the next key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL will be set + * to the size of the key that is stored in the table and that is + * associated with the next key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the next key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the next key. + */ +int table_next_r(table_t *table_p, table_linear_t *linear_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p; + int error; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (linear_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (linear_p->tl_magic != LINEAR_MAGIC) { + return TABLE_ERROR_LINEAR; + } + + /* move to the next entry */ + entry_p = next_entry(table_p, linear_p, &error); + if (entry_p == NULL) { + return error; + } + + SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); + SET_POINTER(key_size_p, entry_p->te_key_size); + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + if (table_p->ta_data_align == 0) { + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + } + SET_POINTER(data_size_p, entry_p->te_data_size); + + return TABLE_ERROR_NONE; +} + +/* + * int table_this_r + * + * DESCRIPTION: + * + * Reetrant version of the table_this routine above. Find current + * element in a table and pass back information about the key/data + * pair. If any of the key/data pointers are NULL then they are + * ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * current element. + * + * linear_p - Pointer to a table linear structure which is accessed + * here. The same pointer must have been passed to table_first_r + * first so that it can be initialized. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the current key that is allocated in the table. + * If an (int) is stored as the current key (for example) then + * key_buf_p should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the current key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the current key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the current key. + */ +int table_this_r(table_t *table_p, table_linear_t *linear_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p; + int entry_c; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (linear_p->tl_magic != LINEAR_MAGIC) { + return TABLE_ERROR_LINEAR; + } + + /* if we removed an item that shorted the bucket list, we may get this */ + if (linear_p->tl_bucket_c >= table_p->ta_bucket_n) { + /* + * NOTE: this might happen if we delete an item which shortens the + * table bucket numbers. + */ + return TABLE_ERROR_NOT_FOUND; + } + + /* find the entry which is the nth in the list */ + for (entry_c = linear_p->tl_entry_c, + entry_p = table_p->ta_buckets[linear_p->tl_bucket_c]; + entry_p != NULL && entry_c > 0; + entry_c--, entry_p = TABLE_POINTER(table_p, table_entry_t *, + entry_p)->te_next_p) { + } + + if (entry_p == NULL) { + return TABLE_ERROR_NOT_FOUND; + } + + SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); + SET_POINTER(key_size_p, entry_p->te_key_size); + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + if (table_p->ta_data_align == 0) { + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + } + SET_POINTER(data_size_p, entry_p->te_data_size); + + return TABLE_ERROR_NONE; +} + +/******************************* mmap routines *******************************/ + +/* + * table_t *table_mmap + * + * DESCRIPTION: + * + * Mmap a table from a file that had been written to disk earlier via + * table_write. + * + * RETURNS: + * + * A pointer to the new table structure which must be passed to + * table_munmap to be deallocated. On error a NULL is returned. + * + * ARGUMENTS: + * + * path - Table file to mmap in. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +table_t *table_mmap(const char *path, int *error_p) +{ +#ifdef NO_MMAP + + /* no mmap support so immediate error */ + SET_POINTER(error_p, TABLE_ERROR_MMAP_NONE); + return NULL; + +#else + + table_t *table_p; + struct stat sbuf; + int fd, state; + + table_p = (table_t *)malloc(sizeof(table_t)); + if (table_p == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ALLOC); + return NULL; + } + + /* open the mmap file */ + fd = open(path, O_RDONLY, 0); + if (fd < 0) { + free(table_p); + SET_POINTER(error_p, TABLE_ERROR_OPEN); + return NULL; + } + + /* get the file size */ + if (fstat(fd, &sbuf) != 0) { + free(table_p); + SET_POINTER(error_p, TABLE_ERROR_OPEN); + return NULL; + } + + /* mmap the space and close the file */ +#ifdef __alpha + state = (MAP_SHARED | MAP_FILE | MAP_VARIABLE); +#else + state = MAP_SHARED; +#endif + + table_p->ta_mmap = (table_t *)mmap((caddr_t)0, sbuf.st_size, PROT_READ, + state, fd, 0); + (void)close(fd); + + if (table_p->ta_mmap == (table_t *)MAP_FAILED) { + SET_POINTER(error_p, TABLE_ERROR_MMAP); + return NULL; + } + + /* is the mmap file contain bad info or maybe another system type? */ + if (table_p->ta_mmap->ta_magic != TABLE_MAGIC) { + SET_POINTER(error_p, TABLE_ERROR_PNT); + return NULL; + } + + /* sanity check on the file size */ + if (table_p->ta_mmap->ta_file_size != sbuf.st_size) { + SET_POINTER(error_p, TABLE_ERROR_SIZE); + return NULL; + } + + /* copy the fields out of the mmap file into our memory version */ + table_p->ta_magic = TABLE_MAGIC; + table_p->ta_flags = table_p->ta_mmap->ta_flags; + table_p->ta_bucket_n = table_p->ta_mmap->ta_bucket_n; + table_p->ta_entry_n = table_p->ta_mmap->ta_entry_n; + table_p->ta_data_align = table_p->ta_mmap->ta_data_align; + table_p->ta_buckets = TABLE_POINTER(table_p, table_entry_t **, + table_p->ta_mmap->ta_buckets); + table_p->ta_linear.tl_magic = 0; + table_p->ta_linear.tl_bucket_c = 0; + table_p->ta_linear.tl_entry_c = 0; + /* mmap is already set */ + table_p->ta_file_size = table_p->ta_mmap->ta_file_size; + + SET_POINTER(error_p, TABLE_ERROR_NONE); + return table_p; + +#endif +} + +/* + * int table_munmap + * + * DESCRIPTION: + * + * Unmmap a table that was previously mmapped using table_mmap. + * + * RETURNS: + * + * Returns table error codes. + * + * ARGUMENTS: + * + * table_p - Mmaped table pointer to unmap. + */ +int table_munmap(table_t *table_p) +{ +#ifdef NO_MMAP + + /* no mmap support so immediate error */ + return TABLE_ERROR_MMAP_NONE; + +#else + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (table_p->ta_mmap == NULL) { + return TABLE_ERROR_PNT; + } + + (void)munmap((caddr_t)table_p->ta_mmap, table_p->ta_file_size); + table_p->ta_magic = 0; + free(table_p); + return TABLE_ERROR_NONE; + +#endif +} + +/******************************* file routines *******************************/ + +/* + * int table_read + * + * DESCRIPTION: + * + * Read in a table from a file that had been written to disk earlier + * via table_write. + * + * RETURNS: + * + * Success - Pointer to the new table structure which must be passed + * to table_free to be deallocated. + * + * Failure - NULL + * + * ARGUMENTS: + * + * path - Table file to read in. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +table_t *table_read(const char *path, int *error_p) +{ + unsigned int size; + int fd, ent_size; + FILE *infile; + table_entry_t entry, **bucket_p, *entry_p = NULL, *last_p; + unsigned long pos; + table_t *table_p; + + /* open the file */ + fd = open(path, O_RDONLY, 0); + if (fd < 0) { + SET_POINTER(error_p, TABLE_ERROR_OPEN); + return NULL; + } + + /* allocate a table structure */ + table_p = malloc(sizeof(table_t)); + if (table_p == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ALLOC); + return NULL; + } + + /* now open the fd to get buffered i/o */ + infile = fdopen(fd, "r"); + if (infile == NULL) { + SET_POINTER(error_p, TABLE_ERROR_OPEN); + return NULL; + } + + /* read the main table struct */ + if (fread(table_p, sizeof(table_t), 1, infile) != 1) { + SET_POINTER(error_p, TABLE_ERROR_READ); + free(table_p); + return NULL; + } + table_p->ta_file_size = 0; + + /* is the mmap file contain bad info or maybe another system type? */ + if (table_p->ta_magic != TABLE_MAGIC) { + SET_POINTER(error_p, TABLE_ERROR_PNT); + return NULL; + } + + /* allocate the buckets */ + table_p->ta_buckets = (table_entry_t **)calloc(table_p->ta_bucket_n, + sizeof(table_entry_t *)); + if (table_p->ta_buckets == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ALLOC); + free(table_p); + return NULL; + } + + if (fread(table_p->ta_buckets, sizeof(table_entry_t *), table_p->ta_bucket_n, + infile) != (size_t)table_p->ta_bucket_n) { + SET_POINTER(error_p, TABLE_ERROR_READ); + free(table_p->ta_buckets); + free(table_p); + return NULL; + } + + /* read in the entries */ + for (bucket_p = table_p->ta_buckets; + bucket_p < table_p->ta_buckets + table_p->ta_bucket_n; + bucket_p++) { + + /* skip null buckets */ + if (*bucket_p == NULL) { + continue; + } + + /* run through the entry list */ + last_p = NULL; + for (pos = *(unsigned long *)bucket_p;; + pos = (unsigned long)entry_p->te_next_p) { + + /* read in the entry */ + if (fseek(infile, pos, SEEK_SET) != 0) { + SET_POINTER(error_p, TABLE_ERROR_SEEK); + free(table_p->ta_buckets); + free(table_p); + if (entry_p != NULL) { + free(entry_p); + } + /* the other table elements will not be freed */ + return NULL; + } + if (fread(&entry, sizeof(struct table_shell_st), 1, infile) != 1) { + SET_POINTER(error_p, TABLE_ERROR_READ); + free(table_p->ta_buckets); + free(table_p); + if (entry_p != NULL) { + free(entry_p); + } + /* the other table elements will not be freed */ + return NULL; + } + + /* make a new entry */ + ent_size = entry_size(table_p, entry.te_key_size, entry.te_data_size); + entry_p = (table_entry_t *)malloc(ent_size); + if (entry_p == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ALLOC); + free(table_p->ta_buckets); + free(table_p); + /* the other table elements will not be freed */ + return NULL; + } + entry_p->te_key_size = entry.te_key_size; + entry_p->te_data_size = entry.te_data_size; + entry_p->te_next_p = entry.te_next_p; + + if (last_p == NULL) { + *bucket_p = entry_p; + } + else { + last_p->te_next_p = entry_p; + } + + /* determine how much more we have to read */ + size = ent_size - sizeof(struct table_shell_st); + if (fread(ENTRY_KEY_BUF(entry_p), sizeof(char), size, infile) != size) { + SET_POINTER(error_p, TABLE_ERROR_READ); + free(table_p->ta_buckets); + free(table_p); + free(entry_p); + /* the other table elements will not be freed */ + return NULL; + } + + /* we are done if the next pointer is null */ + if (entry_p->te_next_p == (unsigned long)0) { + break; + } + last_p = entry_p; + } + } + + (void)fclose(infile); + + SET_POINTER(error_p, TABLE_ERROR_NONE); + return table_p; +} + +/* + * int table_write + * + * DESCRIPTION: + * + * Write a table from memory to file. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to the table that we are writing to the file. + * + * path - Table file to write out to. + * + * mode - Mode of the file. This argument is passed on to open when + * the file is created. + */ +int table_write(const table_t *table_p, const char *path, const int mode) +{ + int fd, rem, ent_size; + unsigned int bucket_c, bucket_size; + unsigned long size; + table_entry_t *entry_p, **buckets, **bucket_p, *next_p; + table_t main_tab; + FILE *outfile; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + + fd = open(path, O_WRONLY | O_CREAT, mode); + if (fd < 0) { + return TABLE_ERROR_OPEN; + } + + outfile = fdopen(fd, "w"); + if (outfile == NULL) { + return TABLE_ERROR_OPEN; + } + + /* allocate a block of sizes for each bucket */ + bucket_size = sizeof(table_entry_t *) * table_p->ta_bucket_n; + if (table_p->ta_alloc_func == NULL) { + buckets = (table_entry_t **)malloc(bucket_size); + } + else { + buckets = + (table_entry_t **)table_p->ta_alloc_func(table_p->ta_mem_pool, + bucket_size); + } + if (buckets == NULL) { + return TABLE_ERROR_ALLOC; + } + + /* make a copy of the main struct */ + main_tab = *table_p; + + /* start counting the bytes */ + size = 0; + size += sizeof(table_t); + + /* buckets go right after main struct */ + main_tab.ta_buckets = (table_entry_t **)size; + size += sizeof(table_entry_t *) * table_p->ta_bucket_n; + + /* run through and count the buckets */ + for (bucket_c = 0; bucket_c < table_p->ta_bucket_n; bucket_c++) { + bucket_p = table_p->ta_buckets + bucket_c; + if (*bucket_p == NULL) { + buckets[bucket_c] = NULL; + continue; + } + buckets[bucket_c] = (table_entry_t *)size; + for (entry_p = *bucket_p; entry_p != NULL; entry_p = entry_p->te_next_p) { + size += entry_size(table_p, entry_p->te_key_size, entry_p->te_data_size); + /* + * We now have to round the file to the nearest long so the + * mmaping of the longs in the entry structs will work. + */ + rem = size & (sizeof(long) - 1); + if (rem > 0) { + size += sizeof(long) - rem; + } + } + } + /* add a \0 at the end to fill the last section */ + size++; + + /* set the main fields */ + main_tab.ta_linear.tl_magic = 0; + main_tab.ta_linear.tl_bucket_c = 0; + main_tab.ta_linear.tl_entry_c = 0; + main_tab.ta_mmap = NULL; + main_tab.ta_file_size = size; + + /* + * Now we can start the writing because we got the bucket offsets. + */ + + /* write the main table struct */ + size = 0; + if (fwrite(&main_tab, sizeof(table_t), 1, outfile) != 1) { + if (table_p->ta_free_func == NULL) { + free(buckets); + } + else { + (void)table_p->ta_free_func(table_p->ta_mem_pool, buckets, bucket_size); + } + return TABLE_ERROR_WRITE; + } + size += sizeof(table_t); + if (fwrite(buckets, sizeof(table_entry_t *), table_p->ta_bucket_n, + outfile) != (size_t)table_p->ta_bucket_n) { + if (table_p->ta_free_func == NULL) { + free(buckets); + } + else { + (void)table_p->ta_free_func(table_p->ta_mem_pool, buckets, bucket_size); + } + return TABLE_ERROR_WRITE; + } + size += sizeof(table_entry_t *) * table_p->ta_bucket_n; + + /* write out the entries */ + for (bucket_p = table_p->ta_buckets; + bucket_p < table_p->ta_buckets + table_p->ta_bucket_n; + bucket_p++) { + for (entry_p = *bucket_p; entry_p != NULL; entry_p = entry_p->te_next_p) { + + ent_size = entry_size(table_p, entry_p->te_key_size, + entry_p->te_data_size); + size += ent_size; + /* round to nearest long here so we can write copy */ + rem = size & (sizeof(long) - 1); + if (rem > 0) { + size += sizeof(long) - rem; + } + next_p = entry_p->te_next_p; + if (next_p != NULL) { + entry_p->te_next_p = (table_entry_t *)size; + } + + /* now write to disk */ + if (fwrite(entry_p, ent_size, 1, outfile) != 1) { + if (table_p->ta_free_func == NULL) { + free(buckets); + } + else { + (void)table_p->ta_free_func(table_p->ta_mem_pool, buckets, + bucket_size); + } + return TABLE_ERROR_WRITE; + } + + /* restore the next pointer */ + if (next_p != NULL) { + entry_p->te_next_p = next_p; + } + + /* now write the padding information */ + if (rem > 0) { + rem = sizeof(long) - rem; + /* + * NOTE: this won't leave fseek'd space at the end but we + * don't care there because there is no accessed memory + * afterwards. We write 1 \0 at the end to make sure. + */ + if (fseek(outfile, rem, SEEK_CUR) != 0) { + if (table_p->ta_free_func == NULL) { + free(buckets); + } + else { + (void)table_p->ta_free_func(table_p->ta_mem_pool, buckets, + bucket_size); + } + return TABLE_ERROR_SEEK; + } + } + } + } + /* + * Write a \0 at the end of the file to make sure that the last + * fseek filled with nulls. + */ + (void)fputc('\0', outfile); + + (void)fclose(outfile); + if (table_p->ta_free_func == NULL) { + free(buckets); + } + else if (! table_p->ta_free_func(table_p->ta_mem_pool, buckets, + bucket_size)) { + return TABLE_ERROR_FREE; + } + + return TABLE_ERROR_NONE; +} + +/******************************** table order ********************************/ + +/* + * table_entry_t *table_order + * + * DESCRIPTION: + * + * Order a table by building an array of table entry pointers and then + * sorting this array using the qsort function. To retrieve the + * sorted entries, you can then use the table_entry routine to access + * each entry in order. + * + * NOTE: This routine is thread safe and makes use of an internal + * status qsort function. + * + * RETURNS: + * + * Success - An allocated list of table-linear structures which must + * be freed by table_order_free later. + * + * Failure - NULL + * + * ARGUMENTS: + * + * table_p - Pointer to the table that we are ordering. + * + * compare - Comparison function defined by the user. Its definition + * is at the top of the table.h file. If this is NULL then it will + * order the table my memcmp-ing the keys. + * + * num_entries_p - Pointer to an integer which, if not NULL, will + * contain the number of entries in the returned entry pointer array. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +table_entry_t **table_order(table_t *table_p, table_compare_t compare, + int *num_entries_p, int *error_p) +{ + table_entry_t *entry_p, **entries, **entries_p; + table_linear_t linear; + compare_t comp_func; + unsigned int entries_size; + int ret; + + if (table_p == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ARG_NULL); + return NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + SET_POINTER(error_p, TABLE_ERROR_PNT); + return NULL; + } + + /* there must be at least 1 element in the table for this to work */ + if (table_p->ta_entry_n == 0) { + SET_POINTER(error_p, TABLE_ERROR_EMPTY); + return NULL; + } + + entries_size = table_p->ta_entry_n * sizeof(table_entry_t *); + if (table_p->ta_alloc_func == NULL) { + entries = (table_entry_t **)malloc(entries_size); + } + else { + entries = + (table_entry_t **)table_p->ta_alloc_func(table_p->ta_mem_pool, + entries_size); + } + if (entries == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ALLOC); + return NULL; + } + + /* get a pointer to all entries */ + entry_p = first_entry(table_p, &linear); + if (entry_p == NULL) { + if (table_p->ta_free_func == NULL) { + free(entries); + } + else { + (void)table_p->ta_free_func(table_p->ta_mem_pool, entries, entries_size); + } + SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); + return NULL; + } + + /* add all of the entries to the array */ + for (entries_p = entries; + entry_p != NULL; + entry_p = next_entry(table_p, &linear, &ret)) { + *entries_p++ = entry_p; + } + + if (ret != TABLE_ERROR_NOT_FOUND) { + if (table_p->ta_free_func == NULL) { + free(entries); + } + else { + (void)table_p->ta_free_func(table_p->ta_mem_pool, entries, entries_size); + } + SET_POINTER(error_p, ret); + return NULL; + } + + if (compare == NULL) { + /* this is regardless of the alignment */ + comp_func = local_compare; + } + else if (table_p->ta_data_align == 0) { + comp_func = external_compare; + } + else { + comp_func = external_compare_align; + } + + /* now qsort the entire entries array from first to last element */ + ret = split((unsigned char *)entries, + (unsigned char *)(entries + table_p->ta_entry_n - 1), + sizeof(table_entry_t *), comp_func, compare, table_p); + if (ret != TABLE_ERROR_NONE) { + if (table_p->ta_free_func == NULL) { + free(entries); + } + else { + (void)table_p->ta_free_func(table_p->ta_mem_pool, entries, entries_size); + } + SET_POINTER(error_p, ret); + return NULL; + } + + SET_POINTER(num_entries_p, table_p->ta_entry_n); + + SET_POINTER(error_p, TABLE_ERROR_NONE); + return entries; +} + +/* + * int table_order_free + * + * DESCRIPTION: + * + * Free the pointer returned by the table_order or table_order_pos + * routines. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to the table. + * + * table_entries - Allocated list of entry pointers returned by + * table_order. + * + * entry_n - Number of entries in the array as passed back by + * table_order or table_order_pos in num_entries_p. + */ +int table_order_free(table_t *table_p, table_entry_t **table_entries, + const int entry_n) +{ + int ret, final = TABLE_ERROR_NONE; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + + if (table_p->ta_free_func == NULL) { + free(table_entries); + } + else { + ret = table_p->ta_free_func(table_p->ta_mem_pool, table_entries, + sizeof(table_entry_t *) * entry_n); + if (ret != 1) { + final = TABLE_ERROR_FREE; + } + } + + return final; +} + +/* + * int table_entry + * + * DESCRIPTION: + * + * Get information about an element. The element is one from the + * array returned by the table_order function. If any of the key/data + * pointers are NULL then they are ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * element. + * + * entry_p - Pointer to a table entry from the array returned by the + * table_order function. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of this entry that is allocated in the table. If an + * (int) is stored as this entry (for example) then key_buf_p should + * be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage of this entry that is allocated in the table. + * If a (long) is stored as this entry data (for example) then + * data_buf_p should be (long **) i.e. the address of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table. + */ +int table_entry(table_t *table_p, table_entry_t *entry_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (entry_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + + SET_POINTER(key_buf_p, ENTRY_KEY_BUF(entry_p)); + SET_POINTER(key_size_p, entry_p->te_key_size); + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + if (table_p->ta_data_align == 0) { + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + } + SET_POINTER(data_size_p, entry_p->te_data_size); + + return TABLE_ERROR_NONE; +} + +/* + * table_linear_t *table_order_pos + * + * DESCRIPTION: + * + * Order a table by building an array of table linear structures and + * then sorting this array using the qsort function. To retrieve the + * sorted entries, you can then use the table_entry_pos routine to + * access each entry in order. + * + * NOTE: This routine is thread safe and makes use of an internal + * status qsort function. + * + * RETURNS: + * + * Success - An allocated list of table-linear structures which must + * be freed by table_order_pos_free later. + * + * Failure - NULL + * + * ARGUMENTS: + * + * table_p - Pointer to the table that we are ordering. + * + * compare - Comparison function defined by the user. Its definition + * is at the top of the table.h file. If this is NULL then it will + * order the table my memcmp-ing the keys. + * + * num_entries_p - Pointer to an integer which, if not NULL, will + * contain the number of entries in the returned entry pointer array. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +table_linear_t *table_order_pos(table_t *table_p, table_compare_t compare, + int *num_entries_p, int *error_p) +{ + table_entry_t *entry_p; + table_linear_t linear, *linears, *linears_p; + compare_t comp_func; + int ret; + + if (table_p == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ARG_NULL); + return NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + SET_POINTER(error_p, TABLE_ERROR_PNT); + return NULL; + } + + /* there must be at least 1 element in the table for this to work */ + if (table_p->ta_entry_n == 0) { + SET_POINTER(error_p, TABLE_ERROR_EMPTY); + return NULL; + } + + if (table_p->ta_alloc_func == NULL) { + linears = (table_linear_t *)malloc(table_p->ta_entry_n * + sizeof(table_linear_t)); + } + else { + linears = + (table_linear_t *)table_p->ta_alloc_func(table_p->ta_mem_pool, + table_p->ta_entry_n * + sizeof(table_linear_t)); + } + if (linears == NULL) { + SET_POINTER(error_p, TABLE_ERROR_ALLOC); + return NULL; + } + + /* get a pointer to all entries */ + entry_p = first_entry(table_p, &linear); + if (entry_p == NULL) { + SET_POINTER(error_p, TABLE_ERROR_NOT_FOUND); + return NULL; + } + + /* add all of the entries to the array */ + for (linears_p = linears; + entry_p != NULL; + entry_p = next_entry(table_p, &linear, &ret)) { + *linears_p++ = linear; + } + + if (ret != TABLE_ERROR_NOT_FOUND) { + SET_POINTER(error_p, ret); + return NULL; + } + + if (compare == NULL) { + /* this is regardless of the alignment */ + comp_func = local_compare_pos; + } + else if (table_p->ta_data_align == 0) { + comp_func = external_compare_pos; + } + else { + comp_func = external_compare_align_pos; + } + + /* now qsort the entire entries array from first to last element */ + split((unsigned char *)linears, + (unsigned char *)(linears + table_p->ta_entry_n - 1), + sizeof(table_linear_t), comp_func, compare, table_p); + + if (num_entries_p != NULL) { + *num_entries_p = table_p->ta_entry_n; + } + + SET_POINTER(error_p, TABLE_ERROR_NONE); + return linears; +} + +/* + * int table_order_pos_free + * + * DESCRIPTION: + * + * Free the pointer returned by the table_order or table_order_pos + * routines. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to the table. + * + * table_entries - Allocated list of entry pointers returned by + * table_order_pos. + * + * entry_n - Number of entries in the array as passed back by + * table_order or table_order_pos in num_entries_p. + */ +int table_order_pos_free(table_t *table_p, table_linear_t *table_entries, + const int entry_n) +{ + int ret, final = TABLE_ERROR_NONE; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + + if (table_p->ta_free_func == NULL) { + free(table_entries); + } + else { + ret = table_p->ta_free_func(table_p->ta_mem_pool, table_entries, + sizeof(table_linear_t) * entry_n); + if (ret != 1) { + final = TABLE_ERROR_FREE; + } + } + + return final; +} + +/* + * int table_entry_pos + * + * DESCRIPTION: + * + * Get information about an element. The element is one from the + * array returned by the table_order function. If any of the key/data + * pointers are NULL then they are ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * element. + * + * linear_p - Pointer to a table linear structure from the array + * returned by the table_order function. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of this entry that is allocated in the table. If an + * (int) is stored as this entry (for example) then key_buf_p should + * be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage of this entry that is allocated in the table. + * If a (long) is stored as this entry data (for example) then + * data_buf_p should be (long **) i.e. the address of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table. + */ +int table_entry_pos(table_t *table_p, table_linear_t *linear_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p; + int ret; + + if (table_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + return TABLE_ERROR_PNT; + } + if (linear_p == NULL) { + return TABLE_ERROR_ARG_NULL; + } + + /* find the associated entry */ + entry_p = this_entry(table_p, linear_p, &ret); + if (entry_p == NULL) { + return ret; + } + + if (key_buf_p != NULL) { + *key_buf_p = ENTRY_KEY_BUF(entry_p); + } + if (key_size_p != NULL) { + *key_size_p = entry_p->te_key_size; + } + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) { + *data_buf_p = NULL; + } + else { + if (table_p->ta_data_align == 0) { + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + } + else { + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + } + if (data_size_p != NULL) { + *data_size_p = entry_p->te_data_size; + } + + return TABLE_ERROR_NONE; +} + +/* + * const char *table_strerror + * + * DESCRIPTION: + * + * Return the corresponding string for the error number. + * + * RETURNS: + * + * Success - String equivalient of the error. + * + * Failure - String "invalid error code" + * + * ARGUMENTS: + * + * error - Error number that we are converting. + */ +const char *table_strerror(const int error) +{ + error_str_t *err_p; + + for (err_p = errors; err_p->es_error != 0; err_p++) { + if (err_p->es_error == error) { + return err_p->es_string; + } + } + + return INVALID_ERROR; +} diff --git a/libs/libks/src/table_util.c b/libs/libks/src/table_util.c new file mode 100644 index 0000000000..2fcfda3778 --- /dev/null +++ b/libs/libks/src/table_util.c @@ -0,0 +1,295 @@ +/* + * Hash table utility program. + * + * Copyright 2000 by Gray Watson + * + * This file is part of the table package. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, + * and that the name of Gray Watson not be used in advertising or + * publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be reached via http://256.com/gray/ + * + * $Id: table_util.c,v 1.5 2000/03/09 03:30:42 gray Exp $ + */ + +#include +#include +#include + +#include "table.h" + +static char *rcs_id = + "$Id: table_util.c,v 1.5 2000/03/09 03:30:42 gray Exp $"; + +#define WRITE_MODE 0640 /* mode to write out table */ +#define SPECIAL_CHARS "e\033^^\"\"''\\\\n\nr\rt\tb\bf\fa\007" + +/* + * expand_chars + * + * DESCRIPTION: + * + * Copies a buffer into a output buffer while translates + * non-printables into %03o octal values. If it can, it will also + * translate certain \ characters (\r, \n, etc.) into \\%c. The + * routine is useful for printing out binary values. + * + * NOTE: It does _not_ add a \0 at the end of the output buffer. + * + * RETURNS: + * + * Returns the number of characters added to the output buffer. + * + * ARGUMENTS: + * + * buf - the buffer to convert. + * + * buf_size - size of the buffer. If < 0 then it will expand till it + * sees a \0 character. + * + * out - destination buffer for the convertion. + * + * out_size - size of the output buffer. + */ +int expand_chars(const void *buf, const int buf_size, + char *out, const int out_size) +{ + int buf_c; + const unsigned char *buf_p, *spec_p; + char *max_p, *out_p = out; + + /* setup our max pointer */ + max_p = out + out_size; + + /* run through the input buffer, counting the characters as we go */ + for (buf_c = 0, buf_p = (const unsigned char *)buf;; buf_c++, buf_p++) { + + /* did we reach the end of the buffer? */ + if (buf_size < 0) { + if (*buf_p == '\0') { + break; + } + } + else { + if (buf_c >= buf_size) { + break; + } + } + + /* search for special characters */ + for (spec_p = (unsigned char *)SPECIAL_CHARS + 1; + *(spec_p - 1) != '\0'; + spec_p += 2) { + if (*spec_p == *buf_p) { + break; + } + } + + /* did we find one? */ + if (*(spec_p - 1) != '\0') { + if (out_p + 2 >= max_p) { + break; + } + (void)sprintf(out_p, "\\%c", *(spec_p - 1)); + out_p += 2; + continue; + } + + /* print out any 7-bit printable characters */ + if (*buf_p < 128 && isprint(*buf_p)) { + if (out_p + 1 >= max_p) { + break; + } + *out_p = *(char *)buf_p; + out_p += 1; + } + else { + if (out_p + 4 >= max_p) { + break; + } + (void)sprintf(out_p, "\\%03o", *buf_p); + out_p += 4; + } + } + + return out_p - out; +} + +/* + * dump_table + * + * DESCRIPTION: + * + * Dump a table file to the screen. + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * tab_p - a table pointer that we are dumping. + */ +static void dump_table(table_t *tab_p) +{ + char buf[10240]; + void *key_p, *data_p; + int ret, key_size, data_size, len, entry_c; + + for (ret = table_first(tab_p, (void **)&key_p, &key_size, + (void **)&data_p, &data_size), entry_c = 0; + ret == TABLE_ERROR_NONE; + ret = table_next(tab_p, (void **)&key_p, &key_size, + (void **)&data_p, &data_size), entry_c++) { + /* expand the key */ + len = expand_chars(key_p, key_size, buf, sizeof(buf)); + (void)printf("%d: key '%.*s' (%d), ", entry_c, len, buf, len); + /* now dump the data */ + len = expand_chars(data_p, data_size, buf, sizeof(buf)); + (void)printf("data '%.*s' (%d)\n", len, buf, len); + } +} + +/* + * usage + * + * DESCRIPTION: + * + * Print the usage message to stderr. + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * tab_p - a table pointer that we are dumping. + */ +static void usage(void) +{ + (void)fprintf(stderr, + "Usage: table_util\n" + " [-b number] or --buckets num buckets to adjust table\n" + " [-o file] or --out-file output filename\n" + " [-v] or --verbose verbose messages\n" + " file input table filename\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + table_t *tab_p; + char do_write = 0, verbose = 0; + char *out_file = NULL, *in_file; + int ret, entry_n, bucket_n, num_buckets = 0; + + /* process the args */ + for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) { + + switch (*(*argv + 1)) { + + case 'b': + argc--, argv++; + if (argc == 0) { + usage(); + } + num_buckets = atoi(*argv); + break; + + case 'o': + argc--, argv++; + if (argc == 0) { + usage(); + } + out_file = *argv; + break; + + case 'v': + verbose = 1; + break; + + default: + usage(); + break; + } + } + + if (argc != 1) { + usage(); + } + + /* take the last argument as the input file */ + in_file = *argv; + + /* read in the table from disk */ + tab_p = table_read(in_file, &ret); + if (tab_p == NULL) { + (void)fprintf(stderr, "table_util: unable to table_read from '%s': %s\n", + in_file, table_strerror(ret)); + exit(1); + } + + /* get info about the table */ + ret = table_info(tab_p, &bucket_n, &entry_n); + if (ret != TABLE_ERROR_NONE) { + (void)fprintf(stderr, + "table_util: unable to get info on table in '%s': %s\n", + in_file, table_strerror(ret)); + exit(1); + } + + (void)printf("Read table of %d buckets and %d entries from '%s'\n", + bucket_n, entry_n, in_file); + + if (verbose) { + dump_table(tab_p); + } + + if (num_buckets > 0) { + /* adjust the table's buckets */ + ret = table_adjust(tab_p, num_buckets); + if (ret != TABLE_ERROR_NONE) { + (void)fprintf(stderr, + "table_util: unable to adjust table to %d buckets: %s\n", + num_buckets, table_strerror(ret)); + exit(1); + } + do_write = 1; + } + + /* did we modify the table at all */ + if (do_write) { + if (out_file == NULL) { + out_file = in_file; + } + + /* write out our table */ + ret = table_write(tab_p, out_file, WRITE_MODE); + if (ret != TABLE_ERROR_NONE) { + (void)fprintf(stderr, "table_util: unable to write table to '%s': %s\n", + out_file, table_strerror(ret)); + exit(1); + } + + (void)printf("Wrote table to '%s'\n", out_file); + } + + /* free the table */ + ret = table_free(tab_p); + if (ret != TABLE_ERROR_NONE) { + (void)fprintf(stderr, "table_util: unable to free table: %s\n", + table_strerror(ret)); + /* NOTE: not a critical error */ + } + + exit(0); +}