#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StaticSegment {
pub name: &'static str,
pub offset: u32,
pub size: u32,
}
impl StaticSegment {
#[inline(always)]
pub const fn new(name: &'static str, offset: u32, size: u32) -> Self {
Self { name, offset, size }
}
#[inline(always)]
pub const fn end(&self) -> u32 {
self.offset + self.size
}
}
pub trait SegmentMap {
const SEGMENTS: &'static [StaticSegment];
#[inline]
fn segment(name: &str) -> Option<StaticSegment> {
let mut i = 0;
while i < Self::SEGMENTS.len() {
let seg = Self::SEGMENTS[i];
if const_str_eq(seg.name, name) {
return Some(seg);
}
i += 1;
}
None
}
#[inline]
fn body_size() -> u32 {
let segments = Self::SEGMENTS;
if segments.is_empty() {
return 0;
}
let last = segments[segments.len() - 1];
last.offset + last.size
}
#[inline(always)]
fn segment_count() -> usize {
Self::SEGMENTS.len()
}
#[inline(always)]
fn segment_by_index(index: usize) -> StaticSegment {
Self::SEGMENTS[index]
}
}
#[inline(always)]
const fn const_str_eq(a: &str, b: &str) -> bool {
let a = a.as_bytes();
let b = b.as_bytes();
if a.len() != b.len() {
return false;
}
let mut i = 0;
while i < a.len() {
if a[i] != b[i] {
return false;
}
i += 1;
}
true
}
pub const fn assert_segment_field_alignment<T: SegmentMap + hopper_runtime::field_map::FieldMap>(
header_offset: usize,
) {
let segments = T::SEGMENTS;
let fields = T::FIELDS;
assert!(
segments.len() == fields.len(),
"SegmentMap and FieldMap have different field counts"
);
let mut i = 0;
while i < segments.len() {
let seg = &segments[i];
let field = &fields[i];
assert!(
seg.offset as usize == field.offset.wrapping_sub(header_offset),
"SegmentMap/FieldMap offset mismatch"
);
assert!(
seg.size as usize == field.size,
"SegmentMap/FieldMap size mismatch"
);
assert!(
const_str_eq(seg.name, field.name),
"SegmentMap/FieldMap name mismatch"
);
i += 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestLayout;
impl SegmentMap for TestLayout {
const SEGMENTS: &'static [StaticSegment] = &[
StaticSegment::new("authority", 0, 32),
StaticSegment::new("balance", 32, 8),
StaticSegment::new("bump", 40, 1),
];
}
#[test]
fn lookup_by_name() {
let seg = TestLayout::segment("balance").unwrap();
assert_eq!(seg.offset, 32);
assert_eq!(seg.size, 8);
}
#[test]
fn lookup_missing() {
assert!(TestLayout::segment("nonexistent").is_none());
}
#[test]
fn body_size_computed() {
assert_eq!(TestLayout::body_size(), 41);
}
#[test]
fn segment_count() {
assert_eq!(TestLayout::segment_count(), 3);
}
#[test]
fn segment_end() {
let seg = TestLayout::segment("authority").unwrap();
assert_eq!(seg.end(), 32);
}
#[test]
fn segment_by_index_constant_access() {
let seg = TestLayout::segment_by_index(1);
assert_eq!(seg.name, "balance");
assert_eq!(seg.offset, 32);
assert_eq!(seg.size, 8);
}
impl hopper_runtime::field_map::FieldMap for TestLayout {
const FIELDS: &'static [hopper_runtime::field_map::FieldInfo] = &[
hopper_runtime::field_map::FieldInfo::new("authority", 16, 32),
hopper_runtime::field_map::FieldInfo::new("balance", 48, 8),
hopper_runtime::field_map::FieldInfo::new("bump", 56, 1),
];
}
const _: () = assert_segment_field_alignment::<TestLayout>(16);
}