use super::pod::{FixedLayout, Pod};
use hopper_runtime::error::ProgramError;
pub const SEGMENT_DESC_SIZE: usize = 12;
pub const MAX_SEGMENTS: usize = 256;
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct SegmentDescriptor {
offset_bytes: [u8; 4],
count_bytes: [u8; 2],
capacity_bytes: [u8; 2],
element_size_bytes: [u8; 2],
flags_bytes: [u8; 2],
}
const _: () = assert!(core::mem::size_of::<SegmentDescriptor>() == SEGMENT_DESC_SIZE);
const _: () = assert!(core::mem::align_of::<SegmentDescriptor>() == 1);
#[cfg(feature = "hopper-native-backend")]
unsafe impl ::hopper_runtime::__hopper_native::bytemuck::Zeroable for SegmentDescriptor {}
#[cfg(feature = "hopper-native-backend")]
unsafe impl ::hopper_runtime::__hopper_native::bytemuck::Pod for SegmentDescriptor {}
unsafe impl Pod for SegmentDescriptor {}
unsafe impl ::hopper_runtime::__sealed::HopperZeroCopySealed for SegmentDescriptor {}
impl FixedLayout for SegmentDescriptor {
const SIZE: usize = SEGMENT_DESC_SIZE;
}
impl SegmentDescriptor {
#[inline(always)]
pub fn offset(&self) -> u32 {
u32::from_le_bytes(self.offset_bytes)
}
#[inline(always)]
pub fn count(&self) -> u16 {
u16::from_le_bytes(self.count_bytes)
}
#[inline(always)]
pub fn capacity(&self) -> u16 {
u16::from_le_bytes(self.capacity_bytes)
}
#[inline(always)]
pub fn element_size(&self) -> u16 {
u16::from_le_bytes(self.element_size_bytes)
}
#[inline(always)]
pub fn flags(&self) -> u16 {
u16::from_le_bytes(self.flags_bytes)
}
#[inline(always)]
pub fn is_full(&self) -> bool {
self.count() >= self.capacity()
}
#[inline(always)]
pub fn data_len(&self) -> usize {
(self.count() as usize) * (self.element_size() as usize)
}
#[inline(always)]
pub fn allocated_len(&self) -> usize {
(self.capacity() as usize) * (self.element_size() as usize)
}
#[inline(always)]
pub fn set_count(&mut self, count: u16) {
self.count_bytes = count.to_le_bytes();
}
#[inline(always)]
pub fn set_offset(&mut self, offset: u32) {
self.offset_bytes = offset.to_le_bytes();
}
#[inline(always)]
pub fn set_capacity(&mut self, capacity: u16) {
self.capacity_bytes = capacity.to_le_bytes();
}
#[inline(always)]
pub fn set_element_size(&mut self, size: u16) {
self.element_size_bytes = size.to_le_bytes();
}
}
pub struct SegmentTable<'a> {
data: &'a [u8],
count: usize,
}
impl<'a> SegmentTable<'a> {
#[inline]
pub fn from_bytes(data: &'a [u8], count: usize) -> Result<Self, ProgramError> {
if data.len() < count * SEGMENT_DESC_SIZE {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(Self { data, count })
}
#[inline(always)]
pub fn segment_count(&self) -> usize {
self.count
}
#[inline(always)]
pub fn descriptor(&self, index: usize) -> Result<&SegmentDescriptor, ProgramError> {
if index >= self.count {
return Err(ProgramError::InvalidArgument);
}
let offset = index * SEGMENT_DESC_SIZE;
Ok(unsafe { &*(self.data.as_ptr().add(offset) as *const SegmentDescriptor) })
}
}
pub struct SegmentTableMut<'a> {
data: &'a mut [u8],
count: usize,
}
impl<'a> SegmentTableMut<'a> {
#[inline]
pub fn from_bytes_mut(data: &'a mut [u8], count: usize) -> Result<Self, ProgramError> {
if data.len() < count * SEGMENT_DESC_SIZE {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(Self { data, count })
}
#[inline(always)]
pub fn descriptor_mut(&mut self, index: usize) -> Result<&mut SegmentDescriptor, ProgramError> {
if index >= self.count {
return Err(ProgramError::InvalidArgument);
}
let offset = index * SEGMENT_DESC_SIZE;
Ok(unsafe { &mut *(self.data.as_mut_ptr().add(offset) as *mut SegmentDescriptor) })
}
#[inline]
pub fn init(&mut self, data_start: u32, specs: &[(u16, u16, u16)]) -> Result<(), ProgramError> {
if specs.len() > self.count {
return Err(ProgramError::InvalidArgument);
}
let mut current_offset = data_start;
for (i, &(element_size, count, capacity)) in specs.iter().enumerate() {
let desc = self.descriptor_mut(i)?;
desc.set_offset(current_offset);
desc.set_count(count);
desc.set_capacity(capacity);
desc.set_element_size(element_size);
current_offset += (capacity as u32) * (element_size as u32);
}
Ok(())
}
}
pub struct SegmentSlice<'a, T: Pod + FixedLayout> {
data: &'a [u8],
count: usize,
_phantom: core::marker::PhantomData<T>,
}
impl<'a, T: Pod + FixedLayout> SegmentSlice<'a, T> {
#[inline]
pub fn from_descriptor(
account_data: &'a [u8],
desc: &SegmentDescriptor,
) -> Result<Self, ProgramError> {
let offset = desc.offset() as usize;
let count = desc.count() as usize;
let needed = offset + count * T::SIZE;
if needed > account_data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(Self {
data: &account_data[offset..],
count,
_phantom: core::marker::PhantomData,
})
}
#[inline(always)]
pub fn len(&self) -> usize {
self.count
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.count == 0
}
#[inline(always)]
pub fn read(&self, index: usize) -> Result<T, ProgramError> {
if index >= self.count {
return Err(ProgramError::InvalidArgument);
}
let offset = index * T::SIZE;
Ok(unsafe { core::ptr::read_unaligned(self.data.as_ptr().add(offset) as *const T) })
}
#[inline(always)]
pub fn get(&self, index: usize) -> Result<&T, ProgramError> {
if index >= self.count {
return Err(ProgramError::InvalidArgument);
}
let offset = index * T::SIZE;
Ok(unsafe { &*(self.data.as_ptr().add(offset) as *const T) })
}
}
pub struct SegmentSliceMut<'a, T: Pod + FixedLayout> {
data: &'a mut [u8],
count: usize,
capacity: usize,
_phantom: core::marker::PhantomData<T>,
}
impl<'a, T: Pod + FixedLayout> SegmentSliceMut<'a, T> {
#[inline]
pub fn from_descriptor(
account_data: &'a mut [u8],
desc: &SegmentDescriptor,
) -> Result<Self, ProgramError> {
let offset = desc.offset() as usize;
let count = desc.count() as usize;
let capacity = desc.capacity() as usize;
let needed = offset + capacity * T::SIZE;
if needed > account_data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(Self {
data: &mut account_data[offset..],
count,
capacity,
_phantom: core::marker::PhantomData,
})
}
#[inline(always)]
pub fn len(&self) -> usize {
self.count
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.count == 0
}
#[inline(always)]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline(always)]
pub fn read(&self, index: usize) -> Result<T, ProgramError> {
if index >= self.count {
return Err(ProgramError::InvalidArgument);
}
let offset = index * T::SIZE;
Ok(unsafe { core::ptr::read_unaligned(self.data.as_ptr().add(offset) as *const T) })
}
#[inline(always)]
pub fn write(&mut self, index: usize, value: T) -> Result<(), ProgramError> {
if index >= self.count {
return Err(ProgramError::InvalidArgument);
}
let offset = index * T::SIZE;
unsafe {
core::ptr::write_unaligned(self.data.as_mut_ptr().add(offset) as *mut T, value);
}
Ok(())
}
#[inline]
pub fn swap(&mut self, i: usize, j: usize) -> Result<(), ProgramError> {
if i >= self.count || j >= self.count {
return Err(ProgramError::InvalidArgument);
}
if i == j {
return Ok(());
}
let size = T::SIZE;
let oi = i * size;
let oj = j * size;
for k in 0..size {
self.data.swap(oi + k, oj + k);
}
Ok(())
}
}