#![allow(incomplete_features)]
#![recursion_limit = "256"]
#![cfg_attr(feature = "nightly", feature(specialization))]
#![deny(missing_docs)]
#![deny(warnings)]
#![allow(clippy::bool_comparison)]
#![allow(clippy::box_default)]
#![allow(clippy::needless_question_mark)]
#![allow(clippy::needless_return)]
#![allow(clippy::manual_try_fold)] #![allow(clippy::needless_range_loop)]
#![allow(clippy::len_zero)]
#![allow(clippy::new_without_default)]
#![allow(clippy::transmute_num_to_bytes)] #![allow(clippy::manual_memcpy)] #![allow(clippy::needless_late_init)]
pub mod prelude;
#[cfg(feature = "serde_derive")]
extern crate serde;
#[cfg(feature = "serde_derive")]
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;
extern crate alloc;
#[cfg(feature = "arrayvec")]
extern crate arrayvec;
extern crate byteorder;
#[cfg(feature = "parking_lot")]
extern crate parking_lot;
#[cfg(feature = "smallvec")]
extern crate smallvec;
#[cfg(feature = "parking_lot")]
use parking_lot::{Mutex, MutexGuard, RwLock, RwLockReadGuard};
use std::borrow::Cow;
use std::fs::File;
use std::io::{BufReader, BufWriter, Read};
use std::io::{ErrorKind, Write};
use std::sync::atomic::{
AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU64, AtomicU8,
AtomicUsize, Ordering,
};
pub use ::byteorder::LittleEndian;
use std::collections::BinaryHeap;
use std::collections::VecDeque;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::hash::Hash;
#[allow(unused_imports)]
use std::mem::MaybeUninit;
#[cfg(feature = "indexmap")]
extern crate indexmap;
#[cfg(feature = "indexmap")]
use indexmap::{IndexMap, IndexSet};
#[cfg(feature = "quickcheck")]
use quickcheck::{Arbitrary, Gen};
#[cfg(feature = "bit-vec")]
extern crate bit_vec;
#[cfg(feature = "bzip2")]
extern crate bzip2;
#[cfg(feature = "bit-set")]
extern crate bit_set;
#[cfg(feature = "rustc-hash")]
extern crate rustc_hash;
extern crate memoffset;
#[cfg(feature = "derive")]
extern crate savefile_derive;
pub const CURRENT_SAVEFILE_LIB_VERSION: u16 = 2;
#[derive(Debug)]
#[must_use]
#[non_exhaustive]
pub enum SavefileError {
IncompatibleSchema {
message: String,
},
IOError {
io_error: std::io::Error,
},
InvalidUtf8 {
msg: String,
},
MemoryAllocationLayoutError,
ArrayvecCapacityError {
msg: String,
},
ShortRead,
CryptographyError,
SizeOverflow,
WrongVersion {
msg: String,
},
GeneralError {
msg: String,
},
PoisonedMutex,
CompressionSupportNotCompiledIn,
InvalidChar,
IncompatibleSavefileLibraryVersion,
MissingMethod {
method_name: String,
},
TooManyArguments,
CalleePanic {
msg: String,
},
LoadLibraryFailed {
libname: String,
msg: String,
},
LoadSymbolFailed {
libname: String,
symbol: String,
msg: String,
},
}
impl From<Utf8Error> for SavefileError {
fn from(value: Utf8Error) -> Self {
SavefileError::InvalidUtf8 {
msg: format!("{:?}", value),
}
}
}
impl Display for SavefileError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
SavefileError::IncompatibleSchema { message } => {
write!(f, "Incompatible schema: {}", message)
}
SavefileError::IOError { io_error } => {
write!(f, "IO error: {}", io_error)
}
SavefileError::InvalidUtf8 { msg } => {
write!(f, "Invalid UTF-8: {}", msg)
}
SavefileError::MemoryAllocationLayoutError => {
write!(f, "Memory allocation layout error")
}
SavefileError::ArrayvecCapacityError { msg } => {
write!(f, "Arrayvec capacity error: {}", msg)
}
SavefileError::ShortRead => {
write!(f, "Short read")
}
SavefileError::CryptographyError => {
write!(f, "Cryptography error")
}
SavefileError::SizeOverflow => {
write!(f, "Size overflow")
}
SavefileError::WrongVersion { msg } => {
write!(f, "Wrong version: {}", msg)
}
SavefileError::GeneralError { msg } => {
write!(f, "General error: {}", msg)
}
SavefileError::PoisonedMutex => {
write!(f, "Poisoned mutex")
}
SavefileError::CompressionSupportNotCompiledIn => {
write!(f, "Compression support missing - recompile with bzip2 feature enabled.")
}
SavefileError::InvalidChar => {
write!(f, "Invalid char value encountered.")
}
SavefileError::IncompatibleSavefileLibraryVersion => {
write!(f, "Incompatible savefile library version. Perhaps a plugin was loaded that is a future unsupported version?")
}
SavefileError::MissingMethod { method_name } => {
write!(f, "Plugin is missing method {}", method_name)
}
SavefileError::TooManyArguments => {
write!(f, "Function has too many arguments")
}
SavefileError::CalleePanic { msg } => {
write!(f, "Invocation target panicked: {}", msg)
}
SavefileError::LoadLibraryFailed { libname, msg } => {
write!(f, "Failed while loading library {}: {}", libname, msg)
}
SavefileError::LoadSymbolFailed { libname, symbol, msg } => {
write!(
f,
"Failed while loading symbol {} from library {}: {}",
symbol, libname, msg
)
}
}
}
}
impl std::error::Error for SavefileError {}
impl SavefileError {
pub fn general(something: impl Display) -> SavefileError {
SavefileError::GeneralError {
msg: format!("{}", something),
}
}
}
pub struct Serializer<'a, W: Write> {
pub writer: &'a mut W,
pub file_version: u32,
}
pub struct Deserializer<'a, R: Read> {
pub reader: &'a mut R,
pub file_version: u32,
pub ephemeral_state: HashMap<TypeId, Box<dyn Any>>,
}
impl<TR: Read> Deserializer<'_, TR> {
pub fn get_state<T: 'static, R: Default + 'static>(&mut self) -> &mut R {
let type_id = TypeId::of::<T>();
let the_any = self
.ephemeral_state
.entry(type_id)
.or_insert_with(|| Box::new(R::default()));
the_any.downcast_mut().unwrap()
}
}
#[derive(Default, Debug)]
pub struct IsPacked(bool);
#[doc(hidden)]
#[deprecated(since = "0.17.0", note = "The 'IsReprC' type has been renamed to 'IsPacked'.")]
pub type IsReprC = IsPacked;
impl std::ops::BitAnd<IsPacked> for IsPacked {
type Output = IsPacked;
fn bitand(self, rhs: Self) -> Self::Output {
IsPacked(self.0 && rhs.0)
}
}
impl IsPacked {
pub unsafe fn yes() -> IsPacked {
IsPacked(true)
}
pub fn no() -> IsPacked {
IsPacked(false)
}
#[inline(always)]
pub fn is_false(self) -> bool {
!self.0
}
#[inline(always)]
pub fn is_yes(self) -> bool {
self.0
}
}
#[cfg_attr(
feature = "rust1_78",
diagnostic::on_unimplemented(
message = "`{Self}` cannot be serialized or deserialized by Savefile, since it doesn't implement trait `savefile::Packed`",
label = "This cannot be serialized or deserialized",
note = "You can implement it by adding `#[derive(Savefile)]` before the declaration of `{Self}`",
note = "Or you can manually implement the `savefile::Packed` trait."
)
)]
pub trait Packed {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::no()
}
}
#[doc(hidden)]
#[deprecated(since = "0.17.0", note = "The 'ReprC' trait has been renamed to 'Packed'.")]
pub struct DeliberatelyUnimplementable {
#[allow(dead_code)]
private: (),
}
#[deprecated(since = "0.17.0", note = "The 'ReprC' trait has been renamed to 'Packed'.")]
#[doc(hidden)]
#[cfg_attr(
feature = "rust1_78",
diagnostic::on_unimplemented(
message = "ReprC has been deprecated and must not be used. Use trait `savefile::Packed` instead!",
label = "ReprC was erroneously required here",
note = "Please change any `ReprC` bounds into `Packed` bounds.",
)
)]
pub trait ReprC {
#[deprecated(since = "0.17.0", note = "The 'ReprC' trait has been renamed to 'Packed'.")]
#[doc(hidden)]
#[allow(non_snake_case)]
#[allow(deprecated)]
fn this_is_a_placeholder__if_you_see_this_it_is_likely_that_you_have_code_that_refers_to_ReprC_trait__this_trait_has_been_renamed_to__Packed(
) -> DeliberatelyUnimplementable;
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::no()
}
}
impl From<std::io::Error> for SavefileError {
fn from(s: std::io::Error) -> SavefileError {
SavefileError::IOError { io_error: s }
}
}
impl<T> From<std::sync::PoisonError<T>> for SavefileError {
fn from(_: std::sync::PoisonError<T>) -> SavefileError {
SavefileError::PoisonedMutex
}
}
impl From<std::string::FromUtf8Error> for SavefileError {
fn from(s: std::string::FromUtf8Error) -> SavefileError {
SavefileError::InvalidUtf8 { msg: s.to_string() }
}
}
#[cfg(feature = "arrayvec")]
impl<T> From<arrayvec::CapacityError<T>> for SavefileError {
fn from(s: arrayvec::CapacityError<T>) -> SavefileError {
SavefileError::ArrayvecCapacityError { msg: s.to_string() }
}
}
impl WithSchema for PathBuf {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_string(VecOrStringLayout::Unknown))
}
}
impl Serialize for PathBuf {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let as_string: String = self.to_string_lossy().to_string();
as_string.serialize(serializer)
}
}
impl Packed for PathBuf {}
impl Deserialize for PathBuf {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(PathBuf::from(String::deserialize(deserializer)?))
}
}
impl Introspect for PathBuf {
fn introspect_value(&self) -> String {
self.to_string_lossy().to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem>> {
None
}
}
impl<'a, T: 'a + WithSchema + ToOwned + ?Sized> WithSchema for Cow<'a, T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
T::schema(version, context)
}
}
impl<'a, T: 'a + ToOwned + ?Sized> Packed for Cow<'a, T> {}
impl<'a, T: 'a + Serialize + ToOwned + ?Sized> Serialize for Cow<'a, T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
(**self).serialize(serializer)
}
}
impl<'a, T: 'a + WithSchema + ToOwned + ?Sized> Deserialize for Cow<'a, T>
where
T::Owned: Deserialize,
{
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Cow<'a, T>, SavefileError> {
Ok(Cow::Owned(<T as ToOwned>::Owned::deserialize(deserializer)?))
}
}
impl<'a, T: 'a + Introspect + ToOwned + ?Sized> Introspect for Cow<'a, T> {
fn introspect_value(&self) -> String {
(**self).introspect_value()
}
fn introspect_child<'b>(&'b self, index: usize) -> Option<Box<dyn IntrospectItem<'b> + 'b>> {
(**self).introspect_child(index)
}
fn introspect_len(&self) -> usize {
(**self).introspect_len()
}
}
impl WithSchema for std::io::Error {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::StdIoError
}
}
impl Packed for std::io::Error {}
impl Serialize for std::io::Error {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let kind = match self.kind() {
ErrorKind::NotFound => 1,
ErrorKind::PermissionDenied => 2,
ErrorKind::ConnectionRefused => 3,
ErrorKind::ConnectionReset => 4,
ErrorKind::ConnectionAborted => 7,
ErrorKind::NotConnected => 8,
ErrorKind::AddrInUse => 9,
ErrorKind::AddrNotAvailable => 10,
ErrorKind::BrokenPipe => 12,
ErrorKind::AlreadyExists => 13,
ErrorKind::WouldBlock => 14,
ErrorKind::InvalidInput => 21,
ErrorKind::InvalidData => 22,
ErrorKind::TimedOut => 23,
ErrorKind::WriteZero => 24,
ErrorKind::Interrupted => 36,
ErrorKind::Unsupported => 37,
ErrorKind::UnexpectedEof => 38,
ErrorKind::OutOfMemory => 39,
ErrorKind::Other => 40,
_ => 42,
};
serializer.write_u16(kind as u16)?;
serializer.write_string(&self.to_string())?;
Ok(())
}
}
impl Deserialize for std::io::Error {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let kind = deserializer.read_u16()?;
let kind = match kind {
1 => ErrorKind::NotFound,
2 => ErrorKind::PermissionDenied,
3 => ErrorKind::ConnectionRefused,
4 => ErrorKind::ConnectionReset,
7 => ErrorKind::ConnectionAborted,
8 => ErrorKind::NotConnected,
9 => ErrorKind::AddrInUse,
10 => ErrorKind::AddrNotAvailable,
12 => ErrorKind::BrokenPipe,
13 => ErrorKind::AlreadyExists,
14 => ErrorKind::WouldBlock,
21 => ErrorKind::InvalidInput,
22 => ErrorKind::InvalidData,
23 => ErrorKind::TimedOut,
24 => ErrorKind::WriteZero,
36 => ErrorKind::Interrupted,
37 => ErrorKind::Unsupported,
38 => ErrorKind::UnexpectedEof,
39 => ErrorKind::OutOfMemory,
40 => ErrorKind::Other,
_ => ErrorKind::Other,
};
let string = String::deserialize(deserializer)?;
Ok(std::io::Error::new(kind, string))
}
}
#[cfg(feature = "ring")]
mod crypto {
use ring::aead;
use ring::aead::{BoundKey, Nonce, NonceSequence, OpeningKey, SealingKey, UnboundKey, AES_256_GCM};
use ring::error::Unspecified;
use std::fs::File;
use std::io::{Error, ErrorKind, Read, Write};
use std::path::Path;
extern crate rand;
use crate::{Deserialize, Deserializer, SavefileError, Serialize, Serializer, WithSchema};
use byteorder::WriteBytesExt;
use byteorder::{LittleEndian, ReadBytesExt};
use rand::rngs::OsRng;
use rand::RngCore;
extern crate ring;
#[derive(Debug)]
struct RandomNonceSequence {
data1: u64,
data2: u32,
}
impl RandomNonceSequence {
pub fn new() -> RandomNonceSequence {
RandomNonceSequence {
data1: OsRng.next_u64(),
data2: OsRng.next_u32(),
}
}
pub fn serialize(&self, writer: &mut dyn Write) -> Result<(), SavefileError> {
writer.write_u64::<LittleEndian>(self.data1)?;
writer.write_u32::<LittleEndian>(self.data2)?;
Ok(())
}
pub fn deserialize(reader: &mut dyn Read) -> Result<RandomNonceSequence, SavefileError> {
Ok(RandomNonceSequence {
data1: reader.read_u64::<LittleEndian>()?,
data2: reader.read_u32::<LittleEndian>()?,
})
}
}
impl NonceSequence for RandomNonceSequence {
fn advance(&mut self) -> Result<Nonce, Unspecified> {
self.data2 = self.data2.wrapping_add(1);
if self.data2 == 0 {
self.data1 = self.data1.wrapping_add(1);
}
let mut bytes = [0u8; 12];
let bytes1: [u8; 8] = self.data1.to_le_bytes();
let bytes2: [u8; 4] = self.data2.to_le_bytes();
for i in 0..8 {
bytes[i] = bytes1[i];
}
for i in 0..4 {
bytes[i + 8] = bytes2[i];
}
Ok(Nonce::assume_unique_for_key(bytes))
}
}
pub struct CryptoWriter<'a> {
writer: &'a mut dyn Write,
buf: Vec<u8>,
sealkey: SealingKey<RandomNonceSequence>,
failed: bool,
}
pub struct CryptoReader<'a> {
reader: &'a mut dyn Read,
buf: Vec<u8>,
offset: usize,
openingkey: OpeningKey<RandomNonceSequence>,
}
impl<'a> CryptoReader<'a> {
pub fn new(reader: &'a mut dyn Read, key_bytes: [u8; 32]) -> Result<CryptoReader<'a>, SavefileError> {
let unboundkey = UnboundKey::new(&AES_256_GCM, &key_bytes).unwrap();
let nonce_sequence = RandomNonceSequence::deserialize(reader)?;
let openingkey = OpeningKey::new(unboundkey, nonce_sequence);
Ok(CryptoReader {
reader,
offset: 0,
buf: Vec::new(),
openingkey,
})
}
}
const CRYPTO_BUFSIZE: usize = 100_000;
impl Drop for CryptoWriter<'_> {
fn drop(&mut self) {
self.flush().expect("The implicit flush in the Drop of CryptoWriter failed. This causes this panic. If you want to be able to handle this, make sure to call flush() manually. If a manual flush has failed, Drop won't panic.");
}
}
impl<'a> CryptoWriter<'a> {
pub fn new(writer: &'a mut dyn Write, key_bytes: [u8; 32]) -> Result<CryptoWriter<'a>, SavefileError> {
let unboundkey = UnboundKey::new(&AES_256_GCM, &key_bytes).unwrap();
let nonce_sequence = RandomNonceSequence::new();
nonce_sequence.serialize(writer)?;
let sealkey = SealingKey::new(unboundkey, nonce_sequence);
Ok(CryptoWriter {
writer,
buf: Vec::new(),
sealkey,
failed: false,
})
}
pub fn flush_final(mut self) -> Result<(), SavefileError> {
if self.failed {
panic!("Call to failed CryptoWriter");
}
self.flush()?;
Ok(())
}
}
impl Read for CryptoReader<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
loop {
if buf.len() <= self.buf.len() - self.offset {
buf.clone_from_slice(&self.buf[self.offset..self.offset + buf.len()]);
self.offset += buf.len();
return Ok(buf.len());
}
{
let oldlen = self.buf.len();
let newlen = self.buf.len() - self.offset;
self.buf.copy_within(self.offset..oldlen, 0);
self.buf.resize(newlen, 0);
self.offset = 0;
}
let mut sizebuf = [0; 8];
let mut sizebuf_bytes_read = 0;
loop {
match self.reader.read(&mut sizebuf[sizebuf_bytes_read..]) {
Ok(gotsize) => {
if gotsize == 0 {
if sizebuf_bytes_read == 0 {
let cur_content_size = self.buf.len() - self.offset;
buf[0..cur_content_size]
.clone_from_slice(&self.buf[self.offset..self.offset + cur_content_size]);
self.offset += cur_content_size;
return Ok(cur_content_size);
} else {
return Err(Error::new(ErrorKind::UnexpectedEof, "Unexpected EOF"));
}
} else {
sizebuf_bytes_read += gotsize;
assert!(sizebuf_bytes_read <= 8);
}
}
Err(err) => return Err(err),
}
if sizebuf_bytes_read == 8 {
break;
}
}
use byteorder::ByteOrder;
let curlen = byteorder::LittleEndian::read_u64(&sizebuf) as usize;
if curlen > CRYPTO_BUFSIZE + 16 {
return Err(Error::new(ErrorKind::Other, "Cryptography error"));
}
let orglen = self.buf.len();
self.buf.resize(orglen + curlen, 0);
self.reader.read_exact(&mut self.buf[orglen..orglen + curlen])?;
match self
.openingkey
.open_in_place(aead::Aad::empty(), &mut self.buf[orglen..])
{
Ok(_) => {}
Err(_) => {
return Err(Error::new(ErrorKind::Other, "Cryptography error"));
}
}
self.buf.resize(self.buf.len() - 16, 0);
}
}
}
impl Write for CryptoWriter<'_> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
if self.failed {
panic!("Call to failed CryptoWriter");
}
self.buf.extend(buf);
if self.buf.len() > CRYPTO_BUFSIZE {
self.flush()?;
}
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Error> {
self.failed = true;
let mut offset = 0;
let mut tempbuf = Vec::new();
if self.buf.len() > CRYPTO_BUFSIZE {
tempbuf = Vec::<u8>::with_capacity(CRYPTO_BUFSIZE + 16);
}
while self.buf.len() > offset {
let curbuf;
if offset == 0 && self.buf.len() <= CRYPTO_BUFSIZE {
curbuf = &mut self.buf;
} else {
let chunksize = (self.buf.len() - offset).min(CRYPTO_BUFSIZE);
tempbuf.resize(chunksize, 0u8);
tempbuf.clone_from_slice(&self.buf[offset..offset + chunksize]);
curbuf = &mut tempbuf;
}
let expected_final_len = curbuf.len() as u64 + 16;
debug_assert!(expected_final_len <= CRYPTO_BUFSIZE as u64 + 16);
self.writer.write_u64::<LittleEndian>(expected_final_len)?; match self.sealkey.seal_in_place_append_tag(aead::Aad::empty(), curbuf) {
Ok(_) => {}
Err(_) => {
return Err(Error::new(ErrorKind::Other, "Cryptography error"));
}
}
debug_assert!(curbuf.len() == expected_final_len as usize, "The size of the TAG generated by the AES 256 GCM in ring seems to have changed! This is very unexpected. File a bug on the savefile-crate");
self.writer.write_all(&curbuf[..])?;
self.writer.flush()?;
offset += curbuf.len() - 16;
curbuf.resize(curbuf.len() - 16, 0);
}
self.buf.clear();
self.failed = false;
Ok(())
}
}
pub fn save_encrypted_file<T: WithSchema + Serialize, P: AsRef<Path>>(
filepath: P,
version: u32,
data: &T,
password: &str,
) -> Result<(), SavefileError> {
use ring::digest;
let actual = digest::digest(&digest::SHA256, password.as_bytes());
let mut key = [0u8; 32];
let password_hash = actual.as_ref();
assert_eq!(password_hash.len(), key.len(), "A SHA256 sum must be 32 bytes");
key.clone_from_slice(password_hash);
let mut f = File::create(filepath)?;
let mut writer = CryptoWriter::new(&mut f, key)?;
Serializer::<CryptoWriter>::save::<T>(&mut writer, version, data, true)?;
writer.flush()?;
Ok(())
}
pub fn load_encrypted_file<T: WithSchema + Deserialize, P: AsRef<Path>>(
filepath: P,
version: u32,
password: &str,
) -> Result<T, SavefileError> {
use ring::digest;
let actual = digest::digest(&digest::SHA256, password.as_bytes());
let mut key = [0u8; 32];
let password_hash = actual.as_ref();
assert_eq!(password_hash.len(), key.len(), "A SHA256 sum must be 32 bytes");
key.clone_from_slice(password_hash);
let mut f = File::open(filepath)?;
let mut reader = CryptoReader::new(&mut f, key).unwrap();
Deserializer::<CryptoReader>::load::<T>(&mut reader, version)
}
}
#[cfg(feature = "ring")]
pub use crypto::{load_encrypted_file, save_encrypted_file, CryptoReader, CryptoWriter};
impl<'a, W: Write + 'a> Serializer<'a, W> {
#[inline(always)]
pub fn write_bool(&mut self, v: bool) -> Result<(), SavefileError> {
Ok(self.writer.write_u8(if v { 1 } else { 0 })?)
}
#[inline(always)]
pub fn write_u8(&mut self, v: u8) -> Result<(), SavefileError> {
Ok(self.writer.write_all(&[v])?)
}
#[inline(always)]
pub fn write_i8(&mut self, v: i8) -> Result<(), SavefileError> {
Ok(self.writer.write_i8(v)?)
}
#[inline(always)]
pub fn write_u16(&mut self, v: u16) -> Result<(), SavefileError> {
Ok(self.writer.write_u16::<LittleEndian>(v)?)
}
#[inline(always)]
pub fn write_i16(&mut self, v: i16) -> Result<(), SavefileError> {
Ok(self.writer.write_i16::<LittleEndian>(v)?)
}
#[inline(always)]
pub fn write_u32(&mut self, v: u32) -> Result<(), SavefileError> {
Ok(self.writer.write_u32::<LittleEndian>(v)?)
}
#[inline(always)]
pub fn write_i32(&mut self, v: i32) -> Result<(), SavefileError> {
Ok(self.writer.write_i32::<LittleEndian>(v)?)
}
#[inline(always)]
pub fn write_f32(&mut self, v: f32) -> Result<(), SavefileError> {
Ok(self.writer.write_f32::<LittleEndian>(v)?)
}
#[inline(always)]
pub fn write_f64(&mut self, v: f64) -> Result<(), SavefileError> {
Ok(self.writer.write_f64::<LittleEndian>(v)?)
}
#[inline(always)]
pub fn write_u64(&mut self, v: u64) -> Result<(), SavefileError> {
Ok(self.writer.write_u64::<LittleEndian>(v)?)
}
#[inline(always)]
pub unsafe fn write_raw_ptr<T: ?Sized>(&mut self, data: *const T) -> Result<(), SavefileError> {
let temp = &data as *const *const T;
let temp_data = temp as *const u8;
let buf = slice::from_raw_parts(temp_data, std::mem::size_of::<*const T>());
self.write_bytes(buf)
}
pub unsafe fn write_raw_ptr_size<T>(&mut self, data: *const T, len: usize) -> Result<(), SavefileError> {
self.write_raw_ptr(data)?;
self.write_usize(len)?;
Ok(())
}
#[inline(always)]
pub fn write_ptr(&mut self, v: *const ()) -> Result<(), SavefileError> {
let slice_to_write = unsafe {
std::slice::from_raw_parts(&v as *const *const () as *const u8, std::mem::size_of::<*const ()>())
};
Ok(self.writer.write_all(slice_to_write)?)
}
#[inline(always)]
pub fn write_i64(&mut self, v: i64) -> Result<(), SavefileError> {
Ok(self.writer.write_i64::<LittleEndian>(v)?)
}
#[inline(always)]
pub fn write_u128(&mut self, v: u128) -> Result<(), SavefileError> {
Ok(self.writer.write_u128::<LittleEndian>(v)?)
}
#[inline(always)]
pub fn write_i128(&mut self, v: i128) -> Result<(), SavefileError> {
Ok(self.writer.write_i128::<LittleEndian>(v)?)
}
#[inline(always)]
pub fn write_usize(&mut self, v: usize) -> Result<(), SavefileError> {
Ok(self.writer.write_u64::<LittleEndian>(v as u64)?)
}
#[inline(always)]
pub fn write_isize(&mut self, v: isize) -> Result<(), SavefileError> {
Ok(self.writer.write_i64::<LittleEndian>(v as i64)?)
}
#[inline(always)]
pub fn write_buf(&mut self, v: &[u8]) -> Result<(), SavefileError> {
Ok(self.writer.write_all(v)?)
}
#[inline(always)]
pub fn write_string(&mut self, v: &str) -> Result<(), SavefileError> {
let asb = v.as_bytes();
self.write_usize(asb.len())?;
Ok(self.writer.write_all(asb)?)
}
#[inline(always)]
pub fn write_bytes(&mut self, v: &[u8]) -> Result<(), SavefileError> {
Ok(self.writer.write_all(v)?)
}
#[inline(always)]
#[doc(hidden)]
pub unsafe fn raw_write_region<T, T1: Packed, T2: Packed>(
&mut self,
full: &T,
t1: &T1,
t2: &T2,
version: u32,
) -> Result<(), SavefileError> {
assert!(T1::repr_c_optimization_safe(version).is_yes());
assert!(T2::repr_c_optimization_safe(version).is_yes());
let base = full as *const T as *const u8;
let totlen = std::mem::size_of::<T>();
let p1 = (t1 as *const T1 as *const u8) as usize;
let p2 = (t2 as *const T2 as *const u8) as usize;
let start = p1 - (base as usize);
let end = (p2 - (base as usize)) + std::mem::size_of::<T2>();
let full_slice = std::slice::from_raw_parts(base, totlen);
Ok(self.writer.write_all(&full_slice[start..end])?)
}
pub fn save<T: WithSchema + Serialize>(
writer: &mut W,
version: u32,
data: &T,
with_compression: bool,
) -> Result<(), SavefileError> {
Ok(Self::save_impl(
writer,
version,
data,
Some(T::schema(version, &mut WithSchemaContext::new())),
with_compression,
None,
)?)
}
pub fn save_noschema<T: Serialize>(writer: &mut W, version: u32, data: &T) -> Result<(), SavefileError> {
Ok(Self::save_impl(writer, version, data, None, false, None)?)
}
#[doc(hidden)]
pub fn save_noschema_internal<T: Serialize>(
writer: &mut W,
version: u32,
data: &T,
lib_version_override: u16,
) -> Result<(), SavefileError> {
Ok(Self::save_impl(
writer,
version,
data,
None,
false,
Some(lib_version_override),
)?)
}
pub fn bare_serialize<T: Serialize>(writer: &mut W, file_version: u32, data: &T) -> Result<(), SavefileError> {
let mut serializer = Serializer { writer, file_version };
data.serialize(&mut serializer)?;
writer.flush()?;
Ok(())
}
#[inline(always)]
fn save_impl<T: Serialize>(
writer: &mut W,
version: u32,
data: &T,
with_schema: Option<Schema>,
with_compression: bool,
lib_version_override: Option<u16>,
) -> Result<(), SavefileError> {
let header = "savefile\0".to_string().into_bytes();
writer.write_all(&header)?;
writer.write_u16::<LittleEndian>(
lib_version_override.unwrap_or(CURRENT_SAVEFILE_LIB_VERSION),
)?;
writer.write_u32::<LittleEndian>(version)?;
{
if with_compression {
writer.write_u8(1)?;
#[cfg(feature = "bzip2")]
{
let mut compressed_writer = bzip2::write::BzEncoder::new(writer, Compression::best());
if let Some(schema) = with_schema {
let mut schema_serializer = Serializer::<bzip2::write::BzEncoder<W>>::new_raw(
&mut compressed_writer,
lib_version_override.unwrap_or(CURRENT_SAVEFILE_LIB_VERSION) as u32,
);
schema.serialize(&mut schema_serializer)?;
}
let mut serializer = Serializer {
writer: &mut compressed_writer,
file_version: version,
}; data.serialize(&mut serializer)?;
compressed_writer.flush()?;
return Ok(());
}
#[cfg(not(feature = "bzip2"))]
{
return Err(SavefileError::CompressionSupportNotCompiledIn);
}
} else {
writer.write_u8(0)?;
if let Some(schema) = with_schema {
let mut schema_serializer = Serializer::<W>::new_raw(
writer,
lib_version_override.unwrap_or(CURRENT_SAVEFILE_LIB_VERSION) as u32,
);
schema.serialize(&mut schema_serializer)?;
}
let mut serializer = Serializer {
writer,
file_version: version,
};
data.serialize(&mut serializer)?;
writer.flush()?;
Ok(())
}
}
}
pub fn new_raw(writer: &mut impl Write, file_version: u32) -> Serializer<impl Write> {
Serializer { writer, file_version }
}
}
impl<TR: Read> Deserializer<'_, TR> {
pub fn read_bool(&mut self) -> Result<bool, SavefileError> {
Ok(self.reader.read_u8()? == 1)
}
pub fn read_u8(&mut self) -> Result<u8, SavefileError> {
let mut buf = [0u8];
self.reader.read_exact(&mut buf)?;
Ok(buf[0])
}
pub fn read_u16(&mut self) -> Result<u16, SavefileError> {
Ok(self.reader.read_u16::<LittleEndian>()?)
}
pub fn read_u32(&mut self) -> Result<u32, SavefileError> {
Ok(self.reader.read_u32::<LittleEndian>()?)
}
pub fn read_u64(&mut self) -> Result<u64, SavefileError> {
Ok(self.reader.read_u64::<LittleEndian>()?)
}
pub unsafe fn read_raw_ptr<T: ?Sized>(&mut self) -> Result<*const T, SavefileError> {
let mut temp = MaybeUninit::<*const T>::zeroed();
let temp_data = &mut temp as *mut MaybeUninit<*const T> as *mut u8;
let temp_size = std::mem::size_of::<*const T>();
let buf = unsafe { slice::from_raw_parts_mut(temp_data, temp_size) };
self.read_bytes_to_buf(buf)?;
Ok(unsafe { temp.assume_init() })
}
pub unsafe fn read_raw_ptr_mut<T: ?Sized>(&mut self) -> Result<*mut T, SavefileError> {
let mut temp = MaybeUninit::<*mut T>::zeroed();
let temp_data = &mut temp as *mut MaybeUninit<*mut T> as *mut u8;
let temp_size = std::mem::size_of::<*mut T>();
let buf = unsafe { slice::from_raw_parts_mut(temp_data, temp_size) };
self.read_bytes_to_buf(buf)?;
Ok(unsafe { temp.assume_init() })
}
pub fn read_ptr(&mut self) -> Result<*const (), SavefileError> {
let mut ptr: MaybeUninit<*const ()> = MaybeUninit::zeroed();
let data = ptr.as_mut_ptr();
let target = unsafe { slice::from_raw_parts_mut(data as *mut u8, std::mem::size_of::<*const ()>()) };
self.reader.read_exact(target)?;
Ok(unsafe { ptr.assume_init() })
}
pub fn read_u128(&mut self) -> Result<u128, SavefileError> {
Ok(self.reader.read_u128::<LittleEndian>()?)
}
pub fn read_i8(&mut self) -> Result<i8, SavefileError> {
Ok(self.reader.read_i8()?)
}
pub fn read_i16(&mut self) -> Result<i16, SavefileError> {
Ok(self.reader.read_i16::<LittleEndian>()?)
}
pub fn read_i32(&mut self) -> Result<i32, SavefileError> {
Ok(self.reader.read_i32::<LittleEndian>()?)
}
pub fn read_i64(&mut self) -> Result<i64, SavefileError> {
Ok(self.reader.read_i64::<LittleEndian>()?)
}
pub fn read_i128(&mut self) -> Result<i128, SavefileError> {
Ok(self.reader.read_i128::<LittleEndian>()?)
}
pub fn read_f32(&mut self) -> Result<f32, SavefileError> {
Ok(self.reader.read_f32::<LittleEndian>()?)
}
pub fn read_f64(&mut self) -> Result<f64, SavefileError> {
Ok(self.reader.read_f64::<LittleEndian>()?)
}
pub fn read_isize(&mut self) -> Result<isize, SavefileError> {
if let Ok(val) = TryFrom::try_from(self.reader.read_i64::<LittleEndian>()?) {
Ok(val)
} else {
Err(SavefileError::SizeOverflow)
}
}
pub fn read_usize(&mut self) -> Result<usize, SavefileError> {
if let Ok(val) = TryFrom::try_from(self.reader.read_u64::<LittleEndian>()?) {
Ok(val)
} else {
Err(SavefileError::SizeOverflow)
}
}
pub fn read_string(&mut self) -> Result<String, SavefileError> {
let l = self.read_usize()?;
#[cfg(feature = "size_sanity_checks")]
{
if l > 1_000_000 {
return Err(SavefileError::GeneralError {
msg: format!("String too large: {}", l),
});
}
}
let mut v = vec![0; l];
self.reader.read_exact(&mut v)?;
Ok(String::from_utf8(v)?)
}
pub fn read_bytes(&mut self, len: usize) -> Result<Vec<u8>, SavefileError> {
let mut v = vec![0; len];
self.reader.read_exact(&mut v)?;
Ok(v)
}
pub fn read_bytes_to_buf(&mut self, buf: &mut [u8]) -> Result<(), SavefileError> {
self.reader.read_exact(buf)?;
Ok(())
}
pub fn load<T: WithSchema + Deserialize>(reader: &mut TR, version: u32) -> Result<T, SavefileError> {
Deserializer::<_>::load_impl::<T>(
reader,
version,
Some(|version| T::schema(version, &mut WithSchemaContext::new())),
)
}
pub fn load_noschema<T: Deserialize>(reader: &mut TR, version: u32) -> Result<T, SavefileError> {
let dummy: Option<fn(u32) -> Schema> = None;
Deserializer::<TR>::load_impl::<T>(reader, version, dummy)
}
pub fn bare_deserialize<T: Deserialize>(reader: &mut TR, file_version: u32) -> Result<T, SavefileError> {
let mut deserializer = Deserializer {
reader,
file_version,
ephemeral_state: HashMap::new(),
};
Ok(T::deserialize(&mut deserializer)?)
}
#[inline(always)]
fn load_impl<T: Deserialize>(
reader: &mut TR,
version: u32,
expected_schema: Option<impl FnOnce(u32) -> Schema>,
) -> Result<T, SavefileError> {
let mut head: [u8; 9] = [0u8; 9];
reader.read_exact(&mut head)?;
if head[..] != ("savefile\0".to_string().into_bytes())[..] {
return Err(SavefileError::GeneralError {
msg: "File is not in new savefile-format.".into(),
});
}
let savefile_lib_version = reader.read_u16::<LittleEndian>()?;
if savefile_lib_version > CURRENT_SAVEFILE_LIB_VERSION {
return Err(SavefileError::GeneralError {
msg: "This file has been created by a future, incompatible version of the savefile crate.".into(),
});
}
let file_ver = reader.read_u32::<LittleEndian>()?;
if file_ver > version {
return Err(SavefileError::WrongVersion {
msg: format!(
"File has later version ({}) than structs in memory ({}).",
file_ver, version
),
});
}
let with_compression = reader.read_u8()? != 0;
if with_compression {
#[cfg(feature = "bzip2")]
{
let mut compressed_reader = bzip2::read::BzDecoder::new(reader);
if let Some(memory_schema) = expected_schema {
let mut schema_deserializer = new_schema_deserializer(&mut compressed_reader, savefile_lib_version);
let memory_schema = memory_schema(file_ver);
let file_schema = Schema::deserialize(&mut schema_deserializer)?;
if let Some(err) = diff_schema(&memory_schema, &file_schema, ".".to_string(), false) {
return Err(SavefileError::IncompatibleSchema {
message: format!(
"Saved schema differs from in-memory schema for version {}. Error: {}",
file_ver, err
),
});
}
}
let mut deserializer = Deserializer {
reader: &mut compressed_reader,
file_version: file_ver,
ephemeral_state: HashMap::new(),
};
Ok(T::deserialize(&mut deserializer)?)
}
#[cfg(not(feature = "bzip2"))]
{
return Err(SavefileError::CompressionSupportNotCompiledIn);
}
} else {
if let Some(memory_schema) = expected_schema {
let mut schema_deserializer = new_schema_deserializer(reader, savefile_lib_version);
let memory_schema = memory_schema(file_ver);
let file_schema = Schema::deserialize(&mut schema_deserializer)?;
if let Some(err) = diff_schema(&memory_schema, &file_schema, ".".to_string(), false) {
return Err(SavefileError::IncompatibleSchema {
message: format!(
"Saved schema differs from in-memory schema for version {}. Error: {}",
file_ver, err
),
});
}
}
let mut deserializer = Deserializer {
reader,
file_version: file_ver,
ephemeral_state: HashMap::new(),
};
Ok(T::deserialize(&mut deserializer)?)
}
}
}
pub fn new_schema_deserializer(reader: &mut impl Read, file_schema_version: u16) -> Deserializer<impl Read> {
Deserializer {
reader,
file_version: file_schema_version as u32,
ephemeral_state: HashMap::new(),
}
}
pub fn load<T: WithSchema + Deserialize>(reader: &mut impl Read, version: u32) -> Result<T, SavefileError> {
Deserializer::<_>::load::<T>(reader, version)
}
pub fn load_from_mem<T: WithSchema + Deserialize>(input: &[u8], version: u32) -> Result<T, SavefileError> {
let mut input = input;
Deserializer::load::<T>(&mut input, version)
}
pub fn save<T: WithSchema + Serialize>(writer: &mut impl Write, version: u32, data: &T) -> Result<(), SavefileError> {
Serializer::save::<T>(writer, version, data, false)
}
pub fn save_compressed<T: WithSchema + Serialize>(
writer: &mut impl Write,
version: u32,
data: &T,
) -> Result<(), SavefileError> {
Serializer::save::<T>(writer, version, data, true)
}
pub fn save_file_compressed<T: WithSchema + Serialize, P: AsRef<Path>>(
path: P,
version: u32,
data: &T,
) -> Result<(), SavefileError> {
let mut f = BufWriter::new(File::create(path)?);
Serializer::save::<T>(&mut f, version, data, true)
}
pub fn save_to_mem<T: WithSchema + Serialize>(version: u32, data: &T) -> Result<Vec<u8>, SavefileError> {
let mut retval = Vec::new();
Serializer::save::<T>(&mut retval, version, data, false)?;
Ok(retval)
}
pub fn load_noschema<T: Deserialize>(reader: &mut impl Read, version: u32) -> Result<T, SavefileError> {
Deserializer::<_>::load_noschema::<T>(reader, version)
}
pub fn save_noschema<T: Serialize>(writer: &mut impl Write, version: u32, data: &T) -> Result<(), SavefileError> {
Serializer::save_noschema::<T>(writer, version, data)
}
pub fn load_file<T: WithSchema + Deserialize, P: AsRef<Path>>(filepath: P, version: u32) -> Result<T, SavefileError> {
let mut f = BufReader::new(File::open(filepath)?);
Deserializer::load::<T>(&mut f, version)
}
pub fn save_file<T: WithSchema + Serialize, P: AsRef<Path>>(
filepath: P,
version: u32,
data: &T,
) -> Result<(), SavefileError> {
let mut f = BufWriter::new(File::create(filepath)?);
Serializer::save::<T>(&mut f, version, data, false)
}
pub fn load_file_noschema<T: Deserialize, P: AsRef<Path>>(filepath: P, version: u32) -> Result<T, SavefileError> {
let mut f = BufReader::new(File::open(filepath)?);
Deserializer::load_noschema::<T>(&mut f, version)
}
pub fn save_file_noschema<T: Serialize, P: AsRef<Path>>(
filepath: P,
version: u32,
data: &T,
) -> Result<(), SavefileError> {
let mut f = BufWriter::new(File::create(filepath)?);
Serializer::save_noschema::<T>(&mut f, version, data)
}
pub struct WithSchemaContext {
seen_types: HashMap<TypeId, usize >,
}
impl WithSchemaContext {
pub fn new() -> WithSchemaContext {
let seen_types = HashMap::new();
WithSchemaContext { seen_types }
}
}
impl WithSchemaContext {
pub fn possible_recursion<T: 'static>(&mut self, cb: impl FnOnce(&mut WithSchemaContext) -> Schema) -> Schema {
let typeid = TypeId::of::<T>();
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
}
}
#[cfg_attr(
feature = "rust1_78",
diagnostic::on_unimplemented(
message = "`{Self}` does not have a defined schema for savefile, since it doesn't implement the trait `savefile::WithSchema`",
label = "This cannot be serialized or deserialized with a schema",
note = "You can implement it by adding `#[derive(Savefile)]` before the declaration of `{Self}`",
note = "Or you can manually implement the `savefile::WithSchema` trait.",
note = "You can also use one of the `*_noschema` functions to save/load without a schema."
)
)]
pub trait WithSchema {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema;
}
pub fn get_schema<T: WithSchema + 'static>(version: u32) -> Schema {
T::schema(version, &mut WithSchemaContext::new())
}
pub fn get_result_schema(ok: Schema, err: Schema) -> Schema {
Schema::Enum(SchemaEnum {
dbg_name: "Result".to_string(),
size: None,
alignment: None,
variants: vec![
Variant {
name: "Ok".to_string(),
discriminant: 0,
fields: vec![Field {
name: "ok".to_string(),
value: Box::new(ok),
offset: None,
}],
},
Variant {
name: "Err".to_string(),
discriminant: 0,
fields: vec![Field {
name: "err".to_string(),
value: Box::new(err),
offset: None,
}],
},
],
discriminant_size: 1,
has_explicit_repr: false,
})
}
#[cfg_attr(
feature = "rust1_78",
diagnostic::on_unimplemented(
message = "`{Self}` cannot be serialized by Savefile, since it doesn't implement the trait `savefile::Serialize`",
label = "This cannot be serialized",
note = "You can implement it by adding `#[derive(Savefile)]` before the declaration of `{Self}`",
note = "Or you can manually implement the `savefile::Serialize` trait."
)
)]
pub trait Serialize: WithSchema {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError>; }
#[cfg_attr(
feature = "rust1_78",
diagnostic::on_unimplemented(
message = "`{Self}` cannot be an introspected value used by Savefile, since it doesn't implement the trait `savefile::IntrospectItem`",
label = "This cannot be the type of an introspected field value",
note = "You can possibly implement IntrospectItem manually for the type `{Self}`, or try to use `String` instead of `{Self}`."
)
)]
pub trait IntrospectItem<'a> {
fn key(&self) -> &str;
fn val(&self) -> &dyn Introspect;
}
struct NullIntrospectable {}
static THE_NULL_INTROSPECTABLE: NullIntrospectable = NullIntrospectable {};
impl Introspect for NullIntrospectable {
fn introspect_value(&self) -> String {
String::new()
}
fn introspect_child<'a>(&'a self, _index: usize) -> Option<Box<dyn IntrospectItem<'a> + 'a>> {
None
}
fn introspect_len(&self) -> usize {
0
}
}
impl IntrospectItem<'_> for str {
fn key(&self) -> &str {
self
}
fn val(&self) -> &dyn Introspect {
&THE_NULL_INTROSPECTABLE
}
}
impl IntrospectItem<'_> for String {
fn key(&self) -> &str {
self
}
fn val(&self) -> &dyn Introspect {
&THE_NULL_INTROSPECTABLE
}
}
pub const MAX_CHILDREN: usize = 10000;
#[cfg_attr(
feature = "rust1_78",
diagnostic::on_unimplemented(
message = "`{Self}` cannot be introspected by Savefile, since it doesn't implement trait `savefile::Introspect`",
label = "This cannot be introspected",
note = "If you get this message after having used the #[savefile_ignore] attribute on a field, consider adding #[savefile_introspect_ignore].",
note = "You can implement it by adding `#[derive(Savefile)]` or `#[derive(SavefileIntrospectOnly)]` before the declaration of `{Self}`",
note = "Or you can manually implement the `savefile::Introspect` trait."
)
)]
pub trait Introspect {
fn introspect_value(&self) -> String;
fn introspect_child<'a>(&'a self, index: usize) -> Option<Box<dyn IntrospectItem<'a> + 'a>>;
fn introspect_len(&self) -> usize {
for child_index in 0..MAX_CHILDREN {
if self.introspect_child(child_index).is_none() {
return child_index;
}
}
return MAX_CHILDREN;
}
}
#[cfg_attr(
feature = "rust1_78",
diagnostic::on_unimplemented(
message = "`{Self}` cannot be deserialized by Savefile, since it doesn't implement the trait `savefile::Deserialize`",
label = "This cannot be deserialized",
note = "You can implement it by adding `#[derive(Savefile)]` before the declaration of `{Self}`",
note = "Or you can manually implement the `savefile::Deserialize` trait."
)
)]
pub trait Deserialize: WithSchema + Sized {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError>; }
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub struct Field {
pub name: String,
pub value: Box<Schema>,
offset: Option<usize>,
}
impl Field {
pub fn new(name: String, value: Box<Schema>) -> Field {
Field {
name,
value,
offset: None,
}
}
pub unsafe fn unsafe_new(name: String, value: Box<Schema>, offset: Option<usize>) -> Field {
Field { name, value, offset }
}
pub fn layout_compatible(&self, other: &Field) -> bool {
let (Some(offset_a), Some(offset_b)) = (self.offset, other.offset) else {
return false;
};
if offset_a != offset_b {
return false;
}
self.value.layout_compatible(&other.value)
}
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub struct SchemaArray {
pub item_type: Box<Schema>,
pub count: usize,
}
impl SchemaArray {
fn layout_compatible(&self, other: &SchemaArray) -> bool {
if self.count != other.count {
return false;
}
self.item_type.layout_compatible(&other.item_type)
}
fn serialized_size(&self) -> Option<usize> {
self.item_type.serialized_size().map(|x| x * self.count)
}
}
#[derive(Debug, PartialEq, Clone)]
#[repr(C)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub struct SchemaStruct {
pub dbg_name: String,
size: Option<usize>,
alignment: Option<usize>,
pub fields: Vec<Field>,
}
fn maybe_add(a: Option<usize>, b: Option<usize>) -> Option<usize> {
if let Some(a) = a {
if let Some(b) = b {
return Some(a + b);
}
}
None
}
impl SchemaStruct {
pub fn new(dbg_name: String, fields: Vec<Field>) -> SchemaStruct {
SchemaStruct {
dbg_name,
fields,
size: None,
alignment: None,
}
}
pub fn new_unsafe(
dbg_name: String,
fields: Vec<Field>,
size: Option<usize>,
alignment: Option<usize>,
) -> SchemaStruct {
SchemaStruct {
dbg_name,
fields,
size,
alignment,
}
}
fn layout_compatible(&self, other: &SchemaStruct) -> bool {
if self.fields.len() != other.fields.len() {
return false;
}
if self.alignment.is_none() || self.size.is_none() {
return false;
}
if self.alignment != other.alignment || self.size != other.size {
return false;
}
for (a, b) in self.fields.iter().zip(other.fields.iter()) {
if !a.layout_compatible(b) {
return false;
}
}
true
}
fn serialized_size(&self) -> Option<usize> {
self.fields
.iter()
.fold(Some(0usize), |prev, x| maybe_add(prev, x.value.serialized_size()))
}
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub struct Variant {
pub name: String,
pub discriminant: u8,
pub fields: Vec<Field>,
}
impl Variant {
fn layout_compatible(&self, other: &Variant) -> bool {
if self.discriminant != other.discriminant {
return false;
}
if self.fields.len() != other.fields.len() {
return false;
}
for (a, b) in self.fields.iter().zip(other.fields.iter()) {
if !a.layout_compatible(b) {
return false;
}
}
true
}
fn serialized_size(&self) -> Option<usize> {
self.fields
.iter()
.fold(Some(0usize), |prev, x| maybe_add(prev, x.value.serialized_size()))
}
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub struct SchemaEnum {
pub dbg_name: String,
pub variants: Vec<Variant>,
pub discriminant_size: u8,
has_explicit_repr: bool,
size: Option<usize>,
alignment: Option<usize>,
}
fn maybe_max(a: Option<usize>, b: Option<usize>) -> Option<usize> {
if let Some(a) = a {
if let Some(b) = b {
return Some(a.max(b));
}
}
None
}
impl SchemaEnum {
pub fn new(dbg_name: String, discriminant_size: u8, variants: Vec<Variant>) -> SchemaEnum {
SchemaEnum {
dbg_name,
variants,
discriminant_size,
has_explicit_repr: false,
size: None,
alignment: None,
}
}
pub fn new_unsafe(
dbg_name: String,
variants: Vec<Variant>,
discriminant_size: u8,
has_explicit_repr: bool,
size: Option<usize>,
alignment: Option<usize>,
) -> SchemaEnum {
SchemaEnum {
dbg_name,
variants,
discriminant_size,
has_explicit_repr,
size,
alignment,
}
}
fn layout_compatible(&self, other: &SchemaEnum) -> bool {
if self.has_explicit_repr == false || other.has_explicit_repr == false {
return false;
}
if self.alignment.is_none() || self.size.is_none() {
return false;
}
if self.alignment != other.alignment || self.size != other.size {
return false;
}
if self.discriminant_size != other.discriminant_size {
return false;
}
if self.variants.len() != other.variants.len() {
return false;
}
for (a, b) in self.variants.iter().zip(other.variants.iter()) {
if !a.layout_compatible(b) {
return false;
}
}
true
}
fn serialized_size(&self) -> Option<usize> {
let discr_size = 1usize; self.variants
.iter()
.fold(Some(discr_size), |prev, x| maybe_max(prev, x.serialized_size()))
}
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub enum SchemaPrimitive {
schema_i8,
schema_u8,
schema_i16,
schema_u16,
schema_i32,
schema_u32,
schema_i64,
schema_u64,
schema_string(VecOrStringLayout),
schema_f32,
schema_f64,
schema_bool,
schema_canary1,
schema_u128,
schema_i128,
schema_char,
}
impl SchemaPrimitive {
fn layout_compatible(&self, other: &SchemaPrimitive) -> bool {
if let (SchemaPrimitive::schema_string(layout1), SchemaPrimitive::schema_string(layout2)) = (self, other) {
if *layout1 == VecOrStringLayout::Unknown || *layout2 == VecOrStringLayout::Unknown {
return false;
}
}
self == other
}
fn name(&self) -> &'static str {
match *self {
SchemaPrimitive::schema_i8 => "i8",
SchemaPrimitive::schema_u8 => "u8",
SchemaPrimitive::schema_i16 => "i16",
SchemaPrimitive::schema_u16 => "u16",
SchemaPrimitive::schema_i32 => "i32",
SchemaPrimitive::schema_u32 => "u32",
SchemaPrimitive::schema_i64 => "i64",
SchemaPrimitive::schema_u64 => "u64",
SchemaPrimitive::schema_string(_) => "String",
SchemaPrimitive::schema_f32 => "f32",
SchemaPrimitive::schema_f64 => "f64",
SchemaPrimitive::schema_bool => "bool",
SchemaPrimitive::schema_canary1 => "u32",
SchemaPrimitive::schema_u128 => "u128",
SchemaPrimitive::schema_i128 => "i128",
SchemaPrimitive::schema_char => "char",
}
}
fn serialized_size(&self) -> Option<usize> {
match *self {
SchemaPrimitive::schema_i8 | SchemaPrimitive::schema_u8 => Some(1),
SchemaPrimitive::schema_i16 | SchemaPrimitive::schema_u16 => Some(2),
SchemaPrimitive::schema_i32 | SchemaPrimitive::schema_u32 => Some(4),
SchemaPrimitive::schema_i64 | SchemaPrimitive::schema_u64 => Some(8),
SchemaPrimitive::schema_string(_) => None,
SchemaPrimitive::schema_f32 => Some(4),
SchemaPrimitive::schema_f64 => Some(8),
SchemaPrimitive::schema_bool => Some(1),
SchemaPrimitive::schema_canary1 => Some(4),
SchemaPrimitive::schema_i128 | SchemaPrimitive::schema_u128 => Some(16),
SchemaPrimitive::schema_char => Some(4),
}
}
}
fn diff_primitive(a: SchemaPrimitive, b: SchemaPrimitive, path: &str) -> Option<String> {
if a != b {
if let (SchemaPrimitive::schema_string(_), SchemaPrimitive::schema_string(_)) = (&a, &b) {
return None; }
return Some(format!(
"At location [{}]: Application protocol has datatype {}, but disk format has {}",
path,
a.name(),
b.name()
));
}
None
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
#[repr(u8)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub enum VecOrStringLayout {
#[default]
Unknown,
DataCapacityLength,
DataLengthCapacity,
CapacityDataLength,
LengthDataCapacity,
CapacityLengthData,
LengthCapacityData,
LengthData,
DataLength,
}
impl Packed for AbiMethodArgument {}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub struct AbiMethodArgument {
pub schema: Schema,
}
impl Deserialize for AbiMethodArgument {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AbiMethodArgument {
schema: <_ as Deserialize>::deserialize(deserializer)?,
})
}
}
impl WithSchema for AbiMethodArgument {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl Serialize for AbiMethodArgument {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.schema.serialize(serializer)?;
Ok(())
}
}
#[non_exhaustive]
#[derive(PartialEq, Debug, Clone, Copy)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum ReceiverType {
Shared, Mut, PinMut, }
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub struct AbiMethodInfo {
pub return_value: Schema,
pub receiver: ReceiverType,
pub arguments: Vec<AbiMethodArgument>,
pub async_trait_heuristic: bool,
}
impl Packed for AbiMethodInfo {}
impl WithSchema for AbiMethodInfo {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl Serialize for AbiMethodInfo {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.return_value.serialize(serializer)?;
if serializer.file_version >= 2 {
serializer.write_u8(match self.receiver {
ReceiverType::Shared => 100,
ReceiverType::Mut => 101,
ReceiverType::PinMut => 102,
})?;
serializer.write_bool(self.async_trait_heuristic)?;
}
self.arguments.serialize(serializer)?;
Ok(())
}
}
impl Deserialize for AbiMethodInfo {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let return_value = <_ as Deserialize>::deserialize(deserializer)?;
let async_trait_heuristic;
let receiver;
if deserializer.file_version >= 2 {
receiver = match deserializer.read_u8()? {
100 => ReceiverType::Shared,
101 => ReceiverType::Mut,
102 => ReceiverType::PinMut,
x => return Err(SavefileError::WrongVersion {
msg: format!("Version 0.17.x (or earlier) of the savefile-library detected. It is not compatible with the current version. Please upgrade to version >0.18. Unexpected value: {}", x),
}),
};
async_trait_heuristic = deserializer.read_bool()?;
} else {
receiver = ReceiverType::Shared;
async_trait_heuristic = false;
};
Ok(AbiMethodInfo {
return_value,
receiver,
arguments: <_ as Deserialize>::deserialize(deserializer)?,
async_trait_heuristic,
})
}
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub struct AbiMethod {
pub name: String,
pub info: AbiMethodInfo,
}
impl Packed for AbiMethod {}
impl WithSchema for AbiMethod {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl Serialize for AbiMethod {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.name.serialize(serializer)?;
self.info.serialize(serializer)?;
Ok(())
}
}
impl Deserialize for AbiMethod {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AbiMethod {
name: <_ as Deserialize>::deserialize(deserializer)?,
info: <_ as Deserialize>::deserialize(deserializer)?,
})
}
}
#[derive(Default, Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub struct AbiTraitDefinition {
pub name: String,
pub methods: Vec<AbiMethod>,
pub sync: bool,
pub send: bool,
}
impl Packed for AbiTraitDefinition {}
impl WithSchema for AbiTraitDefinition {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl Serialize for AbiTraitDefinition {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let mut effective_name = self.name.clone();
if self.sync {
effective_name += "+Sync";
}
if self.send {
effective_name += "+Send";
}
effective_name.serialize(serializer)?;
self.methods.serialize(serializer)?;
Ok(())
}
}
impl Deserialize for AbiTraitDefinition {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let name: String = <_ as Deserialize>::deserialize(deserializer)?;
let actual_name = name.split('+').next().unwrap();
let mut sync = false;
let mut send = false;
for segment in name.split('+').skip(1) {
match segment {
"Sync" => sync = true,
"Send" => send = true,
_ => panic!("Unexpected trait name encountered: {}", name),
}
}
let t = AbiTraitDefinition {
name: actual_name.to_string(),
methods: <_ as Deserialize>::deserialize(deserializer)?,
sync,
send,
};
Ok(t)
}
}
impl AbiTraitDefinition {
fn verify_compatible_with_old_impl(
&self,
old_version: u32,
old: &AbiTraitDefinition,
is_return_position: bool,
) -> Result<(), String> {
if is_return_position {
if !old.sync && self.sync {
return Err(format!("Trait {} was not Sync in version {}, but the Sync-bound has since been added. This is not a backward-compatible change.",
self.name, old_version,
));
}
if !old.send && self.send {
return Err(format!("Trait {} was not Send in version {}, but the Send-bound has since been added. This is not a backward-compatible change.",
self.name, old_version,
));
}
} else {
if old.sync && !self.sync {
return Err(format!("Trait {} was Sync in version {}, but the Sync-bound has since been removed. This is not a backward-compatible change.",
self.name, old_version,
));
}
if old.send && !self.send {
return Err(format!("Trait {} was Send in version {}, but the Send-bound has since been removed. This is not a backward-compatible change.",
self.name, old_version,
));
}
}
for old_method in old.methods.iter() {
let Some(new_method) = self.methods.iter().find(|x| x.name == old_method.name) else {
return Err(format!("In trait {}, the method {} existed in version {}, but has been removed. This is not a backward-compatible change.",
self.name, old_method.name, old_version,
));
};
if new_method.info.async_trait_heuristic != old_method.info.async_trait_heuristic {
if old_method.info.async_trait_heuristic {
return Err(format!("In trait {}, the method {} was previously async, using #[async_trait], but it does no longer. This is not a backward-compatible change.",
self.name, old_method.name
));
} else {
return Err(format!("In trait {}, the method {} is now async, using #[async_trait], but it previously did not. This is not a backward-compatible change.",
self.name, old_method.name
));
}
}
if new_method.info.arguments.len() != old_method.info.arguments.len() {
return Err(format!("In trait {}, method {}, the number of arguments has changed from {} in version {} to {}. This is not a backward-compatible change.",
self.name, old_method.name, old_method.info.arguments.len(), old_version, new_method.info.arguments.len()
));
}
if let Some(diff) = diff_schema(
&new_method.info.return_value,
&old_method.info.return_value,
"".into(),
is_return_position,
) {
return Err(format!("In trait {}, method {}, the return value type has changed from version {}: {}. This is not a backward-compatible change.",
self.name, old_method.name, old_version, diff
));
}
for (arg_index, (new_arg, old_arg)) in new_method
.info
.arguments
.iter()
.zip(old_method.info.arguments.iter())
.enumerate()
{
if let Some(diff) = diff_schema(&new_arg.schema, &old_arg.schema, "".into(), is_return_position) {
return Err(format!("In trait {}, method {}, argument {}, the type has changed from version {}: {}. This is not a backward-compatible change.",
self.name, old_method.name, arg_index , old_version, diff
));
}
}
}
Ok(())
}
pub fn verify_backward_compatible(
&self,
old_version: u32,
old: &AbiTraitDefinition,
is_return_position: bool,
) -> Result<(), SavefileError> {
self.verify_compatible_with_old_impl(old_version, old, is_return_position)
.map_err(|x| SavefileError::IncompatibleSchema { message: x })
}
}
#[derive(Debug, PartialEq, Clone)]
#[repr(C, u32)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub enum Schema {
Struct(SchemaStruct),
Enum(SchemaEnum),
Primitive(SchemaPrimitive),
Vector(Box<Schema>, VecOrStringLayout ),
Array(SchemaArray),
SchemaOption(Box<Schema>),
Undefined,
ZeroSize,
Custom(String),
Boxed(Box<Schema>),
Slice(Box<Schema>),
Str,
Reference(Box<Schema>),
Trait(bool , AbiTraitDefinition),
FnClosure(bool , AbiTraitDefinition),
Recursion(usize ),
StdIoError,
Future(
AbiTraitDefinition,
bool,
bool,
bool,
),
UninitSlice,
}
impl Introspect for Schema {
fn introspect_value(&self) -> String {
"Schema".to_string()
}
fn introspect_child<'a>(&'a self, _index: usize) -> Option<Box<dyn IntrospectItem<'a> + 'a>> {
None
}
}
impl Schema {
pub fn top_level_description(&self) -> String {
match self {
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!("<recursion {}>", depth)
}
Schema::StdIoError => "stdioerror".into(),
Schema::Future(_, _, _, _) => "future".into(),
Schema::UninitSlice => {"UninitSlice".into()}
}
}
pub fn layout_compatible(&self, b_native: &Schema) -> bool {
match (self, b_native) {
(Schema::Struct(a), Schema::Struct(b)) => a.layout_compatible(b),
(Schema::Enum(a), Schema::Enum(b)) => a.layout_compatible(b),
(Schema::Primitive(a), Schema::Primitive(b)) => a.layout_compatible(b),
(Schema::Vector(a, a_standard_layout), Schema::Vector(b, b_standard_layout)) => {
a.layout_compatible(b)
&& *a_standard_layout != VecOrStringLayout::Unknown
&& *b_standard_layout != VecOrStringLayout::Unknown
&& *a_standard_layout == *b_standard_layout
}
(Schema::Array(a), Schema::Array(b)) => a.layout_compatible(b),
(Schema::SchemaOption(_), Schema::SchemaOption(_)) => {
false }
(Schema::ZeroSize, Schema::ZeroSize) => true,
(Schema::Custom(_), Schema::Custom(_)) => {
false }
(Schema::FnClosure(_a1, _a2), Schema::FnClosure(_b1, _b2)) => {
false
}
(Schema::Boxed(a), Schema::Boxed(b)) => {
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,
}
}
pub fn new_tuple1<T1: WithSchema>(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,)>()),
alignment: Some(std::mem::align_of::<(T1,)>()),
fields: vec![Field {
name: "0".to_string(),
value: schema,
offset: Some(offset_of_tuple!((T1,), 0)),
}],
})
}
pub fn new_tuple2<T1: WithSchema, T2: WithSchema>(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Struct(SchemaStruct {
dbg_name: "2-Tuple".to_string(),
size: Some(std::mem::size_of::<(T1, T2)>()),
alignment: Some(std::mem::align_of::<(T1, T2)>()),
fields: vec![
Field {
name: "0".to_string(),
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, context)),
offset: Some(offset_of_tuple!((T1, T2), 1)),
},
],
})
}
pub fn new_tuple3<T1: WithSchema, T2: WithSchema, T3: WithSchema>(
version: u32,
context: &mut WithSchemaContext,
) -> Schema {
Schema::Struct(SchemaStruct {
dbg_name: "3-Tuple".to_string(),
size: Some(std::mem::size_of::<(T1, T2, T3)>()),
alignment: Some(std::mem::align_of::<(T1, T2, T3)>()),
fields: vec![
Field {
name: "0".to_string(),
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, context)),
offset: Some(offset_of_tuple!((T1, T2, T3), 1)),
},
Field {
name: "2".to_string(),
value: Box::new(T3::schema(version, context)),
offset: Some(offset_of_tuple!((T1, T2, T3), 2)),
},
],
})
}
pub fn new_tuple4<T1: WithSchema, T2: WithSchema, T3: WithSchema, T4: WithSchema>(
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)>()),
alignment: Some(std::mem::align_of::<(T1, T2, T3, T4)>()),
fields: vec![
Field {
name: "0".to_string(),
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, context)),
offset: Some(offset_of_tuple!((T1, T2, T3, T4), 1)),
},
Field {
name: "2".to_string(),
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, context)),
offset: Some(offset_of_tuple!((T1, T2, T3, T4), 3)),
},
],
})
}
pub fn serialized_size(&self) -> Option<usize> {
match self {
Schema::Struct(ref schema_struct) => schema_struct.serialized_size(),
Schema::Enum(ref schema_enum) => schema_enum.serialized_size(),
Schema::Primitive(ref schema_primitive) => schema_primitive.serialized_size(),
Schema::Vector(ref _vector, _) => None,
Schema::Array(ref array) => array.serialized_size(),
Schema::SchemaOption(ref _content) => None,
Schema::Undefined => None,
Schema::ZeroSize => Some(0),
Schema::Custom(_) => None,
Schema::Boxed(inner) => inner.serialized_size(),
Schema::FnClosure(_, _) => None,
Schema::Slice(_) => None,
Schema::Str => None,
Schema::Reference(_) => None,
Schema::Trait(_, _) => None,
Schema::Recursion(_) => None,
Schema::StdIoError => None,
Schema::Future(_, _, _, _) => None,
Schema::UninitSlice => None,
}
}
}
fn diff_vector(a: &Schema, b: &Schema, path: String) -> Option<String> {
diff_schema(a, b, path + "/*", false)
}
fn diff_array(a: &SchemaArray, b: &SchemaArray, path: String) -> Option<String> {
if a.count != b.count {
return Some(format!(
"At location [{}]: In memory array has length {}, but disk format length {}.",
path, a.count, b.count
));
}
diff_schema(&a.item_type, &b.item_type, format!("{}/[{}]", path, a.count), false)
}
fn diff_option(a: &Schema, b: &Schema, path: String) -> Option<String> {
diff_schema(a, b, path + "/?", false)
}
fn diff_enum(a: &SchemaEnum, b: &SchemaEnum, path: String) -> Option<String> {
let path = (path + &b.dbg_name).to_string();
if a.variants.len() != b.variants.len() {
return Some(format!(
"At location [{}]: In memory enum has {} variants, but disk format has {} variants.",
path,
a.variants.len(),
b.variants.len()
));
}
if a.discriminant_size != b.discriminant_size {
return Some(format!(
"At location [{}]: In memory enum has a representation with {} bytes for the discriminant, but disk format has {}.",
path,
a.discriminant_size,
b.discriminant_size
));
}
for i in 0..a.variants.len() {
if a.variants[i].name != b.variants[i].name {
return Some(format!(
"At location [{}]: Enum variant #{} in memory is called {}, but in disk format it is called {}",
&path, i, a.variants[i].name, b.variants[i].name
));
}
if a.variants[i].discriminant != b.variants[i].discriminant {
return Some(format!(
"At location [{}]: Enum variant #{} in memory has discriminant {}, but in disk format it has {}",
&path, i, a.variants[i].discriminant, b.variants[i].discriminant
));
}
let r = diff_fields(
&a.variants[i].fields,
&b.variants[i].fields,
&(path.to_string() + "/" + &b.variants[i].name).to_string(),
"enum",
"",
"",
);
if let Some(err) = r {
return Some(err);
}
}
None
}
fn diff_struct(a: &SchemaStruct, b: &SchemaStruct, path: String) -> Option<String> {
diff_fields(
&a.fields,
&b.fields,
&(path + "/" + &b.dbg_name).to_string(),
"struct",
&(" (struct ".to_string() + &a.dbg_name + ")"),
&(" (struct ".to_string() + &b.dbg_name + ")"),
)
}
fn diff_fields(
a: &[Field],
b: &[Field],
path: &str,
structuretype: &str,
extra_a: &str,
extra_b: &str,
) -> Option<String> {
if a.len() != b.len() {
return Some(format!(
"At location [{}]: In memory {}{} has {} fields, disk format{} has {} fields.",
path,
structuretype,
extra_a,
a.len(),
extra_b,
b.len()
));
}
for i in 0..a.len() {
let r = diff_schema(
&a[i].value,
&b[i].value,
(path.to_string() + "/" + &b[i].name).to_string(),
false,
);
if let Some(err) = r {
return Some(err);
}
}
None
}
pub fn diff_schema(a: &Schema, b: &Schema, path: String, is_return_pos: bool) -> Option<String> {
let (atype, btype) = match (a, b) {
(Schema::Struct(a), Schema::Struct(b)) => return diff_struct(a, b, path),
(Schema::Enum(a), Schema::Enum(b)) => return diff_enum(a, b, path),
(Schema::Primitive(a1), Schema::Primitive(b1)) => return diff_primitive(*a1, *b1, &path),
(Schema::Vector(a1, _a2), Schema::Vector(b1, _b2)) => return diff_vector(a1, b1, path),
(Schema::SchemaOption(a), Schema::SchemaOption(b)) => {
return diff_option(a, b, path);
}
(Schema::Undefined, Schema::Undefined) => {
return Some(format!("At location [{}]: Undefined schema encountered.", path))
}
(Schema::ZeroSize, Schema::ZeroSize) => {
return None;
}
(Schema::Array(a), Schema::Array(b)) => return diff_array(a, b, path),
(Schema::Custom(a), Schema::Custom(b)) => {
if a != b {
return Some(format!(
"At location [{}]: Application protocol has datatype Custom({}), but foreign format has Custom({})",
path, a, b
));
}
return None;
}
(Schema::Str, Schema::Str) => {
return None;
}
(Schema::StdIoError, Schema::StdIoError) => {
return None;
}
(Schema::Boxed(a), Schema::Boxed(b)) => {
return diff_schema(a, b, path, is_return_pos);
}
(Schema::Reference(a), Schema::Reference(b)) => {
return diff_schema(a, b, path, is_return_pos);
}
(Schema::Slice(a), Schema::Slice(b)) => {
return diff_schema(a, b, path, is_return_pos);
}
(Schema::Trait(amut, a), Schema::Trait(bmut, b)) | (Schema::FnClosure(amut, a), Schema::FnClosure(bmut, b)) => {
if amut != bmut {
if *amut {
return Some(format!(
"At location [{}]: Application protocol uses FnMut, but foreign format has Fn.",
path
));
}
if *bmut {
return Some(format!(
"At location [{}]: Application protocol uses Fn, but foreign format uses FnMut.",
path
));
}
}
return diff_abi_def(a, b, path, is_return_pos);
}
(Schema::Recursion(adepth), Schema::Recursion(bdepth)) => {
if adepth == bdepth {
return None; } else {
return Some(format!(
"At location [{}]: Application protocol uses recursion up {} levels, but foreign format uses {}.",
path, adepth, bdepth
));
}
}
(Schema::Future(a, a_send, a_sync, a_unpin), Schema::Future(b, b_send, b_sync, b_unpin)) => {
if !is_return_pos {
panic!("Futures are only supported in return position");
}
for (a, b, bound) in [
(*a_send, *b_send, "Send"),
(*a_sync, *b_sync, "Sync"),
(*a_unpin, *b_unpin, "Unpin"),
] {
if a && !b {
return Some(format!(
"At location [{}]: Caller expects a future with an {}-bound, but implementation provides one without. This is an incompatible difference.",
path, bound
));
}
}
return diff_abi_def(a, b, path, is_return_pos);
}
(Schema::UninitSlice, Schema::UninitSlice) => {
return None; }
(a, b) => (a.top_level_description(), b.top_level_description()),
};
Some(format!(
"At location [{}]: In memory schema: {}, file schema: {}",
path, atype, btype
))
}
fn diff_abi_def(a: &AbiTraitDefinition, b: &AbiTraitDefinition, path: String, is_return_pos: bool) -> Option<String> {
for amet in a.methods.iter() {
if let Some(bmet) = b.methods.iter().find(|x| x.name == amet.name) {
if amet.info.arguments.len() != bmet.info.arguments.len() {
return Some(format!(
"At location [{}]: Application protocol method {} has {} args, but foreign version has {}.",
path,
amet.name,
amet.info.arguments.len(),
bmet.info.arguments.len()
));
}
for (arg_index, (a_arg, b_arg)) in amet.info.arguments.iter().zip(bmet.info.arguments.iter()).enumerate() {
if let Some(diff) = diff_schema(
&a_arg.schema,
&b_arg.schema,
format!("{}(arg #{})", amet.name, arg_index),
is_return_pos,
) {
return Some(diff);
}
}
}
}
return None;
}
impl WithSchema for Field {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl Serialize for Field {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_string(&self.name)?;
self.value.serialize(serializer)?;
self.offset.serialize(serializer)?;
Ok(())
}
}
impl Packed for Field {}
impl Deserialize for Field {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(Field {
name: deserializer.read_string()?,
value: Box::new(Schema::deserialize(deserializer)?),
offset: if deserializer.file_version > 0 {
Option::deserialize(deserializer)?
} else {
None
},
})
}
}
impl WithSchema for Variant {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl Serialize for Variant {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_string(&self.name)?;
serializer.write_u8(self.discriminant)?;
serializer.write_usize(self.fields.len())?;
for field in &self.fields {
field.serialize(serializer)?;
}
Ok(())
}
}
impl Packed for Variant {}
impl Deserialize for Variant {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(Variant {
name: deserializer.read_string()?,
discriminant: deserializer.read_u8()?,
fields: {
let l = deserializer.read_usize()?;
let mut ret = Vec::new();
for _ in 0..l {
ret.push(Field {
name: deserializer.read_string()?,
value: Box::new(Schema::deserialize(deserializer)?),
offset: if deserializer.file_version > 0 {
Option::deserialize(deserializer)?
} else {
None
},
});
}
ret
},
})
}
}
impl Serialize for SchemaArray {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_usize(self.count)?;
self.item_type.serialize(serializer)?;
Ok(())
}
}
impl Packed for SchemaArray {}
impl Deserialize for SchemaArray {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let count = deserializer.read_usize()?;
let item_type = Box::new(Schema::deserialize(deserializer)?);
Ok(SchemaArray { count, item_type })
}
}
impl WithSchema for SchemaArray {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl WithSchema for SchemaStruct {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl Serialize for SchemaStruct {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_string(&self.dbg_name)?;
serializer.write_usize(self.fields.len())?;
self.size.serialize(serializer)?;
self.alignment.serialize(serializer)?;
for field in &self.fields {
field.serialize(serializer)?;
}
Ok(())
}
}
impl Packed for SchemaStruct {}
impl Deserialize for SchemaStruct {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let dbg_name = deserializer.read_string()?;
let l = deserializer.read_usize()?;
Ok(SchemaStruct {
dbg_name,
size: if deserializer.file_version > 0 {
<_ as Deserialize>::deserialize(deserializer)?
} else {
None
},
alignment: if deserializer.file_version > 0 {
<_ as Deserialize>::deserialize(deserializer)?
} else {
None
},
fields: {
let mut ret = Vec::new();
for _ in 0..l {
ret.push(Field::deserialize(deserializer)?)
}
ret
},
})
}
}
impl WithSchema for SchemaPrimitive {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl Serialize for SchemaPrimitive {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let discr = match *self {
SchemaPrimitive::schema_i8 => 1,
SchemaPrimitive::schema_u8 => 2,
SchemaPrimitive::schema_i16 => 3,
SchemaPrimitive::schema_u16 => 4,
SchemaPrimitive::schema_i32 => 5,
SchemaPrimitive::schema_u32 => 6,
SchemaPrimitive::schema_i64 => 7,
SchemaPrimitive::schema_u64 => 8,
SchemaPrimitive::schema_f32 => 10,
SchemaPrimitive::schema_f64 => 11,
SchemaPrimitive::schema_bool => 12,
SchemaPrimitive::schema_canary1 => 13,
SchemaPrimitive::schema_i128 => 14,
SchemaPrimitive::schema_u128 => 15,
SchemaPrimitive::schema_char => 16,
SchemaPrimitive::schema_string(layout) => {
serializer.write_u8(9)?;
if serializer.file_version > 0 {
serializer.write_u8(layout as u8)?;
}
return Ok(());
}
};
serializer.write_u8(discr)
}
}
impl WithSchema for VecOrStringLayout {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl Deserialize for VecOrStringLayout {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(match deserializer.read_u8()? {
1 => VecOrStringLayout::DataCapacityLength,
2 => VecOrStringLayout::DataLengthCapacity,
3 => VecOrStringLayout::CapacityDataLength,
4 => VecOrStringLayout::LengthDataCapacity,
5 => VecOrStringLayout::CapacityLengthData,
6 => VecOrStringLayout::LengthCapacityData,
7 => VecOrStringLayout::LengthData,
8 => VecOrStringLayout::DataLength,
_ => VecOrStringLayout::Unknown,
})
}
}
impl Packed for SchemaPrimitive {}
impl Deserialize for SchemaPrimitive {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let var = match deserializer.read_u8()? {
1 => SchemaPrimitive::schema_i8,
2 => SchemaPrimitive::schema_u8,
3 => SchemaPrimitive::schema_i16,
4 => SchemaPrimitive::schema_u16,
5 => SchemaPrimitive::schema_i32,
6 => SchemaPrimitive::schema_u32,
7 => SchemaPrimitive::schema_i64,
8 => SchemaPrimitive::schema_u64,
9 => SchemaPrimitive::schema_string({
if deserializer.file_version > 0 {
VecOrStringLayout::deserialize(deserializer)?
} else {
VecOrStringLayout::Unknown
}
}),
10 => SchemaPrimitive::schema_f32,
11 => SchemaPrimitive::schema_f64,
12 => SchemaPrimitive::schema_bool,
13 => SchemaPrimitive::schema_canary1,
14 => SchemaPrimitive::schema_i128,
15 => SchemaPrimitive::schema_u128,
16 => SchemaPrimitive::schema_char,
c => {
return Err(SavefileError::GeneralError {
msg: format!(
"Corrupt schema, type {} encountered. Perhaps data is from future version?",
c
),
})
}
};
Ok(var)
}
}
impl WithSchema for SchemaEnum {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl Serialize for SchemaEnum {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_string(&self.dbg_name)?;
serializer.write_usize(self.variants.len())?;
for var in &self.variants {
var.serialize(serializer)?;
}
self.discriminant_size.serialize(serializer)?;
self.has_explicit_repr.serialize(serializer)?;
self.size.serialize(serializer)?;
self.alignment.serialize(serializer)?;
Ok(())
}
}
impl Packed for SchemaEnum {}
impl Deserialize for SchemaEnum {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let dbg_name = deserializer.read_string()?;
let l = deserializer.read_usize()?;
let mut ret = Vec::new();
for _ in 0..l {
ret.push(Variant::deserialize(deserializer)?);
}
let (discriminant_size, has_explicit_repr, size, alignment) = if deserializer.file_version > 0 {
(
u8::deserialize(deserializer)?,
bool::deserialize(deserializer)?,
Option::<usize>::deserialize(deserializer)?,
Option::<usize>::deserialize(deserializer)?,
)
} else {
(1, false, None, None)
};
Ok(SchemaEnum {
dbg_name,
variants: ret,
discriminant_size,
has_explicit_repr,
size,
alignment,
})
}
}
#[cfg(feature = "quickcheck")]
impl Arbitrary for VecOrStringLayout {
fn arbitrary(g: &mut Gen) -> Self {
let x = u8::arbitrary(g);
match x % 9 {
0 => VecOrStringLayout::Unknown,
1 => VecOrStringLayout::DataCapacityLength,
2 => VecOrStringLayout::DataLengthCapacity,
3 => VecOrStringLayout::CapacityDataLength,
4 => VecOrStringLayout::LengthDataCapacity,
5 => VecOrStringLayout::CapacityLengthData,
6 => VecOrStringLayout::LengthCapacityData,
7 => VecOrStringLayout::LengthData,
8 => VecOrStringLayout::DataLength,
_ => unreachable!(),
}
}
}
#[cfg(feature = "quickcheck")]
impl Arbitrary for SchemaPrimitive {
fn arbitrary(g: &mut Gen) -> Self {
let x = u8::arbitrary(g);
match x % 16 {
0 => SchemaPrimitive::schema_i8,
1 => SchemaPrimitive::schema_u8,
2 => SchemaPrimitive::schema_i16,
3 => SchemaPrimitive::schema_u16,
4 => SchemaPrimitive::schema_i32,
5 => SchemaPrimitive::schema_u32,
6 => SchemaPrimitive::schema_i64,
7 => SchemaPrimitive::schema_u64,
8 => SchemaPrimitive::schema_string(VecOrStringLayout::arbitrary(g)),
9 => SchemaPrimitive::schema_f32,
10 => SchemaPrimitive::schema_f64,
11 => SchemaPrimitive::schema_bool,
12 => SchemaPrimitive::schema_canary1,
13 => SchemaPrimitive::schema_u128,
14 => SchemaPrimitive::schema_i128,
15 => SchemaPrimitive::schema_char,
_ => unreachable!(),
}
}
}
#[cfg(feature = "quickcheck")]
impl Arbitrary for Field {
fn arbitrary(g: &mut Gen) -> Self {
Field {
name: g.choose(&["", "test"]).unwrap().to_string(),
value: <_ as Arbitrary>::arbitrary(g),
offset: <_ as Arbitrary>::arbitrary(g),
}
}
}
#[cfg(feature = "quickcheck")]
impl Arbitrary for Variant {
fn arbitrary(g: &mut Gen) -> Self {
Variant {
name: g.choose(&["", "test"]).unwrap().to_string(),
discriminant: <_ as Arbitrary>::arbitrary(g),
fields: <_ as Arbitrary>::arbitrary(g),
}
}
}
#[cfg(feature = "quickcheck")]
impl Arbitrary for SchemaEnum {
fn arbitrary(g: &mut Gen) -> Self {
SchemaEnum {
dbg_name: g.choose(&["", "test"]).unwrap().to_string(),
variants: (0..*g.choose(&[0usize, 1, 2, 3]).unwrap())
.map(|_| <_ as Arbitrary>::arbitrary(g))
.collect(),
discriminant_size: *g.choose(&[1, 2, 4]).unwrap(),
has_explicit_repr: *g.choose(&[false, true]).unwrap(),
size: <_ as Arbitrary>::arbitrary(g),
alignment: <_ as Arbitrary>::arbitrary(g),
}
}
}
#[cfg(feature = "quickcheck")]
impl Arbitrary for SchemaStruct {
fn arbitrary(g: &mut Gen) -> Self {
SchemaStruct {
fields: (0..*g.choose(&[0usize, 1, 2, 3]).unwrap())
.map(|_| <_ as Arbitrary>::arbitrary(g))
.collect(),
dbg_name: <_ as Arbitrary>::arbitrary(g),
size: <_ as Arbitrary>::arbitrary(g),
alignment: <_ as Arbitrary>::arbitrary(g),
}
}
}
#[cfg(feature = "quickcheck")]
impl Arbitrary for SchemaArray {
fn arbitrary(g: &mut Gen) -> Self {
SchemaArray {
item_type: <_ as Arbitrary>::arbitrary(g),
count: <_ as Arbitrary>::arbitrary(g),
}
}
}
#[cfg(feature = "quickcheck")]
static QUICKCHECKBOUND: AtomicU8 = AtomicU8::new(0);
#[cfg(feature = "quickcheck")]
impl Arbitrary for Schema {
fn arbitrary(g: &mut Gen) -> Self {
let val = QUICKCHECKBOUND.fetch_add(1, Ordering::Relaxed);
if val > 1 {
QUICKCHECKBOUND.fetch_sub(1, Ordering::Relaxed);
return Schema::ZeroSize;
}
let arg = g.choose(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap_or(&8);
let temp = match arg {
0 => Schema::Struct(<_ as Arbitrary>::arbitrary(g)),
1 => Schema::Enum(<_ as Arbitrary>::arbitrary(g)),
2 => Schema::Primitive(<_ as Arbitrary>::arbitrary(g)),
3 => Schema::Vector(<_ as Arbitrary>::arbitrary(g), VecOrStringLayout::arbitrary(g)),
4 => Schema::Array(SchemaArray::arbitrary(g)),
5 => Schema::SchemaOption(<_ as Arbitrary>::arbitrary(g)),
6 => Schema::ZeroSize,
7 => Schema::Custom(g.choose(&["", "test"]).unwrap().to_string()),
_ => Schema::ZeroSize,
};
_ = QUICKCHECKBOUND.fetch_sub(1, Ordering::Relaxed);
temp
}
}
impl WithSchema for Schema {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Undefined
}
}
impl Serialize for Schema {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
match self {
Schema::Struct(schema_struct) => {
serializer.write_u8(1)?;
schema_struct.serialize(serializer)
}
Schema::Enum(schema_enum) => {
serializer.write_u8(2)?;
schema_enum.serialize(serializer)
}
Schema::Primitive(schema_prim) => {
serializer.write_u8(3)?;
schema_prim.serialize(serializer)?;
Ok(())
}
Schema::Vector(schema_vector, is_standard_layout) => {
serializer.write_u8(4)?;
schema_vector.serialize(serializer)?;
if serializer.file_version > 0 {
serializer.write_u8(*is_standard_layout as u8)?;
}
Ok(())
}
Schema::Undefined => serializer.write_u8(5),
Schema::ZeroSize => serializer.write_u8(6),
Schema::SchemaOption(content) => {
serializer.write_u8(7)?;
content.serialize(serializer)
}
Schema::Array(array) => {
serializer.write_u8(8)?;
array.serialize(serializer)
}
Schema::Custom(custom) => {
serializer.write_u8(9)?;
custom.serialize(serializer)
}
Schema::Boxed(name) => {
serializer.write_u8(10)?;
name.serialize(serializer)
}
Schema::FnClosure(a, b) => {
serializer.write_u8(11)?;
a.serialize(serializer)?;
b.serialize(serializer)?;
Ok(())
}
Schema::Slice(inner) => {
serializer.write_u8(12)?;
inner.serialize(serializer)?;
Ok(())
}
Schema::Str => {
serializer.write_u8(13)?;
Ok(())
}
Schema::Reference(inner) => {
serializer.write_u8(14)?;
inner.serialize(serializer)?;
Ok(())
}
Schema::Trait(a, b) => {
serializer.write_u8(15)?;
serializer.write_bool(*a)?;
b.serialize(serializer)?;
Ok(())
}
Schema::Recursion(depth) => {
serializer.write_u8(16)?;
serializer.write_usize(*depth)?;
Ok(())
}
Schema::StdIoError => {
serializer.write_u8(17)?;
Ok(())
}
Schema::Future(o, send, sync, unpin) => {
serializer.write_u8(18)?;
serializer
.write_u8(if *send { 1 } else { 0 } | if *sync { 2 } else { 0 } | if *unpin { 4 } else { 0 })?;
o.serialize(serializer)?;
Ok(())
}
Schema::UninitSlice => {
serializer.write_u8(19)?;
Ok(())
}
}
}
}
impl Packed for Schema {}
impl Deserialize for Schema {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let x = deserializer.read_u8()?;
let schema = match x {
1 => Schema::Struct(SchemaStruct::deserialize(deserializer)?),
2 => Schema::Enum(SchemaEnum::deserialize(deserializer)?),
3 => Schema::Primitive(SchemaPrimitive::deserialize(deserializer)?),
4 => Schema::Vector(
Box::new(Schema::deserialize(deserializer)?),
if deserializer.file_version > 0 {
VecOrStringLayout::deserialize(deserializer)?
} else {
VecOrStringLayout::Unknown
},
),
5 => Schema::Undefined,
6 => Schema::ZeroSize,
7 => Schema::SchemaOption(Box::new(Schema::deserialize(deserializer)?)),
8 => Schema::Array(SchemaArray::deserialize(deserializer)?),
9 => Schema::Custom(String::deserialize(deserializer)?),
10 => Schema::Boxed(<_ as Deserialize>::deserialize(deserializer)?),
11 => Schema::FnClosure(
<_ as Deserialize>::deserialize(deserializer)?,
<_ as Deserialize>::deserialize(deserializer)?,
),
12 => Schema::Slice(Box::new(<_ as Deserialize>::deserialize(deserializer)?)),
13 => Schema::Str,
14 => Schema::Reference(Box::new(<_ as Deserialize>::deserialize(deserializer)?)),
15 => Schema::Trait(
<_ as Deserialize>::deserialize(deserializer)?,
<_ as Deserialize>::deserialize(deserializer)?,
),
16 => Schema::Recursion(<_ as Deserialize>::deserialize(deserializer)?),
17 => Schema::StdIoError,
18 => {
let mask = deserializer.read_u8()?;
let send = (mask & 1) != 0;
let sync = (mask & 2) != 0;
let unpin = (mask & 4) != 0;
Schema::Future(<_ as Deserialize>::deserialize(deserializer)?, send, sync, unpin)
}
19 => {
Schema::UninitSlice
}
c => {
return Err(SavefileError::GeneralError {
msg: format!("Corrupt, or future schema, schema variant {} encountered", c),
})
}
};
Ok(schema)
}
}
impl WithSchema for str {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_string(VecOrStringLayout::Unknown))
}
}
impl WithSchema for String {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_string(calculate_string_memory_layout()))
}
}
impl Introspect for str {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem>> {
None
}
}
impl Introspect for String {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem>> {
None
}
}
impl Serialize for String {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_string(self)
}
}
impl Serialize for str {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_string(self)
}
}
impl Packed for String {}
impl Packed for str {}
impl Deserialize for String {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<String, SavefileError> {
deserializer.read_string()
}
}
#[cfg(feature = "parking_lot")]
pub struct IntrospectItemMutex<'a, T> {
g: MutexGuard<'a, T>,
}
#[cfg(feature = "parking_lot")]
impl<'a, T: Introspect> IntrospectItem<'a> for IntrospectItemMutex<'a, T> {
fn key(&self) -> &str {
"0"
}
fn val(&self) -> &dyn Introspect {
self.g.deref()
}
}
#[cfg(feature = "parking_lot")]
impl<T: Introspect> Introspect for Mutex<T> {
fn introspect_value(&self) -> String {
format!("Mutex<{}>", std::any::type_name::<T>())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index == 0 {
Some(Box::new(IntrospectItemMutex { g: self.lock() }))
} else {
None
}
}
}
pub struct IntrospectItemStdMutex<'a, T> {
g: std::sync::MutexGuard<'a, T>,
}
impl<'a, T: Introspect> IntrospectItem<'a> for IntrospectItemStdMutex<'a, T> {
fn key(&self) -> &str {
"0"
}
fn val(&self) -> &dyn Introspect {
self.g.deref()
}
}
impl<T: Introspect> Introspect for std::sync::Mutex<T> {
fn introspect_value(&self) -> String {
format!("Mutex<{}>", std::any::type_name::<T>())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
match self.lock() {
Ok(item) => {
if index == 0 {
Some(Box::new(IntrospectItemStdMutex { g: item }))
} else {
None
}
}
Err(_) => None,
}
}
}
impl<T: WithSchema> WithSchema for std::sync::Mutex<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
T::schema(version, context)
}
}
impl<T> Packed for std::sync::Mutex<T> {}
impl<T: Serialize> Serialize for std::sync::Mutex<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let data = self.lock()?;
data.serialize(serializer)
}
}
impl<T: Deserialize> Deserialize for std::sync::Mutex<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<std::sync::Mutex<T>, SavefileError> {
Ok(std::sync::Mutex::new(T::deserialize(deserializer)?))
}
}
#[cfg(feature = "parking_lot")]
impl<T: WithSchema> WithSchema for Mutex<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
T::schema(version, context)
}
}
#[cfg(feature = "parking_lot")]
impl<T> Packed for Mutex<T> {}
#[cfg(feature = "parking_lot")]
impl<T: Serialize> Serialize for Mutex<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let data = self.lock();
data.serialize(serializer)
}
}
#[cfg(feature = "parking_lot")]
impl<T: Deserialize> Deserialize for Mutex<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Mutex<T>, SavefileError> {
Ok(Mutex::new(T::deserialize(deserializer)?))
}
}
#[cfg(feature = "parking_lot")]
pub struct IntrospectItemRwLock<'a, T> {
g: RwLockReadGuard<'a, T>,
}
#[cfg(feature = "parking_lot")]
impl<'a, T: Introspect> IntrospectItem<'a> for IntrospectItemRwLock<'a, T> {
fn key(&self) -> &str {
"0"
}
fn val(&self) -> &dyn Introspect {
self.g.deref()
}
}
impl<T: Introspect> Introspect for std::cell::Ref<'_, T> {
fn introspect_value(&self) -> String {
let sub_value = (**self).introspect_value();
format!("Ref({})", sub_value)
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
(**self).introspect_child(index)
}
fn introspect_len(&self) -> usize {
(**self).introspect_len()
}
}
impl<'a, T: Introspect> IntrospectItem<'a> for std::cell::Ref<'a, T> {
fn key(&self) -> &str {
"ref"
}
fn val(&self) -> &dyn Introspect {
self
}
}
impl<T: Introspect> Introspect for RefCell<T> {
fn introspect_value(&self) -> String {
let sub_value = self.borrow().introspect_value();
format!("RefCell({})", sub_value)
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index != 0 {
return None;
}
let rf = self.borrow();
Some(Box::new(rf))
}
fn introspect_len(&self) -> usize {
1
}
}
impl<T: Introspect> Introspect for Rc<T> {
fn introspect_value(&self) -> String {
format!("Rc({})", self.deref().introspect_value())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
self.deref().introspect_child(index)
}
fn introspect_len(&self) -> usize {
self.deref().introspect_len()
}
}
impl<T: Introspect> Introspect for Arc<T> {
fn introspect_value(&self) -> String {
format!("Arc({})", self.deref().introspect_value())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
self.deref().introspect_child(index)
}
fn introspect_len(&self) -> usize {
self.deref().introspect_len()
}
}
#[cfg(feature = "parking_lot")]
impl<T: Introspect> Introspect for RwLock<T> {
fn introspect_value(&self) -> String {
format!("RwLock<{}>", std::any::type_name::<T>())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index == 0 {
Some(Box::new(IntrospectItemRwLock { g: self.read() }))
} else {
None
}
}
fn introspect_len(&self) -> usize {
1
}
}
#[cfg(feature = "parking_lot")]
impl<T: WithSchema> WithSchema for RwLock<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
T::schema(version, context)
}
}
#[cfg(feature = "parking_lot")]
impl<T> Packed for RwLock<T> {}
#[cfg(feature = "parking_lot")]
impl<T: Serialize> Serialize for RwLock<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let data = self.read();
data.serialize(serializer)
}
}
#[cfg(feature = "parking_lot")]
impl<T: Deserialize> Deserialize for RwLock<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<RwLock<T>, SavefileError> {
Ok(RwLock::new(T::deserialize(deserializer)?))
}
}
pub struct IntrospectItemSimple<'a> {
key: String,
val: &'a dyn Introspect,
}
impl<'a> IntrospectItem<'a> for IntrospectItemSimple<'a> {
fn key(&self) -> &str {
&self.key
}
fn val(&self) -> &dyn Introspect {
self.val
}
}
pub fn introspect_item<'a>(key: String, val: &'a dyn Introspect) -> Box<dyn IntrospectItem<'a> + 'a> {
Box::new(IntrospectItemSimple { key, val })
}
#[cfg(not(feature = "nightly"))]
impl<K: Introspect + Eq + Hash, V: Introspect, S: ::std::hash::BuildHasher> Introspect for HashMap<K, V, S> {
fn introspect_value(&self) -> String {
format!("HashMap<{},{}>", std::any::type_name::<K>(), std::any::type_name::<V>())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
let bucket = index / 2;
let off = index % 2;
if let Some((key, val)) = self.iter().nth(bucket) {
if off == 0 {
Some(introspect_item(format!("Key #{}", index), key))
} else {
Some(introspect_item(format!("Value #{}", index), val))
}
} else {
None
}
}
fn introspect_len(&self) -> usize {
self.len()
}
}
#[cfg(feature = "nightly")]
impl<K: Introspect + Eq + Hash, V: Introspect, S: ::std::hash::BuildHasher> Introspect for HashMap<K, V, S> {
default fn introspect_value(&self) -> String {
format!("HashMap<{},{}>", std::any::type_name::<K>(), std::any::type_name::<V>())
}
default fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
let bucket = index / 2;
let off = index % 2;
if let Some((key, val)) = self.iter().nth(bucket) {
if off == 0 {
Some(introspect_item(format!("Key #{}", index), key))
} else {
Some(introspect_item(format!("Value #{}", index), val))
}
} else {
None
}
}
default fn introspect_len(&self) -> usize {
self.len()
}
}
#[cfg(feature = "nightly")]
impl<K: Introspect + Eq + Hash, V: Introspect, S: ::std::hash::BuildHasher> Introspect for HashMap<K, V, S>
where
K: ToString,
{
fn introspect_value(&self) -> String {
format!("HashMap<{},{}>", std::any::type_name::<K>(), std::any::type_name::<V>())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if let Some((key, val)) = self.iter().nth(index) {
Some(introspect_item(key.to_string(), val))
} else {
None
}
}
fn introspect_len(&self) -> usize {
self.len()
}
}
impl<K: Introspect + Eq + Hash, S: ::std::hash::BuildHasher> Introspect for HashSet<K, S> {
fn introspect_value(&self) -> String {
format!("HashSet<{}>", std::any::type_name::<K>())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if let Some(key) = self.iter().nth(index) {
Some(introspect_item(format!("#{}", index), key))
} else {
None
}
}
fn introspect_len(&self) -> usize {
self.len()
}
}
impl<K: Introspect> Introspect for BTreeSet<K> {
fn introspect_value(&self) -> String {
format!("BTreeSet<{}>", std::any::type_name::<K>())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if let Some(key) = self.iter().nth(index) {
Some(introspect_item(format!("#{}", index), key))
} else {
None
}
}
fn introspect_len(&self) -> usize {
self.len()
}
}
impl<K: Introspect, V: Introspect> Introspect for BTreeMap<K, V> {
fn introspect_value(&self) -> String {
format!(
"BTreeMap<{},{}>",
std::any::type_name::<K>(),
std::any::type_name::<V>()
)
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
let bucket = index / 2;
let off = index % 2;
if let Some((key, val)) = self.iter().nth(bucket) {
if off == 0 {
Some(introspect_item(format!("Key #{}", index), key))
} else {
Some(introspect_item(format!("Value #{}", index), val))
}
} else {
None
}
}
fn introspect_len(&self) -> usize {
self.len()
}
}
impl<K: WithSchema + 'static, V: WithSchema + 'static> WithSchema for BTreeMap<K, V> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(Schema::Struct(SchemaStruct {
dbg_name: "KeyValuePair".to_string(),
size: None,
alignment: None,
fields: vec![
Field {
name: "key".to_string(),
value: Box::new(context.possible_recursion::<K>(|context| K::schema(version, context))),
offset: None,
},
Field {
name: "value".to_string(),
value: Box::new(context.possible_recursion::<V>(|context| V::schema(version, context))),
offset: None,
},
],
})),
VecOrStringLayout::Unknown,
)
}
}
impl<K, V> Packed for BTreeMap<K, V> {}
impl<K: Serialize + 'static, V: Serialize + 'static> Serialize for BTreeMap<K, V> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.len().serialize(serializer)?;
for (k, v) in self {
k.serialize(serializer)?;
v.serialize(serializer)?;
}
Ok(())
}
}
impl<K: Deserialize + Ord + 'static, V: Deserialize + 'static> Deserialize for BTreeMap<K, V> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let mut ret = BTreeMap::new();
let count = <usize as Deserialize>::deserialize(deserializer)?;
for _ in 0..count {
ret.insert(
<_ as Deserialize>::deserialize(deserializer)?,
<_ as Deserialize>::deserialize(deserializer)?,
);
}
Ok(ret)
}
}
impl<K> Packed for BTreeSet<K> {}
impl<K: WithSchema + 'static> WithSchema for BTreeSet<K> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(context.possible_recursion::<K>(|context| K::schema(version, context))),
VecOrStringLayout::Unknown,
)
}
}
impl<K: Serialize + 'static> Serialize for BTreeSet<K> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_usize(self.len())?;
for item in self {
item.serialize(serializer)?;
}
Ok(())
}
}
impl<K: Deserialize + 'static + Ord> Deserialize for BTreeSet<K> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let cnt = deserializer.read_usize()?;
let mut ret = BTreeSet::new();
for _ in 0..cnt {
ret.insert(<_ as Deserialize>::deserialize(deserializer)?);
}
Ok(ret)
}
}
impl<K, S: ::std::hash::BuildHasher> Packed for HashSet<K, S> {}
impl<K: WithSchema + 'static, S: ::std::hash::BuildHasher> WithSchema for HashSet<K, S> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(context.possible_recursion::<K>(|context| K::schema(version, context))),
VecOrStringLayout::Unknown,
)
}
}
impl<K: Serialize + 'static, S: ::std::hash::BuildHasher> Serialize for HashSet<K, S> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_usize(self.len())?;
for item in self {
item.serialize(serializer)?;
}
Ok(())
}
}
impl<K: Deserialize + Eq + Hash + 'static, S: ::std::hash::BuildHasher + Default> Deserialize for HashSet<K, S> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let cnt = deserializer.read_usize()?;
let mut ret = HashSet::with_capacity_and_hasher(cnt, S::default());
for _ in 0..cnt {
ret.insert(<_ as Deserialize>::deserialize(deserializer)?);
}
Ok(ret)
}
}
impl<K: WithSchema + Eq + Hash + 'static, V: WithSchema + 'static, S: ::std::hash::BuildHasher> WithSchema
for HashMap<K, V, S>
{
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(Schema::Struct(SchemaStruct {
dbg_name: "KeyValuePair".to_string(),
size: None,
alignment: None,
fields: vec![
Field {
name: "key".to_string(),
value: Box::new(context.possible_recursion::<K>(|context| K::schema(version, context))),
offset: None,
},
Field {
name: "value".to_string(),
value: Box::new(context.possible_recursion::<K>(|context| V::schema(version, context))),
offset: None,
},
],
})),
VecOrStringLayout::Unknown,
)
}
}
impl<K: Eq + Hash, V, S: ::std::hash::BuildHasher> Packed for HashMap<K, V, S> {}
impl<K: Serialize + Eq + Hash + 'static, V: Serialize + 'static, S: ::std::hash::BuildHasher> Serialize
for HashMap<K, V, S>
{
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_usize(self.len())?;
for (k, v) in self.iter() {
k.serialize(serializer)?;
v.serialize(serializer)?;
}
Ok(())
}
}
impl<K: Deserialize + Eq + Hash + 'static, V: Deserialize + 'static, S: ::std::hash::BuildHasher + Default> Deserialize
for HashMap<K, V, S>
{
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let l = deserializer.read_usize()?;
let mut ret: Self = HashMap::with_capacity_and_hasher(l, Default::default());
for _ in 0..l {
ret.insert(K::deserialize(deserializer)?, V::deserialize(deserializer)?);
}
Ok(ret)
}
}
#[cfg(feature = "indexmap")]
impl<K: WithSchema + Eq + Hash + 'static, V: WithSchema + 'static, S: ::std::hash::BuildHasher> WithSchema
for IndexMap<K, V, S>
{
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(Schema::Struct(SchemaStruct {
dbg_name: "KeyValuePair".to_string(),
size: None,
alignment: None,
fields: vec![
Field {
name: "key".to_string(),
value: Box::new(context.possible_recursion::<K>(|context| K::schema(version, context))),
offset: None,
},
Field {
name: "value".to_string(),
value: Box::new(context.possible_recursion::<K>(|context| V::schema(version, context))),
offset: None,
},
],
})),
VecOrStringLayout::Unknown,
)
}
}
#[cfg(all(not(feature = "nightly"), feature = "indexmap"))]
impl<K: Introspect + Eq + Hash, V: Introspect, S: ::std::hash::BuildHasher> Introspect for IndexMap<K, V, S> {
fn introspect_value(&self) -> String {
format!(
"IndexMap<{},{}>",
std::any::type_name::<K>(),
std::any::type_name::<V>()
)
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
let bucket = index / 2;
let off = index % 2;
if let Some((k, v)) = self.get_index(bucket) {
if off == 0 {
Some(introspect_item(format!("Key #{}", bucket), k))
} else {
Some(introspect_item(format!("Value #{}", bucket), v))
}
} else {
None
}
}
fn introspect_len(&self) -> usize {
self.len()
}
}
#[cfg(all(feature = "nightly", feature = "indexmap"))]
impl<K: Introspect + Eq + Hash, V: Introspect, S: ::std::hash::BuildHasher> Introspect for IndexMap<K, V, S> {
default fn introspect_value(&self) -> String {
format!(
"IndexMap<{},{}>",
std::any::type_name::<K>(),
std::any::type_name::<V>()
)
}
default fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
let bucket = index / 2;
let off = index % 2;
if let Some((k, v)) = self.get_index(bucket) {
if off == 0 {
Some(introspect_item(format!("Key #{}", bucket), k))
} else {
Some(introspect_item(format!("Value #{}", bucket), v))
}
} else {
None
}
}
default fn introspect_len(&self) -> usize {
self.len()
}
}
#[cfg(all(feature = "nightly", feature = "indexmap"))]
impl<K: Introspect + Eq + Hash, V: Introspect, S: ::std::hash::BuildHasher> Introspect for IndexMap<K, V, S>
where
K: ToString,
{
fn introspect_value(&self) -> String {
format!(
"IndexMap<{},{}>",
std::any::type_name::<K>(),
std::any::type_name::<V>()
)
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if let Some((k, v)) = self.get_index(index) {
Some(introspect_item(k.to_string(), v))
} else {
None
}
}
fn introspect_len(&self) -> usize {
self.len()
}
}
#[cfg(feature = "indexmap")]
impl<K: Eq + Hash, V, S: ::std::hash::BuildHasher> Packed for IndexMap<K, V, S> {}
#[cfg(feature = "indexmap")]
impl<K: Serialize + Eq + Hash + 'static, V: Serialize + 'static, S: ::std::hash::BuildHasher> Serialize
for IndexMap<K, V, S>
{
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_usize(self.len())?;
for (k, v) in self.iter() {
k.serialize(serializer)?;
v.serialize(serializer)?;
}
Ok(())
}
}
#[cfg(feature = "indexmap")]
impl<K: Deserialize + Eq + Hash + 'static, V: Deserialize + 'static> Deserialize for IndexMap<K, V> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let l = deserializer.read_usize()?;
let mut ret = IndexMap::with_capacity(l);
for _ in 0..l {
ret.insert(K::deserialize(deserializer)?, V::deserialize(deserializer)?);
}
Ok(ret)
}
}
#[cfg(feature = "indexmap")]
impl<K: Introspect + Eq + Hash, S: ::std::hash::BuildHasher> Introspect for IndexSet<K, S> {
fn introspect_value(&self) -> String {
format!("IndexSet<{}>", std::any::type_name::<K>())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if let Some(val) = self.get_index(index) {
Some(introspect_item(format!("#{}", index), val))
} else {
None
}
}
fn introspect_len(&self) -> usize {
self.len()
}
}
#[cfg(feature = "indexmap")]
impl<K: Eq + Hash, S: ::std::hash::BuildHasher> Packed for IndexSet<K, S> {}
#[cfg(feature = "indexmap")]
impl<K: WithSchema + Eq + Hash + 'static, S: ::std::hash::BuildHasher> WithSchema for IndexSet<K, S> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(Schema::Struct(SchemaStruct {
dbg_name: "Key".to_string(),
size: None,
alignment: None,
fields: vec![Field {
name: "key".to_string(),
value: Box::new(context.possible_recursion::<K>(|context| K::schema(version, context))),
offset: None,
}],
})),
VecOrStringLayout::Unknown,
)
}
}
#[cfg(feature = "indexmap")]
impl<K: Serialize + Eq + Hash + 'static, S: ::std::hash::BuildHasher> Serialize for IndexSet<K, S> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_usize(self.len())?;
for k in self.iter() {
k.serialize(serializer)?;
}
Ok(())
}
}
#[cfg(feature = "indexmap")]
impl<K: Deserialize + Eq + Hash + 'static> Deserialize for IndexSet<K> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let l = deserializer.read_usize()?;
let mut ret = IndexSet::with_capacity(l);
for _ in 0..l {
ret.insert(K::deserialize(deserializer)?);
}
Ok(ret)
}
}
#[cfg_attr(
feature = "rust1_78",
diagnostic::on_unimplemented(
message = "`{Self}` cannot serve as a factory generating default values of type {T}, since it doesn't implement the trait `savefile::ValueConstructor<{T}>`-",
label = "`{Self}` cannot produce values of type `{T}`",
note = "Check that any type used as 2nd type parameter to AbiRemoved implements `savefile::ValueConstructor<{T}>`.",
note = "Alternatively, skip the 2nd parameter entirely, and ensure that `{T}` implements `Default`.",
)
)]
pub trait ValueConstructor<T> {
fn make_value() -> T;
}
#[derive(Debug, PartialEq, Eq)]
pub struct DefaultValueConstructor<T> {
phantom: PhantomData<*const T>,
}
impl<T: Default> ValueConstructor<T> for DefaultValueConstructor<T> {
fn make_value() -> T {
<T as Default>::default()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Removed<T> {
phantom: std::marker::PhantomData<*const T>,
}
unsafe impl<T> Send for Removed<T> {}
unsafe impl<T> Sync for Removed<T> {}
impl<T> Removed<T> {
pub fn new() -> Removed<T> {
Removed {
phantom: std::marker::PhantomData,
}
}
}
impl<T: WithSchema> WithSchema for Removed<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
<T>::schema(version, context)
}
}
impl<T: Introspect> Introspect for Removed<T> {
fn introspect_value(&self) -> String {
format!("Removed<{}>", std::any::type_name::<T>())
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl<T> Packed for Removed<T> {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl<T: WithSchema> Serialize for Removed<T> {
fn serialize(&self, _serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
panic!("Something is wrong with version-specification of fields - there was an attempt to actually serialize a removed field!");
}
}
impl<T: WithSchema + Deserialize> Deserialize for Removed<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
T::deserialize(deserializer)?;
Ok(Removed {
phantom: std::marker::PhantomData,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AbiRemoved<T, D = DefaultValueConstructor<T>>
where
D: ValueConstructor<T>,
{
phantom: std::marker::PhantomData<(*const T, *const D)>,
}
unsafe impl<T, D: ValueConstructor<T>> Send for AbiRemoved<T, D> {}
unsafe impl<T, D: ValueConstructor<T>> Sync for AbiRemoved<T, D> {}
impl<T, D: ValueConstructor<T>> AbiRemoved<T, D> {
pub fn new() -> AbiRemoved<T, D> {
AbiRemoved {
phantom: std::marker::PhantomData,
}
}
}
impl<T: WithSchema, D: ValueConstructor<T>> WithSchema for AbiRemoved<T, D> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
<T>::schema(version, context)
}
}
impl<T: Introspect, D: ValueConstructor<T>> Introspect for AbiRemoved<T, D> {
fn introspect_value(&self) -> String {
format!("AbiRemoved<{}>", std::any::type_name::<T>())
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl<T, D: ValueConstructor<T>> Packed for AbiRemoved<T, D> {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl<T: WithSchema + Serialize + Default, D: ValueConstructor<T>> Serialize for AbiRemoved<T, D> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let dummy = D::make_value();
dummy.serialize(serializer)?;
Ok(())
}
}
impl<T: WithSchema + Deserialize, D: ValueConstructor<T>> Deserialize for AbiRemoved<T, D> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
T::deserialize(deserializer)?;
Ok(AbiRemoved {
phantom: std::marker::PhantomData,
})
}
}
impl<T> Introspect for PhantomData<T> {
fn introspect_value(&self) -> String {
"PhantomData".to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl<T> WithSchema for std::marker::PhantomData<T> {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::ZeroSize
}
}
impl<T> Packed for std::marker::PhantomData<T> {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl<T> Serialize for std::marker::PhantomData<T> {
fn serialize(&self, _serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
Ok(())
}
}
impl<T> Deserialize for std::marker::PhantomData<T> {
fn deserialize(_deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(std::marker::PhantomData)
}
}
impl<T: Introspect> Introspect for Box<T> {
fn introspect_value(&self) -> String {
self.deref().introspect_value()
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
self.deref().introspect_child(index)
}
fn introspect_len(&self) -> usize {
self.deref().introspect_len()
}
}
impl<T: Introspect> Introspect for Option<T> {
fn introspect_value(&self) -> String {
if let Some(cont) = self {
format!("Some({})", cont.introspect_value())
} else {
"None".to_string()
}
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if let Some(cont) = self {
cont.introspect_child(index)
} else {
None
}
}
fn introspect_len(&self) -> usize {
if let Some(cont) = self {
cont.introspect_len()
} else {
0
}
}
}
impl<T: WithSchema> WithSchema for Option<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::SchemaOption(Box::new(T::schema(version, context)))
}
}
impl<T> Packed for Option<T> {} impl<T: Serialize> Serialize for Option<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
match self {
Some(ref x) => {
serializer.write_bool(true)?;
x.serialize(serializer)
}
None => serializer.write_bool(false),
}
}
}
impl<T: Deserialize> Deserialize for Option<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let issome = deserializer.read_bool()?;
if issome {
Ok(Some(T::deserialize(deserializer)?))
} else {
Ok(None)
}
}
}
impl<T: Introspect, R: Introspect> Introspect for Result<T, R> {
fn introspect_value(&self) -> String {
match self {
Ok(cont) => format!("Ok({})", cont.introspect_value()),
Err(cont) => format!("Err({})", cont.introspect_value()),
}
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
match self {
Ok(cont) => cont.introspect_child(index),
Err(cont) => cont.introspect_child(index),
}
}
fn introspect_len(&self) -> usize {
match self {
Ok(cont) => cont.introspect_len(),
Err(cont) => cont.introspect_len(),
}
}
}
impl<T: WithSchema, R: WithSchema> WithSchema for Result<T, R> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Enum(SchemaEnum {
dbg_name: "Result".to_string(),
size: None,
alignment: None,
variants: vec![
Variant {
name: "Ok".to_string(),
discriminant: 0,
fields: vec![Field {
name: "ok".to_string(),
value: Box::new(T::schema(version, context)),
offset: None,
}],
},
Variant {
name: "Err".to_string(),
discriminant: 0,
fields: vec![Field {
name: "err".to_string(),
value: Box::new(R::schema(version, context)),
offset: None,
}],
},
],
discriminant_size: 1,
has_explicit_repr: false,
})
}
}
impl<T, R> Packed for Result<T, R> {} impl<T: Serialize, R: Serialize> Serialize for Result<T, R> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
match self {
Ok(x) => {
serializer.write_bool(true)?;
x.serialize(serializer)
}
Err(x) => {
serializer.write_bool(false)?;
x.serialize(serializer)
}
}
}
}
impl<T: Deserialize, R: Deserialize> Deserialize for Result<T, R> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let issome = deserializer.read_bool()?;
if issome {
Ok(Ok(T::deserialize(deserializer)?))
} else {
Ok(Err(R::deserialize(deserializer)?))
}
}
}
#[cfg(any(feature = "bit-vec", feature = "bit-vec08"))]
#[cfg(target_endian = "big")]
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, context: &mut WithSchemaContext) -> Schema {
Schema::Struct(SchemaStruct {
dbg_name: "BitVec".to_string(),
size: None,
alignment: None,
fields: vec![
Field {
name: "num_bits".to_string(),
value: Box::new(usize::schema(version, context)),
offset: None,
},
Field {
name: "num_bytes".to_string(),
value: Box::new(usize::schema(version, context)),
offset: None,
},
Field {
name: "buffer".to_string(),
value: Box::new(Schema::Vector(
Box::new(u8::schema(version, context)),
VecOrStringLayout::Unknown,
)),
offset: None,
},
],
})
}
}
#[cfg(feature = "bit-vec")]
impl Introspect for bit_vec::BitVec {
fn introspect_value(&self) -> String {
let mut ret = String::new();
for i in 0..self.len() {
if self[i] {
ret.push('1');
} else {
ret.push('0');
}
}
ret
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
#[cfg(feature = "bit-vec")]
impl Serialize for bit_vec::BitVec<u32> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let l = self.len();
serializer.write_usize(l)?;
let storage = self.storage();
let rawbytes_ptr = storage.as_ptr() as *const u8;
let rawbytes: &[u8] = unsafe { std::slice::from_raw_parts(rawbytes_ptr, 4 * storage.len()) };
serializer.write_usize(rawbytes.len() | (1 << 63))?;
serializer.write_bytes(rawbytes)?;
Ok(())
}
}
#[cfg(feature = "bit-vec")]
impl Packed for bit_vec::BitVec<u32> {}
#[cfg(feature = "bit-vec")]
impl Deserialize for bit_vec::BitVec<u32> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let numbits = deserializer.read_usize()?;
let mut numbytes = deserializer.read_usize()?;
if numbytes & (1 << 63) != 0 {
numbytes &= !(1 << 63);
let mut ret = bit_vec::BitVec::with_capacity(numbytes * 8);
unsafe {
let num_words = numbytes / 4;
let storage = ret.storage_mut();
storage.resize(num_words, 0);
let storage_ptr = storage.as_ptr() as *mut u8;
let storage_bytes: &mut [u8] = std::slice::from_raw_parts_mut(storage_ptr, 4 * num_words);
deserializer.read_bytes_to_buf(storage_bytes)?;
ret.set_len(numbits);
}
Ok(ret)
} else {
let bytes = deserializer.read_bytes(numbytes)?;
let mut ret = bit_vec::BitVec::from_bytes(&bytes);
ret.truncate(numbits);
Ok(ret)
}
}
}
#[cfg(feature = "bit-set")]
impl WithSchema for bit_set::BitSet {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Struct(SchemaStruct {
dbg_name: "BitSet".to_string(),
size: None,
alignment: None,
fields: vec![
Field {
name: "num_bits".to_string(),
value: Box::new(usize::schema(version, context)),
offset: None,
},
Field {
name: "num_bytes".to_string(),
value: Box::new(usize::schema(version, context)),
offset: None,
},
Field {
name: "buffer".to_string(),
value: Box::new(Schema::Vector(
Box::new(u8::schema(version, context)),
VecOrStringLayout::Unknown,
)),
offset: None,
},
],
})
}
}
#[cfg(feature = "bit-set")]
impl Introspect for bit_set::BitSet {
fn introspect_value(&self) -> String {
let mut ret = String::new();
for i in 0..self.len() {
if self.contains(i) {
use std::fmt::Write;
if !ret.is_empty() {
ret += " ";
}
write!(&mut ret, "{}", i).unwrap();
}
}
ret
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
#[cfg(feature = "bit-set")]
impl Packed for bit_set::BitSet<u32> {}
#[cfg(feature = "bit-set")]
impl Serialize for bit_set::BitSet<u32> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let bitset = self.get_ref();
bitset.serialize(serializer)
}
}
#[cfg(feature = "bit-set")]
impl Deserialize for bit_set::BitSet<u32> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let bit_vec: bit_vec::BitVec = bit_vec::BitVec::deserialize(deserializer)?;
Ok(bit_set::BitSet::from_bit_vec(bit_vec))
}
}
#[cfg(feature = "bit-vec08")]
impl WithSchema for bit_vec08::BitVec {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Struct(SchemaStruct {
dbg_name: "BitVec".to_string(),
size: None,
alignment: None,
fields: vec![
Field {
name: "num_bits".to_string(),
value: Box::new(usize::schema(version, context)),
offset: None,
},
Field {
name: "num_bytes".to_string(),
value: Box::new(usize::schema(version, context)),
offset: None,
},
Field {
name: "buffer".to_string(),
value: Box::new(Schema::Vector(
Box::new(u8::schema(version, context)),
VecOrStringLayout::Unknown,
)),
offset: None,
},
],
})
}
}
#[cfg(feature = "bit-vec08")]
impl Introspect for bit_vec08::BitVec {
fn introspect_value(&self) -> String {
let mut ret = String::new();
for i in 0..self.len() {
if self[i] {
ret.push('1');
} else {
ret.push('0');
}
}
ret
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
#[cfg(feature = "bit-vec08")]
impl Serialize for bit_vec08::BitVec<u32> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let l = self.len();
serializer.write_usize(l)?;
let storage = self.storage();
let rawbytes_ptr = storage.as_ptr() as *const u8;
let rawbytes: &[u8] = unsafe { std::slice::from_raw_parts(rawbytes_ptr, 4 * storage.len()) };
serializer.write_usize(rawbytes.len() | (1 << 63))?;
serializer.write_bytes(rawbytes)?;
Ok(())
}
}
#[cfg(feature = "bit-vec08")]
impl Packed for bit_vec08::BitVec<u32> {}
#[cfg(feature = "bit-vec08")]
impl Deserialize for bit_vec08::BitVec<u32> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let numbits = deserializer.read_usize()?;
let mut numbytes = deserializer.read_usize()?;
if numbytes & (1 << 63) != 0 {
numbytes &= !(1 << 63);
let mut ret = bit_vec08::BitVec::with_capacity(numbytes * 8);
unsafe {
let num_words = numbytes / 4;
let storage = ret.storage_mut();
storage.resize(num_words, 0);
let storage_ptr = storage.as_ptr() as *mut u8;
let storage_bytes: &mut [u8] = std::slice::from_raw_parts_mut(storage_ptr, 4 * num_words);
deserializer.read_bytes_to_buf(storage_bytes)?;
ret.set_len(numbits);
}
Ok(ret)
} else {
let bytes = deserializer.read_bytes(numbytes)?;
let mut ret = bit_vec08::BitVec::from_bytes(&bytes);
ret.truncate(numbits);
Ok(ret)
}
}
}
#[cfg(feature = "bit-set")]
impl WithSchema for bit_set08::BitSet {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Struct(SchemaStruct {
dbg_name: "BitSet".to_string(),
size: None,
alignment: None,
fields: vec![
Field {
name: "num_bits".to_string(),
value: Box::new(usize::schema(version, context)),
offset: None,
},
Field {
name: "num_bytes".to_string(),
value: Box::new(usize::schema(version, context)),
offset: None,
},
Field {
name: "buffer".to_string(),
value: Box::new(Schema::Vector(
Box::new(u8::schema(version, context)),
VecOrStringLayout::Unknown,
)),
offset: None,
},
],
})
}
}
#[cfg(feature = "bit-set08")]
impl Introspect for bit_set08::BitSet {
fn introspect_value(&self) -> String {
let mut ret = String::new();
for i in 0..self.len() {
if self.contains(i) {
use std::fmt::Write;
if !ret.is_empty() {
ret += " ";
}
write!(&mut ret, "{}", i).unwrap();
}
}
ret
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
#[cfg(feature = "bit-set08")]
impl Packed for bit_set08::BitSet<u32> {}
#[cfg(feature = "bit-set08")]
impl Serialize for bit_set08::BitSet<u32> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let bitset = self.get_ref();
bitset.serialize(serializer)
}
}
#[cfg(feature = "bit-set")]
impl Deserialize for bit_set08::BitSet<u32> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let bit_vec: bit_vec08::BitVec = bit_vec08::BitVec::deserialize(deserializer)?;
Ok(bit_set08::BitSet::from_bit_vec(bit_vec))
}
}
impl<T: Introspect> Introspect for BinaryHeap<T> {
fn introspect_value(&self) -> String {
"BinaryHeap".to_string()
}
fn introspect_child<'a>(&'a self, index: usize) -> Option<Box<dyn IntrospectItem<'a> + 'a>> {
if index >= self.len() {
return None;
}
return Some(introspect_item(index.to_string(), self.iter().nth(index).unwrap()));
}
fn introspect_len(&self) -> usize {
self.len()
}
}
impl<T> Packed for BinaryHeap<T> {}
impl<T: WithSchema + 'static> WithSchema for BinaryHeap<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(context.possible_recursion::<T>(|context| T::schema(version, context))),
VecOrStringLayout::Unknown,
)
}
}
impl<T: Serialize + Ord + 'static> Serialize for BinaryHeap<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let l = self.len();
serializer.write_usize(l)?;
for item in self.iter() {
item.serialize(serializer)?
}
Ok(())
}
}
impl<T: Deserialize + Ord + 'static> Deserialize for BinaryHeap<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let l = deserializer.read_usize()?;
let mut ret = BinaryHeap::with_capacity(l);
for _ in 0..l {
ret.push(T::deserialize(deserializer)?);
}
Ok(ret)
}
}
#[cfg(feature = "smallvec")]
impl<T: smallvec::Array> Introspect for smallvec::SmallVec<T>
where
T::Item: Introspect,
{
fn introspect_value(&self) -> String {
format!("SmallVec<{}>", std::any::type_name::<T>())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if let Some(val) = self.get(index) {
Some(introspect_item(index.to_string(), val))
} else {
None
}
}
fn introspect_len(&self) -> usize {
self.len()
}
}
#[cfg(feature = "smallvec")]
impl<T: smallvec::Array + 'static> WithSchema for smallvec::SmallVec<T>
where
T::Item: WithSchema,
{
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(context.possible_recursion::<T>(|context| T::Item::schema(version, context))),
VecOrStringLayout::Unknown,
)
}
}
#[cfg(feature = "smallvec")]
impl<T: smallvec::Array> Packed for smallvec::SmallVec<T> {}
#[cfg(feature = "smallvec")]
impl<T: smallvec::Array + 'static> Serialize for smallvec::SmallVec<T>
where
T::Item: Serialize,
{
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let l = self.len();
serializer.write_usize(l)?;
for item in self.iter() {
item.serialize(serializer)?
}
Ok(())
}
}
#[cfg(feature = "smallvec")]
impl<T: smallvec::Array + 'static> Deserialize for smallvec::SmallVec<T>
where
T::Item: Deserialize,
{
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let l = deserializer.read_usize()?;
let mut ret = Self::with_capacity(l);
for _ in 0..l {
ret.push(T::Item::deserialize(deserializer)?);
}
Ok(ret)
}
}
fn regular_serialize_vec<T: Serialize>(
items: &[T],
serializer: &mut Serializer<impl Write>,
) -> Result<(), SavefileError> {
let l = items.len();
serializer.write_usize(l)?;
if std::mem::size_of::<T>() == 0 {
return Ok(());
}
if std::mem::size_of::<T>() < 32 {
let chunks = items.chunks_exact((64 / std::mem::size_of::<T>()).max(1));
let remainder = chunks.remainder();
for chunk in chunks {
for item in chunk {
item.serialize(serializer)?;
}
}
for item in remainder {
item.serialize(serializer)?;
}
Ok(())
} else {
for item in items {
item.serialize(serializer)?;
}
Ok(())
}
}
impl<T: WithSchema + 'static> WithSchema for Box<[T]> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(context.possible_recursion::<T>(|context| T::schema(version, context))),
VecOrStringLayout::Unknown,
)
}
}
impl<T: WithSchema + 'static> WithSchema for Arc<[T]> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(context.possible_recursion::<T>(|context| T::schema(version, context))),
VecOrStringLayout::Unknown,
)
}
}
impl<T: Introspect> Introspect for Box<[T]> {
fn introspect_value(&self) -> String {
return "Box[]".to_string();
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index >= self.len() {
return None;
}
return Some(introspect_item(index.to_string(), &self[index]));
}
fn introspect_len(&self) -> usize {
self.len()
}
}
impl<T: Introspect> Introspect for Arc<[T]> {
fn introspect_value(&self) -> String {
return "Arc[]".to_string();
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index >= self.len() {
return None;
}
return Some(introspect_item(index.to_string(), &self[index]));
}
fn introspect_len(&self) -> usize {
self.len()
}
}
impl WithSchema for Arc<str> {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_string(VecOrStringLayout::Unknown))
}
}
impl Introspect for Arc<str> {
fn introspect_value(&self) -> String {
self.deref().to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem>> {
None
}
fn introspect_len(&self) -> usize {
0
}
}
impl Serialize for Arc<str> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_string(self)
}
}
impl Packed for Arc<str> {}
impl Deserialize for Arc<str> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let s = deserializer.read_string()?;
let state = deserializer.get_state::<Arc<str>, HashMap<String, Arc<str>>>();
if let Some(needle) = state.get(&s) {
return Ok(Arc::clone(needle));
}
let arc_ref = state.entry(s.clone()).or_insert(s.into());
Ok(Arc::clone(arc_ref))
}
}
impl<T: Serialize + Packed + 'static> Serialize for Box<[T]> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
unsafe {
if T::repr_c_optimization_safe(serializer.file_version).is_false() {
regular_serialize_vec(self, serializer)
} else {
let l = self.len();
serializer.write_usize(l)?;
serializer.write_buf(std::slice::from_raw_parts(
(*self).as_ptr() as *const u8,
std::mem::size_of::<T>() * l,
))
}
}
}
}
impl<T: Packed> Packed for Box<[T]> {}
impl<T: Serialize + Packed + 'static> Serialize for Arc<[T]> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
unsafe {
if T::repr_c_optimization_safe(serializer.file_version).is_false() {
regular_serialize_vec(self, serializer)
} else {
let l = self.len();
serializer.write_usize(l)?;
serializer.write_buf(std::slice::from_raw_parts(
(*self).as_ptr() as *const u8,
std::mem::size_of::<T>() * l,
))
}
}
}
}
impl<T: Packed> Packed for Arc<[T]> {}
impl<T: Deserialize + Packed + 'static> Deserialize for Arc<[T]> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(Vec::<T>::deserialize(deserializer)?.into())
}
}
impl<T: Deserialize + Packed + 'static> Deserialize for Box<[T]> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(Vec::<T>::deserialize(deserializer)?.into_boxed_slice())
}
}
impl WithSchema for &'_ str {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_string(calculate_string_memory_layout()))
}
}
impl Serialize for &'_ str {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let l = self.len();
serializer.write_usize(l)?;
serializer.write_buf(self.as_bytes())
}
}
impl<T: WithSchema + 'static> WithSchema for &'_ [T] {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(context.possible_recursion::<T>(|context| T::schema(version, context))),
calculate_slice_memory_layout::<T>(),
)
}
}
impl<T: Serialize + Packed + 'static> Serialize for &'_ [T] {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
unsafe {
if T::repr_c_optimization_safe(serializer.file_version).is_false() {
regular_serialize_vec(self, serializer)
} else {
let l = self.len();
serializer.write_usize(l)?;
#[allow(clippy::manual_slice_size_calculation)] serializer.write_buf(std::slice::from_raw_parts(
self.as_ptr() as *const u8,
std::mem::size_of::<T>() * l,
))
}
}
}
}
pub fn deserialize_slice_as_vec<R: Read, T: Deserialize + Packed + 'static>(
deserializer: &mut Deserializer<R>,
) -> Result<Vec<T>, SavefileError> {
Vec::deserialize(deserializer)
}
impl<T> Packed for Vec<T> {}
static STRING_IS_STANDARD_LAYOUT: AtomicU8 = AtomicU8::new(255);
#[derive(Debug)]
#[repr(C)]
struct RawVecInspector {
p1: usize,
p2: usize,
p3: usize,
}
#[derive(Debug)]
#[repr(C)]
struct RawSliceInspector {
p1: usize,
p2: usize,
}
impl RawSliceInspector {
const fn get_layout(&self) -> VecOrStringLayout {
if self.p1 == 0 {
VecOrStringLayout::LengthData
} else {
VecOrStringLayout::DataLength
}
}
}
impl RawVecInspector {
fn get_layout(&self, ptr: *const u8) -> VecOrStringLayout {
let ptr = ptr as usize;
const LENGTH: usize = 0;
const CAPACITY: usize = 7;
match (self.p1, self.p2, self.p3) {
(LENGTH, CAPACITY, x) if x == ptr => VecOrStringLayout::LengthCapacityData,
(CAPACITY, LENGTH, x) if x == ptr => VecOrStringLayout::CapacityLengthData,
(LENGTH, x, CAPACITY) if x == ptr => VecOrStringLayout::LengthDataCapacity,
(CAPACITY, x, LENGTH) if x == ptr => VecOrStringLayout::CapacityDataLength,
(x, LENGTH, CAPACITY) if x == ptr => VecOrStringLayout::DataLengthCapacity,
(x, CAPACITY, LENGTH) if x == ptr => VecOrStringLayout::DataCapacityLength,
_ => VecOrStringLayout::Unknown,
}
}
}
pub const fn calculate_slice_memory_layout<T>() -> VecOrStringLayout {
if std::mem::size_of::<&[T]>() != 16 || std::mem::size_of::<RawSliceInspector>() != 16 {
VecOrStringLayout::Unknown
} else {
let test_slice: &[T] = &[];
let insp: RawSliceInspector = unsafe { std::mem::transmute_copy::<&[T], RawSliceInspector>(&test_slice) };
insp.get_layout()
}
}
pub fn calculate_vec_memory_layout<T>() -> VecOrStringLayout {
if std::mem::size_of::<Vec<u8>>() != 24 || std::mem::size_of::<RawVecInspector>() != 24 {
VecOrStringLayout::Unknown
} else {
let test_vec = Vec::with_capacity(7);
let insp: RawVecInspector = unsafe { std::mem::transmute_copy(&test_vec) };
let ptr = test_vec.as_ptr();
insp.get_layout(ptr)
}
}
fn calculate_string_memory_layout() -> VecOrStringLayout {
let mut is_std = STRING_IS_STANDARD_LAYOUT.load(Ordering::Relaxed);
if is_std != 255 {
return unsafe { std::mem::transmute::<u8, VecOrStringLayout>(is_std) };
}
if std::mem::size_of::<String>() != 24 || std::mem::size_of::<RawVecInspector>() != 24 {
is_std = VecOrStringLayout::Unknown as u8;
} else {
let test_string = String::with_capacity(7);
let insp: RawVecInspector = unsafe { std::mem::transmute_copy(&test_string) };
let ptr = test_string.as_ptr();
is_std = insp.get_layout(ptr) as u8;
drop(test_string);
}
STRING_IS_STANDARD_LAYOUT.store(is_std, Ordering::Relaxed);
return unsafe { std::mem::transmute::<u8, VecOrStringLayout>(is_std) };
}
impl<T: WithSchema + 'static> WithSchema for Vec<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(context.possible_recursion::<T>(|context| T::schema(version, context))),
calculate_vec_memory_layout::<T>(),
)
}
}
impl<T: Introspect> Introspect for Vec<T> {
fn introspect_value(&self) -> String {
return "vec[]".to_string();
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index >= self.len() {
return None;
}
return Some(introspect_item(index.to_string(), &self[index]));
}
fn introspect_len(&self) -> usize {
self.len()
}
}
impl<T: Serialize + Packed + 'static> Serialize for Vec<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
unsafe {
if T::repr_c_optimization_safe(serializer.file_version).is_false() {
regular_serialize_vec(self, serializer)
} else {
let l = self.len();
serializer.write_usize(l)?;
serializer.write_buf(std::slice::from_raw_parts(
self.as_ptr() as *const u8,
std::mem::size_of::<T>() * l,
))
}
}
}
}
fn regular_deserialize_vec<T: Deserialize>(
deserializer: &mut Deserializer<impl Read>,
) -> Result<Vec<T>, SavefileError> {
let l = deserializer.read_usize()?;
#[cfg(feature = "size_sanity_checks")]
{
if l > 1_000_000 {
return Err(SavefileError::GeneralError {
msg: format!("Too many items in Vec: {}", l),
});
}
}
let mut ret = Vec::with_capacity(l);
for _ in 0..l {
ret.push(T::deserialize(deserializer)?);
}
Ok(ret)
}
impl<T: Deserialize + Packed + 'static> Deserialize for Vec<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
if unsafe { T::repr_c_optimization_safe(deserializer.file_version) }.is_false() {
Ok(regular_deserialize_vec(deserializer)?)
} else {
use std::mem;
let align = mem::align_of::<T>();
let elem_size = mem::size_of::<T>();
let num_elems = deserializer.read_usize()?;
if num_elems == 0 {
return Ok(Vec::new());
}
let num_bytes = elem_size * num_elems;
let layout = if let Ok(layout) = std::alloc::Layout::from_size_align(num_bytes, align) {
Ok(layout)
} else {
Err(SavefileError::MemoryAllocationLayoutError)
}?;
let ptr = if elem_size == 0 {
NonNull::dangling().as_ptr()
} else {
let ptr = unsafe { std::alloc::alloc(layout) };
if ptr.is_null() {
panic!("Failed to allocate {} bytes of memory", num_bytes);
}
ptr
};
{
let slice = unsafe { std::slice::from_raw_parts_mut(ptr, num_bytes) };
match deserializer.reader.read_exact(slice) {
Ok(()) => Ok(()),
Err(err) => {
unsafe {
std::alloc::dealloc(ptr, layout);
}
Err(err)
}
}?;
}
let ret = unsafe { Vec::from_raw_parts(ptr as *mut T, num_elems, num_elems) };
Ok(ret)
}
}
}
impl<T: Introspect> Introspect for VecDeque<T> {
fn introspect_value(&self) -> String {
format!("VecDeque<{}>", std::any::type_name::<T>())
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if let Some(val) = self.get(index) {
Some(introspect_item(index.to_string(), val))
} else {
None
}
}
fn introspect_len(&self) -> usize {
self.len()
}
}
impl<T: WithSchema + 'static> WithSchema for VecDeque<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(
Box::new(context.possible_recursion::<T>(|context| T::schema(version, context))),
VecOrStringLayout::Unknown,
)
}
}
impl<T> Packed for VecDeque<T> {}
impl<T: Serialize + 'static> Serialize for VecDeque<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
regular_serialize_vecdeque(self, serializer)
}
}
impl<T: Deserialize + 'static> Deserialize for VecDeque<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(regular_deserialize_vecdeque(deserializer)?)
}
}
fn regular_serialize_vecdeque<T: Serialize>(
item: &VecDeque<T>,
serializer: &mut Serializer<impl Write>,
) -> Result<(), SavefileError> {
let l = item.len();
serializer.write_usize(l)?;
for item in item.iter() {
item.serialize(serializer)?
}
Ok(())
}
fn regular_deserialize_vecdeque<T: Deserialize>(
deserializer: &mut Deserializer<impl Read>,
) -> Result<VecDeque<T>, SavefileError> {
let l = deserializer.read_usize()?;
let mut ret = VecDeque::with_capacity(l);
for _ in 0..l {
ret.push_back(T::deserialize(deserializer)?);
}
Ok(ret)
}
impl Packed for bool {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
} impl Packed for u8 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for i8 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for u16 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for i16 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for u32 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for i32 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for u64 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for u128 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for i128 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for i64 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for char {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for f32 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for f64 {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl Packed for usize {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::no()
} }
impl Packed for isize {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::no()
} }
impl Packed for () {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
IsPacked::yes()
}
}
impl<T: WithSchema + 'static, const N: usize> WithSchema for [T; N] {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Array(SchemaArray {
item_type: Box::new(context.possible_recursion::<T>(|context| T::schema(version, context))),
count: N,
})
}
}
impl<T: Introspect, const N: usize> Introspect for [T; N] {
fn introspect_value(&self) -> String {
format!("[{}; {}]", std::any::type_name::<T>(), N)
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index >= self.len() {
None
} else {
Some(introspect_item(index.to_string(), &self[index]))
}
}
}
impl<T: Packed, const N: usize> Packed for [T; N] {
unsafe fn repr_c_optimization_safe(version: u32) -> IsPacked {
T::repr_c_optimization_safe(version)
}
}
impl<T: Serialize + Packed + 'static, const N: usize> Serialize for [T; N] {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
if N == 0 {
return Ok(());
}
unsafe {
if T::repr_c_optimization_safe(serializer.file_version).is_false() {
for item in self.iter() {
item.serialize(serializer)?
}
Ok(())
} else {
serializer.write_buf(std::slice::from_raw_parts(
self.as_ptr() as *const u8,
std::mem::size_of::<T>() * N,
))
}
}
}
}
impl<T: Deserialize + Packed + 'static, const N: usize> Deserialize for [T; N] {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
if N == 0 {
return Ok([(); N].map(|_| unreachable!()));
}
if unsafe { T::repr_c_optimization_safe(deserializer.file_version) }.is_false() {
let mut data: [MaybeUninit<T>; N] = unsafe {
MaybeUninit::uninit().assume_init() };
for idx in 0..N {
data[idx] = MaybeUninit::new(T::deserialize(deserializer)?); }
let ptr = &mut data as *mut _ as *mut [T; N];
let res = unsafe { ptr.read() };
Ok(res)
} else {
let mut data: [MaybeUninit<T>; N] = unsafe {
MaybeUninit::uninit().assume_init() };
{
let ptr = data.as_mut_ptr();
let num_bytes: usize = std::mem::size_of::<T>() * N;
let slice: &mut [MaybeUninit<u8>] =
unsafe { std::slice::from_raw_parts_mut(ptr as *mut MaybeUninit<u8>, num_bytes) };
deserializer
.reader
.read_exact(unsafe { std::mem::transmute::<&mut [MaybeUninit<u8>], &mut [u8]>(slice) })?;
}
let ptr = &mut data as *mut _ as *mut [T; N];
let res = unsafe { ptr.read() };
Ok(res)
}
}
}
impl<T1> Packed for Range<T1> {}
impl<T1: WithSchema> WithSchema for Range<T1> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::new_tuple2::<T1, T1>(version, context)
}
}
impl<T1: Serialize> Serialize for Range<T1> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.start.serialize(serializer)?;
self.end.serialize(serializer)?;
Ok(())
}
}
impl<T1: Deserialize> Deserialize for Range<T1> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(T1::deserialize(deserializer)?..T1::deserialize(deserializer)?)
}
}
impl<T1: Introspect> Introspect for Range<T1> {
fn introspect_value(&self) -> String {
return "Range".to_string();
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index == 0 {
return Some(introspect_item("start".to_string(), &self.start));
}
if index == 1 {
return Some(introspect_item("end".to_string(), &self.end));
}
return None;
}
}
impl<T1: Packed> Packed for (T1,) {
unsafe fn repr_c_optimization_safe(version: u32) -> IsPacked {
if offset_of_tuple!((T1,), 0) == 0 && std::mem::size_of::<T1>() == std::mem::size_of::<(T1,)>() {
T1::repr_c_optimization_safe(version)
} else {
IsPacked::no()
}
}
}
impl<T1: Packed, T2: Packed> Packed for (T1, T2) {
unsafe fn repr_c_optimization_safe(version: u32) -> IsPacked {
if offset_of_tuple!((T1, T2), 0) == 0
&& std::mem::size_of::<T1>() + std::mem::size_of::<T2>() == std::mem::size_of::<(T1, T2)>()
{
T1::repr_c_optimization_safe(version) & T2::repr_c_optimization_safe(version)
} else {
IsPacked::no()
}
}
}
impl<T1: Packed, T2: Packed, T3: Packed> Packed for (T1, T2, T3) {
unsafe fn repr_c_optimization_safe(version: u32) -> IsPacked {
if offset_of_tuple!((T1, T2, T3), 0) == 0
&& offset_of_tuple!((T1, T2, T3), 1) == std::mem::size_of::<T1>()
&& std::mem::size_of::<T1>() + std::mem::size_of::<T2>() + std::mem::size_of::<T3>()
== std::mem::size_of::<(T1, T2, T3)>()
{
T1::repr_c_optimization_safe(version)
& T2::repr_c_optimization_safe(version)
& T3::repr_c_optimization_safe(version)
} else {
IsPacked::no()
}
}
}
impl<T1: Packed, T2: Packed, T3: Packed, T4: Packed> Packed for (T1, T2, T3, T4) {
unsafe fn repr_c_optimization_safe(version: u32) -> IsPacked {
if offset_of_tuple!((T1, T2, T3, T4), 0) == 0
&& offset_of_tuple!((T1, T2, T3, T4), 1) == std::mem::size_of::<T1>()
&& offset_of_tuple!((T1, T2, T3, T4), 2) == std::mem::size_of::<T1>() + std::mem::size_of::<T2>()
&& std::mem::size_of::<T1>()
+ std::mem::size_of::<T2>()
+ std::mem::size_of::<T3>()
+ std::mem::size_of::<T4>()
== std::mem::size_of::<(T1, T2, T3, T4)>()
{
T1::repr_c_optimization_safe(version)
& T2::repr_c_optimization_safe(version)
& T3::repr_c_optimization_safe(version)
& T4::repr_c_optimization_safe(version)
} else {
IsPacked::no()
}
}
}
impl<T1: WithSchema, T2: WithSchema, T3: WithSchema> WithSchema for (T1, T2, T3) {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::new_tuple3::<T1, T2, T3>(version, context)
}
}
impl<T1: Serialize, T2: Serialize, T3: Serialize> Serialize for (T1, T2, T3) {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.0.serialize(serializer)?;
self.1.serialize(serializer)?;
self.2.serialize(serializer)
}
}
impl<T1: Deserialize, T2: Deserialize, T3: Deserialize> Deserialize for (T1, T2, T3) {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok((
T1::deserialize(deserializer)?,
T2::deserialize(deserializer)?,
T3::deserialize(deserializer)?,
))
}
}
impl<T1: WithSchema, T2: WithSchema> WithSchema for (T1, T2) {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::new_tuple2::<T1, T2>(version, context)
}
}
impl<T1: Serialize, T2: Serialize> Serialize for (T1, T2) {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.0.serialize(serializer)?;
self.1.serialize(serializer)
}
}
impl<T1: Deserialize, T2: Deserialize> Deserialize for (T1, T2) {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok((T1::deserialize(deserializer)?, T2::deserialize(deserializer)?))
}
}
impl<T1: WithSchema> WithSchema for (T1,) {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::new_tuple1::<T1>(version, context)
}
}
impl<T1: Serialize> Serialize for (T1,) {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.0.serialize(serializer)
}
}
impl<T1: Deserialize> Deserialize for (T1,) {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok((T1::deserialize(deserializer)?,))
}
}
#[cfg(feature = "nalgebra")]
impl<T: nalgebra::Scalar> Introspect for nalgebra::Point3<T> {
fn introspect_value(&self) -> String {
format!("{:?}", self)
}
fn introspect_child<'a>(&'a self, _index: usize) -> Option<Box<dyn IntrospectItem<'a> + 'a>> {
None
}
}
#[cfg(feature = "nalgebra")]
impl<T: nalgebra::Scalar> Introspect for nalgebra::Vector3<T> {
fn introspect_value(&self) -> String {
format!("{:?}", self)
}
fn introspect_child<'a>(&'a self, _index: usize) -> Option<Box<dyn IntrospectItem<'a> + 'a>> {
None
}
}
#[cfg(feature = "nalgebra")]
impl<T: nalgebra::Scalar> Introspect for nalgebra::Isometry3<T> {
fn introspect_value(&self) -> String {
format!("{:?}", self)
}
fn introspect_child<'a>(&'a self, _index: usize) -> Option<Box<dyn IntrospectItem<'a> + 'a>> {
None
}
}
#[cfg(feature = "nalgebra")]
impl<T: Packed + nalgebra::Scalar + Default> Packed for nalgebra::Point3<T> {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
let d = nalgebra::Point3::<T>::new(T::default(), T::default(), T::default());
let p1 = &d.x as *const T;
let p2 = &d.y as *const T;
let p3 = &d.z as *const T;
if std::mem::size_of::<nalgebra::Point3<T>>() == 3 * std::mem::size_of::<T>()
&& p1.offset(1) == p2
&& p1.offset(2) == p3
{
IsPacked::yes()
} else {
IsPacked::no()
}
}
}
#[cfg(feature = "nalgebra")]
impl<T: WithSchema + nalgebra::Scalar> WithSchema for nalgebra::Point3<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Array(SchemaArray {
item_type: Box::new(T::schema(version, context)),
count: 3,
})
}
}
#[cfg(feature = "nalgebra")]
impl<T: Serialize + Packed + WithSchema + nalgebra::Scalar> Serialize for nalgebra::Point3<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.coords.x.serialize(serializer)?;
self.coords.y.serialize(serializer)?;
self.coords.z.serialize(serializer)?;
Ok(())
}
}
#[cfg(feature = "nalgebra")]
impl<T: Deserialize + Packed + WithSchema + nalgebra::Scalar + nalgebra::SimdValue + nalgebra::RealField> Deserialize
for nalgebra::Point3<T>
{
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(nalgebra::Point3::new(
<T as Deserialize>::deserialize(deserializer)?,
<T as Deserialize>::deserialize(deserializer)?,
<T as Deserialize>::deserialize(deserializer)?,
))
}
}
#[cfg(feature = "nalgebra")]
impl<T: Packed + nalgebra::Scalar + Default> Packed for nalgebra::Vector3<T> {
unsafe fn repr_c_optimization_safe(_version: u32) -> IsPacked {
let d = nalgebra::Vector3::<T>::new(T::default(), T::default(), T::default());
let p1 = &d.x as *const T;
let p2 = &d.y as *const T;
let p3 = &d.z as *const T;
if std::mem::size_of::<nalgebra::Point3<T>>() == 3 * std::mem::size_of::<T>()
&& p1.offset(1) == p2
&& p1.offset(2) == p3
{
IsPacked::yes()
} else {
IsPacked::no()
}
}
}
#[cfg(feature = "nalgebra")]
impl<T: WithSchema + nalgebra::Scalar> WithSchema for nalgebra::Vector3<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Array(SchemaArray {
item_type: Box::new(T::schema(version, context)),
count: 3,
})
}
}
#[cfg(feature = "nalgebra")]
impl<T: Serialize + Packed + WithSchema + nalgebra::Scalar> Serialize for nalgebra::Vector3<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.x.serialize(serializer)?;
self.y.serialize(serializer)?;
self.z.serialize(serializer)?;
Ok(())
}
}
#[cfg(feature = "nalgebra")]
impl<T: Deserialize + Packed + WithSchema + nalgebra::Scalar + nalgebra::SimdValue + nalgebra::RealField> Deserialize
for nalgebra::Vector3<T>
{
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(nalgebra::Vector3::new(
<T as Deserialize>::deserialize(deserializer)?,
<T as Deserialize>::deserialize(deserializer)?,
<T as Deserialize>::deserialize(deserializer)?,
))
}
}
#[cfg(feature = "nalgebra")]
impl<T: Packed> Packed for nalgebra::Isometry3<T> {}
#[cfg(feature = "nalgebra")]
impl<T: WithSchema> WithSchema for nalgebra::Isometry3<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Array(SchemaArray {
item_type: Box::new(T::schema(version, context)),
count: 7,
})
}
}
#[cfg(feature = "nalgebra")]
impl<T: Serialize + Packed + WithSchema + nalgebra::Scalar> Serialize for nalgebra::Isometry3<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.translation.vector.x.serialize(serializer)?;
self.translation.vector.y.serialize(serializer)?;
self.translation.vector.z.serialize(serializer)?;
self.rotation.coords.w.serialize(serializer)?;
self.rotation.coords.x.serialize(serializer)?;
self.rotation.coords.y.serialize(serializer)?;
self.rotation.coords.z.serialize(serializer)?;
Ok(())
}
}
#[cfg(feature = "nalgebra")]
impl<T: Deserialize + Packed + WithSchema + nalgebra::Scalar + nalgebra::SimdValue + nalgebra::RealField> Deserialize
for nalgebra::Isometry3<T>
{
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(nalgebra::Isometry3::from_parts(
nalgebra::Point3::new(
<T as Deserialize>::deserialize(deserializer)?,
<T as Deserialize>::deserialize(deserializer)?,
<T as Deserialize>::deserialize(deserializer)?,
)
.into(),
nalgebra::UnitQuaternion::new_unchecked(nalgebra::Quaternion::new(
<T as Deserialize>::deserialize(deserializer)?,
<T as Deserialize>::deserialize(deserializer)?,
<T as Deserialize>::deserialize(deserializer)?,
<T as Deserialize>::deserialize(deserializer)?,
)),
))
}
}
#[cfg(feature = "arrayvec")]
impl<const C: usize> Packed for arrayvec::ArrayString<C> {}
#[cfg(feature = "arrayvec")]
impl<const C: usize> WithSchema for arrayvec::ArrayString<C> {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_string(VecOrStringLayout::Unknown))
}
}
#[cfg(feature = "arrayvec")]
impl<const C: usize> Serialize for arrayvec::ArrayString<C> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_string(self.as_str())
}
}
#[cfg(feature = "arrayvec")]
impl<const C: usize> Deserialize for arrayvec::ArrayString<C> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let l = deserializer.read_usize()?;
if l > C {
return Err(SavefileError::ArrayvecCapacityError {
msg: format!("Deserialized data had length {}, but ArrayString capacity is {}", l, C),
});
}
let mut tempbuf = [0u8; C];
deserializer.read_bytes_to_buf(&mut tempbuf[0..l])?;
match std::str::from_utf8(&tempbuf[0..l]) {
Ok(s) => Ok(arrayvec::ArrayString::try_from(s)?),
Err(_err) => Err(SavefileError::InvalidUtf8 {
msg: format!("ArrayString<{}> contained invalid UTF8", C),
}),
}
}
}
#[cfg(feature = "arrayvec")]
impl<const C: usize> Introspect for arrayvec::ArrayString<C> {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem>> {
None
}
}
#[cfg(feature = "arrayvec")]
impl<V: WithSchema, const C: usize> WithSchema for arrayvec::ArrayVec<V, C> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
Schema::Vector(Box::new(V::schema(version, context)), VecOrStringLayout::Unknown)
}
}
#[cfg(feature = "arrayvec")]
impl<V: Introspect + 'static, const C: usize> Introspect for arrayvec::ArrayVec<V, C> {
fn introspect_value(&self) -> String {
return "arrayvec[]".to_string();
}
fn introspect_child<'s>(&'s self, index: usize) -> Option<Box<dyn IntrospectItem<'s> + 's>> {
if index >= self.len() {
return None;
}
return Some(Box::new(IntrospectItemSimple {
key: index.to_string(),
val: &self[index],
}));
}
fn introspect_len(&self) -> usize {
self.len()
}
}
#[cfg(feature = "arrayvec")]
impl<V: Packed, const C: usize> Packed for arrayvec::ArrayVec<V, C> {
unsafe fn repr_c_optimization_safe(version: u32) -> IsPacked {
V::repr_c_optimization_safe(version)
}
}
#[cfg(feature = "arrayvec")]
impl<V: Serialize + Packed, const C: usize> Serialize for arrayvec::ArrayVec<V, C> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
unsafe {
if V::repr_c_optimization_safe(serializer.file_version).is_false() {
regular_serialize_vec(self, serializer)
} else {
let l = self.len();
serializer.write_usize(l)?;
serializer.write_buf(std::slice::from_raw_parts(
self.as_ptr() as *const u8,
std::mem::size_of::<V>() * l,
))
}
}
}
}
#[cfg(feature = "arrayvec")]
impl<V: Deserialize + Packed, const C: usize> Deserialize for arrayvec::ArrayVec<V, C> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<arrayvec::ArrayVec<V, C>, SavefileError> {
let mut ret = arrayvec::ArrayVec::new();
let l = deserializer.read_usize()?;
if l > ret.capacity() {
return Err(SavefileError::ArrayvecCapacityError {
msg: format!("ArrayVec with capacity {} can't hold {} items", ret.capacity(), l),
});
}
if unsafe { V::repr_c_optimization_safe(deserializer.file_version) }.is_false() {
for _ in 0..l {
ret.push(V::deserialize(deserializer)?);
}
} else {
unsafe {
let bytebuf = std::slice::from_raw_parts_mut(ret.as_mut_ptr() as *mut u8, std::mem::size_of::<V>() * l);
deserializer.reader.read_exact(bytebuf)?; ret.set_len(l);
}
}
Ok(ret)
}
}
use std::ops::{Deref, Range};
impl<T: WithSchema + 'static> WithSchema for Box<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
context.possible_recursion::<T>(|context| T::schema(version, context))
}
}
impl<T> Packed for Box<T> {}
impl<T: Serialize + 'static> Serialize for Box<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.deref().serialize(serializer)
}
}
impl<T: Deserialize + 'static> Deserialize for Box<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(Box::new(T::deserialize(deserializer)?))
}
}
use std::rc::Rc;
impl<T> Packed for Rc<T> {}
impl<T: WithSchema + 'static> WithSchema for Rc<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
context.possible_recursion::<T>(|context| T::schema(version, context))
}
}
impl<T: Serialize + 'static> Serialize for Rc<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.deref().serialize(serializer)
}
}
impl<T: Deserialize + 'static> Deserialize for Rc<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(Rc::new(T::deserialize(deserializer)?))
}
}
impl<T> Packed for Arc<T> {}
impl<T: WithSchema + 'static> WithSchema for Arc<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
context.possible_recursion::<T>(|context| T::schema(version, context))
}
}
impl<T: Serialize + 'static> Serialize for Arc<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.deref().serialize(serializer)
}
}
impl<T: Deserialize + 'static> Deserialize for Arc<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(Arc::new(T::deserialize(deserializer)?))
}
}
use byteorder::{ReadBytesExt, WriteBytesExt};
#[cfg(feature = "bzip2")]
use bzip2::Compression;
use memoffset::offset_of_tuple;
use std::any::Any;
use std::cell::Cell;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
#[allow(unused_imports)]
use std::convert::{TryFrom, TryInto};
use std::fmt::{Debug, Display, Formatter};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::ptr::NonNull;
use std::slice;
use std::sync::Arc;
use std::time::{Duration, SystemTime};
impl<T> Packed for RefCell<T> {}
impl<T: WithSchema> WithSchema for RefCell<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
T::schema(version, context)
}
}
impl<T: Serialize> Serialize for RefCell<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
self.borrow().serialize(serializer)
}
}
impl<T: Deserialize> Deserialize for RefCell<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(RefCell::new(T::deserialize(deserializer)?))
}
}
impl<T: Packed> Packed for Cell<T> {
unsafe fn repr_c_optimization_safe(version: u32) -> IsPacked {
T::repr_c_optimization_safe(version)
}
}
impl<T: WithSchema> WithSchema for Cell<T> {
fn schema(version: u32, context: &mut WithSchemaContext) -> Schema {
T::schema(version, context)
}
}
impl<T: Serialize + Copy> Serialize for Cell<T> {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
let t: T = self.get();
t.serialize(serializer)
}
}
impl<T: Deserialize> Deserialize for Cell<T> {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(Cell::new(T::deserialize(deserializer)?))
}
}
impl WithSchema for () {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::ZeroSize
}
}
impl Serialize for () {
fn serialize(&self, _serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
Ok(())
}
}
impl Introspect for () {
fn introspect_value(&self) -> String {
"()".to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Deserialize for () {
fn deserialize(_deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(())
}
}
impl<T: Introspect> Introspect for (T,) {
fn introspect_value(&self) -> String {
return "1-tuple".to_string();
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index == 0 {
return Some(introspect_item("0".to_string(), &self.0));
}
return None;
}
}
impl<T1: Introspect, T2: Introspect> Introspect for (T1, T2) {
fn introspect_value(&self) -> String {
return "2-tuple".to_string();
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index == 0 {
return Some(introspect_item("0".to_string(), &self.0));
}
if index == 1 {
return Some(introspect_item("1".to_string(), &self.1));
}
return None;
}
}
impl<T1: Introspect, T2: Introspect, T3: Introspect> Introspect for (T1, T2, T3) {
fn introspect_value(&self) -> String {
return "3-tuple".to_string();
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index == 0 {
return Some(introspect_item("0".to_string(), &self.0));
}
if index == 1 {
return Some(introspect_item("1".to_string(), &self.1));
}
if index == 2 {
return Some(introspect_item("2".to_string(), &self.2));
}
return None;
}
}
impl<T1: Introspect, T2: Introspect, T3: Introspect, T4: Introspect> Introspect for (T1, T2, T3, T4) {
fn introspect_value(&self) -> String {
return "4-tuple".to_string();
}
fn introspect_child(&self, index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
if index == 0 {
return Some(introspect_item("0".to_string(), &self.0));
}
if index == 1 {
return Some(introspect_item("1".to_string(), &self.1));
}
if index == 2 {
return Some(introspect_item("2".to_string(), &self.2));
}
if index == 3 {
return Some(introspect_item("3".to_string(), &self.3));
}
return None;
}
}
impl Introspect for AtomicBool {
fn introspect_value(&self) -> String {
self.load(Ordering::SeqCst).to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for AtomicU8 {
fn introspect_value(&self) -> String {
self.load(Ordering::SeqCst).to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for AtomicI8 {
fn introspect_value(&self) -> String {
self.load(Ordering::SeqCst).to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for AtomicU16 {
fn introspect_value(&self) -> String {
self.load(Ordering::SeqCst).to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for AtomicI16 {
fn introspect_value(&self) -> String {
self.load(Ordering::SeqCst).to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for AtomicU32 {
fn introspect_value(&self) -> String {
self.load(Ordering::SeqCst).to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for AtomicI32 {
fn introspect_value(&self) -> String {
self.load(Ordering::SeqCst).to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for AtomicU64 {
fn introspect_value(&self) -> String {
self.load(Ordering::SeqCst).to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for AtomicI64 {
fn introspect_value(&self) -> String {
self.load(Ordering::SeqCst).to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for AtomicUsize {
fn introspect_value(&self) -> String {
self.load(Ordering::SeqCst).to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for AtomicIsize {
fn introspect_value(&self) -> String {
self.load(Ordering::SeqCst).to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl WithSchema for AtomicBool {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_bool)
}
}
impl WithSchema for AtomicU8 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_u8)
}
}
impl WithSchema for AtomicI8 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_i8)
}
}
impl WithSchema for AtomicU16 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_u16)
}
}
impl WithSchema for AtomicI16 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_i16)
}
}
impl WithSchema for AtomicU32 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_u32)
}
}
impl WithSchema for AtomicI32 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_i32)
}
}
impl WithSchema for AtomicU64 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_u64)
}
}
impl WithSchema for AtomicI64 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_i64)
}
}
impl WithSchema for AtomicUsize {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
match std::mem::size_of::<usize>() {
4 => Schema::Primitive(SchemaPrimitive::schema_u32),
8 => Schema::Primitive(SchemaPrimitive::schema_u64),
_ => panic!("Size of usize was neither 32 bit nor 64 bit. This is not supported by the savefile crate."),
}
}
}
impl WithSchema for AtomicIsize {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
match std::mem::size_of::<isize>() {
4 => Schema::Primitive(SchemaPrimitive::schema_i32),
8 => Schema::Primitive(SchemaPrimitive::schema_i64),
_ => panic!("Size of isize was neither 32 bit nor 64 bit. This is not supported by the savefile crate."),
}
}
}
impl WithSchema for bool {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_bool)
}
}
impl WithSchema for u8 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_u8)
}
}
impl WithSchema for i8 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_i8)
}
}
impl WithSchema for u16 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_u16)
}
}
impl WithSchema for i16 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_i16)
}
}
impl WithSchema for u32 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_u32)
}
}
impl WithSchema for i32 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_i32)
}
}
impl WithSchema for u64 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_u64)
}
}
impl WithSchema for u128 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_u128)
}
}
impl WithSchema for i128 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_i128)
}
}
impl WithSchema for i64 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_i64)
}
}
impl WithSchema for char {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_char)
}
}
impl WithSchema for usize {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
match std::mem::size_of::<usize>() {
4 => Schema::Primitive(SchemaPrimitive::schema_u32),
8 => Schema::Primitive(SchemaPrimitive::schema_u64),
_ => panic!("Size of usize was neither 32 bit nor 64 bit. This is not supported by the savefile crate."),
}
}
}
impl WithSchema for isize {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
match std::mem::size_of::<isize>() {
4 => Schema::Primitive(SchemaPrimitive::schema_i32),
8 => Schema::Primitive(SchemaPrimitive::schema_i64),
_ => panic!("Size of isize was neither 32 bit nor 64 bit. This is not supported by the savefile crate."),
}
}
}
impl WithSchema for f32 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_f32)
}
}
impl WithSchema for f64 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_f64)
}
}
impl Introspect for bool {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for u8 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for u16 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for u32 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for u64 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for u128 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for i8 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for i16 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for i32 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for i64 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for char {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for i128 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for f32 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for f64 {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for usize {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Introspect for isize {
fn introspect_value(&self) -> String {
self.to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Serialize for u8 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u8(*self)
}
}
impl Deserialize for u8 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_u8()
}
}
impl Serialize for bool {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_bool(*self)
}
}
impl Deserialize for bool {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_bool()
}
}
impl Serialize for f32 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_f32(*self)
}
}
impl Deserialize for f32 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_f32()
}
}
impl Serialize for f64 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_f64(*self)
}
}
impl Deserialize for f64 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_f64()
}
}
impl Serialize for i8 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_i8(*self)
}
}
impl Deserialize for i8 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_i8()
}
}
impl Serialize for u16 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u16(*self)
}
}
impl Deserialize for u16 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_u16()
}
}
impl Serialize for i16 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_i16(*self)
}
}
impl Deserialize for i16 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_i16()
}
}
impl Serialize for u32 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u32(*self)
}
}
impl Deserialize for u32 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_u32()
}
}
impl Serialize for i32 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_i32(*self)
}
}
impl Deserialize for i32 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_i32()
}
}
impl Serialize for u64 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u64(*self)
}
}
impl Deserialize for u64 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_u64()
}
}
impl Serialize for i64 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_i64(*self)
}
}
impl Serialize for char {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u32((*self).into())
}
}
impl Deserialize for i64 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_i64()
}
}
impl Deserialize for char {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let uc = deserializer.read_u32()?;
match uc.try_into() {
Ok(x) => Ok(x),
Err(_) => Err(SavefileError::InvalidChar),
}
}
}
impl Serialize for u128 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u128(*self)
}
}
impl Deserialize for u128 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_u128()
}
}
impl Serialize for i128 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_i128(*self)
}
}
impl Deserialize for i128 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_i128()
}
}
impl Serialize for usize {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_usize(*self)
}
}
impl Deserialize for usize {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_usize()
}
}
impl Serialize for isize {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_isize(*self)
}
}
impl Deserialize for isize {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
deserializer.read_isize()
}
}
impl Serialize for AtomicBool {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_bool(self.load(Ordering::SeqCst))
}
}
impl Deserialize for AtomicBool {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AtomicBool::new(deserializer.read_bool()?))
}
}
impl Serialize for AtomicU8 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u8(self.load(Ordering::SeqCst))
}
}
impl Deserialize for AtomicU8 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AtomicU8::new(deserializer.read_u8()?))
}
}
impl Serialize for AtomicI8 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_i8(self.load(Ordering::SeqCst))
}
}
impl Deserialize for AtomicI8 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AtomicI8::new(deserializer.read_i8()?))
}
}
impl Serialize for AtomicU16 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u16(self.load(Ordering::SeqCst))
}
}
impl Deserialize for AtomicU16 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AtomicU16::new(deserializer.read_u16()?))
}
}
impl Serialize for AtomicI16 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_i16(self.load(Ordering::SeqCst))
}
}
impl Deserialize for AtomicI16 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AtomicI16::new(deserializer.read_i16()?))
}
}
impl Serialize for AtomicU32 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u32(self.load(Ordering::SeqCst))
}
}
impl Deserialize for AtomicU32 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AtomicU32::new(deserializer.read_u32()?))
}
}
impl Serialize for AtomicI32 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_i32(self.load(Ordering::SeqCst))
}
}
impl Deserialize for AtomicI32 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AtomicI32::new(deserializer.read_i32()?))
}
}
impl Serialize for AtomicU64 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u64(self.load(Ordering::SeqCst))
}
}
impl Deserialize for AtomicU64 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AtomicU64::new(deserializer.read_u64()?))
}
}
impl Serialize for AtomicI64 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_i64(self.load(Ordering::SeqCst))
}
}
impl Deserialize for AtomicI64 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AtomicI64::new(deserializer.read_i64()?))
}
}
impl Serialize for AtomicUsize {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_usize(self.load(Ordering::SeqCst))
}
}
impl Deserialize for AtomicUsize {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AtomicUsize::new(deserializer.read_usize()?))
}
}
impl Serialize for AtomicIsize {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_isize(self.load(Ordering::SeqCst))
}
}
impl Deserialize for AtomicIsize {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
Ok(AtomicIsize::new(deserializer.read_isize()?))
}
}
impl Packed for AtomicBool {}
impl Packed for AtomicI8 {}
impl Packed for AtomicU8 {}
impl Packed for AtomicI16 {}
impl Packed for AtomicU16 {}
impl Packed for AtomicI32 {}
impl Packed for AtomicU32 {}
impl Packed for AtomicI64 {}
impl Packed for AtomicU64 {}
impl Packed for AtomicIsize {}
impl Packed for AtomicUsize {}
#[derive(Clone, Copy, Eq, PartialEq, Default, Debug)]
pub struct Canary1 {}
impl Canary1 {
pub fn new() -> Canary1 {
Canary1 {}
}
}
impl Introspect for Canary1 {
fn introspect_value(&self) -> String {
"Canary1".to_string()
}
fn introspect_child(&self, _index: usize) -> Option<Box<dyn IntrospectItem + '_>> {
None
}
}
impl Deserialize for Canary1 {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let magic = deserializer.read_u32()?;
if magic != 0x47566843 {
return Err(SavefileError::GeneralError {
msg: format!(
"Encountered bad magic value when deserializing Canary1. Expected {} but got {}",
0x47566843, magic
),
});
}
Ok(Canary1 {})
}
}
impl Serialize for Canary1 {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u32(0x47566843)
}
}
impl Packed for Canary1 {}
impl WithSchema for Canary1 {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Primitive(SchemaPrimitive::schema_canary1)
}
}
impl WithSchema for Duration {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Struct(SchemaStruct {
dbg_name: "Duration".to_string(),
size: None,
alignment: None,
fields: vec![Field {
name: "Duration".to_string(),
value: Box::new(Schema::Primitive(SchemaPrimitive::schema_u128)),
offset: None,
}],
})
}
}
impl Packed for Duration {}
impl Serialize for Duration {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
serializer.write_u128(self.as_nanos())
}
}
impl Deserialize for Duration {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let temp = deserializer.read_u128()?;
Ok(Duration::from_secs((temp / 1_000_000_000) as u64) + Duration::from_nanos((temp % 1_000_000_000) as u64))
}
}
impl Introspect for Duration {
fn introspect_value(&self) -> String {
format!("{:?}", self)
}
fn introspect_child<'a>(&'a self, _index: usize) -> Option<Box<dyn IntrospectItem<'a> + 'a>> {
None
}
fn introspect_len(&self) -> usize {
0
}
}
impl Introspect for SystemTime {
fn introspect_value(&self) -> String {
format!("{:?}", self)
}
fn introspect_child<'a>(&'a self, _index: usize) -> Option<Box<dyn IntrospectItem<'a> + 'a>> {
None
}
fn introspect_len(&self) -> usize {
0
}
}
impl WithSchema for SystemTime {
fn schema(_version: u32, _context: &mut WithSchemaContext) -> Schema {
Schema::Struct(SchemaStruct {
dbg_name: "SystemTime".to_string(),
size: None,
alignment: None,
fields: vec![Field {
name: "SystemTimeDuration".to_string(),
value: Box::new(Schema::Primitive(SchemaPrimitive::schema_u128)),
offset: None,
}],
})
}
}
impl Packed for SystemTime {}
impl Serialize for SystemTime {
fn serialize(&self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
match self.duration_since(SystemTime::UNIX_EPOCH) {
Ok(nanos) => {
let temp = nanos.as_nanos();
if temp >= 1u128 << 120 {
return Err(SavefileError::GeneralError {
msg: "Savefile cannot handle dates where the year is larger than ca 10^19 years.".to_string(),
});
}
serializer.write_u128(temp)?;
}
Err(err) => {
let mut temp = err.duration().as_nanos();
if temp >= 1u128 << 120 {
return Err(SavefileError::GeneralError {
msg: "Savefile cannot handle dates much earlier than the creation of the universe.".to_string(),
});
}
temp |= 1u128 << 127;
serializer.write_u128(temp)?;
}
}
Ok(())
}
}
impl Introspect for std::time::Instant {
fn introspect_value(&self) -> String {
format!("{:?}", self)
}
fn introspect_child<'a>(&'a self, _index: usize) -> Option<Box<dyn IntrospectItem<'a> + 'a>> {
None
}
}
fn u128_duration_nanos(nanos: u128) -> Duration {
if nanos > u64::MAX as u128 {
Duration::from_nanos((nanos % 1_000_000_000) as u64) + Duration::from_secs((nanos / 1_000_000_000) as u64)
} else {
Duration::from_nanos(nanos as u64)
}
}
impl Deserialize for SystemTime {
fn deserialize(deserializer: &mut Deserializer<impl Read>) -> Result<Self, SavefileError> {
let mut temp = deserializer.read_u128()?;
if temp >= (1u128 << 127) {
temp &= (1u128 << 127) - 1; return Ok(SystemTime::UNIX_EPOCH - u128_duration_nanos(temp));
} else {
return Ok(SystemTime::UNIX_EPOCH + u128_duration_nanos(temp));
}
}
}
#[derive(Clone, Debug)]
struct PathElement {
key: String,
key_disambiguator: usize,
max_children: usize,
}
#[derive(Clone, Debug)]
pub struct Introspector {
path: Vec<PathElement>,
child_load_count: usize,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum IntrospectorNavCommand {
ExpandElement(IntrospectedElementKey),
SelectNth {
select_depth: usize,
select_index: usize,
},
Nothing,
Up,
}
#[derive(PartialEq, Eq, Clone)]
pub struct IntrospectedElementKey {
pub depth: usize,
pub key: String,
pub key_disambiguator: usize,
}
impl Default for IntrospectedElementKey {
fn default() -> Self {
IntrospectedElementKey {
depth: 0,
key: "".to_string(),
key_disambiguator: 0,
}
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct IntrospectedElement {
pub key: IntrospectedElementKey,
pub value: String,
pub has_children: bool,
pub selected: bool,
}
impl Debug for IntrospectedElementKey {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"Key({} (at depth {}, key disambig {}))",
self.key, self.depth, self.key_disambiguator
)
}
}
impl Debug for IntrospectedElement {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"KeyVal({} = {} (at depth {}, key disambig {}))",
self.key.key, self.value, self.key.depth, self.key.key_disambiguator
)
}
}
impl Display for IntrospectedElement {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{} = {}", self.key.key, self.value)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum IntrospectionError {
BadDepth,
UnknownKey,
NoChildren,
IndexOutOfRange,
AlreadyAtTop,
}
#[derive(Debug, Clone)]
pub struct IntrospectionFrame {
pub selected: Option<usize>,
pub keyvals: Vec<IntrospectedElement>,
pub limit_reached: bool,
}
#[derive(Debug, Clone)]
pub struct IntrospectionResult {
pub frames: Vec<IntrospectionFrame>,
cached_total_len: usize,
}
impl Display for IntrospectionResult {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
self.format_result_row(f)
}
}
impl IntrospectionResult {
pub fn total_index(&self, index: usize) -> Option<IntrospectedElement> {
let mut cur = 0;
self.total_index_impl(index, 0, &mut cur)
}
fn total_index_impl(&self, index: usize, depth: usize, cur: &mut usize) -> Option<IntrospectedElement> {
if depth >= self.frames.len() {
return None;
}
let frame = &self.frames[depth];
{
let mut offset = 0;
if let Some(selection) = frame.selected {
if index <= *cur + selection {
return Some(frame.keyvals[index - *cur].clone());
}
*cur += selection + 1;
if let Some(result) = self.total_index_impl(index, depth + 1, cur) {
return Some(result);
}
offset = selection + 1;
}
if (index - *cur) + offset < frame.keyvals.len() {
return Some(frame.keyvals[(index - *cur) + offset].clone());
}
*cur += frame.keyvals.len() - offset;
}
return None;
}
pub fn total_len(&self) -> usize {
self.cached_total_len
}
fn format_result_row(self: &IntrospectionResult, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
if self.frames.len() == 0 {
writeln!(f, "Introspectionresult:\n*empty*")?;
return Ok(());
}
let mut idx = 0;
let mut depth = Vec::new();
writeln!(f, "Introspectionresult:")?;
'outer: loop {
let cur_row = &self.frames[depth.len()];
if idx >= cur_row.keyvals.len() {
if let Some(new_idx) = depth.pop() {
idx = new_idx;
continue;
} else {
break;
}
}
while idx < cur_row.keyvals.len() {
let item = &cur_row.keyvals[idx];
let is_selected = Some(idx) == cur_row.selected;
let pad = if is_selected {
"*"
} else if item.has_children {
">"
} else {
" "
};
writeln!(f, "{:>indent$}{}", pad, item, indent = 1 + 2 * depth.len())?;
idx += 1;
if is_selected && depth.len() + 1 < self.frames.len() {
depth.push(idx);
idx = 0;
continue 'outer;
}
}
}
Ok(())
}
}
impl Display for IntrospectedElementKey {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{}", self.key)
}
}
impl Introspector {
pub fn new() -> Introspector {
Introspector {
path: vec![],
child_load_count: usize::MAX,
}
}
pub fn new_with(child_load_count: usize) -> Introspector {
Introspector {
path: vec![],
child_load_count,
}
}
pub fn num_frames(&self) -> usize {
self.path.len()
}
fn dive(
&mut self,
depth: usize,
object: &dyn Introspect,
navigation_command: IntrospectorNavCommand,
) -> Result<Vec<IntrospectionFrame>, IntrospectionError> {
let mut result_vec = Vec::new();
let mut navigation_command = Some(navigation_command);
let mut cur_path = self.path.get(depth).cloned();
let mut index = 0;
let mut row = IntrospectionFrame {
selected: None,
keyvals: vec![],
limit_reached: false,
};
let mut key_disambig_map = HashMap::new();
let mut do_select_nth = None;
let mut err_if_key_not_found = false;
if let Some(navigation_command) = navigation_command.as_ref() {
match navigation_command {
IntrospectorNavCommand::ExpandElement(elem) => {
if elem.depth > self.path.len() {
return Err(IntrospectionError::BadDepth);
}
if depth == elem.depth {
self.path.drain(depth..);
self.path.push(PathElement {
key: elem.key.clone(),
key_disambiguator: elem.key_disambiguator,
max_children: self.child_load_count,
});
cur_path = self.path.get(depth).cloned();
err_if_key_not_found = true;
}
}
IntrospectorNavCommand::SelectNth {
select_depth,
select_index,
} => {
if depth == *select_depth {
do_select_nth = Some(*select_index);
}
}
IntrospectorNavCommand::Nothing => {}
IntrospectorNavCommand::Up => {}
}
}
loop {
if let Some(child_item) = object.introspect_child(index) {
let key: String = child_item.key().into();
let disambig_counter: &mut usize = key_disambig_map.entry(key.clone()).or_insert(0usize);
let has_children = child_item.val().introspect_child(0).is_some();
row.keyvals.push(IntrospectedElement {
key: IntrospectedElementKey {
depth,
key: key.clone(),
key_disambiguator: *disambig_counter,
},
value: child_item.val().introspect_value(),
has_children,
selected: false,
});
if Some(index) == do_select_nth {
self.path.push(PathElement {
key: key.clone(),
key_disambiguator: *disambig_counter,
max_children: self.child_load_count,
});
do_select_nth = None;
cur_path = self.path.last().cloned();
}
if let Some(cur_path_obj) = &cur_path {
if row.selected.is_none()
&& cur_path_obj.key == key
&& cur_path_obj.key_disambiguator == *disambig_counter
{
row.selected = Some(index);
row.keyvals.last_mut().unwrap().selected = true;
if has_children {
let mut subresult =
self.dive(depth + 1, child_item.val(), navigation_command.take().unwrap())?;
debug_assert_eq!(result_vec.len(), 0);
std::mem::swap(&mut result_vec, &mut subresult);
}
}
}
*disambig_counter += 1;
} else {
break;
}
index += 1;
if index
>= cur_path
.as_ref()
.map(|x| x.max_children)
.unwrap_or(self.child_load_count)
{
row.limit_reached = true;
break;
}
}
if do_select_nth.is_some() {
if index == 0 {
return Err(IntrospectionError::NoChildren);
}
return Err(IntrospectionError::IndexOutOfRange);
}
if err_if_key_not_found && row.selected.is_none() {
self.path.pop().unwrap();
return Err(IntrospectionError::UnknownKey);
}
result_vec.insert(0, row);
Ok(result_vec)
}
pub fn do_introspect(
&mut self,
object: &dyn Introspect,
navigation_command: IntrospectorNavCommand,
) -> Result<IntrospectionResult, IntrospectionError> {
match &navigation_command {
IntrospectorNavCommand::ExpandElement(_) => {}
IntrospectorNavCommand::SelectNth { .. } => {}
IntrospectorNavCommand::Nothing => {}
IntrospectorNavCommand::Up => {
if self.path.len() == 0 {
return Err(IntrospectionError::AlreadyAtTop);
}
self.path.pop();
}
}
let frames = self.dive(0, object, navigation_command)?;
let mut total = 0;
for frame in &frames {
total += frame.keyvals.len();
}
let accum = IntrospectionResult {
frames,
cached_total_len: total,
};
Ok(accum)
}
}