You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
552 lines
16 KiB
C
552 lines
16 KiB
C
#include "layout.h"
|
|
#include "json.h"
|
|
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
//#define DEBUG
|
|
|
|
#ifdef DEBUG
|
|
#define DEBUG_printf printf
|
|
#define DEBUG_fflush fflush
|
|
#define DEBUG_json_type_data debug_json_type_data
|
|
#else
|
|
#define DEBUG_printf(...)
|
|
#define DEBUG_fflush(...)
|
|
#define DEBUG_json_type_data(...)
|
|
#endif
|
|
|
|
int LAYOUT_GET_OFFSET( struct json_layout* layout, int i )
|
|
{
|
|
return *(int*)( &((char*)layout->first_offset )[ layout->stride * i ] );
|
|
}
|
|
struct json_reflection* LAYOUT_GET_REFLECT( struct json_layout* layout, int i )
|
|
{
|
|
return (struct json_reflection*)( &((char*)layout->first_reflection )[ layout->stride * i ] );
|
|
}
|
|
|
|
void json_layout_convert_legacy( struct json_object_field* legacy, struct json_layout* layout )
|
|
{
|
|
// calculate the number of fields
|
|
layout->count = 0;
|
|
while( legacy[layout->count].key ) {
|
|
layout->count += 1;
|
|
}
|
|
|
|
layout->stride = sizeof(struct json_object_field);
|
|
layout->first_offset = &legacy->offset;
|
|
layout->first_reflection = (struct json_reflection*)&legacy->key;
|
|
}
|
|
|
|
const char* get_required_field_name( struct json_layout* layout, int* offset )
|
|
{
|
|
for( int i = 0; i < layout->count; ++i ) {
|
|
struct json_reflection* reflect = LAYOUT_GET_REFLECT( layout, i );
|
|
if( reflect->type == &json_field_inline_sublayout ) {
|
|
// Convert to new struct layout*
|
|
struct json_layout sublayout;
|
|
json_layout_convert_legacy( reflect->sublayout, &sublayout );
|
|
const char* result = get_required_field_name( &sublayout, offset );
|
|
if( result ) { return result; }
|
|
} else if( reflect->required ) {
|
|
*offset -= 1;
|
|
if( *offset == 0 ) {
|
|
return reflect->key;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int count_required_fields( struct json_layout* layout )
|
|
{
|
|
int count = 0;
|
|
for( int i = 0; i < layout->count; ++i ) {
|
|
struct json_reflection* reflect = LAYOUT_GET_REFLECT( layout, i );
|
|
if( reflect->type == &json_field_inline_sublayout ) {
|
|
// Convert to new struct layout*
|
|
struct json_layout sublayout;
|
|
json_layout_convert_legacy( reflect->sublayout, &sublayout );
|
|
count += count_required_fields( &sublayout );
|
|
} else if( reflect->required ) {
|
|
count += 1;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/*
|
|
int count_required_fields( struct json_object_field* layout )
|
|
{
|
|
int count = 0;
|
|
for( int i = 0; layout[i].key; ++i ) {
|
|
if( layout[i].type == &json_field_inline_sublayout ) {
|
|
count += count_required_fields( layout[i].sublayout );
|
|
} else if( layout[i].required ) {
|
|
count += 1;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}*/
|
|
|
|
void debug_json_type_data( struct json_reflection* json_type_data, void* field_data, int offset )
|
|
{
|
|
DEBUG_printf( "layout_data = { .key = %s, .offset = %d, .required = %c, .type_string = %s, ... }, field_data=%p\n",
|
|
json_type_data->key,
|
|
offset,
|
|
json_type_data->required ? 'T' : 'F',
|
|
json_type_data->type->type_string,
|
|
field_data
|
|
);
|
|
}
|
|
|
|
static bool reader_deserialize( struct json_pull_parser* jpp, struct json_value* value, void* field_data, struct json_reflection* json_type_data, int offset )
|
|
{
|
|
if( jpp ) {
|
|
DEBUG_printf( "Using pull parser...\n" );
|
|
if( !json_type_data->type->reader ) {
|
|
printf( "! Missing `reader' field for type %s (%p) used for key %s\n",
|
|
json_type_data->type->type_string, json_type_data->type,
|
|
json_type_data->key
|
|
);
|
|
__builtin_trap();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if( json_type_data->type->reader( jpp, field_data, json_type_data, offset ) ) {
|
|
DEBUG_printf( "read value for %s (%s) success\n", json_type_data->key, json_type_data->type->type_string );
|
|
return true;
|
|
} else {
|
|
DEBUG_printf( "Failed to read value for %s (%s)\n", json_type_data->key, json_type_data->type->type_string );
|
|
return false;
|
|
}
|
|
} else if( value ) {
|
|
if( !json_type_data->type->deserialize ) {
|
|
DEBUG_printf( "No deserialize, creating dummy pull parser\n" );
|
|
// Hack to make this work
|
|
// TODO: rework json_pull_parser to allow reading directly from value without
|
|
// needing to serialize to string
|
|
|
|
// Create dummy file with data
|
|
char* data = NULL;
|
|
size_t size = 0;
|
|
FILE* f = open_memstream(&data,&size);
|
|
struct json_writer jw = {
|
|
.f = f,
|
|
.indentation = "\t",
|
|
.indent = 0,
|
|
};
|
|
json_write_value( &jw, value );
|
|
fprintf( f, "\n" ); // TODO: this should not be required to parse correctly
|
|
fclose(f);
|
|
f = fmemopen( data, size, "r" );
|
|
|
|
DEBUG_printf( "Handling %s\n", json_type_data->key );
|
|
DEBUG_printf( "data: %s\n", data );
|
|
|
|
// Create a new pull parser with string data from value
|
|
struct json_pull_parser* jpp2 = json_pull_parser_new( f );
|
|
if( !jpp2 ) {
|
|
printf( "! Failed to create pull parser\n" );
|
|
return false;
|
|
}
|
|
|
|
// Read the value
|
|
bool result = reader_deserialize( jpp2, NULL, field_data, json_type_data, offset );
|
|
DEBUG_printf( "result=%c\n\n", result ? 'T' : 'F' );
|
|
|
|
// Cleanup
|
|
free(data);
|
|
json_pull_parser_release(jpp2);
|
|
return result;
|
|
}
|
|
if( json_type_data->type->deserialize( *value, field_data, json_type_data, offset ) ) {
|
|
DEBUG_printf( "read value for %s (extra)\n", json_type_data->key );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool unified_handle_layout_field( struct json_pull_parser* jpp, struct json_value* value, const char* key, struct json_layout* layout, void* data, bool** has_field, bool allow_extra )
|
|
{
|
|
for( int i = 0; i < layout->count; ++i ) {
|
|
int offset = LAYOUT_GET_OFFSET( layout, i );
|
|
struct json_reflection* json_type_data = LAYOUT_GET_REFLECT( layout, i );
|
|
|
|
void* field_data = &((char*)data)[offset];
|
|
|
|
if( json_type_data->type == &json_field_inline_sublayout ) {
|
|
DEBUG_printf( "inline layout\n" );
|
|
struct json_layout sublayout;
|
|
json_layout_convert_legacy( json_type_data->sublayout, &sublayout );
|
|
|
|
if( unified_handle_layout_field( jpp, value, key, &sublayout, field_data, has_field, allow_extra ) ) {
|
|
return true;
|
|
}
|
|
DEBUG_printf( "no match\n" );
|
|
} else if( json_type_data->type == &json_field_extra ) {
|
|
if( allow_extra ) {
|
|
DEBUG_printf( "extra layout\n" );
|
|
|
|
json_type_data->key = key;
|
|
DEBUG_json_type_data( json_type_data, field_data, offset );
|
|
|
|
if( reader_deserialize( jpp, value, field_data, json_type_data, offset ) ) {
|
|
DEBUG_printf( "read value for %s\n", key );
|
|
**has_field = true;
|
|
return true;
|
|
}
|
|
}
|
|
} else if( 0 == strcmp( key, json_type_data->key ) ) {
|
|
|
|
DEBUG_json_type_data( json_type_data, field_data, offset );
|
|
|
|
if( reader_deserialize( jpp, value, field_data, json_type_data, offset ) ) {
|
|
DEBUG_printf( "read value for %s, *has_field=%p\n", key, *has_field );
|
|
**has_field = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if( json_type_data->required ) {
|
|
*has_field += 1;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool read_layout_with_downcast( struct json_pull_parser* jpp, struct json_layout* layout, void** data, json_layout_downcast_cb downcast );
|
|
|
|
bool json_read_object_layout_with_downcast( struct json_pull_parser* jpp, struct json_object_field* layout, void** data, json_layout_downcast_cb downcast )
|
|
{
|
|
struct json_layout new_layout;
|
|
json_layout_convert_legacy( layout, &new_layout );
|
|
|
|
return read_layout_with_downcast( jpp, &new_layout, data, downcast );
|
|
}
|
|
bool json_read_reflection_with_downcast( struct json_pull_parser* jpp, struct reflection_list* reflect, void** data, json_layout_downcast_cb downcast )
|
|
{
|
|
struct json_layout layout;
|
|
layout.count = reflect->count;
|
|
layout.stride = reflect->stride;
|
|
layout.first_offset = (int*)reflect_get( reflect, reflect_offset );
|
|
layout.first_reflection = (struct json_reflection*)reflect_get( reflect, reflect_json );
|
|
|
|
return read_layout_with_downcast( jpp, &layout, data, downcast );
|
|
}
|
|
|
|
static bool read_layout_with_downcast( struct json_pull_parser* jpp, struct json_layout* layout, void** data, json_layout_downcast_cb downcast )
|
|
{
|
|
int save;
|
|
char* key;
|
|
bool failed_feedback = false;
|
|
DEBUG_printf( "data=%p\n", data );
|
|
|
|
// Count the number of fields in the layout
|
|
int required_field_count = count_required_fields(layout);
|
|
|
|
// Setup bit array for if the field has been read
|
|
bool* has_field = alloca( sizeof(bool) * required_field_count );
|
|
memset( has_field, 0, sizeof(bool)*required_field_count );
|
|
|
|
DEBUG_printf( "required_field_count = %d\n", required_field_count );
|
|
|
|
if( !json_pull_parser_begin_object( jpp, &save ) ) {
|
|
return false;
|
|
}
|
|
|
|
// If there is a downcast callback, ...
|
|
if( downcast ) {
|
|
struct json_value buffer;
|
|
memset(&buffer,0,sizeof(buffer));
|
|
|
|
// ... buffer values until we get a successfull downcast ...
|
|
while(( key = json_pull_parser_read_object_key(jpp) )) {
|
|
DEBUG_printf( "Delaying field %s handling\n", key );
|
|
struct json_value value;
|
|
memset(&value,0,sizeof(value));
|
|
|
|
json_pull_parser_read_value(jpp,&value);
|
|
json_value_object_set( &buffer, key, value );
|
|
free(key);
|
|
|
|
if( downcast( buffer, data, &layout ) ) {
|
|
DEBUG_printf( "Downcast successful\n" );
|
|
goto feed_back;
|
|
}
|
|
}
|
|
|
|
DEBUG_printf( "No more fields to read...\n" );
|
|
|
|
// ... then handle the buffered key fields ...
|
|
if( !*data ) {
|
|
printf( "! No data allocated\n" );
|
|
return false;
|
|
}
|
|
feed_back:
|
|
// Recalculate the number of required fields
|
|
required_field_count = count_required_fields(layout);
|
|
has_field = alloca( sizeof(bool) * required_field_count );
|
|
memset( has_field, 0, sizeof(bool)*required_field_count );
|
|
|
|
for( int i = 0; i < buffer.o.count; ++i ) {
|
|
bool* has_field_iter = has_field;
|
|
char* key = buffer.o.items[i].key;
|
|
bool item_handled = unified_handle_layout_field( NULL, &buffer.o.items[i].value, key, layout, *data, &has_field_iter, false );
|
|
|
|
// Only handle extra fields after every other possible field has already been tried
|
|
if( !item_handled ) {
|
|
item_handled = unified_handle_layout_field( NULL, &buffer.o.items[i].value, key, layout, *data, &has_field_iter, true );
|
|
}
|
|
|
|
if( !item_handled ) {
|
|
if( jpp->flags & jrol_flag_dont_ignore_unhandled ) {
|
|
printf( "Key %s not handled, flags=0x%08X\n", key, jpp->flags );
|
|
goto has_failed_feedback;
|
|
} else {
|
|
if( !json_pull_parser_read_value(jpp,NULL) ) { goto has_failed_feedback; }
|
|
}
|
|
}
|
|
}
|
|
DEBUG_printf( "Feedback completed successfully\n" );
|
|
if( 0 ) {
|
|
has_failed_feedback:
|
|
failed_feedback = true;
|
|
}
|
|
json_value_free_composite(&buffer);
|
|
if( failed_feedback ) {
|
|
printf( "! Failed to feedback values\n" );
|
|
return false;
|
|
}
|
|
|
|
// ... then continue with the standard pull parser.
|
|
}
|
|
|
|
while(( key = json_pull_parser_read_object_key(jpp) )) {
|
|
DEBUG_printf( "key=%s\n", key );
|
|
|
|
bool* has_field_iter = has_field;
|
|
bool item_handled = unified_handle_layout_field( jpp, NULL, key, layout, *data, &has_field_iter, false );
|
|
|
|
// Only handle extra fields after every other possible field has already been tried
|
|
if( !item_handled ) {
|
|
item_handled = unified_handle_layout_field( jpp, NULL, key, layout, *data, &has_field_iter, true );
|
|
}
|
|
|
|
if( !item_handled && jpp->flags & jrol_flag_dont_ignore_unhandled ) {
|
|
printf( "Key %s not handled, flags=0x%08X\n", key, jpp->flags );
|
|
}
|
|
if( !item_handled ) {
|
|
if( jpp->flags & jrol_flag_dont_ignore_unhandled ) {
|
|
printf( "! missing required field %s\n", key );
|
|
free(key);
|
|
return false;
|
|
} else {
|
|
if( !json_pull_parser_read_value(jpp,NULL) ) {
|
|
printf( "! unable to discard excess value\n" );
|
|
free(key);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
free(key);
|
|
}
|
|
|
|
if( !json_pull_parser_end_object( jpp, &save ) ) {
|
|
printf( "Failed to close object!\n" );
|
|
return false;
|
|
}
|
|
|
|
for( int i = 0; i < required_field_count; ++i ) {
|
|
if( !has_field[i] ) {
|
|
int offset = i;
|
|
printf( "missing field[%d] '%s' &has_field[]=%p\n", i, get_required_field_name(layout,&offset), &has_field[i] );
|
|
return false;
|
|
} else {
|
|
#ifdef DEBUG
|
|
int offset = i;
|
|
#endif
|
|
DEBUG_printf( "present field[%d] '%s' &has_field[]=%p\n", i, get_required_field_name(layout,&offset), &has_field[i] );
|
|
}
|
|
}
|
|
|
|
DEBUG_printf( "parsed\n" );
|
|
return true;
|
|
}
|
|
bool json_read_object_layout( struct json_pull_parser* jpp, struct json_object_field* layout, void* data )
|
|
{
|
|
return json_read_object_layout_with_downcast( jpp, layout, &data, NULL );
|
|
}
|
|
bool json_read_object_layout_from_FILE_ex( FILE* f, struct json_object_field* layout, void** ptr, int flags, json_layout_downcast_cb downcast )
|
|
{
|
|
struct json_pull_parser* jpp = NULL;
|
|
bool result = false;
|
|
|
|
jpp = json_pull_parser_new( f );
|
|
if( !jpp ) { goto failed; }
|
|
jpp->flags = flags;
|
|
|
|
if( !json_read_object_layout_with_downcast( jpp, layout, ptr, downcast ) ) { goto failed; }
|
|
|
|
result = true;
|
|
cleanup:
|
|
json_pull_parser_release(jpp);
|
|
if( f ) { fclose(f); }
|
|
return result;
|
|
failed:
|
|
printf( "Failed to parse file. Remaining data:\n" );
|
|
if( f ) {
|
|
char ch;
|
|
while( (ch=fgetc(f)) != EOF ) {
|
|
printf( "%c", ch );
|
|
}
|
|
}
|
|
result = false;
|
|
goto cleanup;
|
|
}
|
|
bool json_read_object_layout_from_file_ex( const char* filename, struct json_object_field* layout, void* ptr, int flags )
|
|
{
|
|
FILE* f = NULL;
|
|
f = fopen( filename, "r" );
|
|
if( !f ) {
|
|
//printf( "Could not open file '%s'\n", filename );
|
|
return false;
|
|
}
|
|
|
|
return json_read_object_layout_from_FILE_ex( f, layout, &ptr, flags, NULL );
|
|
}
|
|
bool json_read_object_layout_from_FILE( FILE* f, struct json_object_field* layout, void* ptr )
|
|
{
|
|
return json_read_object_layout_from_FILE_ex( f, layout, &ptr, 0, NULL );
|
|
}
|
|
bool json_read_object_layout_from_file( const char* filename, struct json_object_field* layout, void* ptr )
|
|
{
|
|
return json_read_object_layout_from_file_ex( filename, layout, ptr, 0 );
|
|
}
|
|
|
|
|
|
void json_write_pretty_object_layout( struct json_writer* jw, struct json_object_field* layout, void* data )
|
|
{
|
|
struct json_layout new_layout;
|
|
json_layout_convert_legacy( layout, &new_layout );
|
|
json_write_pretty_layout( jw, &new_layout, data );
|
|
}
|
|
void json_write_pretty_layout( struct json_writer* jw, struct json_layout* layout, void* data )
|
|
{
|
|
FILE* f = jw->f;
|
|
int indent = jw->indent;
|
|
const char* indentation = jw->indentation;
|
|
|
|
fprintf( f, *indentation ? "{\n" : "{" );
|
|
|
|
jw->indent = (indent += 1);
|
|
bool old_need_comma = jw->need_comma;
|
|
jw->need_comma = false;
|
|
jw->need_indent = !!*indentation;
|
|
|
|
for( int i = 0; i < layout->count; ++i ) {
|
|
int offset = LAYOUT_GET_OFFSET( layout, i );
|
|
struct json_reflection* json_type_data = LAYOUT_GET_REFLECT( layout, i );
|
|
|
|
void* field_data = &((char*)(data))[ offset ];
|
|
DEBUG_json_type_data( json_type_data, field_data, offset );
|
|
|
|
if( !json_type_data->type->writer ) {
|
|
printf( "! Missing `writer' field for type %s (%p)\n", json_type_data->type->type_string, json_type_data->type );
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if( json_type_data->type->writer( jw, json_type_data->key, field_data, json_type_data, offset ) ) {
|
|
jw->need_comma = true;
|
|
}
|
|
}
|
|
if( *indentation ) {
|
|
fprintf( f, "\n" );
|
|
}
|
|
jw->indent = ( indent -= 1 );
|
|
jw->need_comma = old_need_comma;
|
|
jw->need_indent = true;
|
|
|
|
json_write_indention(jw);
|
|
fprintf( f, "}" );
|
|
}
|
|
void json_write_array( struct json_writer* jw, struct json_field_type* array_item_type, void* data )
|
|
{
|
|
struct json_reflection layout = {
|
|
.type = &json_field_array_of,
|
|
.array_item_type = array_item_type,
|
|
};
|
|
|
|
struct {
|
|
char* items;
|
|
int count;
|
|
} *array = data;
|
|
|
|
const char* indentation = jw->indentation;
|
|
|
|
FILE* f = jw->f;
|
|
|
|
fprintf( f, "[\n" );
|
|
jw->indent += 1;
|
|
bool old_need_comma = jw->need_comma;
|
|
bool old_need_indent = jw->need_indent;
|
|
jw->need_comma = false;
|
|
jw->need_indent = !!*indentation;
|
|
for( int i = 0; i < array->count; ++i ) {
|
|
json_write_indention(jw);
|
|
|
|
char* field_data = &array->items[ array_item_type->size * i ];
|
|
|
|
if( array_item_type->writer( jw, NULL, field_data, &layout, 0 ) ) {
|
|
jw->need_comma = true;
|
|
}
|
|
}
|
|
jw->need_comma = old_need_comma;
|
|
jw->need_indent = old_need_indent;
|
|
jw->indent -= 1;
|
|
if( *indentation ) {
|
|
fprintf( f, "\n" );
|
|
}
|
|
fprintf( f, "]\n" );
|
|
}
|
|
|
|
void json_write_object_layout( FILE* f, struct json_object_field* layout, void* data )
|
|
{
|
|
struct json_writer jw = {
|
|
.f = f,
|
|
.indent = 0,
|
|
.indentation = "",
|
|
};
|
|
json_write_pretty_object_layout( &jw, layout, data );
|
|
}
|
|
void json_write_object_layout_to_FILE( FILE* f, const char* indentation, struct json_object_field* layout, void* ptr )
|
|
{
|
|
struct json_writer jw = {
|
|
.f = f,
|
|
.indent = 0,
|
|
.indentation = indentation,
|
|
};
|
|
json_write_pretty_object_layout( &jw, layout, ptr );
|
|
}
|
|
void json_write_object_layout_to_file( const char* filename, const char* indentation, struct json_object_field* layout, void* ptr )
|
|
{
|
|
char tmp_filename[512+32];
|
|
snprintf( tmp_filename, 512+32, "%s.tmp", filename );
|
|
|
|
struct json_writer jw = {
|
|
.f = fopen( tmp_filename, "w" ),
|
|
.indent = 0,
|
|
.indentation = indentation,
|
|
};
|
|
json_write_pretty_object_layout( &jw, layout, ptr );
|
|
fclose(jw.f);
|
|
|
|
rename( tmp_filename, filename );
|
|
}
|
|
|