From 513f8791d85bbaf75cea6a942f689a9fc128805e Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Mon, 2 Mar 2015 21:35:31 +0000 Subject: [PATCH 1/2] Update libucl to git version 8d3b186 --- ChangeLog.md | 12 + Makefile.am | 2 +- README.md | 16 +- cmake/CMakeLists.txt | 1 + configure.ac | 6 +- doc/Makefile.am | 3 +- doc/api.md | 58 +++- doc/libucl.3 | 76 ++++- doc/pandoc.template | 2 +- include/ucl.h | 40 ++- klib/khash.h | 627 ++++++++++++++++++++++++++++++++++++++++ klib/kvec.h | 103 +++++++ m4/.gitignore | 4 - m4/ax_lua.m4 | 606 -------------------------------------- src/Makefile.am | 1 + src/ucl_emitter.c | 17 +- src/ucl_emitter_utils.c | 1 + src/ucl_hash.c | 312 ++++++++++++++++---- src/ucl_hash.h | 18 +- src/ucl_internal.h | 11 +- src/ucl_parser.c | 22 +- src/ucl_schema.c | 11 +- src/ucl_util.c | 393 +++++++++++++++++-------- tests/basic/14.in | 8 + tests/basic/14.res | 4 + tests/schema.test | 2 +- tests/test_generate.c | 32 +- tests/test_schema.c | 2 + uthash/utstring.h | 8 +- 29 files changed, 1560 insertions(+), 838 deletions(-) create mode 100644 klib/khash.h create mode 100644 klib/kvec.h delete mode 100644 m4/.gitignore delete mode 100644 m4/ax_lua.m4 create mode 100644 tests/basic/14.in create mode 100644 tests/basic/14.res diff --git a/ChangeLog.md b/ChangeLog.md index 0df5e072222e..093ff97a842d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -20,3 +20,15 @@ ### Libucl 0.6.1 - Various utilities fixes + +### Libucl 0.7.0 + +- Move to klib library from uthash to reduce memory overhead and increase performance + +### Libucl 0.7.1 + +- Added safe iterators API + +### Libucl 0.7.2 + +- Fixed serious bugs in schema and arrays iteration diff --git a/Makefile.am b/Makefile.am index ece5b97df488..957e3a2b0f3c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ ACLOCAL_AMFLAGS = -I m4 -EXTRA_DIST = uthash README.md +EXTRA_DIST = uthash klib README.md pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libucl.pc diff --git a/README.md b/README.md index 235cfb6595df..797278ba932a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # LIBUCL -[![Build Status](https://travis-ci.org/vstakhov/libucl.svg?branch=master)](https://travis-ci.org/vstakhov/libucl) +[![Build Status](https://travis-ci.org/vstakhov/libucl.svg?branch=master)](https://travis-ci.org/vstakhov/libucl)[![Coverity](https://scan.coverity.com/projects/4138/badge.svg)](https://scan.coverity.com/projects/4138) **Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)* @@ -156,10 +156,10 @@ is converted to the following object: ```nginx section { blah { - key = value; + key = value; } foo { - key = value; + key = value; } } ``` @@ -177,9 +177,9 @@ is presented as: ```nginx section { blah { - foo { - key = value; - } + foo { + key = value; + } } } ``` @@ -219,8 +219,8 @@ UCL supports external macros both multiline and single line ones: ```nginx .macro "sometext"; .macro { - Some long text - .... + Some long text + .... }; ``` diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 3c57db53784a..e84f61905c58 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -82,6 +82,7 @@ ENDIF(ENABLE_URL_SIGN MATCHES "ON") INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../src") INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../include") INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../uthash") +INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../klib") SET(UCLSRC ../src/ucl_util.c ../src/ucl_parser.c diff --git a/configure.ac b/configure.ac index 32db73ad0fdd..be6f6522f121 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ m4_define([maj_ver], [0]) -m4_define([med_ver], [6]) -m4_define([min_ver], [1]) -m4_define([so_version], [3:0:1]) +m4_define([med_ver], [7]) +m4_define([min_ver], [2]) +m4_define([so_version], [5:0:1]) m4_define([ucl_version], [maj_ver.med_ver.min_ver]) AC_INIT([libucl],[ucl_version],[https://github.com/vstakhov/libucl],[libucl]) diff --git a/doc/Makefile.am b/doc/Makefile.am index 7e57a829f807..a90a6fa44cc6 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -4,5 +4,6 @@ dist_man_MANS = libucl.3 gen-man: @PANDOC@ tail -n +$$(grep -n '# Synopsis' api.md | cut -d':' -f1) api.md | \ - cat pandoc.template - | sed -e 's/^# \(.*\)/# \U\1/' | \ + cat pandoc.template - | sed -e 's/^# \(.*\)/# \U\1/' \ + -e "s/%%date%%/$$(LANG=C date +'%d %B, %Y')/" | \ @PANDOC@ -s -f markdown -t man -o libucl.3 \ No newline at end of file diff --git a/doc/api.md b/doc/api.md index 174c7ff47060..75b954bb302c 100644 --- a/doc/api.md +++ b/doc/api.md @@ -377,7 +377,9 @@ If parsing operations fail then the resulting UCL object will be a `UCL_STRING`. # Iteration functions -Iteration are used to iterate over UCL compound types: arrays and objects. Moreover, iterations could be performed over the keys with multiple values (implicit arrays). To iterate over an object, an array or a key with multiple values there is a function `ucl_iterate_object`. +Iteration are used to iterate over UCL compound types: arrays and objects. Moreover, iterations could be performed over the keys with multiple values (implicit arrays). +There are two types of iterators API: old and unsafe one via `ucl_iterate_object` and the proposed interface of safe iterators. + ## ucl_iterate_object ~~~C @@ -402,6 +404,60 @@ while ((obj = ucl_iterate_object (top, &it, true))) { } ~~~ +## Safe iterators API + +Safe iterators are defined to clarify iterating over UCL objects and simplify flattening of UCL objects in non-trivial cases. +For example, if there is an implicit array that contains another array and a boolean value it is extremely unclear how to iterate over +such an object. Safe iterators are desinged to define two sorts of iteration: + +1. Iteration over complex objects with expanding all values +2. Iteration over complex objects without expanding of values + +The following example demonstrates the difference between these two types of iteration: + +~~~ +key = 1; +key = [2, 3, 4]; + +Iteration with expansion: + +1, 2, 3, 4 + +Iteration without expansion: + +1, [2, 3, 4] +~~~ + +UCL defines the following functions to manage safe iterators: + +- `ucl_object_iterate_new` - creates new safe iterator +- `ucl_object_iterate_reset` - resets iterator to a new object +- `ucl_object_iterate_safe` - safely iterate the object inside iterator +- `ucl_object_iterate_free` - free memory associated with the safe iterator + +Please note that unlike unsafe iterators, safe iterators *must* be explicitly initialized and freed. +An assert is likely generated if you use uninitialized or `NULL` iterator in all safe iterators functions. + +~~~C +ucl_object_iter_t it; +const ucl_object_t *cur; + +it = ucl_object_iterate_new (obj); + +while ((cur = ucl_object_iterate_safe (it, true)) != NULL) { + /* Do something */ +} + +/* Switch to another object */ +it = ucl_object_iterate_reset (it, another_obj); + +while ((cur = ucl_object_iterate_safe (it, true)) != NULL) { + /* Do something else */ +} + +ucl_object_iterate_free (it); +~~~ + # Validation functions Currently, there is only one validation function called `ucl_object_validate`. It performs validation of object using the specified schema. This function is defined as following: diff --git a/doc/libucl.3 b/doc/libucl.3 index e319a0d3f932..ec5046325700 100644 --- a/doc/libucl.3 +++ b/doc/libucl.3 @@ -1,4 +1,4 @@ -.TH "LIBUCL" "3" "July 26, 2014" "Libucl manual" "" +.TH "LIBUCL" "3" "27 December, 2014" "Libucl manual" "" .SH NAME .PP \f[B]ucl_parser_new\f[], \f[B]ucl_parser_register_macro\f[], @@ -528,8 +528,9 @@ Iteration are used to iterate over UCL compound types: arrays and objects. Moreover, iterations could be performed over the keys with multiple values (implicit arrays). -To iterate over an object, an array or a key with multiple values there -is a function \f[C]ucl_iterate_object\f[]. +There are two types of iterators API: old and unsafe one via +\f[C]ucl_iterate_object\f[] and the proposed interface of safe +iterators. .SS ucl_iterate_object .IP .nf @@ -578,6 +579,75 @@ while\ ((obj\ =\ ucl_iterate_object\ (top,\ &it,\ true)))\ { } \f[] .fi +.SS Safe iterators API +.PP +Safe iterators are defined to clarify iterating over UCL objects and +simplify flattening of UCL objects in non\-trivial cases. +For example, if there is an implicit array that contains another array +and a boolean value it is extremely unclear how to iterate over such an +object. +Safe iterators are desinged to define two sorts of iteration: +.IP "1." 3 +Iteration over complex objects with expanding all values +.IP "2." 3 +Iteration over complex objects without expanding of values +.PP +The following example demonstrates the difference between these two +types of iteration: +.IP +.nf +\f[C] +key\ =\ 1; +key\ =\ [2,\ 3,\ 4]; + +Iteration\ with\ expansion: + +1,\ 2,\ 3,\ 4 + +Iteration\ without\ expansion: + +1,\ [2,\ 3,\ 4] +\f[] +.fi +.PP +UCL defines the following functions to manage safe iterators: +.IP \[bu] 2 +\f[C]ucl_object_iterate_new\f[] \- creates new safe iterator +.IP \[bu] 2 +\f[C]ucl_object_iterate_reset\f[] \- resets iterator to a new object +.IP \[bu] 2 +\f[C]ucl_object_iterate_safe\f[] \- safely iterate the object inside +iterator +.IP \[bu] 2 +\f[C]ucl_object_iterate_free\f[] \- free memory associated with the safe +iterator +.PP +Please note that unlike unsafe iterators, safe iterators \f[I]must\f[] +be explicitly initialized and freed. +An assert is likely generated if you use uninitialized or \f[C]NULL\f[] +iterator in all safe iterators functions. +.IP +.nf +\f[C] +ucl_object_iter_t\ it; +const\ ucl_object_t\ *cur; + +it\ =\ ucl_object_iterate_new\ (obj); + +while\ ((cur\ =\ ucl_object_iterate_safe\ (it,\ true))\ !=\ NULL)\ { +\ \ \ \ /*\ Do\ something\ */ +} + +/*\ Switch\ to\ another\ object\ */ +it\ =\ ucl_object_iterate_reset\ (it,\ another_obj); + +while\ ((cur\ =\ ucl_object_iterate_safe\ (it,\ true))\ !=\ NULL)\ { +\ \ \ \ /*\ Do\ something\ else\ */ +} + +ucl_object_iterate_free\ (it); +\f[] +.fi .SH VALIDATION FUNCTIONS .PP Currently, there is only one validation function called diff --git a/doc/pandoc.template b/doc/pandoc.template index 6d5f0277ae40..2effe1a157ef 100644 --- a/doc/pandoc.template +++ b/doc/pandoc.template @@ -1,6 +1,6 @@ % LIBUCL(3) Libucl manual % Vsevolod Stakhov -% July 26, 2014 +% %%date%% # Name diff --git a/include/ucl.h b/include/ucl.h index 1aac6a25332c..823ac8d3bfc9 100644 --- a/include/ucl.h +++ b/include/ucl.h @@ -192,7 +192,7 @@ typedef struct ucl_object_s { int64_t iv; /**< Int value of an object */ const char *sv; /**< String value of an object */ double dv; /**< Double value of an object */ - struct ucl_object_s *av; /**< Array */ + void *av; /**< Array */ void *ov; /**< Object */ void* ud; /**< Opaque user data */ } value; @@ -715,6 +715,37 @@ typedef void* ucl_object_iter_t; */ UCL_EXTERN const ucl_object_t* ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values); + +/** + * Create new safe iterator for the specified object + * @param obj object to iterate + * @return new iterator object that should be used with safe iterators API only + */ +UCL_EXTERN ucl_object_iter_t ucl_object_iterate_new (const ucl_object_t *obj) + UCL_WARN_UNUSED_RESULT; +/** + * Reset initialized iterator to a new object + * @param obj new object to iterate + * @return modified iterator object + */ +UCL_EXTERN ucl_object_iter_t ucl_object_iterate_reset (ucl_object_iter_t it, + const ucl_object_t *obj); + +/** + * Get the next object from the `obj`. This fucntion iterates over arrays, objects + * and implicit arrays + * @param iter safe iterator + * @return the next object in sequence + */ +UCL_EXTERN const ucl_object_t* ucl_object_iterate_safe (ucl_object_iter_t iter, + bool expand_values); + +/** + * Free memory associated with the safe iterator + * @param it safe iterator object + */ +UCL_EXTERN void ucl_object_iterate_free (ucl_object_iter_t it); + /** @} */ @@ -854,6 +885,13 @@ UCL_EXTERN ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser); * @param parser parser object */ UCL_EXTERN const char *ucl_parser_get_error(struct ucl_parser *parser); + +/** + * Clear the error in the parser + * @param parser parser object + */ +UCL_EXTERN void ucl_parser_clear_error(struct ucl_parser *parser); + /** * Free ucl parser object * @param parser parser object diff --git a/klib/khash.h b/klib/khash.h new file mode 100644 index 000000000000..afc3ce3ef0dc --- /dev/null +++ b/klib/khash.h @@ -0,0 +1,627 @@ +/* The MIT License + + Copyright (c) 2008, 2009, 2011 by Attractive Chaos + + 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. +*/ + +/* + An example: + +#include "khash.h" +KHASH_MAP_INIT_INT(32, char) +int main() { + int ret, is_missing; + khiter_t k; + khash_t(32) *h = kh_init(32); + k = kh_put(32, h, 5, &ret); + kh_value(h, k) = 10; + k = kh_get(32, h, 10); + is_missing = (k == kh_end(h)); + k = kh_get(32, h, 5); + kh_del(32, h, k); + for (k = kh_begin(h); k != kh_end(h); ++k) + if (kh_exist(h, k)) kh_value(h, k) = 1; + kh_destroy(32, h); + return 0; +} +*/ + +/* + 2013-05-02 (0.2.8): + + * Use quadratic probing. When the capacity is power of 2, stepping function + i*(i+1)/2 guarantees to traverse each bucket. It is better than double + hashing on cache performance and is more robust than linear probing. + + In theory, double hashing should be more robust than quadratic probing. + However, my implementation is probably not for large hash tables, because + the second hash function is closely tied to the first hash function, + which reduce the effectiveness of double hashing. + + Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php + + 2011-12-29 (0.2.7): + + * Minor code clean up; no actual effect. + + 2011-09-16 (0.2.6): + + * The capacity is a power of 2. This seems to dramatically improve the + speed for simple keys. Thank Zilong Tan for the suggestion. Reference: + + - http://code.google.com/p/ulib/ + - http://nothings.org/computer/judy/ + + * Allow to optionally use linear probing which usually has better + performance for random input. Double hashing is still the default as it + is more robust to certain non-random input. + + * Added Wang's integer hash function (not used by default). This hash + function is more robust to certain non-random input. + + 2011-02-14 (0.2.5): + + * Allow to declare global functions. + + 2009-09-26 (0.2.4): + + * Improve portability + + 2008-09-19 (0.2.3): + + * Corrected the example + * Improved interfaces + + 2008-09-11 (0.2.2): + + * Improved speed a little in kh_put() + + 2008-09-10 (0.2.1): + + * Added kh_clear() + * Fixed a compiling error + + 2008-09-02 (0.2.0): + + * Changed to token concatenation which increases flexibility. + + 2008-08-31 (0.1.2): + + * Fixed a bug in kh_get(), which has not been tested previously. + + 2008-08-31 (0.1.1): + + * Added destructor +*/ + + +#ifndef __AC_KHASH_H +#define __AC_KHASH_H + +/*! + @header + + Generic hash table library. + */ + +#define AC_VERSION_KHASH_H "0.2.8" + +#include +#include +#include + +/* compiler specific configuration */ + +#if UINT_MAX == 0xffffffffu +typedef unsigned int khint32_t; +#elif ULONG_MAX == 0xffffffffu +typedef unsigned long khint32_t; +#endif + +#if ULONG_MAX == ULLONG_MAX +typedef unsigned long khint64_t; +#else +typedef unsigned long long khint64_t; +#endif + +#ifndef kh_inline +#ifdef _MSC_VER +#define kh_inline __inline +#else +#define kh_inline inline +#endif +#endif /* kh_inline */ + +#ifndef kh_unused +# ifdef __GNUC__ +# define kh_unused(x) __attribute__((__unused__)) x +# else +# define kh_unused(x) x +# endif +#endif + +typedef khint32_t khint_t; +typedef khint_t khiter_t; + +#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) +#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) +#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) +#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) +#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) +#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) +#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) + +#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) + +#ifndef kroundup32 +#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#endif + +#ifndef kcalloc +#define kcalloc(N,Z) calloc(N,Z) +#endif +#ifndef kmalloc +#define kmalloc(Z) malloc(Z) +#endif +#ifndef krealloc +#define krealloc(P,Z) realloc(P,Z) +#endif +#ifndef kfree +#define kfree(P) free(P) +#endif + +static const double __ac_HASH_UPPER = 0.77; + +#define __KHASH_TYPE(name, khkey_t, khval_t) \ + typedef struct kh_##name##_s { \ + khint_t n_buckets, size, n_occupied, upper_bound; \ + khint32_t *flags; \ + khkey_t *keys; \ + khval_t *vals; \ + } kh_##name##_t; + +#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ + extern kh_##name##_t * kh_init_##name(void); \ + extern void kh_destroy_##name(kh_##name##_t *h); \ + extern void kh_clear_##name(kh_##name##_t *h); \ + extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ + extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ + extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ + extern void kh_del_##name(kh_##name##_t *h, khint_t x); + +#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + SCOPE kh_##name##_t *kh_init_##name(void) { \ + return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \ + } \ + SCOPE void kh_destroy_##name(kh_##name##_t *h) \ + { \ + if (h) { \ + kfree((void *)h->keys); kfree(h->flags); \ + kfree((void *)h->vals); \ + kfree(h); \ + } \ + } \ + SCOPE void kh_unused(kh_clear_##name)(kh_##name##_t *h) \ + { \ + if (h && h->flags) { \ + memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ + h->size = h->n_occupied = 0; \ + } \ + } \ + SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ + { \ + if (h->n_buckets) { \ + khint_t k, i, last, mask, step = 0; \ + mask = h->n_buckets - 1; \ + k = __hash_func(key); i = k & mask; \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + i = (i + (++step)) & mask; \ + if (i == last) return h->n_buckets; \ + } \ + return __ac_iseither(h->flags, i)? h->n_buckets : i; \ + } else return 0; \ + } \ + SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ + { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ + khint32_t *new_flags = 0; \ + khint_t j = 1; \ + { \ + kroundup32(new_n_buckets); \ + if (new_n_buckets < 4) new_n_buckets = 4; \ + if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \ + else { /* hash table size to be changed (shrink or expand); rehash */ \ + new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (!new_flags) return -1; \ + memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (h->n_buckets < new_n_buckets) { /* expand */ \ + khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (!new_keys) { kfree(new_flags); return -1; } \ + h->keys = new_keys; \ + if (kh_is_map) { \ + khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + if (!new_vals) { kfree(new_flags); return -1; } \ + h->vals = new_vals; \ + } \ + } /* otherwise shrink */ \ + } \ + } \ + if (j) { /* rehashing is needed */ \ + for (j = 0; j != h->n_buckets; ++j) { \ + if (__ac_iseither(h->flags, j) == 0) { \ + khkey_t key = h->keys[j]; \ + khval_t val; \ + khint_t new_mask; \ + new_mask = new_n_buckets - 1; \ + if (kh_is_map) val = h->vals[j]; \ + __ac_set_isdel_true(h->flags, j); \ + while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ + khint_t k, i, step = 0; \ + k = __hash_func(key); \ + i = k & new_mask; \ + while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ + __ac_set_isempty_false(new_flags, i); \ + if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ + { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ + if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ + __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ + } else { /* write the element and jump out of the loop */ \ + h->keys[i] = key; \ + if (kh_is_map) h->vals[i] = val; \ + break; \ + } \ + } \ + } \ + } \ + if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ + h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + kfree(h->flags); /* free the working space */ \ + h->flags = new_flags; \ + h->n_buckets = new_n_buckets; \ + h->n_occupied = h->size; \ + h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ + } \ + return 0; \ + } \ + SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ + { \ + khint_t x; \ + if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ + if (h->n_buckets > (h->size<<1)) { \ + if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ + *ret = -1; return h->n_buckets; \ + } \ + } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ + *ret = -1; return h->n_buckets; \ + } \ + } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ + { \ + khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ + x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ + if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \ + else { \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (__ac_isdel(h->flags, i)) site = i; \ + i = (i + (++step)) & mask; \ + if (i == last) { x = site; break; } \ + } \ + if (x == h->n_buckets) { \ + if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ + else x = i; \ + } \ + } \ + } \ + if (__ac_isempty(h->flags, x)) { /* not present at all */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; ++h->n_occupied; \ + *ret = 1; \ + } else if (__ac_isdel(h->flags, x)) { /* deleted */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + *ret = 2; \ + } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ + return x; \ + } \ + SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ + { \ + if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ + __ac_set_isdel_true(h->flags, x); \ + --h->size; \ + } \ + } + +#define KHASH_DECLARE(name, khkey_t, khval_t) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_PROTOTYPES(name, khkey_t, khval_t) + +#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +/* --- BEGIN OF HASH FUNCTIONS --- */ + +/*! @function + @abstract Integer hash function + @param key The integer [khint32_t] + @return The hash value [khint_t] + */ +#define kh_int_hash_func(key) (khint32_t)(key) +/*! @function + @abstract Integer comparison function + */ +#define kh_int_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract 64-bit integer hash function + @param key The integer [khint64_t] + @return The hash value [khint_t] + */ +#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) +/*! @function + @abstract 64-bit integer comparison function + */ +#define kh_int64_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract const char* hash function + @param s Pointer to a null terminated string + @return The hash value + */ +static kh_inline khint_t __ac_X31_hash_string(const char *s) +{ + khint_t h = (khint_t)*s; + if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; + return h; +} +/*! @function + @abstract Another interface to const char* hash function + @param key Pointer to a null terminated string [const char*] + @return The hash value [khint_t] + */ +#define kh_str_hash_func(key) __ac_X31_hash_string(key) +/*! @function + @abstract Const char* comparison function + */ +#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) + +static kh_inline khint_t __ac_Wang_hash(khint_t key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} +#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key) + +/* --- END OF HASH FUNCTIONS --- */ + +/* Other convenient macros... */ + +/*! + @abstract Type of the hash table. + @param name Name of the hash table [symbol] + */ +#define khash_t(name) kh_##name##_t + +/*! @function + @abstract Initiate a hash table. + @param name Name of the hash table [symbol] + @return Pointer to the hash table [khash_t(name)*] + */ +#define kh_init(name) kh_init_##name() + +/*! @function + @abstract Destroy a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_destroy(name, h) kh_destroy_##name(h) + +/*! @function + @abstract Reset a hash table without deallocating memory. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_clear(name, h) kh_clear_##name(h) + +/*! @function + @abstract Resize a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param s New size [khint_t] + */ +#define kh_resize(name, h, s) kh_resize_##name(h, s) + +/*! @function + @abstract Insert a key to the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @param r Extra return code: -1 if the operation failed; + 0 if the key is present in the hash table; + 1 if the bucket is empty (never used); 2 if the element in + the bucket has been deleted [int*] + @return Iterator to the inserted element [khint_t] + */ +#define kh_put(name, h, k, r) kh_put_##name(h, k, r) + +/*! @function + @abstract Retrieve a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t] + */ +#define kh_get(name, h, k) kh_get_##name(h, k) + +/*! @function + @abstract Remove a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Iterator to the element to be deleted [khint_t] + */ +#define kh_del(name, h, k) kh_del_##name(h, k) + +/*! @function + @abstract Test whether a bucket contains data. + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return 1 if containing data; 0 otherwise [int] + */ +#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) + +/*! @function + @abstract Get key given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Key [type of keys] + */ +#define kh_key(h, x) ((h)->keys[x]) + +/*! @function + @abstract Get value given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Value [type of values] + @discussion For hash sets, calling this results in segfault. + */ +#define kh_val(h, x) ((h)->vals[x]) + +/*! @function + @abstract Alias of kh_val() + */ +#define kh_value(h, x) ((h)->vals[x]) + +/*! @function + @abstract Get the start iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The start iterator [khint_t] + */ +#define kh_begin(h) (khint_t)(0) + +/*! @function + @abstract Get the end iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The end iterator [khint_t] + */ +#define kh_end(h) ((h)->n_buckets) + +/*! @function + @abstract Get the number of elements in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of elements in the hash table [khint_t] + */ +#define kh_size(h) ((h)->size) + +/*! @function + @abstract Get the number of buckets in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of buckets in the hash table [khint_t] + */ +#define kh_n_buckets(h) ((h)->n_buckets) + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach_value(h, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/* More conenient interfaces */ + +/*! @function + @abstract Instantiate a hash set containing integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT(name) \ + KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT(name, khval_t) \ + KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT64(name) \ + KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT64(name, khval_t) \ + KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +typedef const char *kh_cstr_t; +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_STR(name) \ + KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_STR(name, khval_t) \ + KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#endif /* __AC_KHASH_H */ diff --git a/klib/kvec.h b/klib/kvec.h new file mode 100644 index 000000000000..b5cce8508f8e --- /dev/null +++ b/klib/kvec.h @@ -0,0 +1,103 @@ +/* The MIT License + + Copyright (c) 2008, by Attractive Chaos + + 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. +*/ + +/* + An example: + +#include "kvec.h" +int main() { + kvec_t(int) array; + kv_init(array); + kv_push(int, array, 10); // append + kv_a(int, array, 20) = 5; // dynamic + kv_A(array, 20) = 4; // static + kv_destroy(array); + return 0; +} +*/ + +/* + 2008-09-22 (0.1.0): + + * The initial version. + +*/ + +#ifndef AC_KVEC_H +#define AC_KVEC_H + +#include + +#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) + +#define kvec_t(type) struct { size_t n, m; type *a; } +#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0) +#define kv_destroy(v) free((v).a) +#define kv_A(v, i) ((v).a[(i)]) +#define kv_pop(v) ((v).a[--(v).n]) +#define kv_size(v) ((v).n) +#define kv_max(v) ((v).m) + +#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m)) +#define kv_grow_factor 1.5 +#define kv_grow(type, v) ((v).m = ((v).m > 1 ? (v).m * kv_grow_factor : 2), \ + (v).a = (type*)realloc((v).a, sizeof(type) * (v).m)) + +#define kv_copy(type, v1, v0) do { \ + if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \ + (v1).n = (v0).n; \ + memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \ + } while (0) \ + +#define kv_push(type, v, x) do { \ + if ((v).n == (v).m) { \ + kv_grow(type, v); \ + } \ + (v).a[(v).n++] = (x); \ + } while (0) + +#define kv_prepend(type, v, x) do { \ + if ((v).n == (v).m) { \ + kv_grow(type, v); \ + } \ + memmove((v).a + 1, (v).a, sizeof(type) * (v).n); \ + (v).a[0] = (x); \ + (v).n ++; \ +} while (0) + +#define kv_concat(type, v1, v0) do { \ + if ((v1).m < (v0).n + (v1).n) kv_resize(type, v1, (v0).n + (v1).n); \ + memcpy((v1).a + (v1).n, (v0).a, sizeof(type) * ((v0).n + (v1).n)); \ + (v1).n = (v0).n + (v1).n; \ + } while (0) + +#define kv_del(type, v, i) do { \ + if ((i) < (v).n) { \ + memmove((v).a + (i), (v).a + ((i) + 1), sizeof(type) * ((v).n - (i) - 1)); \ + (v).n --; \ + } \ +} while (0) + +#endif diff --git a/m4/.gitignore b/m4/.gitignore deleted file mode 100644 index 5e7d2734cfc6..000000000000 --- a/m4/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore diff --git a/m4/ax_lua.m4 b/m4/ax_lua.m4 deleted file mode 100644 index 7662f4f22d1b..000000000000 --- a/m4/ax_lua.m4 +++ /dev/null @@ -1,606 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_lua.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] -# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] -# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] -# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] -# -# DESCRIPTION -# -# Detect a Lua interpreter, optionally specifying a minimum and maximum -# version number. Set up important Lua paths, such as the directories in -# which to install scripts and modules (shared libraries). -# -# Also detect Lua headers and libraries. The Lua version contained in the -# header is checked to match the Lua interpreter version exactly. When -# searching for Lua libraries, the version number is used as a suffix. -# This is done with the goal of supporting multiple Lua installs (5.1 and -# 5.2 side-by-side). -# -# A note on compatibility with previous versions: This file has been -# mostly rewritten for serial 18. Most developers should be able to use -# these macros without needing to modify configure.ac. Care has been taken -# to preserve each macro's behavior, but there are some differences: -# -# 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as -# AX_PROG_LUA with no arguments. -# -# 2) AX_LUA_HEADERS now checks that the version number defined in lua.h -# matches the interpreter version. AX_LUA_HEADERS_VERSION is therefore -# unnecessary, so it is deprecated and does not expand to anything. -# -# 3) The configure flag --with-lua-suffix no longer exists; the user -# should instead specify the LUA precious variable on the command line. -# See the AX_PROG_LUA description for details. -# -# Please read the macro descriptions below for more information. -# -# This file was inspired by Andrew Dalke's and James Henstridge's -# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4 -# (serial 17). Basically, this file is a mash-up of those two files. I -# like to think it combines the best of the two! -# -# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua -# paths. Adds precious variable LUA, which may contain the path of the Lua -# interpreter. If LUA is blank, the user's path is searched for an -# suitable interpreter. -# -# If MINIMUM-VERSION is supplied, then only Lua interpreters with a -# version number greater or equal to MINIMUM-VERSION will be accepted. If -# TOO-BIG- VERSION is also supplied, then only Lua interpreters with a -# version number greater or equal to MINIMUM-VERSION and less than -# TOO-BIG-VERSION will be accepted. -# -# The Lua version number, LUA_VERSION, is found from the interpreter, and -# substituted. LUA_PLATFORM is also found, but not currently supported (no -# standard representation). -# -# Finally, the macro finds four paths: -# -# luadir Directory to install Lua scripts. -# pkgluadir $luadir/$PACKAGE -# luaexecdir Directory to install Lua modules. -# pkgluaexecdir $luaexecdir/$PACKAGE -# -# These paths a found based on $prefix, $exec_prefix, Lua's package.path, -# and package.cpath. The first path of package.path beginning with $prefix -# is selected as luadir. The first path of package.cpath beginning with -# $exec_prefix is used as luaexecdir. This should work on all reasonable -# Lua installations. If a path cannot be determined, a default path is -# used. Of course, the user can override these later when invoking make. -# -# luadir Default: $prefix/share/lua/$LUA_VERSION -# luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION -# -# These directories can be used by Automake as install destinations. The -# variable name minus 'dir' needs to be used as a prefix to the -# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES. -# -# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is -# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT- -# FOUND is blank, then it will default to printing an error. To prevent -# the default behavior, give ':' as an action. -# -# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be -# expanded before this macro. Adds precious variable LUA_INCLUDE, which -# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If -# LUA_INCLUDE is blank, then this macro will attempt to find suitable -# flags. -# -# LUA_INCLUDE can be used by Automake to compile Lua modules or -# executables with embedded interpreters. The *_CPPFLAGS variables should -# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE). -# -# This macro searches for the header lua.h (and others). The search is -# performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE. -# If the search is unsuccessful, then some common directories are tried. -# If the headers are then found, then LUA_INCLUDE is set accordingly. -# -# The paths automatically searched are: -# -# * /usr/include/luaX.Y -# * /usr/include/lua/X.Y -# * /usr/include/luaXY -# * /usr/local/include/luaX.Y -# * /usr/local/include/lua-X.Y -# * /usr/local/include/lua/X.Y -# * /usr/local/include/luaXY -# -# (Where X.Y is the Lua version number, e.g. 5.1.) -# -# The Lua version number found in the headers is always checked to match -# the Lua interpreter's version number. Lua headers with mismatched -# version numbers are not accepted. -# -# If headers are found, then ACTION-IF-FOUND is performed, otherwise -# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then -# it will default to printing an error. To prevent the default behavior, -# set the action to ':'. -# -# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be -# expanded before this macro. Adds precious variable LUA_LIB, which may -# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank, -# then this macro will attempt to find suitable flags. -# -# LUA_LIB can be used by Automake to link Lua modules or executables with -# embedded interpreters. The *_LIBADD and *_LDADD variables should be used -# for this purpose, e.g. mymod_LIBADD = $(LUA_LIB). -# -# This macro searches for the Lua library. More technically, it searches -# for a library containing the function lua_load. The search is performed -# with a combination of LIBS, LIBRARY_PATH, and LUA_LIB. -# -# If the search determines that some linker flags are missing, then those -# flags will be added to LUA_LIB. -# -# If libraries are found, then ACTION-IF-FOUND is performed, otherwise -# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then -# it will default to printing an error. To prevent the default behavior, -# set the action to ':'. -# -# AX_LUA_READLINE: Search for readline headers and libraries. Requires the -# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the -# Autoconf Archive. -# -# If a readline compatible library is found, then ACTION-IF-FOUND is -# performed, otherwise ACTION-IF-NOT-FOUND is performed. -# -# LICENSE -# -# Copyright (c) 2014 Reuben Thomas -# Copyright (c) 2013 Tim Perkins -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 23 - -dnl ========================================================================= -dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION], -dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -dnl ========================================================================= -AC_DEFUN([AX_PROG_LUA], -[ - dnl Make LUA a precious variable. - AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1]) - - dnl Find a Lua interpreter. - m4_define_default([_AX_LUA_INTERPRETER_LIST], - [lua lua5.2 lua52 lua5.1 lua51 lua50]) - - m4_if([$1], [], - [ dnl No version check is needed. Find any Lua interpreter. - AS_IF([test "x$LUA" = 'x'], - [AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])]) - ax_display_LUA='lua' - - dnl At least check if this is a Lua interpreter. - AC_MSG_CHECKING([if $LUA is a Lua interpreter]) - _AX_LUA_CHK_IS_INTRP([$LUA], - [AC_MSG_RESULT([yes])], - [ AC_MSG_RESULT([no]) - AC_MSG_ERROR([not a Lua interpreter]) - ]) - ], - [ dnl A version check is needed. - AS_IF([test "x$LUA" != 'x'], - [ dnl Check if this is a Lua interpreter. - AC_MSG_CHECKING([if $LUA is a Lua interpreter]) - _AX_LUA_CHK_IS_INTRP([$LUA], - [AC_MSG_RESULT([yes])], - [ AC_MSG_RESULT([no]) - AC_MSG_ERROR([not a Lua interpreter]) - ]) - dnl Check the version. - m4_if([$2], [], - [_ax_check_text="whether $LUA version >= $1"], - [_ax_check_text="whether $LUA version >= $1, < $2"]) - AC_MSG_CHECKING([$_ax_check_text]) - _AX_LUA_CHK_VER([$LUA], [$1], [$2], - [AC_MSG_RESULT([yes])], - [ AC_MSG_RESULT([no]) - AC_MSG_ERROR([version is out of range for specified LUA])]) - ax_display_LUA=$LUA - ], - [ dnl Try each interpreter until we find one that satisfies VERSION. - m4_if([$2], [], - [_ax_check_text="for a Lua interpreter with version >= $1"], - [_ax_check_text="for a Lua interpreter with version >= $1, < $2"]) - AC_CACHE_CHECK([$_ax_check_text], - [ax_cv_pathless_LUA], - [ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do - test "x$ax_cv_pathless_LUA" = 'xnone' && break - _AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue]) - _AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break]) - done - ]) - dnl Set $LUA to the absolute path of $ax_cv_pathless_LUA. - AS_IF([test "x$ax_cv_pathless_LUA" = 'xnone'], - [LUA=':'], - [AC_PATH_PROG([LUA], [$ax_cv_pathless_LUA])]) - ax_display_LUA=$ax_cv_pathless_LUA - ]) - ]) - - AS_IF([test "x$LUA" = 'x:'], - [ dnl Run any user-specified action, or abort. - m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])]) - ], - [ dnl Query Lua for its version number. - AC_CACHE_CHECK([for $ax_display_LUA version], [ax_cv_lua_version], - [ ax_cv_lua_version=`$LUA -e 'print(_VERSION:match "(%d+%.%d+)")'` ]) - AS_IF([test "x$ax_cv_lua_version" = 'x'], - [AC_MSG_ERROR([invalid Lua version number])]) - AC_SUBST([LUA_VERSION], [$ax_cv_lua_version]) - AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | sed 's|\.||'`]) - - dnl The following check is not supported: - dnl At times (like when building shared libraries) you may want to know - dnl which OS platform Lua thinks this is. - AC_CACHE_CHECK([for $ax_display_LUA platform], [ax_cv_lua_platform], - [ax_cv_lua_platform=`$LUA -e "print('unknown')"`]) - AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform]) - - dnl Use the values of $prefix and $exec_prefix for the corresponding - dnl values of LUA_PREFIX and LUA_EXEC_PREFIX. These are made distinct - dnl variables so they can be overridden if need be. However, the general - dnl consensus is that you shouldn't need this ability. - AC_SUBST([LUA_PREFIX], ['${prefix}']) - AC_SUBST([LUA_EXEC_PREFIX], ['${exec_prefix}']) - - dnl Lua provides no way to query the script directory, and instead - dnl provides LUA_PATH. However, we should be able to make a safe educated - dnl guess. If the built-in search path contains a directory which is - dnl prefixed by $prefix, then we can store scripts there. The first - dnl matching path will be used. - AC_CACHE_CHECK([for $ax_display_LUA script directory], - [ax_cv_lua_luadir], - [ AS_IF([test "x$prefix" = 'xNONE'], - [ax_lua_prefix=$ac_default_prefix], - [ax_lua_prefix=$prefix]) - - dnl Initialize to the default path. - ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION" - - dnl Try to find a path with the prefix. - _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [package.path]) - AS_IF([test "x$ax_lua_prefixed_path" != 'x'], - [ dnl Fix the prefix. - _ax_strip_prefix=`echo "$ax_lua_prefix" | sed 's|.|.|g'` - ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \ - sed "s,^$_ax_strip_prefix,$LUA_PREFIX,"` - ]) - ]) - AC_SUBST([luadir], [$ax_cv_lua_luadir]) - AC_SUBST([pkgluadir], [\${luadir}/$PACKAGE]) - - dnl Lua provides no way to query the module directory, and instead - dnl provides LUA_PATH. However, we should be able to make a safe educated - dnl guess. If the built-in search path contains a directory which is - dnl prefixed by $exec_prefix, then we can store modules there. The first - dnl matching path will be used. - AC_CACHE_CHECK([for $ax_display_LUA module directory], - [ax_cv_lua_luaexecdir], - [ AS_IF([test "x$exec_prefix" = 'xNONE'], - [ax_lua_exec_prefix=$ax_lua_prefix], - [ax_lua_exec_prefix=$exec_prefix]) - - dnl Initialize to the default path. - ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION" - - dnl Try to find a path with the prefix. - _AX_LUA_FND_PRFX_PTH([$LUA], - [$ax_lua_exec_prefix], [package.cpathd]) - AS_IF([test "x$ax_lua_prefixed_path" != 'x'], - [ dnl Fix the prefix. - _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | sed 's|.|.|g'` - ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \ - sed "s,^$_ax_strip_prefix,$LUA_EXEC_PREFIX,"` - ]) - ]) - AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir]) - AC_SUBST([pkgluaexecdir], [\${luaexecdir}/$PACKAGE]) - - dnl Run any user specified action. - $3 - ]) -]) - -dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA. -AC_DEFUN([AX_WITH_LUA], -[ - AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA]]) - AX_PROG_LUA -]) - - -dnl ========================================================================= -dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) -dnl ========================================================================= -AC_DEFUN([_AX_LUA_CHK_IS_INTRP], -[ - dnl Just print _VERSION because all Lua interpreters have this global. - AS_IF([$1 -e "print('Hello ' .. _VERSION .. '!')" &>/dev/null], - [$2], [$3]) -]) - - -dnl ========================================================================= -dnl _AX_LUA_CHK_VER(PROG, MINIMUM-VERSION, [TOO-BIG-VERSION], -dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE]) -dnl ========================================================================= -AC_DEFUN([_AX_LUA_CHK_VER], -[ - AS_IF([$1 2>/dev/null -e ' - function norm (v) i,j=v:match "(%d+)%.(%d+)" return 100 * i + j end - v=norm (_VERSION) - os.exit ((v >= norm ("$2") and ("$3" == "" or v < norm ("$3"))) and 0 or 1)'], - [$4], [$5]) -]) - - -dnl ========================================================================= -dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, LUA-PATH-VARIABLE) -dnl ========================================================================= -AC_DEFUN([_AX_LUA_FND_PRFX_PTH], -[ - dnl Invokes the Lua interpreter PROG to print the path variable - dnl LUA-PATH-VARIABLE, usually package.path or package.cpath. Paths are - dnl then matched against PREFIX. The first path to begin with PREFIX is set - dnl to ax_lua_prefixed_path. - - ax_lua_prefixed_path='' - _ax_package_paths=`$1 -e 'print($3)' 2>/dev/null | sed 's|;|\n|g'` - dnl Try the paths in order, looking for the prefix. - for _ax_package_path in $_ax_package_paths; do - dnl Copy the path, up to the use of a Lua wildcard. - _ax_path_parts=`echo "$_ax_package_path" | sed 's|/|\n|g'` - _ax_reassembled='' - for _ax_path_part in $_ax_path_parts; do - echo "$_ax_path_part" | grep '\?' >/dev/null && break - _ax_reassembled="$_ax_reassembled/$_ax_path_part" - done - dnl Check the path against the prefix. - _ax_package_path=$_ax_reassembled - if echo "$_ax_package_path" | grep "^$2" >/dev/null; then - dnl Found it. - ax_lua_prefixed_path=$_ax_package_path - break - fi - done -]) - - -dnl ========================================================================= -dnl AX_LUA_HEADERS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -dnl ========================================================================= -AC_DEFUN([AX_LUA_HEADERS], -[ - dnl Check for LUA_VERSION. - AC_MSG_CHECKING([if LUA_VERSION is defined]) - AS_IF([test "x$LUA_VERSION" != 'x'], - [AC_MSG_RESULT([yes])], - [ AC_MSG_RESULT([no]) - AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION]) - ]) - - dnl Make LUA_INCLUDE a precious variable. - AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1]) - - dnl Some default directories to search. - LUA_SHORT_VERSION=`echo "$LUA_VERSION" | sed 's|\.||'` - m4_define_default([_AX_LUA_INCLUDE_LIST], - [ /usr/include/lua$LUA_VERSION \ - /usr/include/lua/$LUA_VERSION \ - /usr/include/lua$LUA_SHORT_VERSION \ - /usr/local/include/lua$LUA_VERSION \ - /usr/local/include/lua-$LUA_VERSION \ - /usr/local/include/lua/$LUA_VERSION \ - /usr/local/include/lua$LUA_SHORT_VERSION \ - ]) - - dnl Try to find the headers. - _ax_lua_saved_cppflags=$CPPFLAGS - CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" - AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) - CPPFLAGS=$_ax_lua_saved_cppflags - - dnl Try some other directories if LUA_INCLUDE was not set. - AS_IF([test "x$LUA_INCLUDE" = 'x' && - test "x$ac_cv_header_lua_h" != 'xyes'], - [ dnl Try some common include paths. - for _ax_include_path in _AX_LUA_INCLUDE_LIST; do - test ! -d "$_ax_include_path" && continue - - AC_MSG_CHECKING([for Lua headers in]) - AC_MSG_RESULT([$_ax_include_path]) - - AS_UNSET([ac_cv_header_lua_h]) - AS_UNSET([ac_cv_header_lualib_h]) - AS_UNSET([ac_cv_header_lauxlib_h]) - AS_UNSET([ac_cv_header_luaconf_h]) - - _ax_lua_saved_cppflags=$CPPFLAGS - CPPFLAGS="$CPPFLAGS -I$_ax_include_path" - AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) - CPPFLAGS=$_ax_lua_saved_cppflags - - AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], - [ LUA_INCLUDE="-I$_ax_include_path" - break - ]) - done - ]) - - AS_IF([test "x$ac_cv_header_lua_h" = 'xyes' && test "x$cross_compiling" != 'xyes'], - [ dnl Make a program to print LUA_VERSION defined in the header. - dnl TODO This probably shouldn't be a runtime test. - - AC_CACHE_CHECK([for Lua header version], - [ax_cv_lua_header_version], - [ _ax_lua_saved_cppflags=$CPPFLAGS - CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" - AC_RUN_IFELSE( - [ AC_LANG_SOURCE([[ -#include -#include -#include -int main(int argc, char ** argv) -{ - if(argc > 1) printf("%s", LUA_VERSION); - exit(EXIT_SUCCESS); -} -]]) - ], - [ ax_cv_lua_header_version=`./conftest$EXEEXT p | \ - sed "s|^Lua \(.*\)|\1|" | \ - grep -o "^@<:@0-9@:>@\+\\.@<:@0-9@:>@\+"` - ], - [ax_cv_lua_header_version='unknown']) - CPPFLAGS=$_ax_lua_saved_cppflags - ]) - - dnl Compare this to the previously found LUA_VERSION. - AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION]) - AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"], - [ AC_MSG_RESULT([yes]) - ax_header_version_match='yes' - ], - [ AC_MSG_RESULT([no]) - ax_header_version_match='no' - ]) - ], - [ - ax_header_version_match='yes' - ]) - - dnl Was LUA_INCLUDE specified? - AS_IF([test "x$ax_header_version_match" != 'xyes' && - test "x$LUA_INCLUDE" != 'x'], - [AC_MSG_ERROR([cannot find headers for specified LUA_INCLUDE])]) - - dnl Test the final result and run user code. - AS_IF([test "x$ax_header_version_match" = 'xyes'], [$1], - [m4_default([$2], [AC_MSG_ERROR([cannot find Lua includes])])]) -]) - -dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS. -AC_DEFUN([AX_LUA_HEADERS_VERSION], -[ - AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS]]) -]) - - -dnl ========================================================================= -dnl AX_LUA_LIBS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -dnl ========================================================================= -AC_DEFUN([AX_LUA_LIBS], -[ - dnl TODO Should this macro also check various -L flags? - - dnl Check for LUA_VERSION. - AC_MSG_CHECKING([if LUA_VERSION is defined]) - AS_IF([test "x$LUA_VERSION" != 'x'], - [AC_MSG_RESULT([yes])], - [ AC_MSG_RESULT([no]) - AC_MSG_ERROR([cannot check Lua libs without knowing LUA_VERSION]) - ]) - - dnl Make LUA_LIB a precious variable. - AC_ARG_VAR([LUA_LIB], [The Lua library, e.g. -llua5.1]) - - AS_IF([test "x$LUA_LIB" != 'x'], - [ dnl Check that LUA_LIBS works. - _ax_lua_saved_libs=$LIBS - LIBS="$LIBS $LUA_LIB" - AC_SEARCH_LIBS([lua_load], [], - [_ax_found_lua_libs='yes'], - [_ax_found_lua_libs='no']) - LIBS=$_ax_lua_saved_libs - - dnl Check the result. - AS_IF([test "x$_ax_found_lua_libs" != 'xyes'], - [AC_MSG_ERROR([cannot find libs for specified LUA_LIB])]) - ], - [ dnl First search for extra libs. - _ax_lua_extra_libs='' - - _ax_lua_saved_libs=$LIBS - LIBS="$LIBS $LUA_LIB" - AC_SEARCH_LIBS([exp], [m]) - AC_SEARCH_LIBS([dlopen], [dl]) - LIBS=$_ax_lua_saved_libs - - AS_IF([test "x$ac_cv_search_exp" != 'xno' && - test "x$ac_cv_search_exp" != 'xnone required'], - [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_exp"]) - - AS_IF([test "x$ac_cv_search_dlopen" != 'xno' && - test "x$ac_cv_search_dlopen" != 'xnone required'], - [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_dlopen"]) - - dnl Try to find the Lua libs. - _ax_lua_saved_libs=$LIBS - LIBS="$LIBS $LUA_LIB" - AC_SEARCH_LIBS([lua_load], - [ lua$LUA_VERSION \ - lua$LUA_SHORT_VERSION \ - lua-$LUA_VERSION \ - lua-$LUA_SHORT_VERSION \ - lua], - [_ax_found_lua_libs='yes'], - [_ax_found_lua_libs='no'], - [$_ax_lua_extra_libs]) - LIBS=$_ax_lua_saved_libs - - AS_IF([test "x$ac_cv_search_lua_load" != 'xno' && - test "x$ac_cv_search_lua_load" != 'xnone required'], - [LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"]) - ]) - - dnl Test the result and run user code. - AS_IF([test "x$_ax_found_lua_libs" = 'xyes'], [$1], - [m4_default([$2], [AC_MSG_ERROR([cannot find Lua libs])])]) -]) - - -dnl ========================================================================= -dnl AX_LUA_READLINE([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -dnl ========================================================================= -AC_DEFUN([AX_LUA_READLINE], -[ - AX_LIB_READLINE - AS_IF([test "x$ac_cv_header_readline_readline_h" != 'x' && - test "x$ac_cv_header_readline_history_h" != 'x'], - [ LUA_LIBS_CFLAGS="-DLUA_USE_READLINE $LUA_LIBS_CFLAGS" - $1 - ], - [$2]) -]) diff --git a/src/Makefile.am b/src/Makefile.am index 417d34e161ca..c3f0c9fffb60 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,7 @@ libucl_common_cflags= -I$(top_srcdir)/src \ -I$(top_srcdir)/include \ -I$(top_srcdir)/uthash \ + -I$(top_srcdir)/klib \ -Wall -W -Wno-unused-parameter -Wno-pointer-sign lib_LTLIBRARIES= libucl.la libucl_la_SOURCES= ucl_emitter.c \ diff --git a/src/ucl_emitter.c b/src/ucl_emitter.c index 8134d09c1a65..9ddf3584a25b 100644 --- a/src/ucl_emitter.c +++ b/src/ucl_emitter.c @@ -250,6 +250,7 @@ ucl_emitter_common_start_array (struct ucl_emitter_context *ctx, const ucl_object_t *obj, bool print_key, bool compact) { const ucl_object_t *cur; + ucl_object_iter_t iter = NULL; const struct ucl_emitter_functions *func = ctx->func; bool first = true; @@ -266,18 +267,22 @@ ucl_emitter_common_start_array (struct ucl_emitter_context *ctx, if (obj->type == UCL_ARRAY) { /* explicit array */ - cur = obj->value.av; + while ((cur = ucl_iterate_object (obj, &iter, true)) != NULL) { + ucl_emitter_common_elt (ctx, cur, first, false, compact); + first = false; + } } else { /* implicit array */ cur = obj; + while (cur) { + ucl_emitter_common_elt (ctx, cur, first, false, compact); + first = false; + cur = cur->next; + } } - while (cur) { - ucl_emitter_common_elt (ctx, cur, first, false, compact); - first = false; - cur = cur->next; - } + } /** diff --git a/src/ucl_emitter_utils.c b/src/ucl_emitter_utils.c index da412097ffbc..91cad78bcbf6 100644 --- a/src/ucl_emitter_utils.c +++ b/src/ucl_emitter_utils.c @@ -289,6 +289,7 @@ ucl_fd_append_character (unsigned char c, size_t len, void *ud) else { memset (buf, c, len); if (write (fd, buf, len) == -1) { + free(buf); return -1; } free (buf); diff --git a/src/ucl_hash.c b/src/ucl_hash.c index ea55491ed504..275e84d478aa 100644 --- a/src/ucl_hash.c +++ b/src/ucl_hash.c @@ -23,119 +23,331 @@ #include "ucl_internal.h" #include "ucl_hash.h" -#include "utlist.h" +#include "khash.h" +#include "kvec.h" + +struct ucl_hash_elt { + const ucl_object_t *obj; + size_t ar_idx; +}; + +struct ucl_hash_struct { + void *hash; + kvec_t(const ucl_object_t *) ar; + bool caseless; +}; + +static inline uint32_t +ucl_hash_func (const ucl_object_t *o) +{ + return XXH32 (o->key, o->keylen, 0xdeadbeef); +} + +static inline int +ucl_hash_equal (const ucl_object_t *k1, const ucl_object_t *k2) +{ + if (k1->keylen == k2->keylen) { + return strncmp (k1->key, k2->key, k1->keylen) == 0; + } + + return 0; +} + +KHASH_INIT (ucl_hash_node, const ucl_object_t *, struct ucl_hash_elt, 1, + ucl_hash_func, ucl_hash_equal) + +static inline uint32_t +ucl_hash_caseless_func (const ucl_object_t *o) +{ + void *xxh = XXH32_init (0xdeadbeef); + char hash_buf[64], *c; + const char *p; + ssize_t remain = o->keylen; + + p = o->key; + c = &hash_buf[0]; + + while (remain > 0) { + *c++ = tolower (*p++); + + if (c - &hash_buf[0] == sizeof (hash_buf)) { + XXH32_update (xxh, hash_buf, sizeof (hash_buf)); + c = &hash_buf[0]; + } + remain --; + } + + if (c - &hash_buf[0] != 0) { + XXH32_update (xxh, hash_buf, c - &hash_buf[0]); + } + + return XXH32_digest (xxh); +} + +static inline int +ucl_hash_caseless_equal (const ucl_object_t *k1, const ucl_object_t *k2) +{ + if (k1->keylen == k2->keylen) { + return strncasecmp (k1->key, k2->key, k1->keylen) == 0; + } + + return 0; +} + +KHASH_INIT (ucl_hash_caseless_node, const ucl_object_t *, struct ucl_hash_elt, 1, + ucl_hash_caseless_func, ucl_hash_caseless_equal) ucl_hash_t* -ucl_hash_create (void) +ucl_hash_create (bool ignore_case) { ucl_hash_t *new; new = UCL_ALLOC (sizeof (ucl_hash_t)); if (new != NULL) { - new->buckets = NULL; + kv_init (new->ar); + + new->caseless = ignore_case; + if (ignore_case) { + khash_t(ucl_hash_caseless_node) *h = kh_init (ucl_hash_caseless_node); + new->hash = (void *)h; + } + else { + khash_t(ucl_hash_node) *h = kh_init (ucl_hash_node); + new->hash = (void *)h; + } } return new; } void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func) { - ucl_hash_node_t *elt, *tmp; - const ucl_object_t *cur, *otmp; + const ucl_object_t *cur, *tmp; - HASH_ITER (hh, hashlin->buckets, elt, tmp) { - HASH_DELETE (hh, hashlin->buckets, elt); - if (func) { - DL_FOREACH_SAFE (elt->data, cur, otmp) { - /* Need to deconst here */ - func (__DECONST (ucl_object_t *, cur)); + if (hashlin == NULL) { + return; + } + + if (func != NULL) { + /* Iterate over the hash first */ + khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) + hashlin->hash; + khiter_t k; + + for (k = kh_begin (h); k != kh_end (h); ++k) { + if (kh_exist (h, k)) { + cur = (kh_value (h, k)).obj; + while (cur != NULL) { + tmp = cur->next; + func (__DECONST (ucl_object_t *, cur)); + cur = tmp; + } } } - UCL_FREE (sizeof (ucl_hash_node_t), elt); } - UCL_FREE (sizeof (ucl_hash_t), hashlin); + + if (hashlin->caseless) { + khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *) + hashlin->hash; + kh_destroy (ucl_hash_caseless_node, h); + } + else { + khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) + hashlin->hash; + kh_destroy (ucl_hash_node, h); + } + + kv_destroy (hashlin->ar); + UCL_FREE (sizeof (*hashlin), hashlin); } void ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, const char *key, unsigned keylen) { - ucl_hash_node_t *node; + khiter_t k; + int ret; + struct ucl_hash_elt *elt; - node = UCL_ALLOC (sizeof (ucl_hash_node_t)); - node->data = obj; - HASH_ADD_KEYPTR (hh, hashlin->buckets, key, keylen, node); + if (hashlin == NULL) { + return; + } + + if (hashlin->caseless) { + khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *) + hashlin->hash; + k = kh_put (ucl_hash_caseless_node, h, obj, &ret); + if (ret > 0) { + elt = &kh_value (h, k); + kv_push (const ucl_object_t *, hashlin->ar, obj); + elt->obj = obj; + elt->ar_idx = kv_size (hashlin->ar) - 1; + } + } + else { + khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) + hashlin->hash; + k = kh_put (ucl_hash_node, h, obj, &ret); + if (ret > 0) { + elt = &kh_value (h, k); + kv_push (const ucl_object_t *, hashlin->ar, obj); + elt->obj = obj; + elt->ar_idx = kv_size (hashlin->ar) - 1; + } + } } void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old, const ucl_object_t *new) { - ucl_hash_node_t *node; + khiter_t k; + int ret; + struct ucl_hash_elt elt, *pelt; - HASH_FIND (hh, hashlin->buckets, old->key, old->keylen, node); - if (node != NULL) { - /* Direct replacement */ - node->data = new; - node->hh.key = new->key; - node->hh.keylen = new->keylen; + if (hashlin == NULL) { + return; + } + + if (hashlin->caseless) { + khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *) + hashlin->hash; + k = kh_put (ucl_hash_caseless_node, h, old, &ret); + if (ret == 0) { + elt = kh_value (h, k); + kh_del (ucl_hash_caseless_node, h, k); + k = kh_put (ucl_hash_caseless_node, h, new, &ret); + pelt = &kh_value (h, k); + pelt->obj = new; + pelt->ar_idx = elt.ar_idx; + kv_A (hashlin->ar, elt.ar_idx) = new; + } + } + else { + khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) + hashlin->hash; + k = kh_put (ucl_hash_node, h, old, &ret); + if (ret == 0) { + elt = kh_value (h, k); + kh_del (ucl_hash_node, h, k); + k = kh_put (ucl_hash_node, h, new, &ret); + pelt = &kh_value (h, k); + pelt->obj = new; + pelt->ar_idx = elt.ar_idx; + kv_A (hashlin->ar, elt.ar_idx) = new; + } } } +struct ucl_hash_real_iter { + const ucl_object_t **cur; + const ucl_object_t **end; +}; + const void* ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter) { - ucl_hash_node_t *elt = *iter; + struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(*iter); + const ucl_object_t *ret = NULL; - if (elt == NULL) { - if (hashlin == NULL || hashlin->buckets == NULL) { - return NULL; - } - elt = hashlin->buckets; - if (elt == NULL) { - return NULL; - } - } - else if (elt == hashlin->buckets) { + if (hashlin == NULL) { return NULL; } - *iter = elt->hh.next ? elt->hh.next : hashlin->buckets; - return elt->data; + if (it == NULL) { + it = UCL_ALLOC (sizeof (*it)); + it->cur = &hashlin->ar.a[0]; + it->end = it->cur + hashlin->ar.n; + } + + if (it->cur < it->end) { + ret = *it->cur++; + } + else { + UCL_FREE (sizeof (*it), it); + *iter = NULL; + return NULL; + } + + *iter = it; + + return ret; } bool -ucl_hash_iter_has_next (ucl_hash_iter_t iter) +ucl_hash_iter_has_next (ucl_hash_t *hashlin, ucl_hash_iter_t iter) { - ucl_hash_node_t *elt = iter; + struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(iter); - return (elt == NULL || elt->hh.prev != NULL); + return it->cur < it->end - 1; } const ucl_object_t* ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen) { - ucl_hash_node_t *found; + khiter_t k; + const ucl_object_t *ret = NULL; + ucl_object_t search; + struct ucl_hash_elt *elt; + + search.key = key; + search.keylen = keylen; if (hashlin == NULL) { return NULL; } - HASH_FIND (hh, hashlin->buckets, key, keylen, found); - if (found) { - return found->data; + if (hashlin->caseless) { + khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *) + hashlin->hash; + + k = kh_get (ucl_hash_caseless_node, h, &search); + if (k != kh_end (h)) { + elt = &kh_value (h, k); + ret = elt->obj; + } } - return NULL; + else { + khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) + hashlin->hash; + k = kh_get (ucl_hash_node, h, &search); + if (k != kh_end (h)) { + elt = &kh_value (h, k); + ret = elt->obj; + } + } + + return ret; } void ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj) { - ucl_hash_node_t *found; + khiter_t k; + struct ucl_hash_elt *elt; - HASH_FIND (hh, hashlin->buckets, obj->key, obj->keylen, found); + if (hashlin == NULL) { + return; + } - if (found) { - HASH_DELETE (hh, hashlin->buckets, found); - UCL_FREE (sizeof (ucl_hash_node_t), found); + if (hashlin->caseless) { + khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *) + hashlin->hash; + + k = kh_get (ucl_hash_caseless_node, h, obj); + if (k != kh_end (h)) { + elt = &kh_value (h, k); + kv_A (hashlin->ar, elt->ar_idx) = NULL; + kh_del (ucl_hash_caseless_node, h, k); + } + } + else { + khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) + hashlin->hash; + k = kh_get (ucl_hash_node, h, obj); + if (k != kh_end (h)) { + elt = &kh_value (h, k); + kv_A (hashlin->ar, elt->ar_idx) = NULL; + kh_del (ucl_hash_node, h, k); + } } } diff --git a/src/ucl_hash.h b/src/ucl_hash.h index ddbfdba19ce8..64c83eac8bc1 100644 --- a/src/ucl_hash.h +++ b/src/ucl_hash.h @@ -25,15 +25,11 @@ #define __UCL_HASH_H #include "ucl.h" -#include "uthash.h" /******************************************************************************/ -typedef struct ucl_hash_node_s -{ - const ucl_object_t *data; - UT_hash_handle hh; -} ucl_hash_node_t; +struct ucl_hash_node_s; +typedef struct ucl_hash_node_s ucl_hash_node_t; typedef int ucl_hash_cmp_func (const void* void_a, const void* void_b); typedef void ucl_hash_free_func (void *ptr); @@ -43,16 +39,14 @@ typedef void* ucl_hash_iter_t; /** * Linear chained hashtable. */ -typedef struct ucl_hash_struct -{ - ucl_hash_node_t *buckets; /**< array of hash buckets. One list for each hash modulus. */ -} ucl_hash_t; +struct ucl_hash_struct; +typedef struct ucl_hash_struct ucl_hash_t; /** * Initializes the hashtable. */ -ucl_hash_t* ucl_hash_create (void); +ucl_hash_t* ucl_hash_create (bool ignore_case); /** * Deinitializes the hashtable. @@ -94,6 +88,6 @@ const void* ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter); /** * Check whether an iterator has next element */ -bool ucl_hash_iter_has_next (ucl_hash_iter_t iter); +bool ucl_hash_iter_has_next (ucl_hash_t *hashlin, ucl_hash_iter_t iter); #endif diff --git a/src/ucl_internal.h b/src/ucl_internal.h index 2f75872b7bb8..bdbe691d092b 100644 --- a/src/ucl_internal.h +++ b/src/ucl_internal.h @@ -339,14 +339,17 @@ ucl_hash_search_obj (ucl_hash_t* hashlin, ucl_object_t *obj) return (const ucl_object_t *)ucl_hash_search (hashlin, obj->key, obj->keylen); } -static inline ucl_hash_t * -ucl_hash_insert_object (ucl_hash_t *hashlin, const ucl_object_t *obj) UCL_WARN_UNUSED_RESULT; +static inline ucl_hash_t * ucl_hash_insert_object (ucl_hash_t *hashlin, + const ucl_object_t *obj, + bool ignore_case) UCL_WARN_UNUSED_RESULT; static inline ucl_hash_t * -ucl_hash_insert_object (ucl_hash_t *hashlin, const ucl_object_t *obj) +ucl_hash_insert_object (ucl_hash_t *hashlin, + const ucl_object_t *obj, + bool ignore_case) { if (hashlin == NULL) { - hashlin = ucl_hash_create (); + hashlin = ucl_hash_create (ignore_case); } ucl_hash_insert (hashlin, obj, obj->key, obj->keylen); diff --git a/src/ucl_parser.c b/src/ucl_parser.c index 0d118d84b75a..75acba8ecbd3 100644 --- a/src/ucl_parser.c +++ b/src/ucl_parser.c @@ -570,7 +570,7 @@ ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_arra else { obj->type = UCL_OBJECT; } - obj->value.ov = ucl_hash_create (); + obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE); parser->state = UCL_STATE_KEY; } else { @@ -975,7 +975,7 @@ ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont, else { if ((top->flags & UCL_OBJECT_MULTIVALUE) != 0) { /* Just add to the explicit array */ - DL_APPEND (top->value.av, elt); + ucl_array_append (top, elt); } else { /* Convert to an array */ @@ -984,8 +984,8 @@ ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont, nobj->key = top->key; nobj->keylen = top->keylen; nobj->flags |= UCL_OBJECT_MULTIVALUE; - DL_APPEND (nobj->value.av, top); - DL_APPEND (nobj->value.av, elt); + ucl_array_append (nobj, top); + ucl_array_append (nobj, elt); ucl_hash_insert (cont, nobj, nobj->key, nobj->keylen); } } @@ -1016,6 +1016,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke ucl_chunk_skipc (chunk, p); parser->prev_state = parser->state; parser->state = UCL_STATE_MACRO_NAME; + *end_of_object = false; return true; } while (p < chunk->end) { @@ -1195,7 +1196,8 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke nobj->keylen = keylen; tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj)); if (tobj == NULL) { - container = ucl_hash_insert_object (container, nobj); + container = ucl_hash_insert_object (container, nobj, + parser->flags & UCL_PARSER_KEY_LOWERCASE); nobj->prev = nobj; nobj->next = NULL; parser->stack->obj->len ++; @@ -1363,14 +1365,16 @@ ucl_get_value_object (struct ucl_parser *parser) { ucl_object_t *t, *obj = NULL; + if (parser == NULL || parser->stack == NULL || parser->stack->obj == NULL) { + return NULL; + } + if (parser->stack->obj->type == UCL_ARRAY) { /* Object must be allocated */ obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority); - t = parser->stack->obj->value.av; - DL_APPEND (t, obj); + t = parser->stack->obj; + ucl_array_append (t, obj); parser->cur_obj = obj; - parser->stack->obj->value.av = t; - parser->stack->obj->len ++; } else { /* Object has been already allocated */ diff --git a/src/ucl_schema.c b/src/ucl_schema.c index faffe8613ce7..834b62accb5f 100644 --- a/src/ucl_schema.c +++ b/src/ucl_schema.c @@ -525,15 +525,16 @@ ucl_schema_validate_array (const ucl_object_t *schema, ucl_object_iter_t iter = NULL, piter = NULL; bool ret = true, allow_additional = true, need_unique = false; int64_t minmax; + unsigned int idx = 0; while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) { if (strcmp (ucl_object_key (elt), "items") == 0) { if (elt->type == UCL_ARRAY) { - found = obj->value.av; + found = ucl_array_head (obj); while (ret && (it = ucl_iterate_object (elt, &piter, true)) != NULL) { if (found) { ret = ucl_schema_validate (it, found, false, err, root); - found = found->next; + found = ucl_array_find_index (obj, ++idx); } } if (found != NULL) { @@ -608,14 +609,14 @@ ucl_schema_validate_array (const ucl_object_t *schema, ret = false; } else if (additional_schema != NULL) { - elt = first_unvalidated; + elt = ucl_array_find_index (obj, idx); while (elt) { if (!ucl_schema_validate (additional_schema, elt, false, err, root)) { ret = false; break; } - elt = elt->next; + elt = ucl_array_find_index (obj, idx ++); } } } @@ -741,7 +742,7 @@ ucl_schema_resolve_ref_component (const ucl_object_t *cur, "reference %s is invalid, invalid item number", refc); return NULL; } - res = cur->value.av; + res = ucl_array_head (cur); i = 0; while (res != NULL) { if (i == num) { diff --git a/src/ucl_util.c b/src/ucl_util.c index 41702e941f5b..41e012bf15bb 100644 --- a/src/ucl_util.c +++ b/src/ucl_util.c @@ -24,13 +24,21 @@ #include "ucl.h" #include "ucl_internal.h" #include "ucl_chartable.h" +#include "kvec.h" +#ifndef _WIN32 #include +#endif #ifdef HAVE_LIBGEN_H #include /* For dirname */ #endif +typedef kvec_t(ucl_object_t *) ucl_array_t; + +#define UCL_ARRAY_GET(ar, obj) ucl_array_t *ar = \ + (ucl_array_t *)((obj) != NULL ? (obj)->value.av : NULL) + #ifdef HAVE_OPENSSL #include #include @@ -68,6 +76,11 @@ #define MAP_FAILED ((void *) -1) #endif +#ifdef _WIN32 +#include +#define NBBY CHAR_BIT +#endif + static void *ucl_mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset) { void *map = NULL; @@ -195,15 +208,27 @@ ucl_object_dtor_unref (ucl_object_t *obj) static void ucl_object_free_internal (ucl_object_t *obj, bool allow_rec, ucl_object_dtor dtor) { - ucl_object_t *sub, *tmp; + ucl_object_t *tmp, *sub; while (obj != NULL) { if (obj->type == UCL_ARRAY) { - sub = obj->value.av; - while (sub != NULL) { - tmp = sub->next; - dtor (sub); - sub = tmp; + UCL_ARRAY_GET (vec, obj); + unsigned int i; + + if (vec != NULL) { + for (i = 0; i < vec->n; i ++) { + sub = kv_A (*vec, i); + if (sub != NULL) { + tmp = sub; + while (sub) { + tmp = sub->next; + dtor (sub); + sub = tmp; + } + } + } + kv_destroy (*vec); + UCL_FREE (sizeof (*vec), vec); } } else if (obj->type == UCL_OBJECT) { @@ -455,6 +480,15 @@ ucl_parser_get_error(struct ucl_parser *parser) return utstring_body(parser->err); } +UCL_EXTERN void +ucl_parser_clear_error(struct ucl_parser *parser) +{ + if (parser != NULL && parser->err != NULL) { + utstring_free(parser->err); + parser->err = NULL; + } +} + UCL_EXTERN bool ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len) { @@ -933,10 +967,10 @@ ucl_include_file (const unsigned char *data, size_t len, const unsigned char *p = data, *end = data + len; bool need_glob = false; int cnt = 0; - glob_t globbuf; char glob_pattern[PATH_MAX]; size_t i; +#ifndef _WIN32 if (!allow_glob) { return ucl_include_file_single (data, len, parser, check_signature, must_exist, priority); @@ -951,6 +985,7 @@ ucl_include_file (const unsigned char *data, size_t len, p ++; } if (need_glob) { + glob_t globbuf; memset (&globbuf, 0, sizeof (globbuf)); ucl_strlcpy (glob_pattern, (const char *)data, sizeof (glob_pattern)); if (glob (glob_pattern, 0, NULL, &globbuf) != 0) { @@ -978,7 +1013,13 @@ ucl_include_file (const unsigned char *data, size_t len, must_exist, priority); } } - +#else + /* Win32 compilers do not support globbing. Therefore, for Win32, + treat allow_glob/need_glob as a NOOP and just return */ + return ucl_include_file_single (data, len, parser, check_signature, + must_exist, priority); +#endif + return true; } @@ -1394,7 +1435,7 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt, } if (top->value.ov == NULL) { - top->value.ov = ucl_hash_create (); + top->value.ov = ucl_hash_create (false); } if (keylen == 0) { @@ -1427,7 +1468,7 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt, found = __DECONST (ucl_object_t *, ucl_hash_search_obj (top->value.ov, elt)); if (found == NULL) { - top->value.ov = ucl_hash_insert_object (top->value.ov, elt); + top->value.ov = ucl_hash_insert_object (top->value.ov, elt, false); top->len ++; if (replace) { ret = false; @@ -1444,7 +1485,7 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt, ucl_object_insert_key_common (elt, found, found->key, found->keylen, copy_key, false, false); ucl_hash_delete (top->value.ov, found); - top->value.ov = ucl_hash_insert_object (top->value.ov, elt); + top->value.ov = ucl_hash_insert_object (top->value.ov, elt, false); } else if (found->type == UCL_OBJECT && elt->type != UCL_OBJECT) { /* Insert new to old */ @@ -1568,7 +1609,7 @@ ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy) found = __DECONST(ucl_object_t *, ucl_hash_search (top->value.ov, cp->key, cp->keylen)); if (found == NULL) { /* The key does not exist */ - top->value.ov = ucl_hash_insert_object (top->value.ov, cp); + top->value.ov = ucl_hash_insert_object (top->value.ov, cp, false); top->len ++; } else { @@ -1610,7 +1651,7 @@ ucl_object_find_key (const ucl_object_t *obj, const char *key) const ucl_object_t* ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values) { - const ucl_object_t *elt; + const ucl_object_t *elt = NULL; if (obj == NULL || iter == NULL) { return NULL; @@ -1621,19 +1662,25 @@ ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expan case UCL_OBJECT: return (const ucl_object_t*)ucl_hash_iterate (obj->value.ov, iter); break; - case UCL_ARRAY: - elt = *iter; - if (elt == NULL) { - elt = obj->value.av; - if (elt == NULL) { - return NULL; + case UCL_ARRAY: { + unsigned int idx; + UCL_ARRAY_GET (vec, obj); + idx = (unsigned int)(uintptr_t)(*iter); + + if (vec != NULL) { + while (idx < kv_size (*vec)) { + if ((elt = kv_A (*vec, idx)) != NULL) { + idx ++; + break; + } + idx ++; } + *iter = (void *)(uintptr_t)idx; } - else if (elt == obj->value.av) { - return NULL; - } - *iter = elt->next ? elt->next : obj->value.av; + return elt; + break; + } default: /* Go to linear iteration */ break; @@ -1654,6 +1701,95 @@ ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expan return NULL; } +const char safe_iter_magic[4] = {'u', 'i', 't', 'e'}; +struct ucl_object_safe_iter { + char magic[4]; /* safety check */ + const ucl_object_t *impl_it; /* implicit object iteration */ + ucl_object_iter_t expl_it; /* explicit iteration */ +}; + +#define UCL_SAFE_ITER(ptr) (struct ucl_object_safe_iter *)(ptr) +#define UCL_SAFE_ITER_CHECK(it) do { \ + assert (it != NULL); \ + assert (memcmp (it->magic, safe_iter_magic, sizeof (it->magic)) == 0); \ + } while (0) + +ucl_object_iter_t +ucl_object_iterate_new (const ucl_object_t *obj) +{ + struct ucl_object_safe_iter *it; + + it = UCL_ALLOC (sizeof (*it)); + if (it != NULL) { + memcpy (it->magic, safe_iter_magic, sizeof (it->magic)); + it->expl_it = NULL; + it->impl_it = obj; + } + + return (ucl_object_iter_t)it; +} + + +ucl_object_iter_t +ucl_object_iterate_reset (ucl_object_iter_t it, const ucl_object_t *obj) +{ + struct ucl_object_safe_iter *rit = UCL_SAFE_ITER (it); + + UCL_SAFE_ITER_CHECK (rit); + + rit->impl_it = obj; + rit->expl_it = NULL; + + return it; +} + +const ucl_object_t* +ucl_object_iterate_safe (ucl_object_iter_t it, bool expand_values) +{ + struct ucl_object_safe_iter *rit = UCL_SAFE_ITER (it); + const ucl_object_t *ret = NULL; + + UCL_SAFE_ITER_CHECK (rit); + + if (rit->impl_it == NULL) { + return NULL; + } + + if (rit->impl_it->type == UCL_OBJECT || rit->impl_it->type == UCL_ARRAY) { + ret = ucl_iterate_object (rit->impl_it, &rit->expl_it, true); + + if (ret == NULL) { + /* Need to switch to another implicit object in chain */ + rit->impl_it = rit->impl_it->next; + rit->expl_it = NULL; + return ucl_object_iterate_safe (it, expand_values); + } + } + else { + /* Just iterate over the implicit array */ + ret = rit->impl_it; + rit->impl_it = rit->impl_it->next; + if (expand_values) { + /* We flatten objects if need to expand values */ + if (ret->type == UCL_OBJECT || ret->type == UCL_ARRAY) { + return ucl_object_iterate_safe (it, expand_values); + } + } + } + + return ret; +} + +void +ucl_object_iterate_free (ucl_object_iter_t it) +{ + struct ucl_object_safe_iter *rit = UCL_SAFE_ITER (it); + + UCL_SAFE_ITER_CHECK (rit); + + UCL_FREE (sizeof (*rit), it); +} + const ucl_object_t * ucl_lookup_path (const ucl_object_t *top, const char *path_in) { const ucl_object_t *o = NULL, *found; @@ -1733,6 +1869,17 @@ ucl_object_new_full (ucl_type_t type, unsigned priority) new->next = NULL; new->prev = new; ucl_object_set_priority (new, priority); + + if (type == UCL_ARRAY) { + new->value.av = UCL_ALLOC (sizeof (ucl_array_t)); + if (new->value.av) { + memset (new->value.av, 0, sizeof (ucl_array_t)); + UCL_ARRAY_GET (vec, new); + + /* Preallocate some space for arrays */ + kv_resize (ucl_object_t *, *vec, 8); + } + } } } else { @@ -1826,23 +1973,20 @@ ucl_object_frombool (bool bv) bool ucl_array_append (ucl_object_t *top, ucl_object_t *elt) { - ucl_object_t *head; + UCL_ARRAY_GET (vec, top); if (elt == NULL || top == NULL) { return false; } - head = top->value.av; - if (head == NULL) { - top->value.av = elt; - elt->prev = elt; + if (vec == NULL) { + vec = UCL_ALLOC (sizeof (*vec)); + kv_init (*vec); + top->value.av = (void *)vec; } - else { - elt->prev = head->prev; - head->prev->next = elt; - head->prev = elt; - } - elt->next = NULL; + + kv_push (ucl_object_t *, *vec, elt); + top->len ++; return true; @@ -1851,24 +1995,23 @@ ucl_array_append (ucl_object_t *top, ucl_object_t *elt) bool ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt) { - ucl_object_t *head; + UCL_ARRAY_GET (vec, top); if (elt == NULL || top == NULL) { return false; } - - head = top->value.av; - if (head == NULL) { - top->value.av = elt; - elt->prev = elt; + if (vec == NULL) { + vec = UCL_ALLOC (sizeof (*vec)); + kv_init (*vec); + top->value.av = (void *)vec; + kv_push (ucl_object_t *, *vec, elt); } else { - elt->prev = head->prev; - head->prev = elt; + /* Slow O(n) algorithm */ + kv_prepend (ucl_object_t *, *vec, elt); } - elt->next = head; - top->value.av = elt; + top->len ++; return true; @@ -1877,21 +2020,29 @@ ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt) bool ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy) { - ucl_object_t *cur, *tmp, *cp; + unsigned i; + ucl_object_t **obj; + UCL_ARRAY_GET (v1, top); + UCL_ARRAY_GET (v2, elt); if (elt == NULL || top == NULL || top->type != UCL_ARRAY || elt->type != UCL_ARRAY) { return false; } - DL_FOREACH_SAFE (elt->value.av, cur, tmp) { + kv_concat (ucl_object_t *, *v1, *v2); + + for (i = v2->n; i < v1->n; i ++) { + obj = &kv_A (*v1, i); + if (*obj == NULL) { + continue; + } + + top->len ++; if (copy) { - cp = ucl_object_copy (cur); + *obj = ucl_object_copy (*obj); } else { - cp = ucl_object_ref (cur); - } - if (cp != NULL) { - ucl_array_append (top, cp); + ucl_object_ref (*obj); } } @@ -1901,82 +2052,85 @@ ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy) ucl_object_t * ucl_array_delete (ucl_object_t *top, ucl_object_t *elt) { - ucl_object_t *head; + UCL_ARRAY_GET (vec, top); + ucl_object_t *ret = NULL; + unsigned i; - if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { - return NULL; - } - head = top->value.av; - - if (elt->prev == elt) { - top->value.av = NULL; - } - else if (elt == head) { - elt->next->prev = elt->prev; - top->value.av = elt->next; - } - else { - elt->prev->next = elt->next; - if (elt->next) { - elt->next->prev = elt->prev; - } - else { - head->prev = elt->prev; + for (i = 0; i < vec->n; i ++) { + if (kv_A (*vec, i) == elt) { + kv_del (ucl_object_t *, *vec, i); + ret = elt; + top->len --; + break; } } - elt->next = NULL; - elt->prev = elt; - top->len --; - return elt; + return ret; } const ucl_object_t * ucl_array_head (const ucl_object_t *top) { + UCL_ARRAY_GET (vec, top); + if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { return NULL; } - return top->value.av; + + return (vec->n > 0 ? vec->a[0] : NULL); } const ucl_object_t * ucl_array_tail (const ucl_object_t *top) { + UCL_ARRAY_GET (vec, top); + if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { return NULL; } - return top->value.av->prev; + + return (vec->n > 0 ? vec->a[vec->n - 1] : NULL); } ucl_object_t * ucl_array_pop_last (ucl_object_t *top) { - return ucl_array_delete (top, __DECONST(ucl_object_t *, ucl_array_tail (top))); + UCL_ARRAY_GET (vec, top); + ucl_object_t **obj, *ret = NULL; + + if (vec != NULL && vec->n > 0) { + obj = &kv_A (*vec, vec->n - 1); + ret = *obj; + kv_del (ucl_object_t *, *vec, vec->n - 1); + top->len --; + } + + return ret; } ucl_object_t * ucl_array_pop_first (ucl_object_t *top) { - return ucl_array_delete (top, __DECONST(ucl_object_t *, ucl_array_head (top))); + UCL_ARRAY_GET (vec, top); + ucl_object_t **obj, *ret = NULL; + + if (vec != NULL && vec->n > 0) { + obj = &kv_A (*vec, 0); + ret = *obj; + kv_del (ucl_object_t *, *vec, 0); + top->len --; + } + + return ret; } const ucl_object_t * ucl_array_find_index (const ucl_object_t *top, unsigned int index) { - ucl_object_iter_t it = NULL; - const ucl_object_t *ret; + UCL_ARRAY_GET (vec, top); - if (top == NULL || top->type != UCL_ARRAY || top->len == 0 || - (index + 1) > top->len) { - return NULL; - } - - while ((ret = ucl_iterate_object (top, &it, true)) != NULL) { - if (index == 0) { - return ret; - } - --index; + if (vec != NULL && vec->n > 0 && index < vec->n) { + return kv_A (*vec, index); } return NULL; @@ -1986,22 +2140,15 @@ ucl_object_t * ucl_array_replace_index (ucl_object_t *top, ucl_object_t *elt, unsigned int index) { - ucl_object_t *cur, *tmp; + UCL_ARRAY_GET (vec, top); + ucl_object_t *ret = NULL; - if (top == NULL || top->type != UCL_ARRAY || elt == NULL || - top->len == 0 || (index + 1) > top->len) { - return NULL; + if (vec != NULL && vec->n > 0 && index < vec->n) { + ret = kv_A (*vec, index); + kv_A (*vec, index) = elt; } - DL_FOREACH_SAFE (top->value.av, cur, tmp) { - if (index == 0) { - DL_REPLACE_ELEM (top->value.av, cur, elt); - return cur; - } - --index; - } - - return NULL; + return ret; } ucl_object_t * @@ -2314,7 +2461,7 @@ ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2) switch (o1->type) { case UCL_STRING: - if (o1->len == o2->len) { + if (o1->len == o2->len && o1->len > 0) { ret = strcmp (ucl_object_tostring(o1), ucl_object_tostring(o2)); } else { @@ -2330,17 +2477,28 @@ ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2) ret = ucl_object_toboolean (o1) - ucl_object_toboolean (o2); break; case UCL_ARRAY: - if (o1->len == o2->len) { - it1 = o1->value.av; - it2 = o2->value.av; + if (o1->len == o2->len && o1->len > 0) { + UCL_ARRAY_GET (vec1, o1); + UCL_ARRAY_GET (vec2, o2); + unsigned i; + /* Compare all elements in both arrays */ - while (it1 != NULL && it2 != NULL) { - ret = ucl_object_compare (it1, it2); - if (ret != 0) { - break; + for (i = 0; i < vec1->n; i ++) { + it1 = kv_A (*vec1, i); + it2 = kv_A (*vec2, i); + + if (it1 == NULL && it2 != NULL) { + return -1; + } + else if (it2 == NULL && it1 != NULL) { + return 1; + } + else if (it1 != NULL && it2 != NULL) { + ret = ucl_object_compare (it1, it2); + if (ret != 0) { + break; + } } - it1 = it1->next; - it2 = it2->next; } } else { @@ -2348,7 +2506,7 @@ ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2) } break; case UCL_OBJECT: - if (o1->len == o2->len) { + if (o1->len == o2->len && o1->len > 0) { while ((it1 = ucl_iterate_object (o1, &iter, true)) != NULL) { it2 = ucl_object_find_key (o2, ucl_object_key (it1)); if (it2 == NULL) { @@ -2377,11 +2535,14 @@ void ucl_object_array_sort (ucl_object_t *ar, int (*cmp)(const ucl_object_t *o1, const ucl_object_t *o2)) { + UCL_ARRAY_GET (vec, ar); + if (cmp == NULL || ar == NULL || ar->type != UCL_ARRAY) { return; } - DL_SORT (ar->value.av, cmp); + qsort (vec->a, vec->n, sizeof (ucl_object_t *), + (int (*)(const void *, const void *))cmp); } #define PRIOBITS 4 diff --git a/tests/basic/14.in b/tests/basic/14.in new file mode 100644 index 000000000000..62d2e6059296 --- /dev/null +++ b/tests/basic/14.in @@ -0,0 +1,8 @@ +# Bad comments case + +section { +# key = value; +} +.include(try=true) "./1.in" + +key = value; diff --git a/tests/basic/14.res b/tests/basic/14.res new file mode 100644 index 000000000000..f803349a6000 --- /dev/null +++ b/tests/basic/14.res @@ -0,0 +1,4 @@ +section { +} +key = "value"; + diff --git a/tests/schema.test b/tests/schema.test index 0f2c252945c7..fd26804fa295 100755 --- a/tests/schema.test +++ b/tests/schema.test @@ -5,5 +5,5 @@ rm /tmp/_ucl_test_schema.out ||true for i in ${TEST_DIR}/schema/*.json ; do _name=`basename $i` printf "running schema test suite $_name... " - $PROG >> /tmp/_ucl_test_schema.out < $i && ( echo "OK" ) || ( echo "Fail" ) + $PROG >> /tmp/_ucl_test_schema.out < $i && ( echo "OK" ) || ( echo "Fail" ; exit 1 ) done diff --git a/tests/test_generate.c b/tests/test_generate.c index aad92b51fb0e..f09eabb839fe 100644 --- a/tests/test_generate.c +++ b/tests/test_generate.c @@ -30,7 +30,8 @@ int main (int argc, char **argv) { ucl_object_t *obj, *cur, *ar, *ref; - const ucl_object_t *found; + ucl_object_iter_t it; + const ucl_object_t *found, *it_obj; FILE *out; unsigned char *emitted; const char *fname_out = NULL; @@ -59,7 +60,7 @@ main (int argc, char **argv) cur = ucl_object_fromstring_common ("value1", 0, UCL_STRING_TRIM); ucl_object_insert_key (obj, cur, "key0", 0, false); cur = ucl_object_fromdouble (0.1); - ucl_object_replace_key (obj, cur, "key0", 0, false); + assert (ucl_object_replace_key (obj, cur, "key0", 0, false)); /* Create some strings */ cur = ucl_object_fromstring_common (" test string ", 0, UCL_STRING_TRIM); @@ -139,6 +140,33 @@ main (int argc, char **argv) found = ucl_lookup_path (obj, "key9..key1"); assert (found == NULL); + /* Test iteration */ + it = ucl_object_iterate_new (obj); + it_obj = ucl_object_iterate_safe (it, true); + /* key0 = 0.1 */ + assert (ucl_object_type (it_obj) == UCL_FLOAT); + it_obj = ucl_object_iterate_safe (it, true); + /* key1 = "" */ + assert (ucl_object_type (it_obj) == UCL_STRING); + it_obj = ucl_object_iterate_safe (it, true); + /* key2 = "" */ + assert (ucl_object_type (it_obj) == UCL_STRING); + it_obj = ucl_object_iterate_safe (it, true); + /* key3 = "" */ + assert (ucl_object_type (it_obj) == UCL_STRING); + it_obj = ucl_object_iterate_safe (it, true); + /* key4 = ([float, int, float], boolean) */ + ucl_object_iterate_reset (it, it_obj); + it_obj = ucl_object_iterate_safe (it, true); + assert (ucl_object_type (it_obj) == UCL_FLOAT); + it_obj = ucl_object_iterate_safe (it, true); + assert (ucl_object_type (it_obj) == UCL_INT); + it_obj = ucl_object_iterate_safe (it, true); + assert (ucl_object_type (it_obj) == UCL_FLOAT); + it_obj = ucl_object_iterate_safe (it, true); + assert (ucl_object_type (it_obj) == UCL_BOOLEAN); + ucl_object_iterate_free (it); + emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG); fprintf (out, "%s\n", emitted); diff --git a/tests/test_schema.c b/tests/test_schema.c index 7acebb84b934..4f075dae8d68 100644 --- a/tests/test_schema.c +++ b/tests/test_schema.c @@ -79,6 +79,8 @@ perform_test (const ucl_object_t *schema, const ucl_object_t *obj, ucl_object_tostring (description), ucl_object_toboolean (valid) ? "valid" : "invalid", err->msg); + fprintf (stdout, "%s\n", ucl_object_emit (data, UCL_EMIT_CONFIG)); + fprintf (stdout, "%s\n", ucl_object_emit (schema, UCL_EMIT_CONFIG)); return false; } diff --git a/uthash/utstring.h b/uthash/utstring.h index f11f34b77e75..b4399484c70e 100644 --- a/uthash/utstring.h +++ b/uthash/utstring.h @@ -39,7 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #ifndef oom -#define oom() exit(-1) +#define oom abort #endif typedef struct { @@ -54,8 +54,8 @@ do { \ if (((s)->n - (s)->i) < (size_t)(amt)) { \ (s)->d = (char*)realloc((s)->d, (s)->n + amt); \ if ((s)->d == NULL) oom(); \ - (s)->n += amt; \ - if ((s)->pd) *((s)->pd) = (s)->d; \ + else {(s)->n += amt; \ + if ((s)->pd) *((s)->pd) = (s)->d;} \ } \ } while(0) @@ -82,7 +82,7 @@ do { \ do { \ s = (UT_string*)calloc(1, sizeof(UT_string)); \ if (!s) oom(); \ - utstring_init(s); \ + else utstring_init(s); \ } while(0) #define utstring_renew(s) \ From 15b8b407ee0ee485e82a9de35932da2053f2c390 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Mon, 2 Mar 2015 21:36:45 +0000 Subject: [PATCH 2/2] Really update to 8d3b186 --- utils/objdump.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/utils/objdump.c b/utils/objdump.c index d3f7cd1bcec6..74581baafddb 100644 --- a/utils/objdump.c +++ b/utils/objdump.c @@ -46,7 +46,7 @@ ucl_obj_dump (const ucl_object_t *obj, unsigned int shift) if (obj->key != NULL) { printf ("%skey: \"%s\"\n", pre, ucl_object_key (obj)); } - printf ("%sref: %hd\n", pre, obj->ref); + printf ("%sref: %u\n", pre, obj->ref); printf ("%slen: %u\n", pre, obj->len); printf ("%sprev: %p\n", pre, obj->prev); printf ("%snext: %p\n", pre, obj->next); @@ -61,7 +61,10 @@ ucl_obj_dump (const ucl_object_t *obj, unsigned int shift) else if (obj->type == UCL_ARRAY) { printf ("%stype: UCL_ARRAY\n", pre); printf ("%svalue: %p\n", pre, obj->value.av); - ucl_obj_dump (obj->value.av, shift + 2); + it_obj = NULL; + while ((cur = ucl_iterate_object (obj, &it_obj, true))) { + ucl_obj_dump (cur, shift + 2); + } } else if (obj->type == UCL_INT) { printf ("%stype: UCL_INT\n", pre); @@ -96,7 +99,7 @@ int main(int argc, char **argv) { const char *fn = NULL; - char inbuf[8192]; + unsigned char inbuf[8192]; struct ucl_parser *parser; int k, ret = 0, r = 0; ucl_object_t *obj = NULL;