From d212dc2bf89ec79a85fed3e368d171edd77aa7be Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Sun, 14 Feb 2016 13:34:42 +0000 Subject: [PATCH] Backport fixes from libucl --- contrib/libucl/lua_ucl.c | 368 +++++++++++++++++++++++++++++++++-- contrib/libucl/ucl.h | 17 +- contrib/libucl/ucl_msgpack.c | 34 ++-- contrib/libucl/ucl_parser.c | 2 +- contrib/libucl/ucl_util.c | 64 ++++-- 5 files changed, 443 insertions(+), 42 deletions(-) diff --git a/contrib/libucl/lua_ucl.c b/contrib/libucl/lua_ucl.c index 1cb716b24..3c42adbfe 100644 --- a/contrib/libucl/lua_ucl.c +++ b/contrib/libucl/lua_ucl.c @@ -29,6 +29,7 @@ #include "ucl_internal.h" #include "lua_ucl.h" #include +#include /*** * @module ucl @@ -69,6 +70,7 @@ func = "huh"; #define PARSER_META "ucl.parser.meta" #define EMITTER_META "ucl.emitter.meta" #define NULL_META "null.emitter.meta" +#define OBJECT_META "ucl.object.meta" static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj); static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, bool allow_array); @@ -420,9 +422,7 @@ ucl_object_lua_fromelt (lua_State *L, int idx) fd->idx = luaL_ref (L, LUA_REGISTRYINDEX); obj = ucl_object_new_userdata (lua_ucl_userdata_dtor, - lua_ucl_userdata_emitter); - obj->type = UCL_USERDATA; - obj->value.ud = (void *)fd; + lua_ucl_userdata_emitter, (void *)fd); } } } @@ -460,6 +460,24 @@ ucl_object_lua_import (lua_State *L, int idx) return obj; } +static int +lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type) +{ + unsigned char *result; + + result = ucl_object_emit (obj, type); + + if (result != NULL) { + lua_pushstring (L, (const char *)result); + free (result); + } + else { + lua_pushnil (L); + } + + return 1; +} + static int lua_ucl_parser_init (lua_State *L) { @@ -489,6 +507,23 @@ lua_ucl_parser_get (lua_State *L, int index) return *((struct ucl_parser **) luaL_checkudata(L, index, PARSER_META)); } +static ucl_object_t * +lua_ucl_object_get (lua_State *L, int index) +{ + return *((ucl_object_t **) luaL_checkudata(L, index, OBJECT_META)); +} + +static void +lua_ucl_push_opaque (lua_State *L, ucl_object_t *obj) +{ + ucl_object_t **pobj; + + pobj = lua_newuserdata (L, sizeof (*pobj)); + *pobj = obj; + luaL_getmetatable (L, OBJECT_META); + lua_setmetatable (L, -2); +} + /*** * @method parser:parse_file(name) * Parse UCL object from file. @@ -594,6 +629,105 @@ lua_ucl_parser_get_object (lua_State *L) return ret; } +/*** + * @method parser:get_object_wrapped() + * Get top object from parser and export it to userdata object without + * unwrapping to lua. + * @return {ucl.object or nil} ucl object wrapped variable + */ +static int +lua_ucl_parser_get_object_wrapped (lua_State *L) +{ + struct ucl_parser *parser; + ucl_object_t *obj; + int ret = 1; + + parser = lua_ucl_parser_get (L, 1); + obj = ucl_parser_get_object (parser); + + if (obj != NULL) { + lua_ucl_push_opaque (L, obj); + } + else { + lua_pushnil (L); + } + + return ret; +} + +/*** + * @method parser:validate(schema) + * Validates the top object in the parser against schema. Schema might be + * another object or a string that represents file to load schema from. + * + * @param {string/table} schema input schema + * @return {result,err} two values: boolean result and the corresponding error + * + */ +static int +lua_ucl_parser_validate (lua_State *L) +{ + struct ucl_parser *parser, *schema_parser; + ucl_object_t *schema; + const char *schema_file; + struct ucl_schema_error err; + + parser = lua_ucl_parser_get (L, 1); + + if (parser && parser->top_obj) { + if (lua_type (L, 2) == LUA_TTABLE) { + schema = ucl_object_lua_import (L, 2); + + if (schema == NULL) { + lua_pushboolean (L, false); + lua_pushstring (L, "cannot load schema from lua table"); + + return 2; + } + } + else if (lua_type (L, 2) == LUA_TSTRING) { + schema_parser = ucl_parser_new (0); + schema_file = luaL_checkstring (L, 2); + + if (!ucl_parser_add_file (schema_parser, schema_file)) { + lua_pushboolean (L, false); + lua_pushfstring (L, "cannot parse schema file \"%s\": " + "%s", schema_file, ucl_parser_get_error (parser)); + ucl_parser_free (schema_parser); + + return 2; + } + + schema = ucl_parser_get_object (schema_parser); + ucl_parser_free (schema_parser); + } + else { + lua_pushboolean (L, false); + lua_pushstring (L, "invalid schema argument"); + + return 2; + } + + if (!ucl_object_validate (schema, parser->top_obj, &err)) { + lua_pushboolean (L, false); + lua_pushfstring (L, "validation error: " + "%s", err.msg); + } + else { + lua_pushboolean (L, true); + lua_pushnil (L); + } + + ucl_object_unref (schema); + } + else { + lua_pushboolean (L, false); + lua_pushstring (L, "invalid parser or empty top object"); + } + + return 2; +} + static int lua_ucl_parser_gc (lua_State *L) { @@ -605,6 +739,189 @@ lua_ucl_parser_gc (lua_State *L) return 0; } +/*** + * @method object:unwrap() + * Unwraps opaque ucl object to the native lua object (performing copying) + * @return {variant} any lua object + */ +static int +lua_ucl_object_unwrap (lua_State *L) +{ + ucl_object_t *obj; + + obj = lua_ucl_object_get (L, 1); + + if (obj) { + ucl_object_push_lua (L, obj, true); + } + else { + lua_pushnil (L); + } + + return 1; +} + +/*** + * @method object:tostring(type) + * Unwraps opaque ucl object to string (json by default). Optionally you can + * specify output format: + * + * - `json` - fine printed json + * - `json-compact` - compacted json + * - `config` - fine printed configuration + * - `ucl` - same as `config` + * - `yaml` - embedded yaml + * @param {string} type optional + * @return {string} string representation of the opaque ucl object + */ +static int +lua_ucl_object_tostring (lua_State *L) +{ + ucl_object_t *obj; + enum ucl_emitter format = UCL_EMIT_JSON_COMPACT; + + obj = lua_ucl_object_get (L, 1); + + if (obj) { + if (lua_gettop (L) > 1) { + if (lua_type (L, 2) == LUA_TSTRING) { + const char *strtype = lua_tostring (L, 2); + + if (strcasecmp (strtype, "json") == 0) { + format = UCL_EMIT_JSON; + } + else if (strcasecmp (strtype, "json-compact") == 0) { + format = UCL_EMIT_JSON_COMPACT; + } + else if (strcasecmp (strtype, "yaml") == 0) { + format = UCL_EMIT_YAML; + } + else if (strcasecmp (strtype, "config") == 0 || + strcasecmp (strtype, "ucl") == 0) { + format = UCL_EMIT_CONFIG; + } + } + } + + return lua_ucl_to_string (L, obj, format); + } + else { + lua_pushnil (L); + } + + return 1; +} + +/*** + * @method object:validate(schema[, path[, ext_refs]]) + * Validates the given ucl object using schema object represented as another + * opaque ucl object. You can also specify path in the form `#/path/def` to + * specify the specific schema element to perform validation. + * + * @param {ucl.object} schema schema object + * @param {string} path optional path for validation procedure + * @return {result,err} two values: boolean result and the corresponding + * error, if `ext_refs` are also specified, then they are returned as opaque + * ucl object as {result,err,ext_refs} + */ +static int +lua_ucl_object_validate (lua_State *L) +{ + ucl_object_t *obj, *schema, *ext_refs = NULL; + const ucl_object_t *schema_elt; + bool res = false; + struct ucl_schema_error err; + const char *path = NULL; + + obj = lua_ucl_object_get (L, 1); + schema = lua_ucl_object_get (L, 2); + + if (schema && obj && ucl_object_type (schema) == UCL_OBJECT) { + if (lua_gettop (L) > 2) { + if (lua_type (L, 3) == LUA_TSTRING) { + path = lua_tostring (L, 3); + if (path[0] == '#') { + path++; + } + } + else if (lua_type (L, 3) == LUA_TUSERDATA || lua_type (L, 3) == + LUA_TTABLE) { + /* External refs */ + ext_refs = lua_ucl_object_get (L, 3); + } + + if (lua_gettop (L) > 3) { + if (lua_type (L, 4) == LUA_TUSERDATA || lua_type (L, 4) == + LUA_TTABLE) { + /* External refs */ + ext_refs = lua_ucl_object_get (L, 4); + } + } + } + + if (path) { + schema_elt = ucl_lookup_path_char (schema, path, '/'); + } + else { + /* Use the top object */ + schema_elt = schema; + } + + if (schema_elt) { + res = ucl_object_validate_root_ext (schema_elt, obj, schema, + ext_refs, &err); + + if (res) { + lua_pushboolean (L, res); + lua_pushnil (L); + + if (ext_refs) { + lua_ucl_push_opaque (L, ext_refs); + } + } + else { + lua_pushboolean (L, res); + lua_pushfstring (L, "validation error: %s", err.msg); + + if (ext_refs) { + lua_ucl_push_opaque (L, ext_refs); + } + } + } + else { + lua_pushboolean (L, res); + + lua_pushfstring (L, "cannot find the requested path: %s", path); + + if (ext_refs) { + lua_ucl_push_opaque (L, ext_refs); + } + } + } + else { + lua_pushboolean (L, res); + lua_pushstring (L, "invalid object or schema"); + } + + if (ext_refs) { + return 3; + } + + return 2; +} + +static int +lua_ucl_object_gc (lua_State *L) +{ + ucl_object_t *obj; + + obj = lua_ucl_object_get (L, 1); + + ucl_object_unref (obj); + + return 0; +} + static void lua_ucl_parser_mt (lua_State *L) { @@ -625,25 +942,45 @@ lua_ucl_parser_mt (lua_State *L) lua_pushcfunction (L, lua_ucl_parser_get_object); lua_setfield (L, -2, "get_object"); + lua_pushcfunction (L, lua_ucl_parser_get_object_wrapped); + lua_setfield (L, -2, "get_object_wrapped"); + + lua_pushcfunction (L, lua_ucl_parser_validate); + lua_setfield (L, -2, "validate"); + lua_pop (L, 1); } -static int -lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type) +static void +lua_ucl_object_mt (lua_State *L) { - unsigned char *result; + luaL_newmetatable (L, OBJECT_META); - result = ucl_object_emit (obj, type); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); - if (result != NULL) { - lua_pushstring (L, (const char *)result); - free (result); - } - else { - lua_pushnil (L); - } + lua_pushcfunction (L, lua_ucl_object_gc); + lua_setfield (L, -2, "__gc"); - return 1; + lua_pushcfunction (L, lua_ucl_object_tostring); + lua_setfield (L, -2, "__tostring"); + + lua_pushcfunction (L, lua_ucl_object_tostring); + lua_setfield (L, -2, "tostring"); + + lua_pushcfunction (L, lua_ucl_object_unwrap); + lua_setfield (L, -2, "unwrap"); + + lua_pushcfunction (L, lua_ucl_object_unwrap); + lua_setfield (L, -2, "tolua"); + + lua_pushcfunction (L, lua_ucl_object_validate); + lua_setfield (L, -2, "validate"); + + lua_pushstring (L, OBJECT_META); + lua_setfield (L, -2, "class"); + + lua_pop (L, 1); } static int @@ -789,6 +1126,7 @@ luaopen_ucl (lua_State *L) { lua_ucl_parser_mt (L); lua_ucl_null_mt (L); + lua_ucl_object_mt (L); /* Create the refs weak table: */ lua_createtable (L, 0, 2); diff --git a/contrib/libucl/ucl.h b/contrib/libucl/ucl.h index 14e72d119..ba18380a9 100644 --- a/contrib/libucl/ucl.h +++ b/contrib/libucl/ucl.h @@ -288,10 +288,12 @@ UCL_EXTERN ucl_object_t* ucl_object_new_full (ucl_type_t type, unsigned priority /** * Create new object with userdata dtor * @param dtor destructor function + * @param emitter emitter for userdata + * @param ptr opaque pointer * @return new object */ UCL_EXTERN ucl_object_t* ucl_object_new_userdata (ucl_userdata_dtor dtor, - ucl_userdata_emitter emitter) UCL_WARN_UNUSED_RESULT; + ucl_userdata_emitter emitter, void *ptr) UCL_WARN_UNUSED_RESULT; /** * Perform deep copy of an object copying everything @@ -751,6 +753,19 @@ UCL_EXTERN void ucl_object_unref (ucl_object_t *obj); UCL_EXTERN int ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2); +/** + * Compare objects `o1` and `o2` useful for sorting + * @param o1 the first object + * @param o2 the second object + * @return values >0, 0 and <0 if `o1` is more than, equal and less than `o2`. + * The order of comparison: + * 1) Type of objects + * 2) Size of objects + * 3) Content of objects + */ +UCL_EXTERN int ucl_object_compare_qsort (const ucl_object_t **o1, + const ucl_object_t **o2); + /** * Sort UCL array using `cmp` compare function * @param ar diff --git a/contrib/libucl/ucl_msgpack.c b/contrib/libucl/ucl_msgpack.c index 5620a7b11..96f4809f8 100644 --- a/contrib/libucl/ucl_msgpack.c +++ b/contrib/libucl/ucl_msgpack.c @@ -113,19 +113,19 @@ ucl_emitter_print_int_msgpack (struct ucl_emitter_context *ctx, int64_t val) len = 1; buf[0] = mask_positive & val; } - else if (val <= 0xff) { + else if (val <= UINT8_MAX) { len = 2; buf[0] = uint8_ch; buf[1] = val & 0xff; } - else if (val <= 0xffff) { + else if (val <= UINT16_MAX) { uint16_t v = TO_BE16 (val); len = 3; buf[0] = uint16_ch; memcpy (&buf[1], &v, sizeof (v)); } - else if (val <= 0xffffffff) { + else if (val <= UINT32_MAX) { uint32_t v = TO_BE32 (val); len = 5; @@ -149,19 +149,20 @@ ucl_emitter_print_int_msgpack (struct ucl_emitter_context *ctx, int64_t val) len = 1; buf[0] = (mask_negative | uval) & 0xff; } - else if (uval <= 0xff) { + else if (uval <= INT8_MAX) { + uint8_t v = (uint8_t)val; len = 2; buf[0] = int8_ch; - buf[1] = (unsigned char)val; + buf[1] = v; } - else if (uval <= 0xffff) { + else if (uval <= INT16_MAX) { uint16_t v = TO_BE16 (val); len = 3; buf[0] = int16_ch; memcpy (&buf[1], &v, sizeof (v)); } - else if (uval <= 0xffffffff) { + else if (uval <= INT32_MAX) { uint32_t v = TO_BE32 (val); len = 5; @@ -1418,6 +1419,10 @@ ucl_msgpack_parse_int (struct ucl_parser *parser, const unsigned char *pos, size_t remain) { ucl_object_t *obj; + int8_t iv8; + int16_t iv16; + int32_t iv32; + int64_t iv64; if (len > remain) { return -1; @@ -1439,11 +1444,14 @@ ucl_msgpack_parse_int (struct ucl_parser *parser, len = 1; break; case msgpack_int8: - obj->value.iv = (signed char)*pos; + memcpy (&iv8, pos, sizeof (iv8)); + obj->value.iv = iv8; len = 1; break; case msgpack_int16: - obj->value.iv = FROM_BE16 (*(int16_t *)pos); + memcpy (&iv16, pos, sizeof (iv16)); + iv16 = FROM_BE16 (iv16); + obj->value.iv = iv16; len = 2; break; case msgpack_uint16: @@ -1451,7 +1459,9 @@ ucl_msgpack_parse_int (struct ucl_parser *parser, len = 2; break; case msgpack_int32: - obj->value.iv = FROM_BE32 (*(int32_t *)pos); + memcpy (&iv32, pos, sizeof (iv32)); + iv32 = FROM_BE32 (iv32); + obj->value.iv = iv32; len = 4; break; case msgpack_uint32: @@ -1459,7 +1469,9 @@ ucl_msgpack_parse_int (struct ucl_parser *parser, len = 4; break; case msgpack_int64: - obj->value.iv = FROM_BE64 (*(int64_t *)pos); + memcpy (&iv64, pos, sizeof (iv64)); + iv64 = FROM_BE64 (iv64); + obj->value.iv = iv64; len = 8; break; case msgpack_uint64: diff --git a/contrib/libucl/ucl_parser.c b/contrib/libucl/ucl_parser.c index fa906b7fe..9ac535cbe 100644 --- a/contrib/libucl/ucl_parser.c +++ b/contrib/libucl/ucl_parser.c @@ -2198,7 +2198,7 @@ ucl_state_machine (struct ucl_parser *parser) while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { ucl_chunk_skipc (chunk, p); } - if (*p == '}') { + if (p == chunk->end || *p == '}') { /* We have the end of an object */ parser->state = UCL_STATE_AFTER_VALUE; continue; diff --git a/contrib/libucl/ucl_util.c b/contrib/libucl/ucl_util.c index 15439feea..7626e0996 100644 --- a/contrib/libucl/ucl_util.c +++ b/contrib/libucl/ucl_util.c @@ -27,6 +27,7 @@ #include "ucl_chartable.h" #include "kvec.h" #include +#include /* for asprintf */ #ifndef _WIN32 #include @@ -306,6 +307,9 @@ ucl_unescape_json_string (char *str, size_t len) case 'u': /* Unicode escape */ uval = 0; + h ++; /* u character */ + len --; + if (len > 3) { for (i = 0; i < 4; i++) { uval <<= 4; @@ -322,8 +326,7 @@ ucl_unescape_json_string (char *str, size_t len) break; } } - h += 3; - len -= 3; + /* Encode */ if(uval < 0x80) { t[0] = (char)uval; @@ -340,6 +343,8 @@ ucl_unescape_json_string (char *str, size_t len) t[2] = 0x80 + ((uval & 0x003F)); t += 3; } +#if 0 + /* It's not actually supported now */ else if(uval <= 0x10FFFF) { t[0] = 0xF0 + ((uval & 0x1C0000) >> 18); t[1] = 0x80 + ((uval & 0x03F000) >> 12); @@ -347,9 +352,19 @@ ucl_unescape_json_string (char *str, size_t len) t[3] = 0x80 + ((uval & 0x00003F)); t += 4; } +#endif else { *t++ = '?'; } + + /* Consume 4 characters of source */ + h += 4; + len -= 4; + + if (len > 0) { + len --; /* for '\' character */ + } + continue; } else { *t++ = 'u'; @@ -1550,7 +1565,7 @@ ucl_load_handler (const unsigned char *data, size_t len, size_t buflen; unsigned priority; int64_t iv; - ucl_hash_t *container = NULL; + ucl_object_t *container = NULL; enum ucl_string_flags flags; /* Default values */ @@ -1610,19 +1625,32 @@ ucl_load_handler (const unsigned char *data, size_t len, } } - if (prefix == NULL || strlen(prefix) == 0) { + if (prefix == NULL || strlen (prefix) == 0) { ucl_create_err (&parser->err, "No Key specified in load macro"); return false; } if (len > 0) { asprintf (&load_file, "%.*s", (int)len, data); - if (!ucl_fetch_file (load_file, &buf, &buflen, &parser->err, !try_load)) { + + if (!load_file) { + ucl_create_err (&parser->err, "cannot allocate memory for suffix"); + + return false; + } + + if (!ucl_fetch_file (load_file, &buf, &buflen, &parser->err, + !try_load)) { + free (load_file); + return (try_load || false); } - container = parser->stack->obj->value.ov; - old_obj = __DECONST (ucl_object_t *, ucl_hash_search (container, prefix, strlen (prefix))); + free (load_file); + container = parser->stack->obj; + old_obj = __DECONST (ucl_object_t *, ucl_object_find_key (container, + prefix)); + if (old_obj != NULL) { ucl_create_err (&parser->err, "Key %s already exists", prefix); if (buflen > 0) { @@ -1642,7 +1670,7 @@ ucl_load_handler (const unsigned char *data, size_t len, else if (strcasecmp (target, "int") == 0) { asprintf(&tmp, "%.*s", (int)buflen, buf); iv = strtoll(tmp, NULL, 10); - obj = ucl_object_fromint(iv); + obj = ucl_object_fromint (iv); } if (buflen > 0) { @@ -1652,13 +1680,11 @@ ucl_load_handler (const unsigned char *data, size_t len, if (obj != NULL) { obj->key = prefix; obj->keylen = strlen (prefix); - ucl_copy_key_trash(obj); + ucl_copy_key_trash (obj); obj->prev = obj; obj->next = NULL; ucl_object_set_priority (obj, priority); - container = ucl_hash_insert_object (container, obj, - parser->flags & UCL_PARSER_KEY_LOWERCASE); - parser->stack->obj->value.ov = container; + ucl_object_insert_key (container, obj, obj->key, obj->keylen, false); } return true; @@ -2576,7 +2602,7 @@ ucl_object_new_full (ucl_type_t type, unsigned priority) } } else { - new = ucl_object_new_userdata (NULL, NULL); + new = ucl_object_new_userdata (NULL, NULL, NULL); ucl_object_set_priority (new, priority); } @@ -2584,7 +2610,9 @@ ucl_object_new_full (ucl_type_t type, unsigned priority) } ucl_object_t* -ucl_object_new_userdata (ucl_userdata_dtor dtor, ucl_userdata_emitter emitter) +ucl_object_new_userdata (ucl_userdata_dtor dtor, + ucl_userdata_emitter emitter, + void *ptr) { struct ucl_object_userdata *new; size_t nsize = sizeof (*new); @@ -2598,6 +2626,7 @@ ucl_object_new_userdata (ucl_userdata_dtor dtor, ucl_userdata_emitter emitter) new->obj.prev = (ucl_object_t *)new; new->dtor = dtor; new->emitter = emitter; + new->obj.value.ud = ptr; } return (ucl_object_t *)new; @@ -3266,6 +3295,13 @@ ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2) return ret; } +int +ucl_object_compare_qsort (const ucl_object_t **o1, + const ucl_object_t **o2) +{ + return ucl_object_compare (*o1, *o2); +} + void ucl_object_array_sort (ucl_object_t *ar, int (*cmp)(const ucl_object_t **o1, const ucl_object_t **o2)) -- 2.39.5