orbit_core/fleet/
cursor.rs1use std::marker::PhantomData;
2
3use crate::OrbitTyped;
4use crate::fleet::{Fleet, NodeId};
5use crate::ring::cursor::{RingCursor, RingFrameSource, RingLoss, RingPoll, RingRead, poll_ring};
6use crate::ring::{Frame, RingTopology};
7
8struct FleetRingSource<'a, T: OrbitTyped> {
9 fleet: &'a Fleet,
10 _t: PhantomData<T>,
11}
12
13struct FleetLaneSource<'a, T: OrbitTyped> {
14 fleet: &'a Fleet,
15 node_id: NodeId,
16 _t: PhantomData<T>,
17}
18
19impl<'a, T: OrbitTyped> FleetLaneSource<'a, T> {
20 fn new(fleet: &'a Fleet, node_id: NodeId) -> Self {
21 Self {
22 fleet,
23 node_id,
24 _t: PhantomData,
25 }
26 }
27}
28
29impl<T: OrbitTyped> RingFrameSource for FleetLaneSource<'_, T> {
30 fn kind(&self) -> u8 {
31 T::KIND
32 }
33
34 fn head(&self) -> u64 {
35 self.fleet.lane_head::<T>(self.node_id)
36 }
37
38 fn capacity(&self) -> usize {
39 self.fleet.ring_capacity::<T>()
40 }
41
42 fn read_at(&self, counter: u64) -> Option<Frame> {
43 self.fleet.read_lane_at::<T>(self.node_id, counter)
44 }
45
46 fn read_state_at(&self, counter: u64) -> RingRead {
47 self.fleet.read_lane_state_at::<T>(self.node_id, counter)
48 }
49}
50
51#[derive(Clone, Debug, PartialEq, Eq)]
53pub struct FleetLaneCursor {
54 lanes: Vec<RingCursor>,
55 initial_counter: u64,
56}
57
58impl FleetLaneCursor {
59 pub const fn from_counter(initial_counter: u64) -> Self {
60 Self {
61 lanes: Vec::new(),
62 initial_counter,
63 }
64 }
65
66 pub fn minimum_next_counter(&self) -> u64 {
67 self.lanes
68 .iter()
69 .map(|cursor| cursor.next_counter())
70 .min()
71 .unwrap_or(self.initial_counter)
72 }
73}
74
75#[derive(Clone, Debug, Default, PartialEq, Eq)]
77pub struct FleetLanePoll {
78 pub frames: Vec<Frame>,
79 pub loss: RingLoss,
80}
81
82impl<'a, T: OrbitTyped> FleetRingSource<'a, T> {
83 fn new(fleet: &'a Fleet) -> Self {
84 Self {
85 fleet,
86 _t: PhantomData,
87 }
88 }
89}
90
91impl<T: OrbitTyped> RingFrameSource for FleetRingSource<'_, T> {
92 fn kind(&self) -> u8 {
93 T::KIND
94 }
95
96 fn head(&self) -> u64 {
97 self.fleet.head::<T>()
98 }
99
100 fn capacity(&self) -> usize {
101 self.fleet.ring_capacity::<T>()
102 }
103
104 fn read_at(&self, counter: u64) -> Option<Frame> {
105 self.fleet.read_at::<T>(counter)
106 }
107
108 fn read_state_at(&self, counter: u64) -> RingRead {
109 self.fleet.read_state_at::<T>(counter)
110 }
111}
112
113impl Fleet {
114 pub fn cursor_at_head<T: OrbitTyped>(&self) -> RingCursor {
117 RingCursor::from_counter(self.head::<T>())
118 }
119
120 pub const fn cursor_from_start<T: OrbitTyped>(&self) -> RingCursor {
122 let _ = self;
123 let _ = PhantomData::<T>;
124 RingCursor::from_start()
125 }
126
127 pub fn poll_ring<T: OrbitTyped>(&self, cursor: &mut RingCursor) -> RingPoll {
131 poll_ring(&FleetRingSource::<T>::new(self), cursor)
132 }
133
134 pub fn lane_cursor_at_head<T: OrbitTyped>(&self) -> FleetLaneCursor {
137 self.assert_per_node::<T>();
138 let lanes = (0..self.fleet_capacity())
139 .map(|node| RingCursor::from_counter(self.lane_head::<T>(NodeId::new(node))))
140 .collect();
141 FleetLaneCursor {
142 lanes,
143 initial_counter: 0,
144 }
145 }
146
147 pub fn lane_cursor_from_start<T: OrbitTyped>(&self) -> FleetLaneCursor {
150 self.assert_per_node::<T>();
151 FleetLaneCursor {
152 lanes: vec![RingCursor::from_start(); usize::from(self.fleet_capacity())],
153 initial_counter: 0,
154 }
155 }
156
157 pub fn poll_lanes<T: OrbitTyped>(&self, cursor: &mut FleetLaneCursor) -> FleetLanePoll {
161 self.assert_per_node::<T>();
162 if cursor.lanes.is_empty() {
163 cursor.lanes = vec![
164 RingCursor::from_counter(cursor.initial_counter);
165 usize::from(self.fleet_capacity())
166 ];
167 }
168 assert_eq!(
169 cursor.lanes.len(),
170 usize::from(self.fleet_capacity()),
171 "lane cursor belongs to a different fleet capacity"
172 );
173
174 let mut combined = FleetLanePoll::default();
175 for (node, lane_cursor) in cursor.lanes.iter_mut().enumerate() {
176 let source = FleetLaneSource::<T>::new(self, NodeId::new(node as u16));
177 let poll = poll_ring(&source, lane_cursor);
178 combined.frames.extend(poll.frames);
179 combined.loss.overwritten = combined
180 .loss
181 .overwritten
182 .saturating_add(poll.loss.overwritten);
183 combined.loss.unavailable = combined
184 .loss
185 .unavailable
186 .saturating_add(poll.loss.unavailable);
187 }
188 combined
189 }
190
191 fn assert_per_node<T: OrbitTyped>(&self) {
192 assert_eq!(
193 T::RING_SPEC.topology,
194 RingTopology::PerNode,
195 "lane cursor requires a per-node ring"
196 );
197 }
198}