diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 2410019868a19..30ce3095502d9 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -471,6 +471,8 @@ declare_features! ( (unstable, ffi_const, "1.45.0", Some(58328)), /// Allows the use of `#[ffi_pure]` on foreign functions. (unstable, ffi_pure, "1.45.0", Some(58329)), + /// New collection + (unstable, fixed_queue, "1.77.1", Some(126204)), /// Allows using `#[repr(align(...))]` on function items (unstable, fn_align, "1.53.0", Some(82232)), /// Support delegating implementation of functions to other already implemented functions. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e245dfb9f5d77..c8f30c0de217f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -859,6 +859,7 @@ symbols! { field, field_init_shorthand, file, + fixed_queue, float, float_to_int_unchecked, floorf128, diff --git a/library/alloc/src/collections/fixed_queue/mod.rs b/library/alloc/src/collections/fixed_queue/mod.rs new file mode 100644 index 0000000000000..45712374445fd --- /dev/null +++ b/library/alloc/src/collections/fixed_queue/mod.rs @@ -0,0 +1,288 @@ +use crate::vec::Vec; +use core::{ + fmt, mem, + ops::{Index, Range}, + slice, +}; + +#[cfg(test)] +mod tests; + +/// Fixed Size Queue: +/// A linear queue implemented with a static ring buffer of owned nodes. +/// +/// The `FixedQueue` allows pushing and popping elements in constant time. +/// +/// The "default" usage of this type is to use [`push`] to add to +/// the queue, and [`pop`] to remove from the queue. Iterating over +/// `FixedQueue` goes front to back. +/// +/// A `FixedQueue` with a known list of items can be initialized from an array: +/// ``` +/// #![feature(fixed_queue)] +/// use alloc::collections::FixedQueue; +/// +/// let fque = FixedQueue::from([1, 2, 3]); +/// ``` +/// +/// Since `FixedQueue` is an array ring buffer, its elements are contiguous +/// in memory. +/// +/// [`push`]: FixedQueue::push +/// [`pop`]: FixedQueue::pop +#[derive(Debug)] +#[unstable(feature = "fixed_queue", issue = "126204")] +pub struct FixedQueue { + buffer: [Option; N], + head: usize, + tail: usize, + len: usize, +} + +impl FixedQueue { + /// Create a new FixedQueue with given fields. + #[inline] + const fn with( + buffer: [Option; N], + head: usize, + tail: usize, + len: usize, + ) -> FixedQueue { + FixedQueue { buffer, head, tail, len } + } + + /// Create a new FixedQueue with a given capacity. + #[unstable(feature = "fixed_queue", issue = "126204")] + pub const fn new() -> FixedQueue + where + Option: Copy, + { + FixedQueue::with([None; N], 0, 0, 0) + } + + /// Return the max capacity of the FixedQueue. + #[inline] + #[unstable(feature = "fixed_queue", issue = "126204")] + pub const fn capacity(&self) -> usize { + N + } + + /// Returns the number of elements in the FixedQueue. + #[inline] + #[unstable(feature = "fixed_queue", issue = "126204")] + pub const fn len(&self) -> usize { + self.len + } + + /// Check if the queue is empty. + #[inline] + #[unstable(feature = "fixed_queue", issue = "126204")] + pub const fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Check if the queue is full. + #[inline] + #[unstable(feature = "fixed_queue", issue = "126204")] + pub const fn is_full(&self) -> bool { + self.len == N + } + + /// Removes all elements from the queue. + #[unstable(feature = "fixed_queue", issue = "126204")] + pub fn clear(&mut self) { + for i in 0..N { + drop(self.buffer[i].take()); + } + self.head = 0; + self.tail = 0; + self.len = 0; + } + + /// Fills the queue with an element. + #[unstable(feature = "fixed_queue", issue = "126204")] + pub fn fill(&mut self, item: T) + where + Option: Copy, + { + self.buffer = [Some(item); N]; + self.head = 0; + self.tail = 0; + self.len = N; + } + + /// Add an element to the queue. If queue is full, the first element + /// is popped and returned. + #[unstable(feature = "fixed_queue", issue = "126204")] + pub fn push(&mut self, item: T) -> Option { + // 'pop' first + let overwritten = self.buffer[self.tail].take(); + // overwrite head/tail element + self.buffer[self.tail] = Some(item); + // shift tail with 'push' + self.tail = (self.tail + 1) % N; + if overwritten.is_some() { + // shift head ptr on collision + self.head = (self.head + 1) % N; + } else { + // increase len if no collision + self.len += 1; + } + return overwritten; + } + + /// Removes and returns the oldest element from the queue. + #[inline] + #[unstable(feature = "fixed_queue", issue = "126204")] + pub fn pop(&mut self) -> Option + where + Option: Copy, + { + if self.len == 0 { + return None; + } + let popped = self.buffer[self.head].take(); + self.head = (self.head + 1) % N; + self.len -= 1; + popped + } + + /// Converts the queue into its array equivalent. + #[unstable(feature = "fixed_queue", issue = "126204")] + pub fn to_option_array(self) -> [Option; N] + where + Option: Copy, + { + let mut arr: [Option; N] = [None; N]; + for i in 0..N { + arr[i] = self.buffer[(self.head + i) % N]; + } + arr + } + + /// Converts the queue into its vec equivalent. + #[unstable(feature = "fixed_queue", issue = "126204")] + pub fn to_vec(self) -> Vec + where + T: Copy, + { + let mut vec: Vec = Vec::new(); + for i in 0..N { + if let Some(e) = self.buffer[(self.head + i) % N] { + vec.push(e); + } + } + vec + } +} + +#[unstable(feature = "fixed_queue", issue = "126204")] +impl From<[T; N]> for FixedQueue { + /// Creates a FixedQueue from a fixed size array. + fn from(array: [T; N]) -> Self { + FixedQueue::with(array.map(Some), 0, 0, N) + } +} + +#[unstable(feature = "fixed_queue", issue = "126204")] +impl From<&[T; N]> for FixedQueue { + /// Creates a FixedQueue from a fixed size slice. + fn from(array: &[T; N]) -> Self { + FixedQueue::with(array.map(Some), 0, 0, N) + } +} + +#[unstable(feature = "fixed_queue", issue = "126204")] +impl From<&[T]> for FixedQueue { + /// Creates a FixedQueue from an unsized slice. Copies a maximum of N + /// elements of the slice, and a minimum of the slice length into the + /// queue. {[0, 0], 0} - [0, 0] + fn from(array: &[T]) -> Self { + let mut buf: [Option; N] = [None; N]; + let length = N.min(array.len()); + for i in 0..length { + buf[i] = Some(array[i]); + } + FixedQueue::with(buf, 0, array.len() - 1, length) + } +} + +#[unstable(feature = "fixed_queue", issue = "126204")] +impl PartialEq for FixedQueue { + /// This method tests if a FixedQueue is equal to another FixedQueue. + fn eq(&self, other: &FixedQueue) -> bool { + if other.len != self.len { + return false; + } + (0..N).all(|x| self.buffer[(self.head + x) % N] == other.buffer[(other.head + x) % N]) + } +} + +#[unstable(feature = "fixed_queue", issue = "126204")] +impl PartialEq<[T; M]> for FixedQueue { + /// This method tests if a FixedQueue is equal to a fixed size array. + fn eq(&self, other: &[T; M]) -> bool { + if M != self.len { + return false; + } + (0..M).all(|x| self.buffer[(self.head + x) % N].as_ref() == Some(&other[x])) + } +} + +#[unstable(feature = "fixed_queue", issue = "126204")] +impl Index for FixedQueue { + type Output = Option; + + fn index(&self, index: usize) -> &Self::Output { + if index >= N { + panic!("Index out of bounds"); + } + &self.buffer[(self.head + index) % N] + } +} + +#[unstable(feature = "fixed_queue", issue = "126204")] +impl Index> for FixedQueue +where + T: Copy + Default, +{ + type Output = [T]; + + fn index(&self, range: Range) -> &Self::Output { + let start = range.start; + let end = range.end; + + // check bounds + assert!(start <= end && end <= self.len, "Index out of bounds"); + + // create temporary array to store the results + let mut temp = Vec::with_capacity(end - start); + + for i in start..end { + let idx = (self.head + i) % N; + if let Some(value) = self.buffer[idx] { + temp.push(value); + } + } + + // Return a slice from the temporary array + // SAFETY: This is safe because temp will live long enough within this function call. + let result = unsafe { slice::from_raw_parts(temp.as_ptr(), temp.len()) }; + mem::forget(temp); + result + } +} + +#[unstable(feature = "fixed_queue", issue = "126204")] +impl fmt::Display for FixedQueue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.len == 0 { + return write!(f, "{{}}"); + } + write!(f, "{{")?; + for x in 0..(self.len - 1) { + write!(f, "{}, ", self.buffer[(self.head + x) % N].as_ref().unwrap())?; + } + write!(f, "{}}}", self.buffer[(self.head + self.len - 1) % N].as_ref().unwrap()) + } +} diff --git a/library/alloc/src/collections/fixed_queue/tests.rs b/library/alloc/src/collections/fixed_queue/tests.rs new file mode 100644 index 0000000000000..c3fc738880329 --- /dev/null +++ b/library/alloc/src/collections/fixed_queue/tests.rs @@ -0,0 +1,248 @@ +use super::*; +use crate::{string::String, vec::Vec}; + +#[test] +fn with() { + let x = FixedQueue::::with([None; 3], 789, 456, 123); + let y = FixedQueue:: { buffer: [None; 3], head: 789, tail: 456, len: 123 }; + assert_eq!(x, y); +} + +#[test] +fn partial_eq_self() { + let x = FixedQueue::::new(); + let mut y = FixedQueue::with([None; 3], 0, 0, 0); + assert_eq!(x, y); + y.push(1); + assert_ne!(x, y); + y.push(2); + assert_ne!(x, y); + y.push(3); + assert_ne!(x, y); + y.clear(); + assert_eq!(x, y); + let w = FixedQueue::::with([None; 3], 0, 0, 0); + let z = FixedQueue::::with([None; 3], 1, 1, 0); + assert_eq!(w, z); + let u = FixedQueue::::with([Some(20), None, None], 0, 1, 1); + let v = FixedQueue::::with([None, Some(20), None], 1, 2, 1); + assert_eq!(u, v); +} + +#[test] +fn partial_eq_array() { + let x = FixedQueue::::from([1, 2, 3]); + assert_eq!(x, [1, 2, 3]); + assert_ne!(x, [20, 2, 3]); + let y = FixedQueue::::from([80]); + assert_eq!(y, [80]); + let z = FixedQueue::::with([Some(1), Some(2), Some(3)], 1, 1, 3); + assert_eq!(z, [2, 3, 1]); + let w = FixedQueue::::with([Some(20), None, None], 0, 1, 1); + assert_eq!(w, [20]); + let u = FixedQueue::::with([None, Some(20), None], 1, 2, 1); + assert_eq!(u, [20]); +} + +#[test] +fn new() { + let x = FixedQueue::::new(); + let y = FixedQueue::::with([None; 3], 0, 0, 0); + assert_eq!(x, y); +} + +#[test] +fn from_array() { + let x = FixedQueue::from([1i32, 2i32, 3i32]); + let y = FixedQueue::::with([Some(1i32), Some(2i32), Some(3i32)], 0, 0, 3); + assert_eq!(x, y); + let z = FixedQueue::from([true, false, true]); + let w = FixedQueue::::with([Some(true), Some(false), Some(true)], 0, 0, 3); + assert_eq!(z, w); +} + +#[test] +fn from_sized_slice() { + let x = FixedQueue::from(&[3i32, 2i32, 1i32]); + let y = FixedQueue::::with([Some(3i32), Some(2i32), Some(1i32)], 0, 0, 3); + assert_eq!(x, y); +} + +#[test] +fn from_slice() { + let array = [3i32, 2i32, 1i32]; + let x = FixedQueue::::from(&array[0..1]); + let y = FixedQueue::::with([Some(3i32)], 0, 0, 1); + assert_eq!(x, y); + let w = FixedQueue::::from(&array[0..2]); + let z = FixedQueue::::with([Some(3i32), Some(2i32)], 0, 0, 2); + assert_eq!(w, z); + let u = FixedQueue::::from(&array[0..3]); + let v = FixedQueue::::with([Some(3i32), Some(2i32), Some(1i32)], 0, 0, 3); + assert_eq!(u, v); + let s = FixedQueue::::from(&array[..]); + let t = FixedQueue::::with([Some(3i32), Some(2i32), Some(1i32)], 0, 0, 3); + assert_eq!(s, t); +} + +#[test] +fn index() { + let x = FixedQueue::<&str, 3>::from(["a", "b", "c"]); + assert_eq!(x[0], Some("a")); + assert_eq!(x[1], Some("b")); + assert_eq!(x[2], Some("c")); +} + +#[test] +fn index_range() { + let x = FixedQueue::<&str, 3>::from(["a", "b", "c"]); + assert!(x[0..0].is_empty()); + assert_eq!(x[0..1], ["a"]); + assert_eq!(x[0..2], ["a", "b"]); + assert_eq!(x[0..3], ["a", "b", "c"]); +} + +#[test] +fn display() { + let mut x = FixedQueue::::new(); + assert_eq!(format!("{}", x), String::from("{}")); + x.push(10); + assert_eq!(format!("{}", x), String::from("{10}")); + x.pop(); + assert_eq!(format!("{}", x), String::from("{}")); + x.push(20); + assert_eq!(format!("{}", x), String::from("{20}")); + x.push(30); + assert_eq!(format!("{}", x), String::from("{20, 30}")); + x.push(40); + assert_eq!(format!("{}", x), String::from("{20, 30, 40}")); + x.push(50); + assert_eq!(format!("{}", x), String::from("{30, 40, 50}")); + x.pop(); + assert_eq!(format!("{}", x), String::from("{40, 50}")); + x.pop(); + assert_eq!(format!("{}", x), String::from("{50}")); + x.pop(); + assert_eq!(format!("{}", x), String::from("{}")); +} + +#[test] +fn capacity() { + let x = FixedQueue::::new(); + assert_eq!(x.capacity(), 1); + let y = FixedQueue::::new(); + assert_eq!(y.capacity(), 2); + let z = FixedQueue::::new(); + assert_eq!(z.capacity(), 3); +} + +#[test] +fn len() { + let mut x = FixedQueue::::new(); + assert_eq!(x.len(), 0); + x.push(true); + assert_eq!(x.len(), 1); + x.push(false); + assert_eq!(x.len(), 2); + x.push(true); + assert_eq!(x.len(), 3); + x.pop(); + assert_eq!(x.len(), 2); + x.pop(); + assert_eq!(x.len(), 1); + x.pop(); + assert_eq!(x.len(), 0); +} + +#[test] +fn is_empty() { + let mut x = FixedQueue::::new(); + assert!(x.is_empty()); + x.push(1); + assert!(!x.is_empty()); +} + +#[test] +fn is_full() { + let mut x = FixedQueue::::new(); + assert!(!x.is_full()); + x.push(1); + assert!(!x.is_full()); + x.push(1); + assert!(!x.is_full()); + x.push(1); + assert!(x.is_full()); +} + +#[test] +fn clear() { + let mut x = FixedQueue::from([1, 2, 3]); + assert!(!x.is_empty()); + x.clear(); + assert!(x.is_empty()); +} + +#[test] +fn fill() { + let mut x = FixedQueue::::new(); + assert!(!x.is_full()); + x.fill(10); + assert!(x.is_full()); +} + +#[test] +fn push() { + let mut x = FixedQueue::::from([(); 2].map(|_| String::new())); + assert!(x.is_full()); + x.clear(); + assert!(x.is_empty()); + x.push(String::from("a")); + assert_eq!(x.len(), 1); + assert_eq!(x, [String::from("a")]); + x.push(String::from("b")); + assert_eq!(x.len(), 2); + assert_eq!(x, [String::from("a"), String::from("b")]); + x.push(String::from("c")); + assert_eq!(x.len(), 2); + assert_eq!(x, [String::from("b"), String::from("c")]); + x.push(String::from("d")); + assert_eq!(x.len(), 2); + assert_eq!(x, [String::from("c"), String::from("d")]); + x.push(String::from("e")); + assert_eq!(x.len(), 2); + assert_eq!(x, [String::from("d"), String::from("e")]); +} + +#[test] +fn pop() { + let mut u = FixedQueue::<&str, 3>::from(["a", "b", "c"]); + assert!(!u.is_empty()); + let w = u.pop(); + assert_eq!(w, Some("a")); + assert_eq!(u, ["b", "c"]); + let x = u.pop(); + assert_eq!(x, Some("b")); + assert_eq!(u, ["c"]); + let y = u.pop(); + assert_eq!(y, Some("c")); + assert_eq!(u, []); + let z = u.pop(); + assert_eq!(z, None); + assert_eq!(u, []); +} + +#[test] +fn to_option_array() { + let x = FixedQueue::<&str, 3>::from(["a", "b", "c"]); + assert_eq!(x.to_option_array(), [Some("a"), Some("b"), Some("c")]); + let mut y = FixedQueue::<&str, 3>::from(["a", "b", "c"]); + y.pop(); + assert_eq!(y.to_option_array(), [Some("b"), Some("c"), None]) +} + +#[test] +fn to_vec() { + let x = FixedQueue::<&str, 3>::from(["a", "b", "c"]); + let y = Vec::from(["a", "b", "c"]); + assert_eq!(x.to_vec(), y); +} diff --git a/library/alloc/src/collections/mod.rs b/library/alloc/src/collections/mod.rs index 705b81535c279..cffa5f7500818 100644 --- a/library/alloc/src/collections/mod.rs +++ b/library/alloc/src/collections/mod.rs @@ -7,6 +7,10 @@ pub mod binary_heap; #[cfg(not(no_global_oom_handling))] mod btree; #[cfg(not(no_global_oom_handling))] +#[unstable(feature = "fixed_queue", issue = "126204")] +/// This module provides a fixed-size queue implementation. +pub mod fixed_queue; +#[cfg(not(no_global_oom_handling))] pub mod linked_list; #[cfg(not(no_global_oom_handling))] pub mod vec_deque; @@ -42,6 +46,11 @@ pub use btree_map::BTreeMap; #[doc(no_inline)] pub use btree_set::BTreeSet; +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "fixed_queue", issue = "126204")] +#[doc(no_inline)] +pub use fixed_queue::FixedQueue; + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 4749b8880fbc4..2d3f5eccc62c5 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -123,6 +123,7 @@ #![feature(error_generic_member_access)] #![feature(exact_size_is_empty)] #![feature(extend_one)] +#![feature(fixed_queue)] #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(hasher_prefixfree_extras)] diff --git a/tests/ui/feature-gates/feature-gate-fixed-queue.rs b/tests/ui/feature-gates/feature-gate-fixed-queue.rs new file mode 100644 index 0000000000000..a6db96515912e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-fixed-queue.rs @@ -0,0 +1,7 @@ +// This test ensures that the `fixed_queue` feature gate is required +#![allow(unused)] + +// Attempt to use the feature without enabling it +fn main() { + let _queue = FixedQueue::::new(); //~ ERROR the type `FixedQueue` is unstable +}