diff --git a/README.md b/README.md index e211785..c82ed4e 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,18 @@ what the rust project calls it) encode the discriminant. Set to 1 for enums which will never have more than 256 fields. Set to 2 for bigger enums. If you ever need an enum to have more than 65536 fields, set it to 4. +1.5: The WithSchema::schema function now takes a context object. You can just pass this through for +most data types. Only smart pointers, containers, Box etc need ot use this, to guard against +recursion. See documentation of WithSchemaContext. + +1.6: Several savefile trait implementations have now gained 'static-bounds. For example, +Box, Vec and many more now require T:'static. There was no such bound before, but +since references cannot be deserialized, it was still typically not possible to deserialize +anything containing a reference. In principle, someone could have implemented Deserialize +for some type, leaking memory and returning an instance with a non-static lifetime. However, +this is a very niche use, and it seems much more likely that deserializing types with arbitrary +lifetimes is an error. Please file an issue if you have an use-case for deserializing types +with lifetimes. ## 0.17.0-beta.13 diff --git a/savefile-abi/src/lib.rs b/savefile-abi/src/lib.rs index 7d6a7cb..abd939b 100644 --- a/savefile-abi/src/lib.rs +++ b/savefile-abi/src/lib.rs @@ -1174,7 +1174,7 @@ impl AbiConnection { }); } let mut mask = 0; - let mut check_diff = |effective1, effective2, native1, native2, index: Option| { + let mut verify_compatibility = |effective1, effective2, native1, native2, index: Option| { let effective_schema_diff = diff_schema(effective1, effective2, "".to_string()); if let Some(diff) = effective_schema_diff { return Err(SavefileError::IncompatibleSchema { @@ -1192,9 +1192,6 @@ impl AbiConnection { }); } - //let caller_isref = caller_native_method.info.arguments[index].can_be_sent_as_ref; - //let callee_isref = callee_native_method.info.arguments[index].can_be_sent_as_ref; - let comp = arg_layout_compatible(native1, native2, effective1, effective2, effective_version); if comp { @@ -1210,10 +1207,10 @@ impl AbiConnection { let effective2 = &callee_effective_method.info.arguments[index].schema; let native1 = &caller_native_method.info.arguments[index].schema; let native2 = &callee_native_method.info.arguments[index].schema; - check_diff(effective1, effective2, native1, native2, Some(index))?; + verify_compatibility(effective1, effective2, native1, native2, Some(index))?; } - check_diff( + verify_compatibility( &caller_effective_method.info.return_value, &callee_effective_method.info.return_value, &caller_native_method.info.return_value, @@ -1322,6 +1319,23 @@ impl AbiConnection { Self::from_raw(packed.entry, packed.trait_object, owning) } + /// Check if the given argument 'arg' in method 'method' is memory compatible such that + /// it will be sent as a reference, not copied. This will depend on the memory layout + /// of the code being called into. It will not change during the lifetime of an + /// AbiConnector, but it may change if the target library is recompiled. + pub fn get_arg_passable_by_ref(&self, method: &str, arg: usize) -> bool { + if let Some(found) = self.template.methods.iter().find(|var|var.method_name == method) { + let abi_method: &AbiConnectionMethod = found; + if arg >= abi_method.caller_info.arguments.len() { + panic!("Method '{}' has only {} arguments, so there is no argument #{}", method, abi_method.caller_info.arguments.len(), arg); + } + (abi_method.compatibility_mask & (1 << (arg as u64))) != 0 + } else { + let arg_names : Vec<_> = self.template.methods.iter().map(|x|x.method_name.as_str()).collect(); + panic!("Trait has no method with name '{}'. Available methods: {}", method, arg_names.join(", ")); + } + } + /// This routine is mostly for tests. /// It allows using a raw external API entry point directly. /// This is mostly useful for internal testing of the savefile-abi-library. diff --git a/savefile-derive/src/lib.rs b/savefile-derive/src/lib.rs index d52dcab..92a5a25 100644 --- a/savefile-derive/src/lib.rs +++ b/savefile-derive/src/lib.rs @@ -253,7 +253,7 @@ pub fn savefile_abi_exportable( let uses = quote_spanned! { defspan => extern crate savefile; extern crate savefile_abi; - use savefile::prelude::{ReprC, Schema, SchemaPrimitive, WithSchema, Serializer, Serialize, Deserializer, Deserialize, SavefileError, deserialize_slice_as_vec, ReadBytesExt,LittleEndian,AbiMethodArgument, AbiMethod, AbiMethodInfo,AbiTraitDefinition}; + use savefile::prelude::{ReprC, Schema, SchemaPrimitive, WithSchema, WithSchemaContext, Serializer, Serialize, Deserializer, Deserialize, SavefileError, deserialize_slice_as_vec, ReadBytesExt,LittleEndian,AbiMethodArgument, AbiMethod, AbiMethodInfo,AbiTraitDefinition}; use savefile_abi::{parse_return_value_impl,abi_result_receiver,abi_boxed_trait_receiver, FlexBuffer, AbiExportable, TraitObject, PackagedTraitObject, Owning, AbiErrorMsg, RawAbiCallResult, AbiConnection, AbiConnectionMethod, AbiProtocol, abi_entry_light}; use std::collections::HashMap; use std::mem::MaybeUninit; @@ -1574,7 +1574,7 @@ fn implement_withschema( "The Removed type can only be used for removed fields. Use the savefile_version attribute." ); } - fields.push(quote_spanned!( span => #fields1.push(unsafe{#Field::unsafe_new(#name_str.to_string(), std::boxed::Box::new(<#field_type as #WithSchema>::schema(#local_version)), #offset)} ))); + fields.push(quote_spanned!( span => #fields1.push(unsafe{#Field::unsafe_new(#name_str.to_string(), std::boxed::Box::new(<#field_type as #WithSchema>::schema(#local_version, context)), #offset)} ))); } else { let mut version_mappings = Vec::new(); let offset = if field_to_version != u32::MAX { @@ -1589,7 +1589,7 @@ fn implement_withschema( // We don't supply offset in this case, deserialized type doesn't match field type version_mappings.push(quote!{ if #local_version >= #dt_from && local_version <= #dt_to { - #fields1.push(#Field ::new( #name_str.to_string(), std::boxed::Box::new(<#dt_field_type as #WithSchema>::schema(#local_version))) ); + #fields1.push(#Field ::new( #name_str.to_string(), std::boxed::Box::new(<#dt_field_type as #WithSchema>::schema(#local_version, context))) ); } }); } @@ -1598,7 +1598,7 @@ fn implement_withschema( #(#version_mappings)* if #local_version >= #field_from_version && #local_version <= #field_to_version { - #fields1.push(unsafe{#Field ::unsafe_new( #name_str.to_string(), std::boxed::Box::new(<#field_type as #WithSchema>::schema(#local_version)), #offset )} ); + #fields1.push(unsafe{#Field ::unsafe_new( #name_str.to_string(), std::boxed::Box::new(<#field_type as #WithSchema>::schema(#local_version, context)), #offset )} ); } )); } @@ -1831,7 +1831,7 @@ fn savefile_derive_crate_withschema(input: DeriveInput) -> TokenStream { #[allow(unused_mut)] #[allow(unused_comparisons, unused_variables)] - fn schema(version:u32) -> #Schema { + fn schema(version:u32, context: &mut _savefile::prelude::WithSchemaContext) -> #Schema { let local_version = version; #Schema::Enum ( @@ -1912,7 +1912,7 @@ fn savefile_derive_crate_withschema(input: DeriveInput) -> TokenStream { impl #impl_generics #withschema for #name #ty_generics #where_clause #extra_where { #[allow(unused_comparisons)] #[allow(unused_mut, unused_variables)] - fn schema(version:u32) -> #Schema { + fn schema(version:u32, context: &mut _savefile::prelude::WithSchemaContext) -> #Schema { let local_version = version; let mut fields1 = Vec::new(); #(#fields;)* ; diff --git a/savefile-derive/src/savefile_abi.rs b/savefile-derive/src/savefile_abi.rs index 544439d..13cfcb0 100644 --- a/savefile-derive/src/savefile_abi.rs +++ b/savefile-derive/src/savefile_abi.rs @@ -685,7 +685,7 @@ impl ArgType { caller_arg_serializer1: quote! { #arg_name.serialize(&mut serializer) }, - schema: quote!( <#arg_type as WithSchema>::schema(version) ), + schema: quote!( <#arg_type as WithSchema>::schema(version, context) ), known_size_align1: if compile_time_check_reprc(arg_type) { compile_time_size(arg_type) } else { @@ -938,7 +938,7 @@ pub(super) fn generate_method_definitions( //let can_be_sent_as_ref = instruction.can_be_sent_as_ref; metadata_arguments.push(quote! { AbiMethodArgument { - schema: #schema, + schema: { let mut context = WithSchemaContext::new(); let context = &mut context; #schema }, } }); if let Some(total_size) = &mut compile_time_known_size { @@ -975,7 +975,7 @@ pub(super) fn generate_method_definitions( let result_default; let return_ser_temp; if no_return { - return_value_schema = quote!(<() as WithSchema>::schema(0)); + return_value_schema = quote!(<() as WithSchema>::schema(0, &mut WithSchemaContext::new())); ret_deserializer = quote!(()); //Zero-sized, no deserialize actually needed ret_serialize = quote!(()); caller_return_type = quote!(()); @@ -1117,7 +1117,7 @@ pub(super) fn generate_method_definitions( AbiMethod { name: #method_name_str.to_string(), info: AbiMethodInfo { - return_value: #return_value_schema, + return_value: { let mut context = WithSchemaContext::new(); let context = &mut context; #return_value_schema}, arguments: vec![ #(#metadata_arguments,)* ], } } diff --git a/savefile-min-build/src/lib.rs b/savefile-min-build/src/lib.rs index 18c49d0..91af8c8 100644 --- a/savefile-min-build/src/lib.rs +++ b/savefile-min-build/src/lib.rs @@ -1,13 +1,26 @@ extern crate savefile_abi; extern crate savefile_derive; +use savefile_abi::AbiConnection; use savefile_derive::savefile_abi_exportable; #[savefile_abi_exportable(version = 0)] pub trait ExampleTrait { - fn get(&mut self) -> &'static str; + fn test_slices(&mut self, slice: &[u32]) -> u32 { + slice.iter().copied().sum() + } +} + +impl ExampleTrait for () { + } #[test] -fn dummy_test() {} +fn dummy_test() { + let boxed: Box = Box::new(()); + let conn = AbiConnection::from_boxed_trait(boxed).unwrap(); + + assert!( conn.get_arg_passable_by_ref("test_slices", 0) ); + //conn.test_slices(&[1,2,3,4]); +} diff --git a/savefile-test/src/cycles.rs b/savefile-test/src/cycles.rs new file mode 100644 index 0000000..e776978 --- /dev/null +++ b/savefile-test/src/cycles.rs @@ -0,0 +1,87 @@ +use assert_roundtrip; +use savefile::Removed; + +#[derive(Savefile, Debug, PartialEq)] +enum Tree { + Leaf, + Node(Box,Box) +} + +#[test] +pub fn test_cyclic() { + let example = Tree::Node(Box::new(Tree::Leaf), Box::new(Tree::Leaf)); + assert_roundtrip(example); + + let example = Tree::Node(Box::new(Tree::Node(Box::new(Tree::Leaf),Box::new(Tree::Leaf))), Box::new(Tree::Leaf)); + assert_roundtrip(example); +} + + +#[derive(Savefile, Debug, PartialEq)] +struct TreeNode { + tree: Box +} + +#[derive(Savefile, Debug, PartialEq)] +enum Tree2 { + Leaf(String), + Node(TreeNode) +} +#[test] +pub fn test_cyclic2() { + let example = Tree2::Node(TreeNode{tree: Box::new(Tree2::Leaf("hej".into()))}); + assert_roundtrip(example); +} +#[derive(Savefile, Debug, PartialEq)] +enum Version1LevelD { + Leaf, + Node(Box) +} + +#[derive(Savefile, Debug, PartialEq)] +enum Version1LevelC { + Leaf, + Node(Box) +} + +#[derive(Savefile, Debug, PartialEq)] +enum Version1LevelB { + Leaf(Box), + Node(Box) +} + +#[derive(Savefile, Debug, PartialEq)] +enum Version1LevelA { + Leaf, + Node(Box) +} + +#[derive(Savefile, Debug, PartialEq)] +enum Version2LevelC { + Leaf, + Node(Box) +} + +#[derive(Savefile, Debug, PartialEq)] +enum Version2LevelB { + Leaf(Box), + Node(Box) +} + +#[derive(Savefile, Debug, PartialEq)] +enum Version2LevelA { + Leaf, + Node(Box) +} + +#[test] +#[should_panic(expected = "Saved schema differs from in-memory schema for version 0. Error: At location [.Version1LevelA/Node/0Version1LevelB/Leaf/0Version1LevelC/Node/0Version1LevelD/Node/0]: In memory schema: , file schema: enum")] +fn cycles_vertest1() { + use assert_roundtrip_to_new_version; + assert_roundtrip_to_new_version( + Version1LevelA::Leaf, + 0, + Version2LevelA::Leaf, + 1, + ); +} \ No newline at end of file diff --git a/savefile-test/src/lib.rs b/savefile-test/src/lib.rs index adeec2f..25f9df9 100644 --- a/savefile-test/src/lib.rs +++ b/savefile-test/src/lib.rs @@ -460,6 +460,8 @@ pub fn assert_roundtrip_to_new_version< mod enum_variant_versioning; +mod cycles; + #[test] pub fn test_array_string() { use arrayvec::ArrayString; diff --git a/savefile-test/src/savefile_abi_test/advanced_datatypes_test.rs b/savefile-test/src/savefile_abi_test/advanced_datatypes_test.rs index 52f7102..7d5fc85 100644 --- a/savefile-test/src/savefile_abi_test/advanced_datatypes_test.rs +++ b/savefile-test/src/savefile_abi_test/advanced_datatypes_test.rs @@ -12,6 +12,7 @@ pub trait AdvancedTestInterface { fn clone_hashmap(&self, x: &HashMap) -> HashMap; fn return_trait_object(&self) -> Box; + fn test_slices(&mut self, slice: &[u32]) -> u32; fn return_boxed_closure(&self) -> Box u32>; fn return_boxed_closure2(&self) -> Box; @@ -52,11 +53,24 @@ impl AdvancedTestInterface for AdvancedTestInterfaceImpl { Box::new(|| {}) } + fn test_slices(&mut self, slice: &[u32]) -> u32 { + slice.iter().copied().sum() + } + fn many_callbacks(&mut self, x: &mut dyn FnMut(&dyn Fn(&dyn Fn() -> u32) -> u32) -> u32) -> u32 { x(&|y| y()) } } +#[test] +fn abi_test_slice() { + let boxed: Box = Box::new(AdvancedTestInterfaceImpl {}); + let mut conn = AbiConnection::from_boxed_trait(boxed).unwrap(); + + assert!( conn.get_arg_passable_by_ref("test_slices", 0) ); + assert_eq!(conn.test_slices(&[1,2,3,4]), 10); +} + #[test] fn test_trait_object_in_return_position() { let boxed: Box = Box::new(AdvancedTestInterfaceImpl {}); diff --git a/savefile-test/src/savefile_abi_test/argument_backward_compatibility.rs b/savefile-test/src/savefile_abi_test/argument_backward_compatibility.rs index 7bfc3b2..5927c96 100644 --- a/savefile-test/src/savefile_abi_test/argument_backward_compatibility.rs +++ b/savefile-test/src/savefile_abi_test/argument_backward_compatibility.rs @@ -1,5 +1,5 @@ use savefile::prelude::AbiRemoved; -use savefile::SavefileError; +use savefile::{SavefileError, WithSchemaContext}; use savefile_abi::RawAbiCallResult::AbiError; use savefile_abi::{verify_compatiblity, AbiConnection, AbiExportable}; use savefile_abi_test::argument_backward_compatibility::v1::{ArgInterfaceV1, EnumArgument, Implementation1}; @@ -98,7 +98,7 @@ pub fn test_backward_compatibility() -> Result<(), SavefileError> { #[test] pub fn test_arg_argument_metadata() { use savefile::WithSchema; - let schema = v2::ArgArgument::schema(0); + let schema = v2::ArgArgument::schema(0, &mut WithSchemaContext::new()); println!("Schema: {:#?}", schema); assert!(!schema.layout_compatible(&schema)); //Versions containing removed items should never be considered layout compatible (since their schema type is not identical to the memory type) } diff --git a/savefile-test/src/savefile_abi_test/basic_abi_tests.rs b/savefile-test/src/savefile_abi_test/basic_abi_tests.rs index 85b97d6..c34dbbe 100644 --- a/savefile-test/src/savefile_abi_test/basic_abi_tests.rs +++ b/savefile-test/src/savefile_abi_test/basic_abi_tests.rs @@ -24,6 +24,7 @@ pub trait TestInterface { fn do_mut_nothing(&mut self); + fn deref_u32(&self, x: &u32) -> u32; fn count_chars(&self, x: &String) -> usize; fn count_chars_str(&self, x: &str) -> usize; @@ -118,6 +119,10 @@ impl TestInterface for TestInterfaceImpl { fn get_static_str(&self) -> &'static str { "hello world" } + + fn deref_u32(&self, x: &u32) -> u32 { + *x + } } savefile_abi_export!(TestInterfaceImpl, TestInterface); @@ -139,7 +144,11 @@ fn test_basic_call_abi() { assert_eq!(conn.count_chars(&"hejsan".to_string()), 6); assert_eq!(conn.count_chars_str("hejsan"), 6); + assert!(conn.get_arg_passable_by_ref("count_chars", 0)); assert_eq!(conn.get_static_str(), "hello world"); + + assert_eq!(conn.deref_u32(&42), 42); + assert!(conn.get_arg_passable_by_ref("deref_u32", 0)); } #[test] diff --git a/savefile/src/lib.rs b/savefile/src/lib.rs index c676036..b93768e 100644 --- a/savefile/src/lib.rs +++ b/savefile/src/lib.rs @@ -663,7 +663,7 @@ This can be fixed with manual padding: } use savefile::prelude::*; impl WithSchema for MyPathBuf { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_string((Default::default()))) } } @@ -1255,7 +1255,7 @@ impl From> for SavefileError { } impl WithSchema for PathBuf { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_string(VecOrStringLayout::Unknown)) } } @@ -1282,8 +1282,8 @@ impl Introspect for PathBuf { } impl<'a, T: 'a + WithSchema + ToOwned + ?Sized> WithSchema for Cow<'a, T> { - fn schema(version: u32) -> Schema { - T::schema(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + T::schema(version, context) } } impl<'a, T: 'a + ToOwned + ?Sized> ReprC for Cow<'a, T> {} @@ -1785,7 +1785,7 @@ impl<'a, W: Write + 'a> Serializer<'a, W> { writer, version, data, - Some(T::schema(version)), + Some(T::schema(version, &mut WithSchemaContext::new())), with_compression, )?) } @@ -2014,7 +2014,7 @@ impl<'a, TR: Read> Deserializer<'a, TR> { /// Don't use this method directly, use the [crate::load] function /// instead. pub fn load(reader: &mut TR, version: u32) -> Result { - Deserializer::<_>::load_impl::(reader, version, Some(|version| T::schema(version))) + Deserializer::<_>::load_impl::(reader, version, Some(|version| T::schema(version, &mut WithSchemaContext::new()))) } /// Deserialize an object of type T from the given reader. @@ -2232,6 +2232,62 @@ pub fn save_file_noschema>( Serializer::save_noschema::(&mut f, version, data) } + +/// Context object used to keep track of recursion. +/// Datastructures which cannot contain recursion do not need to concern themselves with +/// this. Recursive data structures in rust require the use of Box, Vec, Arc or similar. +/// The most common of these datatypes from std are supported by savefile, and will guard +/// against recursion in a well-defined way. +/// As a user of Savefile, you only need to use this if you are implementing Savefile for +/// container or smart-pointer type. +#[derive(Default)] +pub struct WithSchemaContext { + seen_types: HashMap, +} + +impl WithSchemaContext { + /// Create a new empty WithSchemaContext. + /// This is useful for calling ::schema at the top-level. + pub fn new() -> WithSchemaContext { + Default::default() + } +} + +impl WithSchemaContext { + /// Use this when returning the schema of a type that can be part of a recursion. + /// For example, given a hypothetical user-implemented type MyBox, do + /// + /// ```rust + /// use savefile::{Schema, WithSchema, WithSchemaContext}; + /// #[transparent] + /// struct MyBox { + /// content: *const T + /// } + /// impl WithSchema for MyBox { + /// fn schema(version: u32, context: &mut WithSchemaContext, context: &mut WithSchemaContext) -> Schema { + /// context.possible_recursion::>(|context| Schema::Boxed(T::schema(version, context))) + /// } + /// + /// } + /// ``` + pub fn possible_recursion(&mut self, cb: impl FnOnce(&mut WithSchemaContext) -> Schema) -> Schema { + let typeid = TypeId::of::(); + let prevlen = self.seen_types.len(); + match self.seen_types.entry(typeid) { + Entry::Occupied(occ) => { + let present_value_depth = *occ.get(); + return Schema::Recursion( prevlen - present_value_depth ); + } + Entry::Vacant(vac) => { + vac.insert(prevlen); + } + } + let ret = (cb)(self); + self.seen_types.remove(&typeid); + ret + } +} + /// This trait must be implemented by all data structures you wish to be able to save. /// It must encode the schema for the datastructure when saved using the given version number. /// When files are saved, the schema is encoded into the file. @@ -2241,7 +2297,7 @@ pub fn save_file_noschema>( /// can be disabled). pub trait WithSchema { /// Returns a representation of the schema used by this Serialize implementation for the given version. - fn schema(version: u32) -> Schema; + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema; } /// This trait must be implemented for all data structures you wish to be @@ -2640,6 +2696,7 @@ impl SchemaEnum { } } fn layout_compatible(&self, other: &SchemaEnum) -> bool { + if self.has_explicit_repr == false || other.has_explicit_repr == false { return false; } @@ -2825,7 +2882,7 @@ impl Deserialize for AbiMethodArgument { } impl WithSchema for AbiMethodArgument { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } @@ -2848,7 +2905,7 @@ pub struct AbiMethodInfo { } impl ReprC for AbiMethodInfo {} impl WithSchema for AbiMethodInfo { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } @@ -2881,7 +2938,7 @@ pub struct AbiMethod { } impl ReprC for AbiMethod {} impl WithSchema for AbiMethod { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } @@ -2912,7 +2969,7 @@ pub struct AbiTraitDefinition { } impl ReprC for AbiTraitDefinition {} impl WithSchema for AbiTraitDefinition { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } @@ -3065,6 +3122,13 @@ pub enum Schema { /// Their memory layout is considered to depend on all method signatures, /// and the layouts of all argument types and all return types. FnClosure(bool /*mut self*/, AbiTraitDefinition), + /// The datastructure is recursive, and the datatype now continues from + /// the element that is 'depth' layers higher in the schema tree. + /// Note, the 'depth' only counts possible recursion points, i.e, objects + /// such as 'Box', 'Vec' etc. This works, since the schema will only ever match + /// if it is identical in memory and file, and because of this, counting + /// only the recursion points is non-ambiguous. + Recursion(usize/*depth*/) } /// Introspect is not implemented for Schema, though it could be impl Introspect for Schema { @@ -3080,23 +3144,24 @@ impl Introspect for Schema { impl Schema { /// Get a short description of the major type of this schema. /// 'struct', 'enum' etc. - pub fn top_level_description(&self) -> &'static str { + pub fn top_level_description(&self) -> String { match self { - Schema::Struct(_) => "struct", - Schema::Enum(_) => "enum", - Schema::Primitive(_) => "primitive", - Schema::Vector(_, _) => "vector", - Schema::Array(_) => "array", - Schema::SchemaOption(_) => "option", - Schema::Undefined => "undefined", - Schema::ZeroSize => "zerosize", - Schema::Custom(_) => "custom", - Schema::Boxed(_) => "box", - Schema::FnClosure(_, _) => "fntrait", - Schema::Slice(_) => "slice", - Schema::Str => "str", - Schema::Reference(_) => "reference", - Schema::Trait(_, _) => "trait", + Schema::Struct(_) => "struct".into(), + Schema::Enum(_) => "enum".into(), + Schema::Primitive(_) => "primitive".into(), + Schema::Vector(_, _) => "vector".into(), + Schema::Array(_) => "array".into(), + Schema::SchemaOption(_) => "option".into(), + Schema::Undefined => "undefined".into(), + Schema::ZeroSize => "zerosize".into(), + Schema::Custom(_) => "custom".into(), + Schema::Boxed(_) => "box".into(), + Schema::FnClosure(_, _) => "fntrait".into(), + Schema::Slice(_) => "slice".into(), + Schema::Str => "str".into(), + Schema::Reference(_) => "reference".into(), + Schema::Trait(_, _) => "trait".into(), + Schema::Recursion(depth) => {format!("",depth)} } } /// Determine if the two fields are laid out identically in memory, in their parent objects. @@ -3124,18 +3189,21 @@ impl Schema { // Closures are not supported in any other position false } - (Schema::Boxed(_a), Schema::Boxed(_b)) => { - // Boxed traits can never "just be serialized". We always have to serialize - // if boxed traits are contained in a data structure - false + (Schema::Boxed(a), Schema::Boxed(b)) => { + // The memory layout of boxes is guaranteed in practice (just a pointer) + // Trait pointers (which are fat) could conceivably differ, but we don't + // actually rely on memory layout compatibility for them, and this expression + // will also return false (since Schema::Trait 'layout_compatible' always returns false). + a.layout_compatible(&*b) } (Schema::Reference(a), Schema::Reference(b)) => a.layout_compatible(&*b), + (Schema::Slice(a), Schema::Slice(b)) => a.layout_compatible(&*b), _ => false, } } /// Create a 1-element tuple - pub fn new_tuple1(version: u32) -> Schema { - let schema = Box::new(T1::schema(version)); + pub fn new_tuple1(version: u32, context: &mut WithSchemaContext) -> Schema { + let schema = Box::new(T1::schema(version, context)); Schema::Struct(SchemaStruct { dbg_name: "1-Tuple".to_string(), size: Some(std::mem::size_of::<(T1,)>()), @@ -3149,7 +3217,7 @@ impl Schema { } /// Create a 2-element tuple - pub fn new_tuple2(version: u32) -> Schema { + pub fn new_tuple2(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Struct(SchemaStruct { dbg_name: "2-Tuple".to_string(), size: Some(std::mem::size_of::<(T1, T2)>()), @@ -3157,19 +3225,19 @@ impl Schema { fields: vec![ Field { name: "0".to_string(), - value: Box::new(T1::schema(version)), + value: Box::new(T1::schema(version, context)), offset: Some(offset_of_tuple!((T1, T2), 0)), }, Field { name: "1".to_string(), - value: Box::new(T2::schema(version)), + value: Box::new(T2::schema(version, context)), offset: Some(offset_of_tuple!((T1, T2), 1)), }, ], }) } /// Create a 3-element tuple - pub fn new_tuple3(version: u32) -> Schema { + pub fn new_tuple3(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Struct(SchemaStruct { dbg_name: "3-Tuple".to_string(), size: Some(std::mem::size_of::<(T1, T2, T3)>()), @@ -3177,24 +3245,24 @@ impl Schema { fields: vec![ Field { name: "0".to_string(), - value: Box::new(T1::schema(version)), + value: Box::new(T1::schema(version, context)), offset: Some(offset_of_tuple!((T1, T2, T3), 0)), }, Field { name: "1".to_string(), - value: Box::new(T2::schema(version)), + value: Box::new(T2::schema(version, context)), offset: Some(offset_of_tuple!((T1, T2, T3), 1)), }, Field { name: "2".to_string(), - value: Box::new(T3::schema(version)), + value: Box::new(T3::schema(version, context)), offset: Some(offset_of_tuple!((T1, T2, T3), 2)), }, ], }) } /// Create a 4-element tuple - pub fn new_tuple4(version: u32) -> Schema { + pub fn new_tuple4(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Struct(SchemaStruct { dbg_name: "4-Tuple".to_string(), size: Some(std::mem::size_of::<(T1, T2, T3, T4)>()), @@ -3202,22 +3270,22 @@ impl Schema { fields: vec![ Field { name: "0".to_string(), - value: Box::new(T1::schema(version)), + value: Box::new(T1::schema(version, context)), offset: Some(offset_of_tuple!((T1, T2, T3, T4), 0)), }, Field { name: "1".to_string(), - value: Box::new(T2::schema(version)), + value: Box::new(T2::schema(version, context)), offset: Some(offset_of_tuple!((T1, T2, T3, T4), 1)), }, Field { name: "2".to_string(), - value: Box::new(T3::schema(version)), + value: Box::new(T3::schema(version, context)), offset: Some(offset_of_tuple!((T1, T2, T3, T4), 2)), }, Field { name: "3".to_string(), - value: Box::new(T4::schema(version)), + value: Box::new(T4::schema(version, context)), offset: Some(offset_of_tuple!((T1, T2, T3, T4), 3)), }, ], @@ -3241,6 +3309,7 @@ impl Schema { Schema::Str => None, Schema::Reference(_) => None, Schema::Trait(_, _) => None, + Schema::Recursion(_) => None, } } } @@ -3409,6 +3478,16 @@ pub fn diff_schema(a: &Schema, b: &Schema, path: String) -> Option { } return diff_abi_def(a, b, path); } + (Schema::Recursion(adepth), Schema::Recursion(bdepth)) => { + if adepth == bdepth { + return None; //Ok + } else { + return Some(format!( + "At location [{}]: Application protocol uses recursion up {} levels, but foreign format uses {}.", + path, adepth, bdepth + )); + } + } (a, b) => (a.top_level_description(), b.top_level_description()), }; @@ -3445,7 +3524,7 @@ fn diff_abi_def(a: &AbiTraitDefinition, b: &AbiTraitDefinition, path: String) -> } impl WithSchema for Field { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } @@ -3473,7 +3552,7 @@ impl Deserialize for Field { } } impl WithSchema for Variant { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } @@ -3530,13 +3609,13 @@ impl Deserialize for SchemaArray { } } impl WithSchema for SchemaArray { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } impl WithSchema for SchemaStruct { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } @@ -3573,7 +3652,7 @@ impl Deserialize for SchemaStruct { } impl WithSchema for SchemaPrimitive { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } @@ -3607,7 +3686,7 @@ impl Serialize for SchemaPrimitive { } } impl WithSchema for VecOrStringLayout { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } @@ -3666,7 +3745,7 @@ impl Deserialize for SchemaPrimitive { } impl WithSchema for SchemaEnum { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } @@ -3849,7 +3928,7 @@ impl Arbitrary for Schema { } impl WithSchema for Schema { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Undefined } } @@ -3921,6 +4000,11 @@ impl Serialize for Schema { b.serialize(serializer)?; Ok(()) } + Schema::Recursion(depth) => { + serializer.write_u8(16)?; + serializer.write_usize(*depth)?; + Ok(()) + } } } } @@ -3957,6 +4041,7 @@ impl Deserialize for Schema { <_ as Deserialize>::deserialize(deserializer)?, <_ as Deserialize>::deserialize(deserializer)?, ), + 16 => Schema::Recursion(<_ as Deserialize>::deserialize(deserializer)?), c => { return Err(SavefileError::GeneralError { msg: format!("Corrupt schema, schema variant {} encountered", c), @@ -3969,7 +4054,7 @@ impl Deserialize for Schema { } impl WithSchema for String { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_string(calculate_string_memory_layout())) } } @@ -4064,8 +4149,8 @@ impl Introspect for std::sync::Mutex { } impl WithSchema for std::sync::Mutex { - fn schema(version: u32) -> Schema { - T::schema(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + T::schema(version, context) } } impl ReprC for std::sync::Mutex {} @@ -4084,8 +4169,8 @@ impl Deserialize for std::sync::Mutex { #[cfg(feature = "parking_lot")] impl WithSchema for Mutex { - fn schema(version: u32) -> Schema { - T::schema(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + T::schema(version, context) } } @@ -4215,8 +4300,8 @@ impl Introspect for RwLock { #[cfg(feature = "parking_lot")] impl WithSchema for RwLock { - fn schema(version: u32) -> Schema { - T::schema(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + T::schema(version, context) } } @@ -4374,7 +4459,7 @@ impl Introspect for BTreeMap { } } impl WithSchema for BTreeMap { - fn schema(version: u32) -> Schema { + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Vector( Box::new(Schema::Struct(SchemaStruct { dbg_name: "KeyValuePair".to_string(), @@ -4383,12 +4468,12 @@ impl WithSchema for BTreeMap { fields: vec![ Field { name: "key".to_string(), - value: Box::new(K::schema(version)), + value: Box::new(K::schema(version, context)), offset: None, }, Field { name: "value".to_string(), - value: Box::new(V::schema(version)), + value: Box::new(V::schema(version, context)), offset: None, }, ], @@ -4424,8 +4509,8 @@ impl Deserialize for BTreeMap { impl ReprC for HashSet {} impl WithSchema for HashSet { - fn schema(version: u32) -> Schema { - Schema::Vector(Box::new(K::schema(version)), VecOrStringLayout::Unknown) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::Vector(Box::new(K::schema(version, context)), VecOrStringLayout::Unknown) } } impl Serialize for HashSet { @@ -4449,7 +4534,7 @@ impl Deserial } impl WithSchema for HashMap { - fn schema(version: u32) -> Schema { + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Vector( Box::new(Schema::Struct(SchemaStruct { dbg_name: "KeyValuePair".to_string(), @@ -4458,12 +4543,12 @@ impl With fields: vec![ Field { name: "key".to_string(), - value: Box::new(K::schema(version)), + value: Box::new(K::schema(version, context)), offset: None, }, Field { name: "value".to_string(), - value: Box::new(V::schema(version)), + value: Box::new(V::schema(version, context)), offset: None, }, ], @@ -4499,7 +4584,7 @@ impl WithSchema for IndexMap { - fn schema(version: u32) -> Schema { + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Vector( Box::new(Schema::Struct(SchemaStruct { dbg_name: "KeyValuePair".to_string(), @@ -4508,12 +4593,12 @@ impl With fields: vec![ Field { name: "key".to_string(), - value: Box::new(K::schema(version)), + value: Box::new(K::schema(version, context)), offset: None, }, Field { name: "value".to_string(), - value: Box::new(V::schema(version)), + value: Box::new(V::schema(version, context)), offset: None, }, ], @@ -4657,7 +4742,7 @@ impl ReprC for IndexSet {} #[cfg(feature = "indexmap")] impl WithSchema for IndexSet { - fn schema(version: u32) -> Schema { + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Vector( Box::new(Schema::Struct(SchemaStruct { dbg_name: "Key".to_string(), @@ -4665,7 +4750,7 @@ impl WithSchema for Inde alignment: None, fields: vec![Field { name: "key".to_string(), - value: Box::new(K::schema(version)), + value: Box::new(K::schema(version, context)), offset: None, }], })), @@ -4748,8 +4833,8 @@ impl Removed { } } impl WithSchema for Removed { - fn schema(version: u32) -> Schema { - ::schema(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + ::schema(version, context) } } @@ -4810,8 +4895,8 @@ impl> AbiRemoved { } impl> WithSchema for AbiRemoved { - fn schema(version: u32) -> Schema { - ::schema(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + ::schema(version, context) } } @@ -4855,7 +4940,7 @@ impl Introspect for PhantomData { } } impl WithSchema for std::marker::PhantomData { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::ZeroSize } } @@ -4912,8 +4997,8 @@ impl Introspect for Option { } impl WithSchema for Option { - fn schema(version: u32) -> Schema { - Schema::SchemaOption(Box::new(T::schema(version))) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::SchemaOption(Box::new(T::schema(version, context))) } } impl ReprC for Option {} //Sadly, Option does not allow the #"reprC"-optimization @@ -4962,7 +5047,7 @@ impl Introspect for Result { } impl WithSchema for Result { - fn schema(version: u32) -> Schema { + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Enum(SchemaEnum { dbg_name: "Result".to_string(), size: None, @@ -4973,7 +5058,7 @@ impl WithSchema for Result { discriminant: 0, fields: vec![Field { name: "ok".to_string(), - value: Box::new(T::schema(version)), + value: Box::new(T::schema(version, context)), offset: None, }], }, @@ -4982,7 +5067,7 @@ impl WithSchema for Result { discriminant: 0, fields: vec![Field { name: "err".to_string(), - value: Box::new(R::schema(version)), + value: Box::new(R::schema(version, context)), offset: None, }], }, @@ -5024,7 +5109,7 @@ compile_error!("savefile bit-vec feature does not support big-endian machines"); #[cfg(feature = "bit-vec")] impl WithSchema for bit_vec::BitVec { - fn schema(version: u32) -> Schema { + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Struct(SchemaStruct { dbg_name: "BitVec".to_string(), size: None, @@ -5032,18 +5117,18 @@ impl WithSchema for bit_vec::BitVec { fields: vec![ Field { name: "num_bits".to_string(), - value: Box::new(usize::schema(version)), + value: Box::new(usize::schema(version, context)), offset: None, }, Field { name: "num_bytes".to_string(), - value: Box::new(usize::schema(version)), + value: Box::new(usize::schema(version, context)), offset: None, }, Field { name: "buffer".to_string(), value: Box::new(Schema::Vector( - Box::new(u8::schema(version)), + Box::new(u8::schema(version, context)), VecOrStringLayout::Unknown, )), offset: None, @@ -5119,7 +5204,7 @@ impl Deserialize for bit_vec::BitVec { #[cfg(feature = "bit-set")] impl WithSchema for bit_set::BitSet { - fn schema(version: u32) -> Schema { + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Struct(SchemaStruct { dbg_name: "BitSet".to_string(), size: None, @@ -5127,18 +5212,18 @@ impl WithSchema for bit_set::BitSet { fields: vec![ Field { name: "num_bits".to_string(), - value: Box::new(usize::schema(version)), + value: Box::new(usize::schema(version, context)), offset: None, }, Field { name: "num_bytes".to_string(), - value: Box::new(usize::schema(version)), + value: Box::new(usize::schema(version, context)), offset: None, }, Field { name: "buffer".to_string(), value: Box::new(Schema::Vector( - Box::new(u8::schema(version)), + Box::new(u8::schema(version, context)), VecOrStringLayout::Unknown, )), offset: None, @@ -5207,8 +5292,8 @@ impl Introspect for BinaryHeap { impl ReprC for BinaryHeap {} impl WithSchema for BinaryHeap { - fn schema(version: u32) -> Schema { - Schema::Vector(Box::new(T::schema(version)), VecOrStringLayout::Unknown) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::Vector(Box::new(T::schema(version, context)), VecOrStringLayout::Unknown) } } impl Serialize for BinaryHeap { @@ -5259,8 +5344,8 @@ impl WithSchema for smallvec::SmallVec where T::Item: WithSchema, { - fn schema(version: u32) -> Schema { - Schema::Vector(Box::new(T::Item::schema(version)), VecOrStringLayout::Unknown) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::Vector(Box::new(T::Item::schema(version, context)), VecOrStringLayout::Unknown) } } #[cfg(feature = "smallvec")] @@ -5327,13 +5412,13 @@ fn regular_serialize_vec( } impl WithSchema for Box<[T]> { - fn schema(version: u32) -> Schema { - Schema::Vector(Box::new(T::schema(version)), VecOrStringLayout::Unknown) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::Vector(Box::new(T::schema(version, context)), VecOrStringLayout::Unknown) } } impl WithSchema for Arc<[T]> { - fn schema(version: u32) -> Schema { - Schema::Vector(Box::new(T::schema(version)), VecOrStringLayout::Unknown) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::Vector(Box::new(T::schema(version, context)), VecOrStringLayout::Unknown) } } impl Introspect for Box<[T]> { @@ -5369,7 +5454,7 @@ impl Introspect for Arc<[T]> { } impl WithSchema for Arc { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_string(VecOrStringLayout::Unknown)) } } @@ -5455,7 +5540,7 @@ impl Deserialize for Box<[T]> { } } impl<'a> WithSchema for &'a str { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_string(calculate_string_memory_layout())) //TODO: This is _not_ the same memory layout as vec. Make a new Box type for slices? } @@ -5469,8 +5554,8 @@ impl<'a> Serialize for &'a str { } impl<'a, T: WithSchema> WithSchema for &'a [T] { - fn schema(version: u32) -> Schema { - Schema::Vector(Box::new(T::schema(version)), calculate_slice_memory_layout::()) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::Vector(Box::new(T::schema(version, context)), calculate_slice_memory_layout::()) //TODO: This is _not_ the same memory layout as vec. Make a new Box type for slices? } } @@ -5588,8 +5673,8 @@ fn calculate_string_memory_layout() -> VecOrStringLayout { return unsafe { std::mem::transmute(is_std) }; } impl WithSchema for Vec { - fn schema(version: u32) -> Schema { - Schema::Vector(Box::new(T::schema(version)), calculate_vec_memory_layout::()) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::Vector(Box::new(T::schema(version, context)), calculate_vec_memory_layout::()) } } @@ -5715,8 +5800,8 @@ impl Introspect for VecDeque { } impl WithSchema for VecDeque { - fn schema(version: u32) -> Schema { - Schema::Vector(Box::new(T::schema(version)), VecOrStringLayout::Unknown) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::Vector(Box::new(T::schema(version, context)), VecOrStringLayout::Unknown) } } @@ -5843,9 +5928,9 @@ impl ReprC for () { } impl WithSchema for [T; N] { - fn schema(version: u32) -> Schema { + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Array(SchemaArray { - item_type: Box::new(T::schema(version)), + item_type: Box::new(T::schema(version, context)), count: N, }) } @@ -5921,8 +6006,8 @@ impl Deserialize for [T; N] { impl ReprC for Range {} impl WithSchema for Range { - fn schema(version: u32) -> Schema { - Schema::new_tuple2::(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::new_tuple2::(version, context) } } impl Serialize for Range { @@ -6010,8 +6095,8 @@ impl ReprC for (T1, T2, T3, T4) { } impl WithSchema for (T1, T2, T3) { - fn schema(version: u32) -> Schema { - Schema::new_tuple3::(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::new_tuple3::(version, context) } } impl Serialize for (T1, T2, T3) { @@ -6032,8 +6117,8 @@ impl Deserialize for (T1, T2, } impl WithSchema for (T1, T2) { - fn schema(version: u32) -> Schema { - Schema::new_tuple2::(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::new_tuple2::(version, context) } } impl Serialize for (T1, T2) { @@ -6049,8 +6134,8 @@ impl Deserialize for (T1, T2) { } impl WithSchema for (T1,) { - fn schema(version: u32) -> Schema { - Schema::new_tuple1::(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::new_tuple1::(version, context) } } impl Serialize for (T1,) { @@ -6069,7 +6154,7 @@ impl ReprC for arrayvec::ArrayString {} #[cfg(feature = "arrayvec")] impl WithSchema for arrayvec::ArrayString { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_string(VecOrStringLayout::Unknown)) } } @@ -6112,8 +6197,8 @@ impl Introspect for arrayvec::ArrayString { #[cfg(feature = "arrayvec")] impl WithSchema for arrayvec::ArrayVec { - fn schema(version: u32) -> Schema { - Schema::Vector(Box::new(V::schema(version)), VecOrStringLayout::Unknown) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + Schema::Vector(Box::new(V::schema(version, context)), VecOrStringLayout::Unknown) } } @@ -6188,18 +6273,19 @@ impl Deserialize for arrayvec::ArrayVec< } use std::ops::{Deref, Range}; -impl WithSchema for Box { - fn schema(version: u32) -> Schema { - T::schema(version) +impl WithSchema for Box { + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + context.possible_recursion::(|context|T::schema(version, context)) + } } impl ReprC for Box {} -impl Serialize for Box { +impl Serialize for Box { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { self.deref().serialize(serializer) } } -impl Deserialize for Box { +impl Deserialize for Box { fn deserialize(deserializer: &mut Deserializer) -> Result { Ok(Box::new(T::deserialize(deserializer)?)) } @@ -6209,8 +6295,8 @@ use std::rc::Rc; impl ReprC for Rc {} impl WithSchema for Rc { - fn schema(version: u32) -> Schema { - T::schema(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + T::schema(version, context) } } impl Serialize for Rc { @@ -6226,8 +6312,8 @@ impl Deserialize for Rc { impl ReprC for Arc {} impl WithSchema for Arc { - fn schema(version: u32) -> Schema { - T::schema(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + T::schema(version, context) } } impl Serialize for Arc { @@ -6245,6 +6331,7 @@ use bzip2::Compression; use std::any::{Any, TypeId}; use std::cell::Cell; use std::cell::RefCell; +use std::collections::hash_map::Entry; use std::convert::{TryFrom, TryInto}; use std::fmt::{Debug, Display, Formatter}; use std::marker::PhantomData; @@ -6258,8 +6345,8 @@ use memoffset::offset_of_tuple; impl ReprC for RefCell {} impl WithSchema for RefCell { - fn schema(version: u32) -> Schema { - T::schema(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + T::schema(version, context) } } impl Serialize for RefCell { @@ -6279,8 +6366,8 @@ impl ReprC for Cell { } } impl WithSchema for Cell { - fn schema(version: u32) -> Schema { - T::schema(version) + fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { + T::schema(version, context) } } impl Serialize for Cell { @@ -6296,7 +6383,7 @@ impl Deserialize for Cell { } impl WithSchema for () { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::ZeroSize } } @@ -6478,52 +6565,52 @@ impl Introspect for AtomicIsize { } impl WithSchema for AtomicBool { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_bool) } } impl WithSchema for AtomicU8 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_u8) } } impl WithSchema for AtomicI8 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_i8) } } impl WithSchema for AtomicU16 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_u16) } } impl WithSchema for AtomicI16 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_i16) } } impl WithSchema for AtomicU32 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_u32) } } impl WithSchema for AtomicI32 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_i32) } } impl WithSchema for AtomicU64 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_u64) } } impl WithSchema for AtomicI64 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_i64) } } impl WithSchema for AtomicUsize { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { match std::mem::size_of::() { 4 => Schema::Primitive(SchemaPrimitive::schema_u32), 8 => Schema::Primitive(SchemaPrimitive::schema_u64), @@ -6532,7 +6619,7 @@ impl WithSchema for AtomicUsize { } } impl WithSchema for AtomicIsize { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { match std::mem::size_of::() { 4 => Schema::Primitive(SchemaPrimitive::schema_i32), 8 => Schema::Primitive(SchemaPrimitive::schema_i64), @@ -6542,67 +6629,67 @@ impl WithSchema for AtomicIsize { } impl WithSchema for bool { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_bool) } } impl WithSchema for u8 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_u8) } } impl WithSchema for i8 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_i8) } } impl WithSchema for u16 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_u16) } } impl WithSchema for i16 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_i16) } } impl WithSchema for u32 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_u32) } } impl WithSchema for i32 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_i32) } } impl WithSchema for u64 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_u64) } } impl WithSchema for u128 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_u128) } } impl WithSchema for i128 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_i128) } } impl WithSchema for i64 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_i64) } } impl WithSchema for char { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_char) } } impl WithSchema for usize { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { match std::mem::size_of::() { 4 => Schema::Primitive(SchemaPrimitive::schema_u32), 8 => Schema::Primitive(SchemaPrimitive::schema_u64), @@ -6611,7 +6698,7 @@ impl WithSchema for usize { } } impl WithSchema for isize { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { match std::mem::size_of::() { 4 => Schema::Primitive(SchemaPrimitive::schema_i32), 8 => Schema::Primitive(SchemaPrimitive::schema_i64), @@ -6620,12 +6707,12 @@ impl WithSchema for isize { } } impl WithSchema for f32 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_f32) } } impl WithSchema for f64 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_f64) } } @@ -7100,7 +7187,7 @@ impl Serialize for Canary1 { } impl ReprC for Canary1 {} impl WithSchema for Canary1 { - fn schema(_version: u32) -> Schema { + fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema { Schema::Primitive(SchemaPrimitive::schema_canary1) } } diff --git a/savefile/src/prelude.rs b/savefile/src/prelude.rs index 281e229..bfb560a 100644 --- a/savefile/src/prelude.rs +++ b/savefile/src/prelude.rs @@ -5,7 +5,7 @@ pub use { super::Deserializer, super::Field, super::Introspect, super::IntrospectItem, super::IntrospectedElementKey, super::IntrospectionResult, super::Introspector, super::IntrospectorNavCommand, super::IsReprC, super::Removed, super::ReprC, super::SavefileError, super::Schema, super::SchemaEnum, super::SchemaPrimitive, super::SchemaStruct, - super::Serialize, super::Serializer, super::Variant, super::WithSchema, + super::Serialize, super::Serializer, super::Variant, super::WithSchema, super::WithSchemaContext, }; pub use byteorder::{LittleEndian, ReadBytesExt};