vecdep/
lib.rs

1
2//! A very basic FiFo like structure, that is like std's VecDeque,
3//! and doesn't allocate on creation.
4//! 
5//! It can be used inside static variables.
6//! 
7//! This library uses unsafe code and is only testet minimaly, I can't
8//! promise that it works on anything else then x64 windows,
9//! It should be compatible with most architectures though.
10
11#[cfg(test)]
12mod tests;
13
14pub(crate) mod wrapping;
15
16use std::{alloc::{Layout, alloc, dealloc}, fmt::{Debug, Write}, ptr::null_mut};
17use wrapping::Wrapping;
18
19unsafe fn malloc<T>(size: usize) -> *mut T {
20    alloc(Layout::array::<T>(size).unwrap()) as *mut T
21}
22unsafe fn demalloc<T>(ptr: *mut T, size: usize) {
23    dealloc(ptr as *mut u8, Layout::array::<T>(size).unwrap());
24}
25
26/// A FiFo style queue, similar but not as powerful as std's VecDeque.
27/// 
28/// Poping and pushing can be done in O(1) time and
29/// doesn't require shifting the elements.
30/// The VecDep is backed by a ring buffer with a fixed size.
31/// If the buffer exceedes it's size limit, it starts deleting the oldest elements.
32/// The queue is not really tested for zero sized types or dynamically sized types, althoug ZSTs
33/// should work.
34pub struct VecDep<T> {
35    /* The content is layed out as follows:
36        teil 1 3 7 2 head - -
37        \___________________/
38              capacity
39    */
40    content: *mut T,
41    capacity: usize,
42    head: Wrapping,
43    tail: Wrapping,
44    len: usize,
45}
46
47impl<T> VecDep<T> {
48    
49    /// Construct a new VecDep queue
50    /// # Examples
51    /// 
52    /// ```
53    /// use vecdep::VecDep;
54    /// 
55    /// let mut queue: VecDep<u32> = VecDep::new(32);
56    /// 
57    /// queue.push(19);
58    /// queue.push(12);
59    /// 
60    /// assert!(queue.is_full());
61    /// 
62    /// assert!(queue.peek() == Some(&19));
63    /// assert!(queue.pop() == Some(19));
64    /// assert!(queue.pop() == Some(12));
65    /// 
66    /// assert!(queue.is_empty());
67    /// ```
68    /// 
69    #[inline]
70    pub const fn new(capacity: usize) -> Self {
71        Self {
72            content: null_mut(),
73            capacity,
74            head: Wrapping::new(0, capacity - 1),
75            tail: Wrapping::new(0, capacity - 1),
76            len: 0,
77        }
78    }
79
80    /// Push an element onto the queue.
81    #[inline]
82    pub fn push(&mut self, item: T) {
83        
84        // Allocate, if the space isn't intitialized yet
85        if self.content.is_null() { unsafe {
86            self.content = malloc::<T>(self.capacity);
87            if cfg!(test) { self.content.write_bytes(0, self.capacity) }; // for debugging purposes
88        } }
89
90        // If the buffer would be "filled" with values, start deleting the oldest one
91        if self.is_full() {
92            self.tail += 1;
93            // panic!("Buffer is full!");
94        }
95        // Increment the length only if the buffer isn't filled completely
96        else {
97            self.len += 1;
98        };
99
100        // Write the new element and increment the head index
101        unsafe { *self.content.add(*self.head) = item };
102
103        self.head += 1;
104
105    }
106
107    /// Pop the oldest element from the buffer.
108    #[inline]
109    pub fn pop(&mut self) -> Option<T> {
110
111        if self.is_empty() { return None };
112
113        let item = unsafe { self.content.add(*self.tail).read() };
114
115        self.tail += 1;
116        self.len -= 1;
117
118        Some(item)
119
120    }
121
122    /// Peek the oldest element without removing it
123    #[inline]
124    pub fn peek<'d>(&'d self) -> Option<&'d T> {
125
126        if self.is_empty() { return None };
127
128        let item = unsafe { self.content.add(*self.tail).as_ref().unwrap() };
129
130        Some(item)
131
132    }
133
134    /// Get the length of the stored elements.
135    #[inline(always)]
136    pub const fn len(&self) -> usize {
137        self.len
138    }
139    
140    /// The maximum capacity of this buffer.
141    #[inline(always)]
142    pub const fn capacity(&self) -> usize {
143        self.capacity
144    }
145
146    /// If the buffer is full.
147    #[inline(always)]
148    pub fn is_full(&self) -> bool {
149        self.len == self.capacity 
150    }
151    
152    /// If the buffer is empty.
153    #[inline(always)]
154    pub fn is_empty(&self) -> bool {
155        self.len == 0
156    }
157
158}
159
160impl<T> Drop for VecDep<T> {
161    
162    /// Deallocate the inner storage
163    fn drop(&mut self) {
164
165        unsafe { demalloc(self.content, self.capacity) };
166
167    }
168
169}
170
171impl<T: Debug> Debug for VecDep<T> {
172    
173    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174        
175        if self.content.is_null() {
176
177            f.write_str("[unallocated]")
178
179        } else {
180            
181            f.write_char('[')?;
182            for idx in 0..self.capacity {
183                unsafe {  f.write_fmt(format_args!("{:?}", *self.content.add(idx)))? };
184                if idx == *self.tail { f.write_str(" (T)")? };
185                if idx == *self.head { f.write_str(" (H)")? };
186                if idx + 1 < self.capacity { f.write_str(", ")? };
187            }
188            f.write_char(']')
189
190            // f.write_fmt(format_args!(" (T = {}, H = {})", *self.tail, *self.head))
191
192        }
193
194    }
195
196}