diff options
Diffstat (limited to 'src/util')
| -rw-r--r-- | src/util/fifo_queue.rs | 183 | 
1 files changed, 183 insertions, 0 deletions
diff --git a/src/util/fifo_queue.rs b/src/util/fifo_queue.rs new file mode 100644 index 0000000..7ec939b --- /dev/null +++ b/src/util/fifo_queue.rs @@ -0,0 +1,183 @@ +use crate::sync::NullLock; +use crate::sync::interface::Mutex; +use crate::vprintln; +use core::fmt; +use core::fmt::{Debug,Formatter}; + +/// # Initialize Queue +/// - Name: Symbol name +/// - Size: Number of elements +/// - Default: Default value +/// - Type: Data Type +#[macro_export] +macro_rules! init_queue { +	($name:tt,$size:tt,$default:tt,$type:ty) => { +		init_queue!{@gen [$name,$size,$default,$type,concat!("# ", stringify!($type), " Queue Allocator")]} +	}; +	(@gen [$name:tt,$size:tt,$default:tt,$type:ty,$doc:expr]) => { +		#[doc = $doc] +		#[link_section = ".data.alloc"] +		pub static $name: QueueAllocator<'static, $type, {$size+2}> = QueueAllocator::<$type, {$size+2}>{inner: NullLock::new([QueueItem::new($default); {$size+2}])}; +	}; +} + +#[derive(Copy,Clone)] +/// # Queue Item +/// +/// Encapsulates a data element and a pointer to +/// the next `Queue` item +pub struct QueueItem<'a, T: Sized> { +	/// # Data +	/// +	/// The encapsulated data +	data: T, +	/// # Pointer to the next item +	/// +	/// Stores either `None` or points +	/// to the next item. +	next: Option<*mut QueueItem<'a, T>>, +} + +impl<T> QueueItem<'_,T> { +	/// # Constructor +	pub const fn new(data: T) -> Self { +		Self { +			data: data, +			next: None, +		} +	} +	/// # Get the inner data +	/// +	/// Returns a borrow of the underlying data. +	pub fn inner(&mut self) -> &mut T { +		&mut self.data +	} +	/// # Get pointer to inner data +	pub fn ptr(&mut self) -> *mut u8 { +		self.inner() as *mut T as *mut u8 +	} +} + +/// # Sharing Thread Safety for QueueItem +unsafe impl<T> Send for QueueItem<'_,T> {} + +impl<T: Debug> Debug for QueueItem<'_,T> { +	/// # Debug formatter for `QueueItem` +	/// +	/// Output the encapsulated data +	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { +		#[cfg(feature="verbose")] +		return write!(f, "{:?} {:x} {:?}", self.data, self as *const QueueItem<'_,T> as usize, self.next); + +		#[cfg(not(feature="verbose"))] +		return write!(f, "{:?}", self.data); +	} +} + +/// # Queue Allocator +/// +/// Structure to store a pool of allocated data structures. +pub struct QueueAllocator<'a, T: Sized, const COUNT: usize> { +	/// # Synchronized Pool of items +	/// +	/// Stores synchronization wrapper around the data pool +	pub inner: NullLock<[QueueItem<'a, T>;COUNT]>, +} + +/// # Sharing Thread Safety for QueueAllocator +unsafe impl<T,const COUNT: usize> Send for QueueAllocator<'_,T,COUNT> {} + +impl<'a, T: Sized,const COUNT: usize> QueueAllocator<'a, T, COUNT> { +	/// # Initialization of Fixed-Size Pool +	///  +	/// Establishes the header and footer of the queue +	/// as the first and second elements respectively. +	/// All of the internal elements point to the next +	/// one and the final element points to `None` +	pub fn init(&self) { +		vprintln!("QA: Initializing Queue Allocator!"); +		self.inner.lock(|queue| { +			vprintln!("QA: Clearing internal references..."); +			for idx in 2..COUNT { +				if idx != COUNT-1 { +					queue[idx].next = Some(&mut queue[idx+1] as *mut QueueItem<'a, T>); +				} else { +					queue[idx].next = None; +				} +			} +			vprintln!("QA: Initializing head and tail..."); +			queue[0].next = Some(&mut queue[2] as *mut QueueItem<'a, T>); +			queue[1].next = Some(&mut queue[COUNT-1] as *mut QueueItem<'a, T>); +		}); +		vprintln!("QA: Initialized Queue Allocator!"); +	} + +	/// # Allocate Data +	/// +	/// If there is a data chunk available, +	/// return it, otherwise return `None` +	#[allow(dead_code)] +	pub fn alloc(&self) -> Option<&mut QueueItem<'a,T>> { +		vprintln!("QA: Allocating chunk!"); +		return self.inner.lock(|pool| { +			if let Some(entry) = pool[0].next { +				vprintln!("QA: Found chunk!"); +				pool[0].next = unsafe { (*entry).next }; +				unsafe { +					(*entry).next = None; +				} +				match pool[0].next { +					None => { +						pool[1].next = None +					} +					_ => {} +				} +				vprintln!("QA: \x1b[92mAllocated {:x}\x1b[0m", unsafe{(*entry).ptr() as usize}); +				return Some(unsafe{&mut *entry as &mut QueueItem<'a,T>}); +			} else { +				vprintln!("QA: No chunks available!"); +				return None; +			} +		}); +	} + +	/// # Free +	/// +	/// Add the item to the end of the queue. +	/// If there were no items, set it as the head. +	#[allow(dead_code)] +	pub fn free(&self, freed_item: &mut QueueItem<'a,T>) { +		vprintln!("QA: Deallocating chunk!"); +		self.inner.lock(|pool| { +			freed_item.next = None; +			match pool[1].next { +				None => { +					pool[0].next = Some(freed_item as *mut QueueItem<'a,T>); +				} +				Some(entry) => { +					unsafe { +						(*entry).next = Some(freed_item as *mut QueueItem<'a,T>); +					} +				} +			} +			pool[1].next = Some(freed_item as *mut QueueItem<'a,T>); +			vprintln!("QA: \x1b[91mDeallocated {:x}\x1b[0m", freed_item.ptr() as usize); +		}); +	} +} + +impl<T: Debug,const COUNT: usize> Debug for QueueAllocator<'_,T,COUNT> { +	/// # Debug Formatted Output +	/// +	/// Output each data point in the array with +	/// its debug formatter. +	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { +		self.inner.lock(|queue| { +			#[cfg(feature="verbose")] +			return write!(f, "{:?}", queue); + +			#[cfg(not(feature="verbose"))] +			return write!(f, "{:?}", queue); +		}) +	} +}  | 
