ruda_runtime/runtime/stream/base.rs
1use alloc::vec::Vec;
2use ruda_core::stream_id::StreamId;
3
4/// Trait for creating streams, used by the stream pool to generate streams as needed.
5pub trait StreamFactory {
6 /// The type of stream produced by this factory.
7 type Stream;
8 /// Creates a new stream instance.
9 fn create(&mut self) -> Self::Stream;
10}
11
12/// Represents a pool of streams, managing a collection of streams created by a factory.
13#[derive(Debug)]
14pub struct StreamPool<F: StreamFactory> {
15 /// Vector storing optional streams, where None indicates an uninitialized stream.
16 streams: Vec<Option<F::Stream>>,
17 /// The factory used to create new streams when needed.
18 factory: F,
19 /// Maximum number of regular streams (excludes special streams).
20 max_streams: usize,
21}
22
23impl<F: StreamFactory> StreamPool<F> {
24 /// Creates a new stream pool with the given backend factory and capacity constraints.
25 pub fn new(backend: F, max_streams: u8, num_special: u8) -> Self {
26 // Initialize a vector with capacity for regular and special streams.
27 let mut streams = Vec::with_capacity(max_streams as usize);
28 // Pre-populate the vector with None to reserve space for all streams.
29 for _ in 0..(max_streams.saturating_add(num_special)) {
30 streams.push(None);
31 }
32
33 Self {
34 streams,
35 factory: backend,
36 max_streams: max_streams as usize,
37 }
38 }
39
40 /// Retrieves a mutable reference to a stream for a given stream ID.
41 pub fn get_mut(&mut self, stream_id: &StreamId) -> &mut F::Stream {
42 // Calculate the index for the stream ID.
43 let index = self.stream_index(stream_id);
44
45 // Use unsafe method to retrieve the stream, assuming the index is valid.
46 //
47 // # Safety
48 //
49 // * The `stream_index` function ensures the index is within bounds.
50 unsafe { self.get_mut_index(index) }
51 }
52
53 /// Initialize an unused regular slot with a backend-specific stream. This
54 /// does not modulo-map IDs or replace a stream that may have pending work.
55 /// Returns false for an occupied slot or an ID outside the regular pool.
56 pub fn try_initialize_with<E>(
57 &mut self, stream_id: &StreamId,
58 initialize: impl FnOnce(&mut F) -> Result<F::Stream, E>,
59 ) -> Result<bool, E> {
60 let Ok(index) = usize::try_from(stream_id.value) else { return Ok(false); };
61 if index >= self.max_streams || self.streams[index].is_some() { return Ok(false); }
62 let stream = initialize(&mut self.factory)?;
63 self.streams[index] = Some(stream);
64 Ok(true)
65 }
66
67 /// Retrieves a mutable reference to a stream at the specified index, initializing it if needed.
68 ///
69 /// # Safety
70 ///
71 /// * Caller must ensure the index is valid (less than `max_streams + num_special`).
72 /// * Lifetimes still follow the Rust rules.
73 pub unsafe fn get_mut_index(&mut self, index: usize) -> &mut F::Stream {
74 unsafe {
75 // Access the stream entry without bounds checking for performance.
76 let entry = self.streams.get_unchecked_mut(index);
77 match entry {
78 // If the stream exists, return it.
79 Some(val) => val,
80 // If the stream is None, create a new one using the factory.
81 None => {
82 let stream = self.factory.create();
83 // Store the new stream in the vector.
84 *entry = Some(stream);
85
86 // Re-access the entry, which is now guaranteed to be Some.
87 match entry {
88 Some(val) => val,
89 // Unreachable because we just set it to Some.
90 None => unreachable!(),
91 }
92 }
93 }
94 }
95 }
96
97 /// Retrieves a mutable reference to a special stream at the given index.
98 ///
99 /// # Safety
100 ///
101 /// * Caller must ensure the index corresponds to a valid special stream.
102 /// * Lifetimes still follow the Rust rules.
103 pub unsafe fn get_special(&mut self, index: u8) -> &mut F::Stream {
104 // Calculate the index for the special stream (offset by max_streams).
105 unsafe { self.get_mut_index(self.max_streams + index as usize) }
106 }
107
108 /// Calculates the index for a given stream ID, mapping it to the pool's capacity.
109 pub fn stream_index(&mut self, id: &StreamId) -> usize {
110 stream_index(id, self.max_streams)
111 }
112}
113
114/// Maps a stream ID to an index within the pool's capacity using modulo arithmetic.
115pub fn stream_index(stream_id: &StreamId, max_streams: usize) -> usize {
116 stream_id.value as usize % max_streams
117}