Skip to content

Instantly share code, notes, and snippets.

@b4284
Created July 27, 2017 15:22
Show Gist options
  • Select an option

  • Save b4284/f5da320384fa5e2323ef7b511abcfe34 to your computer and use it in GitHub Desktop.

Select an option

Save b4284/f5da320384fa5e2323ef7b511abcfe34 to your computer and use it in GitHub Desktop.
#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