diff --git a/src/dump.c b/src/dump.c new file mode 100644 index 0000000..7dbb5e6 --- /dev/null +++ b/src/dump.c @@ -0,0 +1,234 @@ +/* This software is licensed by the MIT License, see LICENSE file */ +/* Copyright © 2022 Gregory Lirent */ + +#include + +#include "../modules/libcdsb/include/map.h" +#include "../modules/libcdsb/include/dict.h" +#include "../modules/libcdsb/include/list.h" +#include "../modules/libcdsb/include/array.h" +#include "../modules/libcdsb/include/set.h" + +#include "../modules/libcdsb/modules/libunic/include.h" + +#include "buffer.h" + +typedef struct { + FILE* stream; + indent_t* indent; + size_t level; + bool first_el; +} data_t; + +#define dict_callback (map_access_callback) libcjsonp_builtin_dict_callback +#define list_callback (list_access_callback)libcjsonp_builtin_list_callback +#define set_callback (vset_access_callback)libcjsonp_builtin_set_callback + +#define write_indent libcjsonp_builtin_write_indent +#define write_and_wrap libcjsonp_builtin_write_and_wrap +#define write_iterable libcjsonp_builtin_write_iterable +#define write_value libcjsonp_builtin_write_value +#define write_char(x, v) fputc(v ,x) +#define write(x, v) fputs(v, x) + +static int libcjsonp_builtin_dict_callback(const void* k, vtype kt, const void* v, vtype vt, data_t* data); +static int libcjsonp_builtin_list_callback(const void* v, ssize_t index, vtype vt, data_t* data); +static int libcjsonp_builtin_set_callback (const void* v, vtype vt, data_t* data); + +/*#####################################################################################################################*/ + +static int libcjsonp_builtin_write_indent (FILE* x, indent_t* indent, size_t level) { + int ret = 0; + while (ret >= 0 && level--) ret = write(x, indent->indent); + + return (ret < 0) ? ret : 0; +} + +static int libcjsonp_builtin_write_iterable(FILE* x, const void* s, vtype t, indent_t* indent, size_t level) { + data_t data; + char bracket; + int ret; + + data.stream = x; + data.indent = indent; + data.level = level + 1; + data.first_el = true; + + switch (t) { default: + #ifndef NDEBUG + abort(); + #endif + case VTYPE_DICT: ret = write_char(x, bracket = '{'); + if (ret >= 0) ret = libcdsb_dict_foreach((void*)s, &data, dict_callback, false); + break; + + case VTYPE_LIST: ret = write_char(x, bracket = '['); + if (ret >= 0) ret = libcdsb_list_foreach((void*)s, &data, list_callback, false); + break; + + case VTYPE_ARRAY: ret = write_char(x, bracket = '['); + if (ret >= 0) ret = libcdsb_array_foreach((void*)s, &data, list_callback, false); + break; + + case VTYPE_MAP: ret = write_char(x, bracket = '{'); + if (ret >= 0) ret = libcdsb_map_foreach((void*)s, &data, dict_callback, RBFOREACH_INORDER, false); + break; + + case VTYPE_SET: ret = write_char(x, bracket = '['); + if (ret >= 0) ret = libcdsb_vset_foreach((void*)s, &data, set_callback, RBFOREACH_INORDER, false); + break; + } + + if (indent->nmemb) { + if (ret >= 0) ret = write_char (x, '\n'); + if (ret >= 0) ret = write_indent(x, indent, level); + } + + if (ret >= 0) ret = write_char (x, bracket + 2); + + return (ret < 0) ? ret : 0; +} + +static int libcjsonp_builtin_write_and_wrap(FILE* x, const char* v) { + + char s[7] = { '\\', 'u', '0', '0', 0, 0, 0 }; + int ret = write_char(x, '"'); + + while (ret >= 0 && *v) { + + if (*v <= 0x20 || *v == 0x7f) switch (*v) { + case 0x09 : ret = write(x, "\\t"); break; + case 0x0a : ret = write(x, "\\n"); break; + case 0x0d : ret = write(x, "\\r"); break; + default : if ((*v&0xf0) < 0xa0) { + s[4] = 0x30 + (*v>>4); + } else s[4] = 0x57 + (*v>>4); + + if ((*v&0x0f) < 0x0a) { + s[5] = 0x30 + (*v&0x0f); + } else s[5] = 0x57 + (*v&0x0f); + + ret = write(x, s); + break; + } else if (*v == '\\') ret = write(x, "\\\\"); + else if (*v == '"') ret = write(x, "\\\""); + else ret = write_char(x, *v); + + ++v; + } + + if (ret >= 0) ret = write_char(x, '"'); + + return (ret < 0) ? ret : 0; +} + +static int libcjsonp_builtin_write_value (FILE* x, const void* v, vtype t, indent_t* indent, size_t level) { + if (t < VTYPE_STRING) return write(x, libcdsb_vtype_stringify(v, t)); + else if (t > VTYPE_STRING) return write_iterable(x, v, t, indent, level); + else return write_and_wrap(x, *(void**)v); +} + +/*#####################################################################################################################*/ + +static int libcjsonp_builtin_dict_callback(const void* k, vtype kt, const void* v, vtype vt, data_t* data) { + void* key = 0; + int ret = 0; + + if (!data->first_el) { + ret = write_char(data->stream, ','); + } else data->first_el = false; + + if (data->indent->nmemb) { + if (ret >= 0) ret = write_char (data->stream, '\n'); + if (ret >= 0) ret = write_indent(data->stream, data->indent, data->level); + } + + if (kt > VTYPE_STRING) { + key = libcjsonp_json_stringify(k, kt, 0, 0).buffer; + k = key; + } else k = (kt < VTYPE_STRING) ? libcdsb_vtype_stringify(k, kt) : *(void**)k; + + if (ret >= 0) ret = write_and_wrap(data->stream, k); + if (ret >= 0) ret = write_char (data->stream, ':'); + libcdsb_free(key); + + if (data->indent->nmemb && ret >= 0) { + write_char(data->stream, ' '); + } + + if (ret >= 0) ret = write_value(data->stream, v, vt, data->indent, data->level); + + return (ret < 0) ? ret : 0; +} + +static int libcjsonp_builtin_list_callback(const void* v, ssize_t index, vtype vt, data_t* data) { + int ret = 0; + + if (!data->first_el) { + ret = write_char(data->stream, ','); + } else data->first_el = false; + + if (data->indent->nmemb) { + if (ret >= 0) ret = write_char (data->stream, '\n'); + if (ret >= 0) ret = write_indent(data->stream, data->indent, data->level); + } + + if (ret >= 0) ret = write_value(data->stream, v, vt, data->indent, data->level); + + return (ret < 0) ? ret : 0; +} + +static int libcjsonp_builtin_set_callback (const void* v, vtype vt, data_t* data) { + int ret = 0; + + if (!data->first_el) { + ret = write_char(data->stream, ','); + } else data->first_el = false; + + if (data->indent->nmemb) { + if (ret >= 0) ret = write_char (data->stream, '\n'); + if (ret >= 0) ret = write_indent(data->stream, data->indent, data->level); + } + + if (ret >= 0) ret = write_value(data->stream, v, vt, data->indent, data->level); + + return (ret < 0) ? ret : 0; +} + +/*#####################################################################################################################*/ + +bool libcjsonp_json_dump(FILE* x, const void* v, vtype t, size_t n, int c) { + + int ret; + indent_t indent; + + if (t < VTYPE_STRING) { + return write(x, libcdsb_vtype_stringify(v, t)) >= 0; + } else if (t == VTYPE_STRING) { + return write_and_wrap(x, *(char**)v) >= 0; + } else if (n) { + char *ptr, chr[4]; + + if (!c || !(ptr = tochar_unicode(chr, c))) { + *chr = ' '; + ptr = chr + 1; + } + + indent.nmemb = ptr - chr; + indent.indent = libcdsb_malloc(n * indent.nmemb); + + ptr = indent.indent; + + while (n--) { + memcpy(ptr, chr, indent.nmemb); + ptr += indent.nmemb; + } + + } else indent = *LIBCJSONP_BUILTIN_WITHOUT_INDENT; + + ret = write_iterable(x, v, t, &indent, 0); + + libcdsb_free(indent.indent); + + return ret >= 0; +} diff --git a/src/stringify.c b/src/stringify.c new file mode 100644 index 0000000..9c2f4f8 --- /dev/null +++ b/src/stringify.c @@ -0,0 +1,193 @@ +/* This software is licensed by the MIT License, see LICENSE file */ +/* Copyright © 2022 Gregory Lirent */ + +#include +#include + +#include "../modules/libcdsb/include/map.h" +#include "../modules/libcdsb/include/dict.h" +#include "../modules/libcdsb/include/list.h" +#include "../modules/libcdsb/include/array.h" +#include "../modules/libcdsb/include/set.h" + +#include "../modules/libcdsb/modules/libunic/include.h" + +#include "buffer.h" + +typedef struct { + buffer_t* buffer; + indent_t* indent; + size_t level; +} data_t; + +#define dict_callback (map_access_callback) libcjsonp_builtin_dict_callback +#define list_callback (list_access_callback)libcjsonp_builtin_list_callback +#define set_callback (vset_access_callback)libcjsonp_builtin_set_callback + +#define write_indent buffer_write_indent +#define write_and_wrap buffer_write_and_wrap +#define write_iterable libcjsonp_builtin_write_iterable +#define write_value libcjsonp_builtin_write_value +#define write_char buffer_write_char +#define write buffer_write + +static int libcjsonp_builtin_dict_callback(const void* k, vtype kt, const void* v, vtype vt, data_t* data); +static int libcjsonp_builtin_list_callback(const void* v, ssize_t index, vtype vt, data_t* data); +static int libcjsonp_builtin_set_callback (const void* v, vtype vt, data_t* data); + +/*#####################################################################################################################*/ + +static void libcjsonp_builtin_write_iterable(buffer_t* x, const void* s, vtype t, indent_t* indent, size_t level) { + data_t data; + char* ptr; + char bracket; + + data.buffer = x; + data.indent = indent; + data.level = level + 1; + + buffer_check_size(x, 1); + + ptr = x->ptr + 1; + --x->c; + + switch (t) { default: + #ifndef NDEBUG + abort(); + #endif + case VTYPE_DICT: *(x->ptr++) = bracket = '{'; + libcdsb_dict_foreach((void*)s, &data, dict_callback, false); + break; + + case VTYPE_LIST: *(x->ptr++) = bracket = '['; + libcdsb_list_foreach((void*)s, &data, list_callback, false); + break; + + case VTYPE_ARRAY: *(x->ptr++) = bracket = '['; + libcdsb_array_foreach((void*)s, &data, list_callback, false); + break; + + case VTYPE_MAP: *(x->ptr++) = bracket = '{'; + libcdsb_map_foreach((void*)s, &data, dict_callback, RBFOREACH_INORDER, false); + break; + + case VTYPE_SET: *(x->ptr++) = bracket = '['; + libcdsb_vset_foreach((void*)s, &data, set_callback, RBFOREACH_INORDER, false); + break; + } + + if (ptr == x->ptr) { + write_char(x, bracket + 2); + } else if (indent->nmemb) { + (x->ptr)[-1] = '\n'; + + write_indent(x, indent, level); + write_char(x, bracket + 2); + } else x->ptr[-1] = bracket + 2; +} + +static void libcjsonp_builtin_write_value(buffer_t* x, const void* v, vtype t, indent_t* indent, size_t level) { + if (t < VTYPE_STRING) write(x, libcdsb_vtype_stringify(v, t)); + else if (t > VTYPE_STRING) write_iterable(x, v, t, indent, level); + else write_and_wrap(x, *(void**)v); + write_char(x, ','); +} + +/*#####################################################################################################################*/ + +static int libcjsonp_builtin_dict_callback(const void* k, vtype kt, const void* v, vtype vt, data_t* data) { + buffer_t key; + + memset(&key, 0, sizeof(key)); + + if (data->indent->nmemb) { + write_char (data->buffer, '\n'); + write_indent(data->buffer, data->indent, data->level); + } + + if (kt > VTYPE_STRING) { + write_iterable(&key, k, kt, LIBCJSONP_BUILTIN_WITHOUT_INDENT, 0); + write_char (&key, 0); + k = key.mem; + } else k = (kt < VTYPE_STRING) ? libcdsb_vtype_stringify(k, kt) : *(void**)k; + + write_and_wrap(data->buffer, k); + write_char (data->buffer, ':'); + libcdsb_free (key.mem); + + if (data->indent->nmemb) { + write_char(data->buffer, ' '); + } + + write_value(data->buffer, v, vt, data->indent, data->level); + return 0; +} + +static int libcjsonp_builtin_set_callback (const void* v, vtype vt, data_t* data) { + if (data->indent->nmemb) { + write_char (data->buffer, '\n'); + write_indent(data->buffer, data->indent, data->level); + } + + write_value(data->buffer, v, vt, data->indent, data->level); + return 0; +} + +static int libcjsonp_builtin_list_callback(const void* v, ssize_t index, vtype vt, data_t* data) { + if (data->indent->nmemb) { + write_char (data->buffer, '\n'); + write_indent(data->buffer, data->indent, data->level); + } + + write_value(data->buffer, v, vt, data->indent, data->level); + return 0; +} + +/*#####################################################################################################################*/ + +vtype_string libcjsonp_json_stringify(const void* v, vtype t, size_t n, int c) { + + buffer_t buffer; + indent_t indent; + + if (t < VTYPE_STRING) { + v = libcdsb_strdup(libcdsb_vtype_stringify(v, t)); + return *(vtype_string*)&v; + } + + memset(&buffer, 0, sizeof(buffer)); + + if (t == VTYPE_STRING) { + write_and_wrap(&buffer, *(char**)v); + write_char (&buffer, 0); + + return *(vtype_string*)&buffer; + } else if (n) { + char *ptr, chr[4]; + + if (!c || !(ptr = tochar_unicode(chr, c))) { + *chr = ' '; + ptr = chr + 1; + } + + indent.nmemb = ptr - chr; + indent.indent = malloc(n * indent.nmemb); + + ptr = indent.indent; + + for (size_t i = 0; i < n; ++i) { + memcpy(ptr, chr, indent.nmemb); + ptr += indent.nmemb; + } + + indent.nmemb *= n; + + } else indent = *LIBCJSONP_BUILTIN_WITHOUT_INDENT; + + write_iterable(&buffer, v, t, &indent, 0); + write_char (&buffer, 0); + + free(indent.indent); + + return *(vtype_string*)&buffer; +}