From e079370a92766181ff5281c5d6cea03a1fce5b93 Mon Sep 17 00:00:00 2001 From: "Anna (navi) Figueiredo Gomes" Date: Fri, 5 Jan 2024 22:51:33 +0100 Subject: initial release Signed-off-by: Anna (navi) Figueiredo Gomes --- src/array.c | 124 ++++++++++++++++++++++++++ src/internal.h | 74 ++++++++++++++++ src/json.c | 164 ++++++++++++++++++++++++++++++++++ src/literal.c | 58 ++++++++++++ src/object.c | 193 ++++++++++++++++++++++++++++++++++++++++ src/print.c | 112 +++++++++++++++++++++++ src/string.c | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 998 insertions(+) create mode 100644 src/array.c create mode 100644 src/internal.h create mode 100644 src/json.c create mode 100644 src/literal.c create mode 100644 src/object.c create mode 100644 src/print.c create mode 100644 src/string.c (limited to 'src') diff --git a/src/array.c b/src/array.c new file mode 100644 index 0000000..e8898f4 --- /dev/null +++ b/src/array.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include "internal.h" + +enum json_parse_result parse_array(struct json **json_out, struct raw_json *raw, size_t depth) { + assert(raw->data[raw->index] == '['); + enum json_parse_result ret = JSON_PARSE_OK; + struct json *json = NULL; + struct json *obj = NULL; + + if (depth++ > 9) { + ret = JSON_PARSE_MAX_DEPTH_ERR; + goto err; + } + + json = json_new_array(); + if (!json) + return JSON_OOM; + + do { + raw->index++; + skip_ws(raw); + + if (raw->data[raw->index] == ']') { + if (json->children->nitems == 0) { + break; + } + ret = JSON_PARSE_EXPECTED_VALUE_ERR; + goto err; + } + + ret = parse_value(&obj, raw, depth); + if (ret != JSON_PARSE_OK) { + goto err; + } + + json_array_append(json, obj); + skip_ws(raw); + + obj = NULL; + } while (raw->index < raw->size && raw->data[raw->index] == ','); + + if (raw->data[raw->index] != ']') { + ret = JSON_PARSE_INVALID_TOKEN_ERR; + goto err; + } + raw->index++; + + *json_out = json; + goto end; +err: + json_delete(json); + json_delete(obj); + +end: + return ret; +} + + +bool json_array_append(struct json *dest, struct json *src) { + if (!dest || !src) + return false; + add_children(dest, src); + return true; +} + +struct json *json_array_get(const struct json *json, size_t index) { + if (index > json->children->nitems) + return NULL; + + return json->children->items[index]; +} + +struct json *json_array_add_object(struct json *dest) { + struct json *obj = json_new_array(); + json_array_append(dest, obj); + return obj; +} + +struct json *json_array_add_array(struct json *dest) { + struct json *array = json_new_array(); + json_array_append(dest, array); + return array; +} +struct json *json_array_add_number(struct json *dest, double num) { + struct json *number = json_new_number(num); + json_array_append(dest, number); + return number; +} + +struct json *json_array_add_string(struct json *dest, const char *string) { + struct json *str = json_new_string_copy(string); + json_array_append(dest, str); + return str; +} + +struct json *json_array_add_bool(struct json *dest, bool boolean) { + struct json *b = json_new_bool(boolean); + json_array_append(dest, b); + return b; +} + +struct json *json_array_add_null(struct json *dest) { + struct json *n = json_new_null(); + json_array_append(dest, n); + return n; +} + +struct json *json_array_detach(struct json *json, size_t index) { + if (json->type != JSON_ARRAY || index >= json->children->nitems) { + return NULL; + } + + struct json *tmp = json->children->items[index]; + json_detach(tmp); + return tmp; +} + +void json_array_delete(struct json *json, size_t index) { + struct json *tmp = json_array_detach(json, index); + json_delete(tmp); +} diff --git a/src/internal.h b/src/internal.h new file mode 100644 index 0000000..7ea8a5e --- /dev/null +++ b/src/internal.h @@ -0,0 +1,74 @@ +#ifndef _JSON_INTERNAL_H_ +#define _JSON_INTERNAL_H_ + +#include + +#include + +struct raw_json { + const char *data; + size_t size; + size_t index; +}; + +inline static void skip_ws(struct raw_json *raw) { + // empty loop to move i along until we hit a non-whitespace + while (raw->index < raw->size && + (raw->data[raw->index] == ' ' || + raw->data[raw->index] == '\n' || + raw->data[raw->index] == '\r' || + raw->data[raw->index] == '\t')) + { + raw->index++; + } +} + +inline static bool init_children(struct json *obj) { + const size_t starting_size = 4; + obj->children = calloc(sizeof(*obj->children) + (sizeof(*obj->children->items) * starting_size), 1); + if (!obj->children) + return false; + obj->children->maxitems = starting_size; + obj->children->nitems = 0; + return true; +} + + +inline static void add_children(struct json *dest, struct json *src) { + struct json_children *children = dest->children; + if (children->nitems + 1 >= children->maxitems) { + children->maxitems += 5; + children = realloc(children, sizeof(*children) + (sizeof(*children->items) * children->maxitems)); + if (!children) + return; + dest->children = children; + } + + src->parent = dest; + src->index = children->nitems; + + if (children->nitems > 0) { + children->items[children->nitems - 1]->next = src; + src->prev = children->items[children->nitems - 1]; + } + + children->items[children->nitems++] = src; + src->parent = dest; +} + +inline static void adjust_pointers(struct json* from, struct json* to) { + to->prev = from->prev; + to->next = from->next; + if (from->prev) from->prev->next = to; + if (from->next) from->next->prev = to; +} + + +enum json_parse_result parse_value(struct json **json_out, struct raw_json *raw, size_t depth); +enum json_parse_result parse_object(struct json **json_out, struct raw_json *raw, size_t depth); +enum json_parse_result parse_array(struct json **json_out, struct raw_json *raw, size_t depth); +enum json_parse_result parse_raw_string(char **str_out, struct raw_json *raw); +enum json_parse_result parse_string(struct json **json_out, struct raw_json *raw); +enum json_parse_result parse_literal(struct json **json_out, struct raw_json *raw); + +#endif diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000..2e86eaf --- /dev/null +++ b/src/json.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include +#include "internal.h" + +#include +#include +#include + + +struct json *json_parse(const char *str) { + return json_parse_len(str, strlen(str)); +} + +struct json *json_new(void) { + struct json *json = calloc(1, sizeof(*json)); + if (!json) + return NULL; + json->type = JSON_INVALID; + return json; +} + +void json_detach(struct json *json) { + if (!json || !json->parent) return; + + struct json_children *siblings = json->parent->children; + if (json->index != --siblings->nitems) { + struct json *tmp; + for (size_t i = json->index; i < siblings->nitems; i++) { + tmp = siblings->items[i] = siblings->items[i + 1]; + tmp->index = i; + tmp->prev = i > 0 ? siblings->items[tmp->index - 1] : NULL; + tmp->next = i < siblings->nitems ? siblings->items[tmp->index + 1] : NULL; + } + } + + json->parent = NULL; +} + +void json_delete(struct json *json) { + if (!json) + return; + json_detach(json); + json_clear(json); + free(json); +} + +void json_clear(struct json *json) { + if (!json) return; + switch (json->type) { + case JSON_OBJECT: + case JSON_ARRAY: + for (size_t i = 0; i < json->children->nitems; i++) { + json_clear(json->children->items[i]); + free(json->children->items[i]); + } + free(json->children); + break; + case JSON_STRING: + free(json->string); + break; + default: + break; + } + free(json->key); + json->type = JSON_INVALID; +} + +struct json *json_new_array(void) { + struct json *json = json_new(); + if (!json) + return NULL; + json->type = JSON_ARRAY; + init_children(json); + return json; +} + +struct json *json_new_bool(bool boolean) { + struct json *json = json_new(); + if (!json) + return NULL; + json->type = boolean ? JSON_TRUE : JSON_FALSE; + return json; +} + +struct json *json_new_null(void) { + struct json *json = json_new(); + if (!json) + return NULL; + json->type = JSON_NULL; + return json; +} + +struct json *json_new_number(double num) { + struct json *json = json_new(); + if (!json) + return NULL; + json->type = JSON_NUMBER; + json->num = num; + return json; +} + +struct json *json_new_string(void) { + struct json *json = json_new(); + if (!json) + return NULL; + json->type = JSON_STRING; + json->string = NULL; + return json; +} + +struct json *json_new_string_copy(const char *string) { + struct json *json = json_new(); + if (!json) + return NULL; + json->type = JSON_STRING; + json->string = strdup(string); + return json; +} + +enum json_parse_result parse_value(struct json **json_out, struct raw_json *raw, size_t depth) { + skip_ws(raw); + struct json *json = NULL; + enum json_parse_result ret = JSON_PARSE_OK; + switch (raw->data[raw->index]) { + case '"': + ret = parse_string(&json, raw); + break; + case '{': + ret = parse_object(&json, raw, depth); + break; + case '[': + ret = parse_array(&json, raw, depth); + break; + default: + ret = parse_literal(&json, raw); + break; + } + *json_out = json; + return ret; +} + +struct json *json_parse_len(const char *str, size_t size) { + if (!str || size == 0) + return NULL; + struct json *json = NULL; + struct raw_json raw = { + .index = 0, + .data = str, + .size = size + }; + if (parse_value(&json, &raw, 1) != JSON_PARSE_OK) { + return NULL; + }; + skip_ws(&raw); + if (raw.index != raw.size) { + json_delete(json); + return NULL; + } + return json; +} diff --git a/src/literal.c b/src/literal.c new file mode 100644 index 0000000..8723462 --- /dev/null +++ b/src/literal.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include "internal.h" + +enum json_parse_result parse_literal(struct json **json_out, struct raw_json *raw) { + enum json_parse_result ret = JSON_PARSE_OK; + struct json *json = NULL; + + bool plus_one = raw->data[raw->index] == '+' || raw->data[raw->index] == '-'; + if (plus_one || isdigit(raw->data[raw->index])) { + if (raw->data[raw->index] == '0' && isdigit(raw->data[raw->index + 1])) { + ret = JSON_PARSE_INVALID_NUMBER_ERR; + goto err; + } + const char *start = raw->data + raw->index + plus_one; + char *end; + /* is probably a number here, strtodl will tell */ + double num = strtod(start, &end); + if (end != start) { + if (raw->data[raw->index] == '-') + num = -num; + json = json_new_number(num); + raw->index = end - raw->data; + } else { + ret = JSON_PARSE_INVALID_NUMBER_ERR; + goto err; + } + } else { + size_t end_index; + for (end_index = raw->index; end_index < raw->size; end_index++) { + if (!isalpha(raw->data[end_index])) { + break; + } + } + size_t len = end_index - raw->index; + if (len == 4 && strncmp(raw->data + raw->index, "true", len) == 0) { + json = json_new_bool(true); + } else if (len == 5 && strncmp(raw->data + raw->index, "false", len) == 0) { + json = json_new_bool(false); + } else if (len == 4 && strncmp(raw->data + raw->index, "null", len) == 0) { + json = json_new_null(); + } else { + ret = JSON_PARSE_INVALID_TOKEN_ERR; + } + raw->index = end_index; + } + + if (!json) + return JSON_OOM; + + *json_out = json; + goto end; +err: + json_delete(json); +end: + return ret; +} diff --git a/src/object.c b/src/object.c new file mode 100644 index 0000000..b380008 --- /dev/null +++ b/src/object.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include "internal.h" + +//inline static size_t hash(const char *key) { + //if (!key) + //return 0; + //size_t ret = 5381; + //for (const char *p = key; *p != '\0'; p++) { + //ret = ((ret << 5) + ret) + *p; + //} + //return ret; +//} + +void add_to_object(struct json *dest, char *key, struct json *src) { + assert(dest->type == JSON_OBJECT); + assert(key); + assert(src); + + src->key = (free(src->key), key); + //src->index = hash(src->key) % dest->children->maxitems; + + for (size_t i = 0; i < dest->children->nitems; i++) { + struct json *e = dest->children->items[i]; + if (strcmp(e->key, key) == 0) { + src->index = e->index; + dest->children->items[i] = src; + + adjust_pointers(e, src); + json_delete(e); + return; + } + } + + add_children(dest, src); +} + +struct json *json_new_object(void) { + struct json *json = json_new(); + json->type = JSON_OBJECT; + init_children(json); + return json; +} + +enum json_parse_result parse_object(struct json **json_out, struct raw_json *raw, size_t depth) { + assert(raw->data[raw->index] == '{'); + enum json_parse_result ret = JSON_PARSE_OK; + struct json *json = NULL; + struct json *obj = NULL; + char *key = NULL; + bool empty = true; + + if (depth++ > 9) { + ret = JSON_PARSE_MAX_DEPTH_ERR; + goto err; + } + + json = json_new_object(); + if (!json) + return JSON_OOM; + + do { + raw->index++; + skip_ws(raw); + + if (raw->data[raw->index] == '}') { + if (empty) { + break; + } + ret = JSON_PARSE_EXPECTED_VALUE_ERR; + goto err; + } + + if (raw->data[raw->index] != '"') { + ret = JSON_PARSE_OBJECT_INVALID_KEY_ERR; + goto err; + } + ret = parse_raw_string(&key, raw); + if (ret != JSON_PARSE_OK) { + goto err; + } + skip_ws(raw); + + if (raw->data[raw->index] != ':') { + ret = JSON_PARSE_OBJECT_EXPECTED_VALUE_ERR; + goto err; + } + + raw->index++; + skip_ws(raw); + + ret = parse_value(&obj, raw, depth); + if (ret != JSON_PARSE_OK) { + goto err; + } + + add_to_object(json, key, obj); + empty = false; + skip_ws(raw); + + obj = NULL; + key = NULL; + } while (raw->index < raw->size && raw->data[raw->index] == ','); + + if (raw->data[raw->index] != '}') { + ret = JSON_PARSE_INVALID_TOKEN_ERR; + goto err; + } + raw->index++; + + *json_out = json; + goto end; +err: + json_clear(json); + json_clear(obj); + +end: + return ret; +} + +bool json_object_add(struct json *dest, const char *key, struct json *src) { + if (!dest || !key || !src) + return false; + add_to_object(dest, strdup(key), src); + return true; +} + +struct json *json_object_get(const struct json *obj, const char *key) { + if (obj->type != JSON_OBJECT) return NULL; + struct json *j = NULL; + json_foreach(j, obj) { + if (strcmp(j->key, key) == 0) + break; + } + return j; +} + +struct json *json_object_getn(const struct json *obj, const char *key, size_t n) { + if (obj->type != JSON_OBJECT) return NULL; + struct json *j = NULL; + json_foreach(j, obj) { + if (strncmp(j->key, key, n) == 0) + break; + } + return j; +} + +struct json *json_object_add_object(struct json *dest, const char *key) { + struct json *obj = json_new_object(); + json_object_add(dest, key, obj); + return obj; +} + +struct json *json_object_add_array(struct json *dest, const char *key) { + struct json *array = json_new_array(); + json_object_add(dest, key, array); + return array; +} +struct json *json_object_add_number(struct json *dest, const char *key, double num) { + struct json *number = json_new_number(num); + json_object_add(dest, key, number); + return number; +} + +struct json *json_object_add_string(struct json *dest, const char *key, const char *string) { + struct json *str = json_new_string_copy(string); + json_object_add(dest, key, str); + return str; +} + +struct json *json_object_add_bool(struct json *dest, const char *key, bool boolean) { + struct json *b = json_new_bool(boolean); + json_object_add(dest, key, b); + return b; +} + +struct json *json_object_add_null(struct json *dest, const char *key) { + struct json *n = json_new_null(); + json_object_add(dest, key, n); + return n; +} + +struct json *json_object_detach(struct json *json, const char *key) { + struct json *tmp = json_object_get(json, key); + json_detach(tmp); + return tmp; +} +void json_object_delete(struct json *json, const char *key) { + struct json *tmp = json_object_detach(json, key); + json_delete(tmp); +} diff --git a/src/print.c b/src/print.c new file mode 100644 index 0000000..4b5c72c --- /dev/null +++ b/src/print.c @@ -0,0 +1,112 @@ +#include +#include +#include + +static bool append_printf(char **dest, size_t *size, size_t *index, const char *fmt, ...) { + va_list ap; + int len; + + va_start(ap, fmt); + len = vsnprintf(*dest + *index, *size - *index, fmt, ap); + va_end(ap); + + if (*index + len >= *size) { + *size = *size * 2 > *size + len + 1 ? *size * 2 : *size + len + 1; + *dest = realloc(*dest, *size); + + va_start(ap, fmt); + len = vsnprintf(*dest + *index, *size - *index, fmt, ap); + va_end(ap); + } + + // TODO: Fix this ugly cast + if (len < 0 || len >= (ssize_t)*size) { + free(*dest); + *dest = ""; + return false; + } + + *index += len; + + return true; +} + +void print_item(struct json *json, char **str, size_t *size, size_t *index) { + switch (json->type) { + case JSON_OBJECT: + append_printf(str, size, index, "{"); + for (struct json *j = json->children->items[0]; j; j = j->next) { + append_printf(str, size, index, "\"%s\":", j->key ? j->key : "(null)"); + print_item(j, str, size, index); + if (j->next) + append_printf(str, size, index, ","); + } + append_printf(str, size, index, "}"); + break; + case JSON_ARRAY: + append_printf(str, size, index, "["); + for (size_t i = 0; i < json->children->nitems; i++) { + struct json *j = json->children->items[i]; + print_item(j, str, size, index); + if (i != json->children->nitems - 1) + append_printf(str, size, index, ","); + } + append_printf(str, size, index, "]"); + break; + case JSON_NUMBER: + append_printf(str, size, index, "%lf", json->num); + break; + case JSON_TRUE: + append_printf(str, size, index, "true"); + break; + case JSON_FALSE: + append_printf(str, size, index, "false"); + break; + case JSON_NULL: + append_printf(str, size, index, "null"); + break; + case JSON_STRING: + append_printf(str, size, index, "\""); + for (char *ptr = json->string; *ptr != '\0'; ptr++) { + switch (*ptr) { + case '\\': + append_printf(str, size, index, "%s", "\\\\"); + break; + case '\"': + append_printf(str, size, index, "%s", "\\\""); + break; + case '\n': + append_printf(str, size, index, "%s", "\\n"); + break; + case '\f': + append_printf(str, size, index, "%s", "\\f"); + break; + case '\b': + append_printf(str, size, index, "%s", "\\b"); + break; + case '\r': + append_printf(str, size, index, "%s", "\\r"); + break; + case '\t': + append_printf(str, size, index, "%s", "\\t"); + break; + default: + append_printf(str, size, index, "%c", *ptr); + break; + } + } + append_printf(str, size, index, "\""); + break; + case JSON_INVALID: + break; + } +} + +char *json_print(struct json *json) { + size_t size = 1024; + size_t index = 0; + char *str = calloc(size, sizeof(char)); + print_item(json, &str, &size, &index); + return str; +} + diff --git a/src/string.c b/src/string.c new file mode 100644 index 0000000..2aea375 --- /dev/null +++ b/src/string.c @@ -0,0 +1,273 @@ +#include +#include +#include +#include +#include "internal.h" + +char *json_string_get(const struct json *str) { + if (str->type != JSON_STRING) + return NULL; + return str->string; +} + +enum json_parse_result parse_string(struct json **json_out, struct raw_json *raw) { + struct json *json = json_new_string(); + if (!json) + return JSON_OOM; + enum json_parse_result ret = parse_raw_string(&json->string, raw); + if (ret == JSON_PARSE_OK) { + *json_out = json; + } else { + json_delete(json); + } + return ret; +} + +static inline +enum json_parse_result parse_octet(uint8_t *out, const char ch) { + uint8_t value = ch; + if (value >= '0' && value <= '9') { + *out = value - '0'; + return JSON_PARSE_OK; + } + + value |= 0x20; // set 6th bit to force lowercase. + + if (value >= 'a' && value <= 'f') { + *out = 9 + (value & 0xF); // 'a' & 00001111 == 0001, but 0xA == 10, so +9 + return JSON_PARSE_OK; + } + + return JSON_PARSE_STRING_INVALID_UNICODE_ERR; +} + +static inline +enum json_parse_result parse_hex_pair(uint32_t *out, struct raw_json *raw) { + if (raw->index + 4 > raw->size) { + return JSON_PARSE_STRING_INVALID_UNICODE_ERR; + } + enum json_parse_result ret = JSON_PARSE_OK; + uint8_t hihi = 0; + uint8_t hilo = 0; + + ret = parse_octet(&hihi, raw->data[raw->index++]); + if (ret != JSON_PARSE_OK) + return ret; + hihi <<= 4; + ret = parse_octet(&hilo, raw->data[raw->index++]); + if (ret != JSON_PARSE_OK) + return ret; + + uint8_t hi = hihi | hilo; + + ret = parse_octet(&hihi, raw->data[raw->index++]); + if (ret != JSON_PARSE_OK) + return ret; + hihi <<= 4; + ret = parse_octet(&hilo, raw->data[raw->index++]); + if (ret != JSON_PARSE_OK) + return ret; + + uint8_t lo = hihi | hilo; + + *out = (hi << 8) | lo; + return ret; +} + +static inline +enum json_parse_result parse_unicode(char *str, size_t len, size_t i, struct raw_json *raw) { + enum json_parse_result ret = JSON_PARSE_OK; + uint32_t codepoint = 0; + ret = parse_hex_pair(&codepoint, raw); + if (ret != JSON_PARSE_OK) { + return ret; + } + + if (codepoint <= 0x1F || (codepoint >= 0x7F && codepoint <= 0x9F)) { + if (i + 6 > len || i + 6 > raw->size) { + return JSON_PARSE_STRING_INVALID_ERR; + } + + /* unescaping the same codepoints as we do on ascii */ + switch (codepoint) { + case 0x8: + str[i++] = '\b'; + return JSON_PARSE_OK; + case 0x9: + str[i++] = '\t'; + return JSON_PARSE_OK; + case 0xa: + str[i++] = '\n'; + return JSON_PARSE_OK; + case 0xc: + str[i++] = '\f'; + return JSON_PARSE_OK; + case 0xd: + str[i++] = '\r'; + return JSON_PARSE_OK; + } + + /* rolling back index so we can parse the codepoint again */ + raw->index -= 6; + for (size_t limit = i + 6; i < limit; i++) { + str[i] = raw->data[raw->index++]; + } + /* the calling function expects to do the last advance */ + raw->index--; + return ret; + } + + /* here we're dealing with a utf-16 surrogate pair*/ + if (codepoint >= 0xD800 && codepoint <= 0xDFFF) { + if (raw->index + 6 > raw->size + || (raw->data[raw->index++] != '\\' || raw->data[raw->index++] != 'u') + || codepoint < 0xD800 || codepoint > 0xDBFF) { + ret = JSON_PARSE_STRING_INVALID_UNICODE_ERR; + return ret; + } + uint32_t second_codepoint = 0; + ret = parse_hex_pair(&second_codepoint, raw); + if (ret != JSON_PARSE_OK) { + return ret; + } + + if (second_codepoint < 0xDC00 || second_codepoint > 0xDFFF) { + return JSON_PARSE_STRING_INVALID_UNICODE_ERR; + } + /* 0x3FF = 00000011111111 - we mask the lower 10 bits of both, + * the first codepoint make up the high 10 bits of the result, + * the second makes up the low 10 bits */ + codepoint = ((codepoint & 0x3FF) << 10) | (second_codepoint & 0x3FF); + codepoint += 0x10000; + if (codepoint < 0x110000 && i + 4 < len) { + str[i++] = 0xF0 | (codepoint >> 18); + str[i++] = 0x80 | ((codepoint >> 12) & 0x3F); + str[i++] = 0x80 | ((codepoint >> 6) & 0x3F); + str[i] = 0x80 | (codepoint & 0x3F); + } else { + ret = JSON_PARSE_STRING_INVALID_UNICODE_ERR; + return ret; + } + /* here it's utf-8 */ + } else { + if (codepoint < 0x80) { + str[i] = codepoint; + } else if (codepoint < 0x800) { + str[i++] = 0xC0 | (codepoint >> 6); + str[i] = 0x80 | (codepoint & 0x3F); + } else if (codepoint < 0x10000) { + str[i++] = 0xE0 | (codepoint >> 12); + str[i++] = 0x80 | ((codepoint >> 6) & 0x3F); + str[i] = 0x80 | (codepoint & 0x3F); + } else { + ret = JSON_PARSE_STRING_INVALID_UNICODE_ERR; + return ret; + } + } + + /* the calling function expects to do the last advance */ + raw->index--; + return ret; +} + +enum json_parse_result parse_raw_string(char **str_out, struct raw_json *raw) { + assert(raw->data[raw->index] == '"'); + assert(str_out); + assert(raw); + + char *str = NULL; + enum json_parse_result ret = JSON_PARSE_OK; + size_t end = raw->index + 1; + size_t skipped = 0; + for (; end < raw->size && raw->data[end] != '"'; end++) { + switch (raw->data[end]) { + case '\n': + case '\f': + case '\b': + case '\r': + case '\t': + case 0: + ret = JSON_PARSE_STRING_INVALID_ESCAPE_ERR; + goto err; + case '\\': + /* for unicode escapes, we can't skip bytes, since control + * escape codes will not be parsed, and will be stored as is */ + if (++end < raw->size && raw->data[end] != 'u') { + skipped++; + } + break; + } + } + + if (raw->data[end] != '"') { + ret = JSON_PARSE_STRING_INVALID_ERR; + goto err; + } + + size_t len = end - raw->index - skipped; + str = calloc(len + 1, sizeof(char)); + if (!str) { + return JSON_OOM; + } + size_t i; + for (i = 0, raw->index++; raw->index < raw->size && raw->data[raw->index] != '"' && i < len; raw->index++, i++) { + if (raw->data[raw->index] == '\\' && raw->index + 1 < raw->size) { + switch (raw->data[++raw->index]) { + case '\\': + case '\"': + case '/': + break; + case 'n': + str[i] = '\n'; + continue; + case 'f': + str[i] = '\f'; + continue; + case 'b': + str[i] = '\b'; + continue; + case 'r': + str[i] = '\r'; + continue; + case 't': + str[i] = '\t'; + continue; + case 'u': { + raw->index++; + ret = parse_unicode(str, len, i, raw); + if (ret != JSON_PARSE_OK) + goto err; + continue; + } + default: + ret = JSON_PARSE_STRING_INVALID_ESCAPE_ERR; + goto err; + } + } + str[i] = raw->data[raw->index]; + } + + if (raw->data[raw->index++] != '"') { + ret = JSON_PARSE_INVALID_STRING_ERR; + goto err; + } + + *str_out = str; + goto end; +err: + free(str); +end: + return ret; +} + +void json_string_set(struct json *dest, const char *string) { + if (dest->type != JSON_STRING) { + char *key = dest->key; + dest->key = NULL; + json_clear(dest); + dest->key = key; + dest->type = JSON_STRING; + } + free(dest->string); + dest->string = strdup(string); +} -- cgit v1.2.3