summaryrefslogtreecommitdiff
path: root/userland/json
diff options
context:
space:
mode:
Diffstat (limited to 'userland/json')
-rw-r--r--userland/json/Makefile18
m---------userland/json/hashmap0
-rw-r--r--userland/json/json.c360
-rw-r--r--userland/json/json.h39
-rw-r--r--userland/json/libjson.abin0 -> 6304 bytes
5 files changed, 417 insertions, 0 deletions
diff --git a/userland/json/Makefile b/userland/json/Makefile
new file mode 100644
index 0000000..9fcb567
--- /dev/null
+++ b/userland/json/Makefile
@@ -0,0 +1,18 @@
+#CC="/home/anton/opt/cross/bin/i686-elf-gcc"
+#AR="/home/anton/opt/cross/bin/i686-elf-ar"
+CC="/home/anton/prj/osdev/sysroot/bin/i686-sb-gcc"
+AR="/home/anton/prj/osdev/sysroot/bin/i686-sb-ar"
+CFLAGS = -O0 -Wall -Wextra -pedantic -Wimplicit-fallthrough -static -Wno-undef
+BINS=libjson.a
+all: $(BINS)
+LIBS=-L./hashmap -lhashmap
+OBJ=json.o
+
+json.o: json.c
+ $(CC) $(CFLAGS) $(LIBS) -c json.c
+
+libjson.a: $(OBJ)
+ $(AR) rcs libjson.a $^
+
+clean:
+ rm libjson.a json.o
diff --git a/userland/json/hashmap b/userland/json/hashmap
new file mode 160000
+Subproject 07dfe3b606e0d82b2e181b27344b5e4ce8849f3
diff --git a/userland/json/json.c b/userland/json/json.c
new file mode 100644
index 0000000..6449193
--- /dev/null
+++ b/userland/json/json.c
@@ -0,0 +1,360 @@
+//
+// Copyright (C) 2022 by Anton Kling <anton@kling.gg>
+//
+// SPDX-License-Identifier: BSD-2-Clause
+//
+#include "hashmap/hashmap.h"
+#include <assert.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef enum JSON_TYPE {
+ NONE,
+ STRING,
+ NUMBER,
+ OBJECT,
+ ARRAY, // FIXME
+ BOOL,
+ JSON_NULL,
+} JSON_TYPE;
+
+typedef struct JSON_ENTRY {
+ char *name;
+ JSON_TYPE type;
+ void *value;
+} JSON_ENTRY;
+
+typedef struct JSON_OBJECT {
+ size_t size;
+ JSON_ENTRY *entries;
+ HashMap *hash_indexes;
+} JSON_OBJECT;
+
+typedef struct JSON_CTX {
+ JSON_ENTRY global_object;
+} JSON_CTX;
+
+void JSON_Init(JSON_CTX *ctx) {
+ ctx->global_object.name = NULL;
+ ctx->global_object.type = OBJECT;
+ ctx->global_object.value = malloc(sizeof(JSON_OBJECT));
+ JSON_OBJECT *obj = ctx->global_object.value;
+ obj->size = 0;
+ obj->entries = NULL;
+}
+
+const char *skip_whitespace(const char *str) {
+ for (; *str; str++)
+ switch (*str) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ break;
+ default:
+ goto _exit;
+ }
+_exit:
+ return str;
+}
+
+char to_lower(char c) {
+ if (c <= 'Z')
+ return c | 32;
+ return c;
+}
+
+int low_strequ(const char *s1, const char *s2, size_t l) {
+ for (; 0 < l; l--, s2++, s1++) {
+ if (to_lower(*s2) != to_lower(*s1))
+ return 0;
+ }
+ return 1;
+}
+
+const char *find_name(const char *json, char *name_buffer, size_t buffer_len,
+ size_t *name_length, int *rc) {
+ *rc = 0;
+ json = skip_whitespace(json);
+ if ('"' != *json)
+ return json;
+
+ // This *can* be the name
+ json++;
+ const char *tmp = json;
+ for (; *tmp && '"' != *tmp; tmp++)
+ ;
+ if (!(*tmp && ':' == *(tmp + 1))) {
+ // Invalid name
+ // FIXME: Do something better than this.
+ assert(0);
+ }
+ // The name was found.
+ *rc = 1;
+ size_t str_len;
+ if (tmp == json)
+ str_len = 0;
+ else
+ str_len = (tmp - json - 1);
+ if (str_len + 1 > buffer_len) {
+ // JSON string is too long for the buffer
+ // FIXME: Do something better than this.
+ assert(0);
+ }
+ memcpy(name_buffer, json, str_len + 1);
+ name_buffer[str_len + 1] = '\0';
+ *name_length = str_len;
+ return tmp + 2;
+}
+
+const char *find_entry(const char *json, char *name_buffer, size_t buffer_len,
+ size_t *name_length, JSON_TYPE *entry_type, int *rc) {
+ *rc = 0;
+ json = skip_whitespace(json);
+ json = find_name(json, name_buffer, 4096, name_length, rc);
+ json = skip_whitespace(json);
+ switch (*json) {
+ case '{':
+ *entry_type = OBJECT;
+ break;
+ case '"':
+ *entry_type = STRING;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ *entry_type = NUMBER;
+ break;
+ case '[':
+ *entry_type = ARRAY;
+ break;
+ case 'T':
+ case 'F':
+ case 't':
+ case 'f':
+ *entry_type = BOOL;
+ break;
+ case 'N':
+ case 'n':
+ *entry_type = JSON_NULL;
+ break;
+ case '}':
+ *rc = 2;
+ break;
+ default:
+ *entry_type = NONE;
+ break;
+ }
+ return json;
+}
+
+const char *extract_int(const char *json, uint32_t **value, int *rc) {
+ *value = malloc(sizeof(uint32_t));
+ *rc = 0;
+ json = skip_whitespace(json);
+ // FIXME: Do some checking to determine that this is a int.
+ **value = 0;
+ for (; *json && ',' != *json && '}' != *json;) {
+ **value *= 10;
+ **value += (uint32_t)(*json - '0');
+ json++;
+ json = skip_whitespace(json);
+ }
+ if ('\0' != *json)
+ *rc = 1;
+ return json;
+}
+
+const char *extract_string(const char *json, char **value, int *rc) {
+ *value = malloc(4096);
+ *rc = 0;
+ int escape = 0;
+ json++;
+ char *tmp = *value;
+ for (; *json; json++) {
+ if ('"' == *json && !escape)
+ break;
+ escape = 0;
+
+ if ('\\' == *json && !escape) {
+ escape = 1;
+ continue;
+ }
+ *tmp = *json;
+ tmp++;
+ }
+ *tmp = '\0';
+ if ('\0' != *json)
+ *rc = 1;
+ return json;
+}
+
+const char *extract_bool(const char *json, uint8_t **value, int *rc) {
+ *value = malloc(sizeof(uint8_t));
+ *rc = 0;
+ if (low_strequ(json, "true", 3)) {
+ **value = 1;
+ json += 4;
+ *rc = 1;
+ return json;
+ } else if (low_strequ(json, "false", 4)) {
+ **value = 0;
+ json += 5;
+ *rc = 1;
+ return json;
+ }
+ return json;
+}
+
+const char *extract_null(const char *json, uint8_t **value, int *rc) {
+ *value = malloc(sizeof(uint8_t));
+ *rc = 0;
+
+ if (low_strequ(json, "null", 3)) {
+ **value = 0;
+ json += 4;
+ *rc = 1;
+ return json;
+ }
+ return json;
+}
+
+void hashmap_free_value(char *key, void *value) {
+ (void)key;
+ free(value);
+}
+
+JSON_ENTRY *JSON_at(JSON_ENTRY *entry, size_t i);
+
+void JSON_Parse(JSON_CTX *ctx, const char *json) {
+ int rc;
+ char name_buffer[4096];
+ size_t name_length;
+ JSON_TYPE entry_type;
+ JSON_OBJECT *object_stack[64];
+ memset(object_stack, 0, sizeof(JSON_OBJECT *[64]));
+ size_t parent_num = 0;
+ object_stack[parent_num] = ctx->global_object.value;
+ object_stack[parent_num]->size = 0;
+
+ for (;; json++) {
+ json = find_entry(json, name_buffer, 4096, &name_length, &entry_type, &rc);
+ if ('\0' == *json)
+ return;
+
+ if (2 == rc) {
+ if (0 == parent_num)
+ return;
+ parent_num--;
+ json++;
+ json = skip_whitespace(json);
+ if (',' == *json)
+ json++;
+ continue;
+ }
+
+ if (NULL == object_stack[parent_num]->entries) {
+ object_stack[parent_num]->entries = malloc(sizeof(JSON_ENTRY[30]));
+ object_stack[parent_num]->size = 0;
+ object_stack[parent_num]->hash_indexes = hashmap_create(30);
+ }
+
+ size_t entry_num = object_stack[parent_num]->size;
+ object_stack[parent_num]->size++;
+
+ if (NULL == object_stack[parent_num]->entries) {
+ void *rc = object_stack[parent_num]->entries =
+ malloc(sizeof(JSON_ENTRY[30]));
+ object_stack[parent_num]->entries = rc;
+ object_stack[parent_num]->size = 0;
+ object_stack[parent_num]->hash_indexes = hashmap_create(30);
+ }
+ JSON_ENTRY *entry = &object_stack[parent_num]->entries[entry_num];
+ if (1 == rc) {
+ name_length++;
+ entry->name = malloc(name_length + 2);
+ memcpy(entry->name, name_buffer, name_length);
+ entry->name[name_length] = '\0';
+ size_t *b = malloc(sizeof(size_t));
+ *b = entry_num;
+ hashmap_add_entry(object_stack[parent_num]->hash_indexes, entry->name, b,
+ hashmap_free_value, 1);
+ } else {
+ entry->name = NULL;
+ }
+ entry->type = entry_type;
+ if (OBJECT == entry_type) {
+ entry->value = malloc(sizeof(JSON_OBJECT));
+ parent_num++;
+ object_stack[parent_num] = entry->value;
+ object_stack[parent_num]->entries = NULL;
+ continue;
+ } else if (NUMBER == entry_type) {
+ json = extract_int(json, (uint32_t **)&entry->value, &rc);
+ } else if (STRING == entry_type) {
+ json = extract_string(json, (char **)&entry->value, &rc);
+ } else if (BOOL == entry_type) {
+ json = extract_bool(json, (uint8_t **)&entry->value, &rc);
+ } else if (JSON_NULL == entry_type) {
+ json = extract_null(json, (uint8_t **)&entry->value, &rc);
+ }
+ json++;
+ }
+}
+
+JSON_ENTRY *JSON_at(JSON_ENTRY *entry, size_t i) {
+ if (OBJECT != entry->type)
+ return NULL;
+ return &((JSON_OBJECT *)(entry->value))->entries[i];
+}
+
+JSON_ENTRY *JSON_search_name(JSON_ENTRY *entry, char *name) {
+ if (OBJECT != entry->type)
+ return NULL;
+ size_t *i =
+ hashmap_get_entry(((JSON_OBJECT *)(entry->value))->hash_indexes, name);
+ if (!i)
+ return NULL;
+ return JSON_at(entry, *i);
+}
+
+void write_entry_content(int fd, size_t indent, JSON_ENTRY *entry) {
+#define PLACE_TABS \
+ { \
+ for (size_t i = 0; i < indent; i++) \
+ dprintf(fd, "\t"); \
+ }
+ PLACE_TABS
+ if (entry->name)
+ dprintf(fd, "\"%s\": ", entry->name);
+ if (OBJECT == entry->type) {
+ JSON_OBJECT *object = entry->value;
+ dprintf(fd, "{\n");
+ for (size_t i = 0; i < object->size; i++)
+ write_entry_content(fd, indent + 1, &object->entries[i]);
+ PLACE_TABS
+ dprintf(fd, "},\n");
+ } else if (NUMBER == entry->type)
+ dprintf(fd, "%d,\n", *(uint32_t *)entry->value);
+ else if (STRING == entry->type)
+ dprintf(fd, "\"%s\",\n", (char *)entry->value);
+ else if (BOOL == entry->type)
+ dprintf(fd, "%s,\n", ((*(uint8_t *)entry->value) ? "true" : "false"));
+ else if (JSON_NULL == entry->type)
+ dprintf(fd, "null,\n");
+}
+
+void JSON_extract_fd(JSON_CTX *ctx, int fd) {
+ JSON_ENTRY *entry = &ctx->global_object;
+ write_entry_content(fd, 0, &((JSON_OBJECT *)entry->value)->entries[0]);
+}
diff --git a/userland/json/json.h b/userland/json/json.h
new file mode 100644
index 0000000..642a878
--- /dev/null
+++ b/userland/json/json.h
@@ -0,0 +1,39 @@
+#ifndef JSON_H
+#define JSON_H
+#include "hashmap/hashmap.h"
+#include <stddef.h>
+#include <stdint.h>
+
+typedef enum JSON_TYPE {
+ NONE,
+ STRING,
+ NUMBER,
+ OBJECT,
+ ARRAY, // FIXME
+ BOOL,
+ JSON_NULL,
+} JSON_TYPE;
+
+typedef struct JSON_ENTRY {
+ char *name;
+ JSON_TYPE type;
+ void *value;
+} JSON_ENTRY;
+
+typedef struct JSON_OBJECT {
+ uint64_t size;
+ JSON_ENTRY *entries;
+ HashMap *hash_indexes;
+} JSON_OBJECT;
+
+typedef struct JSON_CTX {
+ JSON_ENTRY global_object;
+} JSON_CTX;
+
+void JSON_Init(JSON_CTX *ctx);
+void JSON_Parse(JSON_CTX *ctx, const char *json);
+void JSON_extract_fd(JSON_CTX *ctx, int whitespace, int fd);
+uint64_t JSON_extract_length(JSON_CTX *ctx, int whitespace);
+JSON_ENTRY *JSON_at(JSON_ENTRY *entry, uint64_t i);
+JSON_ENTRY *JSON_search_name(JSON_ENTRY *entry, char *name);
+#endif
diff --git a/userland/json/libjson.a b/userland/json/libjson.a
new file mode 100644
index 0000000..ca66650
--- /dev/null
+++ b/userland/json/libjson.a
Binary files differ