use crate::mqtt::common::tracing::trace;
use crate::mqtt::common::HashMap;
use crate::mqtt::common::IndexMap;
use alloc::{
string::{String, ToString},
vec::Vec,
};
use crate::mqtt::ValueAllocator;
pub type TopicAliasType = u16;
pub struct TopicAliasSend {
max_alias: TopicAliasType,
alias_to_topic: IndexMap<TopicAliasType, String>,
topic_to_aliases: HashMap<String, Vec<TopicAliasType>>,
value_allocator: ValueAllocator<TopicAliasType>,
}
impl TopicAliasSend {
const MIN_ALIAS: TopicAliasType = 1;
pub fn new(max_alias: TopicAliasType) -> Self {
trace!("Creating TopicAliasSend with max_alias: {}", max_alias);
Self {
max_alias,
alias_to_topic: IndexMap::default(),
topic_to_aliases: HashMap::default(),
value_allocator: ValueAllocator::new(Self::MIN_ALIAS, max_alias),
}
}
pub fn insert_or_update(&mut self, topic: &str, alias: TopicAliasType) {
trace!("TopicAliasSend insert topic: '{}', alias: {}", topic, alias);
assert!(!topic.is_empty() && alias >= Self::MIN_ALIAS && alias <= self.max_alias);
let topic_string = topic.to_string();
let is_new_alias = self.value_allocator.use_value(alias);
if !is_new_alias {
if let Some(old_topic) = self.alias_to_topic.shift_remove(&alias) {
if let Some(aliases) = self.topic_to_aliases.get_mut(&old_topic) {
aliases.retain(|&a| a != alias);
if aliases.is_empty() {
self.topic_to_aliases.remove(&old_topic);
}
}
}
}
self.alias_to_topic.insert(alias, topic_string.clone());
self.topic_to_aliases
.entry(topic_string)
.or_insert_with(Vec::new)
.push(alias);
}
pub fn get(&mut self, alias: TopicAliasType) -> Option<&str> {
trace!("Getting topic by alias: {}", alias);
if alias >= Self::MIN_ALIAS && alias <= self.max_alias {
if let Some(topic) = self.alias_to_topic.get(&alias).cloned() {
self.alias_to_topic.shift_remove(&alias);
self.alias_to_topic.insert(alias, topic);
return Some(self.alias_to_topic.get(&alias).unwrap());
}
}
None
}
pub fn peek(&self, alias: TopicAliasType) -> Option<&str> {
trace!("Peeking topic by alias (no touch): {}", alias);
if alias >= Self::MIN_ALIAS && alias <= self.max_alias {
if let Some(topic) = self.alias_to_topic.get(&alias) {
return Some(topic);
}
}
None
}
pub fn find_by_topic(&self, topic: &str) -> Option<TopicAliasType> {
trace!("Finding alias by topic: '{}'", topic);
self.topic_to_aliases
.get(topic)
.and_then(|aliases| aliases.first().copied())
}
pub fn clear(&mut self) {
trace!("Clearing all topic aliases");
self.alias_to_topic.clear();
self.topic_to_aliases.clear();
self.value_allocator.clear();
}
pub fn get_lru_alias(&self) -> TopicAliasType {
assert!(self.max_alias > 0);
if let Some(alias) = self.value_allocator.first_vacant() {
return alias;
}
self.alias_to_topic
.keys()
.next()
.copied()
.unwrap_or(Self::MIN_ALIAS)
}
pub fn max(&self) -> TopicAliasType {
self.max_alias
}
}