spherenet_program_whitelist_interface/state/
mod.rs

1pub mod account_state;
2pub mod entry;
3pub mod whitelist;
4use {
5    bytemuck::{Pod, Zeroable},
6    core::mem,
7    pinocchio::program_error::ProgramError,
8};
9
10/// Trait for types that have a fixed, known size
11pub trait SizeOf {
12    /// Size of the type in bytes
13    const SIZE_OF: usize;
14}
15
16/// Implement SizeOf for all Pod types
17impl<T: Pod> SizeOf for T {
18    const SIZE_OF: usize = mem::size_of::<T>();
19}
20
21/// Type alias for fields represented as `COption`.
22pub type COption<T> = ([u8; 4], T);
23
24/// `COption<T>` stored as a Pod type for compatibility with bytemuck
25#[repr(C, packed)]
26#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
27pub struct PodCOption<T>
28where
29    T: Pod + Default,
30{
31    pub(crate) option: [u8; 4],
32    pub(crate) value: T,
33}
34
35impl<T> PodCOption<T>
36where
37    T: Pod + Default,
38{
39    /// Represents that no value is stored in the option, like `Option::None`
40    pub const NONE: [u8; 4] = [0; 4];
41    /// Represents that some value is stored in the option, like
42    /// `Option::Some(v)`
43    pub const SOME: [u8; 4] = [1, 0, 0, 0];
44
45    /// Create a `PodCOption` equivalent of `Option::None`
46    ///
47    /// This could be made `const` by using `std::mem::zeroed`, but that would
48    /// require `unsafe` code, which is prohibited at the crate level.
49    pub fn none() -> Self {
50        Self {
51            option: Self::NONE,
52            value: T::default(),
53        }
54    }
55
56    /// Create a `PodCOption` equivalent of `Option::Some(value)`
57    pub const fn some(value: T) -> Self {
58        Self {
59            option: Self::SOME,
60            value,
61        }
62    }
63
64    /// Get the underlying value or another provided value if it isn't set,
65    /// equivalent of `Option::unwrap_or`
66    pub fn unwrap_or(self, default: T) -> T {
67        if self.option == Self::NONE {
68            default
69        } else {
70            self.value
71        }
72    }
73
74    /// Checks to see if a value is set, equivalent of `Option::is_some`
75    pub fn is_some(&self) -> bool {
76        self.option == Self::SOME
77    }
78
79    /// Checks to see if no value is set, equivalent of `Option::is_none`
80    pub fn is_none(&self) -> bool {
81        self.option == Self::NONE
82    }
83
84    /// Converts the option into a Result, similar to `Option::ok_or`
85    pub fn ok_or<E>(self, error: E) -> Result<T, E> {
86        match self {
87            Self {
88                option: Self::SOME,
89                value,
90            } => Ok(value),
91            _ => Err(error),
92        }
93    }
94}
95
96impl<T: Pod + Default> From<COption<T>> for PodCOption<T> {
97    fn from(opt: COption<T>) -> Self {
98        if opt.0[0] == 0 {
99            Self {
100                option: Self::NONE,
101                value: T::default(),
102            }
103        } else {
104            Self {
105                option: Self::SOME,
106                value: opt.1,
107            }
108        }
109    }
110}
111
112/// Trait to represent a type that can be initialized.
113pub trait Initializable {
114    /// Return `true` if the object is initialized.
115    fn is_initialized(&self) -> bool;
116}
117
118/// Load a reference for an initialized `T` from the given bytes
119#[inline(always)]
120pub fn load<'data, T: Pod + Initializable>(input: &'data [u8]) -> Result<&'data T, ProgramError> {
121    if input.len() != T::SIZE_OF {
122        return Err(ProgramError::InvalidAccountData);
123    }
124
125    let account = bytemuck::from_bytes::<T>(input);
126
127    if !account.is_initialized() {
128        return Err(ProgramError::UninitializedAccount);
129    }
130
131    Ok(account)
132}
133
134/// Load a mutable reference for an initialized `T` from the given bytes
135#[inline(always)]
136pub fn load_mut<'data, T: Pod + Initializable>(
137    input: &'data mut [u8],
138) -> Result<&'data mut T, ProgramError> {
139    if input.len() != T::SIZE_OF {
140        return Err(ProgramError::InvalidAccountData);
141    }
142
143    let account = bytemuck::from_bytes_mut::<T>(input);
144
145    if !account.is_initialized() {
146        return Err(ProgramError::UninitializedAccount);
147    }
148
149    Ok(account)
150}
151
152/// Load a reference without checking if the data is initialized
153#[inline(always)]
154pub fn load_unchecked<'data, T: Pod>(input: &'data [u8]) -> Result<&'data T, ProgramError> {
155    if input.len() != T::SIZE_OF {
156        return Err(ProgramError::InvalidAccountData);
157    }
158
159    Ok(bytemuck::from_bytes::<T>(input))
160}
161
162/// Load a mutable reference without checking if the data is initialized
163#[inline(always)]
164pub fn load_mut_unchecked<'data, T: Pod>(
165    input: &'data mut [u8],
166) -> Result<&'data mut T, ProgramError> {
167    if input.len() != T::SIZE_OF {
168        return Err(ProgramError::InvalidAccountData);
169    }
170
171    Ok(bytemuck::from_bytes_mut::<T>(input))
172}