use crate::errors::Result;
use std::{io, mem, pin::Pin, sync::RwLock};
lazy_static::lazy_static! {
static ref ARENA: RwLock<Arena> = {
#[cfg(feature = "arena-delete")]
eprintln!("[ARENA] The memory Arena is compiled with deletions enabled, this should only ever happen in the tremor-language server!");
RwLock::new(Arena::default())
};
}
#[derive(Debug)]
struct ArenaEntry {
src: Option<Pin<String>>,
version: u64,
}
#[derive(Debug, Default)]
pub struct Arena {
sources: Vec<ArenaEntry>,
}
#[derive(
Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Debug,
)]
pub struct Index {
idx: usize,
version: u64,
}
impl std::fmt::Display for Index {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}({})", self.idx, self.version)
}
}
impl Index {
pub const INVALID: Self = Self {
idx: usize::MAX,
version: u64::MAX,
};
}
impl From<usize> for Index {
fn from(idx: usize) -> Self {
Index { idx, version: 0 }
}
}
impl From<u32> for Index {
fn from(idx: u32) -> Self {
Index {
idx: idx as usize,
version: 0,
}
}
}
impl Arena {
#[cfg(feature = "arena-delete")]
unsafe fn delte_index_this_is_really_unsafe_dont_use_it_(&mut self, idx: Index) -> Result<()> {
if let Some(e) = self.sources.get_mut(idx.idx) {
if e.version == idx.version {
e.version += 1;
e.src = None;
eprintln!("[ARENA] Freed arena index {}", idx);
Ok(())
} else {
Err("Invalid version to delete".into())
}
} else {
Err("Index already deleted".into())
}
}
#[allow(clippy::let_and_return)]
fn insert_<S>(&mut self, src: &S) -> Index
where
S: ToString + ?Sized,
{
if let Some((idx, e)) = self
.sources
.iter_mut()
.enumerate()
.find(|e| e.1.src.is_none())
{
e.src = Some(Pin::new(src.to_string()));
let idx = Index {
idx,
version: e.version,
};
#[cfg(feature = "arena-delete")]
eprintln!("[ARENA] Reclaimed arena index {}", idx);
idx
} else {
let idx = self.sources.len();
self.sources.push(ArenaEntry {
src: Some(Pin::new(src.to_string())),
version: 0,
});
let idx = Index { idx, version: 0 };
#[cfg(feature = "arena-delete")]
eprintln!("[ARENA] Added arena index {}", idx);
idx
}
}
fn get_(&self, id: Index) -> Option<&str> {
self.sources.get(id.idx).and_then(|e| {
if e.version == id.version {
e.src.as_deref()
} else {
None
}
})
}
unsafe fn get_static(&self, id: Index) -> Option<&'static str> {
self.get_(id).map(|s| mem::transmute::<_, &'static str>(s))
}
pub fn get(id: Index) -> Result<Option<&'static str>> {
Ok(unsafe { ARENA.read()?.get_static(id) })
}
pub fn io_get(aid: Index) -> io::Result<&'static str> {
Arena::get(aid)?.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "source not found"))
}
pub fn insert<S>(src: &S) -> Result<(Index, &'static str)>
where
S: ToString + ?Sized,
{
let mut a = ARENA.write()?;
let id = a.insert_(src);
let s = unsafe { a.get_static(id).ok_or("this can't happen")? };
Ok((id, s))
}
#[cfg(feature = "arena-delete")]
pub unsafe fn delte_index_this_is_really_unsafe_dont_use_it(id: Index) -> Result<()> {
let mut a = ARENA.write()?;
a.delte_index_this_is_really_unsafe_dont_use_it_(id)
}
}