From 6c5ff67720106e3f8835f980ea87fa477980aee0 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Tue, 14 Jul 2020 21:46:01 +0200 Subject: [PATCH] added implementation of json parser --- lib/webfuse/impl/json/doc.c | 44 +++++ lib/webfuse/impl/json/doc.h | 36 ++++ lib/webfuse/impl/json/node.c | 147 ++++++++++++++ lib/webfuse/impl/json/node.h | 77 ++++++++ lib/webfuse/impl/json/node_intern.h | 54 ++++++ lib/webfuse/impl/json/parser.c | 288 ++++++++++++++++++++++++++++ lib/webfuse/impl/json/parser.h | 25 +++ lib/webfuse/impl/json/reader.c | 179 +++++++++++++++++ lib/webfuse/impl/json/reader.h | 65 +++++++ meson.build | 4 + 10 files changed, 919 insertions(+) create mode 100644 lib/webfuse/impl/json/doc.c create mode 100644 lib/webfuse/impl/json/doc.h create mode 100644 lib/webfuse/impl/json/node.c create mode 100644 lib/webfuse/impl/json/node.h create mode 100644 lib/webfuse/impl/json/node_intern.h create mode 100644 lib/webfuse/impl/json/parser.c create mode 100644 lib/webfuse/impl/json/parser.h create mode 100644 lib/webfuse/impl/json/reader.c create mode 100644 lib/webfuse/impl/json/reader.h diff --git a/lib/webfuse/impl/json/doc.c b/lib/webfuse/impl/json/doc.c new file mode 100644 index 0000000..1aa0b5f --- /dev/null +++ b/lib/webfuse/impl/json/doc.c @@ -0,0 +1,44 @@ +#include "webfuse/impl/json/doc.h" +#include "webfuse/impl/json/node_intern.h" +#include "webfuse/impl/json/reader.h" +#include "webfuse/impl/json/parser.h" + +#include + +struct wf_json_doc +{ + struct wf_json root; +}; + +struct wf_json_doc * +wf_impl_json_doc_loadb( + char * data, + size_t length) +{ + struct wf_json_reader reader; + wf_impl_json_reader_init(&reader, data, length); + + struct wf_json_doc * doc = malloc(sizeof(struct wf_json_doc)); + if (!wf_impl_json_parse_value(&reader, &doc->root)) + { + free(doc); + doc = NULL; + } + + return doc; +} + +void +wf_impl_json_doc_dispose( + struct wf_json_doc * doc) +{ + wf_impl_json_cleanup(&doc->root); + free(doc); +} + +struct wf_json const * +wf_impl_jsoc_doc_root( + struct wf_json_doc * doc) +{ + return &doc->root; +} diff --git a/lib/webfuse/impl/json/doc.h b/lib/webfuse/impl/json/doc.h new file mode 100644 index 0000000..0aff147 --- /dev/null +++ b/lib/webfuse/impl/json/doc.h @@ -0,0 +1,36 @@ +#ifndef WF_IMPL_JSON_DOC_H +#define WF_IMPL_JSON_DOC_H + +#ifndef __cplusplus +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_json_doc; +struct wf_json; + +extern struct wf_json_doc * +wf_impl_json_doc_loadb( + char * data, + size_t length); + +extern void +wf_impl_json_doc_dispose( + struct wf_json_doc * doc); + +extern struct wf_json const * +wf_impl_jsoc_doc_root( + struct wf_json_doc * doc); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/impl/json/node.c b/lib/webfuse/impl/json/node.c new file mode 100644 index 0000000..207211d --- /dev/null +++ b/lib/webfuse/impl/json/node.c @@ -0,0 +1,147 @@ +#include "webfuse/impl/json/node_intern.h" + +#include +#include + +static struct wf_json const wf_json_undefined = +{ + .type = WF_JSON_TYPE_UNDEFINED, + .value = { .b = false } +}; + +enum wf_json_type +wf_impl_json_type( + struct wf_json const * json) +{ + return json->type; +} + +bool +wf_impl_json_bool_get( + struct wf_json const * json) +{ + return (WF_JSON_TYPE_BOOL == json->type) ? json->value.b : false; +} + +int +wf_impl_json_int_get( + struct wf_json const * json) +{ + return (WF_JSON_TYPE_INT == json->type) ? json->value.i : 0; +} + +char const * +wf_impl_json_string_get( + struct wf_json const * json) +{ + return (WF_JSON_TYPE_STRING == json->type) ? json->value.s : ""; +} + +struct wf_json const * +wf_impl_json_array_get( + struct wf_json const * json, + size_t pos) +{ + struct wf_json const * result = &wf_json_undefined; + if ((WF_JSON_TYPE_ARRAY == json->type) && (pos < json->value.a.size)) + { + result = &(json->value.a.items[pos]); + } + + return result; +} + +size_t +wf_impl_json_array_size( + struct wf_json const * json) +{ + return (WF_JSON_TYPE_ARRAY == json->type) ? json->value.a.size : 0; +} + +struct wf_json const * +wf_impl_json_object_get( + struct wf_json const * json, + char const * key) + { + if (WF_JSON_TYPE_OBJECT == json->type) + { + size_t const count = json->value.o.size; + for(size_t i = 0; i < count; i++) + { + struct wf_json_object_item * actual = &(json->value.o.items[i]); + if (0 == strcmp(key, actual->key)) + { + return &(actual->json); + } + } + } + + return &wf_json_undefined; + } + +size_t +wf_impl_json_object_size( + struct wf_json const * json) +{ + return (WF_JSON_TYPE_OBJECT == json->type) ? json->value.o.size : 0; +} + +char const * +wf_impl_json_object_key( + struct wf_json const * json, + size_t pos) +{ + char const * result = ""; + if ((WF_JSON_TYPE_OBJECT == json->type) && (pos < json->value.o.size)) + { + result = json->value.o.items[pos].key; + } + + return result; +} + +struct wf_json const * +wf_impl_json_object_value( + struct wf_json const * json, + size_t pos) +{ + struct wf_json const * result = &wf_json_undefined; + if ((WF_JSON_TYPE_OBJECT == json->type) && (pos < json->value.o.size)) + { + result = &(json->value.o.items[pos].json); + } + + return result; + +} + +void +wf_impl_json_cleanup( + struct wf_json * json) +{ + switch (json->type) + { + case WF_JSON_TYPE_ARRAY: + { + size_t const count = json->value.a.size; + for(size_t i = 0; i < count; i++) + { + struct wf_json * actual = &(json->value.a.items[i]); + wf_impl_json_cleanup(actual); + } + } + break; + case WF_JSON_TYPE_OBJECT: + { + size_t const count = json->value.o.size; + for(size_t i = 0; i < count; i++) + { + struct wf_json * actual = &(json->value.o.items[i].json); + wf_impl_json_cleanup(actual); + } + } + break; + default: + break; + } +} diff --git a/lib/webfuse/impl/json/node.h b/lib/webfuse/impl/json/node.h new file mode 100644 index 0000000..592d402 --- /dev/null +++ b/lib/webfuse/impl/json/node.h @@ -0,0 +1,77 @@ +#ifndef WF_IMPL_JSON_NODE_H +#define WF_IMPL_JSON_NODE_H + +#ifndef __cplusplus +#include +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum wf_json_type +{ + WF_JSON_TYPE_UNDEFINED, + WF_JSON_TYPE_NULL, + WF_JSON_TYPE_BOOL, + WF_JSON_TYPE_INT, + WF_JSON_TYPE_STRING, + WF_JSON_TYPE_ARRAY, + WF_JSON_TYPE_OBJECT +}; + +struct wf_json; + +extern enum wf_json_type +wf_impl_json_type( + struct wf_json const * json); + +extern bool +wf_impl_json_bool_get( + struct wf_json const * json); + +extern int +wf_impl_json_int_get( + struct wf_json const * json); + +extern char const * +wf_impl_json_string_get( + struct wf_json const * json); + +extern struct wf_json const * +wf_impl_json_array_get( + struct wf_json const * json, + size_t pos); + +extern size_t +wf_impl_json_array_size( + struct wf_json const * json); + +extern struct wf_json const * +wf_impl_json_object_get( + struct wf_json const * json, + char const * key); + +extern size_t +wf_impl_json_object_size( + struct wf_json const * json); + +extern char const * +wf_impl_json_object_key( + struct wf_json const * json, + size_t pos); + +extern struct wf_json const * +wf_impl_json_object_value( + struct wf_json const * json, + size_t pos); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/impl/json/node_intern.h b/lib/webfuse/impl/json/node_intern.h new file mode 100644 index 0000000..9d888a9 --- /dev/null +++ b/lib/webfuse/impl/json/node_intern.h @@ -0,0 +1,54 @@ +#ifndef WF_IMPL_JSON_NODE_INTERN_H +#define WF_IMPL_JSON_NODE_INTERN_H + +#include "webfuse/impl/json/node.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_json_array +{ + struct wf_json * items; + size_t size; +}; + +struct wf_json_object_item; + +struct wf_json_object +{ + struct wf_json_object_item * items; + size_t size; +}; + +union wf_json_value +{ + bool b; + int i; + char * s; + struct wf_json_array a; + struct wf_json_object o; +}; + +struct wf_json +{ + enum wf_json_type type; + union wf_json_value value; +}; + +struct wf_json_object_item +{ + struct wf_json json; + char * key; +}; + +extern void +wf_impl_json_cleanup( + struct wf_json * json); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/impl/json/parser.c b/lib/webfuse/impl/json/parser.c new file mode 100644 index 0000000..ab0648f --- /dev/null +++ b/lib/webfuse/impl/json/parser.c @@ -0,0 +1,288 @@ +#include "webfuse/impl/json/parser.h" +#include "webfuse/impl/json/reader.h" +#include "webfuse/impl/json/node_intern.h" + +#include + +#define WF_JSON_PARSER_INITIAL_CAPACITY 4 + +static bool +wf_impl_json_parse_null( + struct wf_json_reader * reader, + struct wf_json * json); + +static bool +wf_impl_json_parse_true( + struct wf_json_reader * reader, + struct wf_json * json); + +static bool +wf_impl_json_parse_false( + struct wf_json_reader * reader, + struct wf_json * json); + +static bool +wf_impl_json_parse_int( + struct wf_json_reader * reader, + struct wf_json * json); + +static bool +wf_impl_json_parse_string( + struct wf_json_reader * reader, + struct wf_json * json); + +static bool +wf_impl_json_parse_array( + struct wf_json_reader * reader, + struct wf_json * json); + +static bool +wf_impl_json_parse_object( + struct wf_json_reader * reader, + struct wf_json * json); + +// -- + +bool +wf_impl_json_parse_value( + struct wf_json_reader * reader, + struct wf_json * json) +{ + bool result = false; + char const c = wf_impl_json_reader_skip_whitespace(reader); + + switch (c) + { + case 'n': + result = wf_impl_json_parse_null(reader, json); + break; + case 't': + result = wf_impl_json_parse_true(reader, json); + break; + case 'f': + result = wf_impl_json_parse_false(reader, json); + break; + case '\"': + result = wf_impl_json_parse_string(reader, json); + break; + case '[': + result = wf_impl_json_parse_array(reader, json); + break; + case '{': + result = wf_impl_json_parse_object(reader, json); + break; + case '-': // fall-through + case '0': // fall-through + case '1': // fall-through + case '2': // fall-through + case '3': // fall-through + case '4': // fall-through + case '5': // fall-through + case '6': // fall-through + case '7': // fall-through + case '8': // fall-through + case '9': + result = wf_impl_json_parse_int(reader, json); + break; + default: + result = false; + break; + } + + return result; +} + +// -- + +static bool +wf_impl_json_parse_null( + struct wf_json_reader * reader, + struct wf_json * json) +{ + bool const result = wf_impl_json_reader_read_const(reader, "null", 4); + if (result) + { + json->type = WF_JSON_TYPE_NULL; + } + + return result; +} + +static bool +wf_impl_json_parse_true( + struct wf_json_reader * reader, + struct wf_json * json) +{ + bool const result = wf_impl_json_reader_read_const(reader, "true", 4); + if (result) + { + json->type = WF_JSON_TYPE_BOOL; + json->value.b = true; + } + + return result; +} + +static bool +wf_impl_json_parse_false( + struct wf_json_reader * reader, + struct wf_json * json) +{ + bool const result = wf_impl_json_reader_read_const(reader, "false", 5); + if (result) + { + json->type = WF_JSON_TYPE_BOOL; + json->value.b = false; + } + + return result; +} + + +static bool +wf_impl_json_parse_int( + struct wf_json_reader * reader, + struct wf_json * json) +{ + int value; + bool const result = wf_impl_json_reader_read_int(reader, &value); + if (result) + { + json->type = WF_JSON_TYPE_INT; + json->value.i = value; + } + + return result; +} + +static bool +wf_impl_json_parse_string( + struct wf_json_reader * reader, + struct wf_json * json) +{ + char * value; + bool const result = wf_impl_json_reader_read_string(reader, &value); + if (result) + { + json->type = WF_JSON_TYPE_STRING; + json->value.s = value; + } + + return result; +} + +static bool +wf_impl_json_parse_array( + struct wf_json_reader * reader, + struct wf_json * json) +{ + wf_impl_json_reader_skip_whitespace(reader); + char c = wf_impl_json_reader_get_char(reader); + if ('[' != c) { return false; } + + size_t capacity = WF_JSON_PARSER_INITIAL_CAPACITY; + json->type = WF_JSON_TYPE_ARRAY; + json->value.a.items = malloc(sizeof(struct wf_json) * capacity); + json->value.a.size = 0; + + c = wf_impl_json_reader_skip_whitespace(reader); + if (']' == c) + { + wf_impl_json_reader_get_char(reader); + return true; + } + + bool result; + do + { + if (json->value.a.size >= capacity) + { + capacity *= 2; + json->value.a.items = realloc(json->value.a.items, sizeof(struct wf_json) * capacity); + } + + result = wf_impl_json_parse_value(reader, &(json->value.a.items[json->value.a.size])); + if (result) + { + json->value.a.size++; + wf_impl_json_reader_skip_whitespace(reader); + c = wf_impl_json_reader_get_char(reader); + } + } while ((result) && (',' == c)); + + if ((result) && (']' == c)) + { + result = false; + } + + if (!result) + { + wf_impl_json_cleanup(json); + } + + return result; +} + +static bool +wf_impl_json_parse_object( + struct wf_json_reader * reader, + struct wf_json * json) +{ + wf_impl_json_reader_skip_whitespace(reader); + char c = wf_impl_json_reader_get_char(reader); + if ('{' != c) { return false; } + + size_t capacity = WF_JSON_PARSER_INITIAL_CAPACITY; + json->type = WF_JSON_TYPE_OBJECT; + json->value.o.items = malloc(sizeof(struct wf_json_object_item) * capacity); + json->value.o.size = 0; + + c = wf_impl_json_reader_skip_whitespace(reader); + if ('}' == c) + { + wf_impl_json_reader_get_char(reader); + return true; + } + + bool result; + do + { + if (json->value.o.size >= capacity) + { + capacity *= 2; + json->value.o.items = realloc(json->value.o.items, sizeof(struct wf_json_object_item) * capacity); + } + + struct wf_json_object_item * item = &(json->value.o.items[json->value.o.size]); + result = wf_impl_json_reader_read_string(reader, &(item->key)); + if (result) + { + wf_impl_json_reader_skip_whitespace(reader); + result = (':' == wf_impl_json_reader_get_char(reader)); + } + + if (result) + { + result = wf_impl_json_parse_value(reader, &(item->json)); + } + + if (result) + { + json->value.o.size++; + wf_impl_json_reader_skip_whitespace(reader); + c = wf_impl_json_reader_get_char(reader); + } + } while ((reader) && (',' == c)); + + if ((reader) && ('}' != c)) + { + result = false; + } + + if (!result) + { + wf_impl_json_cleanup(json); + } + + return result; +} \ No newline at end of file diff --git a/lib/webfuse/impl/json/parser.h b/lib/webfuse/impl/json/parser.h new file mode 100644 index 0000000..93b1380 --- /dev/null +++ b/lib/webfuse/impl/json/parser.h @@ -0,0 +1,25 @@ +#ifndef WF_IMPL_JSON_PARSER_H +#define WF_IMPL_JSON_PARSER_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_json_reader; +struct wf_json; + +extern bool +wf_impl_json_parse_value( + struct wf_json_reader * reader, + struct wf_json * json); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/impl/json/reader.c b/lib/webfuse/impl/json/reader.c new file mode 100644 index 0000000..b20ed4a --- /dev/null +++ b/lib/webfuse/impl/json/reader.c @@ -0,0 +1,179 @@ +#include "webfuse/impl/json/reader.h" + +#include + +static char +wf_impl_json_unescape( + char c); + +void +wf_impl_json_reader_init( + struct wf_json_reader * reader, + char * contents, + size_t length) +{ + reader->contents = contents; + reader->length = length; + reader->pos = 0; +} + +char +wf_impl_json_reader_skip_whitespace( + struct wf_json_reader * reader) +{ + char c = wf_impl_json_reader_peek(reader); + if ((' ' == c) || ('\n' == c) || ('\t' == c) || ('\r' == c)) + { + reader->pos++; + c = wf_impl_json_reader_peek(reader); + } + + return c; +} + +char +wf_impl_json_reader_peek( + struct wf_json_reader * reader) +{ + char result = '\0'; + if (reader->pos < reader->length) + { + result = reader->contents[reader->pos]; + } + + return result; +} + +char +wf_impl_json_reader_get_char( + struct wf_json_reader * reader) +{ + char result = '\0'; + if (reader->pos < reader->length) + { + result = reader->contents[reader->pos++]; + } + + return result; +} + +void +wf_impl_json_reader_unget_char( + struct wf_json_reader * reader) +{ + if (0 < reader->pos) + { + reader->pos--; + } +} + +bool +wf_impl_json_reader_read_const( + struct wf_json_reader * reader, + char const * value, + size_t length) +{ + size_t remaining = reader->length - reader->pos; + bool const result = ((remaining >= length) && (0 == strncmp((&reader->contents[reader->pos]), value, length))); + if (result) + { + reader->pos += length; + } + + return result; +} + +bool +wf_impl_json_reader_read_int( + struct wf_json_reader * reader, + int * value) +{ + char c = wf_impl_json_reader_get_char(reader); + bool const is_signed = ('-' == c); + if (is_signed) + { + c = wf_impl_json_reader_get_char(reader); + } + + bool const result = (('0' <= c) && (c <= '9')); + if (result) + { + int v = c - '0'; + c = wf_impl_json_reader_peek(reader); + while (('0' <= c) && (c <= '9')) + { + v *= 10; + v += c - '0'; + reader->pos++; + c = wf_impl_json_reader_peek(reader); + } + + *value = (is_signed) ? -v : v; + } + + return result; +} + +bool +wf_impl_json_reader_read_string( + struct wf_json_reader * reader, + char * * value) +{ + wf_impl_json_reader_skip_whitespace(reader); + char c = wf_impl_json_reader_get_char(reader); + if ('\"' == c) { return value; } + + size_t p = reader->pos; + *value = &(reader->contents[p]); + c = wf_impl_json_reader_get_char(reader); + while (('\"' != c) && ('\"' != c)) + { + if ('\\' != c) + { + reader->contents[p++] = c; + } + else + { + char unescaped = wf_impl_json_unescape(wf_impl_json_reader_get_char(reader)); + if ('\0' != c) + { + reader->contents[p++] = unescaped; + + } + else + { + *value = NULL; + return false; + } + } + + c = wf_impl_json_reader_get_char(reader); + } + + bool const result = ('\"' == c); + if (result) + { + reader->contents[p] = '\0'; + } + + return result; +} + +static char +wf_impl_json_unescape( + char c) +{ + switch (c) + { + case '\"': return '\"'; + case '\\': return '\\'; + case '/': return '/'; + case 'b': return '\b'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + default: + return '\0'; + } +} diff --git a/lib/webfuse/impl/json/reader.h b/lib/webfuse/impl/json/reader.h new file mode 100644 index 0000000..0af1e47 --- /dev/null +++ b/lib/webfuse/impl/json/reader.h @@ -0,0 +1,65 @@ +#ifndef WF_IMPL_JSON_READER_H +#define WF_IMPL_JSON_READER_H + +#ifndef __cplusplus +#include +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_json_reader +{ + char * contents; + size_t length; + size_t pos; +}; + +extern void +wf_impl_json_reader_init( + struct wf_json_reader * reader, + char * contents, + size_t length); + +extern char +wf_impl_json_reader_skip_whitespace( + struct wf_json_reader * reader); + +extern char +wf_impl_json_reader_peek( + struct wf_json_reader * reader); + +extern char +wf_impl_json_reader_get_char( + struct wf_json_reader * reader); + +extern void +wf_impl_json_reader_unget_char( + struct wf_json_reader * reader); + +extern bool +wf_impl_json_reader_read_const( + struct wf_json_reader * reader, + char const * value, + size_t length); + +extern bool +wf_impl_json_reader_read_int( + struct wf_json_reader * reader, + int * value); + +extern bool +wf_impl_json_reader_read_string( + struct wf_json_reader * reader, + char * * value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/meson.build b/meson.build index 1838a89..a44c1aa 100644 --- a/meson.build +++ b/meson.build @@ -29,6 +29,10 @@ webfuse_static = static_library('webfuse', 'lib/webfuse/impl/timer/timepoint.c', 'lib/webfuse/impl/timer/timer.c', 'lib/webfuse/impl/json/writer.c', + 'lib/webfuse/impl/json/node.c', + 'lib/webfuse/impl/json/doc.c', + 'lib/webfuse/impl/json/reader.c', + 'lib/webfuse/impl/json/parser.c', 'lib/webfuse/impl/jsonrpc/proxy.c', 'lib/webfuse/impl/jsonrpc/proxy_request_manager.c', 'lib/webfuse/impl/jsonrpc/proxy_variadic.c',