Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit cbd138f

Browse files
authored
[libraries/pod]: add PodOption type (#6886)
* Add pod option type * Use nightly fmt * Rename to option * Update description * Remove interger types impl * Use From impl * Add get method * Cleanup API
1 parent d2d1fcd commit cbd138f

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

libraries/pod/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
pub mod bytemuck;
44
pub mod error;
5+
pub mod option;
56
pub mod optional_keys;
67
pub mod primitives;
78
pub mod slice;

libraries/pod/src/option.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
//! Generic `Option` that can be used as a `Pod` for types that can have
2+
//! a designated `None` value.
3+
//!
4+
//! For example, a 64-bit unsigned integer can designate `0` as a `None` value.
5+
//! This would be equivalent to
6+
//! [`Option<NonZeroU64>`](https://doc.rust-lang.org/std/num/type.NonZeroU64.html)
7+
//! and provide the same memory layout optimization.
8+
9+
use {
10+
bytemuck::{Pod, Zeroable},
11+
solana_program::{program_option::COption, pubkey::Pubkey},
12+
};
13+
14+
/// Trait for types that can be `None`.
15+
///
16+
/// This trait is used to indicate that a type can be `None` according to a
17+
/// specific value.
18+
pub trait Nullable: Default + Pod {
19+
/// Indicates whether the value is `None` or not.
20+
fn is_none(&self) -> bool;
21+
22+
/// Indicates whether the value is `Some`` value of type `T`` or not.
23+
fn is_some(&self) -> bool {
24+
!self.is_none()
25+
}
26+
}
27+
28+
/// A "pod-enabled" type that can be used as an `Option<T>` without
29+
/// requiring extra space to indicate if the value is `Some` or `None`.
30+
///
31+
/// This can be used when a specific value of `T` indicates that its
32+
/// value is `None`.
33+
#[repr(transparent)]
34+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
35+
pub struct PodOption<T: Nullable>(T);
36+
37+
impl<T: Nullable> PodOption<T> {
38+
/// Returns the contained value as an `Option`.
39+
#[inline]
40+
pub fn get(self) -> Option<T> {
41+
if self.0.is_none() {
42+
None
43+
} else {
44+
Some(self.0)
45+
}
46+
}
47+
48+
/// Returns the contained value as an `Option`.
49+
#[inline]
50+
pub fn as_ref(&self) -> Option<&T> {
51+
if self.0.is_none() {
52+
None
53+
} else {
54+
Some(&self.0)
55+
}
56+
}
57+
58+
/// Returns the contained value as a mutable `Option`.
59+
#[inline]
60+
pub fn as_mut(&mut self) -> Option<&mut T> {
61+
if self.0.is_none() {
62+
None
63+
} else {
64+
Some(&mut self.0)
65+
}
66+
}
67+
}
68+
69+
unsafe impl<T: Nullable> Pod for PodOption<T> {}
70+
71+
unsafe impl<T: Nullable> Zeroable for PodOption<T> {}
72+
73+
impl<T: Nullable> From<T> for PodOption<T> {
74+
fn from(value: T) -> Self {
75+
PodOption(value)
76+
}
77+
}
78+
79+
impl<T: Nullable> From<Option<T>> for PodOption<T> {
80+
fn from(from: Option<T>) -> Self {
81+
match from {
82+
Some(value) => PodOption(value),
83+
None => PodOption(T::default()),
84+
}
85+
}
86+
}
87+
88+
impl<T: Nullable> From<COption<T>> for PodOption<T> {
89+
fn from(from: COption<T>) -> Self {
90+
match from {
91+
COption::Some(value) => PodOption(value),
92+
COption::None => PodOption(T::default()),
93+
}
94+
}
95+
}
96+
97+
/// Implementation of `Nullable` for `Pubkey`.
98+
///
99+
/// The implementation assumes that the default value of `Pubkey` represents
100+
/// the `None` value.
101+
impl Nullable for Pubkey {
102+
fn is_none(&self) -> bool {
103+
self == &Pubkey::default()
104+
}
105+
}
106+
107+
#[cfg(test)]
108+
mod tests {
109+
110+
use {super::*, crate::bytemuck::pod_slice_from_bytes, solana_program::sysvar};
111+
112+
#[test]
113+
fn test_pod_option_pubkey() {
114+
let some_pubkey = PodOption::from(sysvar::ID);
115+
assert_eq!(some_pubkey.get(), Some(sysvar::ID));
116+
117+
let none_pubkey = PodOption::from(Pubkey::default());
118+
assert_eq!(none_pubkey.get(), None);
119+
120+
let mut data = Vec::with_capacity(64);
121+
data.extend_from_slice(sysvar::ID.as_ref());
122+
data.extend_from_slice(&[0u8; 32]);
123+
124+
let values = pod_slice_from_bytes::<PodOption<Pubkey>>(&data).unwrap();
125+
assert_eq!(values[0], PodOption::from(sysvar::ID));
126+
assert_eq!(values[1], PodOption::from(Pubkey::default()));
127+
128+
let option_pubkey = Some(sysvar::ID);
129+
let pod_option_pubkey: PodOption<Pubkey> = option_pubkey.into();
130+
assert_eq!(pod_option_pubkey, PodOption::from(sysvar::ID));
131+
assert_eq!(pod_option_pubkey, PodOption::from(option_pubkey));
132+
133+
let coption_pubkey = COption::Some(sysvar::ID);
134+
let pod_option_pubkey: PodOption<Pubkey> = coption_pubkey.into();
135+
assert_eq!(pod_option_pubkey, PodOption::from(sysvar::ID));
136+
assert_eq!(pod_option_pubkey, PodOption::from(coption_pubkey));
137+
}
138+
}

0 commit comments

Comments
 (0)