From 9eaa734fbb0a332152252db4b20a57ddceae5290 Mon Sep 17 00:00:00 2001 From: rexy712 Date: Sun, 25 Nov 2018 16:19:14 -0800 Subject: [PATCH] Rudimentary output to json --- include/rjp.h | 16 +-- src/rjp.c | 381 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 339 insertions(+), 58 deletions(-) diff --git a/include/rjp.h b/include/rjp.h index 07563bc..e7a9c09 100644 --- a/include/rjp.h +++ b/include/rjp.h @@ -64,7 +64,6 @@ struct JSON_array{ //hold any json data type struct JSON_value{ - enum JSON_type type; union{ long integer; double dfloat; @@ -74,6 +73,7 @@ struct JSON_value{ struct JSON_array array; }; struct JSON_value* parent; + enum JSON_type type; }; struct JSON_array_element{ @@ -105,20 +105,10 @@ enum JSON_type rjp_get_value_type(struct JSON_value* j); //initialize an empty json handle (outputting to string would result in '{}') struct JSON_value* rjp_init_json(void); -//initialize an empty json object -struct JSON_object* rjp_init_object(void); -//initialize an empty json array -struct JSON_array* rjp_init_array(void); - //add a member to a json object -struct JSON_value* rjp_add_member(struct JSON_object* dest, const char* key, size_t keylen, struct JSON_value* value); +struct JSON_value* rjp_add_member(struct JSON_object* dest, int alloc_key, char* key, size_t keylen, struct JSON_value value); //add an element to a json array -struct JSON_value* rjp_add_element(struct JSON_array* dest, struct JSON_value* value); - -struct JSON_value* rjp_init_integer(long i); -struct JSON_value* rjp_init_dfloat(double d); -struct JSON_value* rjp_init_boolean(char b); -struct JSON_value* rjp_init_string(const char* str, size_t len); +struct JSON_value* rjp_add_element(struct JSON_array* dest, struct JSON_value value); /*struct JSON_value* root = rjp_init_json(); rjp_add_member(root, "intel_backlight", 15, rjp_init_integer(100)); diff --git a/src/rjp.c b/src/rjp.c index 6fc5647..8daa146 100644 --- a/src/rjp.c +++ b/src/rjp.c @@ -47,48 +47,51 @@ enum json_search_target{ static int is_whitespace(char c){ return c == ' ' || c == '\n' || c == '\r' || c == '\t'; } -//allocate a member of the object as a linked list member and assign a name -MAYBE_UNUSED static void add_member(struct JSON_value* j, const char* str, size_t len){ - ++j->object.num_members; - struct JSON_object_member* prev = j->object.members; - j->object.members = calloc(1, sizeof(struct JSON_object_member)); - j->object.members->name.value = malloc(len + 1); - j->object.members->name.length = len; - strncpy(j->object.members->name.value, str, len); - j->object.members->name.value[len] = 0; - j->object.members->next = prev; -} //add an element to an array -static void add_element(struct JSON_value* j){ - ++j->array.num_elements; - if(!j->array.elements){ - j->array.elements = calloc(1, sizeof(struct JSON_array_element)); - j->array.last = j->array.elements; +static void add_element(struct JSON_array* j){ + ++j->num_elements; + if(!j->elements){ + j->elements = calloc(1, sizeof(struct JSON_array_element)); + j->last = j->elements; }else{ - j->array.last->next = calloc(1, sizeof(struct JSON_array_element)); - j->array.last = j->array.last->next; + j->last->next = calloc(1, sizeof(struct JSON_array_element)); + j->last = j->last->next; } } //create member of the object as a linked list member and assign a name with no name allocation -static void add_member_no_alloc(struct JSON_value* j, char* str, size_t len){ - ++j->object.num_members; - if(!j->object.members){ - j->object.members = calloc(1, sizeof(struct JSON_object_member)); - j->object.last = j->object.members; +static void add_member(struct JSON_object* j, char* str, size_t len){ + ++j->num_members; + if(!j->members){ + j->members = calloc(1, sizeof(struct JSON_object_member)); + j->last = j->members; }else{ - j->object.last->next = calloc(1, sizeof(struct JSON_object_member)); - j->object.last = j->object.last->next; + j->last->next = calloc(1, sizeof(struct JSON_object_member)); + j->last = j->last->next; } - j->object.last->name.value = str; - j->object.last->name.length = len; + j->last->name.value = malloc(len + 1); + strncpy(j->last->name.value, str, len); + j->last->name.value[len] = 0; + j->last->name.length = len; +} +static void add_member_no_alloc(struct JSON_object* j, char* str, size_t len){ + ++j->num_members; + if(!j->members){ + j->members = calloc(1, sizeof(struct JSON_object_member)); + j->last = j->members; + }else{ + j->last->next = calloc(1, sizeof(struct JSON_object_member)); + j->last = j->last->next; + } + j->last->name.value = str; + j->last->name.length = len; } static struct JSON_value* add_element_to_array(struct JSON_value* curr, struct JSON_state* state, struct JSON_value* element){ struct JSON_value* tmp = &curr->object.members->value; for(int i = state->in_array-1;i > 0;--i, tmp = &tmp->array.last->value); - add_element(tmp); - tmp->array.elements->value = *element; - return &tmp->array.elements->value; + add_element(&tmp->array); + tmp->array.last->value = *element; + return &tmp->array.last->value; } //Assign object characteristics to previously allocated JSON_value @@ -101,54 +104,54 @@ static struct JSON_value* add_object(struct JSON_value* curr, struct JSON_state* struct JSON_value new_object = {.type = json_object, .integer = 0, .parent = curr}; if(state->in_array) return add_element_to_array(curr, state, &new_object); - curr->object.members->value = new_object; - return &curr->object.members->value; + curr->object.last->value = new_object; + return &curr->object.last->value; } //Assign array characteristics to previously allocated JSON_value static struct JSON_value* add_array(struct JSON_value* curr, struct JSON_state* state){ struct JSON_value new_array = {.type = json_array, .array = {.num_elements = 0, .elements = NULL}, .parent = curr}; if(state->in_array) return add_element_to_array(curr, state, &new_array); - curr->object.members->value = new_array; - return &curr->object.members->value; + curr->object.last->value = new_array; + return &curr->object.last->value; } //Assign string characteristics to previously allocated JSON_value with no string allocation static struct JSON_value* add_string_no_alloc(struct JSON_value* curr, char* str, int len, struct JSON_state* state){ struct JSON_value new_string = {.type = json_string, .string = {.value = str, .length = len}, .parent = curr}; if(state->in_array) return add_element_to_array(curr, state, &new_string); - curr->object.members->value = new_string; - return &curr->object.members->value; + curr->object.last->value = new_string; + return &curr->object.last->value; } //Assign double characteristics to previously allocated JSON_value static struct JSON_value* add_double(struct JSON_value* curr, double value, struct JSON_state* state){ struct JSON_value new_double = {.type = json_dfloat, .dfloat = value, .parent = curr}; if(state->in_array) return add_element_to_array(curr, state, &new_double); - curr->object.members->value = new_double; - return &curr->object.members->value; + curr->object.last->value = new_double; + return &curr->object.last->value; } //Assign integer characteristics to previously allocated JSON_value static struct JSON_value* add_integer(struct JSON_value* curr, long value, struct JSON_state* state){ struct JSON_value new_integer = {.type = json_integer, .integer = value, .parent = curr}; if(state->in_array) return add_element_to_array(curr, state, &new_integer); - curr->object.members->value = new_integer; - return &curr->object.members->value; + curr->object.last->value = new_integer; + return &curr->object.last->value; } static struct JSON_value* add_boolean(struct JSON_value* curr, int value, struct JSON_state* state){ struct JSON_value new_boolean = {.type = json_boolean, .boolean = value, .parent = curr}; if(state->in_array) return add_element_to_array(curr, state, &new_boolean); - curr->object.members->value = new_boolean; - return &curr->object.members->value; + curr->object.last->value = new_boolean; + return &curr->object.last->value; } static struct JSON_value* add_null(struct JSON_value* curr, struct JSON_state* state){ struct JSON_value new_null = {.type = json_null, .integer = 0, .parent = curr}; if(state->in_array) return add_element_to_array(curr, state, &new_null); - curr->object.members->value = new_null; - return &curr->object.members->value; + curr->object.last->value = new_null; + return &curr->object.last->value; } static void free_json_recurse(struct JSON_value* root); @@ -316,7 +319,7 @@ struct JSON_value* rjp_parse(const char* str){ syntax_error("Cannot have empty key name!", row, column); return NULL; } - add_member_no_alloc(curr, new_string, keylen); + add_member(&curr->object, new_string, keylen); str += keylen; top->search_target = json_colon; //end of this object (object is empty) @@ -475,3 +478,291 @@ enum JSON_type rjp_get_member_type(struct JSON_object_member* j){ enum JSON_type rjp_get_value_type(struct JSON_value* j){ return j->type; } + +struct JSON_value* rjp_init_json(void){ + return add_object(NULL, NULL); +} +struct JSON_value* rjp_add_member(struct JSON_object* dest, int alloc_key, char* key, size_t keylen, struct JSON_value value){ + if(!keylen && alloc_key) + keylen = strlen(key); + if(alloc_key) + add_member(dest, key, keylen); + else + add_member_no_alloc(dest, key, keylen); + dest->last->value = value; + dest->last->value.parent = (struct JSON_value*)dest; + return &dest->last->value; +} +struct JSON_value* rjp_add_element(struct JSON_array* dest, struct JSON_value value){ + add_element(dest); + dest->last->value = value; + dest->last->value.parent = (struct JSON_value*)dest; + return &dest->last->value; +} +struct JSON_value rjp_integer(long i){ + return (struct JSON_value){.integer = i, .type = json_integer}; +} +struct JSON_value rjp_boolean(char b){ + return (struct JSON_value){.boolean = b, .type = json_boolean}; +} +struct JSON_value rjp_double(double d){ + return (struct JSON_value){.dfloat = d, .type = json_dfloat}; +} +struct JSON_value rjp_string(char* c, size_t len){ + return (struct JSON_value){.string = {.value = c, .length = len}, .type = json_string}; +} +struct JSON_value rjp_null(void){ + return (struct JSON_value){.integer = 0, .type = json_null}; +} +struct JSON_value rjp_object(void){ + return (struct JSON_value){.object = {0}, .type = json_object}; +} +struct JSON_value rjp_array(void){ + return (struct JSON_value){.array = {0}, .type = json_array}; +} + +size_t escape_strcpy(char* dest, const char* src){ + size_t j = 0; + for(size_t i = 0;src[i];++i){ + switch(src[i]){ + case '"': + dest[j++] = '\\'; + dest[j++] = '"'; + break; + case '\n': + dest[j++] = '\\'; + dest[j++] = 'n'; + break; + case '\r': + dest[j++] = '\\'; + dest[j++] = 'r'; + break; + case '\b': + dest[j++] = '\\'; + dest[j++] = 'b'; + break; + case '\\': + dest[j++] = '\\'; + dest[j++] = '\\'; + break; + case '\t': + dest[j++] = '\\'; + dest[j++] = 't'; + break; + case '\f': + dest[j++] = '\\'; + dest[j++] = 'f'; + break; + default: + dest[j++] = src[i]; + break; + }; + } + return j; +} +size_t escape_strlen(const char* str){ + size_t count = 0; + for(size_t i = 0;str[i];++i){ + switch(str[i]){ + case '"': + case '\n': + case '\r': + case '\b': + case '\\': + case '\t': + case '\f': + ++count; + //fallthrough + default: + ++count; + }; + } + return count; +} +size_t dump_json(struct JSON_value* root, char* dest); +size_t dump_array(struct JSON_value* arr, char* dest){ + struct JSON_array* array = &arr->array; + struct JSON_array_element* element_list = array->elements; + size_t pos = 0; + for(;element_list;element_list = element_list->next){ + switch(element_list->value.type){ + case json_integer: + pos += sprintf(dest+pos, "%li", element_list->value.integer); + break; + case json_dfloat: + pos += sprintf(dest+pos, "%lf", element_list->value.dfloat); + break; + case json_boolean: + pos += sprintf(dest+pos, element_list->value.boolean ? "true" : "false"); + break; + case json_null: + pos +=sprintf(dest+pos, "null"); + break; + case json_string: + //TODO escape characters + pos += sprintf(dest+pos, "\""); + pos += escape_strcpy(dest+pos, element_list->value.string.value); + pos += sprintf(dest+pos, "\""); + break; + case json_array: + pos += sprintf(dest+pos, "["); + pos += dump_array(&element_list->value, dest+pos); + pos += sprintf(dest+pos, "]"); + break; + case json_object: + pos += sprintf(dest+pos, "{"); + pos += dump_json(&element_list->value, dest+pos); + pos += sprintf(dest+pos, "}"); + break; + }; + if(element_list->next) + pos += sprintf(dest+pos, ","); + else + break; + } + return pos; +} + +size_t dump_json(struct JSON_value* root, char* dest){ + struct JSON_object* root_obj = &root->object; + struct JSON_object_member* member_list = root_obj->members; + size_t pos = 0; + for(;member_list;member_list = member_list->next){ + pos += sprintf(dest+pos, "\""); + pos += escape_strcpy(dest+pos, member_list->name.value); + pos += sprintf(dest+pos, "\":"); + switch(member_list->value.type){ + case json_integer: + pos += sprintf(dest+pos, "%li", member_list->value.integer); + break; + case json_dfloat: + pos += sprintf(dest+pos, "%lf", member_list->value.dfloat); + break; + case json_boolean: + pos += sprintf(dest+pos, member_list->value.boolean ? "true" : "false"); + break; + case json_null: + pos += sprintf(dest+pos, "null"); + break; + case json_string:; + pos += sprintf(dest+pos, "\""); + pos += escape_strcpy(dest+pos, member_list->value.string.value); + pos += sprintf(dest+pos, "\""); + break; + case json_array: + pos += sprintf(dest+pos, "["); + pos += dump_array(&member_list->value, dest+pos); + pos += sprintf(dest+pos, "]"); + break; + case json_object: + pos += sprintf(dest+pos, "{"); + pos += dump_json(&member_list->value, dest+pos); + pos += sprintf(dest+pos, "}"); + break; + }; + if(member_list->next) + pos += sprintf(dest+pos, ","); + else + break; + } + return pos; +} +size_t count_json(struct JSON_value* root); +size_t count_array(struct JSON_value* arr){ + size_t count = 2; //[] + struct JSON_array* array = &arr->array; + struct JSON_array_element* element_list = array->elements; + for(;element_list;element_list = element_list->next){ + switch(element_list->value.type){ + case json_integer: + count += snprintf(NULL, 0, "%li", element_list->value.integer); + break; + case json_dfloat: + count += snprintf(NULL, 0, "%lf", element_list->value.dfloat); + break; + case json_boolean: + count += element_list->value.boolean ? 4 : 5; + break; + case json_null: + count += 4; + break; + case json_string: + count += escape_strlen(element_list->value.string.value) + 2; + break; + case json_array: + count += count_array(&element_list->value); + break; + case json_object: + count += count_json(&element_list->value); + break; + }; + if(element_list->next) + ++count; + else + break; + } + return count; +} + +size_t count_json(struct JSON_value* root){ + size_t count = 2; //{} + struct JSON_object* root_obj = &root->object; + struct JSON_object_member* member_list = root_obj->members; + for(;member_list;member_list = member_list->next){ + count += escape_strlen(member_list->name.value) + 3; //"": + switch(member_list->value.type){ + case json_integer: + count += snprintf(NULL, 0, "%li", member_list->value.integer); + break; + case json_dfloat: + count += snprintf(NULL, 0, "%lf", member_list->value.dfloat); + break; + case json_boolean: + count += member_list->value.boolean ? 4 : 5; //true, false + break; + case json_null: + count += 4; //null + break; + case json_string: + count += escape_strlen(member_list->value.string.value) + 2; + break; + case json_array: + count += count_array(&member_list->value); + break; + case json_object: + count += count_json(&member_list->value); + break; + }; + if(member_list->next) + ++count; + else + break; + } + return count; +} +void rjp_dump(struct JSON_value* root){ + size_t len = count_json(root); + char* tmp = malloc(len + 1); + tmp[len] = 0; + size_t pos = 1; + + sprintf(tmp, "{"); + pos += dump_json(root, tmp+pos); + sprintf(tmp+pos, "}"); + printf("%s\n", tmp); + free(tmp); +} +int main(){ + struct JSON_value* root = rjp_init_json(); + rjp_add_member(&root->object, 1, "intel_backlight", 0, (struct JSON_value){.integer = 4, .type = json_integer}); + rjp_add_member(&root->object, 1, "other_backlight", 0, (struct JSON_value){.integer = 14, .type = json_integer}); + struct JSON_value sub_object = rjp_object(); + rjp_add_member(&sub_object.object, 1, "sub_item", 0, rjp_array()); + rjp_add_member(&root->object, 1, "object", 0, sub_object); + printf("%d\n", snprintf(NULL, 0, "%lf", 100.0)); + printf("%lf\n", 100.0); + rjp_dump(root); + printf("\n"); + printf("%lu\n", count_json(root)); + rjp_free(root); +}