use crate::error::ProgramError;
use crate::ProgramResult;
pub const COMPACT_BODY_OFFSET: usize = 1;
pub trait CompactLayout: Sized + Copy + crate::Pod {
const DISC: u8;
const BODY_SIZE: usize = core::mem::size_of::<Self>();
const COMPACT_LEN: usize = COMPACT_BODY_OFFSET + Self::BODY_SIZE;
#[inline(always)]
fn validate_compact(data: &[u8]) -> ProgramResult {
if data.len() < Self::COMPACT_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
if data.len() != Self::COMPACT_LEN {
return Err(ProgramError::InvalidAccountData);
}
if data[0] != Self::DISC {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
}
pub trait CompactDynamicLayout: Sized + Copy + crate::Pod {
const DISC: u8;
const FIXED_HEAD_SIZE: usize = core::mem::size_of::<Self>();
const MIN_LEN: usize = COMPACT_BODY_OFFSET + Self::FIXED_HEAD_SIZE;
const TAIL_OFFSET: usize = Self::MIN_LEN;
#[inline(always)]
fn validate_compact_dynamic(data: &[u8]) -> ProgramResult {
if data.len() < Self::MIN_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
if data[0] != Self::DISC {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pod::{Pod, Zeroable};
#[repr(C)]
#[derive(Clone, Copy)]
struct Body {
authority: [u8; 32],
balance: [u8; 8],
}
unsafe impl Zeroable for Body {}
unsafe impl Pod for Body {}
impl CompactLayout for Body {
const DISC: u8 = 7;
}
#[test]
fn compact_len_is_one_plus_body() {
assert_eq!(Body::BODY_SIZE, 40);
assert_eq!(Body::COMPACT_LEN, 41);
}
#[test]
fn validate_checks_len_and_disc() {
let mut buf = [0u8; 41];
buf[0] = 7;
assert!(Body::validate_compact(&buf).is_ok());
buf[0] = 8;
assert!(matches!(
Body::validate_compact(&buf),
Err(ProgramError::InvalidAccountData)
));
buf[0] = 7;
assert!(matches!(
Body::validate_compact(&buf[..40]),
Err(ProgramError::AccountDataTooSmall)
));
let mut oversized = [0u8; 42];
oversized[0] = 7;
assert!(matches!(
Body::validate_compact(&oversized),
Err(ProgramError::InvalidAccountData)
));
}
}