Feature: Use arena-allocation for memory handling
Arena allocation make deallocation of nested structed MUCH faster, and improves the spatial locality of allocations. It makes no sense to deallocate only parts of a JSON structure so arenas are a good fit here.
This commit is contained in:
committed by
Ole Kristian Morud
parent
f58c5dcfee
commit
b3821cf8bf
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "arena-allocator"]
|
||||||
|
path = arena-allocator
|
||||||
|
url = https://github.com/olemorud/arena-allocator.git
|
||||||
25
Makefile
25
Makefile
@@ -10,15 +10,18 @@ COMPILER ?= gcc
|
|||||||
CC := gcc
|
CC := gcc
|
||||||
|
|
||||||
# -fsanitize={address,undefined}
|
# -fsanitize={address,undefined}
|
||||||
CFLAGS.gcc.debug := -Og -ggdb -fanalyzer -DBACKTRACE -rdynamic -fsanitize=address -fno-omit-frame-pointer
|
CFLAGS.gcc.debug := -Og -ggdb -DBACKTRACE -rdynamic -fanalyzer -fsanitize=address -fno-omit-frame-pointer
|
||||||
CFLAGS.gcc.release := -O3 -g -march=native -DNDEBUG
|
CFLAGS.gcc.release := -O3 -g -march=native -DNDEBUG
|
||||||
CFLAGS.gcc := ${CFLAGS.gcc.${BUILD}} -Iinclude -Wall -Wextra -Wpedantic -Werror -Wno-strict-aliasing -fstack-protector-all -std=gnu11
|
CFLAGS.gcc := ${CFLAGS.gcc.${BUILD}} -fstack-protector-all -std=gnu11 #-Wpedantic
|
||||||
|
|
||||||
CFLAGS.clang.debug=-O0 -g3 -DBACKTRACE -rdynamic
|
CFLAGS.clang.debug=-O0 -g3 -DBACKTRACE -rdynamic
|
||||||
CFLAGS.clang.release=-O3 -g -march=native -DNDEBUG
|
CFLAGS.clang.release=-O3 -g -march=native -DNDEBUG
|
||||||
CFLAGS.clang=-Iinclude -Wall -Wextra -Wpedantic -Werror -Wno-strict-aliasing -fstack-protector-all ${CFLAGS.clang.${BUILD}}
|
CFLAGS.clang=-Iinclude -Wno-strict-aliasing -fstack-protector-all ${CFLAGS.clang.${BUILD}}
|
||||||
|
|
||||||
CFLAGS := ${CFLAGS.${COMPILER}}
|
CFLAGS := ${CFLAGS.${COMPILER}} \
|
||||||
|
-Iinclude -Iarena-allocator/include \
|
||||||
|
-std=c2x \
|
||||||
|
-Wall -Wextra -Wpedantic -Werror
|
||||||
|
|
||||||
LD_PRELOAD:=
|
LD_PRELOAD:=
|
||||||
|
|
||||||
@@ -27,18 +30,24 @@ LD_PRELOAD:=
|
|||||||
BUILD_DIR := bin/${BUILD}
|
BUILD_DIR := bin/${BUILD}
|
||||||
|
|
||||||
OBJ_DIR := .obj/${BUILD}
|
OBJ_DIR := .obj/${BUILD}
|
||||||
_OBJS := main.o parse.o json_value.o util.o
|
_OBJS := main.o parse.o json_value.o util.o libarena.a
|
||||||
OBJS := $(patsubst %,$(OBJ_DIR)/%,$(_OBJS))
|
OBJS := $(patsubst %,$(OBJ_DIR)/%,$(_OBJS))
|
||||||
|
|
||||||
|
|
||||||
all : $(BUILD_DIR)/parse
|
all : $(BUILD_DIR)/parse
|
||||||
|
|
||||||
$(BUILD_DIR)/parse : $(OBJS) | $(BUILD_DIR) Makefile
|
$(BUILD_DIR)/parse : $(OBJS) $(OBJ_DIR)/libarena.a | $(BUILD_DIR) Makefile
|
||||||
$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDLIBS) -o $@
|
$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDLIBS) -o $@
|
||||||
|
|
||||||
$(OBJ_DIR)/main.o : src/main.c | $(OBJ_DIR) Makefile
|
$(OBJ_DIR)/main.o : src/main.c arena-allocator | $(OBJ_DIR) Makefile
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -c $< -o $@
|
$(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -c $< -o $@
|
||||||
|
|
||||||
|
$(OBJ_DIR)/libarena.a : arena-allocator .gitmodules $(wildcard .git/modules/arena-allocator/HEAD) | $(OBJ_DIR) Makefile
|
||||||
|
(cd arena-allocator && make static)
|
||||||
|
cp --force arena-allocator/lib/libarena.a $(OBJ_DIR)/libarena.a
|
||||||
|
|
||||||
|
arena-allocator : .gitmodules
|
||||||
|
git submodule update $@
|
||||||
|
|
||||||
$(OBJ_DIR)/%.o : src/%.c include/%.h | $(OBJ_DIR) Makefile
|
$(OBJ_DIR)/%.o : src/%.c include/%.h | $(OBJ_DIR) Makefile
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -c $< -o $@
|
$(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -c $< -o $@
|
||||||
|
|
||||||
|
|||||||
1
arena-allocator
Submodule
1
arena-allocator
Submodule
Submodule arena-allocator added at f8e6e2e6d2
@@ -1,4 +1,5 @@
|
|||||||
-Iinclude
|
-Iinclude
|
||||||
|
-Iarena-allocator/include
|
||||||
-Wall
|
-Wall
|
||||||
-Werror
|
-Werror
|
||||||
-Wpedantic
|
-Wpedantic
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#ifndef _JSON_VALUE_H
|
#ifndef _JSON_VALUE_H
|
||||||
#define _JSON_VALUE_H
|
#define _JSON_VALUE_H
|
||||||
|
|
||||||
|
#include "arena.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <stdbool.h> // bool
|
#include <stdbool.h> // bool
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ typedef struct obj_entry {
|
|||||||
char const* key;
|
char const* key;
|
||||||
struct json_value* val;
|
struct json_value* val;
|
||||||
struct obj_entry* next;
|
struct obj_entry* next;
|
||||||
} * __p_obj_entry;
|
}* __p_obj_entry;
|
||||||
|
|
||||||
typedef __p_obj_entry obj_t[OBJ_SIZE];
|
typedef __p_obj_entry obj_t[OBJ_SIZE];
|
||||||
|
|
||||||
@@ -32,11 +33,11 @@ struct json_value {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void* obj_at(obj_t m, char* const key);
|
void* obj_at(obj_t m, char* const key);
|
||||||
bool obj_insert(obj_t m, char* const key, struct json_value* value);
|
bool obj_insert(obj_t m, char* const key, struct json_value* value, arena_t* arena);
|
||||||
void obj_delete(obj_t* m);
|
void obj_delete(obj_t* m, arena_t* arena);
|
||||||
|
|
||||||
void print_json(struct json_value val, int indent);
|
void print_json(struct json_value val, int indent);
|
||||||
|
|
||||||
void json_value_delete(struct json_value val);
|
void json_value_delete(struct json_value val, arena_t* arena);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
#include "json_value.h"
|
#include "json_value.h"
|
||||||
|
|
||||||
|
#include "arena.h"
|
||||||
#include <stdio.h> // FILE*
|
#include <stdio.h> // FILE*
|
||||||
|
|
||||||
struct json_value parse_json_value(FILE* fp);
|
struct json_value parse_json_value(FILE* fp, arena_t* arena);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ __attribute__((__noreturn__)) void err_ctx(int exit_code, FILE* fp, const char*
|
|||||||
void* malloc_or_die(size_t size);
|
void* malloc_or_die(size_t size);
|
||||||
void* realloc_or_die(void* ptr, size_t size);
|
void* realloc_or_die(void* ptr, size_t size);
|
||||||
void* calloc_or_die(size_t nmemb, size_t size);
|
void* calloc_or_die(size_t nmemb, size_t size);
|
||||||
void print_trace();
|
void print_trace(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -23,13 +23,11 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "arena.h"
|
||||||
#include "json_value.h"
|
#include "json_value.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
bool obj_insert(obj_t m, char* const key, struct json_value* value);
|
|
||||||
size_t obj_hash(char const* str);
|
size_t obj_hash(char const* str);
|
||||||
void obj_delete(obj_t* m);
|
|
||||||
void* obj_at(obj_t m, char* const key);
|
|
||||||
|
|
||||||
void print_array(struct json_value** arr, int cur_indent, int indent_amount);
|
void print_array(struct json_value** arr, int cur_indent, int indent_amount);
|
||||||
void print_json_value(struct json_value val, int cur_indent, int indent_amount);
|
void print_json_value(struct json_value val, int cur_indent, int indent_amount);
|
||||||
@@ -95,7 +93,7 @@ void* obj_at(obj_t m, char* const key)
|
|||||||
returns true if successful
|
returns true if successful
|
||||||
returns false if key already exists
|
returns false if key already exists
|
||||||
*/
|
*/
|
||||||
bool obj_insert(obj_t m, char* const key, struct json_value* value)
|
bool obj_insert(obj_t m, char* const key, struct json_value* value, arena_t* arena)
|
||||||
{
|
{
|
||||||
size_t i = obj_hash(key);
|
size_t i = obj_hash(key);
|
||||||
struct obj_entry* cur = m[i];
|
struct obj_entry* cur = m[i];
|
||||||
@@ -119,7 +117,7 @@ bool obj_insert(obj_t m, char* const key, struct json_value* value)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* populate new entry */
|
/* populate new entry */
|
||||||
cur = malloc_or_die(sizeof(struct obj_entry));
|
cur = arena_alloc(arena, sizeof *cur);
|
||||||
cur->key = key;
|
cur->key = key;
|
||||||
cur->val = value;
|
cur->val = value;
|
||||||
cur->next = m[i];
|
cur->next = m[i];
|
||||||
@@ -133,24 +131,24 @@ bool obj_insert(obj_t m, char* const key, struct json_value* value)
|
|||||||
/*
|
/*
|
||||||
Free memory allocated for json_value val
|
Free memory allocated for json_value val
|
||||||
*/
|
*/
|
||||||
void json_value_delete(struct json_value val)
|
void json_value_delete(struct json_value val, arena_t* arena)
|
||||||
{
|
{
|
||||||
switch (val.type) {
|
switch (val.type) {
|
||||||
case array:
|
case array:
|
||||||
for (size_t i = 0; val.array[i] != NULL; i++) {
|
for (size_t i = 0; val.array[i] != NULL; i++) {
|
||||||
json_value_delete(*(val.array[i]));
|
json_value_delete(*(val.array[i]), arena);
|
||||||
free(val.array[i]);
|
arena_free(arena, val.array[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(val.array);
|
arena_free(arena, val.array);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case object:
|
case object:
|
||||||
obj_delete(val.object);
|
obj_delete(val.object, arena);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
free(val.string);
|
arena_free(arena, val.string);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -159,26 +157,27 @@ void json_value_delete(struct json_value val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Free memory allocated for obj
|
Recursively deletes object and its children
|
||||||
|
|
||||||
Recursively deletes children objects
|
Included for completeness. To efficiently delete an object, reset
|
||||||
|
its associated arena instead.
|
||||||
*/
|
*/
|
||||||
void obj_delete(obj_t* m)
|
void obj_delete(obj_t* m, arena_t* arena)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < OBJ_SIZE; i++) {
|
for (size_t i = 0; i < OBJ_SIZE; i++) {
|
||||||
struct obj_entry *e = (*m)[i], *tmp;
|
struct obj_entry *e = (*m)[i], *tmp;
|
||||||
|
|
||||||
while (e != NULL) {
|
while (e != NULL) {
|
||||||
json_value_delete(*(e->val));
|
json_value_delete(*(e->val), arena);
|
||||||
free((char*)e->key);
|
arena_free(arena, (char*)e->key);
|
||||||
free(e->val);
|
arena_free(arena, e->val);
|
||||||
|
|
||||||
tmp = e;
|
tmp = e;
|
||||||
e = e->next;
|
e = e->next;
|
||||||
free(tmp);
|
arena_free(arena, tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(m);
|
arena_free(arena, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_indent(int n)
|
void add_indent(int n)
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
arena_t arena_default = arena_new();
|
||||||
|
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
errx(EXIT_FAILURE, "Usage: %s <file>", argv[0]);
|
errx(EXIT_FAILURE, "Usage: %s <file>", argv[0]);
|
||||||
}
|
}
|
||||||
@@ -34,13 +36,13 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
FILE* fp = fopen(argv[1], "r");
|
FILE* fp = fopen(argv[1], "r");
|
||||||
|
|
||||||
struct json_value x = parse_json_value(fp);
|
struct json_value x = parse_json_value(fp, &arena_default);
|
||||||
|
|
||||||
print_json(x, 1);
|
print_json(x, 1);
|
||||||
|
|
||||||
json_value_delete(x);
|
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
|
arena_delete(&arena_default);
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
118
src/parse.c
118
src/parse.c
@@ -20,24 +20,37 @@
|
|||||||
|
|
||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
|
|
||||||
|
#include "arena.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "json_value.h"
|
#include "json_value.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#include <ctype.h> // isspace, isdigit
|
#include <ctype.h> // isspace, isdigit
|
||||||
#include <err.h> // err, errx
|
#include <err.h> // err, errx
|
||||||
|
#include <math.h> // NAN
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h> // exit, EXIT_SUCCESS, EXIT_FAILURE
|
#include <stdlib.h> // exit, EXIT_SUCCESS, EXIT_FAILURE
|
||||||
#include <string.h> // strcmp
|
#include <string.h> // strcmp
|
||||||
|
|
||||||
char* read_string(FILE* fp);
|
|
||||||
obj_t* read_object(FILE* fp);
|
|
||||||
void discard_whitespace(FILE* fp);
|
|
||||||
bool read_boolean(FILE* fp);
|
bool read_boolean(FILE* fp);
|
||||||
void read_null(FILE* fp);
|
char* read_string(FILE* fp, arena_t* arena);
|
||||||
double read_number(FILE* fp);
|
double read_number(FILE* fp);
|
||||||
struct json_value** read_array(FILE* fp);
|
obj_t* read_object(FILE* fp, arena_t* arena);
|
||||||
|
struct json_value** read_array(FILE* fp, arena_t* arena);
|
||||||
void discard_whitespace(FILE* fp);
|
void discard_whitespace(FILE* fp);
|
||||||
|
void read_null(FILE* fp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
ghetto way to hunt down bugs
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define _fgetc(fp) \
|
||||||
|
({int retval; retval = fgetc(fp); fputc(retval, stderr); retval;})
|
||||||
|
#else
|
||||||
|
#define _fgetc(fp) \
|
||||||
|
fgetc(fp)
|
||||||
|
#endif
|
||||||
|
*/
|
||||||
|
#define _fgetc(fp) fgetc(fp)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Consumes the next whitespace character in a file stream
|
Consumes the next whitespace character in a file stream
|
||||||
@@ -47,7 +60,7 @@ void discard_whitespace(FILE* fp)
|
|||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while (isspace(c = fgetc(fp))) {
|
while (isspace(c = _fgetc(fp))) {
|
||||||
if (c == EOF)
|
if (c == EOF)
|
||||||
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
||||||
}
|
}
|
||||||
@@ -64,10 +77,10 @@ void discard_whitespace(FILE* fp)
|
|||||||
|
|
||||||
These structures can be nested.
|
These structures can be nested.
|
||||||
*/
|
*/
|
||||||
struct json_value parse_json_value(FILE* fp)
|
struct json_value parse_json_value(FILE* fp, arena_t* arena)
|
||||||
{
|
{
|
||||||
discard_whitespace(fp);
|
discard_whitespace(fp);
|
||||||
int c = fgetc(fp);
|
int c = _fgetc(fp);
|
||||||
|
|
||||||
struct json_value result = { 0 };
|
struct json_value result = { 0 };
|
||||||
|
|
||||||
@@ -77,17 +90,17 @@ struct json_value parse_json_value(FILE* fp)
|
|||||||
|
|
||||||
case '{':
|
case '{':
|
||||||
result.type = object;
|
result.type = object;
|
||||||
result.object = read_object(fp);
|
result.object = read_object(fp, arena);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '"':
|
case '"':
|
||||||
result.type = string;
|
result.type = string;
|
||||||
result.string = read_string(fp);
|
result.string = read_string(fp, arena);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '[':
|
case '[':
|
||||||
result.type = array;
|
result.type = array;
|
||||||
result.array = read_array(fp);
|
result.array = read_array(fp, arena);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 't':
|
case 't':
|
||||||
@@ -127,21 +140,26 @@ struct json_value parse_json_value(FILE* fp)
|
|||||||
|
|
||||||
Consumes a JSON string from a file stream and returns a corresponding char*
|
Consumes a JSON string from a file stream and returns a corresponding char*
|
||||||
*/
|
*/
|
||||||
char* read_string(FILE* fp)
|
char* read_string(FILE* fp, arena_t* arena)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
size_t i = 0, result_size = 16;
|
size_t i = 0, result_size = 16;
|
||||||
char* result = malloc_or_die(result_size);
|
// char* result = malloc_or_die(result_size);
|
||||||
|
char* result = arena_alloc(arena, result_size);
|
||||||
|
|
||||||
bool escaped = false;
|
bool escaped = false;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (i + 1 >= result_size) {
|
if (i + 1 >= result_size) {
|
||||||
result_size *= 2;
|
result_size *= 2;
|
||||||
result = realloc_or_die(result, result_size);
|
result = arena_realloc_tail(arena, result_size);
|
||||||
|
if (result == NULL)
|
||||||
|
err_ctx(EXIT_FAILURE, fp, "could not allocate memory for string"
|
||||||
|
"%s",
|
||||||
|
__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
c = fgetc(fp);
|
c = _fgetc(fp);
|
||||||
|
|
||||||
if (escaped) {
|
if (escaped) {
|
||||||
escaped = false;
|
escaped = false;
|
||||||
@@ -155,10 +173,9 @@ char* read_string(FILE* fp)
|
|||||||
|
|
||||||
case '"':
|
case '"':
|
||||||
result[i++] = '\0';
|
result[i++] = '\0';
|
||||||
return realloc_or_die(result, i);
|
return arena_realloc_tail(arena, i);
|
||||||
|
|
||||||
case EOF:
|
case EOF:
|
||||||
free(result);
|
|
||||||
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -177,26 +194,25 @@ char* read_string(FILE* fp)
|
|||||||
|
|
||||||
Consumes a JSON object from a file stream and returns a corresponding obj_t
|
Consumes a JSON object from a file stream and returns a corresponding obj_t
|
||||||
*/
|
*/
|
||||||
obj_t* read_object(FILE* fp)
|
obj_t* read_object(FILE* fp, arena_t* arena)
|
||||||
{
|
{
|
||||||
obj_t* result = calloc_or_die(1, sizeof(obj_t));
|
// obj_t* result = calloc_or_die(1, sizeof(obj_t));
|
||||||
|
obj_t* result = arena_calloc(arena, 1, sizeof *result);
|
||||||
char* key;
|
char* key;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
/* read key */
|
/* read key */
|
||||||
discard_whitespace(fp);
|
discard_whitespace(fp);
|
||||||
|
|
||||||
switch (fgetc(fp)) {
|
switch (_fgetc(fp)) {
|
||||||
case EOF:
|
case EOF:
|
||||||
free(result);
|
|
||||||
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
free(result);
|
|
||||||
err_ctx(UNEXPECTED_CHAR, fp, "(%s) expected \"", __func__);
|
err_ctx(UNEXPECTED_CHAR, fp, "(%s) expected \"", __func__);
|
||||||
|
|
||||||
case '"':
|
case '"':
|
||||||
key = read_string(fp);
|
key = read_string(fp, arena);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '}':
|
case '}':
|
||||||
@@ -206,39 +222,35 @@ obj_t* read_object(FILE* fp)
|
|||||||
/* check for ':' separator */
|
/* check for ':' separator */
|
||||||
discard_whitespace(fp);
|
discard_whitespace(fp);
|
||||||
|
|
||||||
switch (fgetc(fp)) {
|
switch (_fgetc(fp)) {
|
||||||
case ':':
|
case ':':
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EOF:
|
case EOF:
|
||||||
free(result);
|
|
||||||
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
free(result);
|
|
||||||
err_ctx(UNEXPECTED_CHAR, fp, "(%s) expected ':'", __func__);
|
err_ctx(UNEXPECTED_CHAR, fp, "(%s) expected ':'", __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read value */
|
/* read value */
|
||||||
discard_whitespace(fp);
|
discard_whitespace(fp);
|
||||||
|
|
||||||
struct json_value* val = calloc_or_die(1, sizeof(struct json_value));
|
// struct json_value* val = calloc_or_die(1, sizeof(struct json_value));
|
||||||
*val = parse_json_value(fp);
|
struct json_value* val = arena_calloc(arena, 1, sizeof *val);
|
||||||
|
*val = parse_json_value(fp, arena);
|
||||||
|
|
||||||
/* insert key-value pair to obj */
|
/* insert key-value pair to obj */
|
||||||
if (!obj_insert(*result, key, val)) {
|
if (!obj_insert(*result, key, val, arena)) {
|
||||||
free(result);
|
fprintf(stderr, "failed to insert pair (%s, %p)\n", key, (void*)val);
|
||||||
free(val);
|
exit(EXIT_FAILURE);
|
||||||
errx(EXIT_FAILURE, "failed to insert pair (%s, %p)", key, (void*)val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read separator or end of object */
|
/* read separator or end of object */
|
||||||
discard_whitespace(fp);
|
discard_whitespace(fp);
|
||||||
|
|
||||||
switch (fgetc(fp)) {
|
switch (_fgetc(fp)) {
|
||||||
case EOF:
|
case EOF:
|
||||||
free(val);
|
|
||||||
free(result);
|
|
||||||
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
||||||
|
|
||||||
case ',':
|
case ',':
|
||||||
@@ -248,8 +260,6 @@ obj_t* read_object(FILE* fp)
|
|||||||
return result;
|
return result;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
free(val);
|
|
||||||
free(result);
|
|
||||||
err_ctx(UNEXPECTED_CHAR, fp, "(%s) expected ',' or '}'", __func__);
|
err_ctx(UNEXPECTED_CHAR, fp, "(%s) expected ',' or '}'", __func__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,42 +274,43 @@ obj_t* read_object(FILE* fp)
|
|||||||
Consumes a JSON array from a file stream and returns
|
Consumes a JSON array from a file stream and returns
|
||||||
a NULL separated array of json_value pointers
|
a NULL separated array of json_value pointers
|
||||||
*/
|
*/
|
||||||
struct json_value** read_array(FILE* fp)
|
struct json_value** read_array(FILE* fp, arena_t* arena)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
size_t i = 0, output_size = 16;
|
size_t i = 0, output_size = 16;
|
||||||
struct json_value** output = malloc_or_die(output_size * sizeof(struct json_value*));
|
struct json_value** output = malloc_or_die(output_size * sizeof *output);
|
||||||
|
// struct json_value** output = arena_alloc(arena, output_size * sizeof *output);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (i + 1 >= output_size) {
|
if (i >= output_size) {
|
||||||
output_size *= 2;
|
output_size *= 2;
|
||||||
output = realloc_or_die(output, output_size * sizeof(struct json_value*));
|
// output = arena_realloc_tail(arena, output_size * sizeof *output);
|
||||||
|
output = realloc_or_die(output, output_size * sizeof *output);
|
||||||
}
|
}
|
||||||
|
|
||||||
discard_whitespace(fp);
|
discard_whitespace(fp);
|
||||||
c = fgetc(fp);
|
c = _fgetc(fp);
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case EOF:
|
case EOF:
|
||||||
free(output);
|
|
||||||
|
|
||||||
for (size_t j = 0; j < i; j++)
|
|
||||||
free(output[j]);
|
|
||||||
|
|
||||||
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ']':
|
case ']':
|
||||||
output[i++] = NULL;
|
output[i++] = NULL;
|
||||||
return realloc_or_die(output, i * sizeof(struct json_value*));
|
struct json_value** x = arena_alloc(arena, i * sizeof *output);
|
||||||
|
memcpy(x, output, i * sizeof *output);
|
||||||
|
free(output);
|
||||||
|
return x;
|
||||||
|
|
||||||
case ',':
|
case ',':
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ungetc(c, fp);
|
ungetc(c, fp);
|
||||||
output[i] = malloc_or_die(sizeof(struct json_value));
|
// output[i] = malloc_or_die(sizeof(struct json_value));
|
||||||
*output[i] = parse_json_value(fp);
|
output[i] = arena_alloc(arena, sizeof *(output[i]));
|
||||||
|
*output[i] = parse_json_value(fp, arena);
|
||||||
i++;
|
i++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -365,15 +376,12 @@ bool read_boolean(FILE* fp)
|
|||||||
*/
|
*/
|
||||||
double read_number(FILE* fp)
|
double read_number(FILE* fp)
|
||||||
{
|
{
|
||||||
static const unsigned long neg_nan = 0xFFFFFFFFFFFFFFFFULL;
|
double n = NAN;
|
||||||
double n = *(double*)&neg_nan;
|
|
||||||
|
|
||||||
int n_read = fscanf(fp, "%lf", &n);
|
int n_read = fscanf(fp, "%lf", &n);
|
||||||
|
|
||||||
/* try to read as long instead */
|
if (n_read == 0)
|
||||||
if (n_read == 0) {
|
|
||||||
err_ctx(UNEXPECTED_CHAR, fp, "(%s) number expected, found %lf", __func__, n);
|
err_ctx(UNEXPECTED_CHAR, fp, "(%s) number expected, found %lf", __func__, n);
|
||||||
}
|
|
||||||
|
|
||||||
if (n_read == EOF)
|
if (n_read == EOF)
|
||||||
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
err_ctx(EARLY_EOF, fp, "(%s) unexpected EOF", __func__);
|
||||||
|
|||||||
14
src/util.c
14
src/util.c
@@ -42,13 +42,12 @@ void* realloc_or_die(void* ptr, size_t size)
|
|||||||
{
|
{
|
||||||
void* tmp = realloc(ptr, size);
|
void* tmp = realloc(ptr, size);
|
||||||
|
|
||||||
if (ptr == NULL) {
|
if (tmp == NULL) {
|
||||||
if (errno != 0) {
|
if (errno != 0) {
|
||||||
free(ptr);
|
|
||||||
err(errno, "realloc_or_die failed");
|
err(errno, "realloc_or_die failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "\nrealloc_or_die returned NULL but errno is 0, ptr: %p size: %zu\n", ptr, size);
|
fprintf(stderr, "\nrealloc_or_die returned NULL but errno is 0, ptr: %p size: %zu\n", tmp, size);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,9 +66,11 @@ void* calloc_or_die(size_t nmemb, size_t size)
|
|||||||
|
|
||||||
// from the glibc man pages
|
// from the glibc man pages
|
||||||
// https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
|
// https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
|
||||||
void print_trace()
|
void print_trace(void)
|
||||||
{
|
{
|
||||||
#ifdef BACKTRACE
|
#ifndef BACKTRACE
|
||||||
|
return;
|
||||||
|
#else
|
||||||
void* array[500];
|
void* array[500];
|
||||||
char** strings;
|
char** strings;
|
||||||
int size, i;
|
int size, i;
|
||||||
@@ -79,9 +80,10 @@ void print_trace()
|
|||||||
|
|
||||||
if (strings != NULL) {
|
if (strings != NULL) {
|
||||||
printf("\n\n=== Obtained %d stack frames. === \n", size);
|
printf("\n\n=== Obtained %d stack frames. === \n", size);
|
||||||
for (i = 0; i < size; i++)
|
for (i = 0; i < size; i++) {
|
||||||
printf("%s\n", strings[i]);
|
printf("%s\n", strings[i]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
free(strings);
|
free(strings);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user