use bumpalo::Bump;
const SHORT_THRESHOLD: usize = 64;
enum StringValue<'a> {
Short(&'a str),
Long(Box<str>),
}
impl<'a> StringValue<'a> {
fn as_str(&self) -> &str {
match self {
StringValue::Short(s) => s,
StringValue::Long(s) => s,
}
}
}
pub struct StringStore<'a> {
arena: &'a Bump,
values: Vec<StringValue<'a>>,
}
impl<'a> StringStore<'a> {
pub fn new(arena: &'a Bump) -> Self {
Self {
arena,
values: Vec::new(),
}
}
pub fn store(&mut self, s: &str) -> u32 {
let val = if s.len() <= SHORT_THRESHOLD {
let copied = self.arena.alloc_str(s);
StringValue::Short(copied)
} else {
StringValue::Long(s.into())
};
self.values.push(val);
self.values.len() as u32 }
pub fn get(&self, idx: u32) -> &str {
if idx == 0 {
return "";
}
self.values[(idx - 1) as usize].as_str()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn store_and_retrieve_short_string() {
let arena = Bump::new();
let mut store = StringStore::new(&arena);
let idx = store.store("hello");
assert_eq!(idx, 1);
assert_eq!(store.get(idx), "hello");
}
#[test]
fn store_and_retrieve_long_string() {
let arena = Bump::new();
let mut store = StringStore::new(&arena);
let long = "x".repeat(100);
let idx = store.store(&long);
assert_eq!(idx, 1);
assert_eq!(store.get(idx), long);
}
#[test]
fn index_zero_returns_empty() {
let arena = Bump::new();
let store = StringStore::new(&arena);
assert_eq!(store.get(0), "");
}
#[test]
fn sequential_one_based_indices() {
let arena = Bump::new();
let mut store = StringStore::new(&arena);
let i1 = store.store("a");
let i2 = store.store("bb");
let i3 = store.store("ccc");
assert_eq!(i1, 1);
assert_eq!(i2, 2);
assert_eq!(i3, 3);
assert_eq!(store.get(1), "a");
assert_eq!(store.get(2), "bb");
assert_eq!(store.get(3), "ccc");
}
#[test]
fn store_empty_string() {
let arena = Bump::new();
let mut store = StringStore::new(&arena);
let idx = store.store("");
assert_eq!(idx, 1);
assert_eq!(store.get(idx), "");
}
#[test]
fn boundary_short_long() {
let arena = Bump::new();
let mut store = StringStore::new(&arena);
let at_threshold = "a".repeat(SHORT_THRESHOLD);
let idx1 = store.store(&at_threshold);
assert_eq!(store.get(idx1), at_threshold);
let over_threshold = "a".repeat(SHORT_THRESHOLD + 1);
let idx2 = store.store(&over_threshold);
assert_eq!(store.get(idx2), over_threshold);
}
}