use crate::{InternalTermError, IntoTerm, OperDefs, Slice, Term, TermError, View};
#[derive(Default, Clone, Debug)]
pub struct Arena {
pub(crate) arena_id: ArenaID,
pub(crate) current_epoch: usize,
pub(crate) epoch_ids: [EpochID; MAX_LIVE_EPOCHS],
pub(crate) bytes: Vec<u8>,
pub(crate) byte_start_by_epoch: [usize; MAX_LIVE_EPOCHS],
pub(crate) terms: Vec<Term>,
pub(crate) term_start_by_epoch: [usize; MAX_LIVE_EPOCHS],
pub(crate) opers: OperDefs,
}
pub const MAX_LIVE_EPOCHS: usize = 8;
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct EpochID(pub(crate) u32);
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub struct ArenaID(pub(crate) u32);
#[derive(Debug, Clone, Copy)]
pub struct ArenaStats {
pub current_epoch: EpochID,
pub bytes_len: usize,
pub terms_len: usize,
}
impl Arena {
pub fn with_capacity(bytes_capacity: usize, terms_capacity: usize) -> Self {
let mut epoch_ids = [EpochID(0); MAX_LIVE_EPOCHS];
epoch_ids[0] = EpochID(rand::random());
Self {
arena_id: ArenaID(rand::random()),
current_epoch: 0,
epoch_ids,
bytes: Vec::with_capacity(bytes_capacity),
byte_start_by_epoch: [0; MAX_LIVE_EPOCHS],
terms: Vec::with_capacity(terms_capacity),
term_start_by_epoch: [0; MAX_LIVE_EPOCHS],
opers: OperDefs::new(),
}
}
pub fn new() -> Self {
Self::with_capacity(4096, 1024)
}
pub fn try_with_default_opers() -> Result<Self, TermError> {
let mut arena = Self::new();
arena.define_default_opers()?;
Ok(arena)
}
pub fn stats(&self) -> ArenaStats {
ArenaStats {
current_epoch: self.epoch_ids[self.current_epoch],
bytes_len: self.bytes.len(),
terms_len: self.terms.len(),
}
}
pub fn current_epoch(&self) -> EpochID {
self.epoch_ids[self.current_epoch]
}
pub fn begin_epoch(&mut self) -> Result<EpochID, TermError> {
let new_epoch = self.current_epoch + 1;
if new_epoch >= MAX_LIVE_EPOCHS {
return Err(TermError::LiveEpochsExceeded);
}
self.epoch_ids[new_epoch] = EpochID(rand::random());
self.byte_start_by_epoch[new_epoch] = self.bytes.len();
self.term_start_by_epoch[new_epoch] = self.terms.len();
self.current_epoch = new_epoch;
Ok(self.epoch_ids[new_epoch])
}
pub fn clear(&mut self) -> Result<(), TermError> {
self.truncate(self.epoch_ids[0])
}
pub fn truncate_current(&mut self) -> Result<(), TermError> {
self.truncate(self.epoch_ids[self.current_epoch])
}
pub fn truncate(&mut self, epoch_id: EpochID) -> Result<(), TermError> {
let epoch = self
.epoch_index(epoch_id)
.map_err(|_| TermError::InvalidEpoch(epoch_id))?;
self.bytes.truncate(self.byte_start_by_epoch[epoch]);
self.terms.truncate(self.term_start_by_epoch[epoch]);
self.current_epoch = epoch;
Ok(())
}
#[inline]
fn epoch_index(&self, epoch_id: EpochID) -> Result<usize, InternalTermError> {
let Some(epoch) = self.epoch_ids[..=self.current_epoch]
.iter()
.position(|&id| id == epoch_id)
else {
return Err(InternalTermError::InvalidEpoch(epoch_id));
};
Ok(epoch)
}
#[inline]
fn verify_byte_slice(&self, slice: &Slice) -> Result<(), InternalTermError> {
let epoch = self.epoch_index(slice.epoch_id)?;
let epoch_start = self.byte_start_by_epoch[epoch];
let epoch_end = if epoch == self.current_epoch {
self.bytes.len()
} else {
self.byte_start_by_epoch[epoch + 1]
};
if (slice.index as usize) < epoch_start
|| (slice.index as usize) + (slice.len as usize) > epoch_end
{
return Err(InternalTermError::InvalidSlice(*slice));
}
Ok(())
}
#[inline]
fn verify_term_slice(&self, slice: &Slice) -> Result<(), InternalTermError> {
let epoch = self.epoch_index(slice.epoch_id)?;
let epoch_start = self.term_start_by_epoch[epoch];
let epoch_end = if epoch == self.current_epoch {
self.terms.len()
} else {
self.term_start_by_epoch[epoch + 1]
};
if (slice.index as usize) < epoch_start
|| (slice.index as usize) + (slice.len as usize) > epoch_end
{
return Err(InternalTermError::InvalidSlice(*slice));
}
Ok(())
}
#[inline]
pub fn term<'a, T: IntoTerm>(&'a mut self, value: T) -> Term {
value.into_term(self)
}
#[inline]
pub fn int(&mut self, i: impl Into<i64>) -> Term {
Term::int(i)
}
#[inline]
pub fn real(&mut self, r: impl Into<f64>) -> Term {
Term::real(r)
}
#[inline]
pub fn date(&mut self, ms: impl Into<i64>) -> Term {
Term::date(ms)
}
#[inline]
pub fn atom(&mut self, name: impl AsRef<str>) -> Term {
Term::atom(self, name)
}
#[inline]
pub fn var(&mut self, name: impl AsRef<str>) -> Term {
Term::var(self, name)
}
#[inline]
pub fn str(&mut self, s: impl AsRef<str>) -> Term {
Term::str(self, s)
}
#[inline]
pub fn bin(&mut self, bytes: impl AsRef<[u8]>) -> Term {
Term::bin(self, bytes)
}
#[inline]
pub fn func(
&mut self,
functor: impl AsRef<str>,
args: impl IntoIterator<Item = impl IntoTerm>,
) -> Term {
Term::func(self, functor, args)
}
#[inline]
pub fn funcv(
&mut self,
terms: impl IntoIterator<Item = impl IntoTerm>,
) -> Result<Term, TermError> {
Term::funcv(self, terms)
}
#[inline]
pub fn list(&mut self, terms: impl IntoIterator<Item = impl IntoTerm>) -> Term {
Term::list(self, terms)
}
#[inline]
pub fn listc(
&mut self,
terms: impl IntoIterator<Item = impl IntoTerm>,
tail: impl IntoTerm,
) -> Term {
Term::listc(self, terms, tail)
}
#[inline]
pub fn tuple(&mut self, terms: impl IntoIterator<Item = impl IntoTerm>) -> Term {
Term::tuple(self, terms)
}
pub const UNIT: Term = Term::UNIT;
pub const NIL: Term = Term::NIL;
#[inline]
pub fn name<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
match self.view(term)? {
View::Var(name) | View::Atom(name) => Ok(name),
View::Func(ar, functor, _) => Ok(functor.atom_name(ar)?),
_ => Err(TermError::UnexpectedKind {
expected: "var, atom, func",
found: term.kind_name(),
}),
}
}
#[inline]
pub fn atom_name<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
self.unpack_atom(term, &[])
}
#[inline]
pub fn var_name<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
self.unpack_var(term, &[])
}
#[inline]
pub fn func_name<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
let (functor, _) = self.unpack_func_any(term, &[])?;
self.atom_name(functor)
}
#[inline]
pub fn unpack_int(&self, term: &Term) -> Result<i64, TermError> {
match self.view(term)? {
View::Int(v) => Ok(v),
_ => Err(TermError::UnexpectedKind {
expected: "int",
found: term.kind_name(),
}),
}
}
#[inline]
pub fn unpack_real(&self, term: &Term) -> Result<f64, TermError> {
match self.view(term)? {
View::Real(v) => Ok(v),
_ => Err(TermError::UnexpectedKind {
expected: "real",
found: term.kind_name(),
}),
}
}
#[inline]
pub fn unpack_date(&self, term: &Term) -> Result<i64, TermError> {
match self.view(term)? {
View::Date(v) => Ok(v),
_ => Err(TermError::UnexpectedKind {
expected: "date",
found: term.kind_name(),
}),
}
}
#[inline]
pub fn unpack_str<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
match self.view(term)? {
View::Str(v) => Ok(v),
_ => Err(TermError::UnexpectedKind {
expected: "str",
found: term.kind_name(),
}),
}
}
#[inline]
pub fn unpack_bin<'a>(&'a self, term: &'a Term) -> Result<&'a [u8], TermError> {
match self.view(term)? {
View::Bin(v) => Ok(v),
_ => Err(TermError::UnexpectedKind {
expected: "bin",
found: term.kind_name(),
}),
}
}
#[inline]
pub fn unpack_atom<'a>(
&'a self,
term: &'a Term,
allowed_names: &[&str],
) -> Result<&'a str, TermError> {
match self.view(term)? {
View::Atom(name) => {
if !allowed_names.is_empty() && !allowed_names.contains(&name) {
return Err(TermError::UnexpectedName(*term));
}
Ok(name)
}
_ => Err(TermError::UnexpectedKind {
expected: "atom",
found: term.kind_name(),
}),
}
}
#[inline]
pub fn unpack_var<'a>(
&'a self,
term: &'a Term,
allowed_names: &[&str],
) -> Result<&'a str, TermError> {
match self.view(term)? {
View::Var(name) => {
if !allowed_names.is_empty() && !allowed_names.contains(&name) {
return Err(TermError::UnexpectedName(*term));
}
Ok(name)
}
_ => Err(TermError::UnexpectedKind {
expected: "var",
found: term.kind_name(),
}),
}
}
#[inline]
pub fn unpack_func_any<'a>(
&'a self,
term: &'a Term,
allowed_names: &[&str],
) -> Result<(&'a Term, &'a [Term]), TermError> {
match self.view(term)? {
View::Atom(name) => {
if !allowed_names.is_empty() && !allowed_names.contains(&name) {
return Err(TermError::UnexpectedName(*term));
}
Ok((term, &[] as &[Term]))
}
View::Func(_, functor, args) => {
if args.is_empty() {
return Err(TermError::InvalidTerm(*term));
}
if !allowed_names.is_empty() {
let name = self.atom_name(functor)?;
if !allowed_names.contains(&name) {
return Err(TermError::UnexpectedName(*term));
}
}
Ok((functor, args))
}
_ => Err(TermError::UnexpectedKind {
expected: "func",
found: term.kind_name(),
}),
}
}
#[inline]
pub fn unpack_func<'a, const ARITY: usize>(
&'a self,
term: &'a Term,
allowed_names: &[&str],
) -> Result<(&'a Term, [Term; ARITY]), TermError> {
let (functor, args) = self.unpack_func_any(term, allowed_names)?;
if args.len() != ARITY {
return Err(TermError::UnexpectedArity {
expected: ARITY,
found: args.len(),
});
}
let arr: [_; ARITY] = args.try_into().unwrap();
return Ok((functor, arr));
}
#[inline]
pub fn unpack_list<'a>(&'a self, term: &'a Term) -> Result<(&'a [Term], &'a Term), TermError> {
match self.view(term)? {
View::Atom(_) if term == &Term::NIL => Ok((&[], &Term::NIL)),
View::List(_, terms, tail) => Ok((terms, tail)),
_ => Err(TermError::UnexpectedKind {
expected: "list",
found: term.kind_name(),
}),
}
}
#[inline]
pub fn unpack_tuple_any<'a>(&'a self, term: &'a Term) -> Result<&'a [Term], TermError> {
match self.view(term)? {
View::Atom(_) if *term == Term::UNIT => Ok(&[]),
View::Tuple(_, terms) => Ok(terms),
_ => Err(TermError::UnexpectedKind {
expected: "tuple",
found: term.kind_name(),
}),
}
}
#[inline]
pub fn unpack_tuple<const ARITY: usize>(
&self,
term: &Term,
) -> Result<[Term; ARITY], TermError> {
let terms = self.unpack_tuple_any(term)?;
if terms.len() != ARITY {
return Err(TermError::UnexpectedArity {
expected: ARITY,
found: terms.len(),
});
}
let arr: [_; ARITY] = terms.try_into().unwrap();
return Ok(arr);
}
#[inline]
pub(crate) fn intern_str(&mut self, s: &str) -> Slice {
let index = self.bytes.len();
self.bytes.extend_from_slice(s.as_bytes());
let len = s.len();
Slice {
epoch_id: self.epoch_ids[self.current_epoch],
index: index as u32,
len: len as u32,
}
}
#[inline]
pub(crate) fn intern_bytes(&mut self, bytes: &[u8]) -> Slice {
let index = self.bytes.len();
self.bytes.extend_from_slice(bytes);
let len = bytes.len();
Slice {
epoch_id: self.epoch_ids[self.current_epoch],
index: index as u32,
len: len as u32,
}
}
#[inline]
pub(crate) fn intern_func(
&mut self,
functor: Term,
args: impl IntoIterator<Item = impl IntoTerm>,
) -> Slice {
let index = self.terms.len();
self.terms.push(functor);
for x in args {
let t = x.into_term(self);
self.terms.push(t);
}
let len = self.terms.len() - index;
Slice {
epoch_id: self.epoch_ids[self.current_epoch],
index: index as u32,
len: len as u32,
}
}
#[inline]
pub(crate) fn intern_seq(&mut self, terms: impl IntoIterator<Item = impl IntoTerm>) -> Slice {
let index = self.terms.len();
for x in terms {
let t = x.into_term(self);
self.terms.push(t);
}
let len = self.terms.len() - index;
Slice {
epoch_id: self.epoch_ids[self.current_epoch],
index: index as u32,
len: len as u32,
}
}
#[inline]
pub(crate) fn intern_seq_plus_one(
&mut self,
terms: impl IntoIterator<Item = impl IntoTerm>,
tail: impl IntoTerm,
) -> Slice {
let index = self.terms.len();
for x in terms {
let t = x.into_term(self);
self.terms.push(t);
}
let t = tail.into_term(self);
self.terms.push(t);
let len = self.terms.len() - index;
Slice {
epoch_id: self.epoch_ids[self.current_epoch],
index: index as u32,
len: len as u32,
}
}
#[inline]
pub(crate) fn byte_slice<'a>(&'a self, slice: &Slice) -> Result<&'a [u8], InternalTermError> {
self.verify_byte_slice(slice)?;
Ok(&self.bytes[(slice.index as usize)..((slice.index + slice.len) as usize)])
}
#[inline]
pub(crate) fn term_slice<'a>(&'a self, slice: &Slice) -> Result<&'a [Term], InternalTermError> {
self.verify_term_slice(slice)?;
Ok(&self.terms[(slice.index as usize)..((slice.index + slice.len) as usize)])
}
}