ngen 0.1.4

A very simple game engine using OpenGL
Documentation
use std::{
    alloc::{alloc, dealloc, Layout},
    cell::RefCell,
};

pub mod array;
pub use array::Array;

pub mod linked_list;
pub use linked_list::List;

const fn align_to(ptr: usize, align: usize) -> usize {
    (ptr + align - 1) & !(align - 1)
}

struct RawMemory {
    capacity: usize,
    memory: usize,
    next: usize,
}

impl RawMemory {
    fn with_capacity(capacity: usize) -> Result<Self, String> {
        unsafe {
            let mem = {
                let layout = Layout::array::<u8>(capacity).map_err(|e| e.to_string())?;
                alloc(layout)
            };

            if mem.is_null() {
                return Err("Memory allocation failed for the arena.".to_string());
            }

            Ok(Self {
                capacity,
                memory: mem as usize,
                next: mem as usize,
            })
        }
    }

    const fn end(&self) -> usize {
        self.memory + self.capacity
    }

    fn reset(&mut self) {
        self.next = self.memory
    }
}

impl Drop for RawMemory {
    fn drop(&mut self) {
        unsafe {
            if let Ok(layout) = Layout::array::<u8>(self.capacity) {
                dealloc(self.memory as *mut u8, layout)
            }
        }
    }
}

/// A temporary memory arena.
pub struct TempArena {
    memory: RefCell<RawMemory>,
}

impl TempArena {
    /// Creates a temporary memory arena with a given capacity, returning an error if it is unable
    /// to allocate the full amount of memory.
    ///
    /// # Example
    /// ```
    ///# use ngen::arena::TempArena;
    /// let arena = TempArena::with_capacity(1024).unwrap();
    /// ```
    pub fn with_capacity(capacity: usize) -> Result<Self, String> {
        let memory = RawMemory::with_capacity(capacity)?;

        Ok(Self {
            memory: RefCell::new(memory),
        })
    }

    /// Returns the total amount of memory used since the last reset.
    ///
    /// # Example
    /// ```
    ///# use ngen::arena::TempArena;
    /// let mut arena = TempArena::with_capacity(1024).unwrap();
    /// arena.push(4.0f32);
    /// assert_eq!(arena.len(), std::mem::size_of::<f32>());
    /// ```
    pub fn len(&self) -> usize {
        let memory = self.memory.borrow();
        memory.next - memory.memory
    }

    /// Pushes a default value for a given `T` into the temp arena, using the correct alignment for the pushed value.
    ///
    /// # Example
    /// ```
    ///# use ngen::arena::TempArena;
    /// let mut arena = TempArena::with_capacity(1024).unwrap();
    /// let forty_two = arena.push_default::<i32>();
    /// assert_eq!(forty_two, &0);
    /// ```
    pub fn push_default<'a, T: Default>(&'a self) -> &'a mut T {
        self.push(T::default())
    }

    /// Pushes a new value into the temp arena, using the correct alignment for the pushed value.
    ///
    /// # Example
    /// ```
    ///# use ngen::arena::TempArena;
    /// let mut arena = TempArena::with_capacity(1024).unwrap();
    /// let forty_two = arena.push(42);
    /// assert_eq!(forty_two, &42);
    /// ```
    pub fn push<'a, T>(&'a self, default: T) -> &'a mut T {
        let mut memory = self.memory.borrow_mut();

        unsafe {
            let layout = Layout::new::<T>();
            let start = align_to(memory.next, layout.align());

            let end = start + layout.size();

            if end > memory.end() {
                panic!("Out of memory.");
            }

            memory.next = end;
            let ptr = start as *mut T;
            ptr.write(default);

            &mut *ptr
        }
    }

    /// Resets the current memory arena, pointing the current location to the start of the
    /// allocated memory.
    ///
    /// # Example
    /// ```
    ///# use ngen::arena::TempArena;
    /// let mut arena = TempArena::with_capacity(1024).unwrap();
    ///
    /// // Push some items into the arena.
    /// for i in 0..42 {
    ///     arena.push(i);
    /// }
    ///
    /// assert_eq!(arena.len(), std::mem::size_of::<i32>() * 42);
    /// arena.reset();
    /// assert_eq!(arena.len(), 0);
    /// ```
    pub fn reset(&mut self) {
        self.memory.borrow_mut().reset()
    }

    /// Returns exclusive access to the memory arena to create a contiguous, and growable, array of
    /// items.
    ///
    /// # Example
    /// ```
    ///# use ngen::arena::TempArena;
    /// let mut arena = TempArena::with_capacity(1024).unwrap();
    ///
    /// let mut array = arena.array();
    /// array.push(42);
    /// ```
    pub fn array<T>(&mut self) -> Array<'_, T> {
        Array::new(self)
    }

    // pub fn list<T>(&self) -> List<'_, T> {
    //     List::new()
    // }
}