Skip to content

Instantly share code, notes, and snippets.

@RuslanUC
Last active March 18, 2025 12:34
Show Gist options
  • Select an option

  • Save RuslanUC/032c3f916adaff0e794e643fa08df1c0 to your computer and use it in GitHub Desktop.

Select an option

Save RuslanUC/032c3f916adaff0e794e643fa08df1c0 to your computer and use it in GitHub Desktop.

Revisions

  1. RuslanUC revised this gist Mar 18, 2025. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions json.h
    Original file line number Diff line number Diff line change
    @@ -80,6 +80,9 @@ JsonBool json_parse_bool(const char* data, const size_t size, size_t* pos);
    JsonObject json_parse_object(const char* data, const size_t size, size_t* pos);
    JsonArray json_parse_array(const char* data, const size_t size, size_t* pos);

    /*
    WARNING: json_dump_string() writes string "as-is", i.e. without escaping quotes, null-termination checks, etc.
    */
    size_t json_dump_string(const JsonString string, char** out, size_t out_pos, size_t* out_cap);
    size_t json_dump_number(const JsonNumber number, char** out, size_t out_pos, size_t* out_cap);
    size_t json_dump_bool(const JsonBool boolean, char** out, size_t out_pos, size_t* out_cap);
  2. RuslanUC revised this gist Mar 18, 2025. 2 changed files with 213 additions and 13 deletions.
    220 changes: 207 additions & 13 deletions json.c
    Original file line number Diff line number Diff line change
    @@ -85,7 +85,10 @@ JsonValue parse_whatever(const char* data, size_t* pos, const size_t size) {
    return ret;
    }
    }
    default: return ret;
    default: {
    fprintf(stderr, "parse_whatever() failed at pos %zu, unexpected character.\n", *pos);
    return ret;
    }
    }
    }

    @@ -114,6 +117,7 @@ JsonString json_parse_string(const char* data, const size_t size, size_t* pos) {
    }

    // TODO: unexpected eof
    fprintf(stderr, "json_parse_string() failed at pos %zu, unexpected eof.\n", *pos);
    return (JsonString){
    .size = 0,
    .value = NULL,
    @@ -134,8 +138,10 @@ JsonNumber json_parse_number(const char* data, const size_t size, size_t* pos) {

    while(*pos < size && (subtoken = next_subtoken(data, pos, size, false)) > 0) {
    if(subtoken == '.') {
    if(is_fractional)
    if(is_fractional) {
    fprintf(stderr, "json_parse_number() failed at pos %zu, fractional number has multiple dots.\n", *pos);
    return ret; // TODO: invalid number
    }

    is_fractional = true;
    continue;
    @@ -155,6 +161,7 @@ JsonNumber json_parse_number(const char* data, const size_t size, size_t* pos) {
    }

    // TODO: unexpected eof
    fprintf(stderr, "json_parse_number() failed at pos %zu, unexpected eof.\n", *pos);
    return ret;
    }

    @@ -163,14 +170,18 @@ JsonBool json_parse_bool(const char* data, const size_t size, size_t* pos) {
    if(pos == NULL) pos = &lpos;
    const size_t bytes_left = size - *pos;

    if(bytes_left < 1 || ((data[*pos] != 't' || bytes_left < 4) && (data[*pos] != 'f' || bytes_left < 5)))
    if(bytes_left < 1 || ((data[*pos] != 't' || bytes_left < 4) && (data[*pos] != 'f' || bytes_left < 5))) {
    fprintf(stderr, "json_parse_bool() failed at pos %zu, invalid first character or unexpected eof.\n", *pos);
    return (JsonBool){ .value = false }; // TODO: invalid bool
    }

    if(data[*pos] == 't') {
    if(memcmp(data + *pos, "true", 4) == 0) {
    *pos += 4;
    return (JsonBool){ .value = true };
    }

    fprintf(stderr, "json_parse_bool() failed at pos %zu, invalid bool.\n", *pos);
    return (JsonBool){ .value = false }; // TODO: invalid bool
    }

    @@ -179,43 +190,64 @@ JsonBool json_parse_bool(const char* data, const size_t size, size_t* pos) {
    *pos += 5;
    return (JsonBool){ .value = false };
    }

    fprintf(stderr, "json_parse_bool() failed at pos %zu, invalid bool.\n", *pos);
    return (JsonBool){ .value = false }; // TODO: invalid bool
    }

    fprintf(stderr, "json_parse_bool() failed at pos %zu, this should be unreachable.\n", *pos);
    return (JsonBool){ .value = false }; // TODO: invalid bool
    }

    JsonObject json_parse_object(const char* data, const size_t size, size_t* pos) {
    char subtoken;
    size_t lpos = 0;
    if(pos == NULL) pos = &lpos;
    (*pos)++;

    JsonObject ret = {0};
    size_t capacity = 0;

    while(*pos < size && (subtoken = next_subtoken(data, pos, size, false)) > 0) {
    if(subtoken == '}')
    if(subtoken == '{')
    break;
    fprintf(stderr, "json_parse_object() failed at pos %zu, expected \"{\", got \"%c\".\n", *pos, subtoken);
    goto error;
    }

    while(*pos < size && (subtoken = next_subtoken(data, pos, size, false)) > 0) {
    if(subtoken == '}') {
    if(ret.items)
    ret.items = realloc(ret.items, ret.size * sizeof(JsonObjectItem));
    return ret;
    }
    if(ret.size > 0) {
    if(subtoken != ',')
    if(subtoken != ',') {
    fprintf(stderr, "json_parse_object() failed at pos %zu, expected \",\", got \"%c\".\n", *pos, subtoken);
    goto error;
    }
    subtoken = next_subtoken(data, pos, size, false);
    if(subtoken == '\0')
    if(subtoken == '\0') {
    fprintf(stderr, "json_parse_object() failed at pos %zu, unexpected eof.\n", *pos);
    goto error;
    }
    }

    --(*pos);
    const JsonValue key = parse_whatever(data, pos, size);
    if(key.type != JSON_VALUE_STRING)
    if(key.type != JSON_VALUE_STRING) {
    fprintf(stderr, "json_parse_object() failed at pos %zu, expected string, got type %d.\n", *pos, key.type);
    goto error;
    }

    subtoken = next_subtoken(data, pos, size, false);
    if(subtoken != ':')
    if(subtoken != ':') {
    fprintf(stderr, "json_parse_object() failed at pos %zu, expected \":\", got \"%c\".\n", *pos, subtoken);
    goto error;
    }

    const JsonValue value = parse_whatever(data, pos, size);
    if(value.type == JSON_VALUE_INVALID) {
    fprintf(stderr, "json_parse_object() failed at pos %zu, invalid value.\n", *pos);
    goto error;
    }

    @@ -235,7 +267,7 @@ JsonObject json_parse_object(const char* data, const size_t size, size_t* pos) {
    }

    error:
    // TODO: unexpected eof
    // TODO: return error "unexpected eof"
    json_free_object(ret);
    ret.items = NULL;
    ret.size = 0;
    @@ -252,8 +284,11 @@ JsonArray json_parse_array(const char* data, const size_t size, size_t* pos) {
    size_t capacity = 0;

    while(*pos < size && (subtoken = next_subtoken(data, pos, size, false)) > 0) {
    if(subtoken == ']')
    if(subtoken == ']') {
    if(ret.items)
    ret.items = realloc(ret.items, ret.size * sizeof(JsonValue));
    return ret;
    }
    if(ret.size > 0) {
    if(subtoken != ',')
    goto error;
    @@ -274,14 +309,15 @@ JsonArray json_parse_array(const char* data, const size_t size, size_t* pos) {
    else
    capacity *= 2;

    ret.items = realloc(ret.items, capacity * sizeof(JsonObjectItem));
    ret.items = realloc(ret.items, capacity * sizeof(JsonValue));
    }

    ret.items[ret.size++] = value;
    }

    error:
    // TODO: unexpected eof
    // TODO: return error "unexpected eof"
    fprintf(stderr, "json_parse_array() failed at pos %zu, unexpected eof.\n", *pos);
    json_free_array(ret);
    ret.items = NULL;
    ret.size = 0;
    @@ -330,6 +366,164 @@ void json_free_array(const JsonArray array) {
    free(array.items);
    }

    void out_buf_realloc_maybe(char** out, size_t pos, size_t* cap, size_t min_size) {
    if((pos + min_size) >= *cap || *cap == 0) {
    if(*cap == 0)
    *cap = min_size * 2;
    else
    *cap = (*cap + min_size) * 1.5;
    *out = realloc(*out, *cap);
    }
    }

    size_t json_dump_whatever(const JsonValue value, char** out, size_t out_pos, size_t* out_cap) {
    size_t lcap = 0;
    if(out_cap == NULL) out_cap = &lcap;

    switch (value.type) {
    case JSON_VALUE_NULL: {
    out_buf_realloc_maybe(out, out_pos, out_cap, 4);
    memcpy(*out + out_pos, "null", 4);
    return 4;
    }
    case JSON_VALUE_STRING: {
    return json_dump_string(value.string, out, out_pos, out_cap);
    }
    case JSON_VALUE_NUMBER: {
    return json_dump_number(value.number, out, out_pos, out_cap);
    }
    case JSON_VALUE_BOOL: {
    return json_dump_bool(value.boolean, out, out_pos, out_cap);
    }
    case JSON_VALUE_OBJECT: {
    return json_dump_object(value.object, out, out_pos, out_cap);
    }
    case JSON_VALUE_ARRAY: {
    return json_dump_array(value.array, out, out_pos, out_cap);
    }
    default: {
    fprintf(stderr, "json_dump_whatever() warning: got invalid value.\n");
    return 0;
    }
    }

    return 0;
    }

    size_t json_dump_string(const JsonString string, char** out, size_t out_pos, size_t* out_cap) {
    size_t lcap = 0;
    if(out_cap == NULL) out_cap = &lcap;

    const size_t start_pos = out_pos;
    out_buf_realloc_maybe(out, out_pos, out_cap, string.size + 2);
    (*out)[out_pos++] = '"';
    memcpy(*out + out_pos, string.value, string.size);
    out_pos += string.size;
    (*out)[out_pos++] = '"';

    return out_pos - start_pos;
    }

    size_t json_dump_number(const JsonNumber number, char** out, size_t out_pos, size_t* out_cap) {
    size_t lcap = 0;
    if(out_cap == NULL) out_cap = &lcap;

    char buffer[sizeof(int64_t) * 8 + 1] = {0};

    if(number.type == JSON_NUMBER_INT) {
    snprintf(buffer, sizeof(int64_t) * 8, "%lld", number.int_.value);
    } else {
    snprintf(buffer, sizeof(int64_t) * 8, "%f", number.double_.value);
    }

    const size_t str_len = strlen(buffer);
    out_buf_realloc_maybe(out, out_pos, out_cap, str_len);
    memcpy(*out + out_pos, buffer, str_len);

    return str_len;
    }

    size_t json_dump_bool(const JsonBool boolean, char** out, size_t out_pos, size_t* out_cap) {
    size_t lcap = 0;
    if(out_cap == NULL) out_cap = &lcap;

    if(boolean.value) {
    out_buf_realloc_maybe(out, out_pos, out_cap, 4);
    memcpy(*out + out_pos, "true", 4);
    return 4;
    }

    out_buf_realloc_maybe(out, out_pos, out_cap, 5);
    memcpy(*out + out_pos, "false", 5);
    return 5;
    }

    size_t json_dump_object(const JsonObject object, char** out, size_t out_pos, size_t* out_cap) {
    size_t lcap = 0;
    if(out_cap == NULL) out_cap = &lcap;

    if(object.size == 0) {
    out_buf_realloc_maybe(out, out_pos, out_cap, 2);
    memcpy(*out + out_pos, "{}", 2);
    return 2;
    }

    const size_t start_pos = out_pos;

    out_buf_realloc_maybe(out, out_pos, out_cap, 1);
    (*out)[out_pos++] = '{';

    for(size_t i = 0; i < object.size; ++i) {
    if(i > 0) {
    out_buf_realloc_maybe(out, out_pos, out_cap, 1);
    (*out)[out_pos++] = ',';
    }

    out_pos += json_dump_string(object.items[i].key, out, out_pos, out_cap);

    out_buf_realloc_maybe(out, out_pos, out_cap, 1);
    (*out)[out_pos++] = ':';

    out_pos += json_dump_whatever(object.items[i].value, out, out_pos, out_cap);
    }

    out_buf_realloc_maybe(out, out_pos, out_cap, 1);
    (*out)[out_pos++] = '}';

    return out_pos - start_pos;
    }

    size_t json_dump_array(const JsonArray array, char** out, size_t out_pos, size_t* out_cap) {
    size_t lcap = 0;
    if(out_cap == NULL) out_cap = &lcap;

    if(array.size == 0) {
    out_buf_realloc_maybe(out, out_pos, out_cap, 2);
    memcpy(*out + out_pos, "[]", 2);
    return 2;
    }

    const size_t start_pos = out_pos;

    out_buf_realloc_maybe(out, out_pos, out_cap, 1);
    (*out)[out_pos++] = '[';

    for(size_t i = 0; i < array.size; ++i) {
    if(i > 0) {
    out_buf_realloc_maybe(out, out_pos, out_cap, 1);
    (*out)[out_pos++] = ',';
    }

    out_pos += json_dump_whatever(array.items[i], out, out_pos, out_cap);
    }

    out_buf_realloc_maybe(out, out_pos, out_cap, 1);
    (*out)[out_pos++] = ']';

    return out_pos - start_pos;
    }


    #ifdef JSON_TEST_FUNC
    #include <assert.h>

    6 changes: 6 additions & 0 deletions json.h
    Original file line number Diff line number Diff line change
    @@ -80,6 +80,12 @@ JsonBool json_parse_bool(const char* data, const size_t size, size_t* pos);
    JsonObject json_parse_object(const char* data, const size_t size, size_t* pos);
    JsonArray json_parse_array(const char* data, const size_t size, size_t* pos);

    size_t json_dump_string(const JsonString string, char** out, size_t out_pos, size_t* out_cap);
    size_t json_dump_number(const JsonNumber number, char** out, size_t out_pos, size_t* out_cap);
    size_t json_dump_bool(const JsonBool boolean, char** out, size_t out_pos, size_t* out_cap);
    size_t json_dump_object(const JsonObject object, char** out, size_t out_pos, size_t* out_cap);
    size_t json_dump_array(const JsonArray array, char** out, size_t out_pos, size_t* out_cap);

    void json_free_string(const JsonString string);
    void json_free_object(const JsonObject object);
    void json_free_array(const JsonArray array);
  3. RuslanUC revised this gist Mar 17, 2025. No changes.
  4. RuslanUC created this gist Mar 17, 2025.
    382 changes: 382 additions & 0 deletions json.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,382 @@
    #include "json.h"

    #include <ctype.h>
    #include <stdlib.h>
    #include <string.h>

    char next_subtoken(const char* data, size_t* pos, const size_t size, const bool in_string) {
    while(*pos < size) {
    if(in_string)
    return data[*pos] == '\n' ? 0 : data[(*pos)++];

    if (isalnum(data[*pos]) || data[*pos] == ':'
    || data[*pos] == '.' || data[*pos] == ','
    || data[*pos] == '\'' || data[*pos] == '"'
    || data[*pos] == '{' || data[*pos] == '}'
    || data[*pos] == '[' || data[*pos] == ']') {
    return data[(*pos)++];
    }

    (*pos)++;
    }

    return 0;
    }

    JsonValue parse_whatever(const char* data, size_t* pos, const size_t size) {
    char subtoken;

    JsonValue ret = { .type = JSON_VALUE_INVALID };

    while(*pos < size && (subtoken = next_subtoken(data, pos, size, false)) > 0) {
    --(*pos);
    switch(subtoken) {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9': {
    ret.type = JSON_VALUE_NUMBER;
    ret.number = json_parse_number(data, size, pos);
    return ret;
    }
    case '{': {
    ret.type = JSON_VALUE_OBJECT;
    ret.object = json_parse_object(data, size, pos);
    return ret;
    }
    case '[': {
    ret.type = JSON_VALUE_ARRAY;
    ret.array = json_parse_array(data, size, pos);
    return ret;
    }
    case '\'':
    case '"': {
    ret.type = JSON_VALUE_STRING;
    ret.string = json_parse_string(data, size, pos);
    return ret;
    }
    case 'n': {
    if(size - *pos >= 4 && memcmp(data + *pos, "null", 4) == 0) {
    ret.type = JSON_VALUE_NULL;
    ret.null = NULL;
    *pos += 4;
    return ret;
    }
    }
    case 't': {
    if(size - *pos >= 4 && memcmp(data + *pos, "true", 4) == 0) {
    ret.type = JSON_VALUE_BOOL;
    ret.boolean.value = true;
    *pos += 4;
    return ret;
    }
    }
    case 'f': {
    if(size - *pos >= 5 && memcmp(data + *pos, "false", 5) == 0) {
    ret.type = JSON_VALUE_BOOL;
    ret.boolean.value = false;
    *pos += 5;
    return ret;
    }
    }
    default: return ret;
    }
    }

    return ret;
    }

    JsonString json_parse_string(const char* data, const size_t size, size_t* pos) {
    char subtoken;
    size_t lpos = 0;
    if(pos == NULL) pos = &lpos;
    const char quote = next_subtoken(data, pos, size, false);
    const size_t str_start = *pos;

    while(*pos < size && (subtoken = next_subtoken(data, pos, size, true)) > 0) {
    if(subtoken == quote) {
    const size_t str_size = *pos - str_start - 1;
    char* string = malloc(str_size + 1);
    memcpy(string, data + str_start, str_size);
    string[str_size] = '\0';

    return (JsonString){
    .size = str_size,
    .value = string,
    };
    }
    }

    // TODO: unexpected eof
    return (JsonString){
    .size = 0,
    .value = NULL,
    };
    }

    JsonNumber json_parse_number(const char* data, const size_t size, size_t* pos) {
    char subtoken;
    size_t lpos = 0;
    if(pos == NULL) pos = &lpos;
    bool is_fractional = false;
    const size_t start_pos = *pos;

    JsonNumber ret = (JsonNumber){
    .type = JSON_NUMBER_INT,
    .int_ = 0,
    };

    while(*pos < size && (subtoken = next_subtoken(data, pos, size, false)) > 0) {
    if(subtoken == '.') {
    if(is_fractional)
    return ret; // TODO: invalid number

    is_fractional = true;
    continue;
    }

    if(!isdigit(subtoken)) {
    --(*pos);
    if(is_fractional) {
    ret.type = JSON_NUMBER_DOUBLE;
    ret.double_.value = strtod(data + start_pos, NULL);
    } else {
    ret.type = JSON_NUMBER_INT;
    ret.int_.value = strtoll(data + start_pos, NULL, 10);
    }
    return ret;
    }
    }

    // TODO: unexpected eof
    return ret;
    }

    JsonBool json_parse_bool(const char* data, const size_t size, size_t* pos) {
    size_t lpos = 0;
    if(pos == NULL) pos = &lpos;
    const size_t bytes_left = size - *pos;

    if(bytes_left < 1 || ((data[*pos] != 't' || bytes_left < 4) && (data[*pos] != 'f' || bytes_left < 5)))
    return (JsonBool){ .value = false }; // TODO: invalid bool

    if(data[*pos] == 't') {
    if(memcmp(data + *pos, "true", 4) == 0) {
    *pos += 4;
    return (JsonBool){ .value = true };
    }
    return (JsonBool){ .value = false }; // TODO: invalid bool
    }

    if(data[*pos] == 'f') {
    if(memcmp(data + *pos, "false", 5) == 0) {
    *pos += 5;
    return (JsonBool){ .value = false };
    }
    return (JsonBool){ .value = false }; // TODO: invalid bool
    }

    return (JsonBool){ .value = false }; // TODO: invalid bool
    }

    JsonObject json_parse_object(const char* data, const size_t size, size_t* pos) {
    char subtoken;
    size_t lpos = 0;
    if(pos == NULL) pos = &lpos;
    (*pos)++;

    JsonObject ret = {0};
    size_t capacity = 0;

    while(*pos < size && (subtoken = next_subtoken(data, pos, size, false)) > 0) {
    if(subtoken == '}')
    return ret;
    if(ret.size > 0) {
    if(subtoken != ',')
    goto error;
    subtoken = next_subtoken(data, pos, size, false);
    if(subtoken == '\0')
    goto error;
    }

    --(*pos);
    const JsonValue key = parse_whatever(data, pos, size);
    if(key.type != JSON_VALUE_STRING)
    goto error;

    subtoken = next_subtoken(data, pos, size, false);
    if(subtoken != ':')
    goto error;

    const JsonValue value = parse_whatever(data, pos, size);
    if(value.type == JSON_VALUE_INVALID) {
    goto error;
    }

    if(ret.size >= capacity) {
    if(capacity == 0)
    capacity = 64;
    else
    capacity *= 2;

    ret.items = realloc(ret.items, capacity * sizeof(JsonObjectItem));
    }

    ret.items[ret.size++] = (JsonObjectItem){
    .key = key.string,
    .value = value,
    };
    }

    error:
    // TODO: unexpected eof
    json_free_object(ret);
    ret.items = NULL;
    ret.size = 0;
    return ret;
    }

    JsonArray json_parse_array(const char* data, const size_t size, size_t* pos) {
    char subtoken;
    size_t lpos = 0;
    if(pos == NULL) pos = &lpos;
    (*pos)++;

    JsonArray ret = {0};
    size_t capacity = 0;

    while(*pos < size && (subtoken = next_subtoken(data, pos, size, false)) > 0) {
    if(subtoken == ']')
    return ret;
    if(ret.size > 0) {
    if(subtoken != ',')
    goto error;
    subtoken = next_subtoken(data, pos, size, false);
    if(subtoken == '\0')
    goto error;
    }

    --(*pos);

    const JsonValue value = parse_whatever(data, pos, size);
    if(value.type == JSON_VALUE_INVALID)
    goto error;

    if(ret.size >= capacity) {
    if(capacity == 0)
    capacity = 64;
    else
    capacity *= 2;

    ret.items = realloc(ret.items, capacity * sizeof(JsonObjectItem));
    }

    ret.items[ret.size++] = value;
    }

    error:
    // TODO: unexpected eof
    json_free_array(ret);
    ret.items = NULL;
    ret.size = 0;
    return ret;
    }

    void free_whatever(const JsonValue value) {
    switch(value.type) {
    case JSON_VALUE_STRING: {
    json_free_string(value.string);
    return;
    }
    case JSON_VALUE_OBJECT: {
    json_free_object(value.object);
    return;
    }
    case JSON_VALUE_ARRAY: {
    json_free_array(value.array);
    return;
    }
    default:
    }
    }

    void json_free_string(const JsonString string) {
    if(string.value != NULL)
    free(string.value);
    }

    void json_free_object(const JsonObject object) {
    for(size_t i = 0; i < object.size; ++i) {
    json_free_string(object.items[i].key);
    free_whatever(object.items[i].value);
    }

    if(object.items != NULL)
    free(object.items);
    }

    void json_free_array(const JsonArray array) {
    for(size_t i = 0; i < array.size; ++i) {
    free_whatever(array.items[i]);
    }

    if(array.items != NULL)
    free(array.items);
    }

    #ifdef JSON_TEST_FUNC
    #include <assert.h>

    void json_simple_test() {
    const char* json_string = "{\"a\": [123, 123.456, \"some string\", null, true, false, [1, 2], {\"test\": \"asd\"}]}";
    const size_t json_len = strlen(json_string);

    JsonObject obj = json_parse_object(json_string, json_len, NULL);

    assert(obj.size == 1);
    assert(obj.items != NULL);
    assert(obj.items[0].key.size == 1);
    assert(strcmp(obj.items[0].key.value, "a") == 0);
    assert(obj.items[0].value.type == JSON_VALUE_ARRAY);
    assert(obj.items[0].value.array.size == 8);
    assert(obj.items[0].value.array.items != NULL);
    assert(obj.items[0].value.array.items[0].type == JSON_VALUE_NUMBER);
    assert(obj.items[0].value.array.items[0].number.type == JSON_NUMBER_INT);
    assert(obj.items[0].value.array.items[0].number.int_.value == 123);
    assert(obj.items[0].value.array.items[1].type == JSON_VALUE_NUMBER);
    assert(obj.items[0].value.array.items[1].number.type == JSON_NUMBER_DOUBLE);
    assert(obj.items[0].value.array.items[1].number.double_.value == 123.456);
    assert(obj.items[0].value.array.items[2].type == JSON_VALUE_STRING);
    assert(obj.items[0].value.array.items[2].string.size == 11);
    assert(strcmp(obj.items[0].value.array.items[2].string.value, "some string") == 0);
    assert(obj.items[0].value.array.items[3].type == JSON_VALUE_NULL);
    assert(obj.items[0].value.array.items[3].null == NULL);
    assert(obj.items[0].value.array.items[4].type == JSON_VALUE_BOOL);
    assert(obj.items[0].value.array.items[4].boolean.value == true);
    assert(obj.items[0].value.array.items[5].type == JSON_VALUE_BOOL);
    assert(obj.items[0].value.array.items[5].boolean.value == false);
    assert(obj.items[0].value.array.items[6].type == JSON_VALUE_ARRAY);
    assert(obj.items[0].value.array.items[6].array.size == 2);
    assert(obj.items[0].value.array.items[6].array.items[0].type == JSON_VALUE_NUMBER);
    assert(obj.items[0].value.array.items[6].array.items[0].number.type == JSON_NUMBER_INT);
    assert(obj.items[0].value.array.items[6].array.items[0].number.int_.value == 1);
    assert(obj.items[0].value.array.items[6].array.items[1].type == JSON_VALUE_NUMBER);
    assert(obj.items[0].value.array.items[6].array.items[1].number.type == JSON_NUMBER_INT);
    assert(obj.items[0].value.array.items[6].array.items[1].number.int_.value == 2);
    assert(obj.items[0].value.array.items[7].type == JSON_VALUE_OBJECT);
    assert(obj.items[0].value.array.items[7].object.size == 1);
    assert(obj.items[0].value.array.items[7].object.items[0].key.size == 4);
    assert(strcmp(obj.items[0].value.array.items[7].object.items[0].key.value, "test") == 0);
    assert(obj.items[0].value.array.items[7].object.items[0].value.type == JSON_VALUE_STRING);
    assert(obj.items[0].value.array.items[7].object.items[0].value.string.size == 3);
    assert(strcmp(obj.items[0].value.array.items[7].object.items[0].value.string.value, "asd") == 0);

    json_free_object(obj);
    }
    #endif
    89 changes: 89 additions & 0 deletions json.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,89 @@
    #pragma once

    #include <stdint.h>
    #include <stdbool.h>
    #include <stdio.h>

    typedef struct JsonString {
    size_t size;
    char* value;
    } JsonString;

    typedef struct JsonBool {
    bool value;
    } JsonBool;

    typedef struct JsonInt {
    int64_t value;
    } JsonInt;

    typedef struct JsonDouble {
    double value;
    } JsonDouble;

    typedef enum JsonNumberType {
    JSON_NUMBER_INT = 0,
    JSON_NUMBER_DOUBLE,
    } JsonNumberType;

    typedef struct JsonNumber {
    JsonNumberType type;
    union {
    JsonInt int_;
    JsonDouble double_;
    };
    } JsonNumber;

    typedef enum JsonValueType {
    JSON_VALUE_INVALID = -1,
    JSON_VALUE_NULL,
    JSON_VALUE_STRING,
    JSON_VALUE_NUMBER,
    JSON_VALUE_BOOL,
    JSON_VALUE_OBJECT,
    JSON_VALUE_ARRAY,
    } JsonValueType;

    struct JsonValue;
    struct JsonObjectItem;

    typedef struct JsonObject {
    size_t size;
    struct JsonObjectItem* items;
    } JsonObject;

    typedef struct JsonArray {
    size_t size;
    struct JsonValue* items;
    } JsonArray;

    typedef struct JsonValue {
    JsonValueType type;
    union {
    JsonString string;
    JsonNumber number;
    JsonBool boolean;
    JsonObject object;
    JsonArray array;
    void* null;
    };
    } JsonValue;

    typedef struct JsonObjectItem {
    JsonString key;
    JsonValue value;
    } JsonObjectItem;

    JsonString json_parse_string(const char* data, const size_t size, size_t* pos);
    JsonNumber json_parse_number(const char* data, const size_t size, size_t* pos);
    JsonBool json_parse_bool(const char* data, const size_t size, size_t* pos);
    JsonObject json_parse_object(const char* data, const size_t size, size_t* pos);
    JsonArray json_parse_array(const char* data, const size_t size, size_t* pos);

    void json_free_string(const JsonString string);
    void json_free_object(const JsonObject object);
    void json_free_array(const JsonArray array);

    #ifdef JSON_TEST_FUNC
    void json_simple_test();
    #endif