Skip to main content

fxfs/serialized_types/
traits.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::serialized_types::DEFAULT_MAX_SERIALIZED_RECORD_SIZE;
6use crate::serialized_types::types::LATEST_VERSION;
7use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
8use fprint::TypeFingerprint;
9use serde::{Deserialize, Serialize};
10
11/// [Version] are themselves serializable both alone and as part
12/// of other [Versioned] structures.
13/// (For obvious recursive reasons, this structure can never be [Versioned] itself.)
14#[derive(
15    Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Serialize, Deserialize, TypeFingerprint,
16)]
17pub struct Version {
18    /// Major version indicates structural layout/encoding changes.
19    /// Note that this is encoded as a u24.
20    pub major: u32,
21    /// Minor version indicates forwards compatible changes.
22    /// e.g. The addition of a layer-file index, bloom filters, file attributes or posix
23    /// features where reversion to a previous minor will simply lead to loss of these features.
24    pub minor: u8,
25}
26impl std::fmt::Display for Version {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        write!(f, "{}.{}", self.major, self.minor)
29    }
30}
31impl Version {
32    pub fn deserialize_from<R: ?Sized>(reader: &mut R) -> anyhow::Result<Self>
33    where
34        R: std::io::Read,
35    {
36        Ok(Version { major: reader.read_u24::<LittleEndian>()?, minor: reader.read_u8()? })
37    }
38    pub fn serialize_into<W>(&self, writer: &mut W) -> anyhow::Result<()>
39    where
40        W: std::io::Write,
41    {
42        writer.write_u24::<LittleEndian>(self.major)?;
43        writer.write_u8(self.minor)?;
44        Ok(())
45    }
46}
47
48/// This trait is assigned to all versions of a versioned type and is the only means of
49/// serialization/deserialization we use.
50///
51/// It also allows versions to serialize/deserialize themselves differently on a per-version basis.
52/// Doing this here enforces consistency at a given filesystem version.
53pub trait Versioned: Serialize + for<'de> Deserialize<'de> {
54    /// Maximum size of a serialized version of this type. Serialization and deserialization will
55    /// fail if the limit is exceeded. If None, there is no limit. None is preferred over u64::MAX
56    /// because serializing and deserializing with a limit has runtime overhead.
57    fn max_serialized_size() -> Option<u64> {
58        Some(DEFAULT_MAX_SERIALIZED_RECORD_SIZE)
59    }
60
61    fn deserialize_from<R: ?Sized>(reader: &mut R, _version: Version) -> anyhow::Result<Self>
62    where
63        R: std::io::Read,
64        for<'de> Self: serde::Deserialize<'de>,
65    {
66        use bincode::Options;
67        let options = bincode::DefaultOptions::new().allow_trailing_bytes();
68        let result = if let Some(limit) = Self::max_serialized_size() {
69            options.with_limit(limit).deserialize_from(reader)
70        } else {
71            options.deserialize_from(reader)
72        };
73        result.map_err(map_bincode_error)
74    }
75    fn serialize_into<W>(&self, writer: &mut W) -> anyhow::Result<()>
76    where
77        W: std::io::Write,
78        Self: serde::Serialize,
79    {
80        use bincode::Options;
81        let options = bincode::DefaultOptions::new().allow_trailing_bytes();
82        let result = if let Some(limit) = Self::max_serialized_size() {
83            options.with_limit(limit).serialize_into(writer, self)
84        } else {
85            options.serialize_into(writer, self)
86        };
87        result.map_err(map_bincode_error)
88    }
89}
90
91/// This trait is only assigned to the latest version of a type and allows the type to deserialize
92/// any older versions and upgrade them to the latest format.
93pub trait VersionedLatest: Versioned + TypeFingerprint {
94    /// Deserializes from a given version format and upgrades to the latest version.
95    fn deserialize_from_version<R>(reader: &mut R, version: Version) -> anyhow::Result<Self>
96    where
97        R: std::io::Read,
98        Self: Sized;
99
100    /// Like `deserialize_from_version` but reads Version from reader first, then uses it to
101    /// deserialize self.
102    fn deserialize_with_version<R>(reader: &mut R) -> anyhow::Result<(Self, Version)>
103    where
104        R: std::io::Read,
105        Self: Sized,
106    {
107        let version = Version::deserialize_from(reader)?;
108        Ok((Self::deserialize_from_version(reader, version)?, version))
109    }
110    /// Like `serialize_into` but serialized Version first, then self.
111    fn serialize_with_version<W>(&self, writer: &mut W) -> anyhow::Result<()>
112    where
113        W: std::io::Write,
114        Self: Sized,
115    {
116        LATEST_VERSION.serialize_into(writer)?;
117        self.serialize_into(writer)
118    }
119}
120
121fn map_bincode_error(e: Box<bincode::ErrorKind>) -> anyhow::Error {
122    // Strip bincode wrapping. anyhow can take std::io::Error.
123    if let bincode::ErrorKind::Io(io) = *e { io.into() } else { e.into() }
124}