Tue, 09 Dec 2025 18:24:48 +0100
implement json primitives serialization
| dbutils/Makefile | file | annotate | diff | comparison | revisions | |
| dbutils/dbutils/dbutils.h | file | annotate | diff | comparison | revisions | |
| dbutils/dbutils/json.h | file | annotate | diff | comparison | revisions | |
| dbutils/field.c | file | annotate | diff | comparison | revisions | |
| dbutils/json.c | file | annotate | diff | comparison | revisions | |
| dbutils/json.h | file | annotate | diff | comparison | revisions | |
| test/Makefile | file | annotate | diff | comparison | revisions | |
| test/database.c | file | annotate | diff | comparison | revisions | |
| test/database.h | file | annotate | diff | comparison | revisions | |
| test/json.c | file | annotate | diff | comparison | revisions | |
| test/json.h | file | annotate | diff | comparison | revisions | |
| test/main.c | file | annotate | diff | comparison | revisions | |
| testdata.sql | file | annotate | diff | comparison | revisions | |
| ucx/Makefile | file | annotate | diff | comparison | revisions |
--- a/dbutils/Makefile Tue Dec 09 14:47:35 2025 +0100 +++ b/dbutils/Makefile Tue Dec 09 18:24:48 2025 +0100 @@ -35,6 +35,7 @@ SRC += db.c SRC += object.c SRC += sqlite.c +SRC += json.c OBJ = $(SRC:%.c=../build/dbutils/%$(OBJ_EXT))
--- a/dbutils/dbutils/dbutils.h Tue Dec 09 14:47:35 2025 +0100 +++ b/dbutils/dbutils/dbutils.h Tue Dec 09 18:24:48 2025 +0100 @@ -54,6 +54,12 @@ typedef int(*DBUFieldDefInitFunc)(DBUField *f, const CxAllocator *a, DBUObject obj); typedef int(*DBUFieldInitFunc)(DBUField *f, const CxAllocator *a, DBUObject obj, const char *value, size_t length); +typedef bool(*DBUFieldToBoolFunc)(DBUField *f, DBUObject obj); +typedef int64_t(*DBUFieldToInt64Func)(DBUField *f, DBUObject obj); +typedef uint64_t(*DBUFieldToUInt64Func)(DBUField *f, DBUObject obj); +typedef uint64_t(*DBUFieldToUInt64Func)(DBUField *f, DBUObject obj); +typedef cxmutstr(*DBUFieldToStringFunc)(DBUField *f, DBUObject obj, const CxAllocator *a); +typedef double(*DBUFieldToDoubleFunc)(DBUField *f, DBUObject obj); struct DBUContext { /* @@ -180,6 +186,49 @@ */ int (*initObjValue)(DBUField *f, const CxAllocator *a, DBUObject obj, void *child); + + /* + * get the field value as a boolean + * + * This should only be implemented, if the field is a boolean + */ + bool (*toBool)(DBUField *f, DBUObject obj); + + /* + * get the field value as an int64 (optional) + * + * This should only be implemented, if the field is a signed integer + */ + int64_t (*toInt64)(DBUField *f, DBUObject obj); + + /* + * get the field value as an int64 (optional) + * + * This should only be implemented, if the field is a unsigned integer + */ + uint64_t (*toUInt64)(DBUField *f, DBUObject obj); + + /* + * get the field value as a string(optional) + * + * This should only be implemented, if the field is represented as a string + */ + cxmutstr (*toString)(DBUField *f, DBUObject obj, const CxAllocator *a); + + /* + * get the field value as a binary (optional) + * + * This should only be implemented, if the field contains binary data + */ + cxmutstr (*toBinary)(DBUField *f, DBUObject obj, const CxAllocator *a); + + /* + * get the field value as double (optional) + * + * This should only be implemented, if the field is a float/double + */ + double (*toDouble)(DBUField *f, DBUObject obj); + /* * destructor (optional) *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbutils/dbutils/json.h Tue Dec 09 18:24:48 2025 +0100 @@ -0,0 +1,56 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBDBU_JSON_H +#define LIBDBU_JSON_H + +#include <cx/json.h> +#include "dbutils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +CxJsonValue* dbuObjectToJson(DBUClass *type, void *obj, const CxAllocator *a); +cxmutstr dbuObjectToJsonString(DBUClass *type, void *obj, const CxAllocator *a); + +CxJsonValue* dbuListToJson(DBUClass *type, CxList *list, const CxAllocator *a); +CxJsonValue* dbuArrayToJson(DBUClass *type, void **objArray, size_t size, const CxAllocator *a); + +cxmutstr dbuListToJsonString(DBUClass *type, CxList *list, const CxAllocator *a); +cxmutstr dbuArrayToJsonString(DBUClass *type, void **objArray, size_t size, const CxAllocator *a); + +CxJsonValue* dbuObjectToJson2(DBUClass *type, void *obj, const CxAllocator *a, bool setNull, bool forceString); + + +#ifdef __cplusplus +} +#endif + +#endif /* LIBDBU_JSON_H */ +
--- a/dbutils/field.c Tue Dec 09 14:47:35 2025 +0100 +++ b/dbutils/field.c Tue Dec 09 18:24:48 2025 +0100 @@ -177,6 +177,11 @@ return 0; } +static int64_t field_get_int8(DBUOffsetField *f, DBUObject obj) { + int8_t *i = (int8_t*)(obj + f->offset); + return *i; +} + static int field_init_uint8( DBUOffsetField *f, const CxAllocator *a, @@ -195,6 +200,11 @@ return 0; } +static uint64_t field_get_uint8(DBUOffsetField *f, DBUObject obj) { + uint8_t *i = (uint8_t*)(obj + f->offset); + return *i; +} + static int field_init_int16( DBUOffsetField *f, const CxAllocator *a, @@ -213,6 +223,11 @@ return 0; } +static int64_t field_get_int16(DBUOffsetField *f, DBUObject obj) { + int16_t *i = (int16_t*)(obj + f->offset); + return *i; +} + static int field_init_uint16( DBUOffsetField *f, const CxAllocator *a, @@ -231,6 +246,11 @@ return 0; } +static uint64_t field_get_uint16(DBUOffsetField *f, DBUObject obj) { + uint16_t *i = (uint16_t*)(obj + f->offset); + return *i; +} + static int field_init_int32( DBUOffsetField *f, const CxAllocator *a, @@ -249,6 +269,11 @@ return 0; } +static int64_t field_get_int32(DBUOffsetField *f, DBUObject obj) { + int32_t *i = (int32_t*)(obj + f->offset); + return *i; +} + static int field_init_uint32( DBUOffsetField *f, const CxAllocator *a, @@ -267,6 +292,12 @@ return 0; } +static uint64_t field_get_uint32(DBUOffsetField *f, DBUObject obj) { + uint32_t *i = (uint32_t*)(obj + f->offset); + return *i; +} + + static int field_init_int64( DBUOffsetField *f, const CxAllocator *a, @@ -282,6 +313,11 @@ return 0; } +static int64_t field_get_int64(DBUOffsetField *f, DBUObject obj) { + int64_t *i = (int64_t*)(obj + f->offset); + return *i; +} + static int field_init_uint64( DBUOffsetField *f, const CxAllocator *a, @@ -297,6 +333,11 @@ return 0; } +static uint64_t field_get_uint64(DBUOffsetField *f, DBUObject obj) { + uint64_t *i = (uint64_t*)(obj + f->offset); + return *i; +} + static int field_init_size( DBUOffsetField *f, const CxAllocator *a, @@ -308,10 +349,15 @@ if(!str2uint(value, &i)) { return 1; } - *(uint64_t*)(obj+f->offset) = i; + *(size_t*)(obj+f->offset) = i; return 0; } +static uint64_t field_get_size(DBUOffsetField *f, DBUObject obj) { + size_t *i = (size_t*)(obj + f->offset); + return *i; +} + static int field_init_ssize( DBUOffsetField *f, const CxAllocator *a, @@ -327,6 +373,11 @@ return 0; } +static int64_t field_get_ssize(DBUOffsetField *f, DBUObject obj) { + ssize_t *i = (ssize_t*)(obj + f->offset); + return *i; +} + static int field_init_bool( DBUOffsetField *f, const CxAllocator *a, @@ -345,6 +396,11 @@ return 0; } +static bool field_get_bool(DBUOffsetField *f, DBUObject obj) { + bool *i = (bool*)(obj + f->offset); + return *i; +} + static int field_init_float( DBUOffsetField *f, const CxAllocator *a, @@ -361,6 +417,11 @@ return 0; } +static double field_get_float(DBUOffsetField *f, DBUObject obj) { + float *d = (float*)(obj + f->offset); + return *d; +} + static int field_init_double( DBUOffsetField *f, const CxAllocator *a, @@ -377,6 +438,10 @@ return 0; } +static double field_get_double(DBUOffsetField *f, DBUObject obj) { + double *d = (double*)(obj + f->offset); + return *d; +} static int field_init_str( DBUOffsetField *f, @@ -393,6 +458,12 @@ return 0; } + +static cxmutstr field_get_str(DBUOffsetField *f, DBUObject obj, const CxAllocator *a) { + char **s = (char**)(obj + f->offset); + return cx_strdup_a(a, cx_str(*s)); +} + static int field_init_cxmutstr( DBUOffsetField *f, const CxAllocator *a, @@ -408,6 +479,11 @@ return 0; } +static cxmutstr field_get_cxstr(DBUOffsetField *f, DBUObject obj, const CxAllocator *a) { + cxmutstr *s = (cxmutstr*)(obj + f->offset); + return cx_strdup_a(a, *s); +} + static int field_init_str_size( DBUObjLenField *f, const CxAllocator *a, @@ -424,6 +500,12 @@ return 0; } +static cxmutstr field_get_str_len(DBUObjLenField *f, DBUObject obj, const CxAllocator *a) { + char *ptr = *(char**)(obj+f->offset_obj); + size_t len = *(size_t*)(obj+f->offset_len); + return cx_strdup_a(a, cx_strn(ptr, len)); +} + static int field_init_str_intlen( DBUObjLenField *f, const CxAllocator *a, @@ -440,6 +522,13 @@ return 0; } +static cxmutstr field_get_str_intlen(DBUObjLenField *f, DBUObject obj, const CxAllocator *a) { + char *ptr = *(char**)(obj+f->offset_obj); + int len = *(int*)(obj+f->offset_len); + return cx_strdup_a(a, cx_strn(ptr, len)); +} + + static int field_init_bytes_size( DBUObjLenField *f, const CxAllocator *a, @@ -494,7 +583,7 @@ return (DBUField*)field; } -static void add_offset_def_field( +static DBUField* add_offset_def_field( DBUClass *cls, const char *name, off_t offset, @@ -503,10 +592,12 @@ bool nonnull, union DBUDefValue def) { - dbuClassAddField(cls, name, create_offset_def_field(offset, def_init, init, nonnull, def)); + DBUField *field = create_offset_def_field(offset, def_init, init, nonnull, def); + dbuClassAddField(cls, name, field); + return field; } -static void add_offset_fk_field( +static DBUField* add_offset_fk_field( DBUClass *cls, const char *name, off_t offset, @@ -514,7 +605,9 @@ DBUFieldInitFunc init, bool nonnull) { - dbuClassAddFKField(cls, name, create_offset_def_field(offset, NULL, init, nonnull, (union DBUDefValue){ 0 }), fkcls); + DBUField *field = create_offset_def_field(offset, NULL, init, nonnull, (union DBUDefValue){ 0 }); + dbuClassAddFKField(cls, name, field, fkcls); + return field; } static void add_bool( @@ -530,6 +623,7 @@ field->field.query_length = false; field->offset = offset; field->def.def = 0; + field->field.toInt64 = (DBUFieldToInt64Func)field_get_bool; dbuClassAddField(cls, name, (DBUField*)field); } @@ -548,24 +642,19 @@ return (DBUField*)field; } -static void add_offset_str_field( +static DBUField* add_offset_str_field( DBUClass *cls, const char *name, DBUFieldInitFunc init, off_t offset, bool nonnull) { - DBUOffsetField *field = calloc(1, sizeof(DBUOffsetField)); - field->field.initDefaultValue = NULL; - field->field.initValue = (DBUFieldInitFunc)init; - field->field.nonnull = nonnull; - field->field.query_length = true; - field->offset = offset; - field->def.def = 0; - dbuClassAddField(cls, name, create_offset_str_field(init, offset, nonnull)); + DBUField *f = create_offset_str_field(init, offset, nonnull); + dbuClassAddField(cls, name, f); + return f; } -static void add_objlen_field( +static DBUField* add_objlen_field( DBUClass *cls, const char *name, DBUFieldInitFunc init, @@ -582,40 +671,53 @@ field->offset_obj = offset; field->offset_len = size_offset; dbuClassAddField(cls, name, (DBUField*)field); + return (DBUField*)field; } DBUField* dbuFieldCreateInt32(off_t offset) { - return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int32, false, (union DBUDefValue){ 0 }); + DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int32, false, (union DBUDefValue){ 0 }); + f->toInt64 = (DBUFieldToInt64Func)field_get_int32; + return f; } DBUField* dbuFieldCreateUInt32(off_t offset) { - return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint32, false, (union DBUDefValue){ 0 }); + DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint32, false, (union DBUDefValue){ 0 }); + f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32; + return f; } DBUField* dbuFieldCreateInt64(off_t offset) { - return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int64, false, (union DBUDefValue){ 0 }); + DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int64, false, (union DBUDefValue){ 0 }); + f->toInt64 = (DBUFieldToInt64Func)field_get_int64; + return f; } DBUField* dbuFieldCreateUInt64(off_t offset) { - return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint64, false, (union DBUDefValue){ 0 }); + DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint64, false, (union DBUDefValue){ 0 }); + f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint64; + return f; } DBUField* dbuFieldCreateString(off_t offset) { - return create_offset_str_field((DBUFieldInitFunc)field_init_str, offset, false); + DBUField *f = create_offset_str_field((DBUFieldInitFunc)field_init_str, offset, false); + f->toString = (DBUFieldToStringFunc)field_get_str; + return f; } DBUField* dbuFieldCreateCxMutStr(off_t offset) { - return create_offset_str_field((DBUFieldInitFunc)field_init_cxmutstr, offset, false); + DBUField *f = create_offset_str_field((DBUFieldInitFunc)field_init_cxmutstr, offset, false); + f->toString = (DBUFieldToStringFunc)field_get_cxstr; + return f; } /* -------------------- PUBLIC -------------------- */ void dbuClassAddInt(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -623,10 +725,11 @@ (DBUFieldInitFunc)field_init_int, nonnull, (union DBUDefValue){ 0 }); + f->toInt64 = (DBUFieldToInt64Func)field_get_int32; } void dbuClassAddUInt(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -634,10 +737,11 @@ (DBUFieldInitFunc)field_init_uint, nonnull, (union DBUDefValue){ 0 }); + f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32; } void dbuClassAddInt8(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -645,10 +749,11 @@ (DBUFieldInitFunc)field_init_int8, nonnull, (union DBUDefValue){ 0 }); + f->toInt64 = (DBUFieldToInt64Func)field_get_int8; } void dbuClassAddUInt8(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -656,10 +761,11 @@ (DBUFieldInitFunc)field_init_uint8, nonnull, (union DBUDefValue){ 0 }); + f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint8; } void dbuClassAddInt16(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -667,10 +773,11 @@ (DBUFieldInitFunc)field_init_int16, nonnull, (union DBUDefValue){ 0 }); + f->toInt64 = (DBUFieldToInt64Func)field_get_int16; } void dbuClassAddUInt16(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -678,10 +785,11 @@ (DBUFieldInitFunc)field_init_uint16, nonnull, (union DBUDefValue){ 0 }); + f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint16; } void dbuClassAddInt32(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -689,10 +797,11 @@ (DBUFieldInitFunc)field_init_int32, nonnull, (union DBUDefValue){ 0 }); + f->toInt64 = (DBUFieldToInt64Func)field_get_int32; } void dbuClassAddUInt32(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -700,10 +809,11 @@ (DBUFieldInitFunc)field_init_uint32, nonnull, (union DBUDefValue){ 0 }); + f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32; } void dbuClassAddInt64(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -711,10 +821,11 @@ (DBUFieldInitFunc)field_init_int64, nonnull, (union DBUDefValue){ 0 }); + f->toInt64 = (DBUFieldToInt64Func)field_get_int64; } void dbuClassAddUInt64(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -722,10 +833,11 @@ (DBUFieldInitFunc)field_init_uint64, nonnull, (union DBUDefValue){ 0 }); + f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint64; } void dbuClassAddSize(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -733,10 +845,11 @@ (DBUFieldInitFunc)field_init_size, nonnull, (union DBUDefValue){ 0 }); + f->toUInt64 = (DBUFieldToUInt64Func)field_get_size; } void dbuClassAddSSize(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -744,6 +857,7 @@ (DBUFieldInitFunc)field_init_ssize, nonnull, (union DBUDefValue){ 0 }); + f->toInt64 = (DBUFieldToInt64Func)field_get_ssize; } void dbuClassAddBool(DBUClass *cls, const char *name, off_t offset, bool nonnull) { @@ -755,7 +869,7 @@ } void dbuClassAddFloat(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -763,10 +877,11 @@ (DBUFieldInitFunc)field_init_float, nonnull, (union DBUDefValue){ 0 }); + f->toDouble = (DBUFieldToDoubleFunc)field_get_float; } void dbuClassAddDouble(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_def_field( + DBUField *f = add_offset_def_field( cls, name, offset, @@ -774,64 +889,71 @@ (DBUFieldInitFunc)field_init_double, nonnull, (union DBUDefValue){ 0 }); + f->toDouble = (DBUFieldToDoubleFunc)field_get_double; } void dbuClassAddString(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_str_field( + DBUField *f = add_offset_str_field( cls, name, (DBUFieldInitFunc)field_init_str, offset, nonnull); + f->toString = (DBUFieldToStringFunc)field_get_str; } void dbuClassAddCXMutStr(DBUClass *cls, const char *name, off_t offset, bool nonnull) { - add_offset_str_field( + DBUField *f = add_offset_str_field( cls, name, (DBUFieldInitFunc)field_init_cxmutstr, offset, nonnull); + f->toString = (DBUFieldToStringFunc)field_get_cxstr; } void dbuClassAddStringSize(DBUClass *cls, const char *name, off_t offset, off_t size_offset, bool nonnull) { - add_objlen_field( + DBUField *f = add_objlen_field( cls, name, (DBUFieldInitFunc)field_init_str_size, offset, size_offset, nonnull); + f->toString = (DBUFieldToStringFunc)field_get_str_len; } void dbuClassAddStringIntLen(DBUClass *cls, const char *name, off_t offset, off_t int_offset, bool nonnull) { - add_objlen_field( + DBUField *f = add_objlen_field( cls, name, (DBUFieldInitFunc)field_init_str_intlen, offset, int_offset, nonnull); + f->toString = (DBUFieldToStringFunc)field_get_str_intlen; } void dbuClassAddBuf(DBUClass *cls, const char *name, off_t offset, off_t size_offset, bool nonnull) { - add_objlen_field( + DBUField *f = add_objlen_field( cls, name, (DBUFieldInitFunc)field_init_bytes_size, offset, size_offset, nonnull); + f->toBinary = (DBUFieldToStringFunc)field_get_str_len; } void dbuClassAddBufIntLen(DBUClass *cls, const char *name, off_t offset, off_t int_offset, bool nonnull) { - add_objlen_field( + DBUField *f = add_objlen_field( cls, name, (DBUFieldInitFunc)field_init_bytes_intlen, offset, int_offset, nonnull); + f->toBinary = (DBUFieldToStringFunc)field_get_str_intlen; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbutils/json.c Tue Dec 09 18:24:48 2025 +0100 @@ -0,0 +1,97 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "json.h" + +CxJsonValue* dbuObjectToJson(DBUClass *type, void *obj, const CxAllocator *a) { + return dbuObjectToJson2(type, obj, a, false, false); +} + +CxJsonValue* dbuObjectToJson2(DBUClass *type, void *obj, const CxAllocator *a, bool setNull, bool forceString) { + if(obj == NULL) { + return cxJsonCreateLiteral(a, CX_JSON_NULL); + } + if(!a) { + a = cxDefaultAllocator; + } + + CxJsonValue *value = cxJsonCreateObj(a); + if(!value) { + return NULL; + } + + // add all primitive values + CxMapIterator i = cxMapIteratorValues(type->fields); + cx_foreach(DBUField *, field, i) { + CxJsonValue *child = NULL; + if(field->toBool) { + bool b = field->toBool(field, obj); + child = cxJsonCreateLiteral(a, b ? CX_JSON_TRUE : CX_JSON_FALSE); + } else if(field->toInt64) { + int64_t i = field->toInt64(field, obj); + child = cxJsonCreateInteger(a, i); + } else if(field->toUInt64) { + uint64_t u = field->toUInt64(field, obj); + child = cxJsonCreateInteger(a, (int64_t)u); + } else if(field->toDouble) { + double d = field->toDouble(field, obj); + child = cxJsonCreateNumber(a, d); + } else if(field->toString) { + cxmutstr s = field->toString(field, obj, a); + if(s.ptr) { + // we don't want to copy the string again, therefore + // we can't use cxJsonCreateString + child = cxMalloc(a, sizeof(CxJsonValue)); + if(!child) { + cxFree(a, s.ptr); + return NULL; + } + child->allocator = a; + child->type = CX_JSON_STRING; + child->value.string = s; + } + } else if(field->toBinary) { + // TODO + } else { + continue; // non-serializable field + } + + if(!child) { + cxJsonValueFree(value); + return NULL; + } + + if(cxJsonObjPut(value, field->name, child)) { + cxJsonValueFree(child); + cxJsonValueFree(value); + return NULL; + } + } + + return value; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbutils/json.h Tue Dec 09 18:24:48 2025 +0100 @@ -0,0 +1,46 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DBU_JSON_H +#define DBU_JSON_H + +#include "dbutils/json.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif /* DBU_JSON_H */ +
--- a/test/Makefile Tue Dec 09 14:47:35 2025 +0100 +++ b/test/Makefile Tue Dec 09 18:24:48 2025 +0100 @@ -33,6 +33,7 @@ SRC = main.c SRC += database.c +SRC += json.c OBJ = $(SRC:%.c=../build/test/%$(OBJ_EXT)) @@ -42,7 +43,7 @@ all: ../build/bin/test $(TESTBIN): $(OBJ) $(LIBDBUTILS) - $(CC) -o $(TESTBIN) $(OBJ) -L$(BUILD_ROOT)/build/lib -ldbutils -lucx $(LDFLAGS) $(DBU_LDFLAGS) + $(CC) -o $(TESTBIN) $(OBJ) -L$(BUILD_ROOT)/build/lib -ldbutils ../build/lib/libucx.a $(LDFLAGS) $(DBU_LDFLAGS) ../build/test/%$(OBJ_EXT): %.c $(CC) $(CFLAGS) $(DBU_CFLAGS) -o $@ -c $<
--- a/test/database.c Tue Dec 09 14:47:35 2025 +0100 +++ b/test/database.c Tue Dec 09 18:24:48 2025 +0100 @@ -253,6 +253,8 @@ CX_TEST_ASSERT(!cx_strcmp(a1->zip, "23456")); CX_TEST_ASSERT(!cx_strcmp(a0->city, "city 17")); CX_TEST_ASSERT(!cx_strcmp(a1->city, "city 18")); + + dbuObjectBuilderDestroy(builder); } cxMempoolFree(mp);
--- a/test/database.h Tue Dec 09 14:47:35 2025 +0100 +++ b/test/database.h Tue Dec 09 18:24:48 2025 +0100 @@ -41,9 +41,7 @@ #endif int init_test_db(void); - int init_db_tests(void); - void cleanup_db_tests(void); CX_TEST(testSqliteConnection);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/json.c Tue Dec 09 18:24:48 2025 +0100 @@ -0,0 +1,138 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBLIITY OF SUCH DAMAGE. + */ + +#include "json.h" + +#include <stdbool.h> +#include <cx/buffer.h> + + +typedef struct Test1 { + char *str; + cxmutstr str2; + int8_t i8; + uint8_t u8; + int64_t i64; + int16_t i16; + int32_t i32; + uint32_t u32; + uint16_t u16; + uint64_t u64; + bool bt; + bool bf; + double d; +} Test1; + +static DBUContext *ctx; +static DBUClass *test1_class; + +int init_json_tests(void) { + ctx = dbuContextCreate(); + + test1_class = dbuRegisterClassWithoutPK(ctx, "test1", sizeof(Test1)); + dbuClassAdd(test1_class, Test1, str); + dbuClassAdd(test1_class, Test1, str2); + dbuClassAdd(test1_class, Test1, i8); + dbuClassAdd(test1_class, Test1, u8); + dbuClassAdd(test1_class, Test1, i16); + dbuClassAdd(test1_class, Test1, u16); + dbuClassAdd(test1_class, Test1, i32); + dbuClassAdd(test1_class, Test1, u32); + dbuClassAdd(test1_class, Test1, i64); + dbuClassAdd(test1_class, Test1, u64); + dbuClassAdd(test1_class, Test1, bt); + dbuClassAdd(test1_class, Test1, bf); + dbuClassAdd(test1_class, Test1, d); +} + +void cleanup_json_tests(void) { + dbuContextFree(ctx); +} + +CX_TEST(testObjectToJsonSimple) { + Test1 test; + test.str = "hello str"; + test.str2 = cx_mutstr("hello cx str"); + test.i8 = 32; + test.u8 = 250; + test.i16 = -5533; + test.u16 = 8000; + test.i32 = -123456; + test.u32 = 4000000000; + test.i64 = -999999999999L; + test.u64 = 2501; + test.bt = true; + test.bf = false; + test.d = 431.15; + + CX_TEST_DO { + CxJsonValue *value = dbuObjectToJson(test1_class, &test, NULL); + + CX_TEST_ASSERT(value); + CX_TEST_ASSERT(value->type == CX_JSON_OBJECT); + + CxJsonValue *str = cxJsonObjGet(value, "str"); + CxJsonValue *str2 = cxJsonObjGet(value, "str2"); + CxJsonValue *i8 = cxJsonObjGet(value, "i8"); + CxJsonValue *u8 = cxJsonObjGet(value, "u8"); + CxJsonValue *i16 = cxJsonObjGet(value, "i16"); + CxJsonValue *u16 = cxJsonObjGet(value, "u16"); + CxJsonValue *i32 = cxJsonObjGet(value, "i32"); + CxJsonValue *u32 = cxJsonObjGet(value, "u32"); + CxJsonValue *i64 = cxJsonObjGet(value, "i64"); + CxJsonValue *u64 = cxJsonObjGet(value, "u64"); + CxJsonValue *bt = cxJsonObjGet(value, "bt"); + CxJsonValue *bf = cxJsonObjGet(value, "bf"); + CxJsonValue *d = cxJsonObjGet(value, "d"); + + CX_TEST_ASSERT(str && str->type == CX_JSON_STRING); + CX_TEST_ASSERT(str2 && str2->type == CX_JSON_STRING); + CX_TEST_ASSERT(i8 && i8->type == CX_JSON_INTEGER); + CX_TEST_ASSERT(u8 && u8->type == CX_JSON_INTEGER); + CX_TEST_ASSERT(i16 && i16->type == CX_JSON_INTEGER); + CX_TEST_ASSERT(u16 && u16->type == CX_JSON_INTEGER); + CX_TEST_ASSERT(i32 && i32->type == CX_JSON_INTEGER); + CX_TEST_ASSERT(u32 && u32->type == CX_JSON_INTEGER); + CX_TEST_ASSERT(i64 && i64->type == CX_JSON_INTEGER); + CX_TEST_ASSERT(u64 && u64->type == CX_JSON_INTEGER); + CX_TEST_ASSERT(d && d->type == CX_JSON_NUMBER); + + CX_TEST_ASSERT(!cx_strcmp(str->value.string, test.str)); + CX_TEST_ASSERT(!cx_strcmp(str2->value.string, test.str2)); + CX_TEST_ASSERT(i8->value.integer == test.i8); + CX_TEST_ASSERT(u8->value.integer == test.u8); + CX_TEST_ASSERT(i16->value.integer == test.i16); + CX_TEST_ASSERT(u16->value.integer == test.u16); + CX_TEST_ASSERT(i32->value.integer == test.i32); + CX_TEST_ASSERT(u32->value.integer == test.u32); + CX_TEST_ASSERT(i64->value.integer == test.i64); + CX_TEST_ASSERT(u64->value.integer == test.u64); + CX_TEST_ASSERT(d->value.number < test.d + 0.1 && d->value.number > test.d - 0.1); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/json.h Tue Dec 09 18:24:48 2025 +0100 @@ -0,0 +1,51 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBLIITY OF SUCH DAMAGE. + */ + +#ifndef TEST_JSON_H +#define TEST_JSON_H + +#include <dbutils/json.h> + +#include <cx/test.h> + +#ifdef __cplusplus +extern "C" { +#endif + +int init_json_tests(void); +void cleanup_json_tests(void); + +CX_TEST(testObjectToJsonSimple); + + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_JSON_H */ +
--- a/test/main.c Tue Dec 09 14:47:35 2025 +0100 +++ b/test/main.c Tue Dec 09 18:24:48 2025 +0100 @@ -38,6 +38,7 @@ #include <cx/printf.h> #include "database.h" +#include "json.h" const char *sql_create_table_person = "create table if not exists Person (" @@ -120,6 +121,7 @@ int main(int argc, char **argv) { CxTestSuite *suite = cx_test_suite_new("dbu"); + init_json_tests(); #ifdef DBU_SQLITE if(init_test_db() || init_db_tests()) { return 1; @@ -132,6 +134,7 @@ cx_test_register(suite, testSqlExecParam); cx_test_register(suite, testSingleTableQuery); #endif + cx_test_register(suite, testObjectToJsonSimple); cx_test_run_stdout(suite); @@ -139,6 +142,7 @@ #ifdef DBU_SQLITE cleanup_db_tests(); #endif + cleanup_json_tests(); /*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testdata.sql Tue Dec 09 18:24:48 2025 +0100 @@ -0,0 +1,40 @@ + +create table Person( + person_id integer primary key autoincrement, + name text, + email text, + age integer, + iscustomer integer, + hash integer, + address_id integer +); + +create table Address( + address_id integer primary key autoincrement, + street text, + zip text, + city text +); + +create table Role( + role_id integer primary key autoincrement, + person_id integer, + name text +); + +insert into address (street, zip, city) +values +('street 1', '12343', 'city 17'), +('street 2', '23456', 'city 18'); + +insert into person (name, email, age, iscustomer, hash, address_id) values +('alice', 'alice@example.com', 30, 0, 123456789, (select address_id from address where street = 'street 1')), +('bob', 'bob@example.com', 25, 1, 987654321, (select address_id from address where street = 'street 2')); + +insert into role (person_id, name) +values +(1, 'finance'), +(1, 'dev'), +(1, 'manager'), +(2, 'extern'); +
--- a/ucx/Makefile Tue Dec 09 14:47:35 2025 +0100 +++ b/ucx/Makefile Tue Dec 09 18:24:48 2025 +0100 @@ -38,6 +38,7 @@ SRC += hash_map.c SRC += iterator.c SRC += linked_list.c +SRC += kv_list.c SRC += list.c SRC += map.c SRC += printf.c @@ -46,21 +47,19 @@ SRC += streams.c SRC += properties.c SRC += json.c -SRC += kv_list.c - OBJ = $(SRC:%.c=../build/ucx/%$(OBJ_EXT)) -UCX_LIB = ../build/lib/libucx$(LIB_EXT) +UCX_LIB = ../build/lib/libucx.a -all: ../build/ucx $(UCX_LIB) +all: $(UCX_LIB) $(UCX_SHLIB) $(UCX_LIB): $(OBJ) - $(AR) $(ARFLAGS) $(UCX_LIB) $(OBJ) + $(AR) $(ARFLAGS) $@ $(OBJ) ../build/ucx: mkdir -p ../build/ucx ../build/ucx/%$(OBJ_EXT): %.c - $(CC) $(CFLAGS) -o $@ -c $< + $(CC) $(CFLAGS) $(SHLIB_CFLAGS) -o $@ -c $<