diff --git a/Makefile b/Makefile index bb77def..64fded2 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ LD_UCL_FLAGS ?= -L$(OBJDIR) -Wl,-rpath,$(OBJDIR) -lucl LD_ADD ?= -lrt COPT_FLAGS ?= -g -O0 HDEPS = $(SRCDIR)/ucl_hash.h $(SRCDIR)/ucl_chartable.h $(SRCDIR)/ucl_internal.h $(INCLUDEDIR)/ucl.h $(SRCDIR)/xxhash.h -OBJECTS = $(OBJDIR)/ucl_hash.o $(OBJDIR)/ucl_util.o $(OBJDIR)/ucl_parser.o $(OBJDIR)/ucl_emitter.o $(OBJDIR)/xxhash.o +OBJECTS = $(OBJDIR)/ucl_hash.o $(OBJDIR)/ucl_util.o $(OBJDIR)/ucl_parser.o $(OBJDIR)/ucl_emitter.o $(OBJDIR)/xxhash.o $(OBJDIR)/ucl_ubj.o all: $(OBJDIR) $(OBJDIR)/$(SONAME) @@ -44,6 +44,8 @@ $(OBJDIR)/ucl_emitter.o: $(SRCDIR)/ucl_emitter.c $(HDEPS) $(CC) -o $(OBJDIR)/ucl_emitter.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_emitter.c $(OBJDIR)/ucl_hash.o: $(SRCDIR)/ucl_hash.c $(HDEPS) $(CC) -o $(OBJDIR)/ucl_hash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_hash.c +$(OBJDIR)/ucl_ubj.o: $(SRCDIR)/ucl_ubj.c $(HDEPS) + $(CC) -o $(OBJDIR)/ucl_ubj.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_ubj.c $(OBJDIR)/xxhash.o: $(SRCDIR)/xxhash.c $(HDEPS) $(CC) -o $(OBJDIR)/xxhash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/xxhash.c diff --git a/include/ucl.h b/include/ucl.h index 50711d9..02170a2 100644 --- a/include/ucl.h +++ b/include/ucl.h @@ -794,6 +794,25 @@ bool ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data, bool ucl_parser_add_file (struct ucl_parser *parser, const char *filename); /** + * Load new chunk to a parser + * @param parser parser structure + * @param data the pointer to the beginning of a chunk + * @param len the length of a chunk + * @param err if *err is NULL it is set to parser error + * @return true if chunk has been added and false in case of error + */ +bool ucl_parser_add_ubj_chunk (struct ucl_parser *parser, const unsigned char *data, size_t len); + +/** + * Load and add data from a file + * @param parser parser structure + * @param filename the name of file + * @param err if *err is NULL it is set to parser error + * @return true if chunk has been added and false in case of error + */ +bool ucl_parser_add_ubj_file (struct ucl_parser *parser, const char *filename); + +/** * Get a top object for a parser * @param parser parser structure * @param err if *err is NULL it is set to parser error diff --git a/src/ucl_ubj.c b/src/ucl_ubj.c new file mode 100644 index 0000000..190cf8b --- /dev/null +++ b/src/ucl_ubj.c @@ -0,0 +1,230 @@ +#include "ucl.h" +#include "ucl_internal.h" +#include + +typedef enum { + UBJ_NULL = 'Z', + UBJ_TRUE = 'T', + UBJ_FALSE = 'F', + UBJ_BYTE = 'B', + UBJ_INT16 = 'i', + UBJ_INT32 = 'I', + UBJ_INT64 = 'L', + UBJ_FLOAT = 'd', + UBJ_DOUBLE = 'D', + UBJ_HUGE_SMALL = 'h', + UBJ_HUGE_LARGE = 'H', + UBJ_STRING_SMALL = 's', + UBJ_STRING_LARGE = 'S', + UBJ_OBJECT_SMALL = 'o', + UBJ_OBJECT_LARGE = 'O', + UBJ_ARRAY_SMALL = 'a', + UBJ_ARRAY_LARGE = 'A', +} ubj_t; + +static ucl_object_t *ubj_parse(struct ucl_parser *parser, bool small, bool is_array); + +static float +floattoh(float value){ + union v { + float f; + uint32_t i; + }; + union v val; + + val.f = value; + val.i = be32toh(val.i); + + return val.f; +} + +static double +doubletoh(double value) +{ + union v { + double d; + uint32_t i; + }; + union v val; + val.d = value; + val.i = be64toh(val.i); + + return val.d; +} + +static ucl_object_t * +parse_value(struct ucl_parser *parser) +{ + ucl_object_t *obj = NULL; + int8_t byte; + int16_t i16; + int32_t i32; + int64_t i64; + float f; + double d; + size_t size; + struct ucl_chunk *chunk = parser->chunks; + + ubj_t type = *chunk->pos; + + chunk->pos++; + + switch (type) { + case UBJ_NULL: + obj = ucl_object_new(); + break; + case UBJ_TRUE: + obj = ucl_object_frombool(true); + break; + case UBJ_FALSE: + obj = ucl_object_frombool(false); + break; + case UBJ_BYTE: + byte = *chunk->pos; + chunk->pos++; + obj = ucl_object_fromint((int64_t) byte); + break; + case UBJ_INT16: + memcpy(&i16, chunk->pos, sizeof(i16)); + chunk->pos+=2; + obj = ucl_object_fromint((int64_t) be16toh(i16)); + break; + case UBJ_INT32: + memcpy(&i32, chunk->pos, sizeof(i32)); + chunk->pos+=4; + obj = ucl_object_fromint((int64_t) be32toh(i32)); + break; + case UBJ_INT64: + memcpy(&i64, chunk->pos, sizeof(i64)); + chunk->pos+=8; + obj = ucl_object_fromint(be64toh(i64)); + break; + case UBJ_FLOAT: + memcpy(&f, chunk->pos, sizeof(f)); + chunk->pos+=4; + obj = ucl_object_fromdouble((double) floattoh(f)); + break; + case UBJ_DOUBLE: + memcpy(&d, chunk->pos, sizeof(d)); + chunk->pos+=8; + obj = ucl_object_fromdouble(doubletoh(d)); + break; + case UBJ_STRING_SMALL: + size = (size_t)*chunk->pos; + chunk->pos++; + obj = ucl_object_fromstring_common(chunk->pos, size, 0); + chunk->pos += size; + break; + case UBJ_STRING_LARGE: + memcpy(&i32, chunk->pos, sizeof(i32)); + chunk->pos += 4; + obj = ucl_object_fromstring_common(chunk->pos, i32, 0); + chunk->pos += i32; + break; + case UBJ_OBJECT_SMALL: + obj = ubj_parse(parser, true, false); + break; + case UBJ_OBJECT_LARGE: + obj = ubj_parse(parser, false, false); + break; + case UBJ_ARRAY_SMALL: + obj = ubj_parse(parser, true, true); + break; + case UBJ_ARRAY_LARGE: + obj = ubj_parse(parser, false, true); + break; + case UBJ_HUGE_SMALL: /*ignore*/ + case UBJ_HUGE_LARGE: + break; + } + + chunk->remain = chunk->end - chunk->pos; + + return obj; +} + +static ucl_object_t * +ubj_parse(struct ucl_parser *parser, bool small, bool is_array) +{ + ucl_object_t *obj = NULL, *me = NULL, *okey = NULL; + size_t count, size; + struct ucl_chunk *chunk = parser->chunks; + const char *key; + + count = 0; + + me = NULL; + + if (small) { + size = (size_t)*chunk->pos; + chunk->pos++; + } + else { + memcpy(&size, chunk->pos, sizeof(uint32_t)); + chunk->pos += 4; + } + + /* objects have key/value */ + if (!is_array) + size *= 2; + + while (count < size && chunk->pos < chunk->end) { + obj = parse_value(parser); + if (is_array) { + me = ucl_array_append(me, obj); + } + else { + if (okey == NULL) { + if (obj->type == UCL_STRING) { + okey = obj; + } + } + else { + key = ucl_object_tostring(okey); + me = ucl_object_insert_key(me, obj, key, strlen(key), true); + ucl_object_free(okey); + okey = NULL; + } + } + count++; + } + + return (me); +} + +bool +ucl_parser_add_ubj_chunk(struct ucl_parser *parser, const unsigned char *data, + size_t len) +{ + struct ucl_chunk *chunk; + ubj_t type; + + chunk = UCL_ALLOC (sizeof (struct ucl_chunk)); + chunk->begin = data; + chunk->remain = len; + chunk->pos = chunk->begin; + chunk->end = chunk->begin + len; + LL_PREPEND (parser->chunks, chunk); + + type = *chunk->pos; + chunk->pos++; + switch (*data) { + case UBJ_ARRAY_SMALL: + parser->top_obj = ubj_parse(parser, true, true); + break; + case UBJ_ARRAY_LARGE: + parser->top_obj = ubj_parse(parser, false, true); + break; + case UBJ_OBJECT_SMALL: + parser->top_obj = ubj_parse(parser, true, false); + break; + case UBJ_OBJECT_LARGE: + parser->top_obj = ubj_parse(parser, false, false); + break; + default: + ucl_create_err (&parser->err, "Invalid ubj file"); + return false; + } + + return (parser->top_obj != NULL); +} diff --git a/src/ucl_util.c b/src/ucl_util.c index 817d02a..33f1734 100644 --- a/src/ucl_util.c +++ b/src/ucl_util.c @@ -734,6 +734,35 @@ ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool n } bool +ucl_parser_add_ubj_file(struct ucl_parser *parser, const char *filename) +{ + unsigned char *buf; + size_t len; + bool ret; + char realbuf[PATH_MAX]; + + if (realpath (filename, realbuf) == NULL) { + ucl_create_err (&parser->err, "cannot open file %s: %s", + filename, + strerror (errno)); + return false; + } + + if (!ucl_fetch_file (realbuf, &buf, &len, &parser->err)) { + return false; + } + + ucl_parser_set_filevars (parser, realbuf, false); + ret = ucl_parser_add_ubj_chunk (parser, buf, len); + + if (len > 0) { + munmap (buf, len); + } + + return (ret); +} + +bool ucl_parser_add_file (struct ucl_parser *parser, const char *filename) { unsigned char *buf;