mirror of
https://github.com/sigmasternchen/libargo
synced 2025-03-15 05:08:54 +00:00
first version
This commit is contained in:
parent
b4995a65ba
commit
92ef70c668
8 changed files with 1513 additions and 0 deletions
41
Makefile
Normal file
41
Makefile
Normal file
|
@ -0,0 +1,41 @@
|
|||
CC = gcc
|
||||
CFLAGS = -std=c99 -Wall -D_POSIX_C_SOURCE=201112L -D_XOPEN_SOURCE=500 -D_GNU_SOURCE -static -g
|
||||
LD = gcc
|
||||
LDFLAGS =
|
||||
AR = ar
|
||||
ARFLAGS = rcs
|
||||
|
||||
BIN_NAME = demo
|
||||
LIB_NAME = libcjson.a
|
||||
|
||||
OBJS = obj/base.o obj/parse.o obj/query.o obj/stringify.o
|
||||
DEPS = $(OBJS:%.o=%.d)
|
||||
|
||||
all: $(BIN_NAME) $(LIB_NAME) test
|
||||
|
||||
|
||||
$(BIN_NAME): obj/demo.o $(OBJS)
|
||||
$(LD) $(LDFLAGS) -o $@ $^
|
||||
|
||||
$(LIB_NAME): CFLAGS += -fPIC
|
||||
$(LIB_NAME): $(OBJS)
|
||||
$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
test: obj/test.o $(OBJS)
|
||||
$(LD) $(LDFLAGS) -o $@ $^
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
obj/%.o: src/%.c obj
|
||||
$(CC) $(CFLAGS) -MMD -c -o $@ $<
|
||||
|
||||
obj:
|
||||
@mkdir -p obj
|
||||
|
||||
clean:
|
||||
@echo "Cleaning up..."
|
||||
@rm -f obj/*.o
|
||||
@rm -f obj/*.d
|
||||
@rm -f test
|
||||
@rm -f $(BIN_NAME)
|
||||
@rm -f $(LIB_NAME)
|
237
src/base.c
Normal file
237
src/base.c
Normal file
|
@ -0,0 +1,237 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "json.h"
|
||||
|
||||
void json_free_r(jsonValue_t* value) {
|
||||
jsonArray_t array;
|
||||
jsonObject_t object;
|
||||
|
||||
switch(value->type) {
|
||||
case JSON_NULL:
|
||||
break;
|
||||
case JSON_ARRAY:
|
||||
array = value->value.array;
|
||||
for (int i = 0; i < array.size; i++) {
|
||||
json_free_r(&(array.entries[i]));
|
||||
}
|
||||
free(array.entries);
|
||||
break;
|
||||
case JSON_OBJECT:
|
||||
object = value->value.object;
|
||||
for (int i = 0; i < object.size; i++) {
|
||||
free(object.entries[i].key);
|
||||
json_free_r(&(object.entries[i].value));
|
||||
}
|
||||
free(object.entries);
|
||||
break;
|
||||
case JSON_STRING:
|
||||
free(value->value.string);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void json_free(jsonValue_t* value) {
|
||||
if (value == NULL)
|
||||
return;
|
||||
|
||||
json_free_r(value);
|
||||
free(value);
|
||||
}
|
||||
|
||||
jsonValue_t* json_value() {
|
||||
jsonValue_t* value = malloc(sizeof(jsonValue_t));
|
||||
return value;
|
||||
}
|
||||
|
||||
jsonValue_t* json_null() {
|
||||
jsonValue_t* value = json_value();
|
||||
value->type = JSON_NULL;
|
||||
return value;
|
||||
}
|
||||
|
||||
jsonValue_t* json_double(double d) {
|
||||
jsonValue_t* value = json_value();
|
||||
value->type = JSON_DOUBLE;
|
||||
value->value.real = d;
|
||||
return value;
|
||||
}
|
||||
|
||||
jsonValue_t* json_long(long l) {
|
||||
jsonValue_t* value = json_value();
|
||||
value->type = JSON_LONG;
|
||||
value->value.integer = l;
|
||||
return value;
|
||||
}
|
||||
|
||||
jsonValue_t* json_bool(bool b) {
|
||||
jsonValue_t* value = json_value();
|
||||
value->type = JSON_BOOL;
|
||||
value->value.boolean = b;
|
||||
return value;
|
||||
}
|
||||
|
||||
jsonValue_t* json_string(const char* s) {
|
||||
jsonValue_t* value = json_value();
|
||||
value->type = JSON_STRING;
|
||||
value->value.string = strdup(s);
|
||||
return value;
|
||||
}
|
||||
|
||||
jsonValue_t* json_array(bool freeAfterwards, size_t size, ...) {
|
||||
jsonValue_t* value = json_value();
|
||||
value->type = JSON_ARRAY;
|
||||
value->value.array.size = size;
|
||||
value->value.array.entries = malloc(sizeof(jsonValue_t) * size);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
jsonValue_t* entry = va_arg(ap, jsonValue_t*);
|
||||
value->value.array.entries[i] = *entry;
|
||||
if (freeAfterwards) {
|
||||
free(entry);
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return value;
|
||||
}
|
||||
jsonValue_t* json_object(bool freeAfterwards, size_t size, ...) {
|
||||
jsonValue_t* value = json_value();
|
||||
value->type = JSON_OBJECT;
|
||||
value->value.object.size = size;
|
||||
value->value.object.entries = malloc(sizeof(jsonObjectEntry_t) * size);
|
||||
|
||||
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 (freeAfterwards) {
|
||||
free(entry);
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void print_repeat(int i, char c) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
printf("%c", c);
|
||||
}
|
||||
}
|
||||
|
||||
void json_print_r(jsonValue_t* value, int indent) {
|
||||
print_repeat(indent, '\t');
|
||||
if (value == NULL) {
|
||||
printf("[invalid]\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch(value->type) {
|
||||
case JSON_ARRAY:
|
||||
printf("array:\n");
|
||||
for (size_t i = 0; i < value->value.array.size; i++) {
|
||||
json_print_r(&value->value.array.entries[i], indent + 1);
|
||||
}
|
||||
break;
|
||||
case JSON_OBJECT:
|
||||
printf("object:\n");
|
||||
for (size_t i = 0; i < value->value.object.size; i++) {
|
||||
print_repeat(indent + 1, '\t');
|
||||
printf("%s:\n", value->value.object.entries[i].key);
|
||||
json_print_r(&value->value.object.entries[i].value, indent + 2);
|
||||
}
|
||||
break;
|
||||
case JSON_DOUBLE:
|
||||
printf("number: %lf\n", value->value.real);
|
||||
break;
|
||||
case JSON_LONG:
|
||||
printf("number: %lld\n", value->value.integer);
|
||||
break;
|
||||
case JSON_STRING:
|
||||
printf("string: \"%s\"\n", value->value.string);
|
||||
break;
|
||||
case JSON_BOOL:
|
||||
printf("bool: %s\n", value->value.boolean ? "true" : "false");
|
||||
break;
|
||||
case JSON_NULL:
|
||||
printf("null\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void json_print(jsonValue_t* value) {
|
||||
json_print_r(value, 0);
|
||||
}
|
||||
|
||||
jsonValue_t json_clone_r(jsonValue_t value) {
|
||||
jsonValue_t clone = value;
|
||||
|
||||
switch(value.type) {
|
||||
case JSON_STRING:
|
||||
clone.value.string = strdup(value.value.string);
|
||||
break;
|
||||
case JSON_ARRAY:
|
||||
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);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < clone.value.array.size; i++) {
|
||||
clone.value.array.entries[i] = json_clone_r(value.value.array.entries[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
case JSON_OBJECT:
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
jsonValue_t* json_clone(jsonValue_t* value) {
|
||||
jsonValue_t* clone = malloc(sizeof(jsonValue_t));
|
||||
if (clone == NULL) {
|
||||
fprintf(stderr, "fuu\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
*clone = json_clone_r(*value);
|
||||
|
||||
return clone;
|
||||
}
|
57
src/demo.c
Normal file
57
src/demo.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "json.h"
|
||||
|
||||
char* strclone(const char* string) {
|
||||
char* clone = malloc(strlen(string) + 1);
|
||||
if (clone == NULL) {
|
||||
fprintf(stderr, "Fuck!\n");
|
||||
exit(1);
|
||||
}
|
||||
strcpy(clone, string);
|
||||
return clone;
|
||||
}
|
||||
|
||||
int main() {
|
||||
jsonValue_t* value = json_array(true, 4,
|
||||
json_string("Hello"),
|
||||
json_string("World"),
|
||||
json_null(),
|
||||
json_object(true, 3,
|
||||
"okay", json_bool(true),
|
||||
"pi", json_double(3.1415),
|
||||
"leet", json_long(1337)
|
||||
)
|
||||
);
|
||||
|
||||
json_print(value);
|
||||
|
||||
printf("\n");
|
||||
char* string = json_stringify(value);
|
||||
printf("%s\n", string);
|
||||
|
||||
free(string);
|
||||
|
||||
printf("\n\n");
|
||||
|
||||
jsonValue_t* tmp = json_query(value, ".[3].okay");
|
||||
|
||||
json_print(tmp);
|
||||
|
||||
json_free(tmp);
|
||||
json_free(value);
|
||||
|
||||
printf("\n\n");
|
||||
|
||||
value = json_parse("{ \"foo\": \"bar\", \"foobar\": [ 1337, 3.1415, null, false] }");
|
||||
|
||||
json_print(value);
|
||||
|
||||
json_free(value);
|
||||
|
||||
return 0;
|
||||
}
|
65
src/json.h
Normal file
65
src/json.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
#ifndef JSON_H
|
||||
#define JSON_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum {
|
||||
JSON_ARRAY,
|
||||
JSON_OBJECT,
|
||||
JSON_DOUBLE,
|
||||
JSON_LONG,
|
||||
JSON_STRING,
|
||||
JSON_BOOL,
|
||||
JSON_NULL,
|
||||
} jsonValueType_t;
|
||||
|
||||
typedef struct {
|
||||
size_t size;
|
||||
struct jsonObjectEntry* entries;
|
||||
} jsonObject_t;
|
||||
|
||||
typedef struct {
|
||||
size_t size;
|
||||
struct jsonValue* entries;
|
||||
} jsonArray_t;
|
||||
|
||||
typedef struct jsonValue {
|
||||
jsonValueType_t type;
|
||||
union {
|
||||
bool boolean;
|
||||
double real;
|
||||
long long integer;
|
||||
char* string;
|
||||
jsonObject_t object;
|
||||
jsonArray_t array;
|
||||
} value;
|
||||
} jsonValue_t;
|
||||
|
||||
typedef struct jsonObjectEntry {
|
||||
char* key;
|
||||
struct jsonValue value;
|
||||
} jsonObjectEntry_t;
|
||||
|
||||
void json_free(jsonValue_t* value);
|
||||
jsonValue_t* json_value();
|
||||
|
||||
jsonValue_t* json_null();
|
||||
jsonValue_t* json_double(double d);
|
||||
jsonValue_t* json_long(long l);
|
||||
jsonValue_t* json_bool(bool b);
|
||||
jsonValue_t* json_string(const char* s);
|
||||
jsonValue_t* json_array(bool freeAfterwards, size_t size, ...);
|
||||
jsonValue_t* json_object(bool freeAfterwards, size_t size, ...);
|
||||
|
||||
void json_print(jsonValue_t* value);
|
||||
|
||||
jsonValue_t* json_clone(jsonValue_t* value);
|
||||
|
||||
jsonValue_t* json_object_get(jsonValue_t* value, const char* key);
|
||||
jsonValue_t* json_array_get(jsonValue_t* value, size_t i);
|
||||
jsonValue_t* json_query(jsonValue_t* value, const char* query);
|
||||
|
||||
char* json_stringify(jsonValue_t* value);
|
||||
jsonValue_t* json_parse(const char* string);
|
||||
|
||||
#endif
|
463
src/parse.c
Normal file
463
src/parse.c
Normal file
|
@ -0,0 +1,463 @@
|
|||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "json.h"
|
||||
|
||||
extern void json_free_r(jsonValue_t* value);
|
||||
|
||||
struct parserToken {
|
||||
size_t length;
|
||||
char* token;
|
||||
};
|
||||
|
||||
#define EMPTY_PARSER_TOKEN ((struct parserToken) { .length = 0, .token = NULL})
|
||||
|
||||
#define PARSER_TOKEN_CHUNK_SIZE (1024)
|
||||
|
||||
void 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);
|
||||
}
|
||||
|
||||
token->token[token->length++] = c;
|
||||
}
|
||||
|
||||
void freeParserToken(struct parserToken* token) {
|
||||
if (token->token != NULL) {
|
||||
free(token->token);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool okay;
|
||||
const char* errorFormat;
|
||||
size_t index;
|
||||
size_t line;
|
||||
jsonValue_t value;
|
||||
} jsonParsedValue_t;
|
||||
|
||||
#define JSON_PARSER_STATE_IDLE (0)
|
||||
|
||||
#define JSON_PARSER_STATE_STRING (10)
|
||||
|
||||
#define JSON_PARSER_STATE_ARRAY (20)
|
||||
|
||||
#define JSON_PARSER_STATE_OBJECT (30)
|
||||
|
||||
#define JSON_PARSER_STATE_LONG (40)
|
||||
|
||||
#define JSON_PARSER_STATE_DOUBLE (50)
|
||||
|
||||
jsonParsedValue_t json_parse_long(jsonParsedValue_t value, struct parserToken token) {
|
||||
addToParserToken(&token, '\0');
|
||||
|
||||
char* endptr;
|
||||
long long l = strtoll(token.token, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
value.index -= strlen(token.token) - (endptr - token.token);
|
||||
value.errorFormat = "line %ld: illegal character '%c'\n";
|
||||
} else {
|
||||
value.okay = true;
|
||||
value.value.type = JSON_LONG;
|
||||
value.value.value.integer = l;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
jsonParsedValue_t json_parse_double(jsonParsedValue_t value, struct parserToken token) {
|
||||
addToParserToken(&token, '\0');
|
||||
|
||||
char* endptr;
|
||||
double d = strtod(token.token, &endptr);
|
||||
if (*endptr != '\0') {
|
||||
value.index -= strlen(token.token) - (endptr - token.token);
|
||||
value.errorFormat = "line %ld: illegal character '%c'";
|
||||
} else {
|
||||
value.okay = true;
|
||||
value.value.type = JSON_DOUBLE;
|
||||
value.value.value.real = d;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
jsonParsedValue_t json_parse_r(const char* string, size_t index, size_t line, size_t length) {
|
||||
jsonParsedValue_t value;
|
||||
value.okay = false;
|
||||
value.line = line;
|
||||
|
||||
int state = JSON_PARSER_STATE_IDLE;
|
||||
|
||||
struct parserToken token = EMPTY_PARSER_TOKEN;
|
||||
|
||||
bool escaped = false;
|
||||
bool readyForNext = true;
|
||||
char* key = NULL;
|
||||
|
||||
for (; index < length; index++) {
|
||||
char c = string[index];
|
||||
|
||||
if (c == '\n') {
|
||||
value.line++;
|
||||
}
|
||||
|
||||
if (state != JSON_PARSER_STATE_STRING && (c == ' ' || c == '\t' || c == '\n')) {
|
||||
continue;
|
||||
}
|
||||
switch(state) {
|
||||
case JSON_PARSER_STATE_IDLE:
|
||||
switch(c) {
|
||||
case '"':
|
||||
state = JSON_PARSER_STATE_STRING;
|
||||
value.value.type = JSON_STRING;
|
||||
break;
|
||||
case '[':
|
||||
state = JSON_PARSER_STATE_ARRAY;
|
||||
value.value.type = JSON_ARRAY;
|
||||
value.value.value.array.size = 0;
|
||||
value.value.value.array.entries = NULL;
|
||||
break;
|
||||
case '{':
|
||||
state = JSON_PARSER_STATE_OBJECT;
|
||||
value.value.type = JSON_OBJECT;
|
||||
value.value.value.object.size = 0;
|
||||
value.value.value.object.entries = NULL;
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-':
|
||||
addToParserToken(&token, c);
|
||||
state = JSON_PARSER_STATE_LONG;
|
||||
break;
|
||||
default:
|
||||
if (length - index >= strlen("null") && strncmp("null", string + index, strlen("null")) == 0) {
|
||||
value.okay = true;
|
||||
value.index = index + strlen("null");
|
||||
value.value.type = JSON_NULL;
|
||||
} else if (length - index >= strlen("true") && strncmp("true", string + index, strlen("true")) == 0) {
|
||||
value.okay = true;
|
||||
value.index = index + strlen("true");
|
||||
value.value.type = JSON_BOOL;
|
||||
value.value.value.boolean = true;
|
||||
} else if (length - index >= strlen("false") && strncmp("false", string + index, strlen("false")) == 0) {
|
||||
value.okay = true;
|
||||
value.index = index + strlen("false");
|
||||
value.value.type = JSON_BOOL;
|
||||
value.value.value.boolean = false;
|
||||
} else {
|
||||
value.index = index;
|
||||
value.errorFormat = "illegal character in line %d: '%c'";
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
break;
|
||||
case JSON_PARSER_STATE_STRING:
|
||||
if (!escaped && c == '"') {
|
||||
addToParserToken(&token, '\0');
|
||||
value.okay = true;
|
||||
value.index = index + 1;
|
||||
value.value.value.string = strdup(token.token);
|
||||
freeParserToken(&token);
|
||||
|
||||
return value;
|
||||
}
|
||||
if (!escaped && c == '\\') {
|
||||
escaped = true;
|
||||
break;
|
||||
} else if (escaped) {
|
||||
switch(c) {
|
||||
case 'b':
|
||||
addToParserToken(&token, '\b');
|
||||
break;
|
||||
case 'f':
|
||||
addToParserToken(&token, '\f');
|
||||
break;
|
||||
case 'n':
|
||||
addToParserToken(&token, '\n');
|
||||
break;
|
||||
case 'r':
|
||||
addToParserToken(&token, '\r');
|
||||
break;
|
||||
case 't':
|
||||
addToParserToken(&token, '\t');
|
||||
break;
|
||||
case 'u':
|
||||
value.okay = false;
|
||||
value.index = index;
|
||||
value.errorFormat = "line %ld: \\u-syntax is not supported";
|
||||
return value;
|
||||
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
addToParserToken(&token, c);
|
||||
break;
|
||||
|
||||
default:
|
||||
addToParserToken(&token, '\\');
|
||||
addToParserToken(&token, c);
|
||||
break;
|
||||
}
|
||||
escaped = false;
|
||||
} else {
|
||||
switch(c) {
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
value.okay = false;
|
||||
value.index = index;
|
||||
value.errorFormat = "line %ld: control characters are not allowed in json strings";
|
||||
return value;
|
||||
default:
|
||||
addToParserToken(&token, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JSON_PARSER_STATE_LONG:
|
||||
if (c >= '0' && c <= '9') {
|
||||
addToParserToken(&token, c);
|
||||
} else if (c == '.' || c == 'e') {
|
||||
addToParserToken(&token, c);
|
||||
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);
|
||||
} else {
|
||||
value.index = index;
|
||||
value = json_parse_double(value, token);
|
||||
freeParserToken(&token);
|
||||
return value;
|
||||
}
|
||||
break;
|
||||
case JSON_PARSER_STATE_ARRAY:
|
||||
{
|
||||
if (c == ']') {
|
||||
value.index = index + 1;
|
||||
value.okay = true;
|
||||
return value;
|
||||
}
|
||||
if (c == ',') {
|
||||
if (readyForNext) {
|
||||
json_free_r(&value.value);
|
||||
|
||||
value.errorFormat = "line %ld: unexpected '%c'";
|
||||
value.index = index;
|
||||
return value;
|
||||
}
|
||||
|
||||
readyForNext = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!readyForNext) {
|
||||
json_free_r(&value.value);
|
||||
|
||||
value.errorFormat = "line %ld: unexpected '%c'; ',' or ']' expected";
|
||||
value.index = index;
|
||||
return value;
|
||||
}
|
||||
|
||||
jsonParsedValue_t entry = json_parse_r(string, index, value.line, length);
|
||||
|
||||
if (!entry.okay) {
|
||||
json_free_r(&value.value);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
jsonValue_t* entries = realloc(value.value.value.array.entries, sizeof(jsonValue_t) * (value.value.value.array.size + 1));
|
||||
|
||||
if (entries == NULL) {
|
||||
json_free_r(&value.value);
|
||||
|
||||
value.errorFormat = "allocation for array failed";
|
||||
return value;
|
||||
}
|
||||
|
||||
entries[value.value.value.array.size++] = entry.value;
|
||||
|
||||
value.value.value.array.entries = entries;
|
||||
|
||||
index = entry.index - 1;
|
||||
|
||||
readyForNext = false;
|
||||
}
|
||||
break;
|
||||
case JSON_PARSER_STATE_OBJECT:
|
||||
{
|
||||
if (c == '}') {
|
||||
value.index = index + 1;
|
||||
value.okay = true;
|
||||
return value;
|
||||
}
|
||||
if (c == ',') {
|
||||
if (readyForNext) {
|
||||
json_free_r(&value.value);
|
||||
|
||||
value.errorFormat = "line %ld: unexpected ','";
|
||||
value.index = index;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (key != NULL) {
|
||||
json_free_r(&value.value);
|
||||
|
||||
value.errorFormat = "line %ld: unexpected ','; ':' expected";
|
||||
value.index = index;
|
||||
return value;
|
||||
}
|
||||
|
||||
readyForNext = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == ':') {
|
||||
if (readyForNext) {
|
||||
json_free_r(&value.value);
|
||||
|
||||
value.errorFormat = "line %ld: unexpected character ':'";
|
||||
value.index = index;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (key == NULL) {
|
||||
json_free_r(&value.value);
|
||||
|
||||
value.errorFormat = "line %ld: unexpected ':'; key is missing";
|
||||
value.index = index;
|
||||
return value;
|
||||
}
|
||||
|
||||
readyForNext = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!readyForNext) {
|
||||
json_free_r(&value.value);
|
||||
|
||||
value.errorFormat = "line %ld: unexpected '%c'; ',' or '}' expected";
|
||||
value.index = index;
|
||||
return value;
|
||||
}
|
||||
|
||||
jsonParsedValue_t entry = json_parse_r(string, index, value.line, length);
|
||||
|
||||
if (!entry.okay) {
|
||||
json_free_r(&value.value);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
if (key == NULL) {
|
||||
if (entry.value.type != JSON_STRING) {
|
||||
json_free_r(&value.value);
|
||||
json_free_r(&entry.value);
|
||||
|
||||
|
||||
value.errorFormat = "line %ld: key is missing";
|
||||
value.index = index;
|
||||
return value;
|
||||
}
|
||||
|
||||
key = entry.value.value.string;
|
||||
} else {
|
||||
|
||||
jsonObjectEntry_t* entries = realloc(value.value.value.array.entries, sizeof(jsonObjectEntry_t) * (value.value.value.object.size + 1));
|
||||
|
||||
if (entries == NULL) {
|
||||
json_free_r(&value.value);
|
||||
|
||||
value.errorFormat = "allocation for object failed";
|
||||
return value;
|
||||
}
|
||||
|
||||
entries[value.value.value.object.size].key = key;
|
||||
entries[value.value.value.object.size++].value = entry.value;
|
||||
|
||||
value.value.value.object.entries = entries;
|
||||
|
||||
key = NULL;
|
||||
|
||||
}
|
||||
|
||||
index = entry.index - 1;
|
||||
|
||||
readyForNext = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
value.index = index;
|
||||
value.errorFormat = "illegal state in line %ld";
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
jsonValue_t* json_parse(const char* string) {
|
||||
size_t length = strlen(string);
|
||||
|
||||
jsonParsedValue_t parsedValue = json_parse_r(string, 0, 1, length);
|
||||
|
||||
if (!parsedValue.okay) {
|
||||
printf("%ld\n", parsedValue.index);
|
||||
printf(parsedValue.errorFormat, parsedValue.line, string[parsedValue.index]);
|
||||
printf("\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (length != parsedValue.index) {
|
||||
printf("unexptected character '%c' in line %ld\n", string[parsedValue.index], parsedValue.line);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jsonValue_t* value = malloc(sizeof(jsonValue_t));
|
||||
*value = parsedValue.value;
|
||||
|
||||
return value;
|
||||
}
|
118
src/query.c
Normal file
118
src/query.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "json.h"
|
||||
|
||||
jsonValue_t* json_object_get(jsonValue_t* value, const char* key) {
|
||||
if (value->type != JSON_OBJECT)
|
||||
return NULL;
|
||||
|
||||
for (size_t i = 0; i < value->value.object.size; i++) {
|
||||
if (strcmp(value->value.object.entries[i].key, key) == 0) {
|
||||
return json_clone(&value->value.object.entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
return json_null();
|
||||
}
|
||||
|
||||
jsonValue_t* json_array_get(jsonValue_t* value, size_t i) {
|
||||
if (value->type != JSON_ARRAY)
|
||||
return NULL;
|
||||
|
||||
if (value->value.array.size <= i) {
|
||||
return json_null();
|
||||
}
|
||||
|
||||
return json_clone(&value->value.array.entries[i]);
|
||||
}
|
||||
|
||||
jsonValue_t* json_query(jsonValue_t* value, const char* query) {
|
||||
#define JSON_QUERY_BUFFER_SIZE (1024)
|
||||
|
||||
char buffer[JSON_QUERY_BUFFER_SIZE];
|
||||
|
||||
value = json_clone(value);
|
||||
|
||||
while(true) {
|
||||
if (query[0] == '\0')
|
||||
break;
|
||||
|
||||
if (query[0] != '.') {
|
||||
json_free(value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t length;
|
||||
for (length = 1; query[length] != '\0' && query[length] != '.'; length++);
|
||||
|
||||
if (length >= JSON_QUERY_BUFFER_SIZE) {
|
||||
json_free(value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(buffer, query, length);
|
||||
buffer[length] = '\0';
|
||||
|
||||
query = query + length;
|
||||
|
||||
if (strcmp(buffer, ".") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char* _buffer = buffer + 1;
|
||||
length--;
|
||||
|
||||
switch(value->type) {
|
||||
case JSON_ARRAY:
|
||||
if (_buffer[0] != '[' || _buffer[length - 1] != ']') {
|
||||
json_free(value);
|
||||
return NULL;
|
||||
}
|
||||
_buffer[length - 1] = '\0';
|
||||
_buffer = _buffer + 1;
|
||||
|
||||
char* endptr;
|
||||
long long index = strtoll(_buffer, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
json_free(value);
|
||||
return NULL;
|
||||
}
|
||||
if (index < 0) {
|
||||
json_free(value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jsonValue_t* arrayEntry = json_array_get(value, index);
|
||||
json_free(value);
|
||||
value = arrayEntry;
|
||||
|
||||
break;
|
||||
case JSON_OBJECT:
|
||||
if (_buffer[0] == '"') {
|
||||
if (_buffer[length - 1] != '"') {
|
||||
json_free(value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_buffer[length - 1] = '\0';
|
||||
_buffer = _buffer + 1;
|
||||
}
|
||||
|
||||
jsonValue_t* objectEntry = json_object_get(value, _buffer);
|
||||
json_free(value);
|
||||
value = objectEntry;
|
||||
|
||||
break;
|
||||
default:
|
||||
json_free(value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (value == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
170
src/stringify.c
Normal file
170
src/stringify.c
Normal file
|
@ -0,0 +1,170 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "json.h"
|
||||
|
||||
size_t string_escaped_length(const char* string) {
|
||||
size_t length = 0;
|
||||
char c;
|
||||
size_t i;
|
||||
for (i = 0, c = string[i]; c != '\0'; c = string[++i]) {
|
||||
switch(c) {
|
||||
case '\\':
|
||||
case '"':
|
||||
case '/':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
length += 2;
|
||||
break;
|
||||
default:
|
||||
length += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
size_t json_length(jsonValue_t* value) {
|
||||
size_t result = 0;
|
||||
switch (value->type) {
|
||||
case JSON_NULL:
|
||||
return 4;
|
||||
case JSON_STRING:
|
||||
return 2 + string_escaped_length(value->value.string);
|
||||
case JSON_DOUBLE:
|
||||
return snprintf(NULL, 0, "%lf", value->value.real);
|
||||
case JSON_LONG:
|
||||
return snprintf(NULL, 0, "%lld", value->value.integer);
|
||||
case JSON_BOOL:
|
||||
return 5;
|
||||
case JSON_ARRAY:
|
||||
result += 2;
|
||||
for (size_t i = 0; i < value->value.array.size; i++) {
|
||||
result += json_length(&(value->value.array.entries[i])) + 1;
|
||||
}
|
||||
return result;
|
||||
case JSON_OBJECT:
|
||||
result += 2;
|
||||
for (size_t i = 0; i < value->value.object.size; i++) {
|
||||
result += strlen(value->value.object.entries[i].key) + 2 + 1;
|
||||
result += json_length(&(value->value.object.entries[i].value)) + 1;
|
||||
}
|
||||
return result;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t json_write_string(char* target, size_t maxSize, const char* source) {
|
||||
|
||||
#define JSON_WRITE_STRING_ADD(c) target[size++] = c; if (size == maxSize) return size;
|
||||
|
||||
size_t size = 0;
|
||||
|
||||
JSON_WRITE_STRING_ADD('"');
|
||||
|
||||
size_t i;
|
||||
char c;
|
||||
for (i = 0, c = source[i]; c != '\0'; c = source[++i]) {
|
||||
switch(c) {
|
||||
case '\\':
|
||||
JSON_WRITE_STRING_ADD('\\');
|
||||
JSON_WRITE_STRING_ADD('\\');
|
||||
break;
|
||||
case '"':
|
||||
JSON_WRITE_STRING_ADD('\\');
|
||||
JSON_WRITE_STRING_ADD('"');
|
||||
break;
|
||||
case '/':
|
||||
JSON_WRITE_STRING_ADD('\\');
|
||||
JSON_WRITE_STRING_ADD('/');
|
||||
break;
|
||||
case '\b':
|
||||
JSON_WRITE_STRING_ADD('\\');
|
||||
JSON_WRITE_STRING_ADD('b');
|
||||
break;
|
||||
case '\f':
|
||||
JSON_WRITE_STRING_ADD('\\');
|
||||
JSON_WRITE_STRING_ADD('f');
|
||||
break;
|
||||
case '\n':
|
||||
JSON_WRITE_STRING_ADD('\\');
|
||||
JSON_WRITE_STRING_ADD('n');
|
||||
break;
|
||||
case '\r':
|
||||
JSON_WRITE_STRING_ADD('\\');
|
||||
JSON_WRITE_STRING_ADD('r');
|
||||
break;
|
||||
case '\t':
|
||||
JSON_WRITE_STRING_ADD('\\');
|
||||
JSON_WRITE_STRING_ADD('t');
|
||||
break;
|
||||
default:
|
||||
JSON_WRITE_STRING_ADD(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JSON_WRITE_STRING_ADD('"');
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t json_stringify_r(char* string, size_t index, size_t totalSize, jsonValue_t* value) {
|
||||
switch(value->type) {
|
||||
case JSON_NULL:
|
||||
return snprintf(string + index, totalSize - index, "null") + index;
|
||||
case JSON_STRING:
|
||||
return json_write_string(string + index, totalSize - index, value->value.string) + index;
|
||||
case JSON_DOUBLE:
|
||||
return snprintf(string + index, totalSize - index, "%lf", value->value.real) + index;
|
||||
case JSON_LONG:
|
||||
return snprintf(string + index, totalSize - index, "%lld", value->value.integer) + index;
|
||||
case JSON_BOOL:
|
||||
return snprintf(string + index, totalSize - index, "%s", value->value.boolean ? "true" : "false") + index;
|
||||
case JSON_ARRAY:
|
||||
index += snprintf(string + index, totalSize - index, "[");
|
||||
|
||||
for (size_t i = 0; i < value->value.array.size; i++) {
|
||||
index = json_stringify_r(string, index, totalSize, &(value->value.array.entries[i]));
|
||||
|
||||
index += snprintf(string + index, totalSize - index, ",");
|
||||
}
|
||||
index--;
|
||||
|
||||
index += snprintf(string + index, totalSize - index, "]");
|
||||
return index;
|
||||
case JSON_OBJECT:
|
||||
index += snprintf(string + index, totalSize - index, "{");
|
||||
|
||||
for (size_t i = 0; i < value->value.object.size; i++) {
|
||||
index += json_write_string(string + index, totalSize - index, value->value.object.entries[i].key);
|
||||
index += snprintf(string + index, totalSize - index, ":");
|
||||
index = json_stringify_r(string, index, totalSize, &(value->value.object.entries[i].value));
|
||||
|
||||
index += snprintf(string + index, totalSize - index, ",");
|
||||
}
|
||||
index--; // replace last , with }
|
||||
index += snprintf(string + index, totalSize - index, "}");
|
||||
return index;
|
||||
default:
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
char* json_stringify(jsonValue_t* value) {
|
||||
size_t size = json_length(value) + 1;
|
||||
|
||||
char* string = malloc(size);
|
||||
if (string == NULL)
|
||||
return NULL;
|
||||
|
||||
json_stringify_r(string, 0, size, value);
|
||||
|
||||
return string;
|
||||
}
|
362
src/test.c
Normal file
362
src/test.c
Normal file
|
@ -0,0 +1,362 @@
|
|||
#include <stdio.h>
|
||||
#include <alloca.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "json.h"
|
||||
|
||||
|
||||
bool global = true;
|
||||
bool overall = true;
|
||||
|
||||
void checkBool(bool ok, const char* check) {
|
||||
const char* result;
|
||||
if (ok) {
|
||||
result = "[ OK ]";
|
||||
} else {
|
||||
result = "[FAILED]";
|
||||
global = false;
|
||||
}
|
||||
|
||||
printf("%s:%*s%s\n", check, (int) (30 - strlen(check)), "", result);
|
||||
}
|
||||
void checkInt(long long value, long long compare, const char* check) {
|
||||
checkBool(value == compare, check);
|
||||
}
|
||||
void checkDouble(double value, double compare, const char* check) {
|
||||
checkBool(value == compare, check);
|
||||
}
|
||||
void checkString(const char* value, const char* compare, const char* check) {
|
||||
checkBool(strcmp(value, compare) == 0, check);
|
||||
}
|
||||
void checkVoid(const void* value, const void* compare, const char* check) {
|
||||
checkBool(value == compare, check);
|
||||
}
|
||||
void checkNull(void* value, const char* check) {
|
||||
checkBool(value != NULL, check);
|
||||
}
|
||||
|
||||
void showError() {
|
||||
fprintf(stderr, "Error: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
/*bool hasData(int fd) {
|
||||
int tmp = poll(&(struct pollfd){ .fd = fd, .events = POLLIN }, 1, 10);
|
||||
|
||||
return tmp == 1;
|
||||
}*/
|
||||
|
||||
void test(const char* name, void (*testFunction)()) {
|
||||
printf("%s\n", name);
|
||||
printf("%.*s\n", (int) strlen(name),
|
||||
"===================================");
|
||||
testFunction();
|
||||
if (!global)
|
||||
overall = false;
|
||||
printf("%s: %s\n\n", name, global ? "OK" : "FAILED");
|
||||
global = true;
|
||||
}
|
||||
|
||||
void header(const char* text) {
|
||||
printf("\n");
|
||||
printf("=======================================\n");
|
||||
printf("== %s\n", text);
|
||||
printf("=======================================\n");
|
||||
}
|
||||
|
||||
void testDouble() {
|
||||
double d;
|
||||
d = 3.1415926;
|
||||
|
||||
jsonValue_t* v = json_double(d);
|
||||
|
||||
checkNull(v, "result is not null");
|
||||
checkInt(v->type, JSON_DOUBLE, "type is correct");
|
||||
checkDouble(v->value.real, d, "value is correct");
|
||||
|
||||
char* string = json_stringify(v);
|
||||
|
||||
size_t size = snprintf(NULL, 0, "%lf", d);
|
||||
char* compare = alloca(size + 1);
|
||||
sprintf(compare, "%lf", d);
|
||||
|
||||
checkString(string, compare, "stringify");
|
||||
|
||||
free(string);
|
||||
json_free(v);
|
||||
}
|
||||
|
||||
void testLong() {
|
||||
long long l;
|
||||
l = 1337;
|
||||
|
||||
jsonValue_t* v = json_long(l);
|
||||
|
||||
checkNull(v, "result is not null");
|
||||
checkInt(v->type, JSON_LONG, "type is correct");
|
||||
checkInt(v->value.integer, l, "value is correct");
|
||||
|
||||
char* string = json_stringify(v);
|
||||
|
||||
size_t size = snprintf(NULL, 0, "%lld", l);
|
||||
char* compare = alloca(size + 1);
|
||||
sprintf(compare, "%lld", l);
|
||||
|
||||
checkString(string, compare, "stringify");
|
||||
|
||||
free(string);
|
||||
json_free(v);
|
||||
}
|
||||
|
||||
void testBool() {
|
||||
bool b;
|
||||
b = true;
|
||||
|
||||
jsonValue_t* v = json_bool(b);
|
||||
|
||||
checkNull(v, "result is not null");
|
||||
checkInt(v->type, JSON_BOOL, "type is correct");
|
||||
checkBool(v->value.boolean, "value is correct");
|
||||
|
||||
char* string = json_stringify(v);
|
||||
char* compare = "true";
|
||||
|
||||
checkString(string, compare, "stringify");
|
||||
|
||||
free(string);
|
||||
json_free(v);
|
||||
|
||||
b = false;
|
||||
|
||||
v = json_bool(b);
|
||||
|
||||
checkNull(v, "result is not null");
|
||||
checkInt(v->type, JSON_BOOL, "type is correct");
|
||||
checkBool(!v->value.boolean, "value is correct");
|
||||
|
||||
string = json_stringify(v);
|
||||
compare = "false";
|
||||
|
||||
checkString(string, compare, "stringify");
|
||||
|
||||
free(string);
|
||||
json_free(v);
|
||||
}
|
||||
|
||||
void testString() {
|
||||
const char* s = "foobar";
|
||||
|
||||
jsonValue_t* v = json_string(s);
|
||||
|
||||
checkNull(v, "result is not null");
|
||||
checkInt(v->type, JSON_STRING, "type is correct");
|
||||
|
||||
char* string = json_stringify(v);
|
||||
|
||||
char* compare = alloca(strlen(s) + 2 + 1);
|
||||
sprintf(compare, "\"%s\"", s);
|
||||
|
||||
checkString(string, compare, "stringify");
|
||||
|
||||
free(string);
|
||||
json_free(v);
|
||||
}
|
||||
|
||||
void testNull() {
|
||||
jsonValue_t* v = json_null();
|
||||
|
||||
checkNull(v, "result is not null");
|
||||
checkInt(v->type, JSON_NULL, "type is correct");
|
||||
|
||||
char* string = json_stringify(v);
|
||||
|
||||
char* compare = "null";
|
||||
|
||||
checkString(string, compare, "stringify");
|
||||
|
||||
free(string);
|
||||
json_free(v);
|
||||
}
|
||||
|
||||
void testArray() {
|
||||
jsonValue_t* value = json_array(true, 4,
|
||||
json_string("Hello"),
|
||||
json_string("World"),
|
||||
json_null(),
|
||||
json_object(true, 3,
|
||||
"okay", json_bool(true),
|
||||
"pi", json_double(3.1415),
|
||||
"leet", json_long(1337)
|
||||
)
|
||||
);
|
||||
|
||||
checkNull(value, "result is not null");
|
||||
checkInt(value->type, JSON_ARRAY, "type is correct");
|
||||
|
||||
checkInt(value->value.array.size, 4, "array length is correct");
|
||||
checkInt(value->value.array.entries[0].type, JSON_STRING, "[0] type is correct");
|
||||
checkInt(value->value.array.entries[1].type, JSON_STRING, "[1] type is correct");
|
||||
checkInt(value->value.array.entries[2].type, JSON_NULL, "[2] type is correct");
|
||||
checkInt(value->value.array.entries[3].type, JSON_OBJECT, "[3] type is correct");
|
||||
|
||||
char* string = json_stringify(value);
|
||||
// the two zeros are technically not wrong but kinda hard to remove => let's keep them
|
||||
char* compare = "[\"Hello\",\"World\",null,{\"okay\":true,\"pi\":3.141500,\"leet\":1337}]";
|
||||
|
||||
checkString(string, compare, "stringify");
|
||||
|
||||
free(string);
|
||||
json_free(value);
|
||||
}
|
||||
|
||||
void testObject() {
|
||||
jsonValue_t* value = json_object(true, 3,
|
||||
"foo", json_string("bar"),
|
||||
"number", json_long(42),
|
||||
"list", json_array(true, 3,
|
||||
json_bool(true),
|
||||
json_double(3.1415),
|
||||
json_null()
|
||||
)
|
||||
);
|
||||
|
||||
checkNull(value, "result is not null");
|
||||
checkInt(value->type, JSON_OBJECT, "type is correct");
|
||||
|
||||
checkInt(value->value.object.size, 3, "object length is correct");
|
||||
|
||||
checkString(value->value.object.entries[0].key, "foo", "[0] key is correct");
|
||||
checkString(value->value.object.entries[1].key, "number", "[1] key is correct");
|
||||
checkString(value->value.object.entries[2].key, "list", "[2] key is correct");
|
||||
|
||||
checkInt(value->value.object.entries[0].value.type, JSON_STRING, "[0] type is correct");
|
||||
checkInt(value->value.object.entries[1].value.type, JSON_LONG, "[1] type is correct");
|
||||
checkInt(value->value.object.entries[2].value.type, JSON_ARRAY, "[2] type is correct");
|
||||
|
||||
char* string = json_stringify(value);
|
||||
char* compare = "{\"foo\":\"bar\",\"number\":42,\"list\":[true,3.141500,null]}";
|
||||
|
||||
checkString(string, compare, "stringify");
|
||||
|
||||
free(string);
|
||||
json_free(value);
|
||||
}
|
||||
|
||||
void testParse() {
|
||||
jsonValue_t* value = json_parse("{ \"foo\": \"bar\", \"foobar\": [ 1337, 3.1415, null, false] }");
|
||||
|
||||
checkNull(value, "result is not null");
|
||||
checkInt(value->type, JSON_OBJECT, "type is correct");
|
||||
checkInt(value->value.object.size, 2, "object length is correct");
|
||||
|
||||
checkString(value->value.object.entries[0].key, "foo", "[0] key is correct");
|
||||
checkString(value->value.object.entries[1].key, "foobar", "[1] key is correct");
|
||||
|
||||
checkInt(value->value.object.entries[0].value.type, JSON_STRING, "[0] type is correct");
|
||||
checkInt(value->value.object.entries[1].value.type, JSON_ARRAY, "[1] type is correct");
|
||||
|
||||
checkString(value->value.object.entries[0].value.value.string, "bar", "[0] value is correct");
|
||||
|
||||
checkInt(value->value.object.entries[1].value.value.array.size, 4, "[0] array length is correct");
|
||||
|
||||
checkInt(value->value.object.entries[1].value.value.array.entries[0].type, JSON_LONG, "[0] type is correct");
|
||||
checkInt(value->value.object.entries[1].value.value.array.entries[1].type, JSON_DOUBLE, "[1] type is correct");
|
||||
checkInt(value->value.object.entries[1].value.value.array.entries[2].type, JSON_NULL, "[2] type is correct");
|
||||
checkInt(value->value.object.entries[1].value.value.array.entries[3].type, JSON_BOOL, "[3] type is correct");
|
||||
|
||||
checkInt(value->value.object.entries[1].value.value.array.entries[0].value.integer, 1337, "[0][0] value is correct");
|
||||
checkDouble(value->value.object.entries[1].value.value.array.entries[1].value.real, 3.1415, "[0][1] value is correct");
|
||||
checkBool(!value->value.object.entries[1].value.value.array.entries[3].value.boolean, "[0][3] value is correct");
|
||||
|
||||
json_free(value);
|
||||
}
|
||||
|
||||
void testQuery() {
|
||||
jsonValue_t* value = json_array(true, 4,
|
||||
json_string("Hello"),
|
||||
json_string("World"),
|
||||
json_null(),
|
||||
json_object(true, 3,
|
||||
"okay", json_bool(true),
|
||||
"pi", json_double(3.1415),
|
||||
"leet", json_long(1337)
|
||||
)
|
||||
);
|
||||
|
||||
jsonValue_t* tmp;
|
||||
|
||||
tmp = json_query(value, ".[0]");
|
||||
checkNull(tmp, "in array, not null");
|
||||
checkInt(tmp->type, JSON_STRING, "in array, type");
|
||||
checkString(tmp->value.string, "Hello", "in array, value");
|
||||
json_free(tmp);
|
||||
|
||||
tmp = json_query(value, ".[3].okay");
|
||||
checkNull(tmp, "in obj in array, not null");
|
||||
checkInt(tmp->type, JSON_BOOL, "in obj in array, type");
|
||||
checkBool(tmp->value.boolean, "in obj in array, value");
|
||||
json_free(tmp);
|
||||
|
||||
tmp = json_query(value, ".[4]");
|
||||
checkNull(tmp, "not in array, not null");
|
||||
checkInt(tmp->type, JSON_NULL, "not in array, type");
|
||||
|
||||
json_free(tmp);
|
||||
|
||||
tmp = json_query(value, ".[3].foobar");
|
||||
checkNull(tmp, "not in obj in array, not null");
|
||||
checkInt(tmp->type, JSON_NULL, "not in obj in array, type");
|
||||
|
||||
json_free(tmp);
|
||||
|
||||
json_free(value);
|
||||
}
|
||||
|
||||
void testClone() {
|
||||
jsonValue_t* value = json_array(true, 4,
|
||||
json_string("Hello"),
|
||||
json_string("World"),
|
||||
json_null(),
|
||||
json_object(true, 3,
|
||||
"okay", json_bool(true),
|
||||
"pi", json_double(3.1415),
|
||||
"leet", json_long(1337)
|
||||
)
|
||||
);
|
||||
|
||||
jsonValue_t* cloned = json_clone(value);
|
||||
|
||||
checkNull(cloned, "clone not null");
|
||||
checkBool(((long long) value) != ((long long) cloned), "different addr");
|
||||
|
||||
jsonValue_t* valueMember = &(value->value.array.entries[3]);
|
||||
jsonValue_t* cloneMember = &(cloned->value.array.entries[3]);
|
||||
|
||||
checkNull(cloneMember, "member clone not null");
|
||||
checkBool(((long long) valueMember) != ((long long) cloneMember), "member different addr");
|
||||
|
||||
json_free(cloned);
|
||||
json_free(value);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
header("Types");
|
||||
test("double", &testDouble);
|
||||
test("long", &testLong);
|
||||
test("bool", &testBool);
|
||||
test("string", &testString);
|
||||
test("null", &testNull);
|
||||
test("array", &testArray);
|
||||
test("array", &testObject);
|
||||
|
||||
header("Functionality");
|
||||
test("parse", &testParse);
|
||||
test("query", &testQuery);
|
||||
test("clone", &testClone);
|
||||
|
||||
|
||||
|
||||
printf("\nOverall: %s\n", overall ? "OK" : "FAILED");
|
||||
}
|
Loading…
Reference in a new issue