Created
July 27, 2017 15:22
-
-
Save b4284/f5da320384fa5e2323ef7b511abcfe34 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdbool.h> | |
| #include <string.h> | |
| #include <stdint.h> | |
| #define JSON_THINGS_SIZE 256 | |
| #define JSON_STRINGS_SIZE 1024 | |
| #define JSON_COMPOUNDS_SIZE 256 | |
| typedef enum { | |
| JSON_UNPARSED, | |
| JSON_NULL, | |
| JSON_BOOL, | |
| JSON_NUMBER, | |
| JSON_STRING, | |
| JSON_ARRAY, | |
| JSON_OBJECT | |
| } json_thing_type_t; | |
| typedef struct json_compound_t { | |
| const char *key; | |
| struct json_thing_t *thing; | |
| struct json_compound_t *next; | |
| } json_compound_t; | |
| typedef struct json_thing_t { | |
| json_thing_type_t type; | |
| union { | |
| const char *string; | |
| int32_t integer; | |
| bool boolean; | |
| json_compound_t *compound; | |
| } val; | |
| } json_thing_t; | |
| typedef struct { | |
| void *mem_things; | |
| void *mem_strings; | |
| void *mem_compounds; | |
| json_thing_t *things; | |
| uint16_t things_rem; | |
| uint16_t things_total; | |
| char *strings; | |
| uint16_t strings_rem; | |
| uint16_t strings_total; | |
| json_compound_t *compounds; | |
| uint16_t compounds_rem; | |
| uint16_t compounds_total; | |
| } json_t; | |
| json_t *json_init(void) | |
| { | |
| void *mem = malloc(sizeof(json_t) | |
| + sizeof(json_thing_t) * JSON_THINGS_SIZE); | |
| if (mem == NULL) { | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| void *mem2 = malloc(JSON_STRINGS_SIZE); | |
| if (mem2 == NULL) { | |
| free(mem); | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| void *mem3 = malloc(sizeof(json_compound_t) * JSON_COMPOUNDS_SIZE); | |
| if (mem3 == NULL) { | |
| free(mem); | |
| free(mem2); | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| json_t *json = (json_t*)mem; | |
| json->mem_things = mem; | |
| json->mem_strings = mem2; | |
| json->mem_compounds = mem3; | |
| json->things = (json_thing_t*)((char*)mem + sizeof(json_t)); | |
| json->things_rem = JSON_THINGS_SIZE; | |
| json->things_total = JSON_THINGS_SIZE; | |
| json->strings = mem2; | |
| json->strings_rem = JSON_STRINGS_SIZE; | |
| json->strings_total = JSON_STRINGS_SIZE; | |
| json->compounds = mem3; | |
| json->compounds_rem = JSON_COMPOUNDS_SIZE; | |
| json->compounds_total = JSON_COMPOUNDS_SIZE; | |
| return json; | |
| } | |
| json_thing_t *json_allocate_thing(json_t *json) | |
| { | |
| if (json->things_rem == 0) { | |
| // TODO: reallocate. | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| json_thing_t *thing = json->things; | |
| json->things += 1; | |
| json->things_rem -= 1; | |
| return thing; | |
| } | |
| const char *json_allocate_string(json_t *json, const char *str, size_t len) | |
| { | |
| if (json->strings_rem < len + 1) { | |
| // TODO: reallocate. | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| char *ret = json->strings; | |
| strncpy(ret, str, len); | |
| ret[len] = '\0'; | |
| json->strings += (len + 1); | |
| json->strings_rem -= (len + 1); | |
| return ret; | |
| } | |
| json_compound_t *json_allocate_compound(json_t *json) | |
| { | |
| if (json->compounds_rem == 0) { | |
| // TODO: reallocate. | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| json_compound_t *compound = json->compounds; | |
| json->compounds += 1; | |
| json->compounds_rem -= 1; | |
| compound->key = NULL; | |
| compound->thing = NULL; | |
| compound->next = NULL; | |
| return compound; | |
| } | |
| json_thing_t *json_get_singleton(json_t *json, const char **json_str, | |
| int8_t type) | |
| { | |
| json_thing_t *thing = json_allocate_thing(json); | |
| if (thing == NULL) { | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| switch (type) { | |
| case 0: | |
| thing->type = JSON_NULL; | |
| (*json_str) += 4; | |
| break; | |
| case 1: | |
| thing->type = JSON_BOOL; | |
| thing->val.boolean = true; | |
| (*json_str) += 4; | |
| break; | |
| case -1: | |
| thing->type = JSON_BOOL; | |
| thing->val.boolean = false; | |
| (*json_str) += 5; | |
| break; | |
| } | |
| return thing; | |
| } | |
| json_thing_t *json_get_unparsed(json_t *json) { | |
| json_thing_t *thing = json_allocate_thing(json); | |
| if (thing == NULL) { | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| thing->type = JSON_UNPARSED; | |
| return thing; | |
| } | |
| json_thing_t *json_get_string(json_t *json, const char **json_str) | |
| { | |
| json_thing_t *thing = json_allocate_thing(json); | |
| if (thing == NULL) { | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| thing->type = JSON_STRING; | |
| thing->val.string = json->strings; | |
| (*json_str)++; | |
| bool escape = false; | |
| for (; **json_str != '\0'; (*json_str)++) { | |
| if (escape) { | |
| switch (**json_str) { | |
| case '\\': | |
| *(json->strings) = '\\'; | |
| break; | |
| case 't': | |
| *(json->strings) = '\t'; | |
| break; | |
| case 'r': | |
| *(json->strings) = '\r'; | |
| break; | |
| case 'n': | |
| *(json->strings) = '\n'; | |
| break; | |
| case '"': | |
| *(json->strings) = '"'; | |
| break; | |
| default: | |
| *(json->strings) = **json_str; | |
| break; | |
| } | |
| json->strings += 1; | |
| json->strings_rem -= 1; | |
| escape = false; | |
| } else { | |
| switch (**json_str) { | |
| case '\\': | |
| escape = true; | |
| break; | |
| case '"': | |
| (*json_str)++; | |
| goto END; | |
| default: | |
| *(json->strings) = **json_str; | |
| json->strings += 1; | |
| json->strings_rem -= 1; | |
| break; | |
| } | |
| } | |
| if (json->strings_rem < 2) { | |
| // TODO: reallocate. | |
| fprintf(stderr, "%d\n", __LINE__); | |
| return NULL; | |
| } | |
| } | |
| END: | |
| *(json->strings) = '\0'; | |
| json->strings += 1; | |
| json->strings_rem -= 1; | |
| return thing; | |
| } | |
| json_thing_t *json_parse(json_t *json, const char **json_str); | |
| json_thing_t *json_get_object(json_t *json, const char **json_str) | |
| { | |
| json_thing_t *obj_head = json_allocate_thing(json); | |
| if (obj_head == NULL) { | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| obj_head->type = JSON_OBJECT; | |
| obj_head->val.compound = NULL; | |
| (*json_str)++; | |
| json_compound_t *last_compound = NULL; | |
| json_compound_t *compound = NULL; | |
| bool is_key = true; | |
| while (**json_str != '\0') { | |
| if (is_key) { | |
| if (compound == NULL) { | |
| compound = json_allocate_compound(json); | |
| if (compound == NULL) { | |
| fprintf(stderr, "%d\n", __LINE__); | |
| return NULL; | |
| } | |
| compound->key = json->strings; | |
| compound->thing = NULL; | |
| compound->next = NULL; | |
| } | |
| switch (**json_str) { | |
| case '}': | |
| (*json_str)++; | |
| goto END; | |
| case ':': | |
| // TODO: detect out of memory. | |
| *(json->strings) = '\0'; | |
| json->strings += 1; | |
| json->strings_rem -= 1; | |
| is_key = false; | |
| (*json_str)++; | |
| break; | |
| case ' ': | |
| case ',': | |
| (*json_str)++; | |
| continue; | |
| default: | |
| *(json->strings) = **json_str; | |
| // TODO: detect out of memory. | |
| json->strings += 1; | |
| json->strings_rem -= 1; | |
| (*json_str)++; | |
| continue; | |
| } | |
| } | |
| json_thing_t *thing = json_parse(json, json_str); | |
| if (thing == NULL) { | |
| fprintf(stderr, "%d\n", __LINE__); | |
| return NULL; | |
| } | |
| compound->thing = thing; | |
| if (last_compound != NULL) { | |
| last_compound->next = compound; | |
| } else { | |
| obj_head->val.compound = compound; | |
| } | |
| last_compound = compound; | |
| compound = NULL; | |
| is_key = true; | |
| } | |
| END: | |
| return obj_head; | |
| } | |
| json_thing_t *json_get_array(json_t *json, const char **json_str) | |
| { | |
| json_thing_t *array_head = json_allocate_thing(json); | |
| if (array_head == NULL) { | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| array_head->type = JSON_ARRAY; | |
| array_head->val.compound = NULL; | |
| (*json_str)++; | |
| json_compound_t *last_compound = NULL; | |
| while (**json_str != '\0') { | |
| switch (**json_str) { | |
| json_thing_t *thing; | |
| json_compound_t *compound; | |
| case ']': | |
| (*json_str)++; | |
| goto END; | |
| case ' ': | |
| case ',': | |
| (*json_str)++; | |
| break; | |
| default: | |
| thing = json_parse(json, json_str); | |
| if (thing == NULL) { | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| compound = json_allocate_compound(json); | |
| if (compound == NULL) { | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| compound->thing = thing; | |
| if (last_compound != NULL) { | |
| last_compound->next = compound; | |
| } else { | |
| array_head->val.compound = compound; | |
| } | |
| last_compound = compound; | |
| break; | |
| } | |
| } | |
| END: | |
| return array_head; | |
| } | |
| json_thing_t *json_get_number(json_t *json, const char **json_str) | |
| { | |
| json_thing_t *thing = json_allocate_thing(json); | |
| if (thing == NULL) { | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| thing->type = JSON_NUMBER; | |
| thing->val.integer = 0; | |
| int8_t sign = 1; | |
| if (**json_str == '-') { | |
| sign = -1; | |
| (*json_str)++; | |
| } else if (**json_str == '+') { | |
| (*json_str)++; | |
| } | |
| for (; **json_str != '\0'; (*json_str)++) { | |
| if (**json_str >= '0' && **json_str <= '9') { | |
| thing->val.integer *= 10; | |
| thing->val.integer += (**json_str - '0'); | |
| } else { | |
| (*json_str)++; | |
| break; | |
| } | |
| } | |
| thing->val.integer *= sign; | |
| return thing; | |
| } | |
| json_thing_t *json_parse(json_t *json, const char **json_str) | |
| { | |
| while (**json_str != '\0') { | |
| switch (**json_str) { | |
| case '{': | |
| return json_get_object(json, json_str); | |
| case '[': | |
| return json_get_array(json, json_str); | |
| case '"': | |
| return json_get_string(json, json_str); | |
| case '+': | |
| case '-': | |
| case '0': | |
| case '1': | |
| case '2': | |
| case '3': | |
| case '4': | |
| case '5': | |
| case '6': | |
| case '7': | |
| case '8': | |
| case '9': | |
| return json_get_number(json, json_str); | |
| case 'n': | |
| case 't': | |
| case 'f': | |
| if (strncmp(*json_str, "null", 4) == 0) { | |
| return json_get_singleton(json, json_str, 0); | |
| } else if (strncmp(*json_str, "true", 4) == 0) { | |
| return json_get_singleton(json, json_str, 1); | |
| } else if (strncmp(*json_str, "false", 5) == 0) { | |
| return json_get_singleton(json, json_str, -1); | |
| } else { | |
| return json_get_unparsed(json); | |
| } | |
| case ' ': | |
| case '\t': | |
| case '\r': | |
| case '\n': | |
| (*json_str)++; | |
| break; | |
| default: | |
| (*json_str)++; | |
| return json_get_unparsed(json); | |
| } | |
| } | |
| fprintf(stderr, "%d\n", __LINE__); return NULL; | |
| } | |
| void json_print_thing(const json_thing_t *thing) { | |
| switch (thing->type) { | |
| case JSON_UNPARSED: | |
| printf("<*UNPARSED*>"); | |
| break; | |
| case JSON_NULL: | |
| printf("null"); | |
| break; | |
| case JSON_BOOL: | |
| printf("%s", thing->val.boolean ? "true" : "false"); | |
| break; | |
| case JSON_NUMBER: | |
| printf("%d", thing->val.integer); | |
| break; | |
| case JSON_STRING: | |
| putchar('"'); | |
| for (const char *c = thing->val.string; *c != '\0'; ++c) { | |
| switch (*c) { | |
| case '\\': | |
| putchar('\\'); | |
| putchar('\\'); | |
| break; | |
| case '\t': | |
| putchar('\\'); | |
| putchar('t'); | |
| break; | |
| case '\r': | |
| putchar('\\'); | |
| putchar('r'); | |
| break; | |
| case '\n': | |
| putchar('\\'); | |
| putchar('n'); | |
| break; | |
| case '"': | |
| putchar('\\'); | |
| putchar('"'); | |
| break; | |
| default: | |
| putchar(*c); | |
| } | |
| } | |
| putchar('"'); | |
| break; | |
| case JSON_ARRAY: | |
| putchar('['); | |
| for (const json_compound_t *compound = thing->val.compound; | |
| compound != NULL; compound = compound->next) | |
| { | |
| json_print_thing(compound->thing); | |
| if (compound->next != NULL) { | |
| putchar(','); | |
| putchar(' '); | |
| } | |
| } | |
| putchar(']'); | |
| break; | |
| case JSON_OBJECT: | |
| putchar('{'); | |
| for (const json_compound_t *compound = thing->val.compound; | |
| compound != NULL; compound = compound->next) | |
| { | |
| printf("\"%s\": ", compound->key); | |
| json_print_thing(compound->thing); | |
| if (compound->next != NULL) { | |
| putchar(','); | |
| putchar(' '); | |
| } | |
| } | |
| putchar('}'); | |
| break; | |
| } | |
| } | |
| void json_print_stats(const json_t *json) | |
| { | |
| size_t size_things = json->things_total - json->things_rem; | |
| size_t size_compounds = json->compounds_total - json->compounds_rem; | |
| size_t size_things_b = size_things * sizeof(json_thing_t); | |
| size_t size_strings_b = json->strings_total - json->strings_rem; | |
| size_t size_compounds_b = size_compounds * sizeof(json_compound_t); | |
| printf("%zu things (%zu bytes), %zu bytes of strings, " | |
| "%zu compounds (%zu bytes).\n%zu bytes in total.\n", | |
| size_things, size_things_b, size_strings_b, size_compounds, | |
| size_compounds_b, | |
| size_things_b + size_strings_b + size_compounds_b); | |
| } | |
| int main(int argc, char *argv[]) { | |
| if (argc < 2) { | |
| puts("Write some json."); | |
| return 1; | |
| } | |
| const char *json_str = argv[1]; | |
| json_t *json = json_init(); | |
| json_thing_t *thing = json_parse(json, &json_str); | |
| if (thing != NULL) { | |
| json_print_thing(thing); | |
| putchar('\n'); | |
| } | |
| json_print_stats(json); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment