Skip to content

Commit ceefd1b

Browse files
author
Adrian Willenbücher
committed
Implement AbiQueue
1 parent d657f83 commit ceefd1b

File tree

5 files changed

+461
-3
lines changed

5 files changed

+461
-3
lines changed

src/abi_datatypes/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515

1616
extern crate alloc;
1717

18+
mod queue;
1819
mod storage;
1920
mod vec;
2021

22+
pub use self::queue::AbiQueue;
23+
pub use self::queue::FixedQueue;
2124
pub use self::vec::AbiVec;
2225
pub use self::vec::FixedVec;

src/abi_datatypes/src/queue.rs

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
// *******************************************************************************
2+
// Copyright (c) 2025 Contributors to the Eclipse Foundation
3+
//
4+
// See the NOTICE file(s) distributed with this work for additional
5+
// information regarding copyright ownership.
6+
//
7+
// This program and the accompanying materials are made available under the
8+
// terms of the Apache License Version 2.0 which is available at
9+
// https://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
// *******************************************************************************
13+
14+
mod generic;
15+
16+
use core::mem::needs_drop;
17+
use core::ops;
18+
19+
use self::generic::GenericQueue;
20+
use crate::storage::Heap;
21+
use crate::storage::Inline;
22+
23+
/// A fixed-capacity, ABI-compatible queue.
24+
///
25+
/// The queue can hold between 0 and `CAPACITY` elements, and behaves similarly to Rust's `VecDeque`,
26+
/// except that it stores the elements inline and doesn't allocate.
27+
///
28+
/// `CAPACITY` must be `>= 1` and `<= u32::MAX`.
29+
#[repr(transparent)]
30+
pub struct AbiQueue<T: Copy, const CAPACITY: usize> {
31+
inner: GenericQueue<T, Inline<T, CAPACITY>>,
32+
}
33+
34+
impl<T: Copy, const CAPACITY: usize> AbiQueue<T, CAPACITY> {
35+
const CHECK_CAPACITY: () = assert!(0 < CAPACITY && CAPACITY <= u32::MAX as usize);
36+
37+
/// Creates an empty queue.
38+
#[must_use]
39+
pub fn new() -> Self {
40+
let () = Self::CHECK_CAPACITY;
41+
42+
Self {
43+
inner: GenericQueue::new(CAPACITY as u32),
44+
}
45+
}
46+
}
47+
48+
impl<T: Copy, const CAPACITY: usize> Default for AbiQueue<T, CAPACITY> {
49+
fn default() -> Self {
50+
Self::new()
51+
}
52+
}
53+
54+
impl<T: Copy, const CAPACITY: usize> ops::Deref for AbiQueue<T, CAPACITY> {
55+
type Target = GenericQueue<T, Inline<T, CAPACITY>>;
56+
57+
fn deref(&self) -> &Self::Target {
58+
&self.inner
59+
}
60+
}
61+
62+
impl<T: Copy, const CAPACITY: usize> ops::DerefMut for AbiQueue<T, CAPACITY> {
63+
fn deref_mut(&mut self) -> &mut Self::Target {
64+
&mut self.inner
65+
}
66+
}
67+
68+
/// A fixed-capacity vector.
69+
///
70+
/// The vector can hold between 0 and `CAPACITY` elements, and behaves similarly to Rust's `Vec`,
71+
/// except that it allocates memory immediately on construction, and can't shrink or grow.
72+
pub struct FixedQueue<T> {
73+
inner: GenericQueue<T, Heap<T>>,
74+
}
75+
76+
impl<T> FixedQueue<T> {
77+
/// Creates an empty queue and allocates memory for up to `capacity` elements, where `capacity <= u32::MAX`.
78+
///
79+
/// # Panics
80+
///
81+
/// - Panics if `capacity > u32::MAX`.
82+
/// - Panics if the memory allocation fails.
83+
#[must_use]
84+
pub fn new(capacity: usize) -> Self {
85+
assert!(capacity <= u32::MAX as usize, "FixedQueue can hold at most u32::MAX elements");
86+
Self {
87+
inner: GenericQueue::new(capacity as u32),
88+
}
89+
}
90+
}
91+
92+
impl<T> Drop for FixedQueue<T> {
93+
fn drop(&mut self) {
94+
if needs_drop::<T>() {
95+
self.inner.clear();
96+
}
97+
}
98+
}
99+
100+
impl<T> ops::Deref for FixedQueue<T> {
101+
type Target = GenericQueue<T, Heap<T>>;
102+
103+
fn deref(&self) -> &Self::Target {
104+
&self.inner
105+
}
106+
}
107+
108+
impl<T> ops::DerefMut for FixedQueue<T> {
109+
fn deref_mut(&mut self) -> &mut Self::Target {
110+
&mut self.inner
111+
}
112+
}
113+
114+
#[cfg(test)]
115+
mod tests {
116+
use std::collections::VecDeque;
117+
118+
use super::*;
119+
120+
fn to_vec<T: Copy>((first, second): (&[T], &[T])) -> Vec<T> {
121+
let mut elements = first.to_vec();
122+
elements.extend_from_slice(second);
123+
elements
124+
}
125+
126+
#[test]
127+
fn abi_queue_push_and_pop() {
128+
fn run_test<const N: usize>() {
129+
let mut queue = AbiQueue::<i64, N>::new();
130+
let mut control = VecDeque::new();
131+
132+
// Completely fill and empty the queue N times, but move the internal start point
133+
// ahead by one each time
134+
for _ in 0..N {
135+
let result = queue.pop_front();
136+
assert_eq!(result, None);
137+
138+
for i in 0..N {
139+
let value = i as i64 * 123 + 456;
140+
let result = queue.push_back(value);
141+
assert_eq!(*result.unwrap(), value);
142+
control.push_back(value);
143+
assert_eq!(to_vec(queue.as_slices()), to_vec(control.as_slices()));
144+
}
145+
146+
let result = queue.push_back(123456);
147+
assert!(result.is_err());
148+
149+
for _ in 0..N {
150+
let expected = control.pop_front().unwrap();
151+
let actual = queue.pop_front();
152+
assert_eq!(actual, Some(expected));
153+
}
154+
155+
let result = queue.pop_front();
156+
assert_eq!(result, None);
157+
158+
// One push and one pop to move the internal start point ahead
159+
queue.push_back(987).unwrap();
160+
assert_eq!(queue.pop_front(), Some(987));
161+
}
162+
}
163+
164+
run_test::<1>();
165+
run_test::<2>();
166+
run_test::<3>();
167+
run_test::<4>();
168+
run_test::<5>();
169+
}
170+
171+
#[test]
172+
fn abi_queue_is_empty_and_is_full() {
173+
fn run_test<const N: usize>() {
174+
let mut queue = AbiQueue::<i64, N>::new();
175+
176+
// Completely fill and empty the queue N times, but move the internal start point
177+
// ahead by one each time
178+
for _ in 0..N {
179+
assert!(queue.is_empty());
180+
181+
for i in 0..N {
182+
assert!(!queue.is_full());
183+
queue.push_back(i as i64 * 123 + 456).unwrap();
184+
assert!(!queue.is_empty());
185+
}
186+
187+
assert!(queue.is_full());
188+
189+
for _ in 0..N {
190+
assert!(!queue.is_empty());
191+
queue.pop_front();
192+
assert!(!queue.is_full());
193+
}
194+
195+
assert!(queue.is_empty());
196+
197+
// One push and one pop to move the internal start point ahead
198+
queue.push_back(987).unwrap();
199+
assert_eq!(queue.pop_front(), Some(987));
200+
}
201+
}
202+
203+
run_test::<1>();
204+
run_test::<2>();
205+
run_test::<3>();
206+
run_test::<4>();
207+
run_test::<5>();
208+
}
209+
210+
#[test]
211+
fn fixed_queue_push_and_pop() {
212+
fn run_test(n: usize) {
213+
let mut queue = FixedQueue::<i64>::new(n);
214+
let mut control = VecDeque::new();
215+
216+
// Completely fill and empty the queue n times, but move the internal start point
217+
// ahead by one each time
218+
for _ in 0..n {
219+
let result = queue.pop_front();
220+
assert_eq!(result, None);
221+
222+
for i in 0..n {
223+
let value = i as i64 * 123 + 456;
224+
let result = queue.push_back(value);
225+
assert_eq!(*result.unwrap(), value);
226+
control.push_back(value);
227+
assert_eq!(to_vec(queue.as_slices()), to_vec(control.as_slices()));
228+
}
229+
230+
let result = queue.push_back(123456);
231+
assert!(result.is_err());
232+
233+
for _ in 0..n {
234+
let expected = control.pop_front().unwrap();
235+
let actual = queue.pop_front();
236+
assert_eq!(actual, Some(expected));
237+
}
238+
239+
let result = queue.pop_front();
240+
assert_eq!(result, None);
241+
242+
// One push and one pop to move the internal start point ahead
243+
queue.push_back(987).unwrap();
244+
assert_eq!(queue.pop_front(), Some(987));
245+
}
246+
}
247+
248+
for i in 0..6 {
249+
run_test(i);
250+
}
251+
}
252+
253+
#[test]
254+
fn fixed_queue_is_empty_and_is_full() {
255+
fn run_test(n: usize) {
256+
let mut queue = FixedQueue::<i64>::new(n);
257+
258+
// Completely fill and empty the queue n times, but move the internal start point
259+
// ahead by one each time
260+
for _ in 0..n {
261+
assert!(queue.is_empty());
262+
263+
for i in 0..n {
264+
assert!(!queue.is_full());
265+
queue.push_back(i as i64 * 123 + 456).unwrap();
266+
assert!(!queue.is_empty());
267+
}
268+
269+
assert!(queue.is_full());
270+
271+
for _ in 0..n {
272+
assert!(!queue.is_empty());
273+
queue.pop_front();
274+
assert!(!queue.is_full());
275+
}
276+
277+
assert!(queue.is_empty());
278+
279+
// One push and one pop to move the internal start point ahead
280+
queue.push_back(987).unwrap();
281+
assert_eq!(queue.pop_front(), Some(987));
282+
}
283+
}
284+
285+
for i in 0..6 {
286+
run_test(i);
287+
}
288+
}
289+
}

0 commit comments

Comments
 (0)