ic-storage 0.68.5

Governed stable allocation and collections for Internet Computer applications
Documentation
// Adapted from ic-stable-structures 0.7.2, DFINITY Foundation.
// SPDX-License-Identifier: Apache-2.0
// See the package LICENSE-APACHE and docs/storage/icss-source.json.
//! A serializable value stored in the stable memory.
use crate::engine::storable::Storable;
use crate::engine::{Memory, WASM_PAGE_SIZE, read_to_vec};
use std::borrow::{Borrow, Cow};

const MAGIC: &[u8; 3] = b"SCL"; // short for "stable cell"
const HEADER_V1_SIZE: u64 = 8;
const LAYOUT_VERSION: u8 = 1;

// NOTE: the size of this structure should be equal to [HEADER_V1_SIZE].
// NOTE: if you have to add more fields, you need to increase the version and handle decoding of
// previous versions in `Cell::read_header`.
//
// # V1 layout
//
// -------------------------------
// Magic "SCL"         ↕ 3 bytes
// -------------------------------
// Layout version      ↕ 1 byte
// -------------------------------
// Value length = N    ↕ 4 bytes
// -------------------------------
// <encoded value>     ↕ N bytes
// -------------------------------
#[derive(Debug, PartialEq, Eq)]
pub enum ValueError {
    ValueTooLarge { value_size: u64 },
}

pub struct Cell<T: Storable, M: Memory> {
    memory: M,
    value: T,
}

impl<T: Storable, M: Memory> Cell<T, M> {
    #[cfg(not(target_arch = "wasm32"))]
    pub(crate) const fn memory(&self) -> &M {
        &self.memory
    }
    pub fn new(memory: M, value: T) -> Self {
        Self::flush_value(&memory, &value).expect("Failed to write initial value to the memory");
        Self { memory, value }
    }

    pub(crate) fn load_with_length(memory: M, length: u32) -> Self {
        let value = Self::read_value(&memory, length);
        Self { memory, value }
    }

    fn read_value(memory: &M, len: u32) -> T {
        let mut buf = vec![];
        read_to_vec(memory, HEADER_V1_SIZE.into(), &mut buf, len as usize);
        T::from_bytes(Cow::Owned(buf))
    }

    pub const fn get(&self) -> &T {
        &self.value
    }

    pub fn set(&mut self, value: T) -> T {
        Self::flush_value(&self.memory, &value).expect("Failed to write value to the memory");
        std::mem::replace(&mut self.value, value)
    }
    fn flush_value(memory: &M, value: &T) -> Result<(), ValueError> {
        let encoded = value.to_bytes();
        let bytes: &[u8] = encoded.borrow();
        let len = bytes.len();
        if len > u32::MAX as usize {
            return Err(ValueError::ValueTooLarge {
                value_size: len as u64,
            });
        }
        let size = memory.size();
        let available_space = size * WASM_PAGE_SIZE;
        if len as u64 > available_space.saturating_sub(HEADER_V1_SIZE) || size == 0 {
            let grow_by =
                (len as u64 + HEADER_V1_SIZE + WASM_PAGE_SIZE - size * WASM_PAGE_SIZE - 1)
                    / WASM_PAGE_SIZE;
            if memory.grow(grow_by) < 0 {
                return Err(ValueError::ValueTooLarge {
                    value_size: len as u64,
                });
            }
        }

        debug_assert!(memory.size() * WASM_PAGE_SIZE >= len as u64 + HEADER_V1_SIZE);

        let mut header = [0; 8];
        header[..3].copy_from_slice(MAGIC);
        header[3] = LAYOUT_VERSION;
        header[4..].copy_from_slice(
            &u32::try_from(len)
                .expect("admitted cell length")
                .to_le_bytes(),
        );
        memory.write(0, &header);
        memory.write(HEADER_V1_SIZE, bytes);
        Ok(())
    }
}