1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use core::marker::PhantomData;

#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
/// A location in memory starting at `ADDR` and ending at `ADDR + SIZE`.
/// This is a zero-sized type to encode addresses that are known at compile time.
pub struct MemLoc<const ADDR: usize, const SIZE: usize>;

impl<const ADDR: usize, const SIZE: usize> MemLoc<ADDR, SIZE> {
    /// Creates a new memory location.
    pub const fn new() -> Self {
        Self
    }
    /// Returns the address of the memory location.
    pub const fn addr(&self) -> usize {
        ADDR
    }

    /// Returns the size of the memory location.
    pub const fn size(&self) -> usize {
        SIZE
    }

    /// Returns the range represented by the memory location.
    pub const fn range(&self) -> core::ops::Range<usize> {
        ADDR..ADDR + SIZE
    }
}

/// Trait that combines a memory location and a type.
pub trait MemLocType<const ADDR: usize, const SIZE: usize> {
    /// The type at this memory location.
    type Type;

    /// The memory locations address.
    const ADDR: usize = ADDR;

    /// The memory locations size.
    const SIZE: usize = SIZE;

    /// The memory location.
    const LOC: MemLoc<ADDR, SIZE> = MemLoc::new();

    /// Combine a memory location and a type.
    /// This will only work if this trait is defined for the memory location.
    fn layout(loc: MemLoc<ADDR, SIZE>) -> LayoutType<ADDR, SIZE, Self> {
        LayoutType(loc, PhantomData)
    }
}

/// A memory location combined with a type.
pub struct LayoutType<const ADDR: usize, const SIZE: usize, T>(MemLoc<ADDR, SIZE>, PhantomData<T>)
where
    T: MemLocType<ADDR, SIZE> + ?Sized;

impl<const ADDR: usize, const SIZE: usize, T> LayoutType<ADDR, SIZE, T>
where
    T: MemLocType<ADDR, SIZE>,
{
    /// Create a new layout type.
    pub const fn new() -> Self {
        Self(MemLoc::new(), PhantomData)
    }
    /// The memory location of this type.
    pub const fn loc(&self) -> MemLoc<ADDR, SIZE> {
        self.0
    }
}

/// Trait that defines a memory layout.
pub trait MemLayout {
    /// The associated memory layout type.
    type Type;
    /// A constant instance of the memory layout.
    const LAYOUT: Self::Type;
    /// The length of the memory layout.
    const LEN: usize;
}

#[macro_export]
/// Defines a memory layout for a type.
/// The address starts at 0 and is incremented by the size of each field.
/// The syntax is `field_name: field_type = field_size_in_bytes, ...`.
macro_rules! mem_layout {
    () => {};
    (@accum () -> ($s:ident for $o:ident $($f:ident: $t:ty = $si:expr, $a:expr);*) -> ($($addr:tt)*)) => {
        mem_layout!(@as_expr ($s $o $($f, $si, $t, $a)*) -> ($($addr)*));
    };
    (@accum ($field:ident: $t:ty = $size:expr, $($tail:tt)*) -> ($s:ident for $o:ident $($f:ident: $typ:ty = $si:expr, $a:expr);*) -> ($($addr:tt)*)) => {
        mem_layout!(@accum ($($tail)*) -> ($s for $o $($f: $typ = $si, $a);*; $field: $t = $size, $($addr)*) -> ($($addr)* + $size ));

    };
    (@as_expr ($s:ident $o:ident $($field:ident, $size:expr, $t:ty, $addr:expr)+) -> ($($len:tt)*)) => {
        #[derive(Debug, Default)]
        #[allow(missing_docs)]
        pub struct $s {
            $(pub $field: $crate::MemLoc<{$addr}, $size>,)+
        }
        impl $s {
            #[allow(missing_docs)]
            pub const fn new() -> Self {
                Self {
                    $($field: $crate::MemLoc::new(),)+
                }
            }
            #[allow(missing_docs)]
            pub const LEN: usize = $($len)*;
        }
        impl $crate::MemLayout for $o {
            type Type = $s;
            const LAYOUT: Self::Type = $s::new();
            const LEN: usize = Self::Type::LEN;
        }
        $(
            impl $crate::MemLocType<{ <$o as $crate::MemLayout>::LAYOUT.$field.addr() }, { <$o as $crate::MemLayout>::LAYOUT.$field.size() }> for $o {
                type Type = $t;
            }
        )+

    };
    ($s:ident for $o:ident $field:ident: $t:ty = $size:expr, $($tail:tt)*) => {
        mem_layout!(@accum ($($tail)*,) -> ($s for $o $field: $t = $size, 0) -> (0 + $size));
    };
}