use crate::{
db::data::{
CanonicalRow, DataKey, RawDataKey, RawRow, SelectiveRowRead, StorageKey,
StructuralRowContract, decode_sparse_indexed_raw_row_with_contract,
decode_sparse_required_slot_with_contract,
},
error::InternalError,
value::Value,
};
use canic_cdk::structures::{BTreeMap, DefaultMemoryImpl, memory::VirtualMemory};
#[cfg(feature = "diagnostics")]
use std::cell::Cell;
#[cfg(feature = "diagnostics")]
thread_local! {
static DATA_STORE_GET_CALL_COUNT: Cell<u64> = const { Cell::new(0) };
}
#[cfg(feature = "diagnostics")]
fn record_data_store_get_call() {
DATA_STORE_GET_CALL_COUNT.with(|count| {
count.set(count.get().saturating_add(1));
});
}
pub struct DataStore {
map: BTreeMap<RawDataKey, RawRow, VirtualMemory<DefaultMemoryImpl>>,
}
impl DataStore {
#[must_use]
pub fn init(memory: VirtualMemory<DefaultMemoryImpl>) -> Self {
Self {
map: BTreeMap::init(memory),
}
}
pub(in crate::db) fn insert(&mut self, key: RawDataKey, row: CanonicalRow) -> Option<RawRow> {
self.map.insert(key, row.into_raw_row())
}
#[cfg(test)]
pub(crate) fn insert_raw_for_test(&mut self, key: RawDataKey, row: RawRow) -> Option<RawRow> {
self.map.insert(key, row)
}
pub fn remove(&mut self, key: &RawDataKey) -> Option<RawRow> {
self.map.remove(key)
}
pub fn get(&self, key: &RawDataKey) -> Option<RawRow> {
#[cfg(feature = "diagnostics")]
record_data_store_get_call();
self.map.get(key)
}
pub(in crate::db) fn read_slot_values(
&self,
key: &RawDataKey,
contract: StructuralRowContract,
expected_key: StorageKey,
required_slots: &[usize],
) -> Result<SelectiveRowRead<Vec<Option<Value>>>, InternalError> {
let Some(raw_row) = self.get(key) else {
return Ok(SelectiveRowRead::MissingRow);
};
let values = if let [required_slot] = required_slots {
vec![decode_sparse_required_slot_with_contract(
&raw_row,
contract,
expected_key,
*required_slot,
)?]
} else {
decode_sparse_indexed_raw_row_with_contract(
&raw_row,
contract,
expected_key,
required_slots,
)?
};
Ok(SelectiveRowRead::Present(values))
}
#[must_use]
pub fn contains(&self, key: &RawDataKey) -> bool {
self.map.contains_key(key)
}
pub fn clear(&mut self) {
self.map.clear();
}
pub fn memory_bytes(&self) -> u64 {
self.iter()
.map(|entry| DataKey::STORED_SIZE_BYTES + entry.value().len() as u64)
.sum()
}
#[cfg(feature = "diagnostics")]
pub(in crate::db) fn current_get_call_count() -> u64 {
DATA_STORE_GET_CALL_COUNT.with(Cell::get)
}
}
impl std::ops::Deref for DataStore {
type Target = BTreeMap<RawDataKey, RawRow, VirtualMemory<DefaultMemoryImpl>>;
fn deref(&self) -> &Self::Target {
&self.map
}
}