Skip to content

Commit

Permalink
Add the PodOption<T> type
Browse files Browse the repository at this point in the history
For [permissioned
markets](#33), we need
to store the `Pubkey`s of market authorities. Assuming that we do not
want to require *all* markets to be permissioned, we will need to store
both (1) whether or not this market has an authority and (2) the
authority, if any.

Traditionally, this would be done in an `Option<Pubkey>`, but we use
[zero copy
deserialization](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/attr.zero_copy.html),
which requires all types to implement `bytemuck::Pod`. `std::option::Option<T>` does not implement `Pod`, and because of the way enum bit patterns work in Rust, it cannot. So we need to add a custom option implementation that *does* implement `Pod`.
  • Loading branch information
metaproph3t committed May 24, 2023
1 parent 40d7bff commit 232da79
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 0 deletions.
1 change: 1 addition & 0 deletions programs/openbook-v2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod accounts_zerocopy;
pub mod error;
pub mod i80f48;
pub mod logs;
pub mod pod_option;
pub mod state;
pub mod types;

Expand Down
73 changes: 73 additions & 0 deletions programs/openbook-v2/src/pod_option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#[cfg(test)]
use anchor_lang::prelude::Pubkey;
use bytemuck::{Pod, Zeroable};
use std::convert::From;

/// Like `Option`, but implements `Pod`.
///
/// To ensure that there are no illegal bit patterns or padding bytes,
/// `PodOption` is laid out as a single byte which is 0 in the case of `None`
/// or non-zero in the case of `Some`, and then the value, if any.
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct PodOption<T: Pod> {
flag: u8,
value: T,
}

#[cfg(target_endian = "little")]
unsafe impl<T: Pod> Zeroable for PodOption<T> {}

#[cfg(target_endian = "little")]
unsafe impl<T: Pod> Pod for PodOption<T> {}

impl<T: Pod> From<PodOption<T>> for Option<T> {
fn from(pod_option: PodOption<T>) -> Self {
if pod_option.flag > 0 {
Some(pod_option.value)
} else {
None
}
}
}

impl<T: Pod> From<Option<T>> for PodOption<T> {
fn from(normal_option: Option<T>) -> Self {
match normal_option {
Some(value) => Self { flag: 1, value },
None => Self {
flag: 0,
value: T::zeroed(),
},
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
pub fn test_somes() {
let pod_option: PodOption<u64> = Some(4).into();
assert_eq!(Option::from(pod_option), Some(4));

let pod_option: PodOption<i8> = Some(-123).into();
assert_eq!(Option::from(pod_option), Some(-123));

let pod_option: PodOption<Pubkey> = Some(Pubkey::default()).into();
assert_eq!(Option::from(pod_option), Some(Pubkey::default()));
}

#[test]
pub fn test_nones() {
let pod_option: PodOption<u64> = None.into();
assert_eq!(Option::<u64>::from(pod_option), None);

let pod_option: PodOption<i8> = None.into();
assert_eq!(Option::<i8>::from(pod_option), None);

let pod_option: PodOption<Pubkey> = None.into();
assert_eq!(Option::<Pubkey>::from(pod_option), None);
}
}

0 comments on commit 232da79

Please sign in to comment.