use crate::wire::TypeDef;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::vec::Vec;
pub(crate) struct TypeCache {
ids: BTreeMap<TypeDef, u32>,
acked: BTreeSet<u32>,
awaiting_ack_stack: Vec<Vec<u32>>,
next_type_id: u32,
}
impl TypeCache {
pub(crate) fn new() -> Self {
Self {
ids: BTreeMap::new(),
acked: BTreeSet::new(),
awaiting_ack_stack: Vec::new(),
next_type_id: 0,
}
}
pub(crate) fn get_or_create_type_id(&mut self, type_def: &TypeDef) -> (u32, bool) {
if let Some(&id) = self.ids.get(type_def) {
(id, self.acked.contains(&id))
} else {
let id = self.next_type_id;
self.next_type_id += 1;
self.ids.insert(type_def.clone(), id);
(id, false)
}
}
pub(crate) fn push_pending_frame(&mut self, type_ids: Vec<u32>) {
self.awaiting_ack_stack.push(type_ids);
}
pub(crate) fn pop_and_ack_pending_frame(&mut self) {
if let Some(type_ids) = self.awaiting_ack_stack.pop() {
self.ack_type_ids(&type_ids);
}
}
pub(crate) fn ack_type_ids(&mut self, type_ids: &[u32]) {
self.acked.extend(type_ids.iter().copied());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn type_id_is_cached_after_pending_frame_is_acked() {
let mut cache = TypeCache::new();
let type_def = TypeDef::of::<u8>();
let (id, cached) = cache.get_or_create_type_id(&type_def);
assert_eq!(id, 0);
assert!(!cached);
cache.push_pending_frame(vec![id]);
assert_eq!(cache.get_or_create_type_id(&type_def), (id, false));
cache.pop_and_ack_pending_frame();
assert_eq!(cache.get_or_create_type_id(&type_def), (id, true));
}
#[test]
fn ack_only_caches_types_in_the_top_frame() {
let mut cache = TypeCache::new();
let first = TypeDef::of::<u8>();
let second = TypeDef::of::<u16>();
let (first_id, _) = cache.get_or_create_type_id(&first);
let (second_id, _) = cache.get_or_create_type_id(&second);
cache.push_pending_frame(vec![first_id]);
cache.push_pending_frame(vec![second_id]);
cache.pop_and_ack_pending_frame();
assert_eq!(cache.get_or_create_type_id(&first), (first_id, false));
assert_eq!(cache.get_or_create_type_id(&second), (second_id, true));
}
#[test]
fn empty_pop_is_a_noop() {
let mut cache = TypeCache::new();
cache.pop_and_ack_pending_frame();
let type_def = TypeDef::of::<u8>();
let (id, cached) = cache.get_or_create_type_id(&type_def);
assert_eq!(id, 0);
assert!(!cached);
}
}