diff --git a/Cargo.lock b/Cargo.lock index 45e1f31..91a4366 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -448,7 +448,7 @@ dependencies = [ [[package]] name = "savefile" -version = "0.17.0-beta.13" +version = "0.17.0-beta.14" dependencies = [ "arrayvec", "bit-set", @@ -471,7 +471,7 @@ dependencies = [ [[package]] name = "savefile-abi" -version = "0.17.0-beta.13" +version = "0.17.0-beta.14" dependencies = [ "byteorder", "libloading", @@ -511,7 +511,7 @@ dependencies = [ [[package]] name = "savefile-derive" -version = "0.17.0-beta.13" +version = "0.17.0-beta.14" dependencies = [ "proc-macro-error", "proc-macro2", diff --git a/savefile-abi/CHANGELOG.md b/savefile-abi/CHANGELOG.md index 34c17e4..c9018c7 100644 --- a/savefile-abi/CHANGELOG.md +++ b/savefile-abi/CHANGELOG.md @@ -6,7 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.17.-beta.13](https://github.com/avl/savefile/compare/savefile-abi-v0.17.0-beta.12...savefile-abi-v0.17.0-beta.13) - 2024-04-27 +## [0.17.0-beta.14](https://github.com/avl/savefile/compare/savefile-abi-v0.17.0-beta.13...savefile-abi-v0.17.0-beta.14) - 2024-04-30 + +### Other +- updated the following local packages: savefile, savefile-derive + +## [0.17.0-beta.13](https://github.com/avl/savefile/compare/savefile-abi-v0.17.0-beta.12...savefile-abi-v0.17.0-beta.13) - 2024-04-27 ### Other - updated the following local packages: savefile diff --git a/savefile-abi/Cargo.toml b/savefile-abi/Cargo.toml index 21156da..c7100c9 100644 --- a/savefile-abi/Cargo.toml +++ b/savefile-abi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "savefile-abi" -version = "0.17.0-beta.13" +version = "0.17.0-beta.14" edition = "2021" authors = ["Anders Musikka "] documentation = "https://docs.rs/savefile-abi/" @@ -16,7 +16,7 @@ keywords = ["dylib", "dlopen", "ffi"] license = "MIT/Apache-2.0" [dependencies] -savefile = { path="../savefile", version = "=0.17.0-beta.13" } -savefile-derive = { path="../savefile-derive", version = "=0.17.0-beta.13" } +savefile = { path="../savefile", version = "=0.17.0-beta.14" } +savefile-derive = { path="../savefile-derive", version = "=0.17.0-beta.14" } byteorder = "1.4" libloading = "0.8" diff --git a/savefile-abi/src/lib.rs b/savefile-abi/src/lib.rs index abd939b..35a15eb 100644 --- a/savefile-abi/src/lib.rs +++ b/savefile-abi/src/lib.rs @@ -468,12 +468,8 @@ pub struct TraitObject { vtable: *const (), } -unsafe impl Sync for TraitObject { - -} -unsafe impl Send for TraitObject { - -} +unsafe impl Sync for TraitObject {} +unsafe impl Send for TraitObject {} impl TraitObject { /// Returns a TraitObject with two null ptrs. This value must never be used, @@ -617,12 +613,8 @@ pub struct AbiConnection { #[doc(hidden)] pub phantom: PhantomData<*const T>, } -unsafe impl Sync for AbiConnection{ - -} -unsafe impl Send for AbiConnection{ - -} +unsafe impl Sync for AbiConnection {} +unsafe impl Send for AbiConnection {} /// A trait object together with its entry point #[repr(C)] @@ -1324,15 +1316,24 @@ impl AbiConnection { /// 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) { + 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); + 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(", ")); + 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(", ") + ); } } diff --git a/savefile-derive/CHANGELOG.md b/savefile-derive/CHANGELOG.md index 92047a1..118a115 100644 --- a/savefile-derive/CHANGELOG.md +++ b/savefile-derive/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.17.0-beta.14](https://github.com/avl/savefile/compare/savefile-derive-v0.17.0-beta.13...savefile-derive-v0.17.0-beta.14) - 2024-04-30 + +### Fixed +- Bad cycles detection + ## [0.17.0-beta.12](https://github.com/avl/savefile/compare/savefile-derive-v0.17.0-beta.11...savefile-derive-v0.17.0-beta.12) - 2024-04-27 ### Other diff --git a/savefile-derive/Cargo.toml b/savefile-derive/Cargo.toml index e06843c..0100607 100644 --- a/savefile-derive/Cargo.toml +++ b/savefile-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "savefile-derive" -version = "0.17.0-beta.13" +version = "0.17.0-beta.14" authors = ["Anders Musikka "] description = "Custom derive macros for savefile crate - simple, convenient, fast, versioned, binary serialization/deserialization library." diff --git a/savefile-derive/src/lib.rs b/savefile-derive/src/lib.rs index 92a5a25..b36d502 100644 --- a/savefile-derive/src/lib.rs +++ b/savefile-derive/src/lib.rs @@ -36,7 +36,10 @@ use syn::__private::bool; use syn::spanned::Spanned; use syn::token::Paren; use syn::Type::Tuple; -use syn::{DeriveInput, FnArg, GenericParam, Generics, Ident, ImplGenerics, Index, ItemTrait, Pat, ReturnType, TraitItem, Type, TypeGenerics, TypeParamBound, TypeTuple}; +use syn::{ + DeriveInput, FnArg, GenericParam, Generics, Ident, ImplGenerics, Index, ItemTrait, Pat, ReturnType, TraitItem, + Type, TypeGenerics, TypeParamBound, TypeTuple, +}; fn implement_fields_serialize( field_infos: Vec, implicit_self: bool, @@ -253,7 +256,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, WithSchemaContext, Serializer, Serialize, Deserializer, Deserialize, SavefileError, deserialize_slice_as_vec, ReadBytesExt,LittleEndian,AbiMethodArgument, AbiMethod, AbiMethodInfo,AbiTraitDefinition}; + use savefile::prelude::{ReprC, Schema, SchemaPrimitive, WithSchema, WithSchemaContext, get_schema, 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; @@ -266,7 +269,10 @@ pub fn savefile_abi_exportable( let mut extra_definitions = HashMap::new(); if parsed.generics.params.is_empty() == false { - abort!(parsed.generics.params.span(), "Savefile does not support generic traits."); + abort!( + parsed.generics.params.span(), + "Savefile does not support generic traits." + ); } for supertrait in parsed.supertraits.iter() { match supertrait { @@ -293,7 +299,10 @@ pub fn savefile_abi_exportable( } if parsed.generics.where_clause.is_some() { - abort!(parsed.generics.where_clause.span(), "Savefile does not support where-clauses for traits"); + abort!( + parsed.generics.where_clause.span(), + "Savefile does not support where-clauses for traits" + ); } for (method_number, item) in parsed.items.iter().enumerate() { @@ -312,7 +321,10 @@ pub fn savefile_abi_exportable( } TraitItem::Method(method) => { if method.sig.generics.where_clause.is_some() { - abort!(method.sig.generics.where_clause.span(), "Savefile does not support where-clauses for methods"); + abort!( + method.sig.generics.where_clause.span(), + "Savefile does not support where-clauses for methods" + ); } let method_name = method.sig.ident.clone(); //let method_name_str = method.sig.ident.to_string(); @@ -392,13 +404,22 @@ pub fn savefile_abi_exportable( } } if method.sig.asyncness.is_some() { - abort!(method.sig.asyncness.span(), "savefile-abi does not support async methods.") + abort!( + method.sig.asyncness.span(), + "savefile-abi does not support async methods." + ) } if method.sig.variadic.is_some() { - abort!(method.sig.variadic.span(), "savefile-abi does not support variadic methods.") + abort!( + method.sig.variadic.span(), + "savefile-abi does not support variadic methods." + ) } if method.sig.unsafety.is_some() { - abort!(method.sig.unsafety.span(), "savefile-abi does not presently support unsafe methods.") + abort!( + method.sig.unsafety.span(), + "savefile-abi does not presently support unsafe methods." + ) } if method.sig.abi.is_some() { abort!(method.sig.abi.span(), "savefile-abi does not need (or support) 'extern \"C\"' or similar ABI-constructs. Just remove this keyword.") @@ -406,12 +427,19 @@ pub fn savefile_abi_exportable( if method.sig.generics.params.is_empty() == false { for item in method.sig.generics.params.iter() { match item { - GenericParam::Type(typ) => abort!(typ.span(), "savefile-abi does not support generic methods."), - GenericParam::Const(typ) => abort!(typ.span(), "savefile-abi does not support const-generic methods."), + GenericParam::Type(typ) => { + abort!(typ.span(), "savefile-abi does not support generic methods.") + } + GenericParam::Const(typ) => { + abort!(typ.span(), "savefile-abi does not support const-generic methods.") + } _ => {} } } - abort!(method.sig.generics.params.span(), "savefile-abi does not support methods with lifetimes."); + abort!( + method.sig.generics.params.span(), + "savefile-abi does not support methods with lifetimes." + ); } let method_defs = crate::savefile_abi::generate_method_definitions( @@ -446,9 +474,17 @@ pub fn savefile_abi_exportable( ); } TraitItem::Verbatim(v) => { - abort!(v.span(), "Unsupported item in trait definition: {}", v.to_token_stream()); + abort!( + v.span(), + "Unsupported item in trait definition: {}", + v.to_token_stream() + ); } - x => abort!(x.span(), "Unsupported item in trait definition: {}", x.to_token_stream()), + x => abort!( + x.span(), + "Unsupported item in trait definition: {}", + x.to_token_stream() + ), } } @@ -544,8 +580,6 @@ pub fn savefile_abi_export(item: proc_macro::TokenStream) -> proc_macro::TokenSt let trait_type = Ident::new(symbols[1], Span::call_site()); let abi_entry = Ident::new(("abi_entry_".to_string() + symbols[1]).as_str(), Span::call_site()); - - let expanded = quote! { #[allow(clippy::double_comparisons)] const _:() = { diff --git a/savefile-derive/src/savefile_abi.rs b/savefile-derive/src/savefile_abi.rs index 13cfcb0..95cc4b1 100644 --- a/savefile-derive/src/savefile_abi.rs +++ b/savefile-derive/src/savefile_abi.rs @@ -137,7 +137,7 @@ fn emit_closure_helpers( pub(crate) enum ArgType { PlainData(Type), Reference(Box, bool /*ismut (only traits objects can be mut here)*/), - Str(bool/*static*/), + Str(bool /*static*/), Boxed(Box), Slice(Box), Trait(Ident, bool /*ismut self*/), @@ -167,7 +167,6 @@ pub(crate) fn parse_box_type( is_reference: bool, is_mut_ref: bool, ) -> ArgType { - let location; if is_return_value { location = format!("In return value of method '{}'", method_name); @@ -175,12 +174,16 @@ pub(crate) fn parse_box_type( location = format!("Method '{}', argument {}", method_name, arg_name); } - if path.segments.len() != 1 { abort!(path.span(), "Savefile does not support types named 'Box', unless they are the standard type Box, and it must be specified as 'Box', without any namespace"); } if is_reference { - abort!(path.span(), "{}. Savefile does not support references to Boxes. Just supply a reference to the inner type: {}", location, typ.to_token_stream()); + abort!( + path.span(), + "{}. Savefile does not support references to Boxes. Just supply a reference to the inner type: {}", + location, + typ.to_token_stream() + ); } let last_seg = path.segments.iter().last().unwrap(); @@ -203,29 +206,33 @@ pub(crate) fn parse_box_type( extra_definitions, true, is_mut_ref, - ){ + ) { ArgType::Boxed(_) => { - abort!(first_gen_arg.span(), "{}. Savefile does not support a Box containing another Box: {}", location, typ.to_token_stream()) + abort!( + first_gen_arg.span(), + "{}. Savefile does not support a Box containing another Box: {}", + location, + typ.to_token_stream() + ) } ArgType::PlainData(_) | ArgType::Str(_) => { return ArgType::PlainData(typ.clone()); //Box is itself a plaintype. So handle it as such. It can matter, if Box implements Serializable, when T does not. (example: str) } - ArgType::Slice(slicetype) => { - match &*slicetype { - ArgType::PlainData(_) => { - return ArgType::Slice(slicetype); - } - _x => - abort!(angargs.span(), "{}. Savefile does not support a Box containing a slice of anything complex, like: {}", location, typ.to_token_stream()) + ArgType::Slice(slicetype) => match &*slicetype { + ArgType::PlainData(_) => { + return ArgType::Slice(slicetype); } - } + _x => abort!( + angargs.span(), + "{}. Savefile does not support a Box containing a slice of anything complex, like: {}", + location, + typ.to_token_stream() + ), + }, ArgType::Reference(_, _) => { abort!(first_gen_arg.span(), "{}. Savefile does not support a Box containing a reference, like: {} (boxing a reference is generally a useless thing to do))", location, typ.to_token_stream()); } - x@ArgType::Trait(_, _) | - x@ArgType::Fn(_, _, _, _) => { - ArgType::Boxed(Box::new(x)) - } + x @ ArgType::Trait(_, _) | x @ ArgType::Fn(_, _, _, _) => ArgType::Boxed(Box::new(x)), } } _ => { @@ -285,7 +292,7 @@ fn parse_type( typref.lifetime.span(), "{}: Specifying lifetimes is not supported by Savefile-Abi.", location, - ) + ), } } else { is_static_lifetime = false; @@ -326,7 +333,12 @@ fn parse_type( ); } if is_mut_ref { - abort!(typ.span(), "{}: Mutable refernces are not supported by savefile-abi, except for FnMut-trait objects. {}", location, typ.to_token_stream()); + abort!( + typ.span(), + "{}: Mutable refernces are not supported by savefile-abi, except for FnMut-trait objects. {}", + location, + typ.to_token_stream() + ); } let argtype = parse_type( version, @@ -343,7 +355,12 @@ fn parse_type( } Type::TraitObject(trait_obj) => { if !is_reference { - abort!(trait_obj.span(), "{}: Trait objects must always be behind references. Try adding a '&' to the type: {}", location, typ.to_token_stream()); + abort!( + trait_obj.span(), + "{}: Trait objects must always be behind references. Try adding a '&' to the type: {}", + location, + typ.to_token_stream() + ); } if trait_obj.dyn_token.is_some() { let type_bounds: Vec<_> = trait_obj @@ -369,7 +386,12 @@ fn parse_type( abort!(trait_obj.bounds.span(), "{}, unsupported trait object reference. Only &dyn Trait is supported. Encountered zero traits.", location); } if type_bounds.len() > 1 { - abort!(trait_obj.bounds.span(), "{}, unsupported Box-type. Only &dyn Trait> is supported. Encountered multiple traits: {:?}", location, trait_obj); + abort!( + trait_obj.bounds.span(), + "{}, unsupported Box-type. Only &dyn Trait> is supported. Encountered multiple traits: {:?}", + location, + trait_obj + ); } let bound = type_bounds.into_iter().next().expect("Internal error, missing bounds"); @@ -685,7 +707,7 @@ impl ArgType { caller_arg_serializer1: quote! { #arg_name.serialize(&mut serializer) }, - schema: quote!( <#arg_type as WithSchema>::schema(version, context) ), + schema: quote!( get_schema::<#arg_type>(version) ), known_size_align1: if compile_time_check_reprc(arg_type) { compile_time_size(arg_type) } else { @@ -896,7 +918,6 @@ pub(super) fn generate_method_definitions( let mut compile_time_known_size = Some(0); for (arg_index, (arg_name, typ)) in args.iter().enumerate() { - let argtype = parse_type( version, &arg_name.to_string(), @@ -906,7 +927,7 @@ pub(super) fn generate_method_definitions( &mut *name_generator, extra_definitions, false, - false + false, ); callee_trampoline_variable_declaration.push(quote! {let #arg_name;}); @@ -975,7 +996,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, &mut WithSchemaContext::new())); + return_value_schema = quote!(get_schema::<()>(0)); ret_deserializer = quote!(()); //Zero-sized, no deserialize actually needed ret_serialize = quote!(()); caller_return_type = quote!(()); diff --git a/savefile-min-build/src/lib.rs b/savefile-min-build/src/lib.rs index 91af8c8..e988b01 100644 --- a/savefile-min-build/src/lib.rs +++ b/savefile-min-build/src/lib.rs @@ -1,10 +1,10 @@ extern crate savefile_abi; extern crate savefile_derive; +#[cfg(test)] use savefile_abi::AbiConnection; use savefile_derive::savefile_abi_exportable; - #[savefile_abi_exportable(version = 0)] pub trait ExampleTrait { fn test_slices(&mut self, slice: &[u32]) -> u32 { @@ -12,15 +12,13 @@ pub trait ExampleTrait { } } -impl ExampleTrait for () { - -} +impl ExampleTrait for () {} #[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) ); + assert!(conn.get_arg_passable_by_ref("test_slices", 0)); //conn.test_slices(&[1,2,3,4]); } diff --git a/savefile-test/Cargo.toml b/savefile-test/Cargo.toml index 3c67e67..f2d9279 100644 --- a/savefile-test/Cargo.toml +++ b/savefile-test/Cargo.toml @@ -12,7 +12,7 @@ nightly=["savefile/nightly"] [dependencies] savefile = { path = "../savefile", features = ["size_sanity_checks", "encryption", "compression","bit-set","bit-vec","rustc-hash","serde_derive", "quickcheck"]} -savefile-derive = { path = "../savefile-derive", version = "=0.17.0-beta.13" } +savefile-derive = { path = "../savefile-derive", version = "=0.17.0-beta.14" } savefile-abi = { path = "../savefile-abi" } bit-vec = "0.6" arrayvec="0.7" diff --git a/savefile-test/src/cycles.rs b/savefile-test/src/cycles.rs index e776978..6785182 100644 --- a/savefile-test/src/cycles.rs +++ b/savefile-test/src/cycles.rs @@ -1,10 +1,10 @@ use assert_roundtrip; -use savefile::Removed; +use savefile::{get_schema, Removed, WithSchema, WithSchemaContext}; #[derive(Savefile, Debug, PartialEq)] enum Tree { Leaf, - Node(Box,Box) + Node(Box, Box), } #[test] @@ -12,76 +12,59 @@ 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)); + 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 + tree: Box, } #[derive(Savefile, Debug, PartialEq)] enum Tree2 { Leaf(String), - Node(TreeNode) + Node(TreeNode), } #[test] pub fn test_cyclic2() { - let example = Tree2::Node(TreeNode{tree: Box::new(Tree2::Leaf("hej".into()))}); + let example = Tree2::Node(TreeNode { + tree: Box::new(Tree2::Leaf("hej".into())), + }); assert_roundtrip(example); } + #[derive(Savefile, Debug, PartialEq)] -enum Version1LevelD { - Leaf, - Node(Box) -} +struct Version1LevelC(Box); #[derive(Savefile, Debug, PartialEq)] -enum Version1LevelC { - Leaf, - Node(Box) -} +struct Version1LevelB(Box); #[derive(Savefile, Debug, PartialEq)] -enum Version1LevelB { - Leaf(Box), - Node(Box) -} +struct Version1LevelA(Option>); #[derive(Savefile, Debug, PartialEq)] -enum Version1LevelA { - Leaf, - Node(Box) -} +struct Version1Base(Option>); #[derive(Savefile, Debug, PartialEq)] -enum Version2LevelC { - Leaf, - Node(Box) -} +struct Version2LevelC(Box); #[derive(Savefile, Debug, PartialEq)] -enum Version2LevelB { - Leaf(Box), - Node(Box) -} +struct Version2LevelB(Box); #[derive(Savefile, Debug, PartialEq)] -enum Version2LevelA { - Leaf, - Node(Box) -} +struct Version2LevelA(Option>); +#[derive(Savefile, Debug, PartialEq)] +struct Version2Base(Option>); #[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")] +#[should_panic( + expected = "Saved schema differs from in-memory schema for version 0. Error: At location [./Version1Base/0/?/Version1LevelA/0/?/Version1LevelB/0/Version1LevelC/0]: Application protocol uses recursion up 3 levels, but foreign format uses 2" +)] 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 + assert_roundtrip_to_new_version(Version1Base(None), 0, Version2Base(None), 1); +} diff --git a/savefile-test/src/lib.rs b/savefile-test/src/lib.rs index 25f9df9..f14135d 100644 --- a/savefile-test/src/lib.rs +++ b/savefile-test/src/lib.rs @@ -60,7 +60,6 @@ use std::io::Cursor; pub fn assert_roundtrip(sample: E) { assert_roundtrip_version(sample, 0, true) } - pub fn assert_roundtrip_version(sample: E, version: u32, schema: bool) { let mut f = Cursor::new(Vec::new()); { 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 7d5fc85..44b1e8d 100644 --- a/savefile-test/src/savefile_abi_test/advanced_datatypes_test.rs +++ b/savefile-test/src/savefile_abi_test/advanced_datatypes_test.rs @@ -67,8 +67,8 @@ 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); + assert!(conn.get_arg_passable_by_ref("test_slices", 0)); + assert_eq!(conn.test_slices(&[1, 2, 3, 4]), 10); } #[test] 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 5927c96..fa72f81 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, WithSchemaContext}; +use savefile::{get_schema, 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, &mut WithSchemaContext::new()); + let schema = get_schema::(0); 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/CHANGELOG.md b/savefile/CHANGELOG.md index 3fe8e66..a96abde 100644 --- a/savefile/CHANGELOG.md +++ b/savefile/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.17.0-beta.14](https://github.com/avl/savefile/compare/savefile-v0.17.0-beta.13...savefile-v0.17.0-beta.14) - 2024-04-30 + +### Fixed +- Bad cycles detection + ## [0.17.0-beta.13](https://github.com/avl/savefile/compare/savefile-v0.17.0-beta.12...savefile-v0.17.0-beta.13) - 2024-04-27 ### Added diff --git a/savefile/Cargo.toml b/savefile/Cargo.toml index e83d164..fc2b2fe 100644 --- a/savefile/Cargo.toml +++ b/savefile/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "savefile" -version = "0.17.0-beta.13" +version = "0.17.0-beta.14" authors = ["Anders Musikka "] documentation = "https://docs.rs/savefile/" homepage = "https://github.com/avl/savefile/" @@ -54,13 +54,13 @@ bit-set = {version = "0.5", optional = true} rustc-hash = {version = "1.1", optional = true} memoffset = "0.9" byteorder = "1.4" -savefile-derive = {path="../savefile-derive", version = "=0.17.0-beta.13", optional = true } +savefile-derive = {path="../savefile-derive", version = "=0.17.0-beta.14", optional = true } serde_derive = {version= "1.0", optional = true} serde = {version= "1.0", optional = true} quickcheck = {version= "1.0", optional = true} [dev-dependencies] -savefile-derive = { path="../savefile-derive", version = "=0.17.0-beta.13" } +savefile-derive = { path="../savefile-derive", version = "=0.17.0-beta.14" } [build-dependencies] rustc_version="0.2" diff --git a/savefile/src/lib.rs b/savefile/src/lib.rs index c8d0f1e..74cbf02 100644 --- a/savefile/src/lib.rs +++ b/savefile/src/lib.rs @@ -863,6 +863,7 @@ extern crate serde_derive; use core::str::Utf8Error; #[cfg(feature = "serde_derive")] use serde_derive::{Deserialize, Serialize}; +use std::any::TypeId; #[cfg(feature = "quickcheck")] extern crate quickcheck; @@ -2014,7 +2015,11 @@ 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, &mut WithSchemaContext::new()))) + Deserializer::<_>::load_impl::( + reader, + version, + Some(|version| T::schema(version, &mut WithSchemaContext::new())), + ) } /// Deserialize an object of type T from the given reader. @@ -2232,7 +2237,6 @@ 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. @@ -2240,16 +2244,16 @@ pub fn save_file_noschema>( /// 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, + 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() + let seen_types = HashMap::new(); + WithSchemaContext { seen_types } } } @@ -2265,18 +2269,22 @@ impl WithSchemaContext { /// } /// impl WithSchema for MyBox { /// fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - /// context.possible_recursion::(|context| Schema::Boxed(Box::new(T::schema(version, context)))) + /// context.possible_recursion::>(|context| Schema::Boxed(Box::new(T::schema(version, context)))) /// } /// /// } /// ``` - pub fn possible_recursion(&mut self, cb: impl FnOnce(&mut WithSchemaContext) -> Schema) -> Schema { + /// + /// If recursion is detected (traversing to exactly MyBox twice, in the above example), the method + /// 'possible_recursion' will return Schema::Recursion, stopping the Schema instance from becoming infinitely big. + /// + 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 ); + return Schema::Recursion(prevlen - present_value_depth); } Entry::Vacant(vac) => { vac.insert(prevlen); @@ -2297,9 +2305,17 @@ impl WithSchemaContext { /// can be disabled). pub trait WithSchema { /// Returns a representation of the schema used by this Serialize implementation for the given version. + /// The WithSchemaContext can be used to guard against recursive data structures. + /// See documentation of WithSchemaContext. fn schema(version: u32, context: &mut WithSchemaContext) -> Schema; } +/// Create a new WithSchemaContext, and then call 'schema' on type T. +/// This is a useful convenience method. +pub fn get_schema(version: u32) -> Schema { + T::schema(version, &mut WithSchemaContext::new()) +} + /// This trait must be implemented for all data structures you wish to be /// able to serialize. To actually serialize data: create a [Serializer], /// then call serialize on your data to save, giving the Serializer @@ -2643,14 +2659,14 @@ impl SchemaEnum { /// Arguments: /// /// * dbg_name - Name of the enum type. - /// * variants - The variants of the enum /// * discriminant_size: /// If this is a repr(uX)-enum, then the size of the discriminant, in bytes. /// Valid values are 1, 2 or 4. /// Otherwise, this is the number of bytes needed to represent the discriminant. /// In either case, this is the size of the enum in the disk-format. + /// * variants - The variants of the enum /// - pub fn new(dbg_name: String, variants: Vec, discriminant_size: u8) -> SchemaEnum { + pub fn new(dbg_name: String, discriminant_size: u8, variants: Vec) -> SchemaEnum { SchemaEnum { dbg_name, variants, @@ -2696,7 +2712,6 @@ impl SchemaEnum { } } fn layout_compatible(&self, other: &SchemaEnum) -> bool { - if self.has_explicit_repr == false || other.has_explicit_repr == false { return false; } @@ -3128,7 +3143,7 @@ pub enum Schema { /// 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*/) + Recursion(usize /*depth*/), } /// Introspect is not implemented for Schema, though it could be impl Introspect for Schema { @@ -3161,7 +3176,9 @@ impl Schema { Schema::Str => "str".into(), Schema::Reference(_) => "reference".into(), Schema::Trait(_, _) => "trait".into(), - Schema::Recursion(depth) => {format!("",depth)} + Schema::Recursion(depth) => { + format!("", depth) + } } } /// Determine if the two fields are laid out identically in memory, in their parent objects. @@ -3237,7 +3254,10 @@ impl Schema { }) } /// Create a 3-element tuple - pub fn new_tuple3(version: u32, context: &mut WithSchemaContext) -> 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)>()), @@ -3262,7 +3282,10 @@ impl Schema { }) } /// Create a 4-element tuple - pub fn new_tuple4(version: u32, context: &mut WithSchemaContext) -> 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)>()), @@ -4458,7 +4481,7 @@ impl Introspect for BTreeMap { self.len() } } -impl WithSchema for BTreeMap { +impl WithSchema for BTreeMap { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Vector( Box::new(Schema::Struct(SchemaStruct { @@ -4468,12 +4491,12 @@ impl WithSchema for BTreeMap fields: vec![ Field { name: "key".to_string(), - value: Box::new(context.possible_recursion::(|context|K::schema(version, context))), + value: Box::new(context.possible_recursion::(|context| K::schema(version, context))), offset: None, }, Field { name: "value".to_string(), - value: Box::new(context.possible_recursion::(|context|V::schema(version, context))), + value: Box::new(context.possible_recursion::(|context| V::schema(version, context))), offset: None, }, ], @@ -4483,7 +4506,7 @@ impl WithSchema for BTreeMap } } impl ReprC for BTreeMap {} -impl Serialize for BTreeMap { +impl Serialize for BTreeMap { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { self.len().serialize(serializer)?; for (k, v) in self { @@ -4493,7 +4516,7 @@ impl Serialize for BTreeMap { Ok(()) } } -impl Deserialize for BTreeMap { +impl Deserialize for BTreeMap { fn deserialize(deserializer: &mut Deserializer) -> Result { let mut ret = BTreeMap::new(); let count = ::deserialize(deserializer)?; @@ -4508,12 +4531,15 @@ impl Deserialize for BTree } impl ReprC for HashSet {} -impl WithSchema for HashSet { +impl WithSchema for HashSet { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - Schema::Vector(Box::new(context.possible_recursion::(|context|K::schema(version, context))), VecOrStringLayout::Unknown) + Schema::Vector( + Box::new(context.possible_recursion::(|context| K::schema(version, context))), + VecOrStringLayout::Unknown, + ) } } -impl Serialize for HashSet { +impl Serialize for HashSet { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { serializer.write_usize(self.len())?; for item in self { @@ -4522,7 +4548,7 @@ impl Serialize for HashSet Deserialize for HashSet { +impl Deserialize for HashSet { fn deserialize(deserializer: &mut Deserializer) -> Result { let cnt = deserializer.read_usize()?; let mut ret = HashSet::with_capacity_and_hasher(cnt, S::default()); @@ -4533,7 +4559,9 @@ impl } } -impl WithSchema for HashMap { +impl WithSchema + for HashMap +{ fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Vector( Box::new(Schema::Struct(SchemaStruct { @@ -4543,12 +4571,12 @@ impl(|context|K::schema(version, context))), + value: Box::new(context.possible_recursion::(|context| K::schema(version, context))), offset: None, }, Field { name: "value".to_string(), - value: Box::new(context.possible_recursion::(|context|V::schema(version, context))), + value: Box::new(context.possible_recursion::(|context| V::schema(version, context))), offset: None, }, ], @@ -4558,7 +4586,9 @@ impl ReprC for HashMap {} -impl Serialize for HashMap { +impl Serialize + for HashMap +{ fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { serializer.write_usize(self.len())?; for (k, v) in self.iter() { @@ -4569,7 +4599,7 @@ impl Deserialize +impl Deserialize for HashMap { fn deserialize(deserializer: &mut Deserializer) -> Result { @@ -4583,7 +4613,9 @@ impl WithSchema for IndexMap { +impl WithSchema + for IndexMap +{ fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Vector( Box::new(Schema::Struct(SchemaStruct { @@ -4593,12 +4625,12 @@ impl(|context|K::schema(version, context))), + value: Box::new(context.possible_recursion::(|context| K::schema(version, context))), offset: None, }, Field { name: "value".to_string(), - value: Box::new(context.possible_recursion::(|context|V::schema(version, context))), + value: Box::new(context.possible_recursion::(|context| V::schema(version, context))), offset: None, }, ], @@ -4695,7 +4727,9 @@ where impl ReprC for IndexMap {} #[cfg(feature = "indexmap")] -impl Serialize for IndexMap { +impl Serialize + for IndexMap +{ fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { serializer.write_usize(self.len())?; for (k, v) in self.iter() { @@ -4707,7 +4741,7 @@ impl Deserialize for IndexMap { +impl Deserialize for IndexMap { fn deserialize(deserializer: &mut Deserializer) -> Result { let l = deserializer.read_usize()?; let mut ret = IndexMap::with_capacity(l); @@ -4741,7 +4775,7 @@ impl Introspect for Inde impl ReprC for IndexSet {} #[cfg(feature = "indexmap")] -impl WithSchema for IndexSet { +impl WithSchema for IndexSet { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Vector( Box::new(Schema::Struct(SchemaStruct { @@ -4750,7 +4784,7 @@ impl WithSchema alignment: None, fields: vec![Field { name: "key".to_string(), - value: Box::new(context.possible_recursion::(|context|K::schema(version, context))), + value: Box::new(context.possible_recursion::(|context| K::schema(version, context))), offset: None, }], })), @@ -4760,7 +4794,7 @@ impl WithSchema } #[cfg(feature = "indexmap")] -impl Serialize for IndexSet { +impl Serialize for IndexSet { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { serializer.write_usize(self.len())?; for k in self.iter() { @@ -4771,7 +4805,7 @@ impl Serialize fo } #[cfg(feature = "indexmap")] -impl Deserialize for IndexSet { +impl Deserialize for IndexSet { fn deserialize(deserializer: &mut Deserializer) -> Result { let l = deserializer.read_usize()?; let mut ret = IndexSet::with_capacity(l); @@ -5291,12 +5325,15 @@ impl Introspect for BinaryHeap { } impl ReprC for BinaryHeap {} -impl WithSchema for BinaryHeap { +impl WithSchema for BinaryHeap { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - Schema::Vector(Box::new(context.possible_recursion::(|context|T::schema(version, context))), VecOrStringLayout::Unknown) + Schema::Vector( + Box::new(context.possible_recursion::(|context| T::schema(version, context))), + VecOrStringLayout::Unknown, + ) } } -impl Serialize for BinaryHeap { +impl Serialize for BinaryHeap { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { let l = self.len(); serializer.write_usize(l)?; @@ -5306,7 +5343,7 @@ impl Serialize for BinaryHeap { Ok(()) } } -impl Deserialize for BinaryHeap { +impl Deserialize for BinaryHeap { fn deserialize(deserializer: &mut Deserializer) -> Result { let l = deserializer.read_usize()?; let mut ret = BinaryHeap::with_capacity(l); @@ -5340,19 +5377,22 @@ where } #[cfg(feature = "smallvec")] -impl WithSchema for smallvec::SmallVec +impl WithSchema for smallvec::SmallVec where T::Item: WithSchema, { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - Schema::Vector(Box::new(context.possible_recursion::(|context|T::Item::schema(version, context))), VecOrStringLayout::Unknown) + Schema::Vector( + Box::new(context.possible_recursion::(|context| T::Item::schema(version, context))), + VecOrStringLayout::Unknown, + ) } } #[cfg(feature = "smallvec")] impl ReprC for smallvec::SmallVec {} #[cfg(feature = "smallvec")] -impl Serialize for smallvec::SmallVec +impl Serialize for smallvec::SmallVec where T::Item: Serialize, { @@ -5366,7 +5406,7 @@ where } } #[cfg(feature = "smallvec")] -impl Deserialize for smallvec::SmallVec +impl Deserialize for smallvec::SmallVec where T::Item: Deserialize, { @@ -5411,14 +5451,20 @@ fn regular_serialize_vec( } } -impl WithSchema for Box<[T]> { +impl WithSchema for Box<[T]> { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - Schema::Vector(Box::new(context.possible_recursion::(|context|T::schema(version, context))), VecOrStringLayout::Unknown) + Schema::Vector( + Box::new(context.possible_recursion::(|context| T::schema(version, context))), + VecOrStringLayout::Unknown, + ) } } -impl WithSchema for Arc<[T]> { +impl WithSchema for Arc<[T]> { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - Schema::Vector(Box::new(context.possible_recursion::(|context|T::schema(version, context))), VecOrStringLayout::Unknown) + Schema::Vector( + Box::new(context.possible_recursion::(|context| T::schema(version, context))), + VecOrStringLayout::Unknown, + ) } } impl Introspect for Box<[T]> { @@ -5493,7 +5539,7 @@ impl Deserialize for Arc { } } -impl Serialize for Box<[T]> { +impl Serialize for Box<[T]> { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { unsafe { if T::repr_c_optimization_safe(serializer.file_version).is_false() { @@ -5511,7 +5557,7 @@ impl Serialize for Box<[T]> { } impl ReprC for Box<[T]> {} -impl Serialize for Arc<[T]> { +impl Serialize for Arc<[T]> { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { unsafe { if T::repr_c_optimization_safe(serializer.file_version).is_false() { @@ -5529,12 +5575,12 @@ impl Serialize for Arc<[T]> { } impl ReprC for Arc<[T]> {} -impl Deserialize for Arc<[T]> { +impl Deserialize for Arc<[T]> { fn deserialize(deserializer: &mut Deserializer) -> Result { Ok(Vec::::deserialize(deserializer)?.into()) } } -impl Deserialize for Box<[T]> { +impl Deserialize for Box<[T]> { fn deserialize(deserializer: &mut Deserializer) -> Result { Ok(Vec::::deserialize(deserializer)?.into_boxed_slice()) } @@ -5553,13 +5599,16 @@ impl<'a> Serialize for &'a str { } } -impl<'a, T: WithSchema+'static> WithSchema for &'a [T] { +impl<'a, T: WithSchema + 'static> WithSchema for &'a [T] { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - Schema::Vector(Box::new(context.possible_recursion::(|context|T::schema(version, context))), calculate_slice_memory_layout::()) + Schema::Vector( + Box::new(context.possible_recursion::(|context| 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? } } -impl<'a, T: Serialize + ReprC+'static> Serialize for &'a [T] { +impl<'a, T: Serialize + ReprC + 'static> Serialize for &'a [T] { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { unsafe { if T::repr_c_optimization_safe(serializer.file_version).is_false() { @@ -5579,7 +5628,7 @@ impl<'a, T: Serialize + ReprC+'static> Serialize for &'a [T] { /// Deserialize a slice into a Vec /// Unsized slices cannot be deserialized into unsized slices. -pub fn deserialize_slice_as_vec( +pub fn deserialize_slice_as_vec( deserializer: &mut Deserializer, ) -> Result, SavefileError> { Vec::deserialize(deserializer) @@ -5672,9 +5721,12 @@ fn calculate_string_memory_layout() -> VecOrStringLayout { STRING_IS_STANDARD_LAYOUT.store(is_std, Ordering::Relaxed); return unsafe { std::mem::transmute(is_std) }; } -impl WithSchema for Vec { +impl WithSchema for Vec { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - Schema::Vector(Box::new(context.possible_recursion::(|context|T::schema(version, context))), calculate_vec_memory_layout::()) + Schema::Vector( + Box::new(context.possible_recursion::(|context| T::schema(version, context))), + calculate_vec_memory_layout::(), + ) } } @@ -5694,7 +5746,7 @@ impl Introspect for Vec { } } -impl Serialize for Vec { +impl Serialize for Vec { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { unsafe { if T::repr_c_optimization_safe(serializer.file_version).is_false() { @@ -5731,7 +5783,7 @@ fn regular_deserialize_vec( Ok(ret) } -impl Deserialize for Vec { +impl Deserialize for Vec { fn deserialize(deserializer: &mut Deserializer) -> Result { if unsafe { T::repr_c_optimization_safe(deserializer.file_version) }.is_false() { Ok(regular_deserialize_vec(deserializer)?) @@ -5799,20 +5851,23 @@ impl Introspect for VecDeque { } } -impl WithSchema for VecDeque { +impl WithSchema for VecDeque { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - Schema::Vector(Box::new(context.possible_recursion::(|context|T::schema(version, context))), VecOrStringLayout::Unknown) + Schema::Vector( + Box::new(context.possible_recursion::(|context| T::schema(version, context))), + VecOrStringLayout::Unknown, + ) } } impl ReprC for VecDeque {} -impl Serialize for VecDeque { +impl Serialize for VecDeque { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { regular_serialize_vecdeque(self, serializer) } } -impl Deserialize for VecDeque { +impl Deserialize for VecDeque { fn deserialize(deserializer: &mut Deserializer) -> Result { Ok(regular_deserialize_vecdeque(deserializer)?) } @@ -5927,10 +5982,10 @@ impl ReprC for () { } } -impl WithSchema for [T; N] { +impl WithSchema for [T; N] { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { Schema::Array(SchemaArray { - item_type: Box::new(context.possible_recursion::(|context|T::schema(version, context))), + item_type: Box::new(context.possible_recursion::(|context| T::schema(version, context))), count: N, }) } @@ -5955,7 +6010,7 @@ impl ReprC for [T; N] { T::repr_c_optimization_safe(version) } } -impl Serialize for [T; N] { +impl Serialize for [T; N] { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { unsafe { if T::repr_c_optimization_safe(serializer.file_version).is_false() { @@ -5973,7 +6028,7 @@ impl Serialize for [T; N] { } } -impl Deserialize for [T; N] { +impl Deserialize for [T; N] { fn deserialize(deserializer: &mut Deserializer) -> Result { if unsafe { T::repr_c_optimization_safe(deserializer.file_version) }.is_false() { let mut data: [MaybeUninit; N] = unsafe { @@ -6275,12 +6330,11 @@ impl Deserialize for arrayvec::ArrayVec< use std::ops::{Deref, Range}; impl WithSchema for Box { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - context.possible_recursion::(|context|T::schema(version, context)) - + 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) } @@ -6294,41 +6348,41 @@ impl Deserialize for Box { use std::rc::Rc; impl ReprC for Rc {} -impl WithSchema for Rc { +impl WithSchema for Rc { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - context.possible_recursion::(|context|T::schema(version, context)) + context.possible_recursion::(|context| T::schema(version, context)) } } -impl Serialize for Rc { +impl Serialize for Rc { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { self.deref().serialize(serializer) } } -impl Deserialize for Rc { +impl Deserialize for Rc { fn deserialize(deserializer: &mut Deserializer) -> Result { Ok(Rc::new(T::deserialize(deserializer)?)) } } impl ReprC for Arc {} -impl WithSchema for Arc { +impl WithSchema for Arc { fn schema(version: u32, context: &mut WithSchemaContext) -> Schema { - context.possible_recursion::(|context|T::schema(version, context)) + context.possible_recursion::(|context| T::schema(version, context)) } } -impl Serialize for Arc { +impl Serialize for Arc { fn serialize(&self, serializer: &mut Serializer) -> Result<(), SavefileError> { self.deref().serialize(serializer) } } -impl Deserialize for Arc { +impl Deserialize for Arc { fn deserialize(deserializer: &mut Deserializer) -> Result { Ok(Arc::new(T::deserialize(deserializer)?)) } } #[cfg(feature = "bzip2")] use bzip2::Compression; -use std::any::{Any, TypeId}; +use std::any::Any; use std::cell::Cell; use std::cell::RefCell; use std::collections::hash_map::Entry; diff --git a/savefile/src/prelude.rs b/savefile/src/prelude.rs index bfb560a..b30ab19 100644 --- a/savefile/src/prelude.rs +++ b/savefile/src/prelude.rs @@ -1,11 +1,12 @@ pub use { - super::deserialize_slice_as_vec, super::introspect_item, super::load, super::load_file, super::load_file_noschema, - super::load_from_mem, super::load_noschema, super::save, super::save_file, super::save_file_noschema, - super::save_noschema, super::save_to_mem, super::AbiRemoved, super::Canary1, super::Deserialize, - 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::WithSchemaContext, + super::deserialize_slice_as_vec, super::get_schema, super::introspect_item, super::load, super::load_file, + super::load_file_noschema, super::load_from_mem, super::load_noschema, super::save, super::save_file, + super::save_file_noschema, super::save_noschema, super::save_to_mem, super::AbiRemoved, super::Canary1, + super::Deserialize, 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::WithSchemaContext, }; pub use byteorder::{LittleEndian, ReadBytesExt};