diff options
Diffstat (limited to 'src/dged/json.c')
| -rw-r--r-- | src/dged/json.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/src/dged/json.c b/src/dged/json.c new file mode 100644 index 0000000..24d5c15 --- /dev/null +++ b/src/dged/json.c @@ -0,0 +1,234 @@ +#include "json.h" + +#include "hash.h" +#include "hashmap.h" +#include "utf8.h" +#include "vec.h" + +#include <stddef.h> +#include <stdio.h> + +HASHMAP_ENTRY_TYPE(json_object_member, struct json_value); + +struct json_object { + HASHMAP(struct json_object_member) members; +}; + +struct json_array { + VEC(struct json_value) values; +}; + +static void setarray(struct json_value *val) { + val->type = Json_Array; + val->value.array = calloc(1, sizeof(struct json_array)); + VEC_INIT(&val->value.array->values, 10); +} + +static void setobject(struct json_value *val) { + val->type = Json_Object; + val->value.object = calloc(1, sizeof(struct json_object)); + HASHMAP_INIT(&val->value.object->members, 10, hash_name); +} + +static void setstring(struct json_value *val, uint8_t *current) { + val->type = Json_String; + val->value.string.s = current; + val->value.string.l = 0; +} + +static bool is_number(uint8_t byte) { return byte >= '0' && byte <= '9'; } + +enum object_parse_state { + ObjectParseState_Key, + ObjectParseState_Value, +}; + +struct json_result json_parse(uint8_t *buf, uint64_t size) { + struct json_result res = { + .ok = true, + .result.document.type = Json_Null, + }; + + struct json_value *parent = NULL; + struct json_value *current = &res.result.document; + struct json_value tmp_key = {0}; + struct json_value tmp_val = {0}; + uint32_t line = 1, col = 0; + + enum object_parse_state obj_parse_state = ObjectParseState_Key; + for (uint64_t bufi = 0; bufi < size; ++bufi) { + uint8_t byte = buf[bufi]; + + // handle appends to the current scope + if (current->type == Json_Array) { + VEC_PUSH(¤t->value.array->values, tmp_val); + parent = current; + + // start looking for next value + tmp_val.type = Json_Null; + current = &tmp_val; + } else if (current->type == Json_Object && + obj_parse_state == ObjectParseState_Key) { + // key is in tmp_key, start looking for value + obj_parse_state = ObjectParseState_Value; + parent = current; + + tmp_val.type = Json_Null; + current = &tmp_val; + } else if (current->type == Json_Object && + obj_parse_state == ObjectParseState_Value) { + // value is in tmp_val + // TODO: remove this alloc, should not be needed + char *k = s8tocstr(tmp_key.value.string); + uint32_t hash = 0; + HASHMAP_INSERT(¤t->value.object->members, struct json_object_member, + k, tmp_val, hash); + (void)hash; + free(k); + + // start looking for next key + obj_parse_state = ObjectParseState_Key; + parent = current; + + tmp_key.type = Json_Null; + current = &tmp_key; + } + + switch (byte) { + case '[': + setarray(current); + parent = current; + + tmp_val.type = Json_Null; + current = &tmp_val; + break; + case ']': + current = parent; + break; + case '{': + setobject(current); + obj_parse_state = ObjectParseState_Key; + parent = current; + + tmp_key.type = Json_Null; + current = &tmp_key; + break; + case '}': + current = parent; + break; + case '"': + if (current->type == Json_String) { + // finish off the string + current->value.string.l = (buf + bufi) - current->value.string.s; + current = parent; + } else { + setstring(current, buf + bufi + 1 /* skip " */); + } + break; + case '\n': + ++line; + col = 0; + break; + default: + if (current->type == Json_String) { + // append to string + } else if (current->type == Json_Number && + !(is_number(byte) || byte == '-' || byte == '.')) { + // end of number + current->value.string.l = (buf + bufi) - current->value.string.s; + char *nmbr = s8tocstr(current->value.string); + current->value.number = atof(nmbr); + free(nmbr); + + current = parent; + + } else if (current->type == Json_Null && + (is_number(byte) || byte == '-' || byte == '.')) { + // borrow string storage in the value for storing number + // as a string + setstring(current, buf + bufi); + current->type = Json_Number; + } else if (byte == 't') { + current->type = Json_Bool; + current->value.boolean = true; + + current = parent; + } else if (byte == 'f') { + current->type = Json_Bool; + current->value.boolean = false; + + current = parent; + } else if (byte == 'n') { + current->type = Json_Null; + + current = parent; + } + break; + } + + // TODO: not entirely correct + ++col; + } + return res; +} + +void json_destroy(struct json_value *value) { + switch (value->type) { + case Json_Array: + struct json_array *arr = value->value.array; + VEC_FOR_EACH(&arr->values, struct json_value * val) { json_destroy(val); } + VEC_DESTROY(&arr->values); + break; + case Json_Object: + struct json_object *obj = value->value.object; + HASHMAP_FOR_EACH(&obj->members, struct json_object_member * memb) { + json_destroy(&memb->value); + } + + HASHMAP_DESTROY(&obj->members); + case Json_Null: + case Json_Number: + case Json_String: + case Json_Bool: + break; + } +} + +uint64_t json_array_len(struct json_array *arr) { + return VEC_SIZE(&arr->values); +} + +struct json_value *json_array_get(struct json_array *arr, uint64_t idx) { + struct json_value *ret = NULL; + + if (idx <= VEC_SIZE(&arr->values)) { + ret = &VEC_ENTRIES(&arr->values)[idx]; + } + + return ret; +} + +uint64_t json_len(struct json_object *obj) { + return HASHMAP_SIZE(&obj->members); +} + +bool json_contains(struct json_object *obj, struct s8 key) { + // TODO: get rid of alloc + char *k = s8tocstr(key); + HASHMAP_CONTAINS_KEY(&obj->members, struct json_object_member, k, bool res); + + free(k); + + return res; +} + +struct json_value *json_get(struct json_object *obj, struct s8 key) { + // TODO: get rid of alloc + char *k = s8tocstr(key); + HASHMAP_GET(&obj->members, struct json_object_member, k, + struct json_value * result); + + free(k); + + return result; +} |
