diff options
author | Anton Kling <anton@kling.gg> | 2023-10-22 19:50:38 +0200 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2023-10-22 19:50:38 +0200 |
commit | 4e09bca9e34c226b6d7e34b4fa11248405fd988e (patch) | |
tree | 80f156b7940d9d19971395f335530170c69516c7 /userland/json/json.c |
Move everything into a new repo.
Diffstat (limited to 'userland/json/json.c')
-rw-r--r-- | userland/json/json.c | 360 |
1 files changed, 360 insertions, 0 deletions
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]); +} |