diff --git a/configure.ac b/configure.ac index 1d5d457..08fbf4e 100644 --- a/configure.ac +++ b/configure.ac @@ -44,10 +44,10 @@ AC_TYPE_UINT8_T AC_FUNC_MKTIME AC_FUNC_MALLOC AC_FUNC_REALLOC -AC_CHECK_FUNCS([memset mkdir strdup strrchr strstr strtoul utime]) +AC_CHECK_FUNCS([memmove memset mkdir strdup strpbrk strrchr strstr strtoul utime]) # test for --with-zlib -AC_MSG_CHECKING([if compile with zlib]) +AC_MSG_CHECKING([whether compile with zlib]) AC_ARG_WITH(zlib, AC_HELP_STRING([--with-zlib],[Use zlib instead of included miniz @<:@default=yes@:>@]), [if test "$withval" = yes; then use_zlib=yes; else use_zlib=no; fi], @@ -70,7 +70,7 @@ AC_SUBST(LIBZ_LDFLAGS) AC_SUBST(MINIZ_CFLAGS) # test for --with-libxml2 -AC_MSG_CHECKING([if compile with libxml2]) +AC_MSG_CHECKING([whether compile with libxml2]) AC_ARG_WITH(libxml2, AC_HELP_STRING([--with-libxml2],[Compile with libxml2 @<:@default=yes@:>@]), [if test "$withval" = yes; then use_libxml2=yes; else use_libxml2=no; fi], @@ -160,7 +160,7 @@ AC_DEFINE_UNQUOTED([MOBI_INLINE], [$def_inline], [How to obtain function inlining.]) # Check --enable-debug -AC_MSG_CHECKING([if enable debugging]) +AC_MSG_CHECKING([whether enable debugging]) AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [enable debugging @<:@default=no@:>@]), @@ -188,7 +188,7 @@ fi AC_SUBST(DEBUG_CFLAGS) # Check --enable-debug-alloc -AC_MSG_CHECKING([if enable alloc debugging]) +AC_MSG_CHECKING([whether enable alloc debugging]) AC_ARG_ENABLE([debug_alloc], AS_HELP_STRING([--enable-debug-alloc], [enable memory allocation debugging @<:@default=no@:>@]), @@ -204,7 +204,7 @@ if test x$debug_alloc = xyes; then fi # Check --enable-mobitool-static -AC_MSG_CHECKING([if link mobitool against static libmobi]) +AC_MSG_CHECKING([whether link mobitool against static libmobi]) AC_ARG_ENABLE([mobitool_static], AS_HELP_STRING([--enable-mobitool-static], [link mobitool against static libmobi @<:@default=no@:>@]), diff --git a/libmobi.pc.in b/libmobi.pc.in index fb6409c..ccfb623 100644 --- a/libmobi.pc.in +++ b/libmobi.pc.in @@ -8,5 +8,6 @@ Description: MOBI ebook format handling library URL: http://www.fabiszewski.net/libmobi Version: @VERSION@ Requires: -Libs: -L${libdir} -lmobi @LIBZ_LDFLAGS@ +Libs: -L${libdir} -lmobi +Libs.private: @LIBZ_LDFLAGS@ @LIBXML2_LDFLAGS@ Cflags: -I${includedir} diff --git a/src/Makefile.am b/src/Makefile.am index f19c447..a00c525 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,8 +1,8 @@ # libmobi lib_LTLIBRARIES = libmobi.la -libmobi_la_SOURCES = buffer.c compression.c debug.c index.c memory.c parse_rawml.c read.c util.c write.c \ - buffer.h compression.h config.h debug.h index.h memory.h mobi.h parse_rawml.h read.h util.h write.h +libmobi_la_SOURCES = buffer.c compression.c debug.c index.c memory.c parse_rawml.c read.c structure.c util.c write.c \ + buffer.h compression.h config.h debug.h index.h memory.h mobi.h parse_rawml.h read.h structure.h util.h write.h if USE_LIBXML2 libmobi_la_SOURCES += opf.c opf.h endif diff --git a/src/buffer.c b/src/buffer.c index 7948866..15b1e2c 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -567,124 +567,3 @@ void buffer_free_null(MOBIBuffer *buf) { if (buf == NULL) { return; } free(buf); } - -/** - @brief Initializer for MOBIArray structure - - It allocates memory for structure and for data: array of uint32_t variables. - Memory should be freed with array_free(). - - @param[in] len Initial size of the array - @return MOBIArray on success, NULL otherwise - */ -MOBIArray * array_init(const size_t len) { - MOBIArray *arr = NULL; - arr = malloc(sizeof(MOBIArray)); - if (arr == NULL) { - debug_print("%s", "Array allocation failed\n"); - return NULL; - } - arr->data = malloc(len * sizeof(*arr->data)); - if (arr->data == NULL) { - free(arr); - debug_print("%s", "Array data allocation failed\n"); - return NULL; - } - arr->maxsize = arr->step = len; - arr->size = 0; - return arr; -} - -/** - @brief Inserts value into MOBIArray array - - @param[in,out] arr MOBIArray array - @param[in] value Value to be inserted - @return MOBI_RET status code (on success MOBI_SUCCESS) - */ -MOBI_RET array_insert(MOBIArray *arr, const uint32_t value) { - if (!arr || arr->maxsize == 0) { - return MOBI_INIT_FAILED; - } - if (arr->maxsize == arr->size) { - arr->maxsize += arr->step; - uint32_t *tmp = realloc(arr->data, arr->maxsize * sizeof(*arr->data)); - if (!tmp) { - free(arr->data); - arr->data = NULL; - debug_print("%s\n", "Memory allocation failed"); - return MOBI_MALLOC_FAILED; - } - arr->data = tmp; - } - arr->data[arr->size] = value; - arr->size++; - return MOBI_SUCCESS; -} - -/** - @brief Helper for qsort in array_sort() function. - - @param[in] a First element to compare - @param[in] b Second element to compare - @return -1 if a < b; 1 if a > b; 0 if a = b - */ -static int array_compare(const void *a, const void *b) { - if (*(uint32_t *) a < *(uint32_t *) b) { - return -1; - }; - if (*(uint32_t *) a > *(uint32_t *) b) { - return 1; - }; - return 0; -} - -/** - @brief Sort MOBIArray in ascending order. - - When unique is set to true, duplicate values are discarded. - - @param[in,out] arr MOBIArray array - @param[in] unique Discard duplicate values if true - */ -void array_sort(MOBIArray *arr, const bool unique) { - if (!arr || !arr->data || arr->size == 0) { - return; - } - qsort(arr->data, arr->size, sizeof(*arr->data), array_compare); - if (unique) { - size_t i = 1, j = 1; - while (i < arr->size) { - if (arr->data[j - 1] == arr->data[i]) { - i++; - continue; - } - arr->data[j++] = arr->data[i++]; - } - arr->size = j; - } -} - -/** - @brief Get size of the array - - @param[in] arr MOBIArray structure - */ -size_t array_size(MOBIArray *arr) { - return arr->size; -} - -/** - @brief Free MOBIArray structure and contained data - - Free data initialized with array_init(); - - @param[in] arr MOBIArray structure - */ -void array_free(MOBIArray *arr) { - if (!arr) { return; } - if (arr->data) { - free(arr->data); - } - free(arr); -} diff --git a/src/buffer.h b/src/buffer.h index dd6409e..cfe089c 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -52,20 +52,4 @@ void buffer_setpos(MOBIBuffer *buf, size_t pos); void buffer_free(MOBIBuffer *buf); void buffer_free_null(MOBIBuffer *buf); -/** - @brief Dynamic array of uint32_t values structure - */ -typedef struct { - uint32_t *data; /**< Array */ - size_t maxsize; /**< Allocated size */ - size_t step; /**< Step by which array will be enlarged if out of memory */ - size_t size; /**< Current size */ -} MOBIArray; - -MOBIArray * array_init(const size_t len); -MOBI_RET array_insert(MOBIArray *arr, uint32_t value); -void array_sort(MOBIArray *arr, bool unique); -size_t array_size(MOBIArray *arr); -void array_free(MOBIArray *arr); - #endif diff --git a/src/index.h b/src/index.h index 045fa61..2601e1d 100644 --- a/src/index.h +++ b/src/index.h @@ -12,7 +12,7 @@ #define mobi_index_h #include "config.h" -#include "buffer.h" +#include "structure.h" #include "mobi.h" /** diff --git a/src/mobi.h b/src/mobi.h index 2ab636d..55c7bde 100644 --- a/src/mobi.h +++ b/src/mobi.h @@ -400,11 +400,6 @@ extern "C" uint32_t *fdst_section_ends; /**< Array of section end offsets */ } MOBIFdst; - /** - @brief Maximum value of tag values in index entry (MOBIIndexTag) - FIXME: is 2 enough? - */ -#define MOBI_INDX_MAXTAGVALUES 100 /** @brief Parsed tag for an index entry diff --git a/src/structure.c b/src/structure.c new file mode 100644 index 0000000..505757c --- /dev/null +++ b/src/structure.c @@ -0,0 +1,360 @@ +/** @file structure.c + * @brief Data structures + * + * Copyright (c) 2014 Bartek Fabiszewski + * http://www.fabiszewski.net + * + * This file is part of libmobi. + * Licensed under LGPL, either version 3, or any later. + * See + */ + +#include +#include +#include "structure.h" +#include "debug.h" + +/** + @brief Initializer for MOBIArray structure + + It allocates memory for structure and for data: array of uint32_t variables. + Memory should be freed with array_free(). + + @param[in] len Initial size of the array + @return MOBIArray on success, NULL otherwise + */ +MOBIArray * array_init(const size_t len) { + MOBIArray *arr = NULL; + arr = malloc(sizeof(MOBIArray)); + if (arr == NULL) { + debug_print("%s", "Array allocation failed\n"); + return NULL; + } + arr->data = malloc(len * sizeof(*arr->data)); + if (arr->data == NULL) { + free(arr); + debug_print("%s", "Array data allocation failed\n"); + return NULL; + } + arr->maxsize = arr->step = len; + arr->size = 0; + return arr; +} + +/** + @brief Inserts value into MOBIArray array + + @param[in,out] arr MOBIArray array + @param[in] value Value to be inserted + @return MOBI_RET status code (on success MOBI_SUCCESS) + */ +MOBI_RET array_insert(MOBIArray *arr, const uint32_t value) { + if (!arr || arr->maxsize == 0) { + return MOBI_INIT_FAILED; + } + if (arr->maxsize == arr->size) { + arr->maxsize += arr->step; + uint32_t *tmp = realloc(arr->data, arr->maxsize * sizeof(*arr->data)); + if (!tmp) { + free(arr->data); + arr->data = NULL; + debug_print("%s\n", "Memory allocation failed"); + return MOBI_MALLOC_FAILED; + } + arr->data = tmp; + } + arr->data[arr->size] = value; + arr->size++; + return MOBI_SUCCESS; +} + +/** + @brief Helper for qsort in array_sort() function. + + @param[in] a First element to compare + @param[in] b Second element to compare + @return -1 if a < b; 1 if a > b; 0 if a = b + */ +static int array_compare(const void *a, const void *b) { + if (*(uint32_t *) a < *(uint32_t *) b) { + return -1; + }; + if (*(uint32_t *) a > *(uint32_t *) b) { + return 1; + }; + return 0; +} + +/** + @brief Sort MOBIArray in ascending order. + + When unique is set to true, duplicate values are discarded. + + @param[in,out] arr MOBIArray array + @param[in] unique Discard duplicate values if true + */ +void array_sort(MOBIArray *arr, const bool unique) { + if (!arr || !arr->data || arr->size == 0) { + return; + } + qsort(arr->data, arr->size, sizeof(*arr->data), array_compare); + if (unique) { + size_t i = 1, j = 1; + while (i < arr->size) { + if (arr->data[j - 1] == arr->data[i]) { + i++; + continue; + } + arr->data[j++] = arr->data[i++]; + } + arr->size = j; + } +} + +/** + @brief Get size of the array + + @param[in] arr MOBIArray structure + */ +size_t array_size(MOBIArray *arr) { + return arr->size; +} + +/** + @brief Free MOBIArray structure and contained data + + Free data initialized with array_init(); + + @param[in] arr MOBIArray structure + */ +void array_free(MOBIArray *arr) { + if (!arr) { return; } + if (arr->data) { + free(arr->data); + } + free(arr); +} + + +/** + @brief Create and return MOBITrie structure + + @return MOBITrie stucture initialized with zeroes + */ +static MOBITrie * mobi_trie_mknode(void) { + MOBITrie *node = calloc(1, sizeof(MOBITrie)); + if (node == NULL) { + debug_print("Memory allocation failed%s", "\n"); + } + return node; +} + +/** + @brief Recursively free MOBITrie trie starting from node + + @param[in] node Starting node + */ +void mobi_trie_free(MOBITrie *node) { + if (node) { + mobi_trie_free(node->next); + mobi_trie_free(node->children); + free(node->values); + free(node); + } +} + +/** + @brief Insert value into array at given MOBITrie node + + @param[in,out] node Starting node + @param[in] value Value to be inserted + @return MOBI_RET status code (on success MOBI_SUCCESS) + */ +static MOBI_RET mobi_trie_addvalue(MOBITrie *node, char *value) { + if (node->values) { + size_t cnt = ++node->values_count; + void *new_values = realloc(node->values, cnt * sizeof(*node->values)); + if (new_values == NULL) { + debug_print("Memory allocation failed%s", "\n"); + return MOBI_MALLOC_FAILED; + } + node->values = new_values; + node->values[cnt - 1] = value; + } else { + node->values = malloc(sizeof(*node->values)); + if (node->values == NULL) { + debug_print("Memory allocation failed%s", "\n"); + return MOBI_MALLOC_FAILED; + } + node->values[0] = value; + node->values_count = 1; + } + return MOBI_SUCCESS; +} + +/** + @brief Insert key character and value (if given) at MOBITrie node + + @param[in,out] node Starting node + @param[in] c Key character + @param[in] value Value to be inserted at terminal node, or NULL if not terminal + @return MOBITrie node: current node if value inserted (terminal), + children node (if transitional) or NULL on error + */ +static MOBITrie * mobi_trie_insert_char(MOBITrie *node, char c, char *value) { + if (!node) { return NULL; } + while (true) { + if (node->c == c) { + break; + } + if (node->next == NULL) { + node->next = mobi_trie_mknode(); + node = node->next; + break; + } + node = node->next; + } + if (node->c == 0) { + node->c = c; + } + if (value) { + /* terminal node */ + if (mobi_trie_addvalue(node, value) == MOBI_SUCCESS) { + return node; + } else { + return NULL; + } + } + if (node->children == NULL) { + node->children = mobi_trie_mknode(); + } + return node->children; +} + +/** + @brief Insert reversed string into MOBITrie trie + + @param[in,out] root Root node + @param[in] string String to be inserted + @param[in] value Value associated with the string + @return MOBI_RET status code (on success MOBI_SUCCESS) + */ +MOBI_RET mobi_trie_insert_reversed(MOBITrie **root, char *string, char *value) { + if (*root == NULL) { + *root = mobi_trie_mknode(); + if (*root == NULL) { + return MOBI_MALLOC_FAILED; + } + } + size_t length = strlen(string); + MOBITrie *node = *root; + while (length > 1) { + node = mobi_trie_insert_char(node, string[length - 1], NULL); + if (node == NULL) { + return MOBI_MALLOC_FAILED; + } + length--; + } + node = mobi_trie_insert_char(node, string[length - 1], value); + if (node == NULL) { + return MOBI_MALLOC_FAILED; + } + return MOBI_SUCCESS; +} + +/** + @brief Fetch values for key c from MOBITrie trie's current level starting at node + + @param[in,out] values Array of values to be fetched + @param[in,out] values_count Array size + @param[in] node MOBITrie node to start search + @param[in] c Key character + @return MOBITrie children node of the node with c key or NULL if not found + */ +MOBITrie * mobi_trie_get_next(char ***values, size_t *values_count, const MOBITrie *node, const char c) { + if (!node) { return NULL; } + while (node) { + if (node->c == c) { + *values = (char**) node->values; + *values_count = node->values_count; + return node->children; + } + node = node->next; + } + return NULL; +} + +#if 0 +/* Simple imprementation of binary tree, storing key strings + and associated arrays of values + currently not used, save for later */ + +typedef struct MOBIBtree { + char *key; /**< key */ + char **array; /**< array of strings */ + size_t value_count; /**< strings count */ + struct MOBIBtree *left; /**< left child */ + struct MOBIBtree *right; /**< right child */ +} MOBIBtree; + +/** + @brief Search MOBIBtree tree for string key + + @param[in] node MOBIBtree node to start search + @param[in] key Key string + @return MOBIBtree node or NULL if not found + */ +MOBIBtree *mobi_btree_search(MOBIBtree *node, const char *key) { + MOBIBtree *found = NULL; + int compare = strcmp(key, node->key); + if (compare < 0) { + found = mobi_btree_search(node->left, key); + } else if (compare > 0) { + found = mobi_btree_search(node->right, key); + } else { + found = node; + } + return found; +} + +/** + @brief Insert key and value (into array) into MOBIBtree tree + + @param[in] node MOBIBtree root node + @param[in] key Key string + @param[in] value Value string will be inserted into array + @return MOBI_RET status code (on success MOBI_SUCCESS) + */ +MOBI_RET mobi_btree_insert(MOBIBtree **node, char *key, char *value) { + MOBI_RET ret = MOBI_SUCCESS; + if (*node == NULL) { + *node = malloc(sizeof(MOBIBtree)); + if (*node == NULL) { + return MOBI_MALLOC_FAILED; + } + (*node)->key = key; + (*node)->value_count = 1; + (*node)->array = malloc(sizeof(*(*node)->array)); + (*node)->array[0] = value; + (*node)->left = NULL; + (*node)->right = NULL; + return MOBI_SUCCESS; + } + int compare = strcmp(key, (*node)->key); + if (compare < 0) { + ret = mobi_btree_insert(&(*node)->left, key, value); + } else if (compare > 0) { + ret = mobi_btree_insert(&(*node)->right, key, value); + } else { + size_t cnt = ++(*node)->value_count; + char **new_array = realloc((*node)->array, cnt * sizeof(*(*node)->array)); + if (new_array) { + (*node)->array = new_array; + (*node)->array[cnt - 1] = value; + } else { + return MOBI_MALLOC_FAILED; + } + } + return ret; +} +#endif diff --git a/src/structure.h b/src/structure.h new file mode 100644 index 0000000..b2793ba --- /dev/null +++ b/src/structure.h @@ -0,0 +1,48 @@ +/** @file structure.h + * + * Copyright (c) 2014 Bartek Fabiszewski + * http://www.fabiszewski.net + * + * This file is part of libmobi. + * Licensed under LGPL, either version 3, or any later. + * See + */ + +#ifndef mobi_structure_h +#define mobi_structure_h + +#include "config.h" +#include "mobi.h" + +/** + @brief Dynamic array of uint32_t values structure + */ +typedef struct { + uint32_t *data; /**< Array */ + size_t maxsize; /**< Allocated size */ + size_t step; /**< Step by which array will be enlarged if out of memory */ + size_t size; /**< Current size */ +} MOBIArray; + +MOBIArray * array_init(const size_t len); +MOBI_RET array_insert(MOBIArray *arr, uint32_t value); +void array_sort(MOBIArray *arr, bool unique); +size_t array_size(MOBIArray *arr); +void array_free(MOBIArray *arr); + +/** + @brief Trie storing arrays of values for character keys + */ +typedef struct MOBITrie { + char c; /**< Key character */ + void **values; /**< Array of values */ + size_t values_count; /**< Array size */ + struct MOBITrie *next; /**< Next node at the same level */ + struct MOBITrie *children; /**< Link to children nodes, lower level */ +} MOBITrie; + +MOBI_RET mobi_trie_insert_reversed(MOBITrie **root, char *string, char *value); +MOBITrie * mobi_trie_get_next(char ***values, size_t *values_count, const MOBITrie *node, const char c); +void mobi_trie_free(MOBITrie *node); + +#endif