#![allow(clippy::cast_possible_truncation)]
use std::collections::HashMap;
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum LabelTableError {
#[error("LabelTable overflow: cannot intern more than {max_labels} labels")]
Overflow {
max_labels: u32,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct LabelId(u32);
impl LabelId {
#[must_use]
pub fn as_u32(self) -> u32 {
self.0
}
#[must_use]
pub fn from_u32(id: u32) -> Self {
Self(id)
}
}
#[derive(Debug, Default)]
pub struct LabelTable {
strings: Vec<String>,
ids: HashMap<String, LabelId>,
}
impl LabelTable {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_capacity(expected_labels: usize) -> Self {
Self {
strings: Vec::with_capacity(expected_labels),
ids: HashMap::with_capacity(expected_labels),
}
}
pub fn intern(&mut self, s: &str) -> Result<LabelId, LabelTableError> {
if let Some(&id) = self.ids.get(s) {
return Ok(id);
}
let len = self.strings.len();
if len >= u32::MAX as usize {
return Err(LabelTableError::Overflow {
max_labels: u32::MAX,
});
}
#[allow(clippy::cast_possible_truncation)]
let id = LabelId(len as u32);
self.strings.push(s.to_string());
self.ids.insert(s.to_string(), id);
Ok(id)
}
#[must_use]
pub fn resolve(&self, id: LabelId) -> Option<&str> {
self.strings.get(id.0 as usize).map(String::as_str)
}
#[must_use]
pub fn len(&self) -> usize {
self.strings.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.strings.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (LabelId, &str)> {
self.strings
.iter()
.enumerate()
.map(|(i, s)| (LabelId(i as u32), s.as_str()))
}
#[must_use]
pub fn get_id(&self, s: &str) -> Option<LabelId> {
self.ids.get(s).copied()
}
#[must_use]
pub fn contains(&self, s: &str) -> bool {
self.ids.contains_key(s)
}
}