summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/array.c124
-rw-r--r--src/internal.h74
-rw-r--r--src/json.c164
-rw-r--r--src/literal.c58
-rw-r--r--src/object.c193
-rw-r--r--src/print.c112
-rw-r--r--src/string.c273
7 files changed, 998 insertions, 0 deletions
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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <json.h>
+#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 <stdlib.h>
+
+#include <json.h>
+
+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 <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <json.h>
+#include "internal.h"
+
+#include <stdio.h>
+#include <time.h>
+#include <signal.h>
+
+
+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 <ctype.h>
+#include <string.h>
+#include <json.h>
+#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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <json.h>
+#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 <stdarg.h>
+#include <stdio.h>
+#include <json.h>
+
+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 <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <json.h>
+#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);
+}