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* json_null() {
jsonValue_t* value = json_value(); jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_NULL; value->type = JSON_NULL;
return value; return value;
} }
jsonValue_t* json_double(double d) { jsonValue_t* json_double(double d) {
jsonValue_t* value = json_value(); jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_DOUBLE; value->type = JSON_DOUBLE;
value->value.real = d; value->value.real = d;
return value; return value;
@ -64,6 +68,8 @@ jsonValue_t* json_double(double d) {
jsonValue_t* json_long(long l) { jsonValue_t* json_long(long l) {
jsonValue_t* value = json_value(); jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_LONG; value->type = JSON_LONG;
value->value.integer = l; value->value.integer = l;
return value; return value;
@ -71,6 +77,8 @@ jsonValue_t* json_long(long l) {
jsonValue_t* json_bool(bool b) { jsonValue_t* json_bool(bool b) {
jsonValue_t* value = json_value(); jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_BOOL; value->type = JSON_BOOL;
value->value.boolean = b; value->value.boolean = b;
return value; return value;
@ -78,75 +86,166 @@ jsonValue_t* json_bool(bool b) {
jsonValue_t* json_string(const char* s) { jsonValue_t* json_string(const char* s) {
jsonValue_t* value = json_value(); jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_STRING; value->type = JSON_STRING;
value->value.string = strdup(s); value->value.string = strdup(s);
if (value->value.string == NULL) {
free(value);
return NULL;
}
return value; return value;
} }
jsonValue_t* json_array(bool freeAfterwards, size_t size, ...) { jsonValue_t* json_array(bool freeAfterwards, size_t size, ...) {
jsonValue_t* value = json_value(); jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_ARRAY; value->type = JSON_ARRAY;
value->value.array.size = size; value->value.array.size = size;
value->value.array.entries = malloc(sizeof(jsonValue_t) * 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_list ap;
va_start(ap, size); va_start(ap, size);
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
jsonValue_t* entry = va_arg(ap, jsonValue_t*); 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; value->value.array.entries[i] = *entry;
if (freeAfterwards) { if (freeAfterwards) {
free(entry); free(entry);
} }
} }
va_end(ap); va_end(ap);
if (abort) {
free(value->value.array.entries);
free(value);
return NULL;
}
return value; return value;
} }
jsonValue_t* json_array_direct(bool freeAfterwards, size_t size, jsonValue_t* values[]) { jsonValue_t* json_array_direct(bool freeAfterwards, size_t size, jsonValue_t* values[]) {
jsonValue_t* value = json_value(); jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_ARRAY; value->type = JSON_ARRAY;
value->value.array.size = size; value->value.array.size = size;
value->value.array.entries = malloc(sizeof(jsonValue_t) * 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++) { 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) { if (freeAfterwards) {
free(values[i]); free(values[i]);
} }
} }
if (abort) {
free(value->value.array.entries);
free(value);
return NULL;
}
return value; return value;
} }
jsonValue_t* json_object(bool freeAfterwards, size_t size, ...) { jsonValue_t* json_object(bool freeAfterwards, size_t size, ...) {
jsonValue_t* value = json_value(); jsonValue_t* value = json_value();
if (value == NULL)
return NULL;
value->type = JSON_OBJECT; value->type = JSON_OBJECT;
value->value.object.size = size; 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_list ap;
va_start(ap, size); va_start(ap, size);
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
const char* key = va_arg(ap, const char*); const char* key = va_arg(ap, const char*);
jsonValue_t* entry = va_arg(ap, jsonValue_t*); 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) { if (freeAfterwards) {
free(entry); free(entry);
} }
} }
va_end(ap); 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; return value;
} }
void print_repeat(int i, char c) { void print_repeat(int i, char c) {
for (int j = 0; j < i; j++) { for (int j = 0; j < i; j++) {
printf("%c", c); printf("%c", c);
@ -197,58 +296,81 @@ void json_print(jsonValue_t* value) {
json_print_r(value, 0); json_print_r(value, 0);
} }
jsonValue_t json_clone_r(jsonValue_t value) { int json_clone_r(jsonValue_t* value, jsonValue_t* clone) {
jsonValue_t clone = value; *clone = *value;
switch(value.type) { switch(value->type) {
case JSON_STRING: 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; break;
case JSON_ARRAY: case JSON_ARRAY:
clone.value.array.size = value.value.array.size; clone->value.array.size = value->value.array.size;
clone.value.array.entries = malloc(sizeof(jsonValue_t) * clone.value.array.size); clone->value.array.entries = malloc(sizeof(jsonValue_t) * clone->value.array.size);
if (clone.value.array.entries == NULL) { if (clone->value.array.entries == NULL) {
fprintf(stderr, "fuu\n"); return -1;
exit(1);
} }
for (size_t i = 0; i < clone.value.array.size; i++) { for (size_t i = 0; i < clone->value.array.size; i++) {
clone.value.array.entries[i] = json_clone_r(value.value.array.entries[i]); if (json_clone_r(&(value->value.array.entries[i]), &(clone->value.array.entries[i])) < 0) {
free(clone->value.array.entries);
return -1;
}
} }
break; break;
case JSON_OBJECT: case JSON_OBJECT:
clone.value.object.size = value.value.object.size; clone->value.object.size = value->value.object.size;
clone.value.object.entries = malloc(sizeof(jsonObjectEntry_t) * clone.value.object.size); clone->value.object.entries = malloc(sizeof(jsonObjectEntry_t) * clone->value.object.size);
if (clone.value.object.entries == NULL) { if (clone->value.object.entries == NULL) {
fprintf(stderr, "fuu\n"); return -1;
exit(1);
} }
for (size_t i = 0; i < clone.value.object.size; i++) { for (size_t i = 0; i < clone->value.object.size; i++) {
clone.value.object.entries[i].key = strdup(value.value.object.entries[i].key); bool okay = true;
clone.value.object.entries[i].value = json_clone_r(value.value.object.entries[i].value);
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; break;
default: default:
// non dynamic members already copied
break; break;
} }
return clone; return 0;
} }
jsonValue_t* json_clone(jsonValue_t* value) { jsonValue_t* json_clone(jsonValue_t* value) {
jsonValue_t* clone = malloc(sizeof(jsonValue_t)); jsonValue_t* clone = malloc(sizeof(jsonValue_t));
if (clone == NULL) { if (clone == NULL) {
fprintf(stderr, "fuu\n"); return NULL;
exit(1);
} }
*clone = json_clone_r(*value); if (json_clone_r(value, clone) < 0) {
free(clone);
return NULL;
}
return clone; return clone;
} }

View file

@ -16,12 +16,20 @@ struct parserToken {
#define PARSER_TOKEN_CHUNK_SIZE (1024) #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) { 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; token->token[token->length++] = c;
return 0;
} }
void freeParserToken(struct parserToken* token) { void freeParserToken(struct parserToken* token) {
@ -51,7 +59,10 @@ typedef struct {
#define JSON_PARSER_STATE_DOUBLE (50) #define JSON_PARSER_STATE_DOUBLE (50)
jsonParsedValue_t json_parse_long(jsonParsedValue_t value, struct parserToken token) { 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; char* endptr;
long long l = strtoll(token.token, &endptr, 10); 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) { 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; char* endptr;
double d = strtod(token.token, &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 '8':
case '9': case '9':
case '-': case '-':
addToParserToken(&token, c); if (addToParserToken(&token, c) < 0) {
value.errorFormat = "internal error in line %ld";
return value;
}
state = JSON_PARSER_STATE_LONG; state = JSON_PARSER_STATE_LONG;
break; break;
default: 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'"; value.errorFormat = "illegal character in line %d: '%c'";
} }
freeParserToken(&token);
return value; return value;
} }
break; break;
case JSON_PARSER_STATE_STRING: case JSON_PARSER_STATE_STRING:
if (!escaped && c == '"') { if (!escaped && c == '"') {
addToParserToken(&token, '\0'); if (addToParserToken(&token, '\0') < 0) {
value.okay = true; value.errorFormat = "internal error while parsing string";
return value;
}
value.index = index + 1; value.index = index + 1;
value.value.value.string = strdup(token.token); 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; return value;
} }
if (!escaped && c == '\\') { if (!escaped && c == '\\') {
escaped = true; escaped = true;
break; break;
} else if (escaped) { } else if (escaped) {
int tmp = 0;
switch(c) { switch(c) {
case 'b': case 'b':
addToParserToken(&token, '\b'); tmp = addToParserToken(&token, '\b');
break; break;
case 'f': case 'f':
addToParserToken(&token, '\f'); tmp = addToParserToken(&token, '\f');
break; break;
case 'n': case 'n':
addToParserToken(&token, '\n'); tmp = addToParserToken(&token, '\n');
break; break;
case 'r': case 'r':
addToParserToken(&token, '\r'); tmp = addToParserToken(&token, '\r');
break; break;
case 't': case 't':
addToParserToken(&token, '\t'); tmp = addToParserToken(&token, '\t');
break; break;
case 'u': case 'u':
value.okay = false; value.okay = false;
value.index = index; value.index = index;
value.errorFormat = "line %ld: \\u-syntax is not supported"; value.errorFormat = "line %ld: \\u-syntax is not supported";
freeParserToken(&token);
return value; return value;
case '"': case '"':
case '\\': case '\\':
case '/': case '/':
addToParserToken(&token, c); tmp = addToParserToken(&token, c);
break; break;
default: default:
addToParserToken(&token, '\\'); tmp = addToParserToken(&token, '\\');
addToParserToken(&token, c); tmp = addToParserToken(&token, c);
break; break;
} }
if (tmp < 0) {
value.errorFormat = "internal error while parsing string escape sequence";
freeParserToken(&token);
return value;
}
escaped = false; escaped = false;
} else { } else {
switch(c) { 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.okay = false;
value.index = index; value.index = index;
value.errorFormat = "line %ld: control characters are not allowed in json strings"; value.errorFormat = "line %ld: control characters are not allowed in json strings";
freeParserToken(&token);
return value; return value;
default: default:
addToParserToken(&token, c); if (addToParserToken(&token, c) < 0) {
value.errorFormat = "internal error while parsing string";
freeParserToken(&token);
return value;
}
break; break;
} }
} }
break; break;
case JSON_PARSER_STATE_LONG: case JSON_PARSER_STATE_LONG:
if (c >= '0' && c <= '9') { 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') { } 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; state = JSON_PARSER_STATE_DOUBLE;
} else { } else {
value.index = index; value.index = index;
value = json_parse_long(value, token); value = json_parse_long(value, token);
freeParserToken(&token); freeParserToken(&token);
return value; return value;
} }
break; break;
case JSON_PARSER_STATE_DOUBLE: case JSON_PARSER_STATE_DOUBLE:
if ((c >= '0' && c <= '9') || c == '.' || c == '+' || c == '-' || c == 'e') { 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 { } else {
value.index = index; value.index = index;
value = json_parse_double(value, token); value = json_parse_double(value, token);
freeParserToken(&token); freeParserToken(&token);
return value; 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.errorFormat = "line %ld: unexpected '%c'";
value.index = index; value.index = index;
freeParserToken(&token);
return value; return value;
} }
@ -286,6 +350,7 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
if (!entry.okay) { if (!entry.okay) {
json_free_r(&value.value); json_free_r(&value.value);
freeParserToken(&token);
return entry; 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); json_free_r(&value.value);
value.errorFormat = "allocation for array failed"; value.errorFormat = "allocation for array failed";
freeParserToken(&token);
return value; 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.errorFormat = "line %ld: unexpected ','; ':' expected";
value.index = index; value.index = index;
freeParserToken(&token);
return value; 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.errorFormat = "line %ld: unexpected character ':'";
value.index = index; value.index = index;
freeParserToken(&token);
return value; 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.errorFormat = "line %ld: unexpected ':'; key is missing";
value.index = index; value.index = index;
freeParserToken(&token);
return value; 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.errorFormat = "line %ld: unexpected '%c'; ',' or '}' expected";
value.index = index; value.index = index;
freeParserToken(&token);
return value; return value;
} }
@ -371,6 +446,7 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
if (!entry.okay) { if (!entry.okay) {
json_free_r(&value.value); json_free_r(&value.value);
freeParserToken(&token);
return entry; 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(&value.value);
json_free_r(&entry.value); json_free_r(&entry.value);
value.errorFormat = "line %ld: key is missing"; value.errorFormat = "line %ld: key is missing";
value.index = index; value.index = index;
freeParserToken(&token);
return value; 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); json_free_r(&value.value);
value.errorFormat = "allocation for object failed"; value.errorFormat = "allocation for object failed";
freeParserToken(&token);
return value; return value;
} }
@ -414,6 +493,8 @@ jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, si
default: default:
value.index = index; value.index = index;
value.errorFormat = "illegal state in line %ld"; value.errorFormat = "illegal state in line %ld";
freeParserToken(&token);
return value; 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) { if (state == JSON_PARSER_STATE_LONG) {
value.index = index; value.index = index;
value = json_parse_long(value, token); value = json_parse_long(value, token);
freeParserToken(&token); freeParserToken(&token);
return value; return value;
} }
if (state == JSON_PARSER_STATE_DOUBLE) { if (state == JSON_PARSER_STATE_DOUBLE) {
value.index = index; value.index = index;
value = json_parse_double(value, token); value = json_parse_double(value, token);
freeParserToken(&token); freeParserToken(&token);
return value; return value;
} }
freeParserToken(&token);
value.index = index - 1; value.index = index - 1;
value.errorFormat = "unexpected end of input on line %ld"; value.errorFormat = "unexpected end of input on line %ld";
freeParserToken(&token);
return value; return value;
} }
@ -445,6 +527,7 @@ jsonValue_t* json_parse(const char* string) {
jsonParsedValue_t parsedValue = json_parse_r(string, 0, 1, length); jsonParsedValue_t parsedValue = json_parse_r(string, 0, 1, length);
if (!parsedValue.okay) { if (!parsedValue.okay) {
// TODO put in extern global instead
printf("%ld\n", parsedValue.index); printf("%ld\n", parsedValue.index);
printf(parsedValue.errorFormat, parsedValue.line, string[parsedValue.index]); printf(parsedValue.errorFormat, parsedValue.line, string[parsedValue.index]);
printf("\n"); printf("\n");
@ -457,6 +540,11 @@ jsonValue_t* json_parse(const char* string) {
} }
jsonValue_t* value = malloc(sizeof(jsonValue_t)); jsonValue_t* value = malloc(sizeof(jsonValue_t));
if (value == NULL) {
json_free_r(&(parsedValue.value));
return NULL;
}
*value = parsedValue.value; *value = parsedValue.value;
return 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]; char buffer[JSON_QUERY_BUFFER_SIZE];
value = json_clone(value); value = json_clone(value);
if (value == NULL) {
return NULL;
}
while(true) { while(true) {
if (query[0] == '\0') if (query[0] == '\0')