-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
409 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,9 @@ | |
|
||
#include <stdint.h> | ||
|
||
#include "s8.h" | ||
|
||
uint32_t hash_name(const char *s); | ||
uint32_t hash_name_s8(struct s8 s); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#ifndef _JSON_H | ||
#define _JSON_H | ||
|
||
#include <stdbool.h> | ||
#include <stdint.h> | ||
|
||
#include "s8.h" | ||
|
||
enum json_type { | ||
Json_Array, | ||
Json_Object, | ||
Json_Null, | ||
Json_Number, | ||
Json_String, | ||
Json_Bool, | ||
}; | ||
|
||
struct json_value { | ||
enum json_type type; | ||
union { | ||
struct s8 string; | ||
struct json_object *object; | ||
struct json_array *array; | ||
double number; | ||
bool boolean; | ||
} value; | ||
}; | ||
|
||
struct json_result { | ||
bool ok; | ||
union { | ||
const char *error; | ||
struct json_value document; | ||
} result; | ||
}; | ||
|
||
struct json_writer; | ||
|
||
struct json_result json_parse(uint8_t *buf, uint64_t size); | ||
void json_destroy(struct json_value *value); | ||
|
||
uint64_t json_len(struct json_object *obj); | ||
bool json_contains(struct json_object *obj, struct s8 key); | ||
struct json_value *json_get(struct json_object *obj, struct s8 key); | ||
|
||
uint64_t json_array_len(struct json_array *arr); | ||
void json_array_foreach(struct json_array *arr, | ||
void (*cb)(uint64_t, struct json_value)); | ||
struct json_value *json_array_get(struct json_array *arr, uint64_t idx); | ||
|
||
struct json_writer *json_writer_create(); | ||
struct s8 json_writer_done(struct json_writer *writer); | ||
|
||
#endif |
Oops, something went wrong.