Skip to content

A stab at API for patching one type into another #181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions include/dl/dl.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,77 @@ dl_error_t DL_DLL_EXPORT dl_instance_load_inplace( dl_ctx_t dl_ctx,
unsigned char* packed_instance, size_t packed_instance_size,
void** loaded_instance, size_t* consumed );

struct dl_patch_params; // Forward decl to avoid msvc warning 4115

/*
Function: dl_patch_func
Callback used by DL to patch one type into another, passed into 'dl_instance_patch()'

Parameters:
dl_ctx - the context which contains 'wanted_type' and all its target sub-types
patch_ctx - the user data context passed in with the patch function
patch_params - settings to control the patching
error_f - callback function to report errors with. Should only be used if the function knows how to produce the 'wanted_type' but failed to interpret the src data. Can be nullptr if no error function is registered
error_ctx - the context to pass into the 'error_f' error report function above

Return:
DL_ERROR_OK if this function successfully patched the data
*/
typedef dl_error_t (*dl_patch_func)( dl_ctx_t dl_ctx, void* patch_ctx, struct dl_patch_params* patch_params, dl_error_msg_handler error_f, void* error_ctx );

/*
Struct: dl_patch_params_t
Passed to dl_instance_patch with settings about patching.

Members:
patch_funcs - the function callbacks which will be called to try to patch the data
patch_ctxs - the contexts to pass into the function callbacks above, there need to be patch_func_count of them
patch_func_count - how many functions there are
packed_instance_as_input - 1 if the 'instance' is provided as packed data with a header, 0 if it is already unpacked
packed_instance_as_output - 1 if the 'out_instance' should be written as packed data with a header, 0 if it should be unpacked
input_type - the type of the input data, can be 0 if 'packed_instance_as_input' is true
wanted_type - the type to convert to, need to be known by the dl_ctx
instance - the data which should be patched
out_instance - a pointer to a buffer where to store the out_instance. Run this function twice and check 'used_out_instance_size' to see the needed size
out_instance_size - the size of the 'out_instance' buffer
used_out_instance_size - the needed/used size of the 'out_instance' buffer
*/
typedef struct dl_patch_params
{
dl_patch_func* patch_funcs;
void** patch_ctxs;
unsigned int patch_func_count;
uint8_t packed_instance_as_input : 1;
uint8_t packed_instance_as_output : 1;
dl_typeid_t input_type;
dl_typeid_t wanted_type;
const uint8_t* instance;
uint8_t* out_instance;
size_t out_instance_size;
size_t* used_out_instance_size;
} dl_patch_params_t;

/*
Function: dl_context_get_patch_func
Returns a callback function which tries to patch data from type ids stored in dl_ctx

Parameters:
dl_ctx - The dl context which contains the type ids which the function can patch from
patch_func - Output: A function pointer which will patch known types
patch_ctx - Output: A context pointer to pass in with the function pointer
*/
void DL_DLL_EXPORT dl_context_get_patch_func( dl_ctx_t dl_ctx, dl_patch_func* patch_func, void** patch_ctx );

/*
Function: dl_instance_patch
This function will try to patch a stale type definition to match the types in the current context

Parameters:
dl_ctx - the target dl context which contains the types to convert to
patch_params - settings to control the patching
*/
dl_error_t DL_DLL_EXPORT dl_instance_patch( dl_ctx_t dl_ctx, dl_patch_params_t* patch_params );

/*
Group: Store
*/
Expand Down
6 changes: 3 additions & 3 deletions src/dl_typelib_write_c_header.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ static dl_error_t dl_context_write_c_header_member( dl_binary_writer* writer, dl
dl_enum_info_t sub_type;
dl_error_t err = dl_reflect_get_enum_info( ctx, member->type_id, &sub_type );
if (DL_ERROR_OK != err) return err;
dl_binary_writer_write_string_fmt( writer, " %s %s;\n", sub_type.name, member->name );
dl_binary_writer_write_string_fmt( writer, " DL_C_ENUM %s %s;\n", sub_type.name, member->name );
}
break;
case DL_TYPE_STORAGE_ENUM_INT64:
Expand All @@ -627,7 +627,7 @@ static dl_error_t dl_context_write_c_header_member( dl_binary_writer* writer, dl
dl_enum_info_t sub_type;
dl_error_t err = dl_reflect_get_enum_info( ctx, member->type_id, &sub_type );
if (DL_ERROR_OK != err) return err;
dl_binary_writer_write_string_fmt( writer, " DL_ALIGN(8) %s %s;\n", sub_type.name, member->name );
dl_binary_writer_write_string_fmt( writer, " DL_ALIGN(8) DL_C_ENUM %s %s;\n", sub_type.name, member->name );
}
break;
default:
Expand Down Expand Up @@ -655,7 +655,7 @@ static dl_error_t dl_context_write_c_header_member( dl_binary_writer* writer, dl
dl_binary_writer_write_string_fmt(writer, " DL_DECLARE_ARRAY(");
dl_error_t err = dl_context_write_operator_array_access_type(ctx, member->storage, member->type_id, writer);
if (DL_ERROR_OK != err) return err;
dl_binary_writer_write_string_fmt(writer, ") %s;\n ", member->name);
dl_binary_writer_write_string_fmt(writer, ") %s;\n", member->name);
}
break;
case DL_TYPE_ATOM_INLINE_ARRAY:
Expand Down
59 changes: 59 additions & 0 deletions tests/dl_tests_struct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include <gtest/gtest.h>
#include "dl_tests_base.h"
#include <dl/dl_txt.h>
#include <dl/dl_typelib.h>

TYPED_TEST(DLBase, pods)
{
Expand Down Expand Up @@ -115,3 +117,60 @@ TYPED_TEST(DLBase, struct_in_struct_in_struct)
EXPECT_EQ(original.p2struct.Pod2.Int1, loaded.p2struct.Pod2.Int1);
EXPECT_EQ(original.p2struct.Pod2.Int2, loaded.p2struct.Pod2.Int2);
}

static dl_error_t PatchPods4_PatchFunction( dl_ctx_t dl_ctx, void*, dl_patch_params* patch_params, dl_error_msg_handler error_f, void* error_ctx )
{
(void)dl_ctx;
(void)error_f;
(void)error_ctx;
if (patch_params->input_type == PodsDefaults_TYPE_ID && patch_params->wanted_type == PatchPods4_TYPE_ID)
{
// Do conversion here
return DL_ERROR_OK;
}
return DL_ERROR_TYPE_NOT_FOUND;
}

TEST_F(DL, struct_patch)
{
dl_patch_func patch_func;
void* patch_ctx;
dl_context_get_patch_func( Ctx, &patch_func, &patch_ctx );

const char typelib[] = {
#include "generated/unittest2.txt.h"
};

const char* text_data = STRINGIFY( { "PodsDefaults" : {} } );
unsigned char instance[1024];
EXPECT_DL_ERR_OK(dl_txt_pack(Ctx, text_data, instance, sizeof(instance), 0x0));

dl_ctx_t target_ctx;
dl_create_params_t p;
DL_CREATE_PARAMS_SET_DEFAULT(p);
EXPECT_DL_ERR_OK(dl_context_create( &target_ctx, &p ));
EXPECT_DL_ERR_OK(dl_context_load_txt_type_library( target_ctx, typelib, sizeof(typelib) - 1 ));

size_t needed_size;
uint8_t out_instance[1024];
dl_patch_params patch_params{ &patch_func, &patch_ctx, 1, true, false, 0, 0, instance, out_instance, sizeof(out_instance), &needed_size };

// Change order
patch_params.wanted_type = PatchPods1_TYPE_ID;
EXPECT_DL_ERR_OK(dl_instance_patch( target_ctx, &patch_params ));
// Remove and add members
//patch_params.wanted_type = PatchPods2_TYPE_ID;
//EXPECT_DL_ERR_OK(dl_instance_patch( target_ctx, &patch_params ));
// Change types
patch_params.wanted_type = PatchPods3_TYPE_ID;
EXPECT_DL_ERR_OK(dl_instance_patch( target_ctx, &patch_params ));
// Advanced type change
patch_params.wanted_type = PatchPods4_TYPE_ID;
EXPECT_DL_ERR_EQ(DL_ERROR_TYPE_NOT_FOUND, dl_instance_patch( target_ctx, &patch_params ));

patch_func = PatchPods4_PatchFunction;
patch_params.wanted_type = PatchPods4_TYPE_ID;
EXPECT_DL_ERR_OK(dl_instance_patch( target_ctx, &patch_params ));

EXPECT_DL_ERR_OK(dl_context_destroy( target_ctx ));
}
58 changes: 58 additions & 0 deletions tests/unittest2.tld
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,64 @@
"members" : [
{ "name" : "members", "type" : "complex_member[]" }
]
},
"PatchPods1" : {
// Change order
"members" : [
{ "name" : "i64", "type" : "int64" },
{ "name" : "i32", "type" : "int32" },
{ "name" : "i16", "type" : "int16" },
{ "name" : "i8", "type" : "int8" },
{ "name" : "u64", "type" : "uint64" },
{ "name" : "u32", "type" : "uint32" },
{ "name" : "u16", "type" : "uint16" },
{ "name" : "u8", "type" : "uint8" },
{ "name" : "f64", "type" : "fp64" },
{ "name" : "f32", "type" : "fp32" }
]
},
"PatchPods2" : {
// Remove and add members
"members" : [
{ "name" : "i8", "type" : "int8" },
{ "name" : "i16", "type" : "int16" },
{ "name" : "i32", "type" : "int32" },
{ "name" : "u32", "type" : "uint32" },
{ "name" : "u64", "type" : "uint64" },
{ "name" : "f64", "type" : "fp64" },
{ "name" : "f64_2", "type" : "fp64", "default": 64.2 }
]
},
"PatchPods3" : {
// Change types
"members" : [
{ "name" : "i8", "type" : "fp32" },
{ "name" : "i16", "type" : "int32" },
{ "name" : "i32", "type" : "uint32" },
{ "name" : "i64", "type" : "fp64" },
{ "name" : "u8", "type" : "int16" },
{ "name" : "u16", "type" : "int8" },
{ "name" : "u32", "type" : "uint64" },
{ "name" : "u64", "type" : "fp32" },
{ "name" : "f32", "type" : "int64" },
{ "name" : "f64", "type" : "int64" }
]
},
"u16_t" : {
"members" : [
{ "name" : "u", "type" : "uint16" }
]
},
"PatchPods4" : {
// Advanced type change
"members" : [
{ "name" : "i8", "type" : "string" },
{ "name" : "i16", "type" : "u16_t*" },
{ "name" : "i32", "type" : "int32[1]" },
{ "name" : "i64", "type" : "int64[]" },
{ "name" : "u8", "type" : "extern_enum" },
{ "name" : "u16", "type" : "u16_t" }
]
}
}
}