// // Copyright (C) 2022 by Anton Kling // // SPDX-License-Identifier: BSD-2-Clause // #include "hashmap/hashmap.h" #include #include #include #include #include #include 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]); }