wasm4fun-storage 0.1.0

Storage primitives and subsystems for WASM-4 fantasy console
Documentation
// Copyright Claudio Mattera 2022.
//
// Distributed under the MIT License or the Apache 2.0 License at your option.
// See the accompanying files License-MIT.txt and License-Apache-2.0.txt, or
// online at
// https://opensource.org/licenses/MIT
// https://opensource.org/licenses/Apache-2.0

#[cfg(feature = "storage")]
use serde::{de::DeserializeOwned, Serialize};

#[cfg(feature = "storage")]
use postcard::from_bytes as from_postcard_slice;

#[cfg(feature = "storage")]
use postcard::to_slice as to_postcard_slice;

#[cfg(feature = "storage")]
use wasm4fun_core::{diskr, diskw};

use wasm4fun_log::debug;

/// Store a payload to disk
///
/// The payload must implement [`Serialize`], and it is serialized using
/// [serde] and [postcard] crates.
///
/// # Example
///
/// ```no_run
/// use serde::Serialize;
///
/// use wasm4fun_storage::store;
///
/// #[derive(Default, Serialize)]
/// struct SomeStruct {
///     field: u8,
///     other_field: f32,
/// }
///
/// let payload: SomeStruct = Default::default();
///
/// const BUFFER_LENGTH: usize = 8;
///
/// store::<SomeStruct, BUFFER_LENGTH>(payload);
/// ```
///
/// # Panics
///
/// This function panics if the serialized data is larger than
/// `BUFFER_LENGTH` bytes.
///
/// # Note
///
/// Regardless of the value of `BUFFER_LENGTH`, WASM-4 only supports storage
/// for up to 1024 bytes.
#[cfg(feature = "storage")]
pub fn store<T, const BUFFER_LENGTH: usize>(payload: T)
where
    T: Serialize,
{
    let mut buffer: [u8; BUFFER_LENGTH] = [0; BUFFER_LENGTH];

    let slice: &[u8] = match to_postcard_slice(&payload, &mut buffer) {
        Ok(slice) => slice,
        Err(message) => {
            debug!("Could not serialize payload");
            debug!("Reason: {}", message);
            panic!("Could not serialize payload");
        }
    };

    debug!("Payload serialized to {} bytes", slice.len());

    let bytes_written = unsafe { diskw(slice.as_ptr(), slice.len() as u32) };
    if bytes_written != slice.len() as u32 {
        debug!("Could not write payload");
        debug!("Written {} bytes instead of {}", bytes_written, slice.len());
    }
}

/// Load a payload from disk
///
/// The payload must implement [`DeserializeOwned`], and it is serialized using
/// [serde] and [postcard] crates.
/// [`DeserializeOwned`] is functionally equivalent to
/// [`Deserialize`][serde::Deserialize], with the additional constraint that
/// `T` can be deserialized without borrowing any data from the deserializer.
///
/// # Example
///
/// ```no_run
/// use serde::Deserialize;
///
/// use wasm4fun_storage::load;
///
/// #[derive(Default, Deserialize)]
/// struct SomeStruct {
///     field: u8,
///     other_field: f32,
/// }
///
/// const BUFFER_LENGTH: usize = 8;
///
/// let payload: SomeStruct = load::<SomeStruct, BUFFER_LENGTH>();
/// ```
///
/// # Panics
///
/// This function panics if the deserialized data is larger than
/// `BUFFER_LENGTH` bytes.
///
/// # Note
///
/// Regardless of the value of `BUFFER_LENGTH`, WASM-4 only supports storage
/// for up to 1024 bytes.
#[cfg(feature = "storage")]
pub fn load<T, const BUFFER_LENGTH: usize>() -> T
where
    T: Default + DeserializeOwned,
{
    let mut buffer: [u8; BUFFER_LENGTH] = [0; BUFFER_LENGTH];

    let bytes_read = unsafe { diskr(buffer.as_mut_ptr(), buffer.len() as u32) };

    let data: &[u8] = &buffer[0..bytes_read as usize];
    let payload: T = match from_postcard_slice(data) {
        Ok(payload) => payload,
        Err(message) => {
            debug!("Could not deserialize payload");
            debug!("Reason: {}", message);
            Default::default()
        }
    };
    debug!("Loaded payload");
    payload
}

#[cfg(not(feature = "storage"))]
pub fn store<T, const BUFFER_LENGTH: usize>(_payload: T) {
    debug!("Storage not enabled");
}

#[cfg(not(feature = "storage"))]
pub fn load<T, const BUFFER_LENGTH: usize>() -> T
where
    T: Default,
{
    debug!("Storage not enabled");
    Default::default()
}