wasmer_runtime_core_fl/
cache.rs

1//! The cache module provides the common data structures used by compiler backends to allow
2//! serializing compiled wasm code to a binary format.  The binary format can be persisted,
3//! and loaded to allow skipping compilation and fast startup.
4
5use crate::{module::ModuleInfo, sys::Memory};
6use std::{io, mem, slice};
7
8/// Indicates the invalid type of invalid cache file
9#[derive(Debug)]
10pub enum InvalidFileType {
11    /// Given cache header slice does not match the expected size of an `ArtifactHeader`
12    InvalidSize,
13    /// Given cache header slice does not contain the expected magic bytes
14    InvalidMagic,
15}
16
17/// Kinds of caching errors
18#[derive(Debug)]
19pub enum Error {
20    /// An IO error while reading/writing a cache binary.
21    IoError(io::Error),
22    /// An error deserializing bytes into a cache data structure.
23    DeserializeError(String),
24    /// An error serializing bytes from a cache data structure.
25    SerializeError(String),
26    /// An undefined caching error with a message.
27    Unknown(String),
28    /// An invalid cache binary given.
29    InvalidFile(InvalidFileType),
30    /// The cached binary has been invalidated.
31    InvalidatedCache,
32    /// The current backend does not support caching.
33    UnsupportedBackend(String),
34}
35
36impl From<io::Error> for Error {
37    fn from(io_err: io::Error) -> Self {
38        Error::IoError(io_err)
39    }
40}
41
42/// The hash of a wasm module.
43///
44/// Used as a key when loading and storing modules in a [`Cache`].
45///
46/// [`Cache`]: trait.Cache.html
47#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
48// WasmHash is made up of a 32 byte array
49pub struct WasmHash([u8; 32]);
50
51impl WasmHash {
52    /// Hash a wasm module.
53    ///
54    /// # Note:
55    /// This does no verification that the supplied data
56    /// is, in fact, a wasm module.
57    pub fn generate(wasm: &[u8]) -> Self {
58        let hash = blake3::hash(wasm);
59        WasmHash(hash.into())
60    }
61
62    /// Create the hexadecimal representation of the
63    /// stored hash.
64    pub fn encode(self) -> String {
65        hex::encode(&self.into_array() as &[u8])
66    }
67
68    /// Create hash from hexadecimal representation
69    pub fn decode(hex_str: &str) -> Result<Self, Error> {
70        let bytes = hex::decode(hex_str).map_err(|e| {
71            Error::DeserializeError(format!(
72                "Could not decode prehashed key as hexadecimal: {}",
73                e
74            ))
75        })?;
76        if bytes.len() != 32 {
77            return Err(Error::DeserializeError(
78                "Prehashed keys must deserialze into exactly 32 bytes".to_string(),
79            ));
80        }
81        use std::convert::TryInto;
82        Ok(WasmHash(bytes[0..32].try_into().map_err(|e| {
83            Error::DeserializeError(format!("Could not get first 32 bytes: {}", e))
84        })?))
85    }
86
87    pub(crate) fn into_array(self) -> [u8; 32] {
88        let mut total = [0u8; 32];
89        total[0..32].copy_from_slice(&self.0);
90        total
91    }
92}
93
94const CURRENT_CACHE_VERSION: u64 = 0;
95static WASMER_CACHE_MAGIC: [u8; 8] = *b"WASMER\0\0";
96
97/// The header of a cache file.
98#[repr(C, packed)]
99struct ArtifactHeader {
100    magic: [u8; 8], // [W, A, S, M, E, R, \0, \0]
101    version: u64,
102    data_len: u64,
103}
104
105impl ArtifactHeader {
106    pub fn read_from_slice(buffer: &[u8]) -> Result<(&Self, &[u8]), Error> {
107        if buffer.len() >= mem::size_of::<ArtifactHeader>() {
108            if &buffer[..8] == &WASMER_CACHE_MAGIC {
109                let (header_slice, body_slice) = buffer.split_at(mem::size_of::<ArtifactHeader>());
110                let header = unsafe { &*(header_slice.as_ptr() as *const ArtifactHeader) };
111
112                if header.version == CURRENT_CACHE_VERSION {
113                    Ok((header, body_slice))
114                } else {
115                    Err(Error::InvalidatedCache)
116                }
117            } else {
118                Err(Error::InvalidFile(InvalidFileType::InvalidMagic))
119            }
120        } else {
121            Err(Error::InvalidFile(InvalidFileType::InvalidSize))
122        }
123    }
124
125    pub fn read_from_slice_mut(buffer: &mut [u8]) -> Result<(&mut Self, &mut [u8]), Error> {
126        if buffer.len() >= mem::size_of::<ArtifactHeader>() {
127            if &buffer[..8] == &WASMER_CACHE_MAGIC {
128                let (header_slice, body_slice) =
129                    buffer.split_at_mut(mem::size_of::<ArtifactHeader>());
130                let header = unsafe { &mut *(header_slice.as_ptr() as *mut ArtifactHeader) };
131
132                if header.version == CURRENT_CACHE_VERSION {
133                    Ok((header, body_slice))
134                } else {
135                    Err(Error::InvalidatedCache)
136                }
137            } else {
138                Err(Error::InvalidFile(InvalidFileType::InvalidMagic))
139            }
140        } else {
141            Err(Error::InvalidFile(InvalidFileType::InvalidSize))
142        }
143    }
144
145    pub fn as_slice(&self) -> &[u8] {
146        let ptr = self as *const ArtifactHeader as *const u8;
147        unsafe { slice::from_raw_parts(ptr, mem::size_of::<ArtifactHeader>()) }
148    }
149}
150
151#[derive(Serialize, Deserialize)]
152struct ArtifactInner {
153    info: Box<ModuleInfo>,
154    #[serde(with = "serde_bytes")]
155    backend_metadata: Box<[u8]>,
156    compiled_code: Memory,
157}
158
159/// Artifact are produced by caching, are serialized/deserialized to binaries, and contain
160/// module info, backend metadata, and compiled code.
161pub struct Artifact {
162    inner: ArtifactInner,
163}
164
165impl Artifact {
166    pub(crate) fn from_parts(
167        info: Box<ModuleInfo>,
168        backend_metadata: Box<[u8]>,
169        compiled_code: Memory,
170    ) -> Self {
171        Self {
172            inner: ArtifactInner {
173                info,
174                backend_metadata,
175                compiled_code,
176            },
177        }
178    }
179
180    /// Deserializes an `Artifact` from the given byte slice.
181    pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
182        let (_, body_slice) = ArtifactHeader::read_from_slice(bytes)?;
183
184        let inner = serde_bench::deserialize(body_slice)
185            .map_err(|e| Error::DeserializeError(format!("{:#?}", e)))?;
186
187        Ok(Artifact { inner })
188    }
189
190    /// A reference to the `Artifact`'s stored `ModuleInfo`
191    pub fn info(&self) -> &ModuleInfo {
192        &self.inner.info
193    }
194
195    #[doc(hidden)]
196    pub fn consume(self) -> (ModuleInfo, Box<[u8]>, Memory) {
197        (
198            *self.inner.info,
199            self.inner.backend_metadata,
200            self.inner.compiled_code,
201        )
202    }
203
204    /// Serializes the `Artifact` into a vector of bytes
205    pub fn serialize(&self) -> Result<Vec<u8>, Error> {
206        let cache_header = ArtifactHeader {
207            magic: WASMER_CACHE_MAGIC,
208            version: CURRENT_CACHE_VERSION,
209            data_len: 0,
210        };
211
212        let mut buffer = cache_header.as_slice().to_vec();
213
214        serde_bench::serialize(&mut buffer, &self.inner)
215            .map_err(|e| Error::SerializeError(e.to_string()))?;
216
217        let data_len = (buffer.len() - mem::size_of::<ArtifactHeader>()) as u64;
218
219        let (header, _) = ArtifactHeader::read_from_slice_mut(&mut buffer)?;
220        header.data_len = data_len;
221
222        Ok(buffer)
223    }
224}
225
226/// A unique ID generated from the version of Wasmer for use with cache versioning
227pub const WASMER_VERSION_HASH: &'static str =
228    include_str!(concat!(env!("OUT_DIR"), "/wasmer_version_hash.txt"));