use std::sync::Arc;
use rustc_hash::FxHashMap;
use super::Jobs;
use crate::data::Patch;
use crate::host::Request;
use crate::tiles::{Key, ancestry};
use crate::{Error, Pending};
const REQUESTS: usize = 8;
const KEPT_BYTES: usize = 64 << 20;
pub(crate) enum Requested {
Fetch(Request),
Wait,
Missing,
}
pub(crate) enum Decoding {
Now(Patch),
Job(Pending<Vec<u8>>),
}
pub(crate) trait Source<K: Key> {
fn request(&mut self, key: K, jobs: &Jobs<'_>) -> Requested;
fn decode(&self, key: K, bytes: &Arc<Vec<u8>>, jobs: &Jobs<'_>) -> Decoding;
}
pub(crate) struct Drawn<K> {
pub(crate) keys: Vec<K>,
pub(crate) standing_in: bool,
pub(crate) failure: Option<Error>,
}
pub(crate) struct Tiling<K: Key> {
held: FxHashMap<K, (Arc<Vec<u8>>, u64)>,
fetching: FxHashMap<K, Pending<Vec<u8>>>,
decoding: FxHashMap<K, (u64, Pending<Vec<u8>>)>,
decoded: FxHashMap<K, (u64, Patch)>,
tick: u64,
}
impl<K: Key> Default for Tiling<K> {
fn default() -> Tiling<K> {
Tiling {
held: FxHashMap::default(),
fetching: FxHashMap::default(),
decoding: FxHashMap::default(),
decoded: FxHashMap::default(),
tick: 0,
}
}
}
impl<K: Key> Tiling<K> {
pub(crate) fn update(
&mut self,
wanted: &[K],
stamp: u64,
source: &mut impl Source<K>,
jobs: &Jobs<'_>,
) -> Drawn<K> {
self.tick += 1;
let failure = self.land(stamp);
let mut decode: Vec<K> = Vec::new();
for &key in wanted {
if self.ready(&key, stamp) {
continue;
}
let held_up = ancestry(key).find(|k| self.held.contains_key(k));
if let Some(k) = held_up
&& !self.ready(&k, stamp)
&& !decode.contains(&k)
{
decode.push(k);
}
if !self.held.contains_key(&key) && !decode.contains(&key) {
self.fetch(key, source, jobs);
}
}
for key in decode {
self.decode(key, stamp, source, jobs);
}
let mut drawn: Vec<K> = Vec::new();
let mut standing_in = false;
for &key in wanted {
let stands = ancestry(key).find(|k| self.ready(k, stamp));
if let Some(stand) = stands
&& !drawn.contains(&stand)
{
drawn.push(stand);
}
standing_in |= stands != Some(key);
}
let tick = self.tick;
for key in &drawn {
if let Some((_, used)) = self.held.get_mut(key) {
*used = tick;
}
}
self.evict(&drawn, wanted);
Drawn { keys: drawn, standing_in, failure }
}
pub(crate) fn decoded(&self, key: &K, stamp: u64) -> Option<&Patch> {
self.decoded.get(key).filter(|(s, _)| *s == stamp).map(|(_, patch)| patch)
}
pub(crate) fn under_way(&self) -> usize {
self.fetching.len() + self.decoding.len()
}
fn ready(&self, key: &K, stamp: u64) -> bool {
self.decoded.get(key).is_some_and(|(s, _)| *s == stamp)
}
fn land(&mut self, stamp: u64) -> Option<Error> {
let mut failure = None;
let fetched: Vec<_> = self.fetching.iter().filter_map(|(key, p)| p.take().map(|r| (*key, r))).collect();
for (key, result) in fetched {
self.fetching.remove(&key);
let bytes = result.unwrap_or_else(|error| {
failure.get_or_insert(error);
Vec::new()
});
self.held.insert(key, (Arc::new(bytes), self.tick));
}
let decoded: Vec<_> =
self.decoding.iter().filter_map(|(key, (s, p))| p.take().map(|r| (*key, *s, r))).collect();
for (key, s, result) in decoded {
self.decoding.remove(&key);
let patch = result.ok().and_then(|bytes| Patch::from_bytes(&bytes).ok()).unwrap_or_default();
if s == stamp {
self.decoded.insert(key, (s, patch));
}
}
failure
}
fn fetch(&mut self, key: K, source: &mut impl Source<K>, jobs: &Jobs<'_>) {
if self.fetching.contains_key(&key) || self.fetching.len() >= REQUESTS {
return;
}
match source.request(key, jobs) {
Requested::Fetch(request) => {
self.fetching.insert(key, jobs.fetch(&request));
}
Requested::Wait => {}
Requested::Missing => {
self.held.insert(key, (Arc::new(Vec::new()), self.tick));
}
}
}
fn decode(&mut self, key: K, stamp: u64, source: &impl Source<K>, jobs: &Jobs<'_>) {
if self.decoding.get(&key).is_some_and(|(s, _)| *s == stamp) || self.decoding.len() >= REQUESTS {
return;
}
let Some((bytes, _)) = self.held.get(&key) else { return };
if bytes.is_empty() {
self.decoded.insert(key, (stamp, Patch::new()));
return;
}
match source.decode(key, bytes, jobs) {
Decoding::Now(patch) => {
self.decoded.insert(key, (stamp, patch));
}
Decoding::Job(pending) => {
self.decoding.insert(key, (stamp, pending));
}
}
}
fn evict(&mut self, drawn: &[K], wanted: &[K]) {
let kept = |k: &K| drawn.contains(k) || wanted.contains(k);
let mut bytes: usize = self.held.values().map(|(b, _)| b.len()).sum();
if bytes > KEPT_BYTES {
let mut old: Vec<(u64, K)> =
self.held.iter().filter(|(k, _)| !kept(k)).map(|(k, (_, used))| (*used, *k)).collect();
old.sort_unstable();
for (_, key) in old {
if bytes <= KEPT_BYTES {
break;
}
if let Some((b, _)) = self.held.remove(&key) {
bytes -= b.len();
}
}
}
self.decoded.retain(|k, _| kept(k) || wanted.iter().any(|w| ancestry(*w).skip(1).any(|a| a == *k)));
}
}