Update libucl to git version 8d3b186
This commit is contained in:
parent
1eea90048f
commit
513f8791d8
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/vendor/libucl/dist/; revision=279546
12
ChangeLog.md
12
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
|
||||
|
@ -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
|
||||
|
16
README.md
16
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
|
||||
....
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -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
|
||||
|
@ -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])
|
||||
|
@ -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
|
58
doc/api.md
58
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:
|
||||
|
76
doc/libucl.3
76
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
|
||||
|
@ -1,6 +1,6 @@
|
||||
% LIBUCL(3) Libucl manual
|
||||
% Vsevolod Stakhov <vsevolod@highsecure.ru>
|
||||
% July 26, 2014
|
||||
% %%date%%
|
||||
|
||||
# Name
|
||||
|
||||
|
@ -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
|
||||
|
627
klib/khash.h
Normal file
627
klib/khash.h
Normal file
@ -0,0 +1,627 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
|
||||
|
||||
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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* 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 */
|
103
klib/kvec.h
Normal file
103
klib/kvec.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk>
|
||||
|
||||
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 <stdlib.h>
|
||||
|
||||
#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
|
4
m4/.gitignore
vendored
4
m4/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
606
m4/ax_lua.m4
606
m4/ax_lua.m4
@ -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 <rrt@sc3d.org>
|
||||
# Copyright (c) 2013 Tim Perkins <tprk77@gmail.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# 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 <lua.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
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])
|
||||
])
|
@ -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 \
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
312
src/ucl_hash.c
312
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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) {
|
||||
|
393
src/ucl_util.c
393
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 <glob.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBGEN_H
|
||||
#include <libgen.h> /* 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 <openssl/err.h>
|
||||
#include <openssl/sha.h>
|
||||
@ -68,6 +76,11 @@
|
||||
#define MAP_FAILED ((void *) -1)
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <limits.h>
|
||||
#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
|
||||
|
8
tests/basic/14.in
Normal file
8
tests/basic/14.in
Normal file
@ -0,0 +1,8 @@
|
||||
# Bad comments case
|
||||
|
||||
section {
|
||||
# key = value;
|
||||
}
|
||||
.include(try=true) "./1.in"
|
||||
|
||||
key = value;
|
4
tests/basic/14.res
Normal file
4
tests/basic/14.res
Normal file
@ -0,0 +1,4 @@
|
||||
section {
|
||||
}
|
||||
key = "value";
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <stdarg.h>
|
||||
|
||||
#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) \
|
||||
|
Loading…
Reference in New Issue
Block a user