From b3f9b25d53b1bd1fc83e8a291ae40b84d02a4478 Mon Sep 17 00:00:00 2001 From: Daniel Friesel Date: Tue, 13 Nov 2018 15:33:56 +0100 Subject: prototest: add ubjson --- include/lib/ubjson/ubj.h | 231 ++++++++++++++ include/lib/ubjson/ubj_internal.h | 163 ++++++++++ src/app/prototest/Makefile.inc | 2 +- src/app/prototest/main.cc | 30 ++ src/lib/ubjson/ubjr.c | 516 +++++++++++++++++++++++++++++++ src/lib/ubjson/ubjrw.c | 169 +++++++++++ src/lib/ubjson/ubjw.c | 618 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 1728 insertions(+), 1 deletion(-) create mode 100644 include/lib/ubjson/ubj.h create mode 100644 include/lib/ubjson/ubj_internal.h create mode 100644 src/lib/ubjson/ubjr.c create mode 100644 src/lib/ubjson/ubjrw.c create mode 100644 src/lib/ubjson/ubjw.c diff --git a/include/lib/ubjson/ubj.h b/include/lib/ubjson/ubj.h new file mode 100644 index 0000000..f3d5ac8 --- /dev/null +++ b/include/lib/ubjson/ubj.h @@ -0,0 +1,231 @@ +#ifndef UBJ_H +#define UBJ_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef enum +{ + UBJ_MIXED=0, //NOT a type...or the type is mixed + + UBJ_NULLTYPE, + UBJ_NOOP, + UBJ_BOOL_TRUE, + UBJ_BOOL_FALSE, + + UBJ_CHAR, + UBJ_STRING, + UBJ_HIGH_PRECISION, + + UBJ_INT8, + UBJ_UINT8 , + UBJ_INT16, + UBJ_INT32, + UBJ_INT64, + UBJ_FLOAT32 , + UBJ_FLOAT64, + + UBJ_ARRAY, + UBJ_OBJECT, + + UBJ_NUM_TYPES //this is the size of how many types there are (chris' trick) +} UBJ_TYPE; + + + +//////////here is the declarations for the writer API//////////////////////////////////// + + + +///////////////////////////////////////////////////////////////////////////////////////// + +struct ubjw_context_t_s; +typedef struct ubjw_context_t_s ubjw_context_t; + +ubjw_context_t* ubjw_open_callback(void* userdata, + size_t(*write_cb)(const void* data, size_t size, size_t count, void* userdata), + int(*close_cb)(void* userdata), + void(*error_cb)(const char* error_msg) + ); +ubjw_context_t* ubjw_open_file(FILE*); +ubjw_context_t* ubjw_open_memory(uint8_t* dst_b, uint8_t* dst_e); + +size_t ubjw_close_context(ubjw_context_t* ctx); + +void ubjw_write_string(ubjw_context_t* dst, const char* out); +void ubjw_write_char(ubjw_context_t* dst, char out); + +void ubjw_write_uint8(ubjw_context_t* dst, uint8_t out); +void ubjw_write_int8(ubjw_context_t* dst, int8_t out); +void ubjw_write_int16(ubjw_context_t* dst, int16_t out); +void ubjw_write_int32(ubjw_context_t* dst, int32_t out); +void ubjw_write_int64(ubjw_context_t* dst, int64_t out); +void ubjw_write_high_precision(ubjw_context_t* dst, const char* hp); + +void ubjw_write_integer(ubjw_context_t* dst, int64_t out); +UBJ_TYPE ubjw_min_integer_type(int64_t in); + +void ubjw_write_float32(ubjw_context_t* dst, float out); +void ubjw_write_float64(ubjw_context_t* dst, double out); + +void ubjw_write_floating_point(ubjw_context_t* dst, double out); + +void ubjw_write_noop(ubjw_context_t* dst); +void ubjw_write_null(ubjw_context_t* dst); +void ubjw_write_bool(ubjw_context_t* dst, uint8_t out); + +void ubjw_begin_array(ubjw_context_t* dst, UBJ_TYPE type, size_t count); + +void ubjw_begin_object(ubjw_context_t* dst, UBJ_TYPE type, size_t count); +void ubjw_write_key(ubjw_context_t* dst, const char* key); +void ubjw_end(ubjw_context_t* dst); + +//output an efficient buffer of types +void ubjw_write_buffer(ubjw_context_t* dst, const uint8_t* data, UBJ_TYPE type, size_t count); + +//Proposal for N-D arrays +void ubjw_begin_ndarray(ubjw_context_t* dst, UBJ_TYPE type, const size_t* dims, uint8_t ndims); +void ubjw_write_ndbuffer(ubjw_context_t* dst,const uint8_t* data, UBJ_TYPE type, const size_t* dims, uint8_t ndims); + + +//////////here is the declarations for the reader API//////////////////////////////////// + + + +///////////////////////////////////////////////////////////////////////////////////////// + +struct ubjr_context_t_s; +typedef struct ubjr_context_t_s ubjr_context_t; + +//Open up a reader context for reading using a custom calllback +ubjr_context_t* ubjr_open_callback(void* userdata, + size_t(*read_cb)(void* data, size_t size, size_t count, void* userdata), + int(*peek_cb)(void* userdata), + int(*close_cb)(void* userdata), + void(*error_cb)(const char* error_msg) + ); + +//Open a context initialized to a UBJ file +ubjr_context_t* ubjr_open_file(FILE*); + +//Open up a context initialized to a memory dump of a UBJ file (or a segment of a UBJ file) +ubjr_context_t* ubjr_open_memory(const uint8_t* dst_b, const uint8_t* dst_e); + +//Close a reader context +size_t ubjr_close_context(ubjr_context_t* ctx); + +typedef char* ubjr_string_t; + +//An array that you read from the stream +typedef struct ubjr_array_t_s +{ + uint8_t originally_sized; + UBJ_TYPE type; + size_t size; //total number of elements + void* values; + uint8_t num_dims; + size_t* dims; //this could be faster if it was constant size, but would also make the size of the dynamic object a LOT bigger + +} ubjr_array_t; + +//a map that you read from the stream +typedef struct ubjr_object_t_s +{ + uint8_t originally_sized; + UBJ_TYPE type; + size_t size; + void* values; + ubjr_string_t* keys; + void* metatable; //don't use this..only useful for computing object_lookup +} ubjr_object_t; + +//a dynamic type that you parsed. +typedef struct ubjr_dynamic_t_s +{ + UBJ_TYPE type; + union + { + uint8_t boolean; + double real; + int64_t integer; + ubjr_string_t string; + ubjr_array_t container_array; + ubjr_object_t container_object; + }; +} ubjr_dynamic_t; + +//Parse a dynamic object from the stream +ubjr_dynamic_t ubjr_read_dynamic(ubjr_context_t* ctx); +void ubjr_cleanup_dynamic(ubjr_dynamic_t* dyn); + +ubjr_dynamic_t ubjr_object_lookup(ubjr_object_t* obj, const char* key); +size_t ubjr_local_type_size(UBJ_TYPE typ);//should be equivalent to sizeof() +size_t ubjr_ndarray_index(const ubjr_array_t* arr, const size_t* indices); + + +//output an efficient buffer of types +///void ubjr_read_buffer(struct ubjr_context_t* dst, const uint8_t* data, UBJ_TYPE type, size_t count); + +void ubjr_cleanup_dynamic(ubjr_dynamic_t* dyn); +void ubjr_cleanup_array(ubjr_array_t* arr); +void ubjr_cleanup_object(ubjr_object_t* obj); + + + +///////UBJ_RW api + +void ubjrw_write_dynamic(ubjw_context_t* ctx, ubjr_dynamic_t dobj,uint8_t optimize); +//ubjrw_append_object(ubjw_context_t* ctx, ubjr_dynamic_t dobj); +//ubjrw_append_array(ubjw_context_t* ctx, ubjr_dynamic_t dobj); + +#ifdef __cplusplus +} + +/* +#include + +static size_t write_os(const void* data, size_t size, size_t count, void* userdata) +{ + size_t n = size*count; + reinterpret_cast(userdata)->write(data, n); + return n; +} +static void close_os(void* userdata) +{ + reinterpret_cast(userdata)->close(); +} + +static size_t read_is(void* data, size_t size, size_t count, void* userdata) +{ + size_t n = size*count; + reinterpret_cast(userdata)->read(data, n); + return n; +} +static int peek_is(void* userdata) +{ + return reinterpret_cast(userdata)->peek(); +} +static void close_is(void* userdata) +{ + reinterpret_cast(userdata)->close(); +} + +static ubjw_context_t* ubjw_open_stream(std::ostream& outstream) +{ + return ubjw_open_callback((void*)&outstream, write_os, close_os, NULL); +} + +static ubjr_context_t* ubjr_open_stream(std::istream& instream) +{ + return ubjr_open_callback((void*)&instream, read_is, peek_is, close_is, NULL); +} +*/ + + +#endif + +#endif diff --git a/include/lib/ubjson/ubj_internal.h b/include/lib/ubjson/ubj_internal.h new file mode 100644 index 0000000..fc61697 --- /dev/null +++ b/include/lib/ubjson/ubj_internal.h @@ -0,0 +1,163 @@ +#ifndef UBJ_INTERNAL_H +#define UBJ_INTERNAL_H + +#include "ubj.h" +#include +#include + +#if _MSC_VER +#define inline __inline +#endif + + +static const uint8_t UBJI_TYPEC_convert[UBJ_NUM_TYPES] = "\x00ZNTFCSHiUIlLdD[{"; + +static const int UBJI_TYPE_size[UBJ_NUM_TYPES] = + { -1, //MIXED + 0, //NULLTYPE + 0, //NOOP + 0, //BOOL_TRUE + 0, //BOOL_FALSE + 1, //CHAR + sizeof(const char*), //STRING + sizeof(const char*), //high-precision + 1, //INT8 + 1, //UINT8 + 2, //int16 + 4, //int32 + 8, //int64 + 4, //float32 + 8, //float64 + -1, //array + -1 //object + }; + +static const size_t UBJR_TYPE_localsize[UBJ_NUM_TYPES] = +{ + sizeof(ubjr_dynamic_t), //MIXED + 0, //NULLTYPE + 0, //NOOP + 0, //BOOL_TRUE + 0, //BOOL_FALSE + sizeof(ubjr_string_t), //CHAR + sizeof(ubjr_string_t), //STRING + sizeof(ubjr_string_t), //high-precision + sizeof(int8_t), //INT8 + sizeof(uint8_t), //UINT8 + sizeof(int16_t), //int16 + sizeof(int32_t), //int32 + sizeof(int64_t), //int64 + sizeof(float), //float32 + sizeof(double), //float64 + sizeof(ubjr_array_t), //array + sizeof(ubjr_object_t) //object +}; + +static inline void _to_bigendian16(uint8_t* outbuffer, uint16_t input) +{ + *outbuffer++ = (input >> 8); // Get top order byte (guaranteed endian-independent since machine registers) + *outbuffer++ = input & 0xFF; // Get bottom order byte +} +static inline void _to_bigendian32(uint8_t* outbuffer, uint32_t input) +{ + _to_bigendian16(outbuffer, (uint16_t)(input >> 16)); // Get top order 2 bytes + _to_bigendian16(outbuffer + 2, (uint16_t)(input & 0xFFFF)); // Get bottom order 2 bytes +} +static inline void _to_bigendian64(uint8_t* outbuffer, uint64_t input) +{ + _to_bigendian32(outbuffer, (uint32_t)(input >> 32)); + _to_bigendian32(outbuffer + 4, (uint32_t)(input & 0xFFFFFFFF)); +} + +static inline uint8_t _is_bigendian() +{ + int i = 1; + char *low = (char*)&i; + return *low ? 0 : 1; +} + +#define BUF_BIG_ENDIAN_SWAP(type,func,ptr,num) \ + { \ + size_t i;type* d = (type*)ptr; \ + for (i = 0; i < num; i++) \ + { \ + func((uint8_t*)&d[i], d[i]); \ + } \ + } \ + +static inline void buf_endian_swap(uint8_t* buf, size_t sz, size_t n) +{ + if (!_is_bigendian()) + { + switch (sz) + { + case 1: + case 0: + break; + case 2: + BUF_BIG_ENDIAN_SWAP(uint16_t, _to_bigendian16,buf,n); + break; + case 4: + BUF_BIG_ENDIAN_SWAP(uint32_t, _to_bigendian32,buf,n); + break; + case 8: + BUF_BIG_ENDIAN_SWAP(uint64_t, _to_bigendian64,buf,n); + break; + }; + } +} + +//warning...null-terminated strings are assumed...when this is not necessarily valid. FIXED: we don't use null-terminated strings in the reader (NOT FIXED...string type is awkward) +static inline ubjr_dynamic_t priv_ubjr_pointer_to_dynamic(UBJ_TYPE typ, const void* dat) +{ + ubjr_dynamic_t outdyn; + outdyn.type = typ; + size_t n = 1; + switch (typ) + { + case UBJ_NULLTYPE: + case UBJ_NOOP: + break; + case UBJ_BOOL_TRUE: + case UBJ_BOOL_FALSE: + outdyn.boolean = (typ == UBJ_BOOL_TRUE ? 1 : 0); + break; + case UBJ_HIGH_PRECISION: + case UBJ_STRING: + case UBJ_CHAR://possibly if char allocate, otherwise don't + outdyn.string = *(const ubjr_string_t*)dat; + break; + case UBJ_INT8: + outdyn.integer = *(const int8_t*)dat; + break; + case UBJ_UINT8: + outdyn.integer = *(const uint8_t*)dat; + break; + case UBJ_INT16: + outdyn.integer = *(const int16_t*)dat; + break; + case UBJ_INT32: + outdyn.integer = *(const int32_t*)dat; + break; + case UBJ_INT64: + outdyn.integer = *(const int64_t*)dat; + break; + case UBJ_FLOAT32: + outdyn.real = *(const float*)dat; + break; + case UBJ_FLOAT64: + outdyn.real = *(const double*)dat; + break; + case UBJ_ARRAY: + outdyn.container_array = *(const ubjr_array_t*)dat; + break; + case UBJ_OBJECT: + outdyn.container_object = *(const ubjr_object_t*)dat; + break; + case UBJ_MIXED: + outdyn = *(const ubjr_dynamic_t*)dat; + }; + return outdyn; +} + +#endif \ No newline at end of file diff --git a/src/app/prototest/Makefile.inc b/src/app/prototest/Makefile.inc index d3dad26..36cfb49 100644 --- a/src/app/prototest/Makefile.inc +++ b/src/app/prototest/Makefile.inc @@ -23,7 +23,7 @@ endif ifeq (${prototest_ubjson}, 1) COMMON_FLAGS += -DPROTOTEST_UBJSON - CXX_TARGETS += src/lib/ubjson/ubjr.cc src/lib/ubjson/ubjw.cc + C_TARGETS += src/lib/ubjson/ubjr.c src/lib/ubjson/ubjw.c INCLUDES += -Iinclude/lib/ubjson endif diff --git a/src/app/prototest/main.cc b/src/app/prototest/main.cc index aebfaea..c1d8359 100644 --- a/src/app/prototest/main.cc +++ b/src/app/prototest/main.cc @@ -17,6 +17,9 @@ #include #include #endif +#ifdef PROTOTEST_UBJSON +#include "ubj.h" +#endif #ifdef PROTOTEST_XDR #include "object/stdbuf.h" #include "object/xdrstream.h" @@ -146,6 +149,33 @@ void loop(void) } kout << endl; +#endif + + /* + * UBJSON + */ + +#ifdef PROTOTEST_UBJSON + + uint8_t buf[128]; + for (unsigned int i = 0; i < 128; i++) { + buf[i] = 0; + } + + ubjw_context_t* ctx = ubjw_open_memory(buf, buf + sizeof(buf)); + ubjw_begin_array(ctx, UBJ_MIXED, 0); + ubjw_write_int16(ctx, ts); + ubjw_write_string(ctx, "Noot Noot"); + ubjw_end(ctx); + + kout << "ubjr_close_context: " << ubjw_close_context(ctx) << endl; + + kout << "ubjr is " << hex; + for (unsigned int i = 0; i < 128; i++) { + kout << (uint8_t)buf[i]; + } + kout << endl; + #endif /* diff --git a/src/lib/ubjson/ubjr.c b/src/lib/ubjson/ubjr.c new file mode 100644 index 0000000..4a5ba97 --- /dev/null +++ b/src/lib/ubjson/ubjr.c @@ -0,0 +1,516 @@ +#include "ubj.h" +#include "ubj_internal.h" +#include +#include + +#if _MSC_VER +#define inline __inline +#endif + +typedef struct ubjr_context_t_s +{ + size_t (*read_cb )(void* data, size_t size, size_t count, void* userdata); + int (*peek_cb )(void* userdata); + int (*close_cb)(void* userdata); + void (*error_cb)(const char* error_msg); + + void* userdata; + +// struct _ubjr_container_t container_stack[CONTAINER_STACK_MAX]; +// struct _ubjr_container_t* head; + + uint8_t ignore_container_flags; + + uint16_t last_error_code; + + size_t total_read; +} ubjr_context_t; + +ubjr_context_t* ubjr_open_callback(void* userdata, + size_t(*read_cb)(void* data, size_t size, size_t count, void* userdata), + int(*peek_cb)(void* userdata), + int(*close_cb)(void* userdata), + void(*error_cb)(const char* error_msg) + ) +{ + ubjr_context_t* ctx = (ubjr_context_t*)malloc(sizeof(ubjr_context_t)); + ctx->userdata = userdata; + ctx->read_cb = read_cb; + ctx->peek_cb = peek_cb; + ctx->close_cb = close_cb; + ctx->error_cb = error_cb; + + +/* ctx->head = ctx->container_stack; + ctx->head->flags = 0; + ctx->head->type = UBJ_MIXED; + ctx->head->elements_remaining = 0; + + ctx->ignore_container_flags = 0;*/ + + ctx->last_error_code = 0; + + ctx->total_read = 0; + return ctx; +} + +size_t ubjr_close_context(ubjr_context_t* ctx) +{ + size_t n = ctx->total_read; + free(ctx); + return n; +} + +static inline uint8_t priv_ubjr_context_getc(ubjr_context_t* ctx) +{ + uint8_t a; + ctx->total_read += 1; + ctx->read_cb(&a, 1, 1, ctx->userdata); + return a; +} + +static int fpeek(void* fp) +{ + int c; + c = fgetc(fp); + ungetc(c, fp); + + return c; +} + +ubjr_context_t* ubjr_open_file(FILE* fd) +{ + return ubjr_open_callback(fd, (void*)fread,(void*)fpeek,(void*)fclose, NULL); +} + +struct mem_r_fd +{ + const uint8_t *begin, *current, *end; +}; +static int memclose(void* mfd) +{ + //free(mfd); + return 0; +} +static size_t memread(void* data, size_t size, size_t count, struct mem_r_fd* fp) +{ + size_t n = size*count; + size_t lim = fp->end - fp->current; + if (lim < n) + { + n = lim; + } + memcpy(data, fp->current, n); + fp->current += n; + return n; +} +static int mempeek(struct mem_r_fd* mfd) +{ + return *mfd->current; +} + +ubjr_context_t* ubjr_open_memory(const uint8_t* be, const uint8_t* en) +{ + struct mem_r_fd* mfd = (struct mem_r_fd*)malloc(sizeof(struct mem_r_fd)); + mfd->current = be; + mfd->begin = be; + mfd->end = en; + return ubjr_open_callback(mfd, (void*)memread, (void*)mempeek,(void*)memclose, NULL); +} + +static inline int priv_ubjr_context_peek(ubjr_context_t* ctx) +{ + return ctx->peek_cb(ctx->userdata); +} +static inline size_t priv_ubjr_context_read(ubjr_context_t* ctx,uint8_t* dst,size_t n) +{ + size_t nr=ctx->read_cb(dst,n,1,ctx->userdata); + ctx->total_read+=nr; + return nr; +} + +typedef struct priv_ubjr_sorted_key_t_s +{ + ubjr_string_t key; + const uint8_t* value; + +} priv_ubjr_sorted_key_t; + +static int _obj_key_cmp(const void* av, const void* bv) +{ + const priv_ubjr_sorted_key_t *a, *b; + a = (const priv_ubjr_sorted_key_t*)av; + b = (const priv_ubjr_sorted_key_t*)bv; + return strcmp(a->key,b->key); +} + +static inline UBJ_TYPE priv_ubjr_type_from_char(uint8_t c) +{ + int i = 0; //TODO: Benchmark this and see if it should be a switch statement where the compiler implements fastest switch e.g. binary search (17 cases might be binary search fast) + for (i = 0; i < UBJ_NUM_TYPES && UBJI_TYPEC_convert[i] != c; i++); + return (UBJ_TYPE)i; +} + +size_t ubjr_local_type_size(UBJ_TYPE typ) +{ + return UBJR_TYPE_localsize[typ]; +} + + +static inline priv_ubjr_sorted_key_t* priv_ubjr_object_build_sorted_keys(ubjr_object_t* obj) +{ + priv_ubjr_sorted_key_t* sorted_keysmem = malloc(obj->size*sizeof(priv_ubjr_sorted_key_t)); + size_t i; + for (i = 0; i < obj->size; i++) + { + sorted_keysmem[i].key = obj->keys[i]; + sorted_keysmem[i].value = (const uint8_t*)obj->values + i*UBJR_TYPE_localsize[obj->type]; + } + qsort(sorted_keysmem, obj->size, sizeof(priv_ubjr_sorted_key_t), _obj_key_cmp); + return sorted_keysmem; +} + +static inline uint8_t priv_ubjr_read_1b(ubjr_context_t* ctx) +{ + return priv_ubjr_context_getc(ctx); +} +static inline uint16_t priv_ubjr_read_2b(ubjr_context_t* ctx) +{ + return (uint16_t)priv_ubjr_read_1b(ctx) << 8 | (uint16_t)priv_ubjr_read_1b(ctx); +} +static inline uint32_t priv_ubjr_read_4b(ubjr_context_t* ctx) +{ + return (uint32_t)priv_ubjr_read_2b(ctx) << 16 | (uint32_t)priv_ubjr_read_2b(ctx); +} +static inline uint64_t priv_ubjr_read_8b(ubjr_context_t* ctx) +{ + return (uint64_t)priv_ubjr_read_4b(ctx) << 32 | (uint64_t)priv_ubjr_read_4b(ctx); +} + +static inline int64_t priv_ubjw_read_integer(ubjr_context_t* ctx) +{ + ubjr_dynamic_t d = ubjr_read_dynamic(ctx); + if (d.type >= UBJ_INT8 && d.type <= UBJ_INT64) + return d.integer; + return 0;//error +} + +static inline ubjr_object_t priv_ubjr_read_raw_object(ubjr_context_t* ctx); +static inline ubjr_array_t priv_ubjr_read_raw_array(ubjr_context_t* ctx); +static inline void priv_ubjr_read_to_ptr(ubjr_context_t* ctx, uint8_t* dst, UBJ_TYPE typ) +{ + int64_t n = 1; + char *tstr; + switch (typ) + { + case UBJ_MIXED: + { + *(ubjr_dynamic_t*)dst = ubjr_read_dynamic(ctx); + break; + } + case UBJ_STRING: + case UBJ_HIGH_PRECISION: + { + n = priv_ubjw_read_integer(ctx); + } + case UBJ_CHAR: + { + tstr = malloc(n + 1); + priv_ubjr_context_read(ctx, tstr, n); + tstr[n] = 0; + *(ubjr_string_t*)dst = tstr; + break; + } + case UBJ_INT8: + case UBJ_UINT8: + { + *dst = priv_ubjr_read_1b(ctx); + break; + } + case UBJ_INT16: + { + *(uint16_t*)dst = priv_ubjr_read_2b(ctx); + break; + } + case UBJ_INT32: + case UBJ_FLOAT32: + { + *(uint32_t*)dst = priv_ubjr_read_4b(ctx); + break; + } + case UBJ_INT64: + case UBJ_FLOAT64: + { + *(uint64_t*)dst = priv_ubjr_read_8b(ctx); + break; + } + case UBJ_ARRAY: + { + *(ubjr_array_t*)dst = priv_ubjr_read_raw_array(ctx); + break; + } + case UBJ_OBJECT: + { + *(ubjr_object_t*)dst = priv_ubjr_read_raw_object(ctx); + break; + } + }; +} + +ubjr_dynamic_t ubjr_object_lookup(ubjr_object_t* obj, const char* key) +{ + if (obj->metatable == NULL) + { + //memcpy(obj->sorted_keys,obj->keys) + obj->metatable = priv_ubjr_object_build_sorted_keys(obj); + } + void* result=bsearch(key, obj->metatable,obj->size, sizeof(priv_ubjr_sorted_key_t),_obj_key_cmp); + if (result == NULL) + { + ubjr_dynamic_t nulldyn; + nulldyn.type = UBJ_NULLTYPE; + return nulldyn; + } + const priv_ubjr_sorted_key_t* result_key = (const priv_ubjr_sorted_key_t*)result; + return priv_ubjr_pointer_to_dynamic(obj->type,result_key->value); +} + +size_t ubjr_ndarray_index(const ubjr_array_t* arr, const size_t* indices) +{ + //multi-dimensional array to linear array lookup + size_t cstride = 1; + size_t cdex = 0; + uint8_t i; + uint8_t nd = arr->num_dims; + const size_t* dims = arr->dims; + for (i = 0; i= (1ULL << arrpot)) + { + arrpot ++; + myarray.values = realloc(myarray.values, (1ULL << arrpot)*ls+1); + } + priv_ubjr_read_to_ptr(ctx,(uint8_t*)myarray.values + ls*myarray.size,myarray.type); + } + priv_ubjr_context_getc(ctx); // read the closing ']' + } + else + { + myarray.originally_sized = 1; + size_t i; + myarray.values = malloc(ls*myarray.size+1); + size_t sz = UBJI_TYPE_size[myarray.type]; + + if (sz >= 0 && myarray.type != UBJ_STRING && myarray.type != UBJ_HIGH_PRECISION && myarray.type != UBJ_CHAR && myarray.type != UBJ_MIXED) //constant size,fastread + { + priv_ubjr_context_read(ctx, myarray.values, sz*myarray.size); + buf_endian_swap(myarray.values, sz, myarray.size); //do nothing for 0-sized buffers + } + else + { + for (i = 0; i < myarray.size; i++) + { + priv_ubjr_read_to_ptr(ctx, (uint8_t*)myarray.values + ls*i, myarray.type); + } + } + } + if (myarray.dims == NULL) + { + myarray.dims = malloc(sizeof(size_t)); + myarray.dims[0] = myarray.size; + } + return myarray; +} + +static inline ubjr_object_t priv_ubjr_read_raw_object(ubjr_context_t* ctx) +{ + ubjr_object_t myobject; + myobject.metatable = NULL; + priv_read_container_params(ctx, &myobject.type, &myobject.size); + + size_t ls = UBJR_TYPE_localsize[myobject.type]; + if (myobject.size == 0) + { + myobject.originally_sized = 0; + size_t arrpot = 0; + myobject.values = malloc(1 * ls + 1); //the +1 is for memory for the 0-size elements + myobject.keys = malloc(1 * sizeof(ubjr_string_t)); + for (myobject.size = 0; priv_ubjr_context_peek(ctx) != '}'; myobject.size++) + { + if (myobject.size >= (1ULL << arrpot)) + { + arrpot++; + myobject.values = realloc(myobject.values, (1ULL << arrpot)*ls + 1); + myobject.keys = realloc((uint8_t*)myobject.keys, (1ULL << arrpot)*sizeof(ubjr_string_t)); + } + priv_ubjr_read_to_ptr(ctx, (uint8_t*)(myobject.keys + myobject.size), UBJ_STRING); + priv_ubjr_read_to_ptr(ctx, (uint8_t*)myobject.values + ls*myobject.size, myobject.type); + } + priv_ubjr_context_getc(ctx); // read the closing '}' + } + else + { + size_t i; + myobject.originally_sized = 1; + myobject.values = malloc(ls*myobject.size + 1); + myobject.keys = malloc(myobject.size * sizeof(ubjr_string_t)); + + for (i = 0; i < myobject.size; i++) + { + priv_ubjr_read_to_ptr(ctx, (uint8_t*)(myobject.keys + i), UBJ_STRING); + priv_ubjr_read_to_ptr(ctx, (uint8_t*)myobject.values + ls*i, myobject.type); + } + } + return myobject; +} +static inline void priv_ubjr_cleanup_pointer(UBJ_TYPE typ,void* value); +static inline priv_ubjr_cleanup_container(UBJ_TYPE type,size_t size,void* values) +{ + if(type == UBJ_MIXED || type == UBJ_ARRAY || type == UBJ_OBJECT || type == UBJ_STRING) + { + size_t ls=UBJR_TYPE_localsize[type]; + uint8_t *viter,*vend; + viter=values; + vend=viter+ls*size; + for(;viter != vend;viter+=ls) + { + priv_ubjr_cleanup_pointer(type,(void*)viter); + } + } + free(values); +} +static inline void priv_ubjr_cleanup_pointer(UBJ_TYPE typ,void* value) +{ + switch(typ) + { + case UBJ_MIXED: + { + ubjr_dynamic_t* dyn=(ubjr_dynamic_t*)value; + switch(dyn->type) + { + case UBJ_STRING: + priv_ubjr_cleanup_pointer(UBJ_STRING,&dyn->string); + break; + case UBJ_ARRAY: + priv_ubjr_cleanup_pointer(UBJ_ARRAY,&dyn->container_array); + break; + case UBJ_OBJECT: + priv_ubjr_cleanup_pointer(UBJ_OBJECT,&dyn->container_object); + break; + }; + break; + } + case UBJ_STRING: + { + ubjr_string_t* st=(ubjr_string_t*)value; + free((void*)*st); + break; + } + case UBJ_ARRAY: + { + ubjr_array_t* arr=(ubjr_array_t*)value; + priv_ubjr_cleanup_container(arr->type,arr->size,arr->values); + free(arr->dims); + break; + } + case UBJ_OBJECT: + { + ubjr_object_t* obj=(ubjr_object_t*)value; + priv_ubjr_cleanup_container(obj->type,obj->size,obj->values); + priv_ubjr_cleanup_container(UBJ_STRING,obj->size,obj->keys); + if(obj->metatable) + { + free(obj->metatable); + } + break; + } + }; +} + +void ubjr_cleanup_dynamic(ubjr_dynamic_t* dyn) +{ + priv_ubjr_cleanup_pointer(UBJ_MIXED,dyn); +} +void ubjr_cleanup_array(ubjr_array_t* arr) +{ + priv_ubjr_cleanup_pointer(UBJ_ARRAY,arr); +} +void ubjr_cleanup_object(ubjr_object_t* obj) +{ + priv_ubjr_cleanup_pointer(UBJ_OBJECT,obj); +} + diff --git a/src/lib/ubjson/ubjrw.c b/src/lib/ubjson/ubjrw.c new file mode 100644 index 0000000..ee43d70 --- /dev/null +++ b/src/lib/ubjson/ubjrw.c @@ -0,0 +1,169 @@ +#include "ubj_internal.h" + +static uint32_t compute_typemask(ubjr_dynamic_t* vals, size_t sz) +{ + uint32_t typemask = 0; + size_t i; + for (i = 0; i < sz; i++) + { + typemask |= 1UL << vals[i].type; + } + return typemask; +} + +static inline UBJ_TYPE typemask2type(uint32_t v) +{ + unsigned int r = 0; // r will be lg(v) + + while (v >>= 1) // unroll for more speed... + { + r++; + } + return (UBJ_TYPE)r; +} +static UBJ_TYPE compute_best_integer_type(ubjr_dynamic_t* vals, size_t sz) +{ + uint32_t typemask = 0; + size_t i; + for (i = 0; i < sz; i++) + { + typemask |= 1UL << ubjw_min_integer_type(vals[i].integer); + } + return typemask2type(typemask); +} +static uint32_t compute_best_string_type(ubjr_dynamic_t* vals, size_t sz) +{ + size_t i; + for (i = 0; i < sz; i++) + { + if (strlen(vals[i].string) > 1) + { + return UBJ_STRING; + } + } + return UBJ_CHAR; +} +static UBJ_TYPE optimize_type(UBJ_TYPE typein,ubjr_dynamic_t* vals, size_t sz) +{ + static const uint32_t intmask = (1 << UBJ_INT8) | (1 << UBJ_UINT8) | (1 << UBJ_INT16) | (1 << UBJ_INT32) | (1 << UBJ_INT64); + static const uint32_t stringmask = (1 << UBJ_STRING) | (1 << UBJ_CHAR); + if (typein != UBJ_MIXED) + return typein; + //integer optimization can be done here... + uint32_t tm = compute_typemask(vals, sz); + if ((tm & intmask) == tm) //if all values are integers + { + return compute_best_integer_type(vals,sz); //calculate the optimum type given the data + } + else if ((tm & stringmask) == tm) + { + return compute_best_string_type(vals,sz); + } + else if(tm && !(tm & (tm- 1))) //if only one bit is set in typemask + { + return typemask2type(tm); //figure out which bit is set. + } + else + { + return UBJ_MIXED; + } +} + +void ubjrw_write_dynamic(ubjw_context_t* ctx, ubjr_dynamic_t dobj,uint8_t optimize) +{ + UBJ_TYPE ctyp,otyp; + size_t csize; + uint8_t* cvalues; + switch (dobj.type) + { + case UBJ_MIXED: + return;///error, can't be mixed + case UBJ_NULLTYPE: + ubjw_write_null(ctx); + return; + case UBJ_NOOP: + ubjw_write_noop(ctx); + return; + case UBJ_BOOL_FALSE: + ubjw_write_bool(ctx, 0); + return; + case UBJ_BOOL_TRUE: + ubjw_write_bool(ctx, 1); + return; + case UBJ_CHAR: + ubjw_write_char(ctx, *dobj.string);//first character of string + return; + case UBJ_STRING: + ubjw_write_string(ctx, dobj.string); + return; + case UBJ_HIGH_PRECISION: + ubjw_write_high_precision(ctx, dobj.string); + return; + case UBJ_INT8: + ubjw_write_int8(ctx, (int8_t)dobj.integer); + return; + case UBJ_UINT8: + ubjw_write_uint8(ctx, (uint8_t)dobj.integer); + return; + case UBJ_INT16: + ubjw_write_int16(ctx, (int16_t)dobj.integer); + return; + case UBJ_INT32: + ubjw_write_int32(ctx, (int32_t)dobj.integer); + return; + case UBJ_INT64: + ubjw_write_int64(ctx, dobj.integer); + return; + case UBJ_FLOAT32: + ubjw_write_float32(ctx, (float)dobj.real); + return; + case UBJ_FLOAT64: + ubjw_write_float64(ctx, dobj.real); + return; + case UBJ_ARRAY: + if ((dobj.container_array.originally_sized || optimize) //if we optimize an unsized array to a sized one or the original is sized + && dobj.container_array.type != UBJ_MIXED + && dobj.container_array.type != UBJ_OBJECT + && dobj.container_array.type != UBJ_ARRAY) + { + ubjw_write_buffer(ctx, dobj.container_array.values, dobj.container_array.type, dobj.container_array.size); + return; + } + else + { + ctyp = dobj.container_array.type; + csize = dobj.container_array.size; + cvalues = dobj.container_array.values; + otyp = optimize ? optimize_type(ctyp,(ubjr_dynamic_t*)cvalues,csize) : ctyp; + ubjw_begin_array(ctx, otyp, (dobj.container_array.originally_sized || optimize) ? csize : 0); + break; + } + case UBJ_OBJECT: + { + ctyp = dobj.container_object.type; + csize = dobj.container_object.size; + cvalues = dobj.container_object.values; + otyp = optimize ? optimize_type(ctyp, (ubjr_dynamic_t*)cvalues, csize) : ctyp; + ubjw_begin_object(ctx, otyp, (dobj.container_object.originally_sized || optimize) ? csize : 0); + break; + } + }; + { + size_t i; + ubjr_dynamic_t scratch; + size_t ls = UBJR_TYPE_localsize[ctyp]; + + for (i = 0; i < csize; i++) + { + if (dobj.type == UBJ_OBJECT) + { + ubjw_write_key(ctx, dobj.container_object.keys[i]); + } + scratch = priv_ubjr_pointer_to_dynamic(ctyp, cvalues + ls*i); + scratch.type = (otyp == UBJ_MIXED ? scratch.type : otyp); + ubjrw_write_dynamic(ctx, scratch,optimize); + } + ubjw_end(ctx); + } + +} diff --git a/src/lib/ubjson/ubjw.c b/src/lib/ubjson/ubjw.c new file mode 100644 index 0000000..5cd060a --- /dev/null +++ b/src/lib/ubjson/ubjw.c @@ -0,0 +1,618 @@ +#include "ubj.h" +#include "ubj_internal.h" + +#define CONTAINER_IS_SIZED 0x1 +#define CONTAINER_IS_TYPED 0x2 +#define CONTAINER_IS_UBJ_ARRAY 0x4 +#define CONTAINER_IS_UBJ_OBJECT 0x8 + +#define CONTAINER_EXPECTS_KEY 0x10 + +#define CONTAINER_STACK_MAX 64 +#define BUFFER_OUT_SIZE 1024 + +#define MAX_DIMS 8 + + +struct priv_ubjw_container_t +{ + uint8_t flags; + UBJ_TYPE type; + size_t elements_remaining; +}; + +struct ubjw_context_t_s +{ + size_t(*write_cb)(const void* data, size_t size, size_t count, void* userdata); + int(*close_cb)(void* userdata); + void (*error_cb)(const char* error_msg); + + void* userdata; + + struct priv_ubjw_container_t container_stack[CONTAINER_STACK_MAX]; + struct priv_ubjw_container_t* head; + + uint8_t ignore_container_flags; + + uint16_t last_error_code; + + size_t total_written; +}; + + + +ubjw_context_t* ubjw_open_callback(void* userdata, + size_t(*write_cb)(const void* data, size_t size, size_t count, void* userdata), + int(*close_cb)(void* userdata), + void (*error_cb)(const char* error_msg) + ) +{ + ubjw_context_t* ctx = (ubjw_context_t*)malloc(sizeof(ubjw_context_t)); + ctx->userdata = userdata; + ctx->write_cb = write_cb; + ctx->close_cb = close_cb; + ctx->error_cb = error_cb; + + ctx->head = ctx->container_stack; + ctx->head->flags = 0; + ctx->head->type = UBJ_MIXED; + ctx->head->elements_remaining = 0; + //ctx->head->num_dims=1; + + ctx->ignore_container_flags = 0; + + ctx->last_error_code = 0; + + ctx->total_written = 0; + return ctx; +} +ubjw_context_t* ubjw_open_file(FILE* fd) +{ + return ubjw_open_callback(fd, (void*)fwrite,(void*)fclose,NULL); +} + +struct mem_w_fd +{ + uint8_t *begin,*current, *end; +}; + +static int memclose(void* mfd) +{ + free(mfd); + return 0; +} +static size_t memwrite(const void* data, size_t size, size_t count, struct mem_w_fd* fp) +{ + size_t n = size*count; + size_t lim = fp->end - fp->current; + if (lim < n) + { + n = lim; + } + memcpy(fp->current, data, n); + fp->current += n; + return n; +} + +ubjw_context_t* ubjw_open_memory(uint8_t* be, uint8_t* en) +{ + struct mem_w_fd* mfd = (struct mem_w_fd*)malloc(sizeof(struct mem_w_fd)); + mfd->current = be; + mfd->begin = be; + mfd->end = en; + return ubjw_open_callback(mfd, (void*)memwrite, (void*)memclose,NULL); +} + +static inline void priv_ubjw_context_append(ubjw_context_t* ctx, uint8_t a) +{ + ctx->total_written += 1; + ctx->write_cb(&a, 1, 1, ctx->userdata); +} + +static inline void priv_disassembly_begin(ubjw_context_t* ctx) +{ +#ifdef UBJW_DISASSEMBLY_MODE + priv_ubjw_context_append(ctx, (uint8_t)'['); +#endif +} +static inline void priv_disassembly_end(ubjw_context_t* ctx) +{ +#ifdef UBJW_DISASSEMBLY_MODE + priv_ubjw_context_append(ctx, (uint8_t)']'); +#endif +} +static inline void priv_disassembly_indent(ubjw_context_t* ctx) +{ +#ifdef UBJW_DISASSEMBLY_MODE + int n = ctx->head - ctx->container_stack; + int i; + priv_ubjw_context_append(ctx, (uint8_t)'\n'); + for (i = 0; i < n; i++) + { + priv_ubjw_context_append(ctx, (uint8_t)'\t'); + } +#endif +} + +static inline void priv_ubjw_context_finish_container(ubjw_context_t* ctx, struct priv_ubjw_container_t* head) +{ + if (head->flags & CONTAINER_IS_SIZED) + { + if (head->elements_remaining > 0) + { + //error not all elements written + } + } + else + { + priv_disassembly_begin(ctx); + if (head->flags & CONTAINER_IS_UBJ_ARRAY) + { + priv_ubjw_context_append(ctx, (uint8_t)']'); + } + else if (head->flags & CONTAINER_IS_UBJ_OBJECT) + { + priv_ubjw_context_append(ctx, (uint8_t)'}'); + } + priv_disassembly_end(ctx); + } +} + +static inline priv_ubjw_container_stack_push(ubjw_context_t* ctx, const struct priv_ubjw_container_t* cnt) +{ + size_t height = ctx->head-ctx->container_stack+1; + if(height < CONTAINER_STACK_MAX) + { + *(++(ctx->head))=*cnt; + } + else + { + //todo::error + } +} +static inline struct priv_ubjw_container_t priv_ubjw_container_stack_pop(ubjw_context_t* ctx) +{ + return *ctx->head--; +} + +size_t ubjw_close_context(ubjw_context_t* ctx) +{ + while (ctx->head > ctx->container_stack) + { + struct priv_ubjw_container_t cnt = priv_ubjw_container_stack_pop(ctx); + priv_ubjw_context_finish_container(ctx, &cnt); + }; + size_t n = ctx->total_written; + if (ctx->close_cb) + ctx->close_cb(ctx->userdata); + free(ctx); + return n; +} + + +static inline size_t priv_ubjw_context_write(ubjw_context_t* ctx, const uint8_t* data, size_t sz) +{ + ctx->total_written += sz; + return ctx->write_cb(data, 1, sz, ctx->userdata); +} + +static inline void priv_ubjw_tag_public(ubjw_context_t* ctx, UBJ_TYPE tid) +{ + struct priv_ubjw_container_t* ch = ctx->head; + if (!ctx->ignore_container_flags) + { + + /*if ( + (!(ch->flags & (CONTAINER_IS_UBJ_ARRAY | CONTAINER_IS_UBJ_OBJECT))) && + (tid != UBJ_ARRAY && tid !=UBJ_OBJECT)) + { + //error, only array and object can be first written + }*/ + + if (ch->flags & CONTAINER_IS_UBJ_OBJECT) + { + if (ch->flags & CONTAINER_EXPECTS_KEY) + { + //error,a key expected + return; + } + ch->flags |= CONTAINER_EXPECTS_KEY; //set key expected next time in this context + } + else + { + priv_disassembly_indent(ctx); + } + + if (ch->flags & CONTAINER_IS_SIZED) + { + ch->elements_remaining--; //todo: error if elements remaining is 0; + } + + if ((ch->flags & CONTAINER_IS_TYPED) && ch->type == tid) + { + return; + } + } + priv_disassembly_begin(ctx); + priv_ubjw_context_append(ctx, UBJI_TYPEC_convert[tid]); + priv_disassembly_end(ctx); +} + +static inline void priv_ubjw_write_raw_string(ubjw_context_t* ctx, const char* out)//TODO: possibly use a safe string +{ + size_t n = strlen(out); + ctx->ignore_container_flags = 1; + ubjw_write_integer(ctx, (int64_t)n); + ctx->ignore_container_flags = 0; + priv_disassembly_begin(ctx); + priv_ubjw_context_write(ctx, (const uint8_t*)out, n); + priv_disassembly_end(ctx); +} +void ubjw_write_string(ubjw_context_t* ctx, const char* out) +{ + priv_ubjw_tag_public(ctx,UBJ_STRING); + priv_ubjw_write_raw_string(ctx, out); +} + +static inline void priv_ubjw_write_raw_char(ubjw_context_t* ctx, char out) +{ + priv_disassembly_begin(ctx); + priv_ubjw_context_append(ctx, (uint8_t)out); + priv_disassembly_end(ctx); +} +void ubjw_write_char(ubjw_context_t* ctx, char out) +{ + priv_ubjw_tag_public(ctx,UBJ_CHAR); + priv_ubjw_write_raw_char(ctx, out); +} + +#ifndef min +static inline size_t min(size_t x,size_t y) +{ + return x < y ? x : y; +} +#endif + +#ifdef UBJW_DISASSEMBLY_MODE +#include +#define DISASSEMBLY_PRINT_BUFFER_SIZE 1024 + +static inline priv_disassembly_print(ubjw_context_t* ctx, const char* format,...) +{ + char buffer[DISASSEMBLY_PRINT_BUFFER_SIZE]; + va_list args; + va_start(args, format); + int n=vsnprintf(buffer, DISASSEMBLY_PRINT_BUFFER_SIZE, format, args); + n = min(n, DISASSEMBLY_PRINT_BUFFER_SIZE); + priv_ubjw_context_write(ctx, buffer,n); + va_end(args); +} +#endif + +static inline void priv_ubjw_write_raw_uint8(ubjw_context_t* ctx, uint8_t out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + priv_ubjw_context_append(ctx, out); +#else + priv_disassembly_print(ctx, "%hhu", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_uint8(ubjw_context_t* ctx, uint8_t out) +{ + priv_ubjw_tag_public(ctx,UBJ_UINT8); + priv_ubjw_write_raw_uint8(ctx, out); +} + +static inline void priv_ubjw_write_raw_int8(ubjw_context_t* ctx, int8_t out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + priv_ubjw_context_append(ctx, *(uint8_t*)&out); +#else + priv_disassembly_print(ctx, "%hhd", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_int8(ubjw_context_t* ctx, int8_t out) +{ + priv_ubjw_tag_public(ctx,UBJ_INT8); + priv_ubjw_write_raw_int8(ctx, out); +} + +static inline void priv_ubjw_write_raw_int16(ubjw_context_t* ctx, int16_t out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + uint8_t buf[2]; + _to_bigendian16(buf, *(uint16_t*)&out); + priv_ubjw_context_write(ctx, buf, 2); +#else + priv_disassembly_print(ctx, "%hd", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_int16(ubjw_context_t* ctx, int16_t out) +{ + priv_ubjw_tag_public(ctx,UBJ_INT16); + priv_ubjw_write_raw_int16(ctx, out); +} +static inline void priv_ubjw_write_raw_int32(ubjw_context_t* ctx, int32_t out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + uint8_t buf[4]; + _to_bigendian32(buf, *(uint32_t*)&out); + priv_ubjw_context_write(ctx, buf, 4); +#else + priv_disassembly_print(ctx, "%ld", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_int32(ubjw_context_t* ctx, int32_t out) +{ + priv_ubjw_tag_public(ctx,UBJ_INT32); + priv_ubjw_write_raw_int32(ctx, out); +} +static inline void priv_ubjw_write_raw_int64(ubjw_context_t* ctx, int64_t out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + uint8_t buf[8]; + _to_bigendian64(buf, *(uint64_t*)&out); + priv_ubjw_context_write(ctx, buf, 8); +#else + priv_disassembly_print(ctx, "%lld", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_int64(ubjw_context_t* ctx, int64_t out) +{ + priv_ubjw_tag_public(ctx,UBJ_INT64); + priv_ubjw_write_raw_int64(ctx, out); +} + +void ubjw_write_high_precision(ubjw_context_t* ctx, const char* hp) +{ + priv_ubjw_tag_public(ctx,UBJ_HIGH_PRECISION); + priv_ubjw_write_raw_string(ctx, hp); +} +UBJ_TYPE ubjw_min_integer_type(int64_t in) +{ + uint64_t mc = llabs(in); + if (mc < 0x80) + { + return UBJ_INT8; + } + else if (in > 0 && mc < 0x100) + { + return UBJ_UINT8; + } + else if (mc < 0x8000) + { + return UBJ_INT16; + } + else if (mc < 0x80000000) + { + return UBJ_INT32; + } + else + { + return UBJ_INT64; + } +} + +void ubjw_write_integer(ubjw_context_t* ctx, int64_t out) +{ + switch (ubjw_min_integer_type(out)) + { + case UBJ_INT8: + ubjw_write_int8(ctx, (int8_t)out); + break; + case UBJ_UINT8: + ubjw_write_uint8(ctx, (uint8_t)out); + break; + case UBJ_INT16: + ubjw_write_int16(ctx, (int16_t)out); + break; + case UBJ_INT32: + ubjw_write_int32(ctx, (int32_t)out); + break; + default: + ubjw_write_int64(ctx, out); + break; + }; +} + +static inline void priv_ubjw_write_raw_float32(ubjw_context_t* ctx, float out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + uint32_t fout = *(uint32_t*)&out; + uint8_t outbuf[4]; + _to_bigendian32(outbuf, fout); + priv_ubjw_context_write(ctx, outbuf, 4); +#else + priv_disassembly_print(ctx, "%g", out); +#endif + priv_disassembly_end(ctx); + +} +void ubjw_write_float32(ubjw_context_t* ctx, float out) +{ + priv_ubjw_tag_public(ctx,UBJ_FLOAT32); + priv_ubjw_write_raw_float32(ctx, out); +} +static inline void priv_ubjw_write_raw_float64(ubjw_context_t* ctx, double out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + uint64_t fout = *(uint64_t*)&out; + uint8_t outbuf[8]; + _to_bigendian64(outbuf, fout); + priv_ubjw_context_write(ctx, outbuf, 8); +#else + priv_disassembly_print(ctx, "%g", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_float64(ubjw_context_t* ctx, double out) +{ + priv_ubjw_tag_public(ctx,UBJ_FLOAT64); + priv_ubjw_write_raw_float64(ctx, out); +} + +void ubjw_write_floating_point(ubjw_context_t* ctx, double out) +{ + //this may not be possible to implement correctly...for now we just write it as a float64' + ubjw_write_float64(ctx,out); +} + +void ubjw_write_noop(ubjw_context_t* ctx) +{ + priv_ubjw_tag_public(ctx,UBJ_NOOP); +} +void ubjw_write_null(ubjw_context_t* ctx) +{ + priv_ubjw_tag_public(ctx,UBJ_NULLTYPE); +} +void ubjw_write_bool(ubjw_context_t* ctx, uint8_t out) +{ + priv_ubjw_tag_public(ctx,(out ? UBJ_BOOL_TRUE : UBJ_BOOL_FALSE)); +} + +void priv_ubjw_begin_container(struct priv_ubjw_container_t* cnt, ubjw_context_t* ctx, UBJ_TYPE typ, size_t count) +{ + cnt->flags=0; + cnt->elements_remaining = count; + cnt->type = typ; + + if (typ != UBJ_MIXED) + { + if (count == 0) + { + //error and return; + } + priv_disassembly_begin(ctx); + priv_ubjw_context_append(ctx, '$'); + priv_disassembly_end(ctx); + + priv_disassembly_begin(ctx); + priv_ubjw_context_append(ctx, UBJI_TYPEC_convert[typ]); + priv_disassembly_end(ctx); + + cnt->flags |= CONTAINER_IS_TYPED; + } + if (count != 0) + { + priv_disassembly_begin(ctx); + priv_ubjw_context_append(ctx, '#'); + priv_disassembly_end(ctx); + + ctx->ignore_container_flags = 1; + ubjw_write_integer(ctx, (int64_t)count); + ctx->ignore_container_flags = 0; + + cnt->flags |= CONTAINER_IS_SIZED; + cnt->elements_remaining = count; + } +} +void ubjw_begin_array(ubjw_context_t* ctx, UBJ_TYPE type, size_t count) +{ + priv_ubjw_tag_public(ctx, UBJ_ARRAY); //todo: should this happen before any erro potential? + struct priv_ubjw_container_t ch; + priv_ubjw_begin_container(&ch, ctx, type, count); + ch.flags |= CONTAINER_IS_UBJ_ARRAY; + priv_ubjw_container_stack_push(ctx, &ch); +} +void ubjw_begin_ndarray(ubjw_context_t* dst, UBJ_TYPE type, const size_t* dims, uint8_t ndims); +void ubjw_write_ndbuffer(ubjw_context_t* dst, const uint8_t* data, UBJ_TYPE type, const size_t* dims, uint8_t ndims); + +void ubjw_begin_object(ubjw_context_t* ctx, UBJ_TYPE type, size_t count) +{ + priv_ubjw_tag_public(ctx, UBJ_OBJECT); + struct priv_ubjw_container_t ch; + priv_ubjw_begin_container(&ch, ctx, type, count); + ch.flags |= CONTAINER_EXPECTS_KEY | CONTAINER_IS_UBJ_OBJECT; + priv_ubjw_container_stack_push(ctx, &ch); +} +void ubjw_write_key(ubjw_context_t* ctx, const char* key) +{ + if (ctx->head->flags & CONTAINER_EXPECTS_KEY && ctx->head->flags & CONTAINER_IS_UBJ_OBJECT) + { + priv_disassembly_indent(ctx); + priv_ubjw_write_raw_string(ctx, key); + ctx->head->flags ^= CONTAINER_EXPECTS_KEY; //turn off container + } + else + { + //error unexpected key + } +} +void ubjw_end(ubjw_context_t* ctx) +{ + struct priv_ubjw_container_t ch = priv_ubjw_container_stack_pop(ctx); + if ((ch.flags & CONTAINER_IS_UBJ_OBJECT) && !(ch.flags & CONTAINER_EXPECTS_KEY)) + { + //error expected value + } + priv_disassembly_indent(ctx); + priv_ubjw_context_finish_container(ctx, &ch); +} + + +static inline void priv_ubjw_write_byteswap(ubjw_context_t* ctx, const uint8_t* data, int sz, size_t count) +{ + uint8_t buf[BUFFER_OUT_SIZE]; + + size_t i; + size_t nbytes = sz*count; + for (i = 0; i < nbytes; i+=BUFFER_OUT_SIZE) + { + size_t npass = min(nbytes - i, BUFFER_OUT_SIZE); + memcpy(buf, data + i, npass); + buf_endian_swap(buf, sz, npass/sz); + priv_ubjw_context_write(ctx, buf, npass); + } +} +void ubjw_write_buffer(ubjw_context_t* ctx, const uint8_t* data, UBJ_TYPE type, size_t count) +{ + int typesz = UBJI_TYPE_size[type]; + if (typesz < 0) + { + //error cannot write an STC buffer of this type. + } + ubjw_begin_array(ctx, type, count); + if (type == UBJ_STRING || type == UBJ_HIGH_PRECISION) + { + const char** databufs = (const char**)data; + size_t i; + for (i = 0; i < count; i++) + { + priv_ubjw_write_raw_string(ctx, databufs[i]); + } + } +#ifndef UBJW_DISASSEMBLY_MODE + else if (typesz == 1 || _is_bigendian()) + { + size_t n = typesz*count; + priv_ubjw_context_write(ctx, data, typesz*count); + } + else if (typesz > 1) //and not big-endian + { + priv_ubjw_write_byteswap(ctx, data,typesz,count); + } +#else + else + { + size_t i; + for (i = 0; i < count; i++) + { + ubjr_dynamic_t dyn = priv_ubjr_pointer_to_dynamic(type, data + i*typesz); + ubjrw_write_dynamic(ctx, dyn, 0); + } + } +#endif + ubjw_end(ctx); +} -- cgit v1.2.3