1use super::*;
2use crate::transport::PacketBuffer;
3use std::ops::Range;
4use std::sync::Arc;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct FipsEndpointDirectPacketRunMeta {
9 source_peer: PeerIdentity,
10 previous_hop_addr: NodeAddr,
11 received_k_bit: bool,
12 direct_path: bool,
13 enqueued_at_ms: u64,
14}
15
16impl FipsEndpointDirectPacketRunMeta {
17 pub(crate) fn new(
18 source_peer: PeerIdentity,
19 previous_hop_addr: NodeAddr,
20 received_k_bit: bool,
21 direct_path: bool,
22 enqueued_at_ms: u64,
23 ) -> Self {
24 Self {
25 source_peer,
26 previous_hop_addr,
27 received_k_bit,
28 direct_path,
29 enqueued_at_ms,
30 }
31 }
32
33 pub fn source_peer(&self) -> &PeerIdentity {
35 &self.source_peer
36 }
37
38 pub fn source_node_addr(&self) -> &NodeAddr {
40 self.source_peer.node_addr()
41 }
42
43 pub fn source_npub(&self) -> String {
45 self.source_peer.npub()
46 }
47
48 pub fn previous_hop_node_addr(&self) -> &NodeAddr {
50 &self.previous_hop_addr
51 }
52
53 pub fn is_direct_path(&self) -> bool {
55 self.direct_path
56 }
57
58 pub fn received_k_bit(&self) -> bool {
60 self.received_k_bit
61 }
62
63 pub fn enqueued_at_ms(&self) -> u64 {
65 self.enqueued_at_ms
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct FipsEndpointDirectSourceRun {
72 source_peer: PeerIdentity,
73 packets: Vec<PacketBuffer>,
74 enqueued_at_ms: u64,
75}
76
77impl FipsEndpointDirectSourceRun {
78 pub(crate) fn from_source_packets(
79 source_peer: PeerIdentity,
80 packets: Vec<PacketBuffer>,
81 enqueued_at_ms: u64,
82 ) -> Self {
83 Self {
84 source_peer,
85 packets,
86 enqueued_at_ms,
87 }
88 }
89
90 pub fn source_peer(&self) -> &PeerIdentity {
92 &self.source_peer
93 }
94
95 pub fn source_node_addr(&self) -> &NodeAddr {
97 self.source_peer.node_addr()
98 }
99
100 pub fn source_npub(&self) -> String {
102 self.source_peer.npub()
103 }
104
105 pub fn enqueued_at_ms(&self) -> u64 {
107 self.enqueued_at_ms
108 }
109
110 pub fn packets(&self) -> &[PacketBuffer] {
112 &self.packets
113 }
114
115 pub fn into_parts(self) -> (PeerIdentity, Vec<PacketBuffer>) {
117 (self.source_peer, self.packets)
118 }
119
120 pub fn into_packets(self) -> Vec<PacketBuffer> {
122 self.packets
123 }
124
125 pub fn len(&self) -> usize {
127 self.packets.len()
128 }
129
130 pub fn is_empty(&self) -> bool {
132 self.packets.is_empty()
133 }
134}
135
136#[derive(Debug, Clone, PartialEq, Eq)]
138struct FipsEndpointDirectPacketSegment {
139 buffer: Arc<PacketBuffer>,
140 ranges: Vec<Range<usize>>,
141 packet_bytes: usize,
142}
143
144impl FipsEndpointDirectPacketSegment {
145 fn new(buffer: PacketBuffer, ranges: Vec<Range<usize>>) -> Self {
146 Self::from_shared_buffer(Arc::new(buffer), ranges)
147 }
148
149 fn from_shared_buffer(buffer: Arc<PacketBuffer>, ranges: Vec<Range<usize>>) -> Self {
150 debug_assert!(ranges.windows(2).all(|pair| pair[0].end <= pair[1].start));
151 let packet_bytes = ranges.iter().map(|range| range.len()).sum();
152 Self {
153 buffer,
154 ranges,
155 packet_bytes,
156 }
157 }
158
159 fn len(&self) -> usize {
160 self.ranges.len()
161 }
162
163 fn is_empty(&self) -> bool {
164 self.ranges.is_empty()
165 }
166
167 fn push_range_from_shared_buffer(
168 &mut self,
169 buffer: &Arc<PacketBuffer>,
170 range: Range<usize>,
171 ) -> bool {
172 if !Arc::ptr_eq(&self.buffer, buffer) {
173 return false;
174 }
175 if self
176 .ranges
177 .last()
178 .is_some_and(|previous| previous.end > range.start)
179 {
180 return false;
181 }
182 self.packet_bytes = self.packet_bytes.saturating_add(range.len());
183 self.ranges.push(range);
184 true
185 }
186}
187
188#[derive(Debug)]
189struct FipsEndpointDirectPacketSplitGroup {
190 lane: usize,
191 segments: Vec<FipsEndpointDirectPacketSegment>,
192}
193
194impl FipsEndpointDirectPacketSplitGroup {
195 fn new(lane: usize) -> Self {
196 Self {
197 lane,
198 segments: Vec::new(),
199 }
200 }
201
202 fn push(&mut self, buffer: Arc<PacketBuffer>, range: Range<usize>) {
203 if let Some(last) = self.segments.last_mut()
204 && last.push_range_from_shared_buffer(&buffer, range.clone())
205 {
206 return;
207 }
208 self.segments
209 .push(FipsEndpointDirectPacketSegment::from_shared_buffer(
210 buffer,
211 vec![range],
212 ));
213 }
214}
215
216#[derive(Debug, Clone, PartialEq, Eq)]
218enum FipsEndpointDirectPacketStorage {
219 Segmented(FipsEndpointDirectPacketSegment),
220 Chained {
221 segments: Vec<FipsEndpointDirectPacketSegment>,
222 packet_ends: Vec<usize>,
223 packet_bytes: usize,
224 },
225}
226
227impl FipsEndpointDirectPacketStorage {
228 fn empty_segmented() -> Self {
229 Self::Segmented(FipsEndpointDirectPacketSegment::new(
230 PacketBuffer::new(Vec::new()),
231 Vec::new(),
232 ))
233 }
234
235 fn build_chained(mut segments: Vec<FipsEndpointDirectPacketSegment>) -> Self {
236 let mut packet_ends = Vec::with_capacity(segments.len());
237 let mut packet_count = 0usize;
238 let mut packet_bytes = 0usize;
239 segments.retain(|segment| {
240 if segment.is_empty() {
241 return false;
242 }
243 packet_count = packet_count.saturating_add(segment.len());
244 packet_ends.push(packet_count);
245 packet_bytes = packet_bytes.saturating_add(segment.packet_bytes);
246 true
247 });
248 Self::Chained {
249 segments,
250 packet_ends,
251 packet_bytes,
252 }
253 }
254
255 fn packet_count(&self) -> usize {
256 match self {
257 Self::Segmented(segment) => segment.len(),
258 Self::Chained { packet_ends, .. } => packet_ends.last().copied().unwrap_or(0),
259 }
260 }
261
262 fn into_segments(self) -> Vec<FipsEndpointDirectPacketSegment> {
263 match self {
264 Self::Segmented(segment) => vec![segment],
265 Self::Chained { segments, .. } => segments,
266 }
267 }
268}
269
270#[derive(Debug, Clone, PartialEq, Eq)]
278pub struct FipsEndpointDirectPacketRun {
279 meta: FipsEndpointDirectPacketRunMeta,
280 storage: FipsEndpointDirectPacketStorage,
281}
282
283pub struct FipsEndpointDirectPacketSlices<'a> {
285 storage: &'a FipsEndpointDirectPacketStorage,
286 index: usize,
287 segment_index: usize,
288 segment_packet_index: usize,
289 remaining: usize,
290}
291
292impl FipsEndpointDirectPacketRun {
293 pub(crate) fn from_segmented_payload(
294 meta: FipsEndpointDirectPacketRunMeta,
295 buffer: PacketBuffer,
296 ranges: Vec<Range<usize>>,
297 ) -> Self {
298 Self {
299 meta,
300 storage: FipsEndpointDirectPacketStorage::Segmented(
301 FipsEndpointDirectPacketSegment::new(buffer, ranges),
302 ),
303 }
304 }
305
306 pub fn meta(&self) -> &FipsEndpointDirectPacketRunMeta {
308 &self.meta
309 }
310
311 pub fn source_peer(&self) -> &PeerIdentity {
313 self.meta.source_peer()
314 }
315
316 pub fn source_node_addr(&self) -> &NodeAddr {
318 self.meta.source_node_addr()
319 }
320
321 pub fn source_npub(&self) -> String {
323 self.meta.source_npub()
324 }
325
326 pub fn previous_hop_node_addr(&self) -> &NodeAddr {
328 self.meta.previous_hop_node_addr()
329 }
330
331 pub fn is_direct_path(&self) -> bool {
333 self.meta.is_direct_path()
334 }
335
336 pub fn received_k_bit(&self) -> bool {
338 self.meta.received_k_bit()
339 }
340
341 pub fn enqueued_at_ms(&self) -> u64 {
343 self.meta.enqueued_at_ms()
344 }
345
346 pub fn len(&self) -> usize {
348 self.storage.packet_count()
349 }
350
351 pub fn is_empty(&self) -> bool {
353 self.len() == 0
354 }
355
356 pub fn packet_bytes(&self) -> usize {
358 match &self.storage {
359 FipsEndpointDirectPacketStorage::Segmented(segment) => segment.packet_bytes,
360 FipsEndpointDirectPacketStorage::Chained { packet_bytes, .. } => *packet_bytes,
361 }
362 }
363
364 pub fn packet_slice(&self, index: usize) -> Option<&[u8]> {
366 match &self.storage {
367 FipsEndpointDirectPacketStorage::Segmented(segment) => segment
368 .ranges
369 .get(index)
370 .map(|range| &segment.buffer.as_slice()[range.clone()]),
371 FipsEndpointDirectPacketStorage::Chained {
372 segments,
373 packet_ends,
374 ..
375 } => {
376 let segment_index = packet_ends.partition_point(|end| *end <= index);
377 let previous_end = segment_index
378 .checked_sub(1)
379 .and_then(|previous| packet_ends.get(previous).copied())
380 .unwrap_or(0);
381 segments.get(segment_index).and_then(|segment| {
382 segment
383 .ranges
384 .get(index - previous_end)
385 .map(|range| &segment.buffer.as_slice()[range.clone()])
386 })
387 }
388 }
389 }
390
391 pub fn packet_slice_mut(&mut self, index: usize) -> Option<&mut [u8]> {
393 match &mut self.storage {
394 FipsEndpointDirectPacketStorage::Segmented(segment) => {
395 let range = segment.ranges.get(index)?.clone();
396 Some(&mut Arc::make_mut(&mut segment.buffer).as_mut_slice()[range])
397 }
398 FipsEndpointDirectPacketStorage::Chained {
399 segments,
400 packet_ends,
401 ..
402 } => {
403 let segment_index = packet_ends.partition_point(|end| *end <= index);
404 let previous_end = segment_index
405 .checked_sub(1)
406 .and_then(|previous| packet_ends.get(previous).copied())
407 .unwrap_or(0);
408 let segment = segments.get_mut(segment_index)?;
409 let range = segment.ranges.get(index - previous_end)?.clone();
410 Some(&mut Arc::make_mut(&mut segment.buffer).as_mut_slice()[range])
411 }
412 }
413 }
414
415 pub(crate) fn try_append_run(
416 &mut self,
417 other: FipsEndpointDirectPacketRun,
418 ) -> Result<(), FipsEndpointDirectPacketRun> {
419 if !self.matches_append_meta(&other) {
420 return Err(other);
421 }
422
423 let current = std::mem::replace(
424 &mut self.storage,
425 FipsEndpointDirectPacketStorage::empty_segmented(),
426 );
427 let mut segments = match current {
428 FipsEndpointDirectPacketStorage::Segmented(segment) => vec![segment],
429 FipsEndpointDirectPacketStorage::Chained { segments, .. } => segments,
430 };
431 match other.storage {
432 FipsEndpointDirectPacketStorage::Segmented(segment) => segments.push(segment),
433 FipsEndpointDirectPacketStorage::Chained {
434 segments: mut other_segments,
435 ..
436 } => segments.append(&mut other_segments),
437 }
438 self.storage = FipsEndpointDirectPacketStorage::build_chained(segments);
439 Ok(())
440 }
441
442 fn matches_append_meta(&self, other: &Self) -> bool {
443 self.source_peer() == other.source_peer()
444 && self.previous_hop_node_addr() == other.previous_hop_node_addr()
445 && self.received_k_bit() == other.received_k_bit()
446 && self.is_direct_path() == other.is_direct_path()
447 }
448
449 pub fn packet_slices(&self) -> FipsEndpointDirectPacketSlices<'_> {
451 FipsEndpointDirectPacketSlices {
452 storage: &self.storage,
453 index: 0,
454 segment_index: 0,
455 segment_packet_index: 0,
456 remaining: self.len(),
457 }
458 }
459
460 pub fn partition_by_packet_lane<F>(
466 self,
467 lane_count: usize,
468 mut lane_for_packet: F,
469 ) -> Vec<(usize, Self)>
470 where
471 F: FnMut(&[u8]) -> usize,
472 {
473 let meta = self.meta;
474 let mut groups: Vec<FipsEndpointDirectPacketSplitGroup> = Vec::new();
475 for segment in self.storage.into_segments() {
476 let buffer = segment.buffer;
477 let bytes = buffer.as_slice();
478 for range in segment.ranges {
479 let lane = if lane_count == 0 {
480 0
481 } else {
482 lane_for_packet(&bytes[range.clone()]) % lane_count
483 };
484 let group_index = groups.iter().position(|group| group.lane == lane);
485 let group = match group_index {
486 Some(index) => &mut groups[index],
487 None => {
488 groups.push(FipsEndpointDirectPacketSplitGroup::new(lane));
489 groups.last_mut().expect("group was just pushed")
490 }
491 };
492 group.push(Arc::clone(&buffer), range);
493 }
494 }
495
496 groups
497 .into_iter()
498 .map(|group| {
499 let run = Self {
500 meta: meta.clone(),
501 storage: FipsEndpointDirectPacketStorage::build_chained(group.segments),
502 };
503 (group.lane, run)
504 })
505 .collect()
506 }
507
508 pub fn retain_packets<F>(&mut self, mut keep: F)
514 where
515 F: FnMut(usize, &[u8]) -> bool,
516 {
517 match &mut self.storage {
518 FipsEndpointDirectPacketStorage::Segmented(segment) => {
519 let bytes = segment.buffer.as_slice();
520 let mut index = 0usize;
521 let mut retained_bytes = 0usize;
522 segment.ranges.retain(|range| {
523 let current_index = index;
524 index = index.saturating_add(1);
525 if keep(current_index, &bytes[range.clone()]) {
526 retained_bytes = retained_bytes.saturating_add(range.len());
527 true
528 } else {
529 false
530 }
531 });
532 segment.packet_bytes = retained_bytes;
533 }
534 FipsEndpointDirectPacketStorage::Chained {
535 segments,
536 packet_ends,
537 packet_bytes,
538 } => {
539 let mut index = 0usize;
540 let mut retained_bytes = 0usize;
541 for segment in segments.iter_mut() {
542 let bytes = segment.buffer.as_slice();
543 let mut segment_retained_bytes = 0usize;
544 segment.ranges.retain(|range| {
545 let current_index = index;
546 index = index.saturating_add(1);
547 if keep(current_index, &bytes[range.clone()]) {
548 retained_bytes = retained_bytes.saturating_add(range.len());
549 segment_retained_bytes =
550 segment_retained_bytes.saturating_add(range.len());
551 true
552 } else {
553 false
554 }
555 });
556 segment.packet_bytes = segment_retained_bytes;
557 }
558 segments.retain(|segment| !segment.is_empty());
559 packet_ends.clear();
560 let mut packet_count = 0usize;
561 for segment in segments.iter() {
562 packet_count = packet_count.saturating_add(segment.len());
563 packet_ends.push(packet_count);
564 }
565 *packet_bytes = retained_bytes;
566 }
567 }
568 }
569
570 pub fn for_each_packet_mut<F>(&mut self, mut visit: F)
572 where
573 F: FnMut(&mut [u8]),
574 {
575 match &mut self.storage {
576 FipsEndpointDirectPacketStorage::Segmented(segment) => {
577 let bytes = Arc::make_mut(&mut segment.buffer).as_mut_slice();
578 for range in &segment.ranges {
579 visit(&mut bytes[range.clone()]);
580 }
581 }
582 FipsEndpointDirectPacketStorage::Chained { segments, .. } => {
583 for segment in segments {
584 let bytes = Arc::make_mut(&mut segment.buffer).as_mut_slice();
585 for range in &segment.ranges {
586 visit(&mut bytes[range.clone()]);
587 }
588 }
589 }
590 }
591 }
592
593 pub fn into_source_run(self) -> FipsEndpointDirectSourceRun {
595 match self.storage {
596 FipsEndpointDirectPacketStorage::Segmented(segment) => {
597 let body = segment.buffer.as_slice();
598 let packets = segment
599 .ranges
600 .into_iter()
601 .map(|range| body[range].to_vec().into())
602 .collect();
603 FipsEndpointDirectSourceRun::from_source_packets(
604 self.meta.source_peer,
605 packets,
606 self.meta.enqueued_at_ms,
607 )
608 }
609 FipsEndpointDirectPacketStorage::Chained { segments, .. } => {
610 let mut packets = Vec::new();
611 for segment in segments {
612 let body = segment.buffer.as_slice();
613 packets.extend(
614 segment
615 .ranges
616 .into_iter()
617 .map(|range| body[range].to_vec().into()),
618 );
619 }
620 FipsEndpointDirectSourceRun::from_source_packets(
621 self.meta.source_peer,
622 packets,
623 self.meta.enqueued_at_ms,
624 )
625 }
626 }
627 }
628
629 pub fn into_packets(self) -> Vec<PacketBuffer> {
631 self.into_source_run().into_packets()
632 }
633}
634
635impl<'a> Iterator for FipsEndpointDirectPacketSlices<'a> {
636 type Item = &'a [u8];
637
638 fn next(&mut self) -> Option<Self::Item> {
639 if self.remaining == 0 {
640 return None;
641 }
642 let packet = match self.storage {
643 FipsEndpointDirectPacketStorage::Segmented(segment) => segment
644 .ranges
645 .get(self.index)
646 .map(|range| &segment.buffer.as_slice()[range.clone()]),
647 FipsEndpointDirectPacketStorage::Chained { segments, .. } => loop {
648 let Some(segment) = segments.get(self.segment_index) else {
649 break None;
650 };
651 if self.segment_packet_index < segment.len() {
652 let packet = segment
653 .ranges
654 .get(self.segment_packet_index)
655 .map(|range| &segment.buffer.as_slice()[range.clone()]);
656 self.segment_packet_index = self.segment_packet_index.saturating_add(1);
657 if self.segment_packet_index >= segment.len() {
658 self.segment_index = self.segment_index.saturating_add(1);
659 self.segment_packet_index = 0;
660 }
661 break packet;
662 }
663 self.segment_index = self.segment_index.saturating_add(1);
664 self.segment_packet_index = 0;
665 },
666 };
667 if packet.is_some() {
668 self.index = self.index.saturating_add(1);
669 self.remaining = self.remaining.saturating_sub(1);
670 }
671 packet
672 }
673
674 fn size_hint(&self) -> (usize, Option<usize>) {
675 (self.remaining, Some(self.remaining))
676 }
677}
678
679impl ExactSizeIterator for FipsEndpointDirectPacketSlices<'_> {}
680
681#[derive(Debug, Clone, PartialEq, Eq)]
683pub struct FipsEndpointDirectPacketBatch {
684 packet_runs: Vec<FipsEndpointDirectPacketRun>,
685}
686
687impl FipsEndpointDirectPacketBatch {
688 pub(crate) fn from_packet_runs(packet_runs: Vec<FipsEndpointDirectPacketRun>) -> Self {
689 Self { packet_runs }
690 }
691
692 pub fn packet_runs(&self) -> &[FipsEndpointDirectPacketRun] {
694 &self.packet_runs
695 }
696
697 pub fn packet_runs_mut(&mut self) -> &mut [FipsEndpointDirectPacketRun] {
699 &mut self.packet_runs
700 }
701
702 pub fn into_packet_runs(self) -> Vec<FipsEndpointDirectPacketRun> {
704 self.packet_runs
705 }
706
707 pub fn is_single_source(&self) -> bool {
709 self.packet_runs
710 .windows(2)
711 .all(|pair| pair[0].source_node_addr() == pair[1].source_node_addr())
712 }
713
714 pub fn len(&self) -> usize {
716 self.packet_runs
717 .iter()
718 .map(FipsEndpointDirectPacketRun::len)
719 .sum()
720 }
721
722 pub fn packet_bytes(&self) -> usize {
724 self.packet_runs
725 .iter()
726 .map(FipsEndpointDirectPacketRun::packet_bytes)
727 .sum()
728 }
729
730 pub fn run_count(&self) -> usize {
732 self.packet_runs.len()
733 }
734
735 pub fn is_empty(&self) -> bool {
737 self.packet_runs.is_empty()
738 }
739}
740
741#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
743pub enum FipsEndpointDirectDeliveryError {
744 #[error("direct endpoint sink unavailable")]
746 Unavailable,
747}
748
749pub trait FipsEndpointDirectSink: Send + Sync + 'static {
754 fn deliver_endpoint_packet_batch(
756 &self,
757 batch: FipsEndpointDirectPacketBatch,
758 ) -> Result<(), FipsEndpointDirectDeliveryError>;
759}
760
761impl<F> FipsEndpointDirectSink for F
762where
763 F: Fn(FipsEndpointDirectPacketBatch) -> Result<(), FipsEndpointDirectDeliveryError>
764 + Send
765 + Sync
766 + 'static,
767{
768 fn deliver_endpoint_packet_batch(
769 &self,
770 batch: FipsEndpointDirectPacketBatch,
771 ) -> Result<(), FipsEndpointDirectDeliveryError> {
772 self(batch)
773 }
774}
775
776#[derive(Clone)]
777pub(crate) struct EndpointDirectSink {
778 sink: Arc<dyn FipsEndpointDirectSink>,
779}
780
781impl std::fmt::Debug for EndpointDirectSink {
782 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
783 f.debug_struct("EndpointDirectSink").finish_non_exhaustive()
784 }
785}
786
787impl EndpointDirectSink {
788 pub(crate) fn new<S>(sink: S) -> Self
789 where
790 S: FipsEndpointDirectSink,
791 {
792 Self {
793 sink: Arc::new(sink),
794 }
795 }
796
797 pub(crate) fn deliver_direct_packet_batch(
798 &self,
799 batch: FipsEndpointDirectPacketBatch,
800 ) -> Result<(), FipsEndpointDirectDeliveryError> {
801 self.sink.deliver_endpoint_packet_batch(batch)
802 }
803}
804
805#[derive(Debug)]
807pub struct ExternalPacketIo {
808 pub outbound_tx: crate::upper::tun::TunOutboundTx,
810 pub inbound_rx: tokio::sync::mpsc::Receiver<NodeDeliveredPacket>,
812}
813
814#[derive(Debug)]
816pub(crate) struct EndpointDataIo {
817 pub(crate) control_tx: tokio::sync::mpsc::Sender<NodeEndpointControlCommand>,
820 pub(crate) data_batch_tx: EndpointDataBatchTx,
826 pub(crate) event_rx: EndpointEventReceiver,
834 pub(crate) event_tx: EndpointEventSender,
840}
841
842#[derive(Debug, Clone)]
844pub(crate) struct EndpointEventSender {
845 tx: tokio::sync::mpsc::Sender<NodeEndpointEvent>,
846 direct_sink: Option<EndpointDirectSink>,
847 queued_messages: Arc<AtomicUsize>,
848 ready: Arc<EndpointEventReady>,
849 message_cap: usize,
850}
851
852#[derive(Debug)]
853pub(crate) struct EndpointEventReceiver {
854 rx: tokio::sync::mpsc::Receiver<NodeEndpointEvent>,
855 queued_messages: Arc<AtomicUsize>,
856 ready: Arc<EndpointEventReady>,
857 closed: bool,
858}
859
860#[derive(Debug, Default)]
861struct EndpointEventReady {
862 sequence: StdMutex<u64>,
863 changed: Condvar,
864}
865
866impl EndpointEventReady {
867 fn notify(&self) {
868 if let Ok(mut sequence) = self.sequence.lock() {
869 *sequence = sequence.wrapping_add(1);
870 self.changed.notify_one();
871 }
872 }
873
874 fn snapshot(&self) -> u64 {
875 self.sequence.lock().map(|sequence| *sequence).unwrap_or(0)
876 }
877
878 fn wait_for_change(&self, observed: &mut u64) {
879 let Ok(mut sequence) = self.sequence.lock() else {
880 return;
881 };
882 while *sequence == *observed {
883 match self.changed.wait(sequence) {
884 Ok(next) => sequence = next,
885 Err(_) => return,
886 }
887 }
888 *observed = *sequence;
889 }
890}
891
892fn endpoint_event_capacity(requested: usize) -> usize {
893 requested.max(1)
894}
895
896fn try_reserve_endpoint_event_messages(
897 counter: &AtomicUsize,
898 capacity: usize,
899 count: usize,
900) -> Option<usize> {
901 if count == 0 {
902 return Some(counter.load(Relaxed));
903 }
904
905 counter
906 .fetch_update(Relaxed, Relaxed, |current| {
907 current.checked_add(count).filter(|next| *next <= capacity)
908 })
909 .ok()
910}
911
912#[derive(Debug, Default)]
919pub(in crate::node) struct EndpointEventRuntime {
920 sender: Option<EndpointEventSender>,
921}
922
923impl EndpointEventSender {
924 pub(in crate::node) fn channel(capacity: usize) -> (Self, EndpointEventReceiver) {
925 Self::channel_with_direct_sink(capacity, None)
926 }
927
928 pub(in crate::node) fn channel_with_direct_sink(
929 capacity: usize,
930 direct_sink: Option<EndpointDirectSink>,
931 ) -> (Self, EndpointEventReceiver) {
932 let message_cap = endpoint_event_capacity(capacity);
933 let (tx, rx) = tokio::sync::mpsc::channel(message_cap);
934 let queued_messages = Arc::new(AtomicUsize::new(0));
935 let ready = Arc::new(EndpointEventReady::default());
936 (
937 Self {
938 tx,
939 direct_sink,
940 queued_messages: Arc::clone(&queued_messages),
941 ready: Arc::clone(&ready),
942 message_cap,
943 },
944 EndpointEventReceiver {
945 rx,
946 queued_messages,
947 ready,
948 closed: false,
949 },
950 )
951 }
952
953 pub(crate) fn direct_sink(&self) -> Option<&EndpointDirectSink> {
954 self.direct_sink.as_ref()
955 }
956
957 #[allow(clippy::result_large_err)]
958 pub(crate) fn send(
959 &self,
960 event: NodeEndpointEvent,
961 ) -> Result<(), tokio::sync::mpsc::error::SendError<NodeEndpointEvent>> {
962 if event.messages.is_empty() {
963 return Ok(());
964 }
965
966 self.send_event(event, true)
967 }
968
969 #[allow(clippy::result_large_err)]
970 fn send_event(
971 &self,
972 event: NodeEndpointEvent,
973 split_on_pressure: bool,
974 ) -> Result<(), tokio::sync::mpsc::error::SendError<NodeEndpointEvent>> {
975 let count = event.message_count();
976 let Some(previous) =
977 try_reserve_endpoint_event_messages(&self.queued_messages, self.message_cap, count)
978 else {
979 if split_on_pressure && count > 1 {
980 return self.split_and_send_event(event);
981 }
982 crate::perf_profile::record_event_count(
983 crate::perf_profile::Event::EndpointEventBulkDropped,
984 count as u64,
985 );
986 return Ok(());
987 };
988
989 let queued = previous.saturating_add(count);
990 match self.tx.try_send(event) {
991 Ok(()) => {
992 self.note_send_success(previous, queued);
993 Ok(())
994 }
995 Err(tokio::sync::mpsc::error::TrySendError::Full(_event)) => {
996 self.note_send_rejected(count);
997 crate::perf_profile::record_event_count(
998 crate::perf_profile::Event::EndpointEventBulkDropped,
999 count as u64,
1000 );
1001 Ok(())
1002 }
1003 Err(tokio::sync::mpsc::error::TrySendError::Closed(event)) => {
1004 self.note_send_rejected(count);
1005 Err(tokio::sync::mpsc::error::SendError(event))
1006 }
1007 }
1008 }
1009
1010 #[allow(clippy::result_large_err)]
1011 fn split_and_send_event(
1012 &self,
1013 event: NodeEndpointEvent,
1014 ) -> Result<(), tokio::sync::mpsc::error::SendError<NodeEndpointEvent>> {
1015 let mut messages = event.messages;
1016 let queued_at = event.queued_at;
1017 if messages.len() <= 1 {
1018 return self.send_event(
1019 NodeEndpointEvent {
1020 messages,
1021 queued_at,
1022 },
1023 false,
1024 );
1025 }
1026
1027 let right = messages.split_off(messages.len() / 2);
1028 if !messages.is_empty() {
1029 self.send_event(
1030 NodeEndpointEvent {
1031 messages,
1032 queued_at,
1033 },
1034 true,
1035 )?;
1036 }
1037 if !right.is_empty() {
1038 self.send_event(
1039 NodeEndpointEvent {
1040 messages: right,
1041 queued_at,
1042 },
1043 true,
1044 )?;
1045 }
1046 Ok(())
1047 }
1048
1049 fn note_send_success(&self, previous: usize, queued: usize) {
1050 if previous < ENDPOINT_EVENT_BACKLOG_HIGH_WATER
1051 && queued >= ENDPOINT_EVENT_BACKLOG_HIGH_WATER
1052 {
1053 crate::perf_profile::record_event(crate::perf_profile::Event::EndpointEventBacklogHigh);
1054 }
1055 self.ready.notify();
1056 }
1057
1058 fn note_send_rejected(&self, count: usize) {
1059 release_endpoint_event_messages(&self.queued_messages, count);
1060 self.ready.notify();
1061 }
1062
1063 #[cfg(test)]
1064 pub(crate) fn queued_messages(&self) -> usize {
1065 self.queued_messages.load(Relaxed)
1066 }
1067}
1068
1069impl Drop for EndpointEventSender {
1070 fn drop(&mut self) {
1071 self.ready.notify();
1072 }
1073}
1074
1075impl Drop for EndpointEventReceiver {
1076 fn drop(&mut self) {
1077 self.queued_messages.store(0, Relaxed);
1078 self.ready.notify();
1079 }
1080}
1081
1082impl EndpointEventRuntime {
1083 pub(in crate::node) fn attach(&mut self, sender: EndpointEventSender) {
1084 self.sender = Some(sender);
1085 }
1086
1087 pub(in crate::node) fn is_attached(&self) -> bool {
1088 self.sender.is_some()
1089 }
1090
1091 pub(in crate::node) fn sender(&self) -> Option<EndpointEventSender> {
1092 self.sender.clone()
1093 }
1094
1095 #[allow(clippy::result_large_err)]
1096 pub(in crate::node) fn deliver_endpoint_data_batch(
1097 &mut self,
1098 messages: Vec<EndpointDataDelivery>,
1099 ) -> Result<(), tokio::sync::mpsc::error::SendError<NodeEndpointEvent>> {
1100 if messages.is_empty() {
1101 return Ok(());
1102 }
1103
1104 let Some(sender) = &self.sender else {
1105 return Ok(());
1106 };
1107 let _t_deliver =
1108 crate::perf_profile::Timer::start(crate::perf_profile::Stage::EndpointDeliver);
1109 sender.send(NodeEndpointEvent {
1110 messages,
1111 queued_at: crate::perf_profile::stamp(),
1112 })
1113 }
1114}
1115
1116impl EndpointEventReceiver {
1117 pub(crate) async fn recv(&mut self) -> Option<NodeEndpointEvent> {
1118 let event = self.rx.recv().await?;
1119 self.note_observed(&event);
1120 Some(event)
1121 }
1122
1123 pub(crate) fn blocking_recv(&mut self) -> Option<NodeEndpointEvent> {
1124 let mut observed = self.ready.snapshot();
1125 loop {
1126 match self.try_recv() {
1127 Ok(event) => return Some(event),
1128 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return None,
1129 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
1130 self.ready.wait_for_change(&mut observed);
1131 }
1132 }
1133 }
1134 }
1135
1136 pub(crate) fn try_recv(
1137 &mut self,
1138 ) -> Result<NodeEndpointEvent, tokio::sync::mpsc::error::TryRecvError> {
1139 match self.rx.try_recv() {
1140 Ok(event) => {
1141 self.note_observed(&event);
1142 Ok(event)
1143 }
1144 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
1145 if self.closed {
1146 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected)
1147 } else {
1148 Err(tokio::sync::mpsc::error::TryRecvError::Empty)
1149 }
1150 }
1151 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
1152 self.closed = true;
1153 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected)
1154 }
1155 }
1156 }
1157
1158 pub(crate) fn release_messages(&self, count: usize) {
1159 release_endpoint_event_messages(&self.queued_messages, count);
1160 }
1161
1162 fn note_observed(&self, event: &NodeEndpointEvent) {
1163 event.record_dequeue_wait();
1164 }
1165}
1166
1167pub(in crate::node) fn release_endpoint_event_messages(counter: &AtomicUsize, count: usize) {
1168 if count == 0 {
1169 return;
1170 }
1171
1172 let previous = counter.fetch_sub(count, Relaxed);
1173 debug_assert!(
1174 previous >= count,
1175 "endpoint event queued message accounting underflow"
1176 );
1177}
1178
1179#[derive(Debug, Clone, Default, PartialEq, Eq)]
1181pub(crate) struct UpdatePeersOutcome {
1182 pub(crate) added: usize,
1183 pub(crate) removed: usize,
1184 pub(crate) updated: usize,
1185 pub(crate) unchanged: usize,
1186}
1187
1188#[derive(Debug, Clone)]
1194pub(crate) struct EndpointDataDelivery {
1195 pub(crate) source_peer: PeerIdentity,
1196 pub(crate) payload: PacketBuffer,
1197 pub(crate) enqueued_at_ms: u64,
1198}
1199
1200impl EndpointDataDelivery {
1201 pub(crate) fn new(source_peer: PeerIdentity, payload: impl Into<PacketBuffer>) -> Self {
1202 Self {
1203 source_peer,
1204 payload: payload.into(),
1205 enqueued_at_ms: crate::time::now_ms(),
1206 }
1207 }
1208}
1209
1210#[derive(Debug)]
1212pub(crate) struct NodeEndpointEvent {
1213 pub(crate) messages: Vec<EndpointDataDelivery>,
1214 pub(crate) queued_at: Option<crate::perf_profile::TraceStamp>,
1215}
1216
1217impl NodeEndpointEvent {
1218 pub(in crate::node) fn message_count(&self) -> usize {
1219 self.messages.len()
1220 }
1221
1222 fn queued_at(&self) -> Option<crate::perf_profile::TraceStamp> {
1223 self.queued_at
1224 }
1225
1226 fn record_dequeue_wait(&self) {
1227 let queued_at = self.queued_at();
1228 if queued_at.is_none() {
1229 return;
1230 }
1231 crate::perf_profile::record_since_count(
1232 crate::perf_profile::Stage::EndpointEventWait,
1233 queued_at,
1234 self.message_count() as u64,
1235 );
1236 }
1237}
1238
1239#[derive(Debug, Clone, PartialEq, Eq)]
1241pub(crate) struct NodeEndpointPeer {
1242 pub(crate) npub: String,
1243 pub(crate) node_addr: NodeAddr,
1244 pub(crate) connected: bool,
1245 pub(crate) transport_addr: Option<String>,
1246 pub(crate) transport_type: Option<String>,
1247 pub(crate) link_id: u64,
1248 pub(crate) srtt_ms: Option<u64>,
1249 pub(crate) srtt_age_ms: Option<u64>,
1250 pub(crate) packets_sent: u64,
1251 pub(crate) packets_recv: u64,
1252 pub(crate) bytes_sent: u64,
1253 pub(crate) bytes_recv: u64,
1254 pub(crate) rekey_in_progress: bool,
1255 pub(crate) rekey_draining: bool,
1256 pub(crate) current_k_bit: Option<bool>,
1257 pub(crate) last_outbound_route: Option<String>,
1258 pub(crate) direct_probe_pending: bool,
1259 pub(crate) direct_probe_after_ms: Option<u64>,
1260 pub(crate) direct_probe_retry_count: u32,
1261 pub(crate) direct_probe_auto_reconnect: bool,
1262 pub(crate) direct_probe_expires_at_ms: Option<u64>,
1263 pub(crate) nostr_traversal_consecutive_failures: u32,
1264 pub(crate) nostr_traversal_in_cooldown: bool,
1265 pub(crate) nostr_traversal_cooldown_until_ms: Option<u64>,
1266 pub(crate) nostr_traversal_last_observed_skew_ms: Option<i64>,
1267}
1268
1269#[derive(Debug, Clone, PartialEq, Eq)]
1271pub(crate) struct NodeEndpointRelayStatus {
1272 pub(crate) url: String,
1273 pub(crate) status: String,
1274}