h3ron_graph/io/
serde_util.rs

1//! Convenience serialization helpers
2//!
3//! The serialization aims to be fast and allows to apply a LZ4 compression.
4//!
5use std::io;
6
7use lz4_flex::frame::{FrameDecoder, FrameEncoder};
8use serde::Serialize;
9
10use crate::Error;
11
12/// hide bincode errors in the io error to avoid having bincode in the public api.
13impl From<bincode::Error> for Error {
14    fn from(b_err: bincode::Error) -> Self {
15        Self::IOError(std::io::Error::new(std::io::ErrorKind::Other, b_err))
16    }
17}
18
19/// hide lz4_flex errors in the io error to avoid having them in the public api.
20impl From<lz4_flex::frame::Error> for Error {
21    fn from(f_err: lz4_flex::frame::Error) -> Self {
22        Self::IOError(std::io::Error::new(std::io::ErrorKind::Other, f_err))
23    }
24}
25
26///
27/// When `compress` is set to `true` LZ4 compression is applied.
28pub fn serialize_into<W, T: ?Sized>(writer: W, value: &T, compress: bool) -> Result<(), Error>
29where
30    W: io::Write,
31    T: Serialize,
32{
33    if compress {
34        let mut encoder = FrameEncoder::new(writer);
35        bincode::serialize_into(&mut encoder, value)?;
36        encoder.finish()?;
37    } else {
38        bincode::serialize_into(writer, value)?;
39    };
40    Ok(())
41}
42
43/// deserialize. When the reader contains LZ4-compressed data, it
44/// is decompressed on-the-fly.
45pub fn deserialize_from<R, T>(reader: R) -> Result<T, Error>
46where
47    R: io::Read + io::Seek,
48    T: serde::de::DeserializeOwned,
49{
50    let mut decoder = FrameDecoder::new(reader);
51    let deserialized = match bincode::deserialize_from(&mut decoder) {
52        Err(_) => {
53            let original_reader = decoder.get_mut();
54            original_reader.seek(io::SeekFrom::Start(0))?;
55            bincode::deserialize_from(original_reader)?
56        }
57        Ok(des) => des,
58    };
59    Ok(deserialized)
60}
61
62/// deserialize. When the reader contains LZ4-compressed data, it
63/// is decompressed on-the-fly.
64///
65/// Has the benefit over `deserialize_from` of not requiring a wrapping `std::io::Cursor` to
66/// get support for `Seek`.
67pub fn deserialize_from_byte_slice<T>(byte_slice: &[u8]) -> Result<T, Error>
68where
69    T: serde::de::DeserializeOwned,
70{
71    let mut decoder = FrameDecoder::new(byte_slice);
72    let deserialized = match bincode::deserialize_from(&mut decoder) {
73        Err(_) => bincode::deserialize_from(byte_slice)?,
74        Ok(des) => des,
75    };
76    Ok(deserialized)
77}
78
79#[cfg(test)]
80mod tests {
81    use std::io::Cursor;
82
83    use crate::io::serde_util::{deserialize_from, serialize_into};
84
85    fn roundtrip(compress: bool) {
86        let data = vec![1_i32, 2, 3, 4];
87        let mut data_bytes: Vec<u8> = vec![];
88        serialize_into(Cursor::new(&mut data_bytes), &data, compress).unwrap();
89        assert!(!data_bytes.is_empty());
90        let data2: Vec<i32> = deserialize_from(Cursor::new(&data_bytes)).unwrap();
91        assert_eq!(data, data2);
92    }
93
94    #[test]
95    fn test_roundtrip_no_compression() {
96        roundtrip(false);
97    }
98
99    #[test]
100    fn test_roundtrip_compression() {
101        roundtrip(true);
102    }
103}