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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//!
//! This type defines types and traits useful for representing elements in untyped containers.
//!

use std::{
    any::{Any, TypeId},
    mem::{align_of, size_of},
};

macro_rules! generate_aligned_types {
    ($($t:ident($n:expr),)*) => {
	$(
	    #[derive(Copy, Clone, Debug, Default)]
	    #[repr(C, align($n))]
	    pub(crate) struct $t(u8);
	)*
    }
}

generate_aligned_types!(
    T0(1),
    T1(2),
    T2(4),
    T3(8),
    T4(16),
    T5(32),
    T6(64),
    T7(128),
    T8(256),
    T9(512),
    T10(1024),
    T11(2048),
    T12(4096),
    T13(8192),
    T14(16384),
    T15(32768),
    T16(65536),
    T17(131072),
    T18(262144),
    T19(524288),
    T20(1048576),
    T21(2097152),
    T22(4194304),
    T23(8388608),
    T24(16777216),
    T25(33554432),
    T26(67108864),
    T27(134217728),
    T28(268435456),
    T29(536870912),
);

#[macro_export]
macro_rules! eval_align {
    // To the best of my understanding:
    // Some operations will fail because some std algorithms will rely on the
    // stack, which may not be large enough to accomodate for large alignments.
    // The program will panic when attempting to use types with very large alignments.
    (limited_stack, $a:expr; $fn:ident ::<_ $(,$params:ident)*>($($args:expr),*) ) => {
	match $a {
	    1         => $fn::<T0, $($params,)*>($($args,)*),
	    2         => $fn::<T1, $($params,)*>($($args,)*),
	    4         => $fn::<T2, $($params,)*>($($args,)*),
	    8         => $fn::<T3, $($params,)*>($($args,)*),
	    16        => $fn::<T4, $($params,)*>($($args,)*),
	    32        => $fn::<T5, $($params,)*>($($args,)*),
	    64        => $fn::<T6, $($params,)*>($($args,)*),
	    128       => $fn::<T7, $($params,)*>($($args,)*),
	    256       => $fn::<T8, $($params,)*>($($args,)*),
	    512       => $fn::<T9, $($params,)*>($($args,)*),
	    1024      => $fn::<T10, $($params,)*>($($args,)*),
	    2048      => $fn::<T11, $($params,)*>($($args,)*),
	    4096      => $fn::<T12, $($params,)*>($($args,)*),
	    8192      => $fn::<T13, $($params,)*>($($args,)*),
	    a => unreachable!("Alignment is too large ({}) for vector manipulations", a)
	}
    };

    // Other algorithms seem to work fine even with large alignments.
    ($a:expr; $fn:ident ::<_ $(,$params:ident)*>($($args:expr),*) ) => {
	match $a {
	    1         => $fn::<T0, $($params,)*>($($args,)*),
	    2         => $fn::<T1, $($params,)*>($($args,)*),
	    4         => $fn::<T2, $($params,)*>($($args,)*),
	    8         => $fn::<T3, $($params,)*>($($args,)*),
	    16        => $fn::<T4, $($params,)*>($($args,)*),
	    32        => $fn::<T5, $($params,)*>($($args,)*),
	    64        => $fn::<T6, $($params,)*>($($args,)*),
	    128       => $fn::<T7, $($params,)*>($($args,)*),
	    256       => $fn::<T8, $($params,)*>($($args,)*),
	    512       => $fn::<T9, $($params,)*>($($args,)*),
	    1024      => $fn::<T10, $($params,)*>($($args,)*),
	    2048      => $fn::<T11, $($params,)*>($($args,)*),
	    4096      => $fn::<T12, $($params,)*>($($args,)*),
	    8192      => $fn::<T13, $($params,)*>($($args,)*),
	    16384     => $fn::<T14, $($params,)*>($($args,)*),
	    32768     => $fn::<T15, $($params,)*>($($args,)*),
	    65536     => $fn::<T16, $($params,)*>($($args,)*),
	    131072    => $fn::<T17, $($params,)*>($($args,)*),
	    262144    => $fn::<T18, $($params,)*>($($args,)*),
	    524288    => $fn::<T19, $($params,)*>($($args,)*),
	    1048576   => $fn::<T20, $($params,)*>($($args,)*),
	    2097152   => $fn::<T21, $($params,)*>($($args,)*),
	    4194304   => $fn::<T22, $($params,)*>($($args,)*),
	    8388608   => $fn::<T23, $($params,)*>($($args,)*),
	    16777216  => $fn::<T24, $($params,)*>($($args,)*),
	    33554432  => $fn::<T25, $($params,)*>($($args,)*),
	    67108864  => $fn::<T26, $($params,)*>($($args,)*),
	    134217728 => $fn::<T27, $($params,)*>($($args,)*),
	    268435456 => $fn::<T28, $($params,)*>($($args,)*),
	    536870912 => $fn::<T29, $($params,)*>($($args,)*),
	    _ => unreachable!("Unsupported alignment detected")
	}
    }
}

pub trait CopyElem: Any + Copy {}
impl<T> CopyElem for T where T: Any + Copy {}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ElemInfo {
    /// Type encoding for hiding the type of data from the compiler.
    pub(crate) type_id: TypeId,
    /// Number of alignment chunks occupied by an element of this buffer.
    ///
    /// The size of the element in bytes is then given by `size * alignment`.
    pub(crate) size: usize,
    /// Alignment info for an element stored in this `Vec`.
    pub(crate) alignment: usize,
}

impl ElemInfo {
    #[inline]
    pub fn new<T: 'static>() -> ElemInfo {
        ElemInfo {
            type_id: TypeId::of::<T>(),
            size: size_of::<T>() / align_of::<T>(),
            alignment: align_of::<T>(),
        }
    }

    #[inline]
    pub const fn num_bytes(self) -> usize {
        self.size * self.alignment
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn info_struct() {
        let elem_u32 = ElemInfo::new::<u32>();
        assert_eq!(
            elem_u32,
            ElemInfo {
                type_id: TypeId::of::<u32>(),
                size: 1,
                alignment: 4,
            }
        );

        assert_eq!(elem_u32.num_bytes(), 4);
    }
}