near / borsh

Binary Object Representation Serializer for Hashing

Home Page:https://borsh.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Implement a customizable borsh_serialize in derive macros

ailisp opened this issue · comments

Currently we can't do much to affect how derive(BorshSerialize, Deserialize) works besides borsh_skip, however one case is very useful. Consider this big structure in wasmer:

pub struct ModuleInfo {
    pub memories: Map<LocalMemoryIndex, MemoryDescriptor>,
    pub globals: Map<LocalGlobalIndex, GlobalInit>,
    pub tables: Map<LocalTableIndex, TableDescriptor>,
    pub imported_functions: Map<ImportedFuncIndex, ImportName>,
    pub imported_memories: Map<ImportedMemoryIndex, (ImportName, MemoryDescriptor)>,
    pub imported_tables: Map<ImportedTableIndex, (ImportName, TableDescriptor)>,
    pub imported_globals: Map<ImportedGlobalIndex, (ImportName, GlobalDescriptor)>,
    pub exports: IndexMap<String, ExportIndex>,
    pub data_initializers: Vec<DataInitializer>,
    pub elem_initializers: Vec<TableInitializer>,
    pub start_func: Option<FuncIndex>,
    pub func_assoc: Map<FuncIndex, SigIndex>,
    pub signatures: Map<SigIndex, FuncSig>,
    pub backend: String,
    pub namespace_table: StringTable<NamespaceIndex>,
    pub name_table: StringTable<NameIndex>,
    pub em_symbol_map: Option<HashMap<u32, String>>,
    pub custom_sections: HashMap<String, Vec<Vec<u8>>>,
    pub generate_debug_info: bool,
    #[borsh_skip]
    pub(crate) debug_info_manager: jit_debug::JitCodeDebugInfoManager,
}

Every fields in this giant struct can derive BorshSerialize and BorshDeserialize, except one: IndexMap<String, ExportIndex>, because IndexMap isn't a type defined in this crate, nor it's a type defined in std or borsh, so you cannot
impl BorshSerialize for IndexMap, but due to this one field, you cannot derive BorshSerialize of the giant struct. There's two workaround of this:

  1. make pub exports: IndexMap<String, ExportIndex>, to pub exports: ExportsMap and define a struct ExportsMap enclosing IndexMap, so you can impl BorshSerialize/Deserialize on ExportsMap and make ModuleInfo Borsh-derivable. But this cause any reference to exports become exports.inner or exports.0
  2. implement BorshSerialize/Deserialize on ModuleInfo manually.
    Either one is a big inconvenience or cause structural change hacks. So i propose a borsh_serializer/borsh_deserializer macro:
fn borsh_serialize_index_map<K:BorshSerialize, V:BorshSerialize, W: Write>(index_map: &IndexMap<K,V>, writer: &mut W) -> std::io::Result<()> {
...
}

#[borsh_serializer(borsh_serialize_index_map)]
#[borsh_deserializer(borsh_deserialize_index_map)]
pub exports: IndexMap<String, ExportIndex>,

With help of these macros, user can specify a customize borsh serializer/deserializer to a field of struct, making the whole struct borsh-derivable

I think it would be nice to have the same API as serde for familiarity. They have an option for choosing a module for the functions to avoid having to specify both individually, as well as options for each specifically.