From 054d70b4835b4a79014704d850cc767256a03b0b Mon Sep 17 00:00:00 2001 From: Jim <0xJ1M@users.noreply.github.com> Date: Wed, 27 May 2026 21:13:18 +0100 Subject: [PATCH] RD-5: Implemented Rust equivalent zone allocator --- README.md | 4 +- src/main.rs | 1 + src/z_zone/mod.rs | 165 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 src/z_zone/mod.rs diff --git a/README.md b/README.md index 0ff6e44..a445d99 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # RustyDoom -A Rust version of the original DOOM from the offically released source code found at [https://github.com/id-software/doom](https://github.com/id-software/doom) +A Rust version of the original DOOM base on the offically released source code found at [https://github.com/id-software/doom](https://github.com/id-software/doom) -This is more of a learning exercise than something good! +This is more of a learning exercise than something good! For example, the original `z_zone.c` and `z_zone.h` will be more "Rust-like" compared to the original version. This repository will only contain the SHAREWARE verison of the original DOOM1, but will be able to run both IWAD and PWADS. diff --git a/src/main.rs b/src/main.rs index 1b18085..0cb8eb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod m_argv; mod m_misc; mod v_video; mod w_wad; +mod z_zone; mod math; diff --git a/src/z_zone/mod.rs b/src/z_zone/mod.rs new file mode 100644 index 0000000..bc9e228 --- /dev/null +++ b/src/z_zone/mod.rs @@ -0,0 +1,165 @@ +/// Memory allocator stuff +/// Compared to the original source, this will be some what "lean n' mean" +use std::cell::RefCell; +use std::collections::HashMap; + +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +/// The PurgeTags (PUs) +/// Tags < 100 are not overwritten untill freed +pub enum PurgeTag { + /// Static the entire execution time + Static = 1, + /// Static while playing + Sound = 2, + /// static while playing + Music = 3, + /// Original source comment: anything else dave wants static + Dave = 4, + /// static until level exit + Level = 50, + /// static for special thinkers in a level + LevSpec = 51, + // Tags >= 100 are "purgable" whenever needed + PurgeLevel = 100, + Cache = 101, +} + + +/// Provides metadata on +struct AllocMeta { + /// The size of the allocation + size: usize, + /// The PU tag associated with the allocation + tag: i32, + /// A pointer to the caller's tracking pointer, cleared automatically upon freeing + user: *mut *mut u8, +} + + +thread_local! { + // We promise not to be naughty and access this from another thread :) + /// Mapping from pointers (as usize) to the correspond AllocationMeta + static REGISTRY: RefCell> = RefCell::new(HashMap::new()); +} + +// Helper method that we can use to access the registry +fn with_registry(f: F) -> R +where + F: FnOnce(&mut HashMap) -> R +{ + REGISTRY.with(|registry| f(&mut *registry.borrow_mut())) +} + +#[allow(non_snake_case)] +/// Initalized the Zone memory allocator sub-system +pub fn Z_Init() { + // do nothing, we get lazy init of the registry. +} + +#[allow(non_snake_case)] +/// Allocate a contiguous chunk of zeroed memory from the zone allocator +/// # Safety +/// This function returns a raw pointer that bypasses Rust's automatic memory +/// tracking. The returned memory must be manually reclaimed using `Z_Free`. +pub unsafe fn Z_Malloc(size: usize, tag: i32, user: *mut *mut u8) -> *mut u8 { + let mut buffer = vec![0u8; size]; + let ptr = buffer.as_mut_ptr(); + + // ensures Rust does not drop the buffer at the end of this block + std::mem::forget(buffer); + + // Update the engine's tracking pointer if one was provided + if !user.is_null() { + unsafe { + *user = ptr; + } + } + + // Save allocation details using the pointer address as the key + with_registry(|map| { + map.insert(ptr as usize, AllocMeta { size, tag, user }); + }); + + ptr +} + +#[allow(non_snake_case)] +/// Reclaims and frees a allocation from Z_Malloc +/// # Safety +/// The provided pointer must point to a valid allocation tracked by the registry. +/// Passing an invalid or already freed pointer can result in undefined behavior +pub unsafe fn Z_Free(ptr: *mut u8) { + if ptr.is_null() { + return; + } + let removed = with_registry(|map| map.remove(&(ptr as usize))); + + if let Some(meta) = removed { + // Clear the caller's tracking pointer to prevent use-after-free + if !meta.user.is_null() { + unsafe { + *meta.user = std::ptr::null_mut(); + } + } + // re-build the vector so Rust can drop it + unsafe { + let _dropped_vec = Vec::from_raw_parts(ptr, meta.size, meta.size); + } + } +} + +#[allow(non_snake_case)] +/// Frees all memory allocations between the specific lifetime PU tags. +/// # Safety +/// All pointers tracked within this tag boundary are invalidated and deallocate +pub unsafe fn Z_FreeTags(low_tag: i32, high_tag: i32) { + + // get all of the allocations to free + let tags_to_free: Vec = with_registry(|map| { + map.iter() + .filter(|(_, meta)| meta.tag >= low_tag && meta.tag <= high_tag) + .map(|(ptr_addr,_)| *ptr_addr) + .collect() + }); + + // remove each pointer returned from the map and drop the memory + + for ptr_addr in tags_to_free { + let removed_ptr = with_registry(|map| map.remove(&ptr_addr)); + + if let Some(meta) = removed_ptr { + let p = ptr_addr as *mut u8; + unsafe { + // clear caller tracking pointer to preven use after free + if !meta.user.is_null() { + *meta.user = std::ptr::null_mut(); + } + + let _dropped_vec = Vec::from_raw_parts(p, meta.size, meta.size); + } + } + } +} + +// Internal ChangeTag function +#[allow(non_snake_case)] +fn Z_ChangeTagInternal(ptr: *mut u8, tag: i32) { + if ptr.is_null() { + return; + } + + with_registry(|map| { + if let Some(meta) = map.get_mut(&(ptr as usize)) { + meta.tag = tag; + } + }) +} +/// Modifies the lifetime tag of an active allocation. +/// +/// # Safety +/// The provided pointer must point to a valid allocation tracked by the registry +#[allow(non_snake_case)] +pub unsafe fn Z_ChangeTag(ptr: *mut u8, tag: i32) { + // removes the original helper macro from the original C, we don't need withcraft here + Z_ChangeTagInternal(ptr, tag); +}