use core::marker::PhantomData;
use bytemuck::Pod;
use crate::{status::StatusError, sys};
pub fn max_persist() -> usize {
#[cfg(pbl_sdk_frozen)]
return 4096;
#[cfg(not(pbl_sdk_frozen))]
unsafe {
sys::persist_get_max_size()
}
}
pub struct PersistKey<T: Persist + ?Sized> {
key: u32,
data: PhantomData<T>,
}
impl<T: Persist + ?Sized> Clone for PersistKey<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Persist + ?Sized> Copy for PersistKey<T> {}
impl<T: Persist + ?Sized> PersistKey<T> {
#[doc(hidden)]
pub const fn __new(key: u32) -> Self {
Self {
key,
data: PhantomData,
}
}
pub const fn retype<U: Persist + ?Sized>(self) -> PersistKey<U> {
PersistKey::__new(self.key)
}
pub const fn key(self) -> u32 {
self.key
}
pub fn exists(self) -> bool {
unsafe { sys::persist_exists(self.key) }
}
pub fn delete(self) {
unsafe { sys::persist_delete(self.key) };
}
pub fn size(self) -> Result<u32, StatusError> {
StatusError::check(unsafe { sys::persist_get_size(self.key) })
}
}
pub trait Persist {
fn persist(&self, key: PersistKey<Self>) -> Result<u32, StatusError>;
}
pub trait Recover: Persist + Sized {
fn recover(key: PersistKey<Self>) -> Option<Self>;
fn recover_or_default(key: PersistKey<Self>) -> Self
where
Self: Default,
{
Self::recover(key).unwrap_or_default()
}
}
pub trait RecoverSlice: Persist {
fn recover(key: PersistKey<Self>, buf: &mut [u8]) -> Result<&Self, StatusError>;
}
impl Persist for bool {
fn persist(&self, key: PersistKey<Self>) -> Result<u32, StatusError> {
StatusError::check(unsafe { sys::persist_write_bool(key.key, *self) })
}
}
impl Recover for bool {
fn recover(key: PersistKey<Self>) -> Option<Self> {
key.exists().then(|| Self::recover_or_default(key))
}
fn recover_or_default(key: PersistKey<Self>) -> Self {
let mut byte = 0u8;
unsafe { sys::persist_read_data(key.key, (&raw mut byte).cast(), 1) };
byte != 0
}
}
impl Persist for i32 {
fn persist(&self, key: PersistKey<Self>) -> Result<u32, StatusError> {
StatusError::check(unsafe { sys::persist_write_int(key.key, *self) })
}
}
impl Recover for i32 {
fn recover(key: PersistKey<Self>) -> Option<Self> {
key.exists().then(|| Self::recover_or_default(key))
}
fn recover_or_default(key: PersistKey<Self>) -> Self {
unsafe { sys::persist_read_int(key.key) }
}
}
impl Persist for [u8] {
fn persist(&self, key: PersistKey<Self>) -> Result<u32, StatusError> {
if self.len() <= sys::PERSIST_DATA_MAX_LENGTH as usize {
StatusError::check(unsafe {
sys::persist_write_data(key.key, self.as_ptr().cast(), self.len())
})
} else {
Err(StatusError::OutOfRange)
}
}
}
impl RecoverSlice for [u8] {
fn recover(key: PersistKey<Self>, buf: &mut [u8]) -> Result<&Self, StatusError> {
let bytes = StatusError::check(unsafe {
sys::persist_read_data(key.key, buf.as_mut_ptr().cast(), buf.len())
})?;
let read = buf.get(..bytes as usize).ok_or(StatusError::Internal)?;
Ok(read)
}
}
impl Persist for core::ffi::CStr {
fn persist(&self, key: PersistKey<Self>) -> Result<u32, StatusError> {
if self.count_bytes() < sys::PERSIST_STRING_MAX_LENGTH as usize {
StatusError::check(unsafe { sys::persist_write_string(key.key, self.as_ptr()) })
} else {
Err(StatusError::OutOfRange)
}
}
}
impl RecoverSlice for core::ffi::CStr {
fn recover(key: PersistKey<Self>, buf: &mut [u8]) -> Result<&Self, StatusError> {
if buf.is_empty() {
return Err(StatusError::OutOfRange);
}
let bytes = StatusError::check(unsafe {
sys::persist_read_string(key.key, buf.as_mut_ptr().cast(), buf.len())
})?;
let read = buf.get(..bytes as usize).ok_or(StatusError::Internal)?;
let string =
core::ffi::CStr::from_bytes_until_nul(read).map_err(|_| StatusError::Internal)?;
Ok(string)
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct AsBytes<T: Pod>(pub T);
impl<T: Pod> Default for AsBytes<T> {
fn default() -> Self {
Self(T::zeroed())
}
}
impl<T: Pod> Persist for AsBytes<T> {
fn persist(&self, key: PersistKey<Self>) -> Result<u32, StatusError> {
__persist_pod(&self.0, key.key)
}
}
impl<T: Pod> Recover for AsBytes<T> {
fn recover(key: PersistKey<Self>) -> Option<Self> {
__recover_pod(key.key).map(Self)
}
}
#[doc(hidden)]
pub fn __persist_pod<T: Pod>(value: &T, key: u32) -> Result<u32, StatusError> {
const {
assert!(
size_of::<T>() <= sys::PERSIST_DATA_MAX_LENGTH as usize,
"persisted value is over PERSIST_DATA_MAX_LENGTH"
);
}
let bytes = bytemuck::bytes_of(value);
StatusError::check(unsafe { sys::persist_write_data(key, bytes.as_ptr().cast(), bytes.len()) })
}
#[doc(hidden)]
pub fn __recover_pod<T: Pod>(key: u32) -> Option<T> {
let size = size_of::<T>();
if usize::try_from(unsafe { sys::persist_get_size(key) }) != Ok(size) {
return None;
}
let mut value = T::zeroed();
let bytes = bytemuck::bytes_of_mut(&mut value);
let read = unsafe { sys::persist_read_data(key, bytes.as_mut_ptr().cast(), size) };
(usize::try_from(read) == Ok(size)).then_some(value)
}
pub struct PersistBlock<T: Persist + ?Sized> {
first: u32,
len: u32,
data: PhantomData<T>,
}
impl<T: Persist + ?Sized> Clone for PersistBlock<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Persist + ?Sized> Copy for PersistBlock<T> {}
impl<T: Persist + ?Sized> PersistBlock<T> {
#[doc(hidden)]
pub const fn __new(first: u32, len: u32) -> Self {
Self {
first,
len,
data: PhantomData,
}
}
pub const fn len(self) -> u32 {
self.len
}
pub const fn is_empty(self) -> bool {
self.len == 0
}
pub const fn get(self, index: u32) -> Option<PersistKey<T>> {
if index < self.len {
Some(PersistKey::__new(self.first + index))
} else {
None
}
}
pub fn keys(self) -> impl Iterator<Item = PersistKey<T>> {
(self.first..self.first + self.len).map(PersistKey::__new)
}
}
pub struct PersistTail<T: Persist + ?Sized> {
first: u32,
data: PhantomData<T>,
}
impl<T: Persist + ?Sized> Clone for PersistTail<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Persist + ?Sized> Copy for PersistTail<T> {}
impl<T: Persist + ?Sized> PersistTail<T> {
#[doc(hidden)]
pub const fn __new(first: u32) -> Self {
Self {
first,
data: PhantomData,
}
}
pub const fn get(self, index: u32) -> Option<PersistKey<T>> {
match self.first.checked_add(index) {
Some(key) => Some(PersistKey::__new(key)),
None => None,
}
}
}