use crate::{
merkle::{Family, Location},
qmdb::sync::{Request, engine::IndexedFetchResult},
};
use commonware_cryptography::Digest;
use commonware_utils::futures::{AbortablePool, Aborter};
use futures::future::Aborted;
use std::{
collections::{BTreeMap, HashMap},
future::Future,
ops::Range,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) struct Id(u64);
struct TrackedRequest<F: Family> {
request: Request<F>,
_aborter: Aborter,
}
pub(super) struct Requests<F: Family, Op, D: Digest, E> {
futures: AbortablePool<'static, IndexedFetchResult<F, Op, D, E>>,
next_id: u64,
tracked: HashMap<Id, TrackedRequest<F>>,
by_location: BTreeMap<Location<F>, Id>,
}
impl<F: Family, Op: Send, D: Digest, E: Send> Requests<F, Op, D, E> {
pub fn new() -> Self {
Self {
futures: AbortablePool::default(),
next_id: 0,
tracked: HashMap::new(),
by_location: BTreeMap::new(),
}
}
pub fn insert<Fut>(&mut self, request: Request<F>, make: impl FnOnce(Id) -> Fut) -> Id
where
Fut: Future<Output = IndexedFetchResult<F, Op, D, E>> + Send + 'static,
{
let id = Id(self.next_id);
self.next_id += 1;
if let Some(old_id) = self.by_location.insert(request.start(), id) {
self.tracked.remove(&old_id);
}
let aborter = self.futures.push(make(id));
self.tracked.insert(
id,
TrackedRequest {
request,
_aborter: aborter,
},
);
id
}
pub fn remove(&mut self, id: Id) -> Option<Request<F>> {
if let Some(TrackedRequest {
request,
_aborter: _,
}) = self.tracked.remove(&id)
{
let start = request.start();
if self.by_location.get(&start) == Some(&id) {
self.by_location.remove(&start);
}
Some(request)
} else {
None
}
}
pub fn remove_before(&mut self, loc: Location<F>) {
let keep = self.by_location.split_off(&loc);
for id in self.by_location.values() {
self.tracked.remove(id);
}
self.by_location = keep;
}
pub fn ranges(&self) -> impl Iterator<Item = Range<Location<F>>> + '_ {
self.by_location.values().map(|id| {
let request = &self
.tracked
.get(id)
.expect("location index must reference a tracked request")
.request;
let start = request.start();
start..start.checked_add(request.max_ops().get()).unwrap()
})
}
pub fn contains(&self, loc: &Location<F>) -> bool {
self.by_location.contains_key(loc)
}
pub async fn next_completed(&mut self) -> Result<IndexedFetchResult<F, Op, D, E>, Aborted> {
self.futures.next_completed().await
}
pub fn len(&self) -> usize {
self.tracked.len()
}
}
impl<F: Family, Op: Send, D: Digest, E: Send> Default for Requests<F, Op, D, E> {
fn default() -> Self {
Self::new()
}
}