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

#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 );
}