1use buggy::{BugExt as _, bug};
2use heapless::Vec;
3use serde::{Deserialize, Serialize};
4
5use super::{
6 COMMAND_RESPONSE_MAX, COMMAND_SAMPLE_MAX, MAX_SYNC_MESSAGE_SIZE, PEER_HEAD_MAX, PollIncoming,
7 SEGMENT_BUFFER_MAX, SyncError,
8 requester::SyncRequestMessage,
9 wire::{CommandMeta, SyncType},
10};
11use crate::{
12 LocatedAddress, Prior, StorageError,
13 command::{Address, CmdId, Command as _},
14 storage::{
15 GraphId, Location, MaxCut, Segment as _, Storage, StorageProvider, TraversalBuffer,
16 TraversalBuffers,
17 },
18};
19
20#[derive(Default, Debug)]
21pub struct PeerCache {
22 heads: Vec<LocatedAddress, { PEER_HEAD_MAX }>,
23}
24
25impl PeerCache {
26 pub const fn new() -> Self {
27 Self { heads: Vec::new() }
28 }
29
30 pub fn heads(&self) -> &[LocatedAddress] {
31 &self.heads
32 }
33
34 pub fn add_command<S>(
35 &mut self,
36 storage: &S,
37 new: LocatedAddress,
38 buffer: &mut TraversalBuffer,
39 ) -> Result<(), StorageError>
40 where
41 S: Storage,
42 {
43 let mut add_command = true;
44
45 let mut retain_head = |old: &LocatedAddress| -> Result<bool, StorageError> {
46 if old.id == new.id || storage.is_ancestor(new.location(), old.location(), buffer)? {
47 add_command = false;
49 return Ok(true);
50 }
51 if storage.is_ancestor(old.location(), new.location(), buffer)? {
52 return Ok(false);
54 }
55 Ok(true)
57 };
58 self.heads.retain(|h| retain_head(h).unwrap_or(false));
59 if add_command {
60 self.heads.push(new).ok();
62 }
63
64 Ok(())
65 }
66}
67
68#[derive(Serialize, Deserialize, Debug)]
75#[allow(clippy::large_enum_variant)]
76pub(crate) enum SyncResponseMessage {
77 SyncResponse {
79 session_id: u128,
81 response_index: u64,
86 commands: Vec<CommandMeta, COMMAND_RESPONSE_MAX>,
88 },
89
90 SyncEnd {
93 session_id: u128,
96 max_index: u64,
98 remaining: bool,
101 },
102
103 Offer {
108 session_id: u128,
111 head: CmdId,
113 },
114
115 EndSession { session_id: u128 },
118}
119
120impl SyncResponseMessage {
121 pub(crate) fn session_id(&self) -> u128 {
122 match self {
123 Self::SyncResponse { session_id, .. } => *session_id,
124 Self::SyncEnd { session_id, .. } => *session_id,
125 Self::Offer { session_id, .. } => *session_id,
126 Self::EndSession { session_id, .. } => *session_id,
127 }
128 }
129}
130
131#[derive(Debug, Default)]
132enum SyncResponderState {
133 #[default]
134 New,
135 Start,
136 Send,
137 Idle,
138 Reset,
139 Stopped,
140}
141
142pub struct SyncResponder {
143 session_id: Option<u128>,
144 graph_id: Option<GraphId>,
145 state: SyncResponderState,
146 bytes_sent: u64,
147 next_send: usize,
148 message_index: usize,
149 has: Vec<Address, COMMAND_SAMPLE_MAX>,
150 to_send: Vec<Location, SEGMENT_BUFFER_MAX>,
151}
152
153impl Default for SyncResponder {
154 fn default() -> Self {
155 Self::new()
156 }
157}
158
159fn push_bounded(v: &mut Vec<Location, SEGMENT_BUFFER_MAX>, loc: Location) {
163 if v.push(loc).is_err() {
164 let (max_idx, _) = v
166 .iter()
167 .enumerate()
168 .max_by_key(|(_, l)| l.max_cut)
169 .expect("non-empty");
170 if loc.max_cut < v[max_idx].max_cut {
171 v[max_idx] = loc;
172 }
173 }
174}
175
176fn skip_jump<S: Storage>(
181 storage: &S,
182 head: Location,
183 target: MaxCut,
184) -> Result<Location, StorageError> {
185 if head.max_cut <= target {
186 return Ok(head);
187 }
188 let mut current = head;
189 loop {
190 let seg = storage.get_segment(current)?;
191
192 let best = seg
194 .skip_list()
195 .iter()
196 .copied()
197 .filter(|s| s.max_cut >= target && s.max_cut < current.max_cut)
198 .min_by_key(|s| s.max_cut);
199 if let Some(skip) = best {
200 current = skip;
201 continue;
202 }
203
204 let prior_below = match seg.prior() {
206 Prior::Single(p) => p.max_cut < target,
207 Prior::Merge(a, b) => a.max_cut < target || b.max_cut < target,
208 Prior::None => true,
209 };
210 if prior_below {
211 return Ok(current);
212 }
213
214 match seg.prior() {
215 Prior::Single(p) => current = p,
216 _ => return Ok(current),
217 }
218 }
219}
220
221impl SyncResponder {
222 pub const fn new() -> Self {
224 Self {
225 session_id: None,
226 graph_id: None,
227 state: SyncResponderState::New,
228 bytes_sent: 0,
229 next_send: 0,
230 message_index: 0,
231 has: Vec::new(),
232 to_send: Vec::new(),
233 }
234 }
235
236 pub fn ready(&self) -> bool {
238 use SyncResponderState::*;
239 match self.state {
240 Reset | Start | Send => true, New | Idle | Stopped => false,
242 }
243 }
244
245 pub fn poll(
248 &mut self,
249 target: &mut [u8],
250 provider: &mut impl StorageProvider,
251 response_cache: &mut PeerCache,
252 buffers: &mut TraversalBuffers,
253 ) -> Result<usize, SyncError> {
254 use SyncResponderState as S;
256 let length = match self.state {
257 S::New | S::Idle | S::Stopped => {
258 return Err(SyncError::NotReady); }
260 S::Start => {
261 let Some(graph_id) = self.graph_id else {
262 self.state = S::Reset;
263 bug!("poll called before graph_id was set");
264 };
265
266 let storage = match provider.get_storage(graph_id) {
267 Ok(s) => s,
268 Err(e) => {
269 self.state = S::Reset;
270 return Err(e.into());
271 }
272 };
273
274 self.state = S::Send;
275 for command in &self.has {
276 if let Some(cmd_loc) = storage.get_location(*command, &mut buffers.primary)? {
278 response_cache.add_command(
279 storage,
280 LocatedAddress {
281 id: command.id,
282 segment: cmd_loc.segment,
283 max_cut: command.max_cut,
284 },
285 &mut buffers.primary,
286 )?;
287 }
288 }
289 self.to_send = Self::find_needed_segments(&self.has, storage, buffers)?;
290
291 self.get_next(target, provider)?
292 }
293 S::Send => self.get_next(target, provider)?,
294 S::Reset => {
295 self.state = S::Stopped;
296 let message = SyncResponseMessage::EndSession {
297 session_id: self.session_id()?,
298 };
299 Self::write(target, message)?
300 }
301 };
302
303 Ok(length)
304 }
305
306 pub fn receive(&mut self, poll: PollIncoming) -> Result<(), SyncError> {
308 self.dispatch(poll.message)
309 }
310
311 pub fn start_session(
318 &mut self,
319 session_id: u128,
320 graph_id: GraphId,
321 max_bytes: u64,
322 heads: impl IntoIterator<Item = Address>,
323 ) -> Result<(), SyncError> {
324 let mut commands: Vec<Address, COMMAND_SAMPLE_MAX> = Vec::new();
325 heads
326 .into_iter()
327 .try_for_each(|head| commands.push(head).ok())
328 .ok_or(SyncError::CommandOverflow)?;
329 self.dispatch(SyncRequestMessage::SyncRequest {
330 session_id,
331 graph_id,
332 max_bytes,
333 commands,
334 })
335 }
336
337 fn dispatch(&mut self, message: SyncRequestMessage) -> Result<(), SyncError> {
338 if self.session_id.is_none() {
339 self.session_id = Some(message.session_id());
340 }
341 if self.session_id != Some(message.session_id()) {
342 return Err(SyncError::SessionMismatch);
343 }
344
345 match message {
346 SyncRequestMessage::SyncRequest {
347 graph_id,
348 max_bytes,
349 commands,
350 ..
351 } => {
352 self.state = SyncResponderState::Start;
353 self.graph_id = Some(graph_id);
354 self.bytes_sent = max_bytes;
355 self.to_send = Vec::new();
356 self.has = commands;
357 self.next_send = 0;
358 return Ok(());
359 }
360 SyncRequestMessage::RequestMissing { .. } => {
361 todo!()
362 }
363 SyncRequestMessage::SyncResume { .. } => {
364 todo!()
365 }
366 SyncRequestMessage::EndSession { .. } => {
367 self.state = SyncResponderState::Stopped;
368 }
369 }
370
371 Ok(())
372 }
373
374 fn write_sync_type(target: &mut [u8], msg: SyncType) -> Result<usize, SyncError> {
375 Ok(postcard::to_slice(&msg, target)?.len())
376 }
377
378 fn write(target: &mut [u8], msg: SyncResponseMessage) -> Result<usize, SyncError> {
379 Ok(postcard::to_slice(&msg, target)?.len())
380 }
381
382 fn find_needed_segments(
388 commands: &[Address],
389 storage: &impl Storage,
390 buffers: &mut TraversalBuffers,
391 ) -> Result<Vec<Location, SEGMENT_BUFFER_MAX>, SyncError> {
392 if commands.len() > COMMAND_SAMPLE_MAX {
395 bug!(
396 "commands length {} exceeds COMMAND_SAMPLE_MAX",
397 commands.len()
398 );
399 }
400 let mut have_locations: Vec<Location, COMMAND_SAMPLE_MAX> = Vec::new();
401 for &addr in commands {
402 if let Some(location) = storage.get_location(addr, &mut buffers.primary)? {
403 let _ = have_locations.push(location);
404 }
405 }
406
407 have_locations.sort_by_key(|loc| core::cmp::Reverse(loc.max_cut));
410
411 let mut have_cursor: usize = 0;
414
415 let heads = buffers.primary.get();
417
418 let head = storage.get_head()?;
422 let highest_have = have_locations
423 .first()
424 .map(|l| l.max_cut)
425 .unwrap_or(MaxCut::new(0));
426 let skip_target = highest_have
427 .checked_add(SEGMENT_BUFFER_MAX as u64)
428 .assume("skip target overflow")?;
429 let start = skip_jump(storage, head, skip_target)?;
430 heads.push(start)?;
431
432 let pending = buffers.secondary.get();
434
435 let mut collected: Vec<Location, SEGMENT_BUFFER_MAX> = Vec::new();
439 let mut prev_max_cut: Option<MaxCut> = None;
440
441 while let Some((head, covered)) = heads.pop_covered()? {
442 if prev_max_cut != Some(head.max_cut) {
446 pending.drain_above(head.max_cut, |loc| push_bounded(&mut collected, loc))?;
447 prev_max_cut = Some(head.max_cut);
448 }
449
450 let segment = storage.get_segment(head)?;
451
452 if covered {
453 let longest = segment.longest_max_cut()?;
457 pending.cover_up_to(head.segment, head.max_cut, longest)?;
458 for prior in segment.prior() {
461 heads.push_covered(prior, true)?;
462 }
463 if heads.all_covered() && !heads.is_empty() {
466 break;
467 }
468 continue;
469 }
470
471 let longest = segment.longest_max_cut()?;
474 while have_locations
475 .get(have_cursor)
476 .is_some_and(|h| h.max_cut > longest)
477 {
478 have_cursor = have_cursor
479 .checked_add(1)
480 .assume("index must not overflow")?;
481 }
482
483 let shortest = segment.shortest_max_cut();
486 let mut best_have: Option<(usize, Location)> = None;
487 for scan in have_cursor..have_locations.len() {
488 let hloc = have_locations[scan];
489 if hloc.max_cut < shortest {
490 break; }
492 if hloc.segment == head.segment {
493 best_have = Some((scan, hloc));
494 break; }
496 }
497
498 if let Some((_idx, hloc)) = best_have {
499 for prior in segment.prior() {
503 heads.push_covered(prior, true)?;
504 }
505
506 if hloc.max_cut < longest {
510 let next_max_cut = hloc
511 .max_cut
512 .checked_add(1)
513 .assume("command + 1 mustn't overflow")?;
514 let partial_loc = Location {
515 max_cut: next_max_cut,
516 segment: head.segment,
517 };
518 pending.push(partial_loc)?;
519 }
520 } else {
522 pending.push(segment.first_location())?;
525 for prior in segment.prior() {
526 heads.push(prior)?;
527 }
528 }
529
530 if heads.all_covered() && !heads.is_empty() {
533 break;
534 }
535 }
536
537 pending.drain_all(|loc| push_bounded(&mut collected, loc));
540
541 collected.sort();
543
544 Ok(collected)
545 }
546
547 fn get_next(
548 &mut self,
549 target: &mut [u8],
550 provider: &mut impl StorageProvider,
551 ) -> Result<usize, SyncError> {
552 if self.next_send >= self.to_send.len() {
553 self.state = SyncResponderState::Idle;
554 let message = SyncResponseMessage::SyncEnd {
555 session_id: self.session_id()?,
556 max_index: self.message_index as u64,
557 remaining: false,
558 };
559 let length = Self::write(target, message)?;
560 return Ok(length);
561 }
562
563 let (commands, command_data, next_send) = self.get_commands(provider)?;
564
565 let message = SyncResponseMessage::SyncResponse {
566 session_id: self.session_id()?,
567 response_index: self.message_index as u64,
568 commands,
569 };
570 self.message_index = self
571 .message_index
572 .checked_add(1)
573 .assume("message_index overflow")?;
574 self.next_send = next_send;
575
576 let length = Self::write(target, message)?;
577 let total_length = length
578 .checked_add(command_data.len())
579 .assume("length + command_data_length mustn't overflow")?;
580 target
581 .get_mut(length..total_length)
582 .assume("sync message fits in target")?
583 .copy_from_slice(&command_data);
584 Ok(total_length)
585 }
586
587 pub fn push(
590 &mut self,
591 target: &mut [u8],
592 provider: &mut impl StorageProvider,
593 buffers: &mut TraversalBuffers,
594 ) -> Result<usize, SyncError> {
595 use SyncResponderState as S;
596 let Some(graph_id) = self.graph_id else {
597 self.state = S::Reset;
598 bug!("poll called before graph_id was set");
599 };
600
601 let storage = match provider.get_storage(graph_id) {
602 Ok(s) => s,
603 Err(e) => {
604 self.state = S::Reset;
605 return Err(e.into());
606 }
607 };
608 self.to_send = Self::find_needed_segments(&self.has, storage, buffers)?;
609 let (commands, command_data, next_send) = self.get_commands(provider)?;
610 let mut length = 0;
611 if !commands.is_empty() {
612 let message = SyncType::Push {
613 message: SyncResponseMessage::SyncResponse {
614 session_id: self.session_id()?,
615 response_index: self.message_index as u64,
616 commands,
617 },
618 graph_id: self.graph_id.assume("graph id must exist")?,
619 };
620 self.message_index = self
621 .message_index
622 .checked_add(1)
623 .assume("message_index increment overflow")?;
624 self.next_send = next_send;
625
626 length = Self::write_sync_type(target, message)?;
627 let total_length = length
628 .checked_add(command_data.len())
629 .assume("length + command_data_length mustn't overflow")?;
630 target
631 .get_mut(length..total_length)
632 .assume("sync message fits in target")?
633 .copy_from_slice(&command_data);
634 length = total_length;
635 }
636 Ok(length)
637 }
638
639 fn get_commands(
640 &mut self,
641 provider: &mut impl StorageProvider,
642 ) -> Result<
643 (
644 Vec<CommandMeta, COMMAND_RESPONSE_MAX>,
645 Vec<u8, MAX_SYNC_MESSAGE_SIZE>,
646 usize,
647 ),
648 SyncError,
649 > {
650 let Some(graph_id) = self.graph_id.as_ref() else {
651 self.state = SyncResponderState::Reset;
652 bug!("get_next called before graph_id was set");
653 };
654 let storage = match provider.get_storage(*graph_id) {
655 Ok(s) => s,
656 Err(e) => {
657 self.state = SyncResponderState::Reset;
658 return Err(e.into());
659 }
660 };
661 let mut commands: Vec<CommandMeta, COMMAND_RESPONSE_MAX> = Vec::new();
662 let mut command_data: Vec<u8, MAX_SYNC_MESSAGE_SIZE> = Vec::new();
663 let mut index = self.next_send;
664 for i in self.next_send..self.to_send.len() {
665 if commands.is_full() {
666 break;
667 }
668 index = index.checked_add(1).assume("index + 1 mustn't overflow")?;
669 let Some(&location) = self.to_send.get(i) else {
670 self.state = SyncResponderState::Reset;
671 bug!("send index OOB");
672 };
673
674 let segment = storage
675 .get_segment(location)
676 .inspect_err(|_| self.state = SyncResponderState::Reset)?;
677
678 let found = segment.get_from(location);
679
680 for command in &found {
681 let mut policy_length = 0;
682
683 if let Some(policy) = command.policy() {
684 policy_length = policy.len();
685 command_data
686 .extend_from_slice(policy)
687 .ok()
688 .assume("command_data is too large")?;
689 }
690
691 let bytes = command.bytes();
692 command_data
693 .extend_from_slice(bytes)
694 .ok()
695 .assume("command_data is too large")?;
696
697 let max_cut = command.max_cut()?;
698 let meta = CommandMeta {
699 id: command.id(),
700 priority: command.priority(),
701 parent: command.parent(),
702 policy_length: policy_length as u32,
703 length: bytes.len() as u32,
704 max_cut,
705 };
706
707 commands
709 .push(meta)
710 .ok()
711 .assume("too many commands in segment")?;
712 if commands.is_full() {
713 break;
714 }
715 }
716 }
717 Ok((commands, command_data, index))
718 }
719
720 fn session_id(&self) -> Result<u128, SyncError> {
721 Ok(self.session_id.assume("session id is set")?)
722 }
723}