1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#![deny(missing_docs, missing_debug_implementations)]
//! Defines a generic interface for version tolerant serialization and
//! implements it for primitive data types using `bincode` as backend.
//!
//! The interface has two components:
//! - `Versionize` trait
//! - `VersionMap` helper
//!
//! `VersionMap` maps individual structure/enum versions to a root version
//! (app version). This mapping is required both when serializing or
//! deserializing structures as it needs to know which version of structure
//! to serialize for a given target app version.
//!
//! `Versionize` trait is implemented for the following primitives:
//! u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, char, f32, f64,
//! String, Vec<T>, Arrays up to 32 elements, Box<T>, Wrapping<T>, Option<T>,
//! FamStructWrapper<T>, and (T, U).
//!
//! Known issues and limitations:
//! - Union serialization is not supported via the `Versionize` proc macro.
//! - Implementing `Versionize` for non-repr(C) unions can result in undefined
//! behaviour and MUST be avoided.
//! - Versionize trait implementations for repr(C) unions must be backed by
//! extensive testing.
//! - Semantic serialization and deserialization is available only for
//! structures.
extern crate bincode;
extern crate crc64;
extern crate serde;
extern crate serde_derive;
extern crate versionize_derive;
extern crate vmm_sys_util;
pub mod crc;
pub mod primitives;
pub mod version_map;
use std::any::TypeId;
use std::io::{Read, Write};
pub use version_map::VersionMap;
use versionize_derive::Versionize;
/// Versioned serialization/deserialization error definitions.
#[allow(clippy::derive_partial_eq_without_eq)] // FIXME: next major release
#[derive(Debug, PartialEq)]
pub enum VersionizeError {
/// An IO error occured.
Io(i32),
/// Generic serialization error.
Serialize(String),
/// Generic deserialization error.
Deserialize(String),
/// Semantic translation/validation error.
Semantic(String),
/// String length exceeded.
StringLength(usize),
/// Vector length exceeded.
VecLength(usize),
}
impl std::fmt::Display for VersionizeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
use VersionizeError::*;
match self {
Io(e) => write!(f, "An IO error occured: {}", e),
Serialize(e) => write!(f, "A serialization error occured: {}", e),
Deserialize(e) => write!(f, "A deserialization error occured: {}", e),
Semantic(e) => write!(f, "A user generated semantic error occured: {}", e),
StringLength(bad_len) => write!(
f,
"String length exceeded {} > {} bytes",
bad_len,
primitives::MAX_STRING_LEN
),
VecLength(bad_len) => write!(
f,
"Vec of length {} exceeded maximum size of {} bytes",
bad_len,
primitives::MAX_VEC_SIZE
),
}
}
}
/// Versioned serialization/deserialization result.
pub type VersionizeResult<T> = std::result::Result<T, VersionizeError>;
/// Trait that provides an interface for version aware serialization and
/// deserialization.
/// The [Versionize proc macro][1] can generate an implementation for a given
/// type if generics are not used, otherwise a manual implementation is
/// required.
///
/// Example implementation
/// ```
/// extern crate versionize;
/// extern crate versionize_derive;
/// use versionize::{VersionMap, Versionize, VersionizeResult};
/// use versionize_derive::Versionize;
///
/// struct MyType<T>(T);
///
/// impl<T> Versionize for MyType<T>
/// where
/// T: Versionize,
/// {
/// #[inline]
/// fn serialize<W: std::io::Write>(
/// &self,
/// writer: &mut W,
/// version_map: &VersionMap,
/// app_version: u16,
/// ) -> VersionizeResult<()> {
/// self.0.serialize(writer, version_map, app_version)
/// }
///
/// #[inline]
/// fn deserialize<R: std::io::Read>(
/// reader: &mut R,
/// version_map: &VersionMap,
/// app_version: u16,
/// ) -> VersionizeResult<Self> {
/// Ok(MyType(T::deserialize(reader, version_map, app_version)?))
/// }
///
/// fn version() -> u16 {
/// 1
/// }
/// }
/// ```
/// [1]: https://docs.rs/versionize_derive/latest/versionize_derive/derive.Versionize.html
pub trait Versionize {
/// Serializes `self` to `target_verion` using the specficifed `writer` and
/// `version_map`.
fn serialize<W: Write>(
&self,
writer: &mut W,
version_map: &VersionMap,
target_version: u16,
) -> VersionizeResult<()>;
/// Returns a new instance of `Self` by deserializing from `source_version`
/// using the specficifed `reader` and `version_map`.
fn deserialize<R: Read>(
reader: &mut R,
version_map: &VersionMap,
source_version: u16,
) -> VersionizeResult<Self>
where
Self: Sized;
/// Returns the `Self` type id.
/// The returned ID represents a globally unique identifier for a type.
/// It is required by the `VersionMap` implementation.
fn type_id() -> std::any::TypeId
where
Self: 'static,
{
TypeId::of::<Self>()
}
/// Returns latest `Self` version number.
fn version() -> u16;
}
#[cfg(test)]
mod tests {
#[test]
fn test_error_debug_display() {
// Validates Debug and Display are implemented.
use VersionizeError::*;
let str = String::from("test");
format!("{:?}{}", Io(0), Io(0));
format!("{:?}{}", Serialize(str.clone()), Serialize(str.clone()));
format!("{:?}{}", Deserialize(str.clone()), Deserialize(str.clone()));
format!("{:?}{}", Semantic(str.clone()), Semantic(str));
}
}