1573 lines
47 KiB
HolyC
1573 lines
47 KiB
HolyC
#define JSON_SAME -1
|
|
#define JSON_UNDEFINED 0
|
|
#define JSON_OBJECT 1
|
|
#define JSON_ARRAY 2
|
|
#define JSON_STRING 3
|
|
#define JSON_NUMBER 4
|
|
#define JSON_BOOLEAN 5
|
|
#define JSON_NULL 6
|
|
#define JSON_HTML 7
|
|
|
|
#define JSON_ELEMENT_IS_KEY 1
|
|
#define JSON_ELEMENT_IS_ITEM 2
|
|
|
|
#define JSON_STATE_OBJECT_OR_ARRAY 0
|
|
|
|
#define JSON_STATE_OBJECT 100
|
|
#define JSON_STATE_OBJECT_KEY 101
|
|
#define JSON_STATE_OBJECT_SEPARATOR 102
|
|
#define JSON_STATE_OBJECT_TYPE 103
|
|
#define JSON_STATE_OBJECT_NEXT 104
|
|
|
|
#define JSON_STATE_OBJECT_OBJECT 105
|
|
#define JSON_STATE_OBJECT_ARRAY 106
|
|
#define JSON_STATE_OBJECT_STRING 107
|
|
#define JSON_STATE_OBJECT_NUMBER 108
|
|
#define JSON_STATE_OBJECT_BOOLEAN 109
|
|
#define JSON_STATE_OBJECT_NULL 110
|
|
|
|
#define JSON_STATE_ARRAY 200
|
|
#define JSON_STATE_ARRAY_TYPE 201
|
|
#define JSON_STATE_ARRAY_NEXT 202
|
|
|
|
#define JSON_STATE_ARRAY_OBJECT 203
|
|
#define JSON_STATE_ARRAY_ARRAY 204
|
|
#define JSON_STATE_ARRAY_STRING 205
|
|
#define JSON_STATE_ARRAY_NUMBER 206
|
|
#define JSON_STATE_ARRAY_BOOLEAN 207
|
|
#define JSON_STATE_ARRAY_NULL 208
|
|
|
|
#define JSON_PARSER_FIFO_SIZE 32768
|
|
|
|
#define JSON_WRAPPER_MAGIC_NUMBER 0xDEADC0DEDEADC0DE
|
|
|
|
#define JSON_SIG 0xFABACEAE
|
|
|
|
class @json_stringify_string
|
|
{
|
|
I64 length;
|
|
U8* value;
|
|
I64 capacity;
|
|
CTask* mem_task;
|
|
}
|
|
|
|
class @json_element
|
|
{
|
|
U32 sig;
|
|
@json_element* prev;
|
|
@json_element* next;
|
|
I64 type;
|
|
};
|
|
|
|
class @json_key : @json_element
|
|
{
|
|
U8* name;
|
|
U64 value;
|
|
};
|
|
|
|
class @json_item : @json_element { U64 value; };
|
|
|
|
class @json_object : @json_element
|
|
{
|
|
I64 length;
|
|
@json_key* keys;
|
|
CTask* mem_task;
|
|
};
|
|
|
|
class @json_array : @json_element
|
|
{
|
|
I64 length;
|
|
@json_item* items;
|
|
@json_item* last_item;
|
|
CTask* mem_task;
|
|
};
|
|
|
|
extern class @json_callable_object;
|
|
|
|
class @json_callable_array : @json_array
|
|
{
|
|
U64 (*@)(I64 index, Bool return_item = FALSE);
|
|
@json_callable_array* (*a)(I64 index, Bool return_item = FALSE);
|
|
@json_callable_object* (*o)(I64 index, Bool return_item = FALSE);
|
|
U0 (*append)(U64 value, I64 type = NULL);
|
|
Bool (*contains)(U64 value, I64 type = NULL, Bool match_case = FALSE);
|
|
U0 (*insert)(I64 index, U64 value, I64 type = NULL);
|
|
U0 (*prepend)(U64 value, I64 type = NULL);
|
|
U0 (*remove)(I64 index);
|
|
};
|
|
|
|
class @json_callable_object : @json_object
|
|
{
|
|
U64 (*@)(U8* key, Bool return_key = FALSE);
|
|
@json_callable_array* (*a)(U8* key, Bool return_key = FALSE);
|
|
@json_callable_object* (*o)(U8* key, Bool return_key = FALSE);
|
|
U0 (*set)(U8* key, U64 value, I64 type = JSON_SAME);
|
|
U0 (*unset)(U8* key);
|
|
};
|
|
|
|
class @json_parser
|
|
{
|
|
U8* stream;
|
|
U8 token;
|
|
CFifoU8* consumed;
|
|
I64 pos;
|
|
I64 state;
|
|
Bool debug;
|
|
CTask* mem_task;
|
|
};
|
|
|
|
#define JsonArray @json_callable_array
|
|
#define JsonElement @json_element
|
|
#define JsonItem @json_item
|
|
#define JsonKey @json_key
|
|
#define JsonObject @json_callable_object
|
|
|
|
U0 @json_debug_parser_state(@json_parser* parser)
|
|
{
|
|
switch (parser->state) {
|
|
case JSON_STATE_OBJECT:
|
|
"JSON_STATE_OBJECT\n";
|
|
break;
|
|
case JSON_STATE_OBJECT_KEY:
|
|
"JSON_STATE_OBJECT_KEY\n";
|
|
break;
|
|
case JSON_STATE_OBJECT_SEPARATOR:
|
|
"JSON_STATE_OBJECT_SEPARATOR\n";
|
|
break;
|
|
case JSON_STATE_OBJECT_TYPE:
|
|
"JSON_STATE_OBJECT_TYPE\n";
|
|
break;
|
|
case JSON_STATE_OBJECT_NEXT:
|
|
"JSON_STATE_OBJECT_NEXT\n";
|
|
break;
|
|
case JSON_STATE_OBJECT_STRING:
|
|
"JSON_STATE_OBJECT_STRING\n";
|
|
break;
|
|
case JSON_STATE_OBJECT_NUMBER:
|
|
"JSON_STATE_OBJECT_NUMBER\n";
|
|
break;
|
|
case JSON_STATE_ARRAY:
|
|
"JSON_STATE_ARRAY\n";
|
|
break;
|
|
case JSON_STATE_ARRAY_TYPE:
|
|
"JSON_STATE_ARRAY_TYPE\n";
|
|
break;
|
|
case JSON_STATE_ARRAY_NEXT:
|
|
"JSON_STATE_ARRAY_NEXT\n";
|
|
break;
|
|
case JSON_STATE_ARRAY_STRING:
|
|
"JSON_STATE_ARRAY_STRING\n";
|
|
break;
|
|
case JSON_STATE_ARRAY_NUMBER:
|
|
"JSON_STATE_ARRAY_NUMBER\n";
|
|
break;
|
|
}
|
|
}
|
|
|
|
@json_item* @json_create_item(U64 value, I64 type = NULL, CTask* mem_task)
|
|
{
|
|
@json_item* item = CAlloc(sizeof(@json_item), mem_task);
|
|
item->sig = JSON_SIG;
|
|
item->type = type;
|
|
if (!item->type) {
|
|
if (value(@json_element*)->sig == JSON_SIG) {
|
|
item->type = value(@json_element*)->type;
|
|
} else if (value > 0x1000) {
|
|
item->type = JSON_STRING;
|
|
} else {
|
|
item->type = JSON_BOOLEAN;
|
|
value = value != 0;
|
|
}
|
|
}
|
|
if (item->type == JSON_STRING)
|
|
item->value = StrNew(value, mem_task);
|
|
else
|
|
item->value = value;
|
|
return item;
|
|
}
|
|
|
|
U0 @json_append_item(@json_array* arr, U64 value, I64 type = NULL)
|
|
{
|
|
if (!arr)
|
|
return;
|
|
if (arr->type != JSON_ARRAY)
|
|
return;
|
|
@json_item* append_item = @json_create_item(value, type, arr->mem_task);
|
|
@json_item* item = arr->last_item;
|
|
if (!item) {
|
|
append_item->prev = NULL;
|
|
append_item->next = NULL;
|
|
arr->items = append_item;
|
|
arr->last_item = append_item;
|
|
arr->length++;
|
|
return;
|
|
}
|
|
item->next = append_item;
|
|
append_item->prev = item;
|
|
arr->last_item = append_item;
|
|
arr->length++;
|
|
}
|
|
|
|
U8* @json_string_from_fifo(CFifoU8* f, CTask* mem_task)
|
|
{
|
|
U8 ch;
|
|
I64 i = 0;
|
|
U8* str = CAlloc(FifoU8Cnt(f) + 1, mem_task);
|
|
while (FifoU8Cnt(f)) {
|
|
FifoU8Rem(f, &ch);
|
|
str[i] = ch;
|
|
i++;
|
|
}
|
|
FifoU8Flush(f);
|
|
return str;
|
|
}
|
|
|
|
U0 @json_insert_key(@json_object* obj, @json_key* key)
|
|
{
|
|
if (!obj)
|
|
return;
|
|
if (!obj->keys) {
|
|
obj->keys = key;
|
|
obj->length++;
|
|
return;
|
|
}
|
|
@json_key* k = obj->keys;
|
|
while (k->next)
|
|
k = k->next;
|
|
k->next = key;
|
|
key->prev = k;
|
|
obj->length++;
|
|
}
|
|
|
|
U0 @json_rstrip(U8* str)
|
|
{
|
|
if (!str || !StrLen(str))
|
|
return;
|
|
I64 r_pos = StrLen(str) - 1;
|
|
while (str[r_pos] == ' ' || str[r_pos] == '\r' || str[r_pos] == '\n' || str[r_pos] == '\t') {
|
|
str[r_pos] = NULL;
|
|
--r_pos;
|
|
}
|
|
}
|
|
|
|
extern @json_element* @json_parse_object_or_array(@json_parser* parser);
|
|
|
|
I64 @json_unescape_char(I64 escape_ch)
|
|
{
|
|
I64 return_ch = escape_ch;
|
|
|
|
// FIXME: unicode
|
|
switch (escape_ch) {
|
|
case 'b':
|
|
return_ch = 0x08;
|
|
break;
|
|
case 'f':
|
|
return_ch = 0x0c;
|
|
break;
|
|
case 'n':
|
|
return_ch = 0x0a;
|
|
break;
|
|
case 'r':
|
|
return_ch = 0x0d;
|
|
break;
|
|
case 't':
|
|
return_ch = 0x09;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return return_ch;
|
|
}
|
|
|
|
U0 @json_parse_object(@json_parser* parser, @json_object* obj)
|
|
{
|
|
@json_key* key = NULL;
|
|
while (1) {
|
|
switch (parser->stream[parser->pos]) {
|
|
case '\\':
|
|
// NOTE: We keep escaped unicode in its original form, and let the program ingesting the JSON handle the UTF-8 conversion.
|
|
if (parser->state == JSON_STATE_OBJECT_STRING) {
|
|
if (parser->stream[parser->pos + 1] == 'u') {
|
|
FifoU8Ins(parser->consumed, '\\');
|
|
} else {
|
|
FifoU8Ins(parser->consumed, @json_unescape_char(parser->stream[++parser->pos]));
|
|
}
|
|
}
|
|
break;
|
|
case '}':
|
|
switch (parser->state) {
|
|
case JSON_STATE_OBJECT_KEY:
|
|
case JSON_STATE_OBJECT_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_OBJECT_NUMBER:
|
|
key->value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_rstrip(key->value);
|
|
key->value = Str2F64(key->value);
|
|
@json_insert_key(obj, key);
|
|
return;
|
|
break;
|
|
case JSON_STATE_OBJECT_BOOLEAN:
|
|
key->value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_rstrip(key->value);
|
|
if (StrCmp("true", key->value) && StrCmp("false", key->value)) {
|
|
PrintErr("@json_parse_object: Illegal boolean value at position %d",
|
|
parser->pos);
|
|
while (1)
|
|
Sleep(1);
|
|
}
|
|
if (!StrCmp("true", key->value))
|
|
key->value = TRUE;
|
|
else
|
|
key->value = FALSE;
|
|
@json_insert_key(obj, key);
|
|
return;
|
|
break;
|
|
case JSON_STATE_OBJECT_NULL:
|
|
key->value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_rstrip(key->value);
|
|
if (StrCmp("null", key->value)) {
|
|
PrintErr("@json_parse_object: Illegal null value at position %d",
|
|
parser->pos);
|
|
while (1)
|
|
Sleep(1);
|
|
}
|
|
key->value = NULL;
|
|
@json_insert_key(obj, key);
|
|
return;
|
|
break;
|
|
case JSON_STATE_OBJECT:
|
|
case JSON_STATE_OBJECT_NEXT:
|
|
return;
|
|
break;
|
|
}
|
|
break;
|
|
case ',':
|
|
switch (parser->state) {
|
|
case JSON_STATE_OBJECT_KEY:
|
|
case JSON_STATE_OBJECT_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_OBJECT_NUMBER:
|
|
key->value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
key->value = Str2F64(key->value);
|
|
@json_insert_key(obj, key);
|
|
parser->state = JSON_STATE_OBJECT;
|
|
break;
|
|
case JSON_STATE_OBJECT_BOOLEAN:
|
|
key->value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_rstrip(key->value);
|
|
if (StrCmp("true", key->value) && StrCmp("false", key->value)) {
|
|
PrintErr("@json_parse_object: Illegal boolean value at position %d",
|
|
parser->pos);
|
|
while (1)
|
|
Sleep(1);
|
|
}
|
|
if (!StrCmp("true", key->value))
|
|
key->value = TRUE;
|
|
else
|
|
key->value = FALSE;
|
|
@json_insert_key(obj, key);
|
|
parser->state = JSON_STATE_OBJECT;
|
|
break;
|
|
case JSON_STATE_OBJECT_NULL:
|
|
key->value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_rstrip(key->value);
|
|
if (StrCmp("null", key->value)) {
|
|
PrintErr("@json_parse_object: Illegal null value at position %d",
|
|
parser->pos);
|
|
while (1)
|
|
Sleep(1);
|
|
}
|
|
key->value = NULL;
|
|
@json_insert_key(obj, key);
|
|
parser->state = JSON_STATE_OBJECT;
|
|
break;
|
|
case JSON_STATE_OBJECT_NEXT:
|
|
parser->state = JSON_STATE_OBJECT;
|
|
break;
|
|
}
|
|
break;
|
|
case ':':
|
|
switch (parser->state) {
|
|
case JSON_STATE_OBJECT_KEY:
|
|
case JSON_STATE_OBJECT_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_OBJECT_SEPARATOR:
|
|
parser->state = JSON_STATE_OBJECT_TYPE;
|
|
break;
|
|
}
|
|
break;
|
|
case '[':
|
|
switch (parser->state) {
|
|
case JSON_STATE_OBJECT_KEY:
|
|
case JSON_STATE_OBJECT_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_OBJECT_TYPE:
|
|
key->type = JSON_ARRAY;
|
|
key->value = @json_parse_object_or_array(parser);
|
|
@json_insert_key(obj, key);
|
|
parser->state = JSON_STATE_OBJECT_NEXT;
|
|
break;
|
|
}
|
|
break;
|
|
case '{':
|
|
switch (parser->state) {
|
|
case JSON_STATE_OBJECT_KEY:
|
|
case JSON_STATE_OBJECT_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_OBJECT_TYPE:
|
|
key->type = JSON_OBJECT;
|
|
key->value = @json_parse_object_or_array(parser);
|
|
@json_insert_key(obj, key);
|
|
parser->state = JSON_STATE_OBJECT_NEXT;
|
|
break;
|
|
}
|
|
break;
|
|
case '"':
|
|
switch (parser->state) {
|
|
case JSON_STATE_OBJECT_STRING:
|
|
key->value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_insert_key(obj, key);
|
|
parser->state = JSON_STATE_OBJECT_NEXT;
|
|
break;
|
|
case JSON_STATE_OBJECT_TYPE:
|
|
key->type = JSON_STRING;
|
|
parser->state = JSON_STATE_OBJECT_STRING;
|
|
break;
|
|
case JSON_STATE_OBJECT_KEY:
|
|
key->name = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
parser->state = JSON_STATE_OBJECT_SEPARATOR;
|
|
break;
|
|
case JSON_STATE_OBJECT:
|
|
key = CAlloc(sizeof(@json_key), parser->mem_task);
|
|
key->sig = JSON_SIG;
|
|
parser->state = JSON_STATE_OBJECT_KEY;
|
|
break;
|
|
}
|
|
break;
|
|
case '-':
|
|
case '0' ... '9':
|
|
case '.':
|
|
switch (parser->state) {
|
|
case JSON_STATE_OBJECT_KEY:
|
|
case JSON_STATE_OBJECT_STRING:
|
|
case JSON_STATE_OBJECT_NUMBER:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_OBJECT_TYPE:
|
|
key->type = JSON_NUMBER;
|
|
parser->state = JSON_STATE_OBJECT_NUMBER;
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
}
|
|
break;
|
|
case 't':
|
|
case 'f':
|
|
switch (parser->state) {
|
|
case JSON_STATE_OBJECT_KEY:
|
|
case JSON_STATE_OBJECT_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_OBJECT_TYPE:
|
|
key->type = JSON_BOOLEAN;
|
|
parser->state = JSON_STATE_OBJECT_BOOLEAN;
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
}
|
|
break;
|
|
case 'n':
|
|
switch (parser->state) {
|
|
case JSON_STATE_OBJECT_KEY:
|
|
case JSON_STATE_OBJECT_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_OBJECT_TYPE:
|
|
key->type = JSON_NULL;
|
|
parser->state = JSON_STATE_OBJECT_NULL;
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
switch (parser->state) {
|
|
case JSON_STATE_OBJECT_KEY:
|
|
case JSON_STATE_OBJECT_STRING:
|
|
case JSON_STATE_OBJECT_BOOLEAN:
|
|
case JSON_STATE_OBJECT_NULL:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (parser->debug) {
|
|
@json_debug_parser_state(parser);
|
|
"Object: %08X, Pos: %d, Token: %c\n", obj, parser->pos,
|
|
parser->stream[parser->pos];
|
|
Sleep(50);
|
|
}
|
|
parser->pos++;
|
|
}
|
|
}
|
|
|
|
U0 @json_parse_array(@json_parser* parser, @json_array* arr)
|
|
{
|
|
U64 value = NULL;
|
|
while (1) {
|
|
if (parser->state == JSON_STATE_ARRAY) {
|
|
switch (parser->stream[parser->pos]) {
|
|
case 0:
|
|
PrintErr("@json_parse_array: Malformed array");
|
|
while (1)
|
|
Sleep(1);
|
|
break;
|
|
case ']':
|
|
return;
|
|
break;
|
|
}
|
|
parser->state = JSON_STATE_ARRAY_TYPE;
|
|
}
|
|
switch (parser->stream[parser->pos]) {
|
|
case '\\':
|
|
// NOTE: We keep escaped unicode in its original form, and let the program ingesting the JSON handle the UTF-8 conversion.
|
|
if (parser->state == JSON_STATE_ARRAY_STRING) {
|
|
if (parser->stream[parser->pos + 1] == 'u') {
|
|
FifoU8Ins(parser->consumed, '\\');
|
|
} else {
|
|
FifoU8Ins(parser->consumed, @json_unescape_char(parser->stream[++parser->pos]));
|
|
}
|
|
}
|
|
break;
|
|
case ']':
|
|
switch (parser->state) {
|
|
case JSON_STATE_ARRAY_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_ARRAY_NUMBER:
|
|
value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_rstrip(value);
|
|
value = Str2F64(value);
|
|
@json_append_item(arr, value, JSON_NUMBER);
|
|
return;
|
|
break;
|
|
case JSON_STATE_ARRAY_BOOLEAN:
|
|
value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_rstrip(value);
|
|
if (StrCmp("true", value) && StrCmp("false", value)) {
|
|
PrintErr("@json_parse_array: Illegal boolean value at position %d",
|
|
parser->pos);
|
|
while (1)
|
|
Sleep(1);
|
|
}
|
|
if (!StrCmp("true", value))
|
|
value = TRUE;
|
|
else
|
|
value = FALSE;
|
|
@json_append_item(arr, value, JSON_BOOLEAN);
|
|
break;
|
|
case JSON_STATE_ARRAY_NULL:
|
|
value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_rstrip(value);
|
|
if (StrCmp("null", value)) {
|
|
PrintErr("@json_parse_array: Illegal null value at position %d",
|
|
parser->pos);
|
|
while (1)
|
|
Sleep(1);
|
|
}
|
|
value = NULL;
|
|
@json_append_item(arr, value, JSON_NULL);
|
|
break;
|
|
case JSON_STATE_ARRAY:
|
|
case JSON_STATE_ARRAY_NEXT:
|
|
return;
|
|
break;
|
|
}
|
|
break;
|
|
case ',':
|
|
switch (parser->state) {
|
|
case JSON_STATE_ARRAY_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_ARRAY_NUMBER:
|
|
value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
value = Str2F64(value);
|
|
@json_append_item(arr, value, JSON_NUMBER);
|
|
parser->state = JSON_STATE_ARRAY;
|
|
break;
|
|
case JSON_STATE_ARRAY_BOOLEAN:
|
|
value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_rstrip(value);
|
|
if (StrCmp("true", value) && StrCmp("false", value)) {
|
|
PrintErr("@json_parse_array: Illegal boolean value at position %d",
|
|
parser->pos);
|
|
while (1)
|
|
Sleep(1);
|
|
}
|
|
if (!StrCmp("true", value))
|
|
value = TRUE;
|
|
else
|
|
value = FALSE;
|
|
@json_append_item(arr, value, JSON_BOOLEAN);
|
|
parser->state = JSON_STATE_ARRAY;
|
|
break;
|
|
case JSON_STATE_ARRAY_NULL:
|
|
value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_rstrip(value);
|
|
if (StrCmp("null", value)) {
|
|
PrintErr("@json_parse_array: Illegal null value at position %d",
|
|
parser->pos);
|
|
while (1)
|
|
Sleep(1);
|
|
}
|
|
value = NULL;
|
|
@json_append_item(arr, value, JSON_NULL);
|
|
parser->state = JSON_STATE_ARRAY;
|
|
break;
|
|
case JSON_STATE_ARRAY_NEXT:
|
|
parser->state = JSON_STATE_ARRAY;
|
|
break;
|
|
}
|
|
break;
|
|
case '[':
|
|
switch (parser->state) {
|
|
case JSON_STATE_ARRAY_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_ARRAY_TYPE:
|
|
value = @json_parse_object_or_array(parser);
|
|
@json_append_item(arr, value, JSON_ARRAY);
|
|
parser->state = JSON_STATE_ARRAY_NEXT;
|
|
break;
|
|
}
|
|
break;
|
|
case '{':
|
|
switch (parser->state) {
|
|
case JSON_STATE_ARRAY_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_ARRAY_TYPE:
|
|
value = @json_parse_object_or_array(parser);
|
|
@json_append_item(arr, value, JSON_OBJECT);
|
|
parser->state = JSON_STATE_ARRAY_NEXT;
|
|
break;
|
|
}
|
|
break;
|
|
case '"':
|
|
switch (parser->state) {
|
|
case JSON_STATE_ARRAY_STRING:
|
|
value = @json_string_from_fifo(parser->consumed, parser->mem_task);
|
|
@json_append_item(arr, value, JSON_STRING);
|
|
parser->state = JSON_STATE_ARRAY_NEXT;
|
|
break;
|
|
case JSON_STATE_ARRAY_TYPE:
|
|
parser->state = JSON_STATE_ARRAY_STRING;
|
|
break;
|
|
}
|
|
break;
|
|
case '-':
|
|
case '0' ... '9':
|
|
case '.':
|
|
switch (parser->state) {
|
|
case JSON_STATE_ARRAY_STRING:
|
|
case JSON_STATE_ARRAY_NUMBER:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_ARRAY_TYPE:
|
|
parser->state = JSON_STATE_ARRAY_NUMBER;
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
}
|
|
break;
|
|
case 't':
|
|
case 'f':
|
|
switch (parser->state) {
|
|
case JSON_STATE_ARRAY_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_ARRAY_TYPE:
|
|
parser->state = JSON_STATE_ARRAY_BOOLEAN;
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
}
|
|
break;
|
|
case 'n':
|
|
switch (parser->state) {
|
|
case JSON_STATE_ARRAY_STRING:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
case JSON_STATE_OBJECT_TYPE:
|
|
parser->state = JSON_STATE_ARRAY_NULL;
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
switch (parser->state) {
|
|
case JSON_STATE_ARRAY_STRING:
|
|
case JSON_STATE_ARRAY_BOOLEAN:
|
|
case JSON_STATE_ARRAY_NULL:
|
|
FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (parser->debug) {
|
|
@json_debug_parser_state(parser);
|
|
"Array: %08X, Pos: %d, Token: %c\n", arr, parser->pos,
|
|
parser->stream[parser->pos];
|
|
Sleep(50);
|
|
}
|
|
parser->pos++;
|
|
}
|
|
}
|
|
|
|
extern @json_callable_array* @json_create_callable_array(@json_array* arr);
|
|
extern @json_callable_object* @json_create_callable_object(@json_object* obj);
|
|
|
|
@json_element* @json_parse_object_or_array(@json_parser* parser)
|
|
{
|
|
@json_element* el = CAlloc(sizeof(@json_array), parser->mem_task);
|
|
el->sig = JSON_SIG;
|
|
while (1) {
|
|
switch (parser->stream[parser->pos]) {
|
|
case 0:
|
|
return el;
|
|
break;
|
|
case ' ':
|
|
case '\r':
|
|
case '\n':
|
|
case '\t':
|
|
break;
|
|
case '{':
|
|
el->type = JSON_OBJECT;
|
|
el(@json_object*)->mem_task = parser->mem_task;
|
|
parser->pos++;
|
|
parser->state = JSON_STATE_OBJECT;
|
|
@json_parse_object(parser, el);
|
|
return @json_create_callable_object(el);
|
|
break;
|
|
case '[':
|
|
el->type = JSON_ARRAY;
|
|
el(@json_array*)->mem_task = parser->mem_task;
|
|
parser->pos++;
|
|
parser->state = JSON_STATE_ARRAY;
|
|
@json_parse_array(parser, el);
|
|
return @json_create_callable_array(el);
|
|
break;
|
|
default:
|
|
PrintErr("@json_parse_object_or_array: Invalid token: '%c'", parser->stream[parser->pos]);
|
|
while (1) {
|
|
Sleep(1);
|
|
};
|
|
break;
|
|
}
|
|
parser->pos++;
|
|
Sleep(1);
|
|
}
|
|
}
|
|
|
|
@json_element* @json_parse(U8* str, CTask* mem_task)
|
|
{
|
|
if (!str || !mem_task) {
|
|
return NULL;
|
|
}
|
|
@json_parser* parser = CAlloc(sizeof(@json_parser), mem_task);
|
|
parser->mem_task = mem_task;
|
|
parser->consumed = FifoU8New(JSON_PARSER_FIFO_SIZE, parser->mem_task);
|
|
parser->stream = str;
|
|
return @json_parse_object_or_array(parser);
|
|
}
|
|
|
|
U0 @json_prepend_item(@json_array* arr, U64 value, I64 type)
|
|
{
|
|
if (!arr)
|
|
return;
|
|
if (arr->type != JSON_ARRAY)
|
|
return;
|
|
@json_item* prepend_item = @json_create_item(value, type, arr->mem_task);
|
|
@json_item* items = arr->items;
|
|
arr->items = prepend_item;
|
|
arr->items->next = items;
|
|
arr->length++;
|
|
}
|
|
|
|
U0 @json_insert_item(@json_array* arr, I64 index, U64 value, I64 type)
|
|
{
|
|
if (!arr)
|
|
return;
|
|
if (arr->type != JSON_ARRAY)
|
|
return;
|
|
if (index <= 0) {
|
|
@json_prepend_item(arr, value, type);
|
|
return;
|
|
}
|
|
if (index >= arr->length) {
|
|
@json_append_item(arr, value, type);
|
|
return;
|
|
}
|
|
@json_item* insert_item = @json_create_item(value, type, arr->mem_task);
|
|
@json_item* insert_at_item = arr->items;
|
|
@json_item* insert_after_item = NULL;
|
|
I64 i;
|
|
for (i = 0; i < index; i++)
|
|
insert_at_item = insert_at_item->next;
|
|
insert_after_item = insert_at_item->prev;
|
|
insert_after_item->next = insert_item;
|
|
insert_item->prev = insert_after_item;
|
|
insert_item->next = insert_at_item;
|
|
insert_at_item->prev = insert_item;
|
|
arr->length++;
|
|
}
|
|
|
|
U0 @json_stringify_check_capacity(@json_stringify_string* str)
|
|
{
|
|
if (str->length >= str->capacity) {
|
|
str->capacity *= 2;
|
|
U8* new_value = CAlloc(str->capacity * 2, str->mem_task);
|
|
MemCpy(new_value, str->value, str->length);
|
|
str->value = new_value;
|
|
}
|
|
}
|
|
|
|
U0 @json_stringify_append_char(@json_stringify_string* str, U8 char)
|
|
{
|
|
// FIXME: unicode
|
|
switch (char) {
|
|
case '\\':
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = '\\';
|
|
break;
|
|
case 0x08:
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = 'b';
|
|
break;
|
|
case 0x0c:
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = 'f';
|
|
break;
|
|
case 0x0a:
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = 'n';
|
|
break;
|
|
case 0x0d:
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = 'r';
|
|
break;
|
|
case 0x09:
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = 't';
|
|
break;
|
|
default:
|
|
str->value[str->length++] = char;
|
|
break;
|
|
}
|
|
str->value[str->length] = NULL;
|
|
@json_stringify_check_capacity(str);
|
|
}
|
|
|
|
U0 @json_stringify_append_char_escape_quotes(@json_stringify_string* str, U8 char)
|
|
{
|
|
// FIXME: unicode
|
|
switch (char) {
|
|
case '"':
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = '"';
|
|
break;
|
|
case '\\':
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = '\\';
|
|
break;
|
|
case 0x08:
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = 'b';
|
|
break;
|
|
case 0x0c:
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = 'f';
|
|
break;
|
|
case 0x0a:
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = 'n';
|
|
break;
|
|
case 0x0d:
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = 'r';
|
|
break;
|
|
case 0x09:
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = 't';
|
|
break;
|
|
default:
|
|
str->value[str->length++] = char;
|
|
break;
|
|
}
|
|
str->value[str->length] = NULL;
|
|
@json_stringify_check_capacity(str);
|
|
}
|
|
|
|
U0 @json_stringify_append_str(@json_stringify_string* str, U8* str2)
|
|
{
|
|
while (*str2) {
|
|
// NOTE: We keep escaped unicode in its original form, and let the program ingesting the JSON handle the UTF-8 conversion.
|
|
if (*str2 == '\\' && *(str2 + 1) == 'u') {
|
|
str->value[str->length++] = '\\';
|
|
str->value[str->length++] = 'u';
|
|
str->value[str->length] = NULL;
|
|
str2++;
|
|
} else {
|
|
@json_stringify_append_char_escape_quotes(str, *str2);
|
|
}
|
|
str2++;
|
|
@json_stringify_check_capacity(str);
|
|
}
|
|
}
|
|
|
|
U0 @json_stringify_append_number(@json_stringify_string* str, F64 num)
|
|
{
|
|
U8 buf[256];
|
|
MemSet(buf, 0, 256);
|
|
StrPrint(buf, "%.6f", num);
|
|
I64 i = StrLen(buf) - 1;
|
|
while (buf[i] == '0') {
|
|
buf[i] = NULL;
|
|
i--;
|
|
}
|
|
i = StrLen(buf) - 1;
|
|
if (buf[i] == '.')
|
|
buf[i] = NULL;
|
|
@json_stringify_append_str(str, buf);
|
|
}
|
|
|
|
extern U0 @json_stringify_object_or_array(@json_stringify_string* str, @json_element* el);
|
|
|
|
U0 @json_stringify_object(@json_stringify_string* str, @json_object* obj)
|
|
{
|
|
@json_stringify_append_char(str, '{');
|
|
@json_key* key = obj->keys;
|
|
while (key) {
|
|
@json_stringify_append_char(str, '"');
|
|
@json_stringify_append_str(str, key->name);
|
|
@json_stringify_append_char(str, '"');
|
|
@json_stringify_append_char(str, ':');
|
|
switch (key->type) {
|
|
case JSON_OBJECT:
|
|
case JSON_ARRAY:
|
|
@json_stringify_object_or_array(str, key->value);
|
|
break;
|
|
case JSON_STRING:
|
|
@json_stringify_append_char(str, '"');
|
|
@json_stringify_append_str(str, key->value);
|
|
@json_stringify_append_char(str, '"');
|
|
break;
|
|
case JSON_NUMBER:
|
|
@json_stringify_append_number(str, key->value);
|
|
break;
|
|
case JSON_BOOLEAN:
|
|
@json_stringify_append_str(str, @t(key->value, "true", "false"));
|
|
break;
|
|
case JSON_NULL:
|
|
@json_stringify_append_str(str, "null");
|
|
break;
|
|
default:
|
|
PrintErr("@json_stringify_object: Invalid element type: %d", key->type);
|
|
while (1) {
|
|
Sleep(1);
|
|
};
|
|
}
|
|
if (key->next)
|
|
@json_stringify_append_char(str, ',');
|
|
key = key->next;
|
|
}
|
|
@json_stringify_append_char(str, '}');
|
|
}
|
|
|
|
U0 @json_stringify_array(@json_stringify_string* str, @json_array* arr)
|
|
{
|
|
@json_stringify_append_char(str, '[');
|
|
@json_item* item = arr->items;
|
|
while (item) {
|
|
switch (item->type) {
|
|
case JSON_OBJECT:
|
|
case JSON_ARRAY:
|
|
@json_stringify_object_or_array(str, item->value);
|
|
break;
|
|
case JSON_STRING:
|
|
@json_stringify_append_char(str, '"');
|
|
@json_stringify_append_str(str, item->value);
|
|
@json_stringify_append_char(str, '"');
|
|
break;
|
|
case JSON_NUMBER:
|
|
@json_stringify_append_number(str, item->value);
|
|
break;
|
|
case JSON_BOOLEAN:
|
|
@json_stringify_append_str(str, @t(item->value, "true", "false"));
|
|
break;
|
|
case JSON_NULL:
|
|
@json_stringify_append_str(str, "null");
|
|
break;
|
|
default:
|
|
PrintErr("@json_stringify_array: Invalid element type: %d", item->type);
|
|
while (1) {
|
|
Sleep(1);
|
|
};
|
|
}
|
|
if (item->next)
|
|
@json_stringify_append_char(str, ',');
|
|
item = item->next;
|
|
}
|
|
@json_stringify_append_char(str, ']');
|
|
}
|
|
|
|
U0 @json_stringify_object_or_array(@json_stringify_string* str, @json_element* el)
|
|
{
|
|
while (el) {
|
|
switch (el->type) {
|
|
case JSON_OBJECT:
|
|
@json_stringify_object(str, el);
|
|
break;
|
|
case JSON_ARRAY:
|
|
@json_stringify_array(str, el);
|
|
break;
|
|
default:
|
|
PrintErr("@json_stringify_object_or_array: Invalid element type: %d", el->type);
|
|
while (1) {
|
|
Sleep(1);
|
|
};
|
|
break;
|
|
}
|
|
el = el->next;
|
|
}
|
|
}
|
|
|
|
U8* @json_stringify(@json_element* el, CTask* mem_task)
|
|
{
|
|
if (!el || !mem_task) {
|
|
return NULL;
|
|
}
|
|
@json_stringify_string* str = CAlloc(sizeof(@json_stringify_string), mem_task);
|
|
str->mem_task = mem_task;
|
|
str->capacity = 256;
|
|
str->value = CAlloc(str->capacity * 2, str->mem_task);
|
|
@json_stringify_object_or_array(str, el);
|
|
return str->value;
|
|
}
|
|
|
|
U64 @json_get(@json_object* obj, U8* key, Bool return_key = FALSE)
|
|
{
|
|
if (!obj || !key)
|
|
return NULL;
|
|
if (!obj->keys || obj->type != JSON_OBJECT)
|
|
return NULL;
|
|
@json_key* iter_key = obj->keys;
|
|
while (iter_key) {
|
|
if (!StrICmp(iter_key->name, key))
|
|
if (return_key)
|
|
return iter_key;
|
|
else
|
|
return iter_key->value;
|
|
iter_key = iter_key->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
U0 @json_set(@json_object* obj, U8* key, U64 value, I64 type = JSON_SAME)
|
|
{
|
|
if (!obj || !key || !type)
|
|
return;
|
|
if (obj->type != JSON_OBJECT)
|
|
return;
|
|
@json_key* iter_key = obj->keys;
|
|
while (iter_key) {
|
|
if (!StrCmp(iter_key->name, key)) {
|
|
if (type != JSON_SAME)
|
|
iter_key->type = type;
|
|
iter_key->value = value;
|
|
return;
|
|
}
|
|
iter_key = iter_key->next;
|
|
}
|
|
@json_key* new_key = CAlloc(sizeof(@json_key), obj->mem_task);
|
|
new_key->sig = JSON_SIG;
|
|
new_key->name = StrNew(key, obj->mem_task);
|
|
new_key->type = type;
|
|
if (new_key->type == JSON_STRING)
|
|
new_key->value = StrNew(value, obj->mem_task);
|
|
else
|
|
new_key->value = value;
|
|
@json_insert_key(obj, new_key);
|
|
}
|
|
|
|
U0 @json_unset(@json_object* obj, U8* key)
|
|
{
|
|
if (!obj || !key)
|
|
return;
|
|
if (obj->type != JSON_OBJECT)
|
|
return;
|
|
@json_key* iter_key = obj->keys;
|
|
@json_key* prev_key = NULL;
|
|
@json_key* next_key = NULL;
|
|
while (iter_key) {
|
|
if (!StrCmp(iter_key->name, key)) {
|
|
prev_key = iter_key->prev;
|
|
next_key = iter_key->next;
|
|
if (prev_key)
|
|
prev_key->next = next_key;
|
|
if (next_key)
|
|
next_key->prev = prev_key;
|
|
return;
|
|
}
|
|
iter_key = iter_key->next;
|
|
}
|
|
}
|
|
|
|
U64 @json_callable_object_get_wrapper_function(U8* key, Bool return_key = FALSE)
|
|
{
|
|
return @json_get(JSON_WRAPPER_MAGIC_NUMBER, key, return_key);
|
|
}
|
|
|
|
U0 @json_callable_object_set_wrapper_function(U8* key, U64 value, I64 type = JSON_SAME)
|
|
{
|
|
@json_set(JSON_WRAPPER_MAGIC_NUMBER, key, value, type);
|
|
}
|
|
|
|
U0 @json_callable_object_unset_wrapper_function(U8* key)
|
|
{
|
|
@json_unset(JSON_WRAPPER_MAGIC_NUMBER, key);
|
|
}
|
|
|
|
@json_callable_object* @json_create_callable_object(@json_object* obj)
|
|
{
|
|
@json_callable_object* cobj = CAlloc(sizeof(@json_callable_object), obj->mem_task);
|
|
MemCpy(cobj, obj, sizeof(@json_object));
|
|
Free(obj);
|
|
|
|
U64 a;
|
|
I64 buffer_size = (MSize(&@json_callable_object_get_wrapper_function) + MSize(&@json_callable_object_set_wrapper_function) + MSize(&@json_callable_object_unset_wrapper_function));
|
|
buffer_size += buffer_size % 16;
|
|
|
|
U64 code_ptr = CAlloc(buffer_size, cobj->mem_task->code_heap);
|
|
I64 code_size = 0;
|
|
|
|
// Create a copy of function and patch Get
|
|
code_size = MSize(&@json_callable_object_get_wrapper_function);
|
|
cobj->@ = code_ptr;
|
|
MemCpy(cobj->@, &@json_callable_object_get_wrapper_function, code_size);
|
|
code_ptr += code_size;
|
|
|
|
a = cobj->@;
|
|
a += 0x13;
|
|
MemSetI64(a, cobj, 1);
|
|
|
|
a = cobj->@;
|
|
a += 0x1c;
|
|
@patch_call_rel32(a, &@json_get);
|
|
|
|
// Create a copy of function and patch Set
|
|
code_size = MSize(&@json_callable_object_set_wrapper_function);
|
|
cobj->set = code_ptr;
|
|
MemCpy(cobj->set, &@json_callable_object_set_wrapper_function, code_size);
|
|
code_ptr += code_size;
|
|
|
|
a = cobj->set;
|
|
a += 0x1a;
|
|
MemSetI64(a, cobj, 1);
|
|
|
|
a = cobj->set;
|
|
a += 0x23;
|
|
@patch_call_rel32(a, &@json_set);
|
|
|
|
// Create a copy of function and patch Unset
|
|
code_size = MSize(&@json_callable_object_unset_wrapper_function);
|
|
cobj->unset = code_ptr;
|
|
MemCpy(cobj->unset, &@json_callable_object_unset_wrapper_function, code_size);
|
|
code_ptr += code_size;
|
|
|
|
a = cobj->unset;
|
|
a += 0x0c;
|
|
MemSetI64(a, cobj, 1);
|
|
|
|
a = cobj->unset;
|
|
a += 0x15;
|
|
@patch_call_rel32(a, &@json_unset);
|
|
|
|
cobj->a = cobj->@;
|
|
cobj->o = cobj->@;
|
|
|
|
return cobj;
|
|
}
|
|
|
|
@json_callable_object* @json_create_object(CTask* mem_task)
|
|
{
|
|
if (!mem_task) {
|
|
return NULL;
|
|
}
|
|
@json_object* obj = CAlloc(sizeof(@json_object), mem_task);
|
|
obj->mem_task = mem_task;
|
|
obj->sig = JSON_SIG;
|
|
obj->type = JSON_OBJECT;
|
|
return @json_create_callable_object(obj);
|
|
}
|
|
|
|
U64 @json_array_index(@json_array* arr, I64 index, Bool return_item = FALSE)
|
|
{
|
|
if (!arr)
|
|
return NULL;
|
|
if (arr->type != JSON_ARRAY)
|
|
return NULL;
|
|
if (index < 0)
|
|
return NULL;
|
|
if (!arr->length)
|
|
return NULL;
|
|
if (index > 0 && index > arr->length - 1)
|
|
return NULL;
|
|
@json_item* item = arr->items;
|
|
if (!item)
|
|
return NULL;
|
|
I64 i;
|
|
for (i = 0; i < index; i++)
|
|
item = item->next;
|
|
if (return_item)
|
|
return item;
|
|
else
|
|
return item->value;
|
|
}
|
|
|
|
Bool @json_array_contains(@json_array* arr, U64 value, I64 type = NULL, Bool match_case = FALSE)
|
|
{
|
|
if (!arr)
|
|
return FALSE;
|
|
if (arr->type != JSON_ARRAY)
|
|
return FALSE;
|
|
if (!arr->length)
|
|
return FALSE;
|
|
|
|
if (!type) {
|
|
if (value > 0x1000) {
|
|
type = JSON_STRING;
|
|
} else {
|
|
type = JSON_BOOLEAN;
|
|
}
|
|
}
|
|
|
|
I64 i;
|
|
@json_item* item = NULL;
|
|
for (i = 0; i < arr->length; i++) {
|
|
item = @json_array_index(arr, i, TRUE);
|
|
if (item->type == type) {
|
|
switch (type) {
|
|
case JSON_STRING:
|
|
if (match_case) {
|
|
if (!StrCmp(value, item->value)) {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
if (!StrICmp(value, item->value)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case JSON_BOOLEAN:
|
|
case JSON_NULL:
|
|
case JSON_NUMBER:
|
|
if (item->value == value) {
|
|
return TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
U0 @json_remove_item(@json_array* arr, I64 index)
|
|
{
|
|
if (!arr)
|
|
return;
|
|
if (arr->type != JSON_ARRAY)
|
|
return;
|
|
if (index < 0)
|
|
return;
|
|
if (!arr->length)
|
|
return;
|
|
if (index > 0 && index > arr->length - 1)
|
|
return;
|
|
@json_item* item = arr->items;
|
|
if (!item)
|
|
return;
|
|
@json_item* prev_item = NULL;
|
|
@json_item* next_item = NULL;
|
|
I64 i;
|
|
for (i = 0; i < index; i++)
|
|
item = item->next;
|
|
if (!index) {
|
|
arr->items = item->next;
|
|
goto @json_remove_item_final;
|
|
}
|
|
prev_item = item->prev;
|
|
next_item = item->next;
|
|
if (arr->last_item == item)
|
|
arr->last_item = item->prev;
|
|
prev_item->next = next_item;
|
|
if (next_item)
|
|
next_item->prev = prev_item;
|
|
@json_remove_item_final : --arr->length;
|
|
if (!arr->length)
|
|
arr->last_item = NULL;
|
|
}
|
|
|
|
U64 @json_callable_array_index_wrapper_function(I64 index, Bool return_item = FALSE)
|
|
{
|
|
return @json_array_index(JSON_WRAPPER_MAGIC_NUMBER, index, return_item);
|
|
}
|
|
|
|
U0 @json_callable_array_append_wrapper_function(U64 value, I64 type = NULL)
|
|
{
|
|
@json_append_item(JSON_WRAPPER_MAGIC_NUMBER, value, type);
|
|
}
|
|
|
|
Bool @json_callable_array_contains_wrapper_function(U64 value, I64 type = NULL, Bool match_case = FALSE)
|
|
{
|
|
return @json_array_contains(JSON_WRAPPER_MAGIC_NUMBER, value, type, match_case);
|
|
}
|
|
|
|
U0 @json_callable_array_insert_wrapper_function(I64 index, U64 value, I64 type = NULL)
|
|
{
|
|
@json_insert_item(JSON_WRAPPER_MAGIC_NUMBER, index, value, type);
|
|
}
|
|
|
|
U0 @json_callable_array_prepend_wrapper_function(U64 value, I64 type = NULL)
|
|
{
|
|
@json_prepend_item(JSON_WRAPPER_MAGIC_NUMBER, value, type);
|
|
}
|
|
|
|
U0 @json_callable_array_remove_wrapper_function(I64 index)
|
|
{
|
|
@json_remove_item(JSON_WRAPPER_MAGIC_NUMBER, index);
|
|
}
|
|
|
|
@json_callable_array* @json_create_callable_array(@json_array* arr)
|
|
{
|
|
// Alloc callable object and copy instance
|
|
@json_callable_array* carr = CAlloc(sizeof(@json_callable_array), arr->mem_task);
|
|
MemCpy(carr, arr, sizeof(@json_array));
|
|
Free(arr);
|
|
|
|
U64 a;
|
|
I64 buffer_size = (MSize(&@json_callable_array_index_wrapper_function) + MSize(&@json_callable_array_append_wrapper_function) + MSize(&@json_callable_array_contains_wrapper_function) + MSize(&@json_callable_array_prepend_wrapper_function) + MSize(&@json_callable_array_insert_wrapper_function) + MSize(&@json_callable_array_remove_wrapper_function));
|
|
buffer_size += buffer_size % 16;
|
|
|
|
U64 code_ptr = CAlloc(buffer_size, carr->mem_task->code_heap);
|
|
I64 code_size = 0;
|
|
|
|
// Create a copy of function and patch Index
|
|
code_size = MSize(&@json_callable_array_index_wrapper_function);
|
|
carr->@ = code_ptr;
|
|
MemCpy(carr->@, &@json_callable_array_index_wrapper_function, code_size);
|
|
code_ptr += code_size;
|
|
|
|
a = carr->@;
|
|
a += 0x13;
|
|
MemSetI64(a, carr, 1);
|
|
|
|
a = carr->@;
|
|
a += 0x1c;
|
|
@patch_call_rel32(a, &@json_array_index);
|
|
|
|
carr->a = carr->@;
|
|
carr->o = carr->@;
|
|
|
|
// Create a copy of function and patch Append
|
|
code_size = MSize(&@json_callable_array_append_wrapper_function);
|
|
carr->append = code_ptr;
|
|
MemCpy(carr->append, &@json_callable_array_append_wrapper_function, code_size);
|
|
code_ptr += code_size;
|
|
|
|
a = carr->append;
|
|
a += 0x12;
|
|
MemSetI64(a, carr, 1);
|
|
|
|
a = carr->append;
|
|
a += 0x1b;
|
|
@patch_call_rel32(a, &@json_append_item);
|
|
|
|
// Create a copy of function and patch Contains
|
|
code_size = MSize(&@json_callable_array_contains_wrapper_function);
|
|
carr->contains = code_ptr;
|
|
MemCpy(carr->contains, &@json_callable_array_contains_wrapper_function, code_size);
|
|
code_ptr += code_size;
|
|
|
|
a = carr->contains;
|
|
a += 0x1b;
|
|
MemSetI64(a, carr, 1);
|
|
|
|
a = carr->contains;
|
|
a += 0x24;
|
|
@patch_call_rel32(a, &@json_array_contains);
|
|
|
|
// Create a copy of function and patch Prepend
|
|
code_size = MSize(&@json_callable_array_prepend_wrapper_function);
|
|
carr->prepend = code_ptr;
|
|
MemCpy(carr->prepend, &@json_callable_array_prepend_wrapper_function, code_size);
|
|
code_ptr += code_size;
|
|
|
|
a = carr->prepend;
|
|
a += 0x12;
|
|
MemSetI64(a, carr, 1);
|
|
|
|
a = carr->prepend;
|
|
a += 0x1b;
|
|
@patch_call_rel32(a, &@json_prepend_item);
|
|
|
|
// Create a copy of function and patch Insert
|
|
code_size = MSize(&@json_callable_array_insert_wrapper_function);
|
|
carr->insert = code_ptr;
|
|
MemCpy(carr->insert, &@json_callable_array_insert_wrapper_function, code_size);
|
|
code_ptr += code_size;
|
|
|
|
a = carr->insert;
|
|
a += 0x1a;
|
|
MemSetI64(a, carr, 1);
|
|
|
|
a = carr->insert;
|
|
a += 0x23;
|
|
@patch_call_rel32(a, &@json_insert_item);
|
|
|
|
// Create a copy of function and patch Remove
|
|
code_size = MSize(&@json_callable_array_remove_wrapper_function);
|
|
carr->remove = code_ptr;
|
|
MemCpy(carr->remove, &@json_callable_array_remove_wrapper_function, code_size);
|
|
code_ptr += code_size;
|
|
|
|
a = carr->remove;
|
|
a += 0x0c;
|
|
MemSetI64(a, carr, 1);
|
|
|
|
a = carr->remove;
|
|
a += 0x15;
|
|
@patch_call_rel32(a, &@json_remove_item);
|
|
|
|
return carr;
|
|
}
|
|
|
|
@json_array* @json_create_array(CTask* mem_task)
|
|
{
|
|
if (!mem_task) {
|
|
return NULL;
|
|
}
|
|
@json_array* arr = CAlloc(sizeof(@json_array), mem_task);
|
|
arr->mem_task = mem_task;
|
|
arr->sig = JSON_SIG;
|
|
arr->type = JSON_ARRAY;
|
|
return @json_create_callable_array(arr);
|
|
}
|
|
|
|
U64 @json_parse_file(U8* path, CTask* mem_task)
|
|
{
|
|
if (!path || !mem_task || !FileFind(path)) {
|
|
return NULL;
|
|
}
|
|
U64 res = NULL;
|
|
U8* json_string = FileRead(path);
|
|
if (json_string) {
|
|
res = @json_parse(json_string, mem_task);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
U0 @json_dump_to_file(U8* path, @json_element* el, CTask* mem_task)
|
|
{
|
|
if (!path || !el || !mem_task)
|
|
return;
|
|
U8* json_string = @json_stringify(el, mem_task);
|
|
FileWrite(path, json_string, StrLen(json_string));
|
|
}
|
|
|
|
@json_array* @json_element_value_as_array(@json_element* el, CTask* mem_task, I64 key_or_item)
|
|
{
|
|
if (!el || !mem_task || !key_or_item) {
|
|
return NULL;
|
|
}
|
|
switch (el->type) {
|
|
case JSON_ARRAY:
|
|
switch (key_or_item) {
|
|
case JSON_ELEMENT_IS_ITEM:
|
|
return el(@json_item*)->value;
|
|
case JSON_ELEMENT_IS_KEY:
|
|
return el(@json_key*)->value;
|
|
default:
|
|
return NULL;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
@json_array* arr = CAlloc(sizeof(@json_array), mem_task);
|
|
arr->mem_task = mem_task;
|
|
arr->sig = JSON_SIG;
|
|
arr->type = JSON_ARRAY;
|
|
switch (key_or_item) {
|
|
case JSON_ELEMENT_IS_ITEM:
|
|
@json_append_item(arr, el(@json_item*)->value, el->type);
|
|
break;
|
|
case JSON_ELEMENT_IS_KEY:
|
|
@json_append_item(arr, el(@json_key*)->value, el->type);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return @json_create_callable_array(arr);
|
|
}
|
|
|
|
@json_array* @json_item_value_as_array(@json_item* item, CTask* mem_task)
|
|
{
|
|
return @json_element_value_as_array(item, mem_task, JSON_ELEMENT_IS_ITEM);
|
|
}
|
|
|
|
@json_array* @json_key_value_as_array(@json_key* key, CTask* mem_task)
|
|
{
|
|
return @json_element_value_as_array(key, mem_task, JSON_ELEMENT_IS_KEY);
|
|
}
|
|
|
|
@json_element* @json_clone(@json_element* el, CTask* mem_task)
|
|
{
|
|
if (!el || !mem_task) {
|
|
return NULL;
|
|
}
|
|
U8* tmp = @json_stringify(el, mem_task);
|
|
if (!tmp) {
|
|
return NULL;
|
|
}
|
|
return @json_parse(tmp, mem_task);
|
|
}
|
|
|
|
class @json
|
|
{
|
|
@json_element* (*Clone)(@json_element* el, CTask* mem_task);
|
|
@json_array* (*CreateArray)(CTask* mem_task);
|
|
@json_object* (*CreateObject)(CTask* mem_task);
|
|
U0 (*DumpToFile)(U8* path, @json_element* el, CTask* mem_task);
|
|
@json_array* (*KeyValueAsArray)(@json_key* key, CTask* mem_task);
|
|
@json_array* (*ItemValueAsArray)(@json_item* item, CTask* mem_task);
|
|
@json_element* (*Parse)(U8* str, CTask* mem_task);
|
|
U64 (*ParseFile)(U8* path, CTask* mem_task);
|
|
U8* (*Stringify)(@json_element* el, CTask* mem_task);
|
|
};
|
|
|
|
@json Json;
|
|
Json.Clone = &@json_clone;
|
|
Json.CreateArray = &@json_create_array;
|
|
Json.CreateObject = &@json_create_object;
|
|
Json.DumpToFile = &@json_dump_to_file;
|
|
Json.ItemValueAsArray = &@json_item_value_as_array;
|
|
Json.KeyValueAsArray = &@json_key_value_as_array;
|
|
Json.Parse = &@json_parse;
|
|
Json.ParseFile = &@json_parse_file;
|
|
Json.Stringify = &@json_stringify;
|
|
|
|
"json ";
|