wasm-static-alloc 0.1.0

Statically allocate memory and encode them to WASM data section.
Documentation
struct Entry {
    alignment: usize,
    data:      Vec<u8>,
}

pub struct Stack {
    entries: Vec<Entry>,
}

impl Stack {
    pub fn new() -> Self {
        Self { entries: vec![] }
    }

    pub fn push<D>(&mut self, data: D, alignment: usize)
    where
        D: IntoIterator<Item = u8>,
        D::IntoIter: ExactSizeIterator,
    {
        self.entries.push(Entry {
            alignment,
            data: data.into_iter().collect(),
        });
    }

    pub fn iter_entry(&self) -> DataEntryIterator {
        DataEntryIterator {
            stack:           self,
            next_idx:        0,
            prev_end_offset: 0,
        }
    }

    pub fn memory_pages_needed(&self) -> usize {
        const PAGE_SIZE_BYTES: usize = 65536;

        let entry = match self.iter_entry().last() {
            | None => return 0,
            | Some(e) => e,
        };
        let data_len = entry.offset + entry.data.len();
        let extra_page_needed = data_len % PAGE_SIZE_BYTES > 0;

        (entry.offset + entry.data.len()) / PAGE_SIZE_BYTES + if extra_page_needed { 1 } else { 0 }
    }
}

pub struct DataEntryIterator<'a> {
    stack:           &'a Stack,
    next_idx:        usize,
    prev_end_offset: usize,
}

impl<'a> Iterator for DataEntryIterator<'a> {
    type Item = DataEntry<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        let entry = self.stack.entries.get(self.next_idx)?;
        let offset = self.prev_end_offset
            + (entry.alignment - self.prev_end_offset % entry.alignment) % entry.alignment;

        self.next_idx += 1;
        self.prev_end_offset = offset + entry.data.len();

        Some(DataEntry {
            offset,
            data: &entry.data,
        })
    }
}

impl ExactSizeIterator for DataEntryIterator<'_> {
    fn len(&self) -> usize {
        self.stack.entries.len()
    }
}

#[derive(PartialEq, Eq, Clone, Debug)]
pub struct DataEntry<'a> {
    pub offset: usize,
    pub data:   &'a [u8],
}

impl Default for Stack {
    fn default() -> Self {
        Self::new()
    }
}

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

    #[test]
    fn ok() {
        let mut stack = Stack::default();

        stack.push([1; 4], 4);
        stack.push([2; 5], 1);
        stack.push([3; 4], 4);
        stack.push([4; 65536], 8);

        let mut entries = stack.iter_entry();
        let expected_entries = [
            DataEntry {
                offset: 0,
                data:   &[1; 4],
            },
            DataEntry {
                offset: 4,
                data:   &[2; 5],
            },
            DataEntry {
                offset: 12,
                data:   &[3; 4],
            },
            DataEntry {
                offset: 16,
                data:   &[4; 65536],
            },
        ];

        for expected_entry in expected_entries {
            let entry = entries.next().unwrap();

            assert_eq!(entry, expected_entry);
        }

        assert_eq!(stack.memory_pages_needed(), 2);
    }
}