mirror of
https://github.com/kgabis/parson.git
synced 2025-02-05 17:05:29 +00:00
Fixed bug in nget function, added 2 functions to API, improved memory allocs.
Details: - Fixed bug, where json_object_nget_value returned wrong values. - json_object_get_count returns a number of object's name-value pairs. - json_object_get_name returns a name at a specific index. - Both functions allow iterating over every value in a object. - Changed max capacity for JSON_Array and JSON_Object (they're not equal anymore). - Added functions to resize object and array, which are also used after parsing to "trim" them to their real lengths. - Added try_realloc function. - Added SUCCESS and ERROR macros to make code more readable. - Code cleanup.
This commit is contained in:
parent
aaf1d45f16
commit
f7f11572c9
212
parson.c
212
parson.c
@ -28,11 +28,14 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define STARTING_CAPACITY 15
|
||||
#define MAX_CAPACITY 10000
|
||||
#define MAX_NESTING 19
|
||||
#define sizeof_token(a) (sizeof(a) - 1)
|
||||
#define skip_char(str) ((*str)++)
|
||||
#define ERROR 0
|
||||
#define SUCCESS 1
|
||||
#define STARTING_CAPACITY 15
|
||||
#define ARRAY_MAX_CAPACITY 122880 /* 15*(2^13) */
|
||||
#define OBJECT_MAX_CAPACITY 960 /* 15*(2^6) */
|
||||
#define MAX_NESTING 19
|
||||
#define sizeof_token(a) (sizeof(a) - 1)
|
||||
#define skip_char(str) ((*str)++)
|
||||
#define skip_whitespaces(str) while (isspace(**string)) { skip_char(string); }
|
||||
|
||||
#define parson_malloc(a) malloc(a)
|
||||
@ -67,15 +70,23 @@ struct json_array_t {
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
/* Various */
|
||||
static int try_realloc(void **ptr, size_t new_size);
|
||||
static char * parson_strndup(const char *string, size_t n);
|
||||
static int is_utf(const char *string);
|
||||
static int is_decimal(const char *string, size_t length);
|
||||
|
||||
/* JSON Object */
|
||||
static JSON_Object * json_object_init(void);
|
||||
static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value);
|
||||
static int json_object_resize(JSON_Object *object, size_t capacity);
|
||||
static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n);
|
||||
static void json_object_free(JSON_Object *object);
|
||||
|
||||
/* JSON Array */
|
||||
static JSON_Array * json_array_init(void);
|
||||
static int json_array_add(JSON_Array *array, JSON_Value *value);
|
||||
static int json_array_resize(JSON_Array *array, size_t capacity);
|
||||
static void json_array_free(JSON_Array *array);
|
||||
|
||||
/* JSON Value */
|
||||
@ -87,10 +98,7 @@ static JSON_Value * json_value_init_boolean(int boolean);
|
||||
static JSON_Value * json_value_init_null(void);
|
||||
|
||||
/* Parser */
|
||||
static char * parson_strndup(const char *string, size_t n);
|
||||
static void skip_quotes(const char **string);
|
||||
static int is_utf(const char *string);
|
||||
static int is_decimal(const char *string, size_t length);
|
||||
static void skip_quotes(const char **string);
|
||||
static const char * get_processed_string(const char **string);
|
||||
static JSON_Value * parse_object_value(const char **string, size_t nesting);
|
||||
static JSON_Value * parse_array_value(const char **string, size_t nesting);
|
||||
@ -100,6 +108,33 @@ static JSON_Value * parse_number_value(const char **string);
|
||||
static JSON_Value * parse_null_value(const char **string);
|
||||
static JSON_Value * parse_value(const char **string, size_t nesting);
|
||||
|
||||
/* Various */
|
||||
static int try_realloc(void **ptr, size_t new_size) {
|
||||
void *reallocated_ptr = parson_realloc(*ptr, new_size);
|
||||
if (!reallocated_ptr) { return ERROR; }
|
||||
*ptr = reallocated_ptr;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static char * parson_strndup(const char *string, size_t n) {
|
||||
char *output_string = (char*)parson_malloc(n + 1);
|
||||
if (!output_string) { return NULL; }
|
||||
output_string[n] = '\0';
|
||||
strncpy(output_string, string, n);
|
||||
return output_string;
|
||||
}
|
||||
|
||||
static int is_utf(const char *s) {
|
||||
return isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]);
|
||||
}
|
||||
|
||||
static int is_decimal(const char *string, size_t length) {
|
||||
if (length > 1 && string[0] == '0' && string[1] != '.') { return 0; }
|
||||
if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') { return 0; }
|
||||
while (length--) { if (strchr("xX", string[length])) { return 0; } }
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* JSON Object */
|
||||
static JSON_Object * json_object_init(void) {
|
||||
JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object));
|
||||
@ -115,41 +150,41 @@ static JSON_Object * json_object_init(void) {
|
||||
|
||||
static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value) {
|
||||
size_t index;
|
||||
void *reallocated_ptr;
|
||||
if (object->count >= object->capacity) {
|
||||
size_t new_capacity = object->capacity * 2;
|
||||
if (new_capacity > MAX_CAPACITY) { return 0; }
|
||||
reallocated_ptr = parson_realloc((void*)object->names, new_capacity * sizeof(char*));
|
||||
if (!reallocated_ptr) { return 0;}
|
||||
object->names = (const char**)reallocated_ptr;
|
||||
reallocated_ptr = parson_realloc(object->values, new_capacity * sizeof(JSON_Value*));
|
||||
if (!reallocated_ptr) { return 0;}
|
||||
object->values = (JSON_Value**)reallocated_ptr;
|
||||
object->capacity = new_capacity;
|
||||
if (new_capacity > OBJECT_MAX_CAPACITY) { return ERROR; }
|
||||
if (json_object_resize(object, new_capacity) == ERROR) { return ERROR; }
|
||||
}
|
||||
if (json_object_get_value(object, name) != NULL) { return 0; }
|
||||
if (json_object_get_value(object, name) != NULL) { return ERROR; }
|
||||
index = object->count;
|
||||
object->names[index] = parson_strndup(name, strlen(name));
|
||||
if (!object->names[index]) { return 0; }
|
||||
if (!object->names[index]) { return ERROR; }
|
||||
object->values[index] = value;
|
||||
object->count++;
|
||||
return 1;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static int json_object_resize(JSON_Object *object, size_t capacity) {
|
||||
if (try_realloc((void**)&object->names, capacity * sizeof(char*)) == ERROR) { return ERROR; }
|
||||
if (try_realloc((void**)&object->values, capacity * sizeof(JSON_Value*)) == ERROR) { return ERROR; }
|
||||
object->capacity = capacity;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n) {
|
||||
size_t i;
|
||||
if (!object) { return NULL; }
|
||||
for (i = 0; i < object->count; i++) {
|
||||
size_t i, name_length;
|
||||
for (i = 0; i < json_object_get_count(object); i++) {
|
||||
name_length = strlen(object->names[i]);
|
||||
if (name_length != n) { continue; }
|
||||
if (strncmp(object->names[i], name, n) == 0) { return object->values[i]; }
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void json_object_free(JSON_Object *object) {
|
||||
size_t i;
|
||||
for (i = 0; i < object->count; i++) {
|
||||
parson_free(object->names[i]);
|
||||
json_value_free(object->values[i]);
|
||||
while(object->count--) {
|
||||
parson_free(object->names[object->count]);
|
||||
json_value_free(object->values[object->count]);
|
||||
}
|
||||
parson_free(object->names);
|
||||
parson_free(object->values);
|
||||
@ -168,23 +203,24 @@ static JSON_Array * json_array_init(void) {
|
||||
}
|
||||
|
||||
static int json_array_add(JSON_Array *array, JSON_Value *value) {
|
||||
void *reallocated_ptr;
|
||||
if (array->count >= array->capacity) {
|
||||
size_t new_capacity = array->capacity * 2;
|
||||
if (new_capacity > MAX_CAPACITY) { return 0; }
|
||||
reallocated_ptr = parson_realloc(array->items, new_capacity * sizeof(JSON_Value*));
|
||||
if (!reallocated_ptr) { return 0; }
|
||||
array->items = (JSON_Value**)reallocated_ptr;
|
||||
array->capacity = new_capacity;
|
||||
if (new_capacity > ARRAY_MAX_CAPACITY) { return ERROR; }
|
||||
if (!json_array_resize(array, new_capacity)) { return ERROR; }
|
||||
}
|
||||
array->items[array->count] = value;
|
||||
array->count++;
|
||||
return 1;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static int json_array_resize(JSON_Array *array, size_t capacity) {
|
||||
if (try_realloc((void**)&array->items, capacity * sizeof(JSON_Value*)) == ERROR) { return ERROR; }
|
||||
array->capacity = capacity;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static void json_array_free(JSON_Array *array) {
|
||||
size_t i;
|
||||
for (i = 0; i < array->count; i++) { json_value_free(array->items[i]); }
|
||||
while (array->count--) { json_value_free(array->items[array->count]); }
|
||||
parson_free(array->items);
|
||||
parson_free(array);
|
||||
}
|
||||
@ -240,55 +276,28 @@ static JSON_Value * json_value_init_null(void) {
|
||||
}
|
||||
|
||||
/* Parser */
|
||||
static char * parson_strndup(const char *string, size_t n) {
|
||||
char *output_string = (char*)parson_malloc(n + 1);
|
||||
if (!output_string) { return NULL; }
|
||||
output_string[n] = '\0';
|
||||
strncpy(output_string, string, n);
|
||||
return output_string;
|
||||
}
|
||||
|
||||
static void skip_quotes(const char **string) {
|
||||
skip_char(string);
|
||||
while (**string != '\"') {
|
||||
if (**string == '\0') { return; }
|
||||
if (**string == '\\') { skip_char(string); if (**string == '\0') { return; }}
|
||||
skip_char(string);
|
||||
skip_char(string);
|
||||
}
|
||||
skip_char(string);
|
||||
}
|
||||
|
||||
static int is_utf(const char *string) {
|
||||
if (!isxdigit(string[0])) { return 0; }
|
||||
if (!isxdigit(string[1])) { return 0; }
|
||||
if (!isxdigit(string[2])) { return 0; }
|
||||
if (!isxdigit(string[3])) { return 0; }
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int is_decimal(const char *string, size_t length) {
|
||||
if (length > 1 && string[0] == '0' && string[1] != '.') { return 0; }
|
||||
if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') { return 0; }
|
||||
while (length--) { if (strchr("xX", string[length])) { return 0; } }
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Returns contents of a string inside double quotes and parses escaped
|
||||
characters inside.
|
||||
Example: "\u006Corem ipsum" -> lorem ipsum */
|
||||
static const char * get_processed_string(const char **string) {
|
||||
const char *string_start = *string;
|
||||
char *output;
|
||||
char *processed_ptr;
|
||||
char *unprocessed_ptr;
|
||||
char current_char;
|
||||
char *output, *processed_ptr, *unprocessed_ptr, current_char;
|
||||
unsigned int utf_val;
|
||||
void *reallocated_ptr;
|
||||
skip_quotes(string);
|
||||
if (**string == '\0') { return NULL; }
|
||||
output = parson_strndup(string_start + 1, *string - string_start - 2);
|
||||
if (!output) { return NULL; }
|
||||
processed_ptr = unprocessed_ptr = output;
|
||||
processed_ptr = unprocessed_ptr = output;
|
||||
while (*unprocessed_ptr) {
|
||||
current_char = *unprocessed_ptr;
|
||||
if (current_char == '\\') {
|
||||
@ -304,7 +313,7 @@ static const char * get_processed_string(const char **string) {
|
||||
case 'u':
|
||||
unprocessed_ptr++;
|
||||
if (!is_utf(unprocessed_ptr) ||
|
||||
sscanf(unprocessed_ptr, "%4x", &utf_val) == EOF) {
|
||||
sscanf(unprocessed_ptr, "%4x", &utf_val) == EOF) {
|
||||
parson_free(output); return NULL;
|
||||
}
|
||||
if (utf_val < 0x80) {
|
||||
@ -333,47 +342,37 @@ static const char * get_processed_string(const char **string) {
|
||||
unprocessed_ptr++;
|
||||
}
|
||||
*processed_ptr = '\0';
|
||||
reallocated_ptr = parson_realloc(output, strlen(output) + 1);
|
||||
if (!reallocated_ptr) { parson_free(output); return NULL; }
|
||||
output = (char*)reallocated_ptr;
|
||||
if (try_realloc((void**)&output, strlen(output) + 1) == ERROR) { return NULL; }
|
||||
return output;
|
||||
}
|
||||
|
||||
static JSON_Value * parse_value(const char **string, size_t nesting) {
|
||||
JSON_Value *output_value = NULL;
|
||||
if (*string == NULL || nesting > MAX_NESTING) { return NULL; }
|
||||
if (nesting > MAX_NESTING) { return NULL; }
|
||||
skip_whitespaces(string);
|
||||
switch (**string) {
|
||||
case '{':
|
||||
output_value = parse_object_value(string, nesting + 1);
|
||||
break;
|
||||
return parse_object_value(string, nesting + 1);
|
||||
case '[':
|
||||
output_value = parse_array_value(string, nesting + 1);
|
||||
break;
|
||||
return parse_array_value(string, nesting + 1);
|
||||
case '\"':
|
||||
output_value = parse_string_value(string);
|
||||
break;
|
||||
return parse_string_value(string);
|
||||
case 'f': case 't':
|
||||
output_value = parse_boolean_value(string);
|
||||
break;
|
||||
return parse_boolean_value(string);
|
||||
case '-':
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
output_value = parse_number_value(string);
|
||||
break;
|
||||
return parse_number_value(string);
|
||||
case 'n':
|
||||
output_value = parse_null_value(string);
|
||||
break;
|
||||
return parse_null_value(string);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
return output_value;
|
||||
}
|
||||
|
||||
static JSON_Value * parse_object_value(const char **string, size_t nesting) {
|
||||
JSON_Value *output_value = json_value_init_object();
|
||||
JSON_Value *output_value = json_value_init_object(), *new_value = NULL;
|
||||
JSON_Object *output_object = json_value_get_object(output_value);
|
||||
const char *new_key = NULL;
|
||||
JSON_Value *new_value = NULL;
|
||||
if (!output_value) { return NULL; }
|
||||
skip_char(string);
|
||||
skip_whitespaces(string);
|
||||
@ -392,7 +391,7 @@ static JSON_Value * parse_object_value(const char **string, size_t nesting) {
|
||||
json_value_free(output_value);
|
||||
return NULL;
|
||||
}
|
||||
if(!json_object_add(json_value_get_object(output_value), new_key, new_value)) {
|
||||
if(!json_object_add(output_object, new_key, new_value)) {
|
||||
parson_free(new_key);
|
||||
parson_free(new_value);
|
||||
json_value_free(output_value);
|
||||
@ -405,14 +404,18 @@ static JSON_Value * parse_object_value(const char **string, size_t nesting) {
|
||||
skip_whitespaces(string);
|
||||
}
|
||||
skip_whitespaces(string);
|
||||
if (**string != '}') { json_value_free(output_value); return NULL; }
|
||||
if (**string != '}' || /* Trim object after parsing is over */
|
||||
json_object_resize(output_object, json_object_get_count(output_object)) == ERROR) {
|
||||
json_value_free(output_value);
|
||||
return NULL;
|
||||
}
|
||||
skip_char(string);
|
||||
return output_value;
|
||||
}
|
||||
|
||||
static JSON_Value * parse_array_value(const char **string, size_t nesting) {
|
||||
JSON_Value *output_value = json_value_init_array();
|
||||
JSON_Value *new_array_value = NULL;
|
||||
JSON_Value *output_value = json_value_init_array(), *new_array_value = NULL;
|
||||
JSON_Array *output_array = json_value_get_array(output_value);
|
||||
if (!output_value) { return NULL; }
|
||||
skip_char(string);
|
||||
skip_whitespaces(string);
|
||||
@ -426,7 +429,7 @@ static JSON_Value * parse_array_value(const char **string, size_t nesting) {
|
||||
json_value_free(output_value);
|
||||
return NULL;
|
||||
}
|
||||
if(!json_array_add(json_value_get_array(output_value), new_array_value)) {
|
||||
if(json_array_add(output_array, new_array_value) == ERROR) {
|
||||
parson_free(new_array_value);
|
||||
json_value_free(output_value);
|
||||
return NULL;
|
||||
@ -437,7 +440,8 @@ static JSON_Value * parse_array_value(const char **string, size_t nesting) {
|
||||
skip_whitespaces(string);
|
||||
}
|
||||
skip_whitespaces(string);
|
||||
if (**string != ']') {
|
||||
if (**string != ']' || /* Trim array after parsing is over */
|
||||
json_array_resize(output_array, json_array_get_count(output_array)) == ERROR) {
|
||||
json_value_free(output_value);
|
||||
return NULL;
|
||||
}
|
||||
@ -507,17 +511,16 @@ JSON_Value * json_parse_file(const char *filename) {
|
||||
}
|
||||
|
||||
JSON_Value * json_parse_string(const char *string) {
|
||||
JSON_Value *output_value = NULL;
|
||||
if (!string) { return NULL; }
|
||||
if (*string == '{' || *string == '[') {
|
||||
output_value = parse_value((const char**)&string, 0);
|
||||
if (string && (*string == '{' || *string == '[')) {
|
||||
return parse_value((const char**)&string, 0);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
return output_value;
|
||||
}
|
||||
|
||||
/* JSON Object API */
|
||||
JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) {
|
||||
return json_object_nget_value(object, name, strlen(name) + 1);
|
||||
return json_object_nget_value(object, name, strlen(name));
|
||||
}
|
||||
|
||||
const char * json_object_get_string(const JSON_Object *object, const char *name) {
|
||||
@ -567,6 +570,15 @@ int json_object_dotget_boolean(const JSON_Object *object, const char *name) {
|
||||
return json_value_get_boolean(json_object_dotget_value(object, name));
|
||||
}
|
||||
|
||||
size_t json_object_get_count(const JSON_Object *object) {
|
||||
return object ? object->count : 0;
|
||||
}
|
||||
|
||||
const char * json_object_get_name(const JSON_Object *object, size_t index) {
|
||||
if (index >= json_object_get_count(object)) { return NULL; }
|
||||
return object->names[index];
|
||||
}
|
||||
|
||||
/* JSON Array API */
|
||||
JSON_Value * json_array_get_value(const JSON_Array *array, size_t index) {
|
||||
if (index >= json_array_get_count(array)) { return NULL; }
|
||||
|
4
parson.h
4
parson.h
@ -71,6 +71,10 @@ JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *
|
||||
double json_object_dotget_number (const JSON_Object *object, const char *name);
|
||||
int json_object_dotget_boolean(const JSON_Object *object, const char *name);
|
||||
|
||||
/* Functions to get available names */
|
||||
size_t json_object_get_count(const JSON_Object *object);
|
||||
const char * json_object_get_name (const JSON_Object *object, size_t index);
|
||||
|
||||
/* JSON Array */
|
||||
JSON_Value * json_array_get_value (const JSON_Array *array, size_t index);
|
||||
const char * json_array_get_string (const JSON_Array *array, size_t index);
|
||||
|
Loading…
Reference in New Issue
Block a user