use alloc::vec::Vec;
use crate::engine::CommandId;
use crate::engine::InstantMillis;
use crate::identity::IdentitySigningPublicKey;
use crate::routing::dedup::PacketHash;
use crate::routing::delivery::receipts::{
OutstandingReceipt, ReceiptDeadline, ReceiptKind, ReceiptTable, TrackReceiptError,
};
pub const DEFAULT_MAX_OUTSTANDING_RECEIPTS: usize = 1024;
#[derive(Debug, Default)]
pub struct HeapReceiptTable {
packet_hashes: Vec<PacketHash>,
command_ids: Vec<CommandId>,
kinds: Vec<ReceiptKind>,
signing_keys: Vec<IdentitySigningPublicKey>,
sent_ats: Vec<InstantMillis>,
deadlines: Vec<ReceiptDeadline>,
}
impl ReceiptTable for HeapReceiptTable {
fn capacity(&self) -> usize {
DEFAULT_MAX_OUTSTANDING_RECEIPTS
}
fn len(&self) -> usize {
self.packet_hashes.len()
}
fn packet_hashes(&self) -> &[PacketHash] {
&self.packet_hashes
}
fn command_ids(&self) -> &[CommandId] {
&self.command_ids
}
fn kinds(&self) -> &[ReceiptKind] {
&self.kinds
}
fn signing_keys(&self) -> &[IdentitySigningPublicKey] {
&self.signing_keys
}
fn sent_ats(&self) -> &[InstantMillis] {
&self.sent_ats
}
fn deadlines(&self) -> &[ReceiptDeadline] {
&self.deadlines
}
fn set_deadline(&mut self, index: usize, deadline: ReceiptDeadline) {
if let Some(slot) = self.deadlines.get_mut(index) {
*slot = deadline;
}
}
fn push(&mut self, receipt: OutstandingReceipt) -> Result<usize, TrackReceiptError> {
if self.packet_hashes.len() >= DEFAULT_MAX_OUTSTANDING_RECEIPTS {
return Err(TrackReceiptError::TableFull);
}
let index = self.packet_hashes.len();
self.packet_hashes.push(receipt.packet_hash);
self.command_ids.push(receipt.command_id);
self.kinds.push(receipt.kind);
self.signing_keys.push(receipt.peer_signing_key);
self.sent_ats.push(receipt.sent_at);
self.deadlines
.push(ReceiptDeadline::Due(receipt.timeout_at));
Ok(index)
}
fn remove(&mut self, index: usize) {
if index >= self.packet_hashes.len() {
return;
}
self.packet_hashes.remove(index);
self.command_ids.remove(index);
self.kinds.remove(index);
self.signing_keys.remove(index);
self.sent_ats.remove(index);
self.deadlines.remove(index);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn grows_to_the_reference_cap_then_reports_full_for_the_wrapper_to_cull() {
let key = IdentitySigningPublicKey::new(crate::crypto::ed25519_public_key(
&crate::crypto::Ed25519SecretKey::new([0x21; 32]),
));
let mut table = HeapReceiptTable::default();
for i in 0..DEFAULT_MAX_OUTSTANDING_RECEIPTS {
let receipt = OutstandingReceipt {
packet_hash: PacketHash::new([(i % 251) as u8; 32]),
command_id: CommandId(i as u64),
kind: ReceiptKind::SendSinglePacket,
peer_signing_key: key,
sent_at: InstantMillis(0),
timeout_at: InstantMillis(7_000),
};
assert_eq!(table.push(receipt), Ok(i));
}
let overflow = OutstandingReceipt {
packet_hash: PacketHash::new([0xFF; 32]),
command_id: CommandId(9_999),
kind: ReceiptKind::SendSinglePacket,
peer_signing_key: key,
sent_at: InstantMillis(0),
timeout_at: InstantMillis(7_000),
};
assert_eq!(table.push(overflow), Err(TrackReceiptError::TableFull));
assert_eq!(table.len(), DEFAULT_MAX_OUTSTANDING_RECEIPTS);
}
}