Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions core/patina_internal_collections/src/bst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ use crate::{
/// A binary search tree that can hold up to `SIZE` nodes.
pub struct Bst<'a, D>
where
D: SliceKey,
D: SliceKey + Default,
{
storage: Storage<'a, D>,
root: Cell<*mut Node<D>>,
}

impl<'a, D> Bst<'a, D>
where
D: SliceKey + 'a,
D: SliceKey + Default + 'a,
{
/// Creates a zero capacity red-black tree.
///
Expand Down Expand Up @@ -600,7 +600,7 @@ where

impl<D> Default for Bst<'_, D>
where
D: SliceKey,
D: SliceKey + Default,
{
fn default() -> Self {
Self::new()
Expand All @@ -610,7 +610,7 @@ where
/// Methods that require D to also be [Copy](core::marker::Copy).
impl<'a, D> Bst<'a, D>
where
D: Copy + SliceKey + 'a,
D: Copy + SliceKey + Default + 'a,
{
/// Replaces the memory of the tree with a new slice, copying the data from the old slice to the new slice.
pub fn resize(&mut self, slice: &'a mut [u8]) {
Expand Down Expand Up @@ -646,7 +646,7 @@ where
}
impl<D> core::fmt::Debug for Bst<'_, D>
where
D: SliceKey,
D: SliceKey + Default,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Bst")
Expand Down Expand Up @@ -702,7 +702,7 @@ mod tests {

#[test]
fn test_get_functions() {
#[derive(Debug)]
#[derive(Debug, Default)]
struct MyType(usize, usize);
impl crate::SliceKey for MyType {
type Key = usize;
Expand Down
83 changes: 75 additions & 8 deletions core/patina_internal_collections/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//!
//! SPDX-License-Identifier: Apache-2.0
//!
use core::{cell::Cell, mem, ptr::NonNull, slice};
use core::{cell::Cell, mem, mem::MaybeUninit, ptr::NonNull, slice};

use crate::{Error, Result, SliceKey};

Expand All @@ -23,7 +23,7 @@ pub const fn node_size<D: SliceKey>() -> usize {
/// A on-stack storage container for the nodes of a red-black tree.
pub(crate) struct Storage<'a, D>
where
D: SliceKey,
D: SliceKey + Default,
{
/// The storage container for the nodes.
data: &'a mut [Node<D>],
Expand All @@ -35,7 +35,7 @@ where

impl<'a, D> Storage<'a, D>
where
D: SliceKey,
D: SliceKey + Default,
{
/// Creates a empty, zero-capacity storage container.
pub const fn new() -> Storage<'a, D> {
Expand Down Expand Up @@ -176,7 +176,7 @@ where

impl<'a, D> Storage<'a, D>
where
D: SliceKey + Copy,
D: SliceKey + Copy + Default,
{
/// Resizes the storage container to a new slice of memory.
///
Expand All @@ -188,17 +188,38 @@ where
///
/// O(n)
pub fn resize(&mut self, slice: &'a mut [u8]) {
// SAFETY: This is reinterpreting a byte slice as a Node<D> slice.
// SAFETY: This is reinterpreting a byte slice as a MaybeUninit<Node<D>> slice.
// Using MaybeUninit explicitly represents uninitialized memory and avoids undefined
// behavior from creating references to uninitialized Node<D>.
// 1. The alignment is handled by slice casting rules
// 2. The correct number of Node<D> elements that fit in the byte slice is calculated
// 3. The lifetime 'a ensures the byte slice remains valid for the storage's lifetime
let buffer = unsafe {
slice::from_raw_parts_mut::<'a, Node<D>>(
slice as *mut [u8] as *mut Node<D>,
// 4. MaybeUninit<T> has the same size and alignment as T
let uninit_buffer = unsafe {
slice::from_raw_parts_mut::<'a, MaybeUninit<Node<D>>>(
slice as *mut [u8] as *mut MaybeUninit<Node<D>>,
slice.len() / mem::size_of::<Node<D>>(),
)
};

assert!(uninit_buffer.len() >= self.len());

// Initialize all nodes in the buffer to prevent undefined behavior.
// Cell::set() internally uses mem::replace() which reads the old value before writing.
// By fully initializing each Node with Node::new(D::default()), we ensure all fields
// (including the data field) are in a well-defined state before any operations.
for elem in uninit_buffer.iter_mut() {
// SAFETY: We're initializing uninitialized memory using MaybeUninit::write,
// which is the safe way to initialize MaybeUninit without reading the old value.
elem.write(Node::new(D::default()));
}

// SAFETY: All elements have been initialized in the loop above. We can now safely
// convert the MaybeUninit<Node<D>> slice to a Node<D> slice.
// MaybeUninit<T> has the same memory layout as T, so this cast is valid.
let buffer =
unsafe { slice::from_raw_parts_mut(uninit_buffer.as_mut_ptr() as *mut Node<D>, uninit_buffer.len()) };

assert!(buffer.len() >= self.len());

// When current capacity is 0, we just need to copy the data and build the available list
Expand Down Expand Up @@ -621,6 +642,52 @@ mod tests {
assert_eq!(node.data, 12);
}

#[test]
fn test_storage_resize() {
// Test the capacity > 0 resize path to ensure uninitialized memory is properly handled

// Use properly aligned wrapper to ensure alignment for Node<usize>
#[repr(align(8))]
struct AlignedMem<const N: usize>([u8; N]);

// Create initial storage with capacity for 5 nodes
let mut memory1 = AlignedMem([0; 5 * node_size::<usize>()]);
let mut storage = Storage::<usize>::with_capacity(&mut memory1.0);

// Add 3 nodes to the initial storage
for i in 0..3 {
let (index, node) = storage.add(i * 10).unwrap();
assert_eq!(index, i);
assert_eq!(node.data, i * 10);
}
assert_eq!(storage.len(), 3);
assert_eq!(storage.capacity(), 5);

// Resize to larger capacity (10 nodes) - this exercises the capacity > 0 path
let mut memory2 = AlignedMem([0; 10 * node_size::<usize>()]);
storage.resize(&mut memory2.0);

// Verify the old nodes are still accessible and have correct data
assert_eq!(storage.len(), 3);
assert_eq!(storage.capacity(), 10);
assert_eq!(storage.get(0).unwrap().data, 0);
assert_eq!(storage.get(1).unwrap().data, 10);
assert_eq!(storage.get(2).unwrap().data, 20);

// Verify we can add more nodes in the new capacity
for i in 3..10 {
let (index, node) = storage.add(i * 10).unwrap();
assert_eq!(index, i);
assert_eq!(node.data, i * 10);
}
assert_eq!(storage.len(), 10);

// Verify all nodes are accessible
for i in 0..10 {
assert_eq!(storage.get(i).unwrap().data, i * 10);
}
}

#[test]
fn test_sibling() {
let p1 = &Node::new(1);
Expand Down
12 changes: 6 additions & 6 deletions core/patina_internal_collections/src/rbt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ use core::{cell::Cell, cmp::Ordering, ptr};
/// A red-black tree that can hold up to `SIZE` nodes.
pub struct Rbt<'a, D>
where
D: SliceKey,
D: SliceKey + Default,
{
storage: Storage<'a, D>,
root: Cell<*mut Node<D>>,
}

impl<'a, D> Rbt<'a, D>
where
D: SliceKey + 'a,
D: SliceKey + Default + 'a,
{
/// Creates a zero capacity red-black tree.
///
Expand Down Expand Up @@ -841,7 +841,7 @@ where

impl<'a, D> Rbt<'a, D>
where
D: SliceKey + Copy + 'a,
D: SliceKey + Copy + Default + 'a,
{
/// Replaces the memory of the tree with a new slice, copying the data from the old slice to the new slice.
pub fn resize(&mut self, slice: &'a mut [u8]) {
Expand Down Expand Up @@ -878,7 +878,7 @@ where

impl<D> Default for Rbt<'_, D>
where
D: SliceKey,
D: SliceKey + Default,
{
fn default() -> Self {
Self::new()
Expand All @@ -887,7 +887,7 @@ where

impl<D> core::fmt::Debug for Rbt<'_, D>
where
D: SliceKey,
D: SliceKey + Default,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Rbt")
Expand Down Expand Up @@ -1646,7 +1646,7 @@ mod tests {

#[test]
fn test_get_functions() {
#[derive(Debug)]
#[derive(Debug, Default)]
struct MyType(usize, usize);
impl crate::SliceKey for MyType {
type Key = usize;
Expand Down
12 changes: 12 additions & 0 deletions patina_dxe_core/src/gcd/io_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ pub enum IoBlock {
Allocated(dxe_services::IoSpaceDescriptor),
}

impl Default for IoBlock {
fn default() -> Self {
Self::Unallocated(dxe_services::IoSpaceDescriptor {
base_address: 0,
length: 0,
io_type: dxe_services::GcdIoType::NonExistent,
device_handle: core::ptr::null_mut(),
image_handle: core::ptr::null_mut(),
})
}
}

#[derive(Debug)]
pub enum StateTransition {
Add(dxe_services::GcdIoType),
Expand Down
14 changes: 14 additions & 0 deletions patina_dxe_core/src/gcd/memory_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ pub enum MemoryBlock {
Allocated(dxe_services::MemorySpaceDescriptor),
}

impl Default for MemoryBlock {
fn default() -> Self {
Self::Unallocated(dxe_services::MemorySpaceDescriptor {
base_address: 0,
length: 0,
memory_type: dxe_services::GcdMemoryType::NonExistent,
attributes: 0,
capabilities: 0,
device_handle: 0 as efi::Handle,
image_handle: 0 as efi::Handle,
})
}
}

pub enum StateTransition {
Add(dxe_services::GcdMemoryType, u64, u64),
Remove,
Expand Down