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));
    }
}