use std::collections::HashMap;
use std::io;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex as StdMutex, OnceLock, PoisonError, Weak};
use bao_tree::io::fsm::Outboard;
use bao_tree::{BaoTree, BlockSize, ChunkNum, ChunkRanges, TreeNode, blake3};
use decdn_bao_range::align_range_clamped;
use tokio::sync::Notify;
use tokio_util::sync::CancellationToken;
use crate::Hash;
use crate::engine::CacheEngine;
use crate::error::{CacheResult, ErrorChain};
const IROH_BLOCK_SIZE: BlockSize = BlockSize::from_chunk_log(4);
const HASH_PAIR_BYTES: usize = 64;
const CHUNK_BYTES: u64 = 1024;
#[derive(Debug, Clone)]
pub struct FillError(String);
impl FillError {
#[must_use]
pub fn new(msg: impl Into<String>) -> Self {
Self(msg.into())
}
}
impl std::fmt::Display for FillError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for FillError {}
#[derive(Debug)]
struct OutboardState {
bytes: Vec<u8>,
captured: Vec<bool>,
}
#[derive(Debug)]
pub struct HashOutboard {
tree: BaoTree,
root: blake3::Hash,
state: StdMutex<OutboardState>,
captured: Notify,
liveness: Arc<Notify>,
}
impl HashOutboard {
#[must_use]
fn new(root: blake3::Hash, total_bytes: u64) -> Arc<Self> {
Arc::new(Self {
tree: BaoTree::new(total_bytes, IROH_BLOCK_SIZE),
root,
state: StdMutex::new(OutboardState {
bytes: Vec::new(),
captured: Vec::new(),
}),
captured: Notify::new(),
liveness: Arc::new(Notify::new()),
})
}
fn write_pair(
&self,
state: &mut OutboardState,
node: TreeNode,
pair: (blake3::Hash, blake3::Hash),
) -> bool {
let Some(offset) = self.tree.pre_order_offset(node) else {
return false; };
let Ok(idx) = usize::try_from(offset) else {
return false; };
if state.bytes.is_empty() {
let Ok(size) = usize::try_from(self.tree.outboard_size()) else {
return false;
};
state.bytes = vec![0u8; size];
state.captured = vec![false; size / HASH_PAIR_BYTES];
}
let byte_off = idx.saturating_mul(HASH_PAIR_BYTES);
let (l, r) = pair;
let bytes_written = if let Some(slot) =
state.bytes.get_mut(byte_off..byte_off + HASH_PAIR_BYTES)
&& let Some((left, right)) = slot.split_at_mut_checked(32)
{
left.copy_from_slice(l.as_bytes());
right.copy_from_slice(r.as_bytes());
true
} else {
false
};
let flag_set = if let Some(flag) = state.captured.get_mut(idx) {
*flag = true;
true
} else {
false
};
bytes_written && flag_set
}
fn capture(&self, node: TreeNode, pair: (blake3::Hash, blake3::Hash)) {
let wrote = {
let mut state = self.state.lock().unwrap_or_else(PoisonError::into_inner);
self.write_pair(&mut state, node, pair)
};
if wrote {
self.captured.notify_waiters();
}
}
fn capture_many(
&self,
pairs: impl IntoIterator<Item = (TreeNode, (blake3::Hash, blake3::Hash))>,
) {
let wrote = {
let mut state = self.state.lock().unwrap_or_else(PoisonError::into_inner);
pairs.into_iter().fold(false, |acc, (node, pair)| {
self.write_pair(&mut state, node, pair) || acc
})
};
if wrote {
self.captured.notify_waiters();
}
}
fn try_load(&self, idx: usize) -> Option<(blake3::Hash, blake3::Hash)> {
let state = self.state.lock().unwrap_or_else(PoisonError::into_inner);
if state.captured.get(idx).copied() != Some(true) {
return None;
}
let byte_off = idx.saturating_mul(HASH_PAIR_BYTES);
let slot = state.bytes.get(byte_off..byte_off + HASH_PAIR_BYTES)?;
let (l, r) = slot.split_at_checked(32)?;
let left: [u8; 32] = l.try_into().ok()?;
let right: [u8; 32] = r.try_into().ok()?;
Some((blake3::Hash::from(left), blake3::Hash::from(right)))
}
}
#[derive(Debug, Clone)]
pub struct Frontier {
value: Arc<AtomicU64>,
advanced: Arc<Notify>,
}
impl Frontier {
fn new(start: u64, advanced: Arc<Notify>) -> Self {
Self {
value: Arc::new(AtomicU64::new(start)),
advanced,
}
}
#[must_use]
pub fn get(&self) -> u64 {
self.value.load(Ordering::Acquire)
}
fn raise(&self, to: u64) {
if self.value.fetch_max(to, Ordering::AcqRel) < to {
self.advanced.notify_waiters();
}
}
}
#[derive(Debug, Clone)]
pub struct Demand {
legs: Arc<StdMutex<HashMap<u64, u64>>>,
advanced: Arc<Notify>,
}
impl Demand {
fn new(advanced: Arc<Notify>) -> Self {
Self {
legs: Arc::new(StdMutex::new(HashMap::new())),
advanced,
}
}
#[must_use]
pub fn get(&self) -> u64 {
nearest(&self.legs.lock().unwrap_or_else(PoisonError::into_inner))
}
fn stand(&self, leg: u64, end: u64) {
self.update(|legs| {
legs.insert(leg, end);
});
}
fn withdraw(&self, leg: u64) {
self.update(|legs| {
legs.remove(&leg);
});
}
fn update(&self, change: impl FnOnce(&mut HashMap<u64, u64>)) {
let moved = {
let mut legs = self.legs.lock().unwrap_or_else(PoisonError::into_inner);
let before = nearest(&legs);
change(&mut legs);
nearest(&legs) != before
};
if moved {
self.advanced.notify_waiters();
}
}
}
fn nearest(legs: &HashMap<u64, u64>) -> u64 {
legs.values().copied().min().unwrap_or(0)
}
static NEXT_DEMAND_SLOT: AtomicU64 = AtomicU64::new(1);
#[derive(Debug)]
pub struct DemandSlot {
session: Arc<FillSession>,
id: u64,
}
impl DemandSlot {
pub fn stand(&self, end: u64) {
let end = end.min(self.session.total_bytes());
let Some(byte) = end.checked_sub(1) else {
self.withdraw();
return;
};
match self.session.registry_and_hash() {
Some((registry, hash)) => registry.route_demand(hash, self.id, byte, end),
None => self.session.route_own_demand(self.id, byte, end),
}
}
pub fn withdraw(&self) {
match self.session.registry_and_hash() {
Some((registry, hash)) => registry.withdraw_demand(hash, self.id),
None => self.session.serve_demand.withdraw(self.id),
}
}
}
impl Drop for DemandSlot {
fn drop(&mut self) {
self.withdraw();
}
}
#[derive(Debug, Clone)]
pub struct DownstreamWatch {
served_paid: Frontier,
serve_demand: Demand,
}
impl DownstreamWatch {
#[must_use]
pub fn served_paid(&self) -> u64 {
self.served_paid.get()
}
#[must_use]
pub fn serve_demand(&self) -> u64 {
self.serve_demand.get()
}
pub async fn past(&self, observed_paid: u64, observed_demand: u64) {
let notified = self.served_paid.advanced.notified();
tokio::pin!(notified);
loop {
notified.as_mut().enable();
if self.served_paid() > observed_paid || self.serve_demand() != observed_demand {
return;
}
notified.as_mut().await;
notified.set(self.served_paid.advanced.notified());
}
}
}
#[derive(Debug)]
pub struct FillSession {
outboard: StdMutex<Arc<HashOutboard>>,
ended: StdMutex<Option<Result<(), FillError>>>,
served_paid: Frontier,
served_start: u64,
serve_demand: Demand,
covered: StdMutex<ChunkRanges>,
observers: AtomicUsize,
cancel: CancellationToken,
registry: OnceLock<(Weak<FillRegistry>, Hash)>,
pull_thread: StdMutex<Option<std::thread::JoinHandle<()>>>,
}
impl FillSession {
#[must_use]
pub fn new(root: blake3::Hash, total_bytes: u64) -> Arc<Self> {
Self::starting_at(root, total_bytes, 0)
}
#[must_use]
pub fn starting_at(root: blake3::Hash, total_bytes: u64, served_start: u64) -> Arc<Self> {
let downstream_advanced = Arc::new(Notify::new());
Arc::new(Self {
outboard: StdMutex::new(HashOutboard::new(root, total_bytes)),
ended: StdMutex::new(None),
served_paid: Frontier::new(served_start, Arc::clone(&downstream_advanced)),
served_start,
serve_demand: Demand::new(downstream_advanced),
covered: StdMutex::new(ChunkRanges::all()),
observers: AtomicUsize::new(0),
cancel: CancellationToken::new(),
registry: OnceLock::new(),
pull_thread: StdMutex::new(None),
})
}
pub fn set_pull_handle(&self, handle: std::thread::JoinHandle<()>) {
let mut slot = self
.pull_thread
.lock()
.unwrap_or_else(PoisonError::into_inner);
debug_assert!(
slot.is_none(),
"a FillSession parks exactly one pull-thread handle; a second park would \
detach the first and orphan its pull"
);
if slot.is_none() {
*slot = Some(handle);
}
}
fn take_pull_handle(&self) -> Option<std::thread::JoinHandle<()>> {
self.pull_thread
.lock()
.unwrap_or_else(PoisonError::into_inner)
.take()
}
fn outboard(&self) -> Arc<HashOutboard> {
self.outboard
.lock()
.unwrap_or_else(PoisonError::into_inner)
.clone()
}
fn adopt_outboard(&self, shared: Arc<HashOutboard>) {
*self.outboard.lock().unwrap_or_else(PoisonError::into_inner) = shared;
}
fn bind_registry(&self, registry: Weak<FillRegistry>, hash: Hash) {
let _ = self.registry.set((registry, hash));
}
pub fn set_covered(&self, covered: ChunkRanges) {
*self.covered.lock().unwrap_or_else(PoisonError::into_inner) = covered;
}
fn covered_ranges(&self) -> ChunkRanges {
self.covered
.lock()
.unwrap_or_else(PoisonError::into_inner)
.clone()
}
#[must_use]
pub fn total_bytes(&self) -> u64 {
self.outboard().tree.size()
}
#[must_use]
pub const fn served_start(&self) -> u64 {
self.served_start
}
#[must_use]
pub fn served_paid(&self) -> u64 {
self.served_paid.get()
}
#[must_use]
pub fn observer_count(&self) -> usize {
self.observers.load(Ordering::Acquire)
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
self.cancel.is_cancelled()
}
#[must_use]
pub const fn cancel_token(&self) -> &CancellationToken {
&self.cancel
}
#[must_use]
fn is_dead(&self) -> bool {
self.outcome().is_some() || self.cancel.is_cancelled()
}
#[must_use]
pub fn outboard_reader(self: &Arc<Self>) -> SessionOutboardReader {
SessionOutboardReader {
session: Arc::clone(self),
outboard: self.outboard(),
parked_on: Arc::new(AtomicU64::new(0)),
store: None,
}
}
#[must_use]
pub const fn served_frontier(&self) -> &Frontier {
&self.served_paid
}
pub fn advance_served(&self, served: u64) {
self.served_paid.raise(served);
}
pub fn extend_served_from(&self, offset: u64, served: u64) {
if self.served_paid.get() >= offset {
self.served_paid.raise(served);
}
}
#[must_use]
pub fn downstream_watch(&self) -> DownstreamWatch {
DownstreamWatch {
served_paid: self.served_paid.clone(),
serve_demand: self.serve_demand.clone(),
}
}
#[must_use]
pub const fn serve_demand(&self) -> &Demand {
&self.serve_demand
}
#[must_use]
pub fn demand_slot(self: &Arc<Self>) -> DemandSlot {
DemandSlot {
session: Arc::clone(self),
id: NEXT_DEMAND_SLOT.fetch_add(1, Ordering::Relaxed),
}
}
fn registry_and_hash(&self) -> Option<(Arc<FillRegistry>, Hash)> {
self.registry
.get()
.and_then(|(weak, hash)| weak.upgrade().map(|registry| (registry, *hash)))
}
fn route_own_demand(&self, slot: u64, byte: u64, end: u64) {
let chunk = bao_tree::ChunkNum::full_chunks(byte);
let produces = !self.is_dead() && {
let covered = self.covered.lock().unwrap_or_else(PoisonError::into_inner);
!(&*covered & &ChunkRanges::from(chunk..chunk + 1)).is_empty()
};
if produces {
self.serve_demand.stand(slot, end);
} else {
self.serve_demand.withdraw(slot);
}
}
#[must_use]
pub fn liveness_signal(&self) -> Arc<Notify> {
Arc::clone(&self.outboard().liveness)
}
#[must_use]
pub fn range_still_live(&self, range: &ChunkRanges) -> bool {
match self.registry.get() {
Some((weak, hash)) => match weak.upgrade() {
Some(registry) => registry.range_still_live(*hash, range),
None => false,
},
None => !self.is_dead() && !(&self.covered_ranges() & range).is_empty(),
}
}
pub fn capture(&self, node: TreeNode, pair: (blake3::Hash, blake3::Hash)) {
self.outboard().capture(node, pair);
}
pub fn capture_many(
&self,
pairs: impl IntoIterator<Item = (TreeNode, (blake3::Hash, blake3::Hash))>,
) {
self.outboard().capture_many(pairs);
}
pub fn mark_ended(&self, result: Result<(), FillError>) {
{
let mut guard = self.ended.lock().unwrap_or_else(PoisonError::into_inner);
*guard = Some(result);
}
self.outboard().liveness.notify_waiters();
}
#[must_use]
pub fn outcome(&self) -> Option<Result<(), FillError>> {
self.ended
.lock()
.unwrap_or_else(PoisonError::into_inner)
.clone()
}
}
#[derive(Debug)]
pub struct SessionOutboardReader {
session: Arc<FillSession>,
outboard: Arc<HashOutboard>,
parked_on: Arc<AtomicU64>,
store: Option<StoreFallback>,
}
#[derive(Debug)]
struct StoreFallback {
engine: CacheEngine,
present: ChunkRanges,
}
impl StoreFallback {
async fn capture(
&mut self,
outboard: &HashOutboard,
node_range: &ChunkRanges,
) -> CacheResult<bool> {
let hash = Hash::from_bytes(*outboard.root.as_bytes());
if (node_range & &self.present).is_empty() {
self.present = self
.engine
.present_ranges(hash)
.await?
.chunk_ranges()
.clone();
}
let held = node_range & &self.present;
let Some(first) = held.boundaries().first().copied() else {
return Ok(false);
};
let group_chunks = 1u64 << IROH_BLOCK_SIZE.chunk_log();
let group_start = first.0 / group_chunks * group_chunks;
let group = ChunkRanges::from(
ChunkNum(group_start)..ChunkNum(group_start.saturating_add(group_chunks)),
);
let pairs = self.engine.outboard_pairs(hash, &(&held & &group)).await?;
let exported = !pairs.is_empty();
outboard.capture_many(pairs);
Ok(exported)
}
}
impl SessionOutboardReader {
#[must_use]
pub fn with_store_fallback(mut self, engine: CacheEngine) -> Self {
self.store = Some(StoreFallback {
engine,
present: ChunkRanges::empty(),
});
self
}
#[must_use]
pub fn parked_on(&self) -> Arc<AtomicU64> {
Arc::clone(&self.parked_on)
}
fn log_store_hit(&self, node: TreeNode) {
let hash = self.outboard.root;
if matches!(self.session.outcome(), Some(Ok(()))) {
tracing::warn!(
%hash,
?node,
"a clean pull never captured an outboard node; read it from the store"
);
} else {
tracing::debug!(
%hash,
?node,
"read an outboard node no live fill will capture from the store"
);
}
}
fn dead_range_error(&self, node: TreeNode) -> io::Error {
match self.session.outcome() {
Some(Err(msg)) => io::Error::other(format!(
"upstream pull failed before supplying outboard node {node:?}: {msg}"
)),
Some(Ok(())) => io::Error::other(format!(
"upstream pull completed but outboard node {node:?} was never captured"
)),
None => io::Error::other(format!(
"no live fill covers outboard node {node:?}; every covering pull ended"
)),
}
}
}
impl Outboard for SessionOutboardReader {
fn root(&self) -> blake3::Hash {
self.outboard.root
}
fn tree(&self) -> BaoTree {
self.outboard.tree
}
async fn load(&mut self, node: TreeNode) -> io::Result<Option<(blake3::Hash, blake3::Hash)>> {
let Some(offset) = self.outboard.tree.pre_order_offset(node) else {
return Ok(None); };
let idx = usize::try_from(offset).unwrap_or(usize::MAX);
let node_range = ChunkRanges::from(node.chunk_range());
loop {
let captured = self.outboard.captured.notified();
let liveness = self.outboard.liveness.notified();
tokio::pin!(captured);
tokio::pin!(liveness);
captured.as_mut().enable();
liveness.as_mut().enable();
if let Some(pair) = self.outboard.try_load(idx) {
return Ok(Some(pair));
}
if !self.session.range_still_live(&node_range) {
if let Some(pair) = self.outboard.try_load(idx) {
return Ok(Some(pair));
}
let exported = match self.store.as_mut() {
Some(fallback) => fallback.capture(&self.outboard, &node_range).await,
None => Ok(false),
};
match exported {
Ok(exported) => {
if let Some(pair) = self.outboard.try_load(idx) {
if exported {
self.log_store_hit(node);
}
return Ok(Some(pair));
}
if exported {
return Err(io::Error::other(format!(
"the store holds content under outboard node {node:?} but \
its export did not yield the node"
)));
}
}
Err(err) => {
return Err(io::Error::other(format!(
"{}; the store read for it failed: {}",
self.dead_range_error(node),
ErrorChain::new(&err)
)));
}
}
return Err(self.dead_range_error(node));
}
let node_start = node.chunk_range().start.to_bytes();
self.parked_on
.store(node_start.saturating_add(1), Ordering::Relaxed);
tokio::select! {
biased;
() = liveness.as_mut() => {}
() = captured.as_mut() => {}
}
}
}
}
#[derive(Debug)]
pub struct ObserverLease {
session: Arc<FillSession>,
registry: Weak<FillRegistry>,
hash: Hash,
armed: bool,
}
impl ObserverLease {
#[must_use]
pub fn release(mut self) -> Option<std::thread::JoinHandle<()>> {
self.teardown()
}
fn teardown(&mut self) -> Option<std::thread::JoinHandle<()>> {
if !self.armed {
return None;
}
self.armed = false;
let Some(registry) = self.registry.upgrade() else {
return None;
};
let mut map = registry.map.lock().unwrap_or_else(PoisonError::into_inner);
let prev = self.session.observers.fetch_sub(1, Ordering::AcqRel);
if prev != 1 {
return None;
}
if let Some(entry) = map.get_mut(&self.hash) {
entry.sessions.retain(|s| !Arc::ptr_eq(s, &self.session));
if entry.sessions.is_empty() {
map.remove(&self.hash);
}
}
if self.session.outcome().is_none() {
self.session.cancel.cancel();
}
self.session.outboard().liveness.notify_waiters();
self.session.take_pull_handle()
}
}
impl Drop for ObserverLease {
fn drop(&mut self) {
let _ = self.teardown();
}
}
#[derive(Debug)]
pub enum FillClaim {
Attach {
session: Arc<FillSession>,
lease: ObserverLease,
},
Owner {
session: Arc<FillSession>,
lease: ObserverLease,
},
Mixed {
owner: Arc<FillSession>,
owner_lease: ObserverLease,
attach: Arc<FillSession>,
attach_lease: ObserverLease,
remainder_offset: u64,
remainder_len: u64,
},
}
fn chunk_span(ranges: &ChunkRanges) -> u64 {
let (pairs, _open_end) = ranges.boundaries().as_chunks::<2>();
pairs
.iter()
.map(|[start, end]| end.0.saturating_sub(start.0))
.sum()
}
fn contiguous_byte_span(ranges: &ChunkRanges, total: u64) -> Option<(u64, u64)> {
match ranges.boundaries() {
[start, end] => {
let offset = start.0.saturating_mul(CHUNK_BYTES);
let end_bytes = end.0.saturating_mul(CHUNK_BYTES).min(total);
Some((offset, end_bytes.saturating_sub(offset)))
}
_ => None,
}
}
#[derive(Debug)]
struct HashEntry {
outboard: Arc<HashOutboard>,
sessions: Vec<Arc<FillSession>>,
}
#[derive(Debug, Default)]
pub struct FillRegistry {
map: StdMutex<HashMap<Hash, HashEntry>>,
metrics: Option<Arc<crate::metrics::CacheMetrics>>,
}
impl FillRegistry {
#[must_use]
pub fn new() -> Self {
Self::with_metrics(None)
}
#[must_use]
pub fn with_metrics(metrics: Option<Arc<crate::metrics::CacheMetrics>>) -> Self {
Self {
map: StdMutex::new(HashMap::new()),
metrics,
}
}
fn mint_lease(self: &Arc<Self>, session: &Arc<FillSession>, hash: Hash) -> ObserverLease {
session.observers.fetch_add(1, Ordering::AcqRel);
ObserverLease {
session: Arc::clone(session),
registry: Arc::downgrade(self),
hash,
armed: true,
}
}
fn insert_session(
self: &Arc<Self>,
map: &mut HashMap<Hash, HashEntry>,
hash: Hash,
session: &Arc<FillSession>,
) {
let entry = map.entry(hash).or_insert_with(|| HashEntry {
outboard: session.outboard(),
sessions: Vec::new(),
});
session.adopt_outboard(Arc::clone(&entry.outboard));
session.bind_registry(Arc::downgrade(self), hash);
entry.sessions.push(Arc::clone(session));
}
pub fn register_fill(
self: &Arc<Self>,
hash: Hash,
session: &Arc<FillSession>,
) -> ObserverLease {
let mut map = self.map.lock().unwrap_or_else(PoisonError::into_inner);
self.insert_session(&mut map, hash, session);
self.mint_lease(session, hash)
}
#[must_use]
pub fn in_flight_total(&self, hash: Hash) -> Option<u64> {
let map = self.map.lock().unwrap_or_else(PoisonError::into_inner);
map.get(&hash)?
.sessions
.iter()
.find(|session| !session.is_dead())
.map(|session| session.total_bytes())
}
fn route_demand(&self, hash: Hash, slot: u64, byte: u64, end: u64) {
let map = self.map.lock().unwrap_or_else(PoisonError::into_inner);
if let Some(entry) = map.get(&hash) {
for session in &entry.sessions {
session.route_own_demand(slot, byte, end);
}
}
}
fn withdraw_demand(&self, hash: Hash, slot: u64) {
let map = self.map.lock().unwrap_or_else(PoisonError::into_inner);
if let Some(entry) = map.get(&hash) {
for session in &entry.sessions {
session.serve_demand.withdraw(slot);
}
}
}
#[must_use]
pub fn range_still_live(&self, hash: Hash, range: &ChunkRanges) -> bool {
let map = self.map.lock().unwrap_or_else(PoisonError::into_inner);
match map.get(&hash) {
Some(entry) => entry
.sessions
.iter()
.any(|s| !s.is_dead() && !(&s.covered_ranges() & range).is_empty()),
None => false,
}
}
pub fn claim(
self: &Arc<Self>,
hash: Hash,
offset: u64,
len: u64,
total: u64,
make_session: impl FnOnce() -> Arc<FillSession>,
) -> FillClaim {
let mut map = self.map.lock().unwrap_or_else(PoisonError::into_inner);
let (r, fetch_start) = match align_range_clamped(offset, len, total) {
Ok(aligned) => (aligned.chunk_ranges().clone(), aligned.fetch_start()),
Err(_) => (ChunkRanges::empty(), 0),
};
if !r.is_empty() {
let mut covered_union = ChunkRanges::empty();
let mut best: Option<(Arc<FillSession>, ChunkRanges, u64)> = None;
let mut not_coalesced = false;
if let Some(entry) = map.get(&hash) {
for session in &entry.sessions {
if session.is_dead() {
continue;
}
if fetch_start > session.served_paid() {
if !(&r & &session.covered_ranges()).is_empty() {
not_coalesced = true;
tracing::debug!(
%hash,
fetch_start,
served_paid = session.served_paid(),
"fill not coalesced: request starts ahead of the live fill's paid frontier",
);
}
continue;
}
let covered = session.covered_ranges();
let overlap = &r & &covered;
if !overlap.is_empty() {
let span = chunk_span(&overlap);
if best
.as_ref()
.is_none_or(|(_, _, best_span)| span > *best_span)
{
best = Some((Arc::clone(session), covered.clone(), span));
}
}
covered_union |= covered;
}
}
if not_coalesced && let Some(m) = &self.metrics {
m.fill_not_coalesced.inc();
}
let attach = &r & &covered_union;
let remainder = &r - &covered_union;
if !attach.is_empty()
&& let Some((sibling, sib_covered, _)) = best
{
if remainder.is_empty() {
let lease = self.mint_lease(&sibling, hash);
return FillClaim::Attach {
session: sibling,
lease,
};
}
let attach_covered_by_sibling = (&attach - &sib_covered).is_empty();
let remainder_vs_sibling = &r - &sib_covered;
if attach_covered_by_sibling
&& let Some((rem_offset, rem_len)) =
contiguous_byte_span(&remainder_vs_sibling, total)
{
let owner = make_session();
owner.set_covered(remainder_vs_sibling);
self.insert_session(&mut map, hash, &owner);
let owner_lease = self.mint_lease(&owner, hash);
let attach_lease = self.mint_lease(&sibling, hash);
return FillClaim::Mixed {
owner,
owner_lease,
attach: sibling,
attach_lease,
remainder_offset: rem_offset,
remainder_len: rem_len,
};
}
}
}
let owner = make_session();
owner.set_covered(r);
self.insert_session(&mut map, hash, &owner);
let lease = self.mint_lease(&owner, hash);
FillClaim::Owner {
session: owner,
lease,
}
}
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing
)] mod tests;
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing
)] mod fill_registry_tests;