better memory management and cleanup on malloc fail

This commit is contained in:
overflowerror 2021-05-06 16:00:24 +02:00
parent 4b3ce34e2b
commit a1cf81a8ee
3 changed files with 269 additions and 56 deletions

View file

@ -51,12 +51,16 @@ jsonValue_t* json_value() {
jsonValue_t* json_null() {
jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_NULL;
return value;
}
jsonValue_t* json_double(double d) {
jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_DOUBLE;
value->value.real = d;
return value;
@ -64,6 +68,8 @@ jsonValue_t* json_double(double d) {
jsonValue_t* json_long(long l) {
jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_LONG;
value->value.integer = l;
return value;
@ -71,6 +77,8 @@ jsonValue_t* json_long(long l) {
jsonValue_t* json_bool(bool b) {
jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_BOOL;
value->value.boolean = b;
return value;
@ -78,75 +86,166 @@ jsonValue_t* json_bool(bool b) {
jsonValue_t* json_string(const char* s) {
jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_STRING;
value->value.string = strdup(s);
if (value->value.string == NULL) {
free(value);
return NULL;
}
return value;
}
jsonValue_t* json_array(bool freeAfterwards, size_t size, ...) {
jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_ARRAY;
value->value.array.size = size;
value->value.array.entries = malloc(sizeof(jsonValue_t) * size);
if (value->value.array.entries == NULL) {
free(value);
return NULL;
}
bool abort = false;
va_list ap;
va_start(ap, size);
for (size_t i = 0; i < size; i++) {
jsonValue_t* entry = va_arg(ap, jsonValue_t*);
if (entry == NULL) {
abort = true;
if (freeAfterwards) {
// we need to free each argument
// this one is null -> don't free
continue;
} else {
break;
}
}
// if aborted this does nothing
value->value.array.entries[i] = *entry;
if (freeAfterwards) {
free(entry);
}
}
va_end(ap);
if (abort) {
free(value->value.array.entries);
free(value);
return NULL;
}
return value;
}
jsonValue_t* json_array_direct(bool freeAfterwards, size_t size, jsonValue_t* values[]) {
jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_ARRAY;
value->value.array.size = size;
value->value.array.entries = malloc(sizeof(jsonValue_t) * size);
if (value->value.array.entries == NULL) {
free(value);
return NULL;
}
bool abort = false;
for (size_t i = 0; i < size; i++) {
value->value.array.entries[i] = *values[i];
if ((values[i]) == NULL) {
abort = true;
if (freeAfterwards) {
continue;
} else {
break;
}
}
// if aborted this does nothing
value->value.array.entries[i] = *(values[i]);
if (freeAfterwards) {
free(values[i]);
}
}
if (abort) {
free(value->value.array.entries);
free(value);
return NULL;
}
return value;
}
jsonValue_t* json_object(bool freeAfterwards, size_t size, ...) {
jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_OBJECT;
value->value.object.size = size;
value->value.object.entries = malloc(sizeof(jsonObjectEntry_t) * size);
// using calloc so the memory is set to 0
value->value.object.entries = calloc(size, sizeof(jsonObjectEntry_t));
if (value->value.object.entries == NULL) {
free(value);
return NULL;
}
bool abort = false;
va_list ap;
va_start(ap, size);
for (size_t i = 0; i < size; i++) {
const char* key = va_arg(ap, const char*);
jsonValue_t* entry = va_arg(ap, jsonValue_t*);
value->value.object.entries[i].key = strdup(key);
value->value.object.entries[i].value = *entry;
if (key == NULL) {
abort = true;
if (!freeAfterwards) {
break;
}
}
if (entry == NULL) {
abort = true;
if (freeAfterwards) {
continue;
} else {
break;
}
}
if (!abort) {
value->value.object.entries[i].key = strdup(key);
value->value.object.entries[i].value = *entry;
}
if (freeAfterwards) {
free(entry);
}
}
va_end(ap);
if (abort) {
for (size_t i = 0; i < size; i++) {
if (value->value.object.entries[i].key != NULL) {
free(value->value.object.entries[i].key);
}
}
free(value->value.object.entries);
free(value);
return NULL;
}
return value;
}
void print_repeat(int i, char c) {
for (int j = 0; j < i; j++) {
printf("%c", c);
@ -197,58 +296,81 @@ void json_print(jsonValue_t* value) {
json_print_r(value, 0);
}
jsonValue_t json_clone_r(jsonValue_t value) {
jsonValue_t clone = value;
int json_clone_r(jsonValue_t* value, jsonValue_t* clone) {
*clone = *value;
switch(value.type) {
switch(value->type) {
case JSON_STRING:
clone.value.string = strdup(value.value.string);
clone->value.string = strdup(value->value.string);
if (clone->value.string == NULL) {
return -1;
}
break;
case JSON_ARRAY:
clone.value.array.size = value.value.array.size;
clone.value.array.entries = malloc(sizeof(jsonValue_t) * clone.value.array.size);
clone->value.array.size = value->value.array.size;
clone->value.array.entries = malloc(sizeof(jsonValue_t) * clone->value.array.size);
if (clone.value.array.entries == NULL) {
fprintf(stderr, "fuu\n");
exit(1);
if (clone->value.array.entries == NULL) {
return -1;
}
for (size_t i = 0; i < clone.value.array.size; i++) {
clone.value.array.entries[i] = json_clone_r(value.value.array.entries[i]);
for (size_t i = 0; i < clone->value.array.size; i++) {
if (json_clone_r(&(value->value.array.entries[i]), &(clone->value.array.entries[i])) < 0) {
free(clone->value.array.entries);
return -1;
}
}
break;
case JSON_OBJECT:
clone.value.object.size = value.value.object.size;
clone.value.object.entries = malloc(sizeof(jsonObjectEntry_t) * clone.value.object.size);
clone->value.object.size = value->value.object.size;
clone->value.object.entries = malloc(sizeof(jsonObjectEntry_t) * clone->value.object.size);
if (clone.value.object.entries == NULL) {
fprintf(stderr, "fuu\n");
exit(1);
if (clone->value.object.entries == NULL) {
return -1;
}
for (size_t i = 0; i < clone.value.object.size; i++) {
clone.value.object.entries[i].key = strdup(value.value.object.entries[i].key);
clone.value.object.entries[i].value = json_clone_r(value.value.object.entries[i].value);
for (size_t i = 0; i < clone->value.object.size; i++) {
bool okay = true;
clone->value.object.entries[i].key = strdup(value->value.object.entries[i].key);
if (clone->value.object.entries[i].key == NULL) {
okay = false;
} else if (json_clone_r(&(value->value.object.entries[i].value), &(clone->value.object.entries[i].value)) < 0) {
free(clone->value.object.entries[i].key);
okay = false;
}
if (!okay) {
for (size_t j = 0; j < i; j++) {
free(clone->value.object.entries[j].key);
json_free_r(&(clone->value.object.entries[j].value));
}
free(clone->value.object.entries);
return -1;
}
}
break;
default:
// non dynamic members already copied
break;
}
return clone;
return 0;
}
jsonValue_t* json_clone(jsonValue_t* value) {
jsonValue_t* clone = malloc(sizeof(jsonValue_t));
if (clone == NULL) {
fprintf(stderr, "fuu\n");
exit(1);
return NULL;
}
*clone = json_clone_r(*value);
if (json_clone_r(value, clone) < 0) {
free(clone);
return NULL;
}
return clone;
}

View file

@ -16,12 +16,20 @@ struct parserToken {
#define PARSER_TOKEN_CHUNK_SIZE (1024)
void addToParserToken(struct parserToken* token, char c) {
int addToParserToken(struct parserToken* token, char c) {
if (token->length % PARSER_TOKEN_CHUNK_SIZE == 0) {
token->token = realloc(token->token, sizeof(char) * (token->length / PARSER_TOKEN_CHUNK_SIZE + 1) * PARSER_TOKEN_CHUNK_SIZE);
char* tmp = realloc(token->token, sizeof(char) * (token->length / PARSER_TOKEN_CHUNK_SIZE + 1) * PARSER_TOKEN_CHUNK_SIZE);
if (tmp == NULL) {
free(token->token);
token->token = NULL;
return -1;
}
token->token = tmp;
}
token->token[token->length++] = c;
return 0;
}
void freeParserToken(struct parserToken* token) {
@ -51,7 +59,10 @@ typedef struct {
#define JSON_PARSER_STATE_DOUBLE (50)
jsonParsedValue_t json_parse_long(jsonParsedValue_t value, struct parserToken token) {
addToParserToken(&token, '\0');
if (addToParserToken(&token, '\0') < 0) {
value.errorFormat = "internal error while parsing numgber";
return value;
}
char* endptr;
long long l = strtoll(token.token, &endptr, 10);
@ -68,7 +79,10 @@ jsonParsedValue_t json_parse_long(jsonParsedValue_t value, struct parserToken to
}
jsonParsedValue_t json_parse_double(jsonParsedValue_t value, struct parserToken token) {
addToParserToken(&token, '\0');
if (addToParserToken(&token, '\0') < 0) {
value.errorFormat = "internal error while parsing numgber";
return value;
}
char* endptr;
double d = strtod(token.token, &endptr);
@ -138,7 +152,10 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
case '8':
case '9':
case '-':
addToParserToken(&token, c);
if (addToParserToken(&token, c) < 0) {
value.errorFormat = "internal error in line %ld";
return value;
}
state = JSON_PARSER_STATE_LONG;
break;
default:
@ -161,56 +178,77 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
value.errorFormat = "illegal character in line %d: '%c'";
}
freeParserToken(&token);
return value;
}
break;
case JSON_PARSER_STATE_STRING:
if (!escaped && c == '"') {
addToParserToken(&token, '\0');
value.okay = true;
if (addToParserToken(&token, '\0') < 0) {
value.errorFormat = "internal error while parsing string";
return value;
}
value.index = index + 1;
value.value.value.string = strdup(token.token);
freeParserToken(&token);
if (value.value.value.string == NULL) {
value.errorFormat = "couldn't strdup while parsing string";
freeParserToken(&token);
return value;
}
value.okay = true;
freeParserToken(&token);
return value;
}
if (!escaped && c == '\\') {
escaped = true;
break;
} else if (escaped) {
int tmp = 0;
switch(c) {
case 'b':
addToParserToken(&token, '\b');
tmp = addToParserToken(&token, '\b');
break;
case 'f':
addToParserToken(&token, '\f');
tmp = addToParserToken(&token, '\f');
break;
case 'n':
addToParserToken(&token, '\n');
tmp = addToParserToken(&token, '\n');
break;
case 'r':
addToParserToken(&token, '\r');
tmp = addToParserToken(&token, '\r');
break;
case 't':
addToParserToken(&token, '\t');
tmp = addToParserToken(&token, '\t');
break;
case 'u':
value.okay = false;
value.index = index;
value.errorFormat = "line %ld: \\u-syntax is not supported";
freeParserToken(&token);
return value;
case '"':
case '\\':
case '/':
addToParserToken(&token, c);
tmp = addToParserToken(&token, c);
break;
default:
addToParserToken(&token, '\\');
addToParserToken(&token, c);
tmp = addToParserToken(&token, '\\');
tmp = addToParserToken(&token, c);
break;
}
if (tmp < 0) {
value.errorFormat = "internal error while parsing string escape sequence";
freeParserToken(&token);
return value;
}
escaped = false;
} else {
switch(c) {
@ -222,32 +260,56 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
value.okay = false;
value.index = index;
value.errorFormat = "line %ld: control characters are not allowed in json strings";
freeParserToken(&token);
return value;
default:
addToParserToken(&token, c);
if (addToParserToken(&token, c) < 0) {
value.errorFormat = "internal error while parsing string";
freeParserToken(&token);
return value;
}
break;
}
}
break;
case JSON_PARSER_STATE_LONG:
if (c >= '0' && c <= '9') {
addToParserToken(&token, c);
if (addToParserToken(&token, c) < 0) {
value.errorFormat = "internal error while parsing number";
freeParserToken(&token);
return value;
}
} else if (c == '.' || c == 'e') {
addToParserToken(&token, c);
if (addToParserToken(&token, c) < 0) {
value.errorFormat = "internal error while parsing number";
freeParserToken(&token);
return value;
}
state = JSON_PARSER_STATE_DOUBLE;
} else {
value.index = index;
value = json_parse_long(value, token);
freeParserToken(&token);
return value;
}
break;
case JSON_PARSER_STATE_DOUBLE:
if ((c >= '0' && c <= '9') || c == '.' || c == '+' || c == '-' || c == 'e') {
addToParserToken(&token, c);
if (addToParserToken(&token, c) < 0) {
value.errorFormat = "internal error while parsing number";
freeParserToken(&token);
return value;
}
} else {
value.index = index;
value = json_parse_double(value, token);
freeParserToken(&token);
return value;
}
@ -265,6 +327,8 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
value.errorFormat = "line %ld: unexpected '%c'";
value.index = index;
freeParserToken(&token);
return value;
}
@ -286,6 +350,7 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
if (!entry.okay) {
json_free_r(&value.value);
freeParserToken(&token);
return entry;
}
@ -295,6 +360,8 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
json_free_r(&value.value);
value.errorFormat = "allocation for array failed";
freeParserToken(&token);
return value;
}
@ -328,6 +395,8 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
value.errorFormat = "line %ld: unexpected ','; ':' expected";
value.index = index;
freeParserToken(&token);
return value;
}
@ -342,6 +411,8 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
value.errorFormat = "line %ld: unexpected character ':'";
value.index = index;
freeParserToken(&token);
return value;
}
@ -350,6 +421,8 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
value.errorFormat = "line %ld: unexpected ':'; key is missing";
value.index = index;
freeParserToken(&token);
return value;
}
@ -363,6 +436,8 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
value.errorFormat = "line %ld: unexpected '%c'; ',' or '}' expected";
value.index = index;
freeParserToken(&token);
return value;
}
@ -371,6 +446,7 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
if (!entry.okay) {
json_free_r(&value.value);
freeParserToken(&token);
return entry;
}
@ -379,9 +455,10 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
json_free_r(&value.value);
json_free_r(&entry.value);
value.errorFormat = "line %ld: key is missing";
value.index = index;
freeParserToken(&token);
return value;
}
@ -394,6 +471,8 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
json_free_r(&value.value);
value.errorFormat = "allocation for object failed";
freeParserToken(&token);
return value;
}
@ -414,6 +493,8 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
default:
value.index = index;
value.errorFormat = "illegal state in line %ld";
freeParserToken(&token);
return value;
}
}
@ -421,21 +502,22 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
if (state == JSON_PARSER_STATE_LONG) {
value.index = index;
value = json_parse_long(value, token);
freeParserToken(&token);
return value;
}
if (state == JSON_PARSER_STATE_DOUBLE) {
value.index = index;
value = json_parse_double(value, token);
freeParserToken(&token);
return value;
}
freeParserToken(&token);
value.index = index - 1;
value.errorFormat = "unexpected end of input on line %ld";
freeParserToken(&token);
return value;
}
@ -445,6 +527,7 @@ jsonValue_t* json_parse(const char* string) {
jsonParsedValue_t parsedValue = json_parse_r(string, 0, 1, length);
if (!parsedValue.okay) {
// TODO put in extern global instead
printf("%ld\n", parsedValue.index);
printf(parsedValue.errorFormat, parsedValue.line, string[parsedValue.index]);
printf("\n");
@ -457,6 +540,11 @@ jsonValue_t* json_parse(const char* string) {
}
jsonValue_t* value = malloc(sizeof(jsonValue_t));
if (value == NULL) {
json_free_r(&(parsedValue.value));
return NULL;
}
*value = parsedValue.value;
return value;

View file

@ -34,6 +34,9 @@ jsonValue_t* json_query(jsonValue_t* value, const char* query) {
char buffer[JSON_QUERY_BUFFER_SIZE];
value = json_clone(value);
if (value == NULL) {
return NULL;
}
while(true) {
if (query[0] == '\0')