Sat, 17 Aug 2013 12:04:04 +0200
new dav_query function
--- a/dav/Makefile Fri Aug 16 12:41:30 2013 +0200 +++ b/dav/Makefile Sat Aug 17 12:04:04 2013 +0200 @@ -35,6 +35,7 @@ SRC += utils.c SRC += webdav.c SRC += methods.c +SRC += davql.c SRC += config.c SRC += crypto.c SRC += optparser.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/davql.c Sat Aug 17 12:04:04 2013 +0200 @@ -0,0 +1,139 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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 <stdio.h> +#include <stdlib.h> + +#include "davql.h" +#include "methods.h" +#include "webdav.h" +#include "utils.h" + +DavQuery dav_ql_parse(char *query, va_list ap) { + DavQuery davquery; + davquery.command = DAV_QUERY_ERROR; + davquery.command_data = NULL; + sstr_t q = sstr(query); + q = sstrtrim(q); + + // get query command + sstr_t cmd; + cmd.ptr = NULL; + int i; + for(i=0;i<q.length;i++) { + if(q.ptr[i] == ' ') { + cmd = sstrsubsl(q, 0, i); + break; + } + } + if(!cmd.ptr) { + fprintf(stderr, "DQL syntax error\n"); + return davquery; + } + + cmd = sstrtrim(cmd); + q = sstrtrim(sstrsubs(q, i)); + if(!sstrcmp(cmd, S("get"))) { + davquery.command = DAV_QUERY_GET; + davquery.command_data = dav_ql_parse_get(q, ap); + } + + return davquery; +} + +DavGetQuery* dav_ql_parse_get(sstr_t q, va_list ap) { + sstr_t property_query = q; + q = util_getsubstr_until_token(q, S("from"), &property_query); + + sstr_t from_query = q; + sstr_t cond = util_getsubstr_until_token(q, S("where"), &from_query); + + // insert variable values + UcxBuffer *fbuf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); + int var = 0; + for(int i=0;i<from_query.length;i++) { + char c = from_query.ptr[i]; + if(c == '%') { + if(var) { + ucx_buffer_putc(fbuf, '%'); // previous '%' + } else { + var = 1; + } + } else if(var) { + switch(c) { + case 's': { + char *arg = va_arg(ap, char*); + ucx_buffer_puts(fbuf, arg); + break; + } + default: { + ucx_buffer_putc(fbuf, '%'); + ucx_buffer_putc(fbuf, c); + } + } + var = 0; + } else { + ucx_buffer_putc(fbuf, c); + } + } + + DavGetQuery *getquery = malloc(sizeof(DavGetQuery)); + getquery->properties = sstrdup(property_query); + getquery->from = sstrn(fbuf->space, fbuf->pos); + // TODO: condition + return getquery; +} + +void free_get_query(DavGetQuery *q) { + free(q->from.ptr); + free(q->properties.ptr); + free(q); +} + +int parse_path_query(sstr_t query, char **path, int *depth) { + if(query.length == 1) { + if(query.ptr[0] == '/') { + *path = sstrdup(query).ptr; + *depth = 1; + return 0; + } else { + *path = NULL; + return 1; + } + } + + if(query.ptr[query.length-1] == '*') { + *depth = -1; + *path = sstrdup(sstrsubsl(query, 0, query.length-1)).ptr; + } else { + *path = sstrdup(query).ptr; + *depth = 1; + } + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/davql.h Sat Aug 17 12:04:04 2013 +0200 @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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 DAVQL_H +#define DAVQL_H + +#include <ucx/string.h> +#include <stdarg.h> + +#ifdef __cplusplus +extern "C" { +#endif + +enum DavQueryType { + DAV_QUERY_ERROR = 0, + DAV_QUERY_GET +}; +typedef enum DavQueryType DavQueryType; + +typedef struct { + DavQueryType command; + void *command_data; +} DavQuery; + +typedef struct { + sstr_t properties; + sstr_t from; + // TODO: condition +} DavGetQuery; + +DavQuery dav_ql_parse(char *query, va_list ap); +DavGetQuery* dav_ql_parse_get(sstr_t q, va_list ap); +void free_get_query(DavGetQuery *q); + +int parse_path_query(sstr_t query, char **path, int *depth); + + +#ifdef __cplusplus +} +#endif + +#endif /* DAVQL_H */ +
--- a/dav/main.c Fri Aug 16 12:41:30 2013 +0200 +++ b/dav/main.c Sat Aug 17 12:04:04 2013 +0200 @@ -42,6 +42,8 @@ #include "crypto.h" #include "main.h" +#include "davql.h" + static DavContext *ctx; void xmlerrorfnc(void * c, const char * msg, ... ) { @@ -202,7 +204,8 @@ //printf("baseurl: %s\n", sn->base_url); - DavResource *ls = dav_get(sn, path, "U:crypto-key"); + //DavResource *ls = dav_get(sn, path, "U:crypto-key"); + DavResource *ls = dav_query(sn, "get U:crypto-key from %s", path); if(!ls) { print_resource_error(sn, path); return -1;
--- a/dav/methods.c Fri Aug 16 12:41:30 2013 +0200 +++ b/dav/methods.c Sat Aug 17 12:04:04 2013 +0200 @@ -150,10 +150,12 @@ return buf; } -DavResource* parse_propfind_response(DavSession *sn, UcxBuffer *response) { +DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response) { char *url = NULL; curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url); - DavResource *root = resource_new_href(sn, util_url_path(url)); + if(!root) { + root = resource_new_href(sn, util_url_path(url)); + } xmlDoc *doc = xmlReadMemory(response->space, response->size, url, NULL, 0); if(!doc) {
--- a/dav/methods.h Fri Aug 16 12:41:30 2013 +0200 +++ b/dav/methods.h Sat Aug 17 12:04:04 2013 +0200 @@ -53,7 +53,7 @@ UcxBuffer* create_allprop_propfind_request(); UcxBuffer* create_propfind_request(UcxList *properties); -DavResource* parse_propfind_response(DavSession *sn, UcxBuffer *response); +DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response); int parse_response_tag(DavResource *resource, xmlNode *node); void set_davprops(DavResource *res);
--- a/dav/utils.c Fri Aug 16 12:41:30 2013 +0200 +++ b/dav/utils.c Sat Aug 17 12:04:04 2013 +0200 @@ -191,3 +191,48 @@ return out; } + +/* + * gets a substring from 0 to the appearance of the token + * tokens are separated by space + * sets sub to the substring and returns the remaining string + */ +sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) { + int i; + int token_start = -1; + int token_end = -1; + for(i=0;i<=str.length;i++) { + int c; + if(i == str.length) { + c = ' '; + } else { + c = str.ptr[i]; + } + if(c < 33) { + if(token_start != -1) { + token_end = i; + size_t len = token_end - token_start; + sstr_t tk = sstrsubsl(str, token_start, len); + //printf("token: {%.*s}\n", token.length, token.ptr); + if(!sstrcmp(tk, token)) { + *sub = sstrtrim(sstrsubsl(str, 0, token_start)); + break; + } + token_start = -1; + token_end = -1; + } + } else { + if(token_start == -1) { + token_start = i; + } + } + } + + if(i < str.length) { + return sstrtrim(sstrsubs(str, i)); + } else { + str.ptr = NULL; + str.length = 0; + return str; + } +}
--- a/dav/utils.h Fri Aug 16 12:41:30 2013 +0200 +++ b/dav/utils.h Sat Aug 17 12:04:04 2013 +0200 @@ -31,6 +31,7 @@ #include <sys/types.h> #include <libxml/tree.h> +#include <ucx/string.h> #ifdef __cplusplus extern "C" { @@ -50,6 +51,8 @@ char* util_base64decode(char* in); +sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub); + #ifdef __cplusplus } #endif
--- a/dav/webdav.c Fri Aug 16 12:41:30 2013 +0200 +++ b/dav/webdav.c Sat Aug 17 12:04:04 2013 +0200 @@ -34,6 +34,7 @@ #include "utils.h" #include "webdav.h" #include "methods.h" +#include "davql.h" #include "ucx/buffer.h" #include "ucx/utils.h" @@ -209,10 +210,87 @@ curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && status == 207) { //printf("response\n%s\n", rpbuf->space); - resource = parse_propfind_response(sn, rpbuf); + resource = parse_propfind_response(sn, NULL, rpbuf); + sn->error = DAV_OK; + } else { + session_set_error(sn, ret, status); + } + return resource; +} + +DavResource* dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf, char *path) { + char *url = util_concat_path(sn->base_url, path); + CURL *handle = sn->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + DavResource *resource = root; + CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf); + int status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && status == 207) { + //printf("response\n%s\n", rpbuf->space); + resource = parse_propfind_response(sn, resource, rpbuf); sn->error = DAV_OK; } else { session_set_error(sn, ret, status); + resource = NULL; + } + ucx_buffer_free(rpbuf); + return resource; +} + +UcxList* propfind_stack_push(UcxList *stack, DavResource *children) { + while(children) { + if(children->iscollection) { + stack = ucx_list_prepend(stack, children); + } + children = children->next; + } + return stack; +} + +DavResource* dav_get2(DavSession *sn, DavGetQuery *query) { + char *path; + int depth = 0; + if(parse_path_query(query->from, &path, &depth)) { + sn->error = DAV_ERROR; + return NULL; + } + + sstr_t ps = query->properties; + UcxBuffer *rqbuf; + if(!sstrcmp(ps, S("*"))) { + rqbuf = create_allprop_propfind_request(); + } else if(!sstrcmp(ps, S("-"))) { + rqbuf = create_propfind_request(NULL); + } else { + UcxList *proplist = parse_properties_string(sn->context, ps); + rqbuf = create_propfind_request(proplist); + } + + //fwrite(rqbuf->space, 1, rqbuf->size, stdout); + //printf("\n"); + + DavResource *resource = dav_propfind(sn, NULL, rqbuf, path); + free(path); + int error = 0; + if(resource && depth == -1) { + UcxList *stack = NULL; // stack with davResource* elements + stack = propfind_stack_push(stack, resource->children); + while(stack) { + DavResource *sr = stack->data; // get first element from the stack + stack = ucx_list_remove(stack, stack); // remove first element + // do propfind request for sr + sr = dav_propfind(sn, sr, rqbuf, sr->path); + if(!sr) { + error = 1; + printf("subrequest failed\n"); + break; + } + stack = propfind_stack_push(stack, sr->children); // add children + } } return resource; } @@ -247,6 +325,21 @@ return proplist; } +DavResource* dav_query(DavSession *sn, char *query, ...) { + va_list ap; + va_start(ap, query); + DavQuery q = dav_ql_parse(query, ap); + va_end(ap); + DavResource *res = NULL; + switch(q.command) { + case DAV_QUERY_GET: { + res = dav_get2(sn, q.command_data); + free_get_query(q.command_data); + break; + } + } + return res; +}
--- a/dav/webdav.h Fri Aug 16 12:41:30 2013 +0200 +++ b/dav/webdav.h Sat Aug 17 12:04:04 2013 +0200 @@ -36,6 +36,7 @@ #include <ucx/buffer.h> #include <curl/curl.h> #include <libxml/tree.h> +#include "davql.h" #ifdef __cplusplus extern "C" { @@ -149,9 +150,11 @@ void dav_session_destroy(DavSession *sn); DavResource* dav_get(DavSession *sn, char *path, char *properties); +DavResource* dav_get2(DavSession *sn, DavGetQuery *query); UcxList* parse_properties_string(DavContext *context, sstr_t str); +DavResource* dav_query(DavSession *sn, char *query, ...); DavResource* dav_resource_new(DavSession *sn, char *path); DavResource* resource_new_href(DavSession *sn, char *href);
--- a/ucx/allocator.h Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/allocator.h Sat Aug 17 12:04:04 2013 +0200 @@ -63,16 +63,19 @@ * @see UcxAllocator */ typedef void*(*ucx_allocator_malloc)(void *pool, size_t n); + /** * A function pointer to the allocators <code>calloc()</code> function. * @see UcxAllocator */ typedef void*(*ucx_allocator_calloc)(void *pool, size_t n, size_t size); + /** * A function pointer to the allocators <code>realloc()</code> function. * @see UcxAllocator */ typedef void*(*ucx_allocator_realloc)(void *pool, void *data, size_t n); + /** * A function pointer to the allocators <code>free()</code> function. * @see UcxAllocator
--- a/ucx/buffer.c Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/buffer.c Sat Aug 17 12:04:04 2013 +0200 @@ -91,17 +91,16 @@ } int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence) { - size_t npos = 0; + size_t npos; switch (whence) { - case SEEK_SET: - npos = 0; - break; case SEEK_CUR: npos = buffer->pos; break; case SEEK_END: npos = buffer->size; break; + default: + npos = 0; } npos += offset; @@ -210,51 +209,6 @@ } } -size_t ucx_buffer_generic_copy(void *s1, void *s2, - read_func readfnc, write_func writefnc, size_t bufsize) { - size_t ncp = 0; - char *buf = (char*)malloc(bufsize); - if(buf == NULL) { - return 0; - } - - size_t r; - while((r = readfnc(buf, 1, bufsize, s1)) != 0) { - r = writefnc(buf, 1, r, s2); - ncp += r; - if(r == 0) { - break; - } - } - - free(buf); - return ncp; +size_t ucx_buffer_puts(UcxBuffer *buffer, char *str) { + return ucx_buffer_write((const void*)str, 1, strlen(str), buffer); } - -size_t ucx_buffer_generic_ncopy(void *s1, void *s2, - read_func readfnc, write_func writefnc, size_t bufsize, size_t n) { - if(n == 0) { - return 0; - } - - size_t ncp = 0; - char *buf = (char*)malloc(bufsize); - if(buf == NULL) { - return 0; - } - - size_t r; - size_t rn = bufsize > n ? n : bufsize; - while((r = readfnc(buf, 1, rn, s1)) != 0) { - r = writefnc(buf, 1, r, s2); - ncp += r; - n -= r; - rn = bufsize > n ? n : bufsize; - if(r == 0 || n == 0) { - break; - } - } - - free(buf); - return ncp; -}
--- a/ucx/buffer.h Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/buffer.h Sat Aug 17 12:04:04 2013 +0200 @@ -26,6 +26,23 @@ * POSSIBILITY OF SUCH DAMAGE. */ +/** + * @file buffer.h + * + * Advanced buffer implementation. + * + * Instances of UcxBuffer can be used to read from or to write to like one + * would do with a stream. This allows the use of ucx_stream_copy() to copy + * contents from one buffer to another. + * + * Some features for convenient use of the buffer + * can be enabled. See the documentation of the macro constants for more + * information. + * + * @author Mike Becker + * @author Olaf Wintermann + */ + #ifndef UCX_BUFFER_H #define UCX_BUFFER_H @@ -37,94 +54,210 @@ extern "C" { #endif -/* no autoextend or autofree behaviour */ +/** + * No buffer features enabled (all flags cleared). + */ #define UCX_BUFFER_DEFAULT 0x00 -/* the buffer shall free the occupied memory space */ + +/** + * If this flag is enabled, the buffer will automatically free its contents. + */ #define UCX_BUFFER_AUTOFREE 0x01 -/* the buffer may automatically double its size on write operations */ + +/** + * If this flag is enabled, the buffer will automatically extends its capacity. + */ #define UCX_BUFFER_AUTOEXTEND 0x02 -/* the user shall not modify values, but may get the latest pointer */ +/** UCX Buffer. */ typedef struct { + /** A pointer to the buffer contents. */ char *space; + /** Current position of the buffer. */ size_t pos; + /** Current capacity (i.e. maximum size) of the buffer. */ size_t capacity; + /** Current size of the buffer content. */ size_t size; + /** + * Flag register for buffer features. + * @see #UCX_BUFFER_DEFAULT + * @see #UCX_BUFFER_AUTOFREE + * @see #UCX_BUFFER_AUTOEXTEND + */ int flags; } UcxBuffer; -/* if space is NULL, new space is allocated and the autofree flag is enforced */ +/** + * Creates a new buffer. + * + * <b>Note:</b> you may provide <code>NULL</code> as argument for + * <code>space</code>. Then this function will allocate the space and enforce + * the #UCX_BUFFER_AUTOFREE flag. + * + * @param space pointer to the memory area, or <code>NULL</code> to allocate + * new memory + * @param size the size of the buffer + * @param flags buffer features (see UcxBuffer.flags) + * @return the new buffer + */ UcxBuffer *ucx_buffer_new(void *space, size_t size, int flags); + +/** + * Destroys a buffer. + * + * If the #UCX_BUFFER_AUTOFREE feature is enabled, the contents of the buffer + * are also freed. + * + * @param buffer the buffer to destroy + */ void ucx_buffer_free(UcxBuffer* buffer); -/* - * the autofree flag is enforced for the new buffer - * if length is zero, the whole remaining buffer shall be extracted - * the position of the new buffer is set to zero +/** + * Creates a new buffer and fills it with extracted content from another buffer. + * + * <b>Note:</b> the #UCX_BUFFER_AUTOFREE feature is enforced for the new buffer. + * + * @param src the source buffer + * @param start the start position of extraction + * @param length the count of bytes to extract or 0 if all of the remaining + * bytes shall be extracted + * @param flags feature mask for the new buffer + * @return */ UcxBuffer* ucx_buffer_extract(UcxBuffer *src, size_t start, size_t length, int flags); + +/** + * A shorthand macro for the full extraction of the buffer. + * + * @param src the source buffer + * @param flags feature mask for the new buffer + * @return a new buffer with the extracted content + */ #define ucx_buffer_clone(src,flags) \ ucx_buffer_extract(src, 0, 0, flags) -/* - * Moves the position of the buffer to a new position relative to whence. +/** + * Moves the position of the buffer. + * + * The new position is relative to the <code>whence</code> argument. * - * SEEK_SET marks the start of the buffer - * SEEK_CUR marks the current position - * SEEK_END marks the first 0-byte in the buffer - * - * ucx_buffer_seek returns 0 on success and -1 if the new position is beyond the - * bounds of the allocated buffer. In that case the position of the buffer - * remains unchanged. + * SEEK_SET marks the start of the buffer. + * SEEK_CUR marks the current position. + * SEEK_END marks the first 0-byte in the buffer. + * + * @param buffer + * @param offset position offset relative to <code>whence</code> + * @param whence one of SEEK_SET, SEEK_CUR or SEEK_END + * @return 0 on success, non-zero if the position is invalid * */ int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence); -#define ucx_buffer_clear(buffer) memset(buffer->space, 0, buffer->size); \ - buffer->size = 0; buffer->pos = 0; +/** + * Clears the buffer by resetting the position and deleting the data. + * + * The data is deleted by a zeroing it with call to <code>memset()</code>. + * + * @param buffer the buffer to be cleared + */ +#define ucx_buffer_clear(buffer) memset(buffer->space, 0, buffer->size); \ + buffer->size = 0; buffer->pos = 0; -/* - * returns non-zero, if the current buffer position has exceeded the last - * available byte of the underlying buffer - * +/** + * Tests, if the buffer position has exceeded the buffer capacity. + * + * @param buffer the buffer to test + * @return non-zero, if the current buffer position has exceeded the last + * available byte of the buffer. */ int ucx_buffer_eof(UcxBuffer *buffer); -int ucx_buffere_extend(UcxBuffer *buffer, size_t len); +/** + * Extends the capacity of the buffer. + * + * <b>Note:</b> The buffer capacity increased by a power of two. I.e. + * the buffer capacity is doubled, as long as it would not hold the current + * content plus the additional required bytes. + * + * <b>Attention:</b> the argument provided is the count of <i>additional</i> + * bytes the buffer shall hold. It is <b>NOT</b> the total count of bytes the + * buffer shall hold. + * + * @param buffer the buffer to extend + * @param additional_bytes the count of additional bytes the buffer shall + * <i>at least</i> hold + * @return 0 on success or a non-zero value on failure + */ +int ucx_buffer_extend(UcxBuffer *buffer, size_t additional_bytes); +/** + * Writes data to an UcxBuffer. + * + * The position of the buffer is increased by the number of bytes read. + * + * @param ptr a pointer to the memory area containing the bytes to be written + * @param size the length of one element + * @param nitems the element count + * @param buffer the UcxBuffer to write to + * @return the total count of bytes written + */ size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems, UcxBuffer *buffer); +/** + * Reads data from an UcxBuffer. + * + * The position of the buffer is increased by the number of bytes read. + * + * @param ptr a pointer to the memory area where to store the read data + * @param size the length of one element + * @param nitems the element count + * @param buffer the UcxBuffer to read from + * @return the total count of bytes read + */ size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems, UcxBuffer *buffer); -int ucx_buffer_putc(UcxBuffer *b, int c); -int ucx_buffer_getc(UcxBuffer *b); - - -/* - * copies all bytes from s1 to s2 - * uses the read function r to read from s1 und writes the data using the - * write function w to s2 - * returns the number of bytes copied +/** + * Writes a character to a buffer. + * + * The least significant byte of the argument is written to the buffer. If the + * end of the buffer is reached and #UCX_BUFFER_AUTOEXTEND feature is enabled, + * the buffer capacity is extended by ucx_buffer_extend(). If the feature is + * disabled or buffer extension fails, <code>EOF</code> is returned. + * + * On successful write the position of the buffer is increased. + * + * @param buffer the buffer to write to + * @param c the character to write as <code>int</code> value + * @return the byte that has bean written as <code>int</code> value or + * <code>EOF</code> when the end of the stream is reached and automatic + * extension is not enabled or not possible */ -size_t ucx_buffer_generic_copy(void *s1, void *s2, read_func r, write_func w, - size_t bufsize); +int ucx_buffer_putc(UcxBuffer *buffer, int c); -size_t ucx_buffer_generic_ncopy(void *s1, void *s2, read_func r, write_func w, - size_t bufsize, size_t n); - -#define UCX_DEFAULT_BUFFER_SIZE 0x1000 +/** + * Gets a character from a buffer. + * + * The current position of the buffer is increased after a successful read. + * + * @param buffer the buffer to read from + * @return the character as <code>int</code> value or <code>EOF</code>, if the + * end of the buffer is reached + */ +int ucx_buffer_getc(UcxBuffer *buffer); -#define ucx_buffer_copy(s1,s2,r,w) \ - ucx_buffer_generic_copy(s1, s2, (read_func)r, (write_func)w, \ - UCX_DEFAULT_BUFFER_SIZE) - -#define ucx_buffer_ncopy(s1,s2,r,w, n) \ - ucx_buffer_generic_ncopy(s1, s2, (read_func)r, (write_func)w, \ - UCX_DEFAULT_BUFFER_SIZE, n) +/** + * Writes a string to a buffer. + * + * @param buffer the buffer + * @param str the string + * @return the number of bytes written + */ +size_t ucx_buffer_puts(UcxBuffer *buffer, char *str); #ifdef __cplusplus }
--- a/ucx/list.h Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/list.h Sat Aug 17 12:04:04 2013 +0200 @@ -64,6 +64,7 @@ * @see UcxList */ typedef struct UcxList UcxList; + /** * UCX list structure. */ @@ -99,6 +100,7 @@ * @return a pointer to the copy */ UcxList *ucx_list_clone(UcxList *list, copy_func cpyfnc, void* data); + /** * Creates an element-wise copy of a list using an UcxAllocator. * @@ -152,6 +154,7 @@ * @param list the list to free */ void ucx_list_free(UcxList *list); + /** * Destroys the entire list using an UcxAllocator. * @@ -162,6 +165,7 @@ * @see ucx_list_free() */ void ucx_list_free_a(UcxAllocator *allocator, UcxList *list); + /** * Inserts an element at the end of the list. * @@ -175,6 +179,7 @@ * the newly created list otherwise */ UcxList *ucx_list_append(UcxList *list, void *data); + /** * Inserts an element at the end of the list using an UcxAllocator. * @@ -189,6 +194,7 @@ * @see ucx_list_append() */ UcxList *ucx_list_append_a(UcxAllocator *allocator, UcxList *list, void *data); + /** * Inserts an element at the beginning of the list. * @@ -204,6 +210,7 @@ * @return a pointer to the new list head */ UcxList *ucx_list_prepend(UcxList *list, void *data); + /** * Inserts an element at the beginning of the list using an UcxAllocator. * @@ -217,6 +224,7 @@ * @see ucx_list_prepend() */ UcxList *ucx_list_prepend_a(UcxAllocator *allocator, UcxList *list, void *data); + /** * Concatenates two lists. * @@ -232,6 +240,7 @@ * returned, otherwise <code>list1</code> is returned */ UcxList *ucx_list_concat(UcxList *list1, UcxList *list2); + /** * Returns the first element of a list. * @@ -243,6 +252,7 @@ * @return the first element of the list, the specified element is a member of */ UcxList *ucx_list_first(const UcxList *elem); + /** * Returns the last element of a list. * @@ -254,6 +264,7 @@ * @return the last element of the list, the specified element is a member of */ UcxList *ucx_list_last(const UcxList *elem); + /** * Returns the list element at the specified index. * @@ -263,6 +274,7 @@ * index is greater than the list size */ UcxList *ucx_list_get(const UcxList *list, int index); + /** * Returns the index of an element. * @@ -272,6 +284,7 @@ * element */ ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem); + /** * Returns the element count of the list. * @@ -279,6 +292,7 @@ * @return the element count */ size_t ucx_list_size(const UcxList *list); + /** * Returns the index of an element containing the specified data. * @@ -297,6 +311,7 @@ * data is not found in this list */ ssize_t ucx_list_find(UcxList *list, void *elem, cmp_func cmpfnc, void *data); + /** * Checks, if a list contains a specific element. * @@ -340,6 +355,7 @@ * is now empty */ UcxList *ucx_list_remove(UcxList *list, UcxList *element); + /** * Removes an element from the list using an UcxAllocator. *
--- a/ucx/logging.c Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/logging.c Sat Aug 17 12:04:04 2013 +0200 @@ -64,7 +64,7 @@ void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file, const unsigned int line, const char *format, ...) { if (level <= logger->level) { - const size_t max = 4096; // estimated maximum message length + const size_t max = 4096; // estimated max. message length (documented) char msg[max]; char *text; size_t k = 0;
--- a/ucx/logging.h Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/logging.h Sat Aug 17 12:04:04 2013 +0200 @@ -44,14 +44,19 @@ #endif /* leave enough space for custom log levels */ + /** Log level for error messages. */ #define UCX_LOGGER_ERROR 0x00 + /** Log level for warning messages. */ #define UCX_LOGGER_WARN 0x10 + /** Log level for information messages. */ #define UCX_LOGGER_INFO 0x20 + /** Log level for debug messages. */ #define UCX_LOGGER_DEBUG 0x30 + /** Log level for trace messages. */ #define UCX_LOGGER_TRACE 0x40 @@ -61,12 +66,14 @@ * @see UcxLogger.mask */ #define UCX_LOGGER_LEVEL 0x01 + /** * Output flag for the timestmap. * If this flag is set, the log message will contain the timestmap. * @see UcxLogger.mask */ #define UCX_LOGGER_TIMESTAMP 0x02 + /** * Output flag for the source. * If this flag is set, the log message will contain the source file and line @@ -81,24 +88,28 @@ typedef struct { /** The stream this logger writes its messages to.*/ void *stream; + /** * The write function that shall be used. * For standard file or stdout loggers this might be standard fwrite * (default). */ write_func writer; + /** * The date format for timestamp outputs * (default: <code>"%F %T %z "</code>). * @see UCX_LOGGER_TIMESTAMP */ char *dateformat; + /** * The level, this logger operates on. * If a log command is issued, the message will only be logged, if the log * level of the message is less or equal than the log level of the logger. */ unsigned int level; + /** * A configuration mask for automatic output. * For each flag that is set, the logger automatically outputs some extra @@ -106,6 +117,7 @@ * See the documentation for the flags for details. */ unsigned int mask; + /** * A map of valid log levels for this logger. * @@ -128,6 +140,7 @@ * @return a new logger object */ UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask); + /** * Destroys the logger. * @@ -148,6 +161,9 @@ * * <code>[LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message</code> * + * <b>Attention:</b> the message (including automatically generated information) + * <b>MUST NOT</b> exceed the size of 4 KB. + * * @param logger the logger to use * @param level the level to log on * @param file information about the source file @@ -177,6 +193,7 @@ */ #define ucx_logger_error(logger, ...) \ ucx_logger_log(logger, UCX_LOGGER_ERROR, __VA_ARGS__) + /** * Shortcut for logging an information message. * @param logger the logger to use @@ -185,6 +202,7 @@ */ #define ucx_logger_info(logger, ...) \ ucx_logger_log(logger, UCX_LOGGER_INFO, __VA_ARGS__) + /** * Shortcut for logging a warning message. * @param logger the logger to use @@ -193,6 +211,7 @@ */ #define ucx_logger_warn(logger, ...) \ ucx_logger_log(logger, UCX_LOGGER_WARN, __VA_ARGS__) + /** * Shortcut for logging a debug message. * @param logger the logger to use @@ -201,6 +220,7 @@ */ #define ucx_logger_debug(logger, ...) \ ucx_logger_log(logger, UCX_LOGGER_DEBUG, __VA_ARGS__) + /** * Shortcut for logging a trace message. * @param logger the logger to use
--- a/ucx/map.c Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/map.c Sat Aug 17 12:04:04 2013 +0200 @@ -45,7 +45,7 @@ } UcxMap *map = (UcxMap*)allocator->malloc(allocator->pool, sizeof(UcxMap)); - if(map == NULL) { + if (!map) { return NULL; } @@ -89,8 +89,7 @@ UcxMapIterator i = ucx_map_iterator(from); void *value; UCX_MAP_FOREACH(key, value, i) { - int ret = ucx_map_put(to, i.cur->key, fnc ? fnc(value, data) : value); - if(ret != 0) { + if (ucx_map_put(to, key, fnc ? fnc(value, data) : value)) { return 1; } } @@ -100,7 +99,7 @@ UcxMap *ucx_map_clone(UcxMap *map, copy_func fnc, void *data) { size_t bs = (map->count * 5) >> 1; UcxMap *newmap = ucx_map_new(bs > map->size ? bs : map->size); - if(newmap == NULL) { + if (!newmap) { return NULL; } ucx_map_copy(map, newmap, fnc, data); @@ -121,7 +120,7 @@ map->allocator->pool, map->size, sizeof(UcxMapElement*)); - if(map->map == NULL) { + if (!map->map) { *map = oldmap; return 1; } @@ -137,7 +136,7 @@ int ucx_map_put(UcxMap *map, UcxKey key, void *data) { UcxAllocator *allocator = map->allocator; - if(key.hash == 0) { + if (key.hash == 0) { key.hash = ucx_hash((char*)key.data, key.len); } @@ -145,16 +144,16 @@ UcxMapElement *restrict elm = map->map[slot]; UcxMapElement *restrict prev = NULL; - while (elm != NULL && elm->key.hash < key.hash) { + while (elm && elm->key.hash < key.hash) { prev = elm; elm = elm->next; } - if (elm == NULL || elm->key.hash != key.hash) { + if (!elm || elm->key.hash != key.hash) { UcxMapElement *e = (UcxMapElement*)allocator->malloc( allocator->pool, sizeof(UcxMapElement)); - if(e == NULL) { + if (!e) { return -1; } e->key.data = NULL; @@ -167,9 +166,9 @@ elm = e; } - if(elm->key.data == NULL) { + if (!elm->key.data) { void *kd = allocator->malloc(allocator->pool, key.len); - if (kd == NULL) { + if (!kd) { return -1; } memcpy(kd, key.data, key.len); @@ -201,6 +200,7 @@ } else { map->map[slot] = elm->next; } + map->allocator->free(map->allocator->pool, elm->key.data); map->allocator->free(map->allocator->pool, elm); map->count--; } @@ -285,31 +285,31 @@ int ucx_map_iter_next(UcxMapIterator *i, UcxKey *key, void **elm) { UcxMapElement *e = i->cur; - if(e == NULL) { + if (e) { + e = e->next; + } else { e = i->map->map[0]; - } else { - e = e->next; } - while(i->index < i->map->size) { - if(e != NULL) { - if(e->data != NULL) { + while (i->index < i->map->size) { + if (e) { + if (e->data) { i->cur = e; *elm = e->data; *key = e->key; - return 0; + return 1; } e = e->next; } else { i->index++; - if(i->index < i->map->size) { + if (i->index < i->map->size) { e = i->map->map[i->index]; } } } - return 1; + return 0; }
--- a/ucx/map.h Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/map.h Sat Aug 17 12:04:04 2013 +0200 @@ -50,36 +50,81 @@ extern "C" { #endif -#define UCX_MAP_FOREACH(key,elm,iter) \ - for(UcxKey key;ucx_map_iter_next(&iter,&key, (void**)&elm)==0;) +/** + * Loop statement for UCX maps. + * + * The <code>key</code> variable is implicitly defined, but the + * <code>value</code> variable must be already declared as type information + * cannot be inferred. + * + * @param key the variable name for the key + * @param value the variable name for the value + * @param iter an UcxMapIterator + * @see ucx_map_iterator() + */ +#define UCX_MAP_FOREACH(key,value,iter) \ + for(UcxKey key;ucx_map_iter_next(&iter,&key, (void**)&value);) +/** Type for the UCX map. @see UcxMap */ typedef struct UcxMap UcxMap; + +/** Type for a key of an UcxMap. @see UcxKey */ typedef struct UcxKey UcxKey; + +/** Type for an element of an UcxMap. @see UcxMapElement */ typedef struct UcxMapElement UcxMapElement; + +/** Type for an iterator over an UcxMap. @see UcxMapIterator */ typedef struct UcxMapIterator UcxMapIterator; +/** Structure for the UCX map. */ struct UcxMap { + /** An allocator that is used for the map elements. */ UcxAllocator *allocator; + /** The array of map element lists. */ UcxMapElement **map; + /** The size of the map is the length of the element list array. */ size_t size; + /** The count of elements currently stored in this map. */ size_t count; }; +/** Structure for a key of an UcxMap. */ struct UcxKey { + /** The key data. */ void *data; + /** The length of the key data. */ size_t len; + /** The hash value of the key data. */ int hash; }; +/** Structure for an element of an UcxMap. */ struct UcxMapElement { + /** The value data. */ void *data; + + /** A pointer to the next element in the current list. */ UcxMapElement *next; + + /** The corresponding key. */ UcxKey key; }; +/** Structure for an iterator over an UcxMap. */ struct UcxMapIterator { + /** The map to iterate over. */ UcxMap *map; + + /** The current map element. */ UcxMapElement *cur; + + /** + * The current index of the element list array. + * <b>Attention: </b> this is <b>NOT</b> the element index! Do <b>NOT</b> + * manually iterate over the map by increasing this index. Use + * ucx_map_iter_next(). + * @see UcxMap.map*/ size_t index; }; @@ -108,14 +153,83 @@ */ void ucx_map_free(UcxMap *map); -/* you cannot clone maps with more than 390 mio entries */ +/** + * Copies contents from a map to another map using a copy function. + * + * <b>Note:</b> The destination map does not need to be empty. However, if it + * contains data with keys that are also present in the source map, the contents + * are overwritten. + * + * @param from the source map + * @param to the destination map + * @param fnc the copy function or <code>NULL</code> if the pointer address + * shall be copied + * @param data additional data for the copy function + * @return 0 on success or a non-zero value on memory allocation errors + */ int ucx_map_copy(UcxMap *restrict from, UcxMap *restrict to, copy_func fnc, void *data); + +/** + * Clones the map and rehashes if necessary. + * + * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant. + * This function <i>always</i> ensures a new UcxMap.size of at least + * 2.5*UcxMap.count. + * + * @param map the map to clone + * @param fnc the copy function to use or <code>NULL</code> if the new and + * the old map shall share the data pointers + * @param data additional data for the copy function + * @return the cloned map + * @see ucx_map_copy() + */ UcxMap *ucx_map_clone(UcxMap *map, copy_func fnc, void *data); + +/** + * Increases size of the hash map, if necessary. + * + * The load value is 0.75*UcxMap.size. If the element count exceeds the load + * value, the map needs to be rehashed. Otherwise no action is performed and + * this function simply returns 0. + * + * The rehashing process ensures, that the UcxMap.size is at least + * 2.5*UcxMap.count. So there is enough room for additional elements without + * the need of another soon rehashing. + * + * You can use this function to dramatically increase access performance. + * + * @param map the map to rehash + * @return 1, if a memory allocation error occurred, 0 otherwise + */ int ucx_map_rehash(UcxMap *map); -int ucx_map_put(UcxMap *map, UcxKey key, void *data); +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +int ucx_map_put(UcxMap *map, UcxKey key, void *value); + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ void* ucx_map_get(UcxMap *map, UcxKey key); + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + * @return the removed value + */ void* ucx_map_remove(UcxMap *map, UcxKey key); /** @@ -123,84 +237,151 @@ * @param map the map * @param key the key * @param value the value + * @return 0 on success, non-zero value on failure * @see ucx_map_put() */ #define ucx_map_sstr_put(map, key, value) \ ucx_map_put(map, ucx_key(key.ptr, key.length), (void*)value) + /** * Shorthand for putting data with a C string key into the map. * @param map the map * @param key the key * @param value the value + * @return 0 on success, non-zero value on failure * @see ucx_map_put() */ #define ucx_map_cstr_put(map, key, value) \ ucx_map_put(map, ucx_key((void*)key, strlen(key)), (void*)value) + /** * Shorthand for putting data with an integer key into the map. * @param map the map * @param key the key * @param value the value + * @return 0 on success, non-zero value on failure * @see ucx_map_put() */ #define ucx_map_int_put(map, key, value) \ ucx_map_put(map, ucx_key((void*)&key, sizeof(key)), (void*)value) - /** * Shorthand for getting data from the map with a sstr_t key. * @param map the map * @param key the key + * @return the value * @see ucx_map_get() */ #define ucx_map_sstr_get(map, key) \ ucx_map_get(map, ucx_key(key.ptr, key.length)) + /** * Shorthand for getting data from the map with a C string key. + * @param map the map + * @param key the key + * @return the value * @see ucx_map_get() */ #define ucx_map_cstr_get(map, key) \ ucx_map_get(map, ucx_key((void*)key, strlen(key))) + /** * Shorthand for getting data from the map with an integer key. * @param map the map * @param key the key + * @return the value * @see ucx_map_get() */ #define ucx_map_int_get(map, key) \ ucx_map_get(map, ucx_key((void*)&key, sizeof(int))) + /** * Shorthand for removing data from the map with a sstr_t key. * @param map the map * @param key the key + * @return the removed value * @see ucx_map_remove() */ #define ucx_map_sstr_remove(map, key) \ ucx_map_remove(map, ucx_key(key.ptr, key.length)) + /** * Shorthand for removing data from the map with a C string key. * @param map the map * @param key the key + * @return the removed value * @see ucx_map_remove() */ #define ucx_map_cstr_remove(map, key) \ ucx_map_remove(map, ucx_key((void*)key, strlen(key))) + /** * Shorthand for removing data from the map with an integer key. * @param map the map * @param key the key + * @return the removed value * @see ucx_map_remove() */ #define ucx_map_int_remove(map, key) \ ucx_map_remove(map, ucx_key((void*)&key, sizeof(key))) +/** + * Creates an UcxKey based on the given data. + * + * This function implicitly computes the hash. + * + * @param data the data for the key + * @param len the length of the data + * @return an UcxKey with implicitly computed hash + * @see ucx_hash() + */ UcxKey ucx_key(void *data, size_t len); +/** + * Computes a murmur hash-2. + * + * @param data the data to hash + * @param len the length of the data + * @return the murmur hash-2 of the data + */ int ucx_hash(const char *data, size_t len); +/** + * Creates an iterator for a map. + * + * <b>Note:</b> An UcxMapIterator iterates over all elements in all element + * lists successively. Therefore the order highly depends on the key hashes and + * may vary under different map sizes. So generally you may <b>NOT</b> rely on + * the iteration order. + * + * <b>Note:</b> The iterator is <b>NOT</b> initialized. You need to call + * ucx_map_iter_next() at least once before accessing any information. However, + * it is not recommended to access the fields of an UcxMapIterator directly. + * + * @param map the map to create the iterator for + * @return an iterator initialized on the first element of the + * first element list + * @see ucx_map_iter_next() + */ UcxMapIterator ucx_map_iterator(UcxMap *map); -int ucx_map_iter_next(UcxMapIterator *i, UcxKey *key, void **elm); +/** + * Proceeds to the next element of the map (if any). + * + * Subsequent calls on the same iterator proceed to the next element and + * store the key/value-pair into the memory specified as arguments of this + * function. + * + * If no further elements are found, this function returns zero and leaves the + * last found key/value-pair in memory. + * + * @param iterator the iterator to use + * @param key a pointer to the memory where to store the key + * @param value a pointer to the memory where to store the value + * @return 1, if another element was found, 0 if all elements has been processed + * @see ucx_map_iterator() + */ +int ucx_map_iter_next(UcxMapIterator *iterator, UcxKey *key, void **value); #ifdef __cplusplus
--- a/ucx/mempool.c Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/mempool.c Sat Aug 17 12:04:04 2013 +0200 @@ -36,13 +36,23 @@ #include "mempool.h" +/** Capsule for destructible memory chunks. */ typedef struct { + /** The destructor for the memory chunk. */ ucx_destructor destructor; + /** + * First byte of the memory chunk. + * Note, that the address <code>&c</code> is also the address + * of the whole memory chunk. + */ char c; } ucx_memchunk; +/** Capsule for data and its destructor. */ typedef struct { + /** The destructor for the data. */ ucx_destructor destructor; + /** A pointer to the data. */ void *ptr; } ucx_regdestr; @@ -80,16 +90,18 @@ } void *ucx_mempool_malloc(UcxMempool *pool, size_t n) { + if (pool->ndata >= pool->size) { + // The hard coded 16 is documented for this function and ucx_mempool_new + if (ucx_mempool_chcap(pool, pool->size + 16) == EXIT_FAILURE) { + return NULL; + } + } + ucx_memchunk *mem = (ucx_memchunk*)malloc(sizeof(ucx_destructor) + n); if (!mem) { return NULL; } - if (pool->ndata >= pool->size) { - // The hard coded 16 is documented for this function and ucx_mempool_new - ucx_mempool_chcap(pool, pool->size + 16); - } - mem->destructor = NULL; pool->data[pool->ndata] = mem; pool->ndata++; @@ -121,7 +133,7 @@ } fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n", (intptr_t)ptr, (intptr_t)pool); - exit(1); + exit(EXIT_FAILURE); } else { return newm + sizeof(ucx_destructor); } @@ -132,12 +144,13 @@ for(size_t i=0 ; i<pool->ndata ; i++) { if(chunk == pool->data[i]) { if(chunk->destructor != NULL) { - chunk->destructor(&chunk->c); + chunk->destructor(&(chunk->c)); } free(chunk); size_t last_index = pool->ndata - 1; if(i != last_index) { pool->data[i] = pool->data[last_index]; + pool->data[last_index] = NULL; } pool->ndata--; return;
--- a/ucx/mempool.h Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/mempool.h Sat Aug 17 12:04:04 2013 +0200 @@ -59,8 +59,10 @@ typedef struct { /** List of pointers to pooled memory. */ void **data; + /** Count of pooled memory items. */ size_t ndata; + /** Memory pool size. */ size_t size; } UcxMempool; @@ -92,6 +94,19 @@ int ucx_mempool_chcap(UcxMempool *pool, size_t newcap); /** + * Changes the pool size to the next smallest multiple of 16. + * + * You may use this macro, to reduce the pool size after freeing + * many pooled memory items. + * + * @param pool the pool to clamp + * @return <code>EXIT_SUCCESS</code> on success or + * <code>EXIT_FAILURE</code> on failure + */ +#define ucx_mempool_clamp(pool) ucx_mempool_chcap(pool, \ + (pool->ndata & ~0xF)+0x10) + +/** * Allocates pooled memory. * * @param pool the memory pool @@ -112,9 +127,15 @@ * @see ucx_allocator_calloc() */ void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize); + /** * Reallocates pooled memory. * + * If the memory to be reallocated is not contained by the specified pool, this + * function will possibly fail. In case the memory had to be moved to another + * location, this function will print out a message to <code>stderr</code> + * and exit the program with error code <code>EXIT_FAILURE</code>. + * * @param pool the memory pool * @param ptr a pointer to the memory that shall be reallocated * @param n the new size of the memory @@ -122,6 +143,7 @@ * @see ucx_allocator_realloc() */ void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n); + /** * Frees pooled memory. *
--- a/ucx/properties.h Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/properties.h Sat Aug 17 12:04:04 2013 +0200 @@ -56,52 +56,62 @@ * Automatically set by calls to ucx_properties_fill(). */ char *buffer; + /** * Length of the input buffer (don't set manually). * Automatically set by calls to ucx_properties_fill(). */ size_t buflen; + /** * Current buffer position (don't set manually). * Used by ucx_properties_next(). */ size_t pos; + /** * Internal temporary buffer (don't set manually). * Used by ucx_properties_next(). */ char *tmp; + /** * Internal temporary buffer length (don't set manually). * Used by ucx_properties_next(). */ size_t tmplen; + /** * Internal temporary buffer capacity (don't set manually). * Used by ucx_properties_next(). */ size_t tmpcap; + /** * Parser error code. * This is always 0 on success and a nonzero value on syntax errors. * The value is set by ucx_properties_next(). */ int error; + /** * The delimiter that shall be used. * This is '=' by default. */ char delimiter; + /** * The first comment character. * This is '#' by default. */ char comment1; + /** * The second comment character. * This is not set by default. */ char comment2; + /** * The third comment character. * This is not set by default. @@ -115,11 +125,13 @@ * @return a pointer to the new UcxProperties object */ UcxProperties *ucx_properties_new(); + /** * Destroys an UcxProperties object. * @param prop the UcxProperties object to destroy */ void ucx_properties_free(UcxProperties *prop); + /** * Sets the input buffer for the properties parser. * @@ -136,6 +148,7 @@ * @see ucx_properties2map() */ void ucx_properties_fill(UcxProperties *prop, char *buf, size_t len); + /** * Retrieves the next key/value-pair. * @@ -155,6 +168,7 @@ * @see ucx_properties_fill() */ int ucx_properties_next(UcxProperties *prop, sstr_t *name, sstr_t *value); + /** * Retrieves all available key/value-pairs and puts them into an UcxMap. * @@ -184,6 +198,7 @@ * @see ucx_properties2map() */ int ucx_properties_load(UcxMap *map, FILE *file); + /** * Stores an UcxMap to a file. *
--- a/ucx/string.c Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/string.c Sat Aug 17 12:04:04 2013 +0200 @@ -252,3 +252,34 @@ return newstr; } + +int sstrprefix(sstr_t string, sstr_t prefix) { + if (string.length == 0) { + return prefix.length == 0; + } + if (prefix.length == 0) { + return 1; + } + + if (prefix.length > string.length) { + return 0; + } else { + return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; + } +} + +int sstrsuffix(sstr_t string, sstr_t suffix) { + if (string.length == 0) { + return suffix.length == 0; + } + if (suffix.length == 0) { + return 1; + } + + if (suffix.length > string.length) { + return 0; + } else { + return memcmp(string.ptr+string.length-suffix.length, + suffix.ptr, suffix.length) == 0; + } +}
--- a/ucx/string.h Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/string.h Sat Aug 17 12:04:04 2013 +0200 @@ -54,6 +54,7 @@ /** Shortcut for a <code>sstr_t struct</code> literal. */ #define ST(s) { (char*)s, sizeof(s)-1 } + /** Shortcut for the conversion of a C string to a <code>sstr_t</code>. */ #define S(s) sstrn((char*)s, sizeof(s)-1) @@ -338,6 +339,22 @@ */ sstr_t sstrtrim(sstr_t string); +/** + * Checks, if a string has a specific prefix. + * @param string the string to check + * @param prefix the prefix the string should have + * @return 1, if and only if the string has the specified prefix, 0 otherwise + */ +int sstrprefix(sstr_t string, sstr_t prefix); + +/** + * Checks, if a string has a specific suffix. + * @param string the string to check + * @param suffix the suffix the string should have + * @return 1, if and only if the string has the specified suffix, 0 otherwise + */ +int sstrsuffix(sstr_t string, sstr_t suffix); + #ifdef __cplusplus } #endif
--- a/ucx/test.c Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/test.c Sat Aug 17 12:04:04 2013 +0200 @@ -28,12 +28,6 @@ #include "test.h" - -struct UcxTestList{ - UcxTest test; - UcxTestList *next; -}; - UcxTestSuite* ucx_test_suite_new() { UcxTestSuite* suite = (UcxTestSuite*) malloc(sizeof(UcxTestSuite)); if (suite != NULL) {
--- a/ucx/test.h Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/test.h Sat Aug 17 12:04:04 2013 +0200 @@ -81,6 +81,7 @@ #endif #ifndef __FUNCTION__ + /** * Alias for the <code>__func__</code> preprocessor macro. * Some compilers use <code>__func__</code> and others use __FUNC__. @@ -90,21 +91,36 @@ #define __FUNCTION__ __func__ #endif +/** Type for the UcxTestSuite. */ +typedef struct UcxTestSuite UcxTestSuite; + +/** Pointer to a test function. */ +typedef void(*UcxTest)(UcxTestSuite*,FILE*); + /** Type for the internal list of test cases. */ typedef struct UcxTestList UcxTestList; -/** Type for the UcxTestSuite. */ -typedef struct UcxTestSuite UcxTestSuite; -/** Pointer to a test function. */ -typedef void(*UcxTest)(UcxTestSuite*,FILE*); + +/** Structure for the internal list of test cases. */ +struct UcxTestList { + + /** Test case. */ + UcxTest test; + + /** Pointer to the next list element. */ + UcxTestList *next; +}; /** * A test suite containing multiple test cases. */ struct UcxTestSuite { + /** The number of successful tests after the suite has been run. */ unsigned int success; + /** The number of failed tests after the suite has been run. */ unsigned int failure; + /** * Internal list of test cases. * Use ucx_test_register() to add tests to this list. @@ -117,9 +133,10 @@ * @return a new test suite */ UcxTestSuite* ucx_test_suite_new(); + /** * Destroys a test suite. - * @param the test suite to destroy + * @param suite the test suite to destroy */ void ucx_test_suite_free(UcxTestSuite* suite); @@ -132,6 +149,7 @@ * <code>EXIT_FAILURE</code> on failure */ int ucx_test_register(UcxTestSuite* suite, UcxTest test); + /** * Runs a test suite and writes the test log to the specified stream. * @param suite the test suite to run
--- a/ucx/utils.c Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/utils.c Sat Aug 17 12:04:04 2013 +0200 @@ -27,7 +27,10 @@ */ #include "utils.h" -#include "math.h" +#include <math.h> +#include <stdio.h> +#include <limits.h> +#include <errno.h> /* COPY FUCNTIONS */ void* ucx_strcpy(void* s, void* data) { @@ -45,7 +48,37 @@ return cpy; } -/* COMPARE FUNCTION */ +size_t ucx_stream_copy(void *src, void *dest, read_func readfnc, + write_func writefnc, char* buf, size_t bufsize, size_t n) { + if(n == 0 || bufsize == 0) { + return 0; + } + + size_t ncp = 0; + if (!buf) { + buf = (char*)malloc(bufsize); + if(buf == NULL) { + return 0; + } + } + + size_t r; + size_t rn = bufsize > n ? n : bufsize; + while((r = readfnc(buf, 1, rn, src)) != 0) { + r = writefnc(buf, 1, r, dest); + ncp += r; + n -= r; + rn = bufsize > n ? n : bufsize; + if(r == 0 || n == 0) { + break; + } + } + + free(buf); + return ncp; +} + +/* COMPARE FUNCTIONS */ int ucx_strcmp(void *s1, void *s2, void *data) { return strcmp((char*)s1, (char*)s2); @@ -98,3 +131,86 @@ int ucx_memcmp(void *ptr1, void *ptr2, void *n) { return memcmp(ptr1, ptr2, *((size_t*)n)); } + +/* PRINTF FUNCTIONS */ + +#define UCX_PRINTF_BUFSIZE 256 + +int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...) { + va_list ap; + int ret; + va_start(ap, fmt); + ret = ucx_vfprintf(stream, wfc, fmt, ap); + va_end(ap); + return ret; +} + +int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap) { + char buf[UCX_PRINTF_BUFSIZE]; + va_list ap2; + va_copy(ap2, ap); + int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); + if (ret < 0) { + return ret; + } else if (ret < UCX_PRINTF_BUFSIZE) { + return (int)wfc(buf, 1, ret, stream); + } else { + if (ret == INT_MAX) { + errno = ENOMEM; + return -1; + } + + int len = ret + 1; + char *newbuf = (char*)malloc(len); + if (!newbuf) { + return -1; + } + + ret = vsnprintf(newbuf, len, fmt, ap2); + va_end(ap2); + if (ret > 0) { + ret = (int)wfc(newbuf, 1, ret, stream); + } + free(newbuf); + } + return ret; +} + +sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...) { + va_list ap; + sstr_t ret; + va_start(ap, fmt); + ret = ucx_vasprintf(allocator, fmt, ap); + va_end(ap); + return ret; +} + +sstr_t ucx_vasprintf(UcxAllocator *a, const char *fmt, va_list ap) { + sstr_t s; + s.ptr = NULL; + s.length = 0; + va_list ap2; + va_copy(ap2, ap); + char buf[UCX_PRINTF_BUFSIZE]; + int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); + if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) { + s.ptr = (char*)a->malloc(a->pool, ret + 1); + s.length = (size_t)ret; + memcpy(s.ptr, buf, ret); + s.ptr[s.length] = '\0'; + } else if (ret == INT_MAX) { + errno = ENOMEM; + } else { + int len = ret + 1; + s.ptr = (char*)a->malloc(a->pool, len); + ret = vsnprintf(s.ptr, len, fmt, ap2); + va_end(ap2); + if (ret < 0) { + free(s.ptr); + s.ptr = NULL; + } else { + s.length = (size_t)ret; + } + } + return s; +}
--- a/ucx/utils.h Fri Aug 16 12:41:30 2013 +0200 +++ b/ucx/utils.h Sat Aug 17 12:04:04 2013 +0200 @@ -29,7 +29,7 @@ /** * @file utils.h * - * Common utilities like compare and copy functions. + * Compare, copy and printf functions. * * @author Mike Becker * @author Olaf Wintermann @@ -43,7 +43,11 @@ #endif #include "ucx.h" +#include "string.h" +#include "allocator.h" +#include <stdint.h> #include <string.h> +#include <stdarg.h> /** * Copies a string. @@ -62,6 +66,50 @@ */ void *ucx_memcpy(void *m, void *n); + +/** + * Reads data from a stream and writes it to another stream. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param buf a pointer to the copy buffer or <code>NULL</code> if a buffer + * shall be implicitly created on the heap + * @param bufsize the size of the copy buffer - if <code>NULL</code> was + * provided for <code>buf</code>, this is the size of the buffer that shall be + * implicitly created + * @param n the maximum number of bytes that shall be copied + * @return the total number of bytes copied + */ +size_t ucx_stream_copy(void *src, void *dest, read_func rfnc, write_func wfnc, + char* buf, size_t bufsize, size_t n); + +/** + * Shorthand for ucx_stream_copy using the default copy buffer. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @return total number of bytes copied + */ +#define ucx_stream_hcopy(src,dest,rfnc,wfnc) ucx_stream_copy(\ + src, dest, (read_func)rfnc, (write_func)wfnc, NULL, 0x100, SIZE_MAX) + +/** + * Shorthand for ucx_stream_copy using the default copy buffer and a copy limit. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param n maximum number of bytes that shall be copied + * @return total number of bytes copied + */ +#define ucx_stream_ncopy(src,dest,rfnc,wfnc, n) ucx_stream_copy(\ + src, dest, (read_func)rfnc, (write_func)wfnc, NULL, 0x100, n) + /** * Wraps the strcmp function. * @param s1 string one @@ -88,14 +136,13 @@ * @return -1, if *i1 is less than *i2, 0 if both are equal, * 1 if *i1 is greater than *i2 */ - int ucx_intcmp(void *i1, void *i2, void *data); /** * Compares two real numbers of type float. * @param f1 pointer to float one * @param f2 pointer to float two - * @param if provided: a pointer to precision (default: 1e-6f) + * @param data if provided: a pointer to precision (default: 1e-6f) * @return -1, if *f1 is less than *f2, 0 if both are equal, * 1 if *f1 is greater than *f2 */ @@ -104,13 +151,12 @@ /** * Compares two real numbers of type double. - * @param f1 pointer to double one - * @param f2 pointer to double two -* @param if provided: a pointer to precision (default: 1e-14) + * @param d1 pointer to double one + * @param d2 pointer to double two + * @param data if provided: a pointer to precision (default: 1e-14) * @return -1, if *d1 is less than *d2, 0 if both are equal, * 1 if *d1 is greater than *d2 */ - int ucx_doublecmp(void *d1, void *d2, void *data); /** @@ -132,6 +178,70 @@ */ int ucx_memcmp(void *ptr1, void *ptr2, void *n); +/** + * A <code>printf()</code> like function which writes the output to a stream by + * using a write_func(). + * @param stream the stream the data is written to + * @param wfc the write function + * @param fmt format string + * @param ... additional arguments + * @return the total number of bytes written + */ +int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...); + +/** + * <code>va_list</code> version of ucx_fprintf(). + * @param stream the stream the data is written to + * @param wfc the write function + * @param fmt format string + * @param ap argument list + * @return the total number of bytes written + * @see ucx_fprintf() + */ +int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap); + +/** + * A <code>printf()</code> like function which allocates space for a sstr_t + * the result is written to. + * + * <b>Attention</b>: The sstr_t data is allocated with the allocators + * ucx_allocator_malloc() function. So it is implementation dependent, if + * the returned sstr_t.ptr pointer must be passed to the allocators + * ucx_allocator_free() function manually. + * + * <b>Note</b>: The sstr_t.ptr of the return value will <i>always</i> be + * <code>NULL</code>-terminated. + * + * @param allocator the UcxAllocator used for allocating the result sstr_t + * @param fmt format string + * @param ... additional arguments + * @return a sstr_t containing the formatted string + */ +sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...); + +/** + * <code>va_list</code> version of ucx_asprintf(). + * + * @param allocator the UcxAllocator used for allocating the result sstr_t + * @param fmt format string + * @param ap argument list + * @return a sstr_t containing the formatted string + * @see ucx_asprintf() + */ +sstr_t ucx_vasprintf(UcxAllocator *allocator, const char *fmt, va_list ap); + +/** + * A <code>printf()</code> like function which writes the output to an + * UcxBuffer. + * + * @param buffer the buffer the data is written to + * @param ... format string and additional arguments + * @return the total number of bytes written + * @see ucx_fprintf() + */ +#define ucx_bprintf(buffer, ...) ucx_fprintf((UcxBuffer*)buffer, \ + (write_func)ucx_buffer_write, __VA_ARGS__) + #ifdef __cplusplus } #endif