spectrusty_core/memory/
serde.rs

1/*
2    Copyright (C) 2020-2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8//! Utilities for serializing memory as base64 strings or just bytes in binary serializers.
9use core::fmt;
10use std::borrow::Cow;
11use std::rc::Rc;
12use std::convert::TryInto;
13#[cfg(feature = "compression")] use core::iter::FromIterator;
14#[cfg(feature = "compression")] use compression::prelude::*;
15#[cfg(feature = "compression")] use serde::ser;
16
17use serde::{
18    Serializer, Deserialize, Deserializer,
19    de::{self, Visitor}
20};
21
22pub fn serialize_mem<T, S>(mem: &T, serializer: S) -> Result<S::Ok, S::Error>
23    where S: Serializer,
24          T: MemSerExt
25{
26    #[cfg(not(feature = "compression"))]
27    {
28        serialize_mem_slice(mem.as_slice(), serializer)
29    }
30    #[cfg(feature = "compression")]
31    {
32        let compr = mem.as_slice().iter().copied()
33            .encode(&mut GZipEncoder::new(), Action::Finish)
34            .collect::<Result<Vec<_>, _>>()
35            .map_err(ser::Error::custom)?;
36        serialize_mem_slice(&compr, serializer)
37    }
38}
39
40pub fn serialize_mem_slice<S>(slice: &[u8], serializer: S) -> Result<S::Ok, S::Error>
41    where S: Serializer
42{
43    if serializer.is_human_readable() {
44        serializer.serialize_str(&base64::encode(slice))
45    }
46    else {
47        serializer.serialize_bytes(slice)
48    }
49}
50
51pub fn deserialize_mem<'de, T, D>(deserializer: D) -> Result<T, D::Error>
52    where T: MemDeExt,
53          D: Deserializer<'de>
54{
55    if deserializer.is_human_readable() {
56        Deserialize::deserialize(deserializer).and_then(|string: Cow<str>|
57            base64::decode(&*string).map_err(de::Error::custom)
58        )
59        .and_then(T::try_from_byte_buf)
60    }
61    else if T::PREFER_FROM_BYTE_BUF {
62        deserializer.deserialize_byte_buf(ByteBufVisitor)
63                    .and_then(T::try_from_byte_buf)
64    }
65    else {
66        Deserialize::deserialize(deserializer)
67                    .and_then(T::try_from_bytes)
68    }
69}
70
71pub trait MemSerExt: Sized {
72    fn as_slice(&self) -> &[u8];
73}
74
75pub trait MemDeExt: Sized {
76    const PREFER_FROM_BYTE_BUF: bool;
77    fn try_from_byte_buf<E: de::Error>(buf: Vec<u8>) -> Result<Self, E>;
78    fn try_from_bytes<E: de::Error>(slice: &[u8]) -> Result<Self, E>;
79}
80
81impl<const LEN: usize> MemSerExt for Box<[u8;LEN]> {
82    fn as_slice(&self) -> &[u8] {
83        &self[..]
84    }
85}
86
87impl<const LEN: usize> MemDeExt for Box<[u8;LEN]> {
88    const PREFER_FROM_BYTE_BUF: bool = true;
89
90    #[allow(unused_mut)]
91    fn try_from_byte_buf<E: de::Error>(mut buf: Vec<u8>) -> Result<Self, E> {
92        #[cfg(feature = "compression")]
93        {
94            if is_compressed(&buf) {
95                buf = decompress(&buf)?;
96            }
97        }
98        let buf = buf.into_boxed_slice();
99        buf.try_into().map_err(|buf: Box<[_]>|
100            de::Error::custom(
101                format!("failed to deserialize memory, {} bytes required, received: {}",
102                    LEN, buf.len())))
103    }
104
105    fn try_from_bytes<E: de::Error>(slice: &[u8]) -> Result<Self, E> {
106        #[cfg(feature = "compression")]
107        {
108            if is_compressed(slice) {
109                return Self::try_from_byte_buf(decompress(slice)?)
110            }
111        }
112        Self::try_from_byte_buf(Vec::from(slice))
113    }
114}
115
116impl<const LEN: usize> MemDeExt for [u8;LEN] {
117    const PREFER_FROM_BYTE_BUF: bool = false;
118
119    fn try_from_byte_buf<E: de::Error>(buf: Vec<u8>) -> Result<Self, E> {
120        Self::try_from_bytes(&buf)
121    }
122
123    fn try_from_bytes<E: de::Error>(slice: &[u8]) -> Result<Self, E> {
124        slice.try_into().map_err(|_|
125            de::Error::custom(
126                format!("failed to deserialize a byte array, {} bytes required, received: {}",
127                    LEN, slice.len())))
128    }
129}
130
131impl<'a, T: MemSerExt> MemSerExt for &'a T {
132    fn as_slice(&self) -> &[u8] {
133        T::as_slice(self)
134    }
135}
136
137impl MemSerExt for Rc<[u8]> {
138    fn as_slice(&self) -> &[u8] {
139        &self[..]
140    }
141}
142
143impl MemDeExt for Rc<[u8]> {
144    const PREFER_FROM_BYTE_BUF: bool = false;
145
146    fn try_from_byte_buf<E: de::Error>(buf: Vec<u8>) -> Result<Self, E> {
147        #[cfg(feature = "compression")]
148        {
149            if is_compressed(&buf) {
150                return decompress(&buf)
151            }
152        }
153        Ok(Rc::from(buf))
154    }
155
156    fn try_from_bytes<E: de::Error>(slice: &[u8]) -> Result<Self, E> {
157        #[cfg(feature = "compression")]
158        {
159            if is_compressed(slice) {
160                return decompress(slice)
161            }
162        }
163        Ok(Rc::from(slice))
164    }
165}
166
167struct ByteBufVisitor;
168
169impl Visitor<'_> for ByteBufVisitor {
170    type Value = Vec<u8>;
171
172    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
173        formatter.write_str("a byte array")
174    }
175
176    fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
177        Ok(v)
178    }
179
180    fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
181        Ok(Vec::from(v))
182    }
183
184    fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
185        Ok(Vec::from(v))
186    }
187
188    fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
189        Ok(Vec::from(v))
190    }
191
192}
193
194#[cfg(feature = "compression")]
195fn is_compressed(data: &[u8]) -> bool {
196    match data.get(0..3) {
197        Some(&[0x1f, 0x8b, 0x08]) => true,
198        // Some(&[b'B', b'Z', b'h', b'1'..=b'9', 0x31, 0x41, 0x59, 0x26, 0x53, 0x59]) => true
199        _ => false
200    }
201}
202
203#[cfg(feature = "compression")]
204fn decompress<T: FromIterator<u8>, E: de::Error>(data: &[u8]) -> Result<T, E> {
205    data.iter().copied()
206        .decode(&mut GZipDecoder::new())
207        .collect::<Result<T, _>>()
208        .map_err(de::Error::custom)
209}