#[doc(hidden)]
#[macro_export]
macro_rules! __gen_offsets {
(@acc $offset:expr ;) => {};
(@acc $offset:expr ; $field:ident = $fsize:expr, $( $rest_field:ident = $rest_fsize:expr, )*) => {
#[allow(non_upper_case_globals)]
#[doc(hidden)]
pub const $field: usize = $offset;
$crate::__gen_offsets!(@acc ($offset + $fsize) ; $( $rest_field = $rest_fsize, )*);
};
( $( $field:ident = $fsize:expr ),+ $(,)? ) => {
$crate::__gen_offsets!(@acc 0usize ; $( $field = $fsize, )+);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __gen_offset_const {
($field:ident, $offset:expr) => {
#[doc = concat!("Byte offset of the `", stringify!($field), "` field.")]
#[allow(non_upper_case_globals)]
pub const $field: usize = $offset;
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __field_ref_type {
($ignore:ident) => { $crate::abi::FieldRef<'_> };
}
#[doc(hidden)]
#[macro_export]
macro_rules! __field_mut_type {
($ignore:ident) => { $crate::abi::FieldMut<'_> };
}
#[doc(hidden)]
#[macro_export]
macro_rules! __canonical_type {
(u8) => { "u8" };
(u16) => { "u16" };
(u32) => { "u32" };
(u64) => { "u64" };
(u128) => { "u128" };
(i8) => { "i8" };
(i16) => { "i16" };
(i32) => { "i32" };
(i64) => { "i64" };
(i128) => { "i128" };
(bool) => { "bool" };
(Address) => { "pubkey" };
(AccountHeader) => { "header" };
(LeU16) => { "u16" };
(LeU32) => { "u32" };
(LeU64) => { "u64" };
(LeU128) => { "u128" };
(LeI16) => { "i16" };
(LeI32) => { "i32" };
(LeI64) => { "i64" };
(LeI128) => { "i128" };
(LeBool) => { "bool" };
($other:ident) => { stringify!($other) };
}
#[macro_export]
macro_rules! zero_copy_layout {
(
$(#[$meta:meta])*
$vis:vis struct $name:ident, discriminator = $disc:literal, version = $ver:literal {
$( $(#[$fmeta:meta])* $field:ident : $fty:ident = $fsize:expr ),+ $(,)?
}
) => {
$(#[$meta])*
#[repr(C)]
#[derive(Clone, Copy)]
$vis struct $name {
$( $(#[$fmeta])* pub $field: $fty ),+
}
unsafe impl $crate::account::Pod for $name {}
impl $crate::account::FixedLayout for $name {
const SIZE: usize = 0 $( + $fsize )+;
}
const _: () = assert!(
core::mem::size_of::<$name>() == 0 $( + $fsize )+,
"size_of does not match declared LEN - check field sizes"
);
const _: () = assert!(
core::mem::align_of::<$name>() <= 8,
"layout alignment exceeds 8 bytes - use Le* wrappers for u128 fields"
);
impl $name {
pub const LEN: usize = 0 $( + $fsize )+;
pub const DISC: u8 = $disc;
pub const VERSION: u8 = $ver;
pub const LAYOUT_ID: [u8; 8] = {
const INPUT: &str = concat!(
"jiminy:v1:",
stringify!($name), ":",
stringify!($ver), ":",
$( stringify!($field), ":", $crate::__canonical_type!($fty), ":", stringify!($fsize), ",", )+
);
const HASH: [u8; 32] = $crate::__sha256_const(INPUT.as_bytes());
[HASH[0], HASH[1], HASH[2], HASH[3], HASH[4], HASH[5], HASH[6], HASH[7]]
};
#[inline(always)]
pub fn overlay(data: &[u8]) -> Result<&Self, $crate::ProgramError> {
$crate::account::pod_from_bytes::<Self>(data)
}
#[inline(always)]
pub fn overlay_mut(data: &mut [u8]) -> Result<&mut Self, $crate::ProgramError> {
$crate::account::pod_from_bytes_mut::<Self>(data)
}
#[inline(always)]
pub fn read(data: &[u8]) -> Result<Self, $crate::ProgramError> {
$crate::account::pod_read::<Self>(data)
}
#[inline(always)]
pub fn load_checked(data: &[u8]) -> Result<&Self, $crate::ProgramError> {
$crate::account::check_header(data, Self::DISC, Self::VERSION, &Self::LAYOUT_ID)?;
$crate::account::pod_from_bytes::<Self>(data)
}
#[inline(always)]
pub fn load_checked_mut(data: &mut [u8]) -> Result<&mut Self, $crate::ProgramError> {
$crate::account::check_header(data, Self::DISC, Self::VERSION, &Self::LAYOUT_ID)?;
$crate::account::pod_from_bytes_mut::<Self>(data)
}
#[inline(always)]
pub fn load<'a>(
account: &'a $crate::AccountView,
program_id: &$crate::Address,
) -> Result<$crate::account::VerifiedAccount<'a, Self>, $crate::ProgramError> {
let data = $crate::account::view::validate_account(
account, program_id, Self::DISC, Self::VERSION, &Self::LAYOUT_ID, Self::LEN,
)?;
$crate::account::VerifiedAccount::new(data)
}
#[inline(always)]
pub fn load_mut<'a>(
account: &'a $crate::AccountView,
program_id: &$crate::Address,
) -> Result<$crate::account::VerifiedAccountMut<'a, Self>, $crate::ProgramError> {
let data = $crate::account::view::validate_account_mut(
account, program_id, Self::DISC, Self::VERSION, &Self::LAYOUT_ID, Self::LEN,
)?;
$crate::account::VerifiedAccountMut::new(data)
}
#[inline(always)]
pub fn load_foreign<'a>(
account: &'a $crate::AccountView,
expected_owner: &$crate::Address,
) -> Result<$crate::account::VerifiedAccount<'a, Self>, $crate::ProgramError> {
let data = $crate::account::view::validate_foreign(
account, expected_owner, &Self::LAYOUT_ID, Self::LEN,
)?;
$crate::account::VerifiedAccount::new(data)
}
#[inline(always)]
pub unsafe fn load_unchecked(data: &[u8]) -> Result<&Self, $crate::ProgramError> {
$crate::account::pod_from_bytes::<Self>(data)
}
#[inline(always)]
pub fn load_unverified_overlay(data: &[u8]) -> Result<(&Self, bool), $crate::ProgramError> {
$crate::account::view::load_unverified_overlay::<Self>(
data, Self::DISC, Self::VERSION, &Self::LAYOUT_ID,
)
}
$crate::__gen_offsets!( $( $field = $fsize ),+ );
#[inline]
#[allow(unused_variables)]
pub fn split_fields(data: &[u8]) -> Result<( $( $crate::__field_ref_type!($field), )+ ), $crate::ProgramError> {
if data.len() < Self::LEN {
return Err($crate::ProgramError::AccountDataTooSmall);
}
let mut _pos = 0usize;
Ok(( $({
let start = _pos;
_pos += $fsize;
$crate::abi::FieldRef::new(&data[start..start + $fsize])
}, )+ ))
}
#[inline]
#[allow(unused_variables)]
pub fn split_fields_mut(data: &mut [u8]) -> Result<( $( $crate::__field_mut_type!($field), )+ ), $crate::ProgramError> {
if data.len() < Self::LEN {
return Err($crate::ProgramError::AccountDataTooSmall);
}
let _remaining = &mut data[..Self::LEN];
$(
let ($field, _remaining) = _remaining.split_at_mut($fsize);
)+
Ok(( $( $crate::abi::FieldMut::new($field), )+ ))
}
}
};
(
$(#[$meta:meta])*
$vis:vis struct $name:ident, discriminator = $disc:literal, version = $ver:literal, extends = $parent:ty {
$( $(#[$fmeta:meta])* $field:ident : $fty:ident = $fsize:expr ),+ $(,)?
}
) => {
$crate::zero_copy_layout! {
$(#[$meta])*
$vis struct $name, discriminator = $disc, version = $ver {
$( $(#[$fmeta])* $field : $fty = $fsize ),+
}
}
const _: () = assert!(
<$name>::DISC == <$parent>::DISC,
"extends: child and parent must share the same discriminator"
);
const _: () = assert!(
<$name>::LEN >= <$parent>::LEN,
"extends: child layout must be at least as large as parent (append-only)"
);
const _: () = assert!(
<$name>::VERSION > <$parent>::VERSION,
"extends: child version must be strictly greater than parent version"
);
};
}
#[macro_export]
macro_rules! assert_legacy_layout {
($ty:ty, $size:expr $(,)?) => {
$crate::assert_legacy_layout!(@inner $ty, $size, 8usize);
};
($ty:ty, size = $size:expr $(,)?) => {
$crate::assert_legacy_layout!(@inner $ty, $size, 8usize);
};
($ty:ty, size = $size:expr, max_align = $max_align:expr $(,)?) => {
$crate::assert_legacy_layout!(@inner $ty, $size, $max_align);
};
(@inner $ty:ty, $size:expr, $max_align:expr) => {
const _: fn() = {
fn __jiminy_assert_legacy_layout<T: $crate::account::Pod + $crate::account::FixedLayout>() {}
__jiminy_assert_legacy_layout::<$ty>
};
const _: () = assert!(
core::mem::size_of::<$ty>() == $size,
"legacy layout size_of does not match declared size"
);
const _: () = assert!(
<$ty as $crate::account::FixedLayout>::SIZE == $size,
"legacy layout FixedLayout::SIZE does not match declared size"
);
const _: () = assert!(
core::mem::align_of::<$ty>() <= $max_align,
"legacy layout alignment exceeds configured maximum"
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __count_segments {
() => { 0usize };
($head:ident $($tail:ident)*) => { 1usize + $crate::__count_segments!($($tail)*) };
}
#[doc(hidden)]
#[macro_export]
macro_rules! __gen_segment_indices {
(@acc $offset:expr ;) => {};
(@acc $offset:expr ; $seg_name:ident, $( $rest:ident, )*) => {
#[doc = concat!("Index of the `", stringify!($seg_name), "` segment in the segment table.")]
#[allow(non_upper_case_globals)]
pub const $seg_name: usize = $offset;
$crate::__gen_segment_indices!(@acc ($offset + 1usize) ; $( $rest, )*);
};
($( $seg_name:ident ),+ $(,)?) => {
$crate::__gen_segment_indices!(@acc 0usize ; $( $seg_name, )+);
};
}
#[macro_export]
macro_rules! segmented_layout {
(
$(#[$meta:meta])*
$vis:vis struct $name:ident, discriminator = $disc:literal, version = $ver:literal {
$( $(#[$fmeta:meta])* $field:ident : $fty:ident = $fsize:expr ),+ $(,)?
} segments {
$( $seg_name:ident : $seg_ty:ident = $seg_elem_size:expr ),+ $(,)?
}
) => {
$crate::zero_copy_layout! {
$(#[$meta])*
$vis struct $name, discriminator = $disc, version = $ver {
$( $(#[$fmeta])* $field : $fty = $fsize ),+
}
}
impl $name {
pub const SEGMENT_COUNT: usize = $crate::__count_segments!($($seg_name)+);
pub const FIXED_LEN: usize = Self::LEN;
pub const TABLE_OFFSET: usize = Self::LEN;
pub const DATA_START_OFFSET: usize =
Self::LEN + Self::SEGMENT_COUNT * $crate::account::segment::SEGMENT_DESC_SIZE;
pub const SEGMENTED_LAYOUT_ID: [u8; 8] = {
const INPUT: &str = concat!(
"jiminy:v1:",
stringify!($name), ":",
stringify!($ver), ":",
$( stringify!($field), ":", $crate::__canonical_type!($fty), ":", stringify!($fsize), ",", )+
$( "seg:", stringify!($seg_name), ":", stringify!($seg_ty), ":", stringify!($seg_elem_size), ",", )+
);
const HASH: [u8; 32] = $crate::__sha256_const(INPUT.as_bytes());
[HASH[0], HASH[1], HASH[2], HASH[3], HASH[4], HASH[5], HASH[6], HASH[7]]
};
#[inline(always)]
pub const fn segment_sizes() -> &'static [u16] {
$(
const _: () = assert!(
<$seg_ty as $crate::account::FixedLayout>::SIZE == $seg_elem_size,
"segmented_layout! segment size does not match type FixedLayout::SIZE"
);
)+
&[ $( <$seg_ty as $crate::account::FixedLayout>::SIZE as u16, )+ ]
}
pub const MIN_ACCOUNT_SIZE: usize = Self::DATA_START_OFFSET;
#[inline(always)]
pub fn segment_table(data: &[u8]) -> Result<$crate::account::segment::SegmentTable<'_>, $crate::ProgramError> {
if data.len() < Self::DATA_START_OFFSET {
return Err($crate::ProgramError::AccountDataTooSmall);
}
$crate::account::segment::SegmentTable::from_bytes(
&data[Self::TABLE_OFFSET..],
Self::SEGMENT_COUNT,
)
}
#[inline(always)]
pub fn segment_table_mut(data: &mut [u8]) -> Result<$crate::account::segment::SegmentTableMut<'_>, $crate::ProgramError> {
if data.len() < Self::DATA_START_OFFSET {
return Err($crate::ProgramError::AccountDataTooSmall);
}
$crate::account::segment::SegmentTableMut::from_bytes(
&mut data[Self::TABLE_OFFSET..],
Self::SEGMENT_COUNT,
)
}
#[inline]
pub fn validate_segments(data: &[u8]) -> Result<(), $crate::ProgramError> {
let table = Self::segment_table(data)?;
table.validate(data.len(), Self::segment_sizes(), Self::DATA_START_OFFSET)
}
#[inline]
pub fn init_segments(
data: &mut [u8],
counts: &[u16],
) -> Result<(), $crate::ProgramError> {
if counts.len() != Self::SEGMENT_COUNT {
return Err($crate::ProgramError::InvalidArgument);
}
let sizes = Self::segment_sizes();
let specs: [_; Self::SEGMENT_COUNT] = {
let mut arr = [(0u16, 0u16, 0u16); Self::SEGMENT_COUNT];
let mut i = 0;
while i < Self::SEGMENT_COUNT {
arr[i] = (sizes[i], counts[i], counts[i]);
i += 1;
}
arr
};
$crate::account::segment::SegmentTableMut::init(
&mut data[Self::TABLE_OFFSET..],
Self::DATA_START_OFFSET as u32,
&specs,
)?;
Ok(())
}
#[inline]
pub fn init_segments_with_capacity(
data: &mut [u8],
capacities: &[u16],
) -> Result<(), $crate::ProgramError> {
if capacities.len() != Self::SEGMENT_COUNT {
return Err($crate::ProgramError::InvalidArgument);
}
let sizes = Self::segment_sizes();
let specs: [_; Self::SEGMENT_COUNT] = {
let mut arr = [(0u16, 0u16, 0u16); Self::SEGMENT_COUNT];
let mut i = 0;
while i < Self::SEGMENT_COUNT {
arr[i] = (sizes[i], 0, capacities[i]);
i += 1;
}
arr
};
$crate::account::segment::SegmentTableMut::init(
&mut data[Self::TABLE_OFFSET..],
Self::DATA_START_OFFSET as u32,
&specs,
)?;
Ok(())
}
#[inline]
pub fn compute_account_size(capacities: &[u16]) -> Result<usize, $crate::ProgramError> {
if capacities.len() != Self::SEGMENT_COUNT {
return Err($crate::ProgramError::InvalidArgument);
}
let sizes = Self::segment_sizes();
let mut total = Self::DATA_START_OFFSET;
let mut i = 0;
while i < Self::SEGMENT_COUNT {
let seg_bytes = (capacities[i] as usize)
.checked_mul(sizes[i] as usize)
.ok_or($crate::ProgramError::ArithmeticOverflow)?;
total = total
.checked_add(seg_bytes)
.ok_or($crate::ProgramError::ArithmeticOverflow)?;
i += 1;
}
Ok(total)
}
$crate::__gen_segment_indices!($($seg_name),+);
#[inline(always)]
pub fn segment<T: $crate::account::Pod + $crate::account::FixedLayout>(
data: &[u8],
index: usize,
) -> Result<$crate::account::segment::SegmentSlice<'_, T>, $crate::ProgramError> {
let desc = {
let table = Self::segment_table(data)?;
table.descriptor(index)?
};
$crate::account::segment::SegmentSlice::from_descriptor(data, &desc)
}
#[inline(always)]
pub fn segment_mut<T: $crate::account::Pod + $crate::account::FixedLayout>(
data: &mut [u8],
index: usize,
) -> Result<$crate::account::segment::SegmentSliceMut<'_, T>, $crate::ProgramError> {
let desc = {
let table = Self::segment_table(data)?;
table.descriptor(index)?
};
$crate::account::segment::SegmentSliceMut::from_descriptor(data, &desc)
}
#[inline(always)]
pub fn push<T: $crate::account::Pod + $crate::account::FixedLayout>(
data: &mut [u8],
index: usize,
value: &T,
) -> Result<(), $crate::ProgramError> {
$crate::account::segment::segment_push::<T>(
data, Self::TABLE_OFFSET, Self::SEGMENT_COUNT, index, value,
)
}
#[inline(always)]
pub fn swap_remove<T: $crate::account::Pod + $crate::account::FixedLayout>(
data: &mut [u8],
index: usize,
elem_index: u16,
) -> Result<T, $crate::ProgramError> {
$crate::account::segment::segment_swap_remove::<T>(
data, Self::TABLE_OFFSET, Self::SEGMENT_COUNT, index, elem_index,
)
}
}
};
}