use std::fmt::Debug;
use stable_arena::DroplessArena;
use crate::{
ast::{AHref, Node},
super_char::SuperChar,
table::{ArraySpec, ColumnSpec, RowLabelInfo},
};
pub struct Arena {
inner: DroplessArena,
}
impl Arena {
pub fn new() -> Self {
Arena {
inner: DroplessArena::default(),
}
}
pub fn push<'arena>(&'arena self, node: Node<'arena>) -> &'arena mut Node<'arena> {
self.inner.alloc(node)
}
pub fn push_slice<'arena>(
&'arena self,
nodes: &[&'arena Node<'arena>],
) -> &'arena [&'arena Node<'arena>] {
if nodes.is_empty() {
&[]
} else {
self.inner.alloc_slice(nodes)
}
}
pub fn alloc_str(&self, src: &str) -> &str {
if src.is_empty() {
""
} else {
self.inner.alloc_str(src)
}
}
pub fn alloc_column_specs<'arena>(
&'arena self,
column_specs: &[ColumnSpec],
) -> &'arena [ColumnSpec] {
if column_specs.is_empty() {
&[]
} else {
self.inner.alloc_slice(column_specs)
}
}
pub fn alloc_array_spec<'arena>(
&'arena self,
array_spec: ArraySpec<'arena>,
) -> &'arena ArraySpec<'arena> {
self.inner.alloc(array_spec)
}
pub fn alloc_row_label_info<'arena>(
&'arena self,
info: RowLabelInfo<'arena>,
) -> &'arena RowLabelInfo<'arena> {
self.inner.alloc(info)
}
pub fn alloc_ahref<'arena, 'attrs>(
&'arena self,
ahref: AHref<'attrs>,
) -> &'arena AHref<'attrs> {
self.inner.alloc(ahref)
}
}
impl Default for Arena {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct Buffer(String);
impl Buffer {
pub fn new(size_hint: usize) -> Self {
Buffer(String::with_capacity(size_hint))
}
pub fn get_builder(&mut self) -> StringBuilder<'_> {
StringBuilder::new(self)
}
}
#[derive(Debug)]
pub struct StringBuilder<'buffer> {
buffer: &'buffer mut Buffer,
}
impl<'buffer> StringBuilder<'buffer> {
pub fn new(buffer: &'buffer mut Buffer) -> Self {
buffer.0.clear();
StringBuilder { buffer }
}
#[inline]
pub fn push_str(&mut self, src: &str) {
self.buffer.0.push_str(src)
}
pub fn push_char(&mut self, c: char) {
self.buffer.0.push(c)
}
#[inline]
pub fn push_superchar(&mut self, sc: SuperChar) {
self.buffer.0.extend(sc.chars());
}
pub fn finish(self, arena: &Arena) -> &str {
arena.alloc_str(&self.buffer.0)
}
pub fn is_empty(&self) -> bool {
self.buffer.0.is_empty()
}
}
impl std::fmt::Write for StringBuilder<'_> {
#[inline]
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.push_str(s);
Ok(())
}
#[inline]
fn write_char(&mut self, c: char) -> std::fmt::Result {
self.push_char(c);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arena() {
let arena = Arena::new();
let node = Node::HardcodedMathML("Hello, world!");
let reference = arena.push(node);
assert!(matches!(reference, Node::HardcodedMathML("Hello, world!")));
}
#[test]
fn test_buffer_extend() {
let arena = Arena::new();
let mut buffer = Buffer::new(0);
let mut builder = buffer.get_builder();
builder.push_char('H');
builder.push_char('i');
let str_ref = builder.finish(&arena);
assert_eq!(str_ref, "Hi");
}
#[test]
fn test_buffer_manual_reference() {
let arena = Arena::new();
let mut buffer = Buffer::new(0);
let mut builder = buffer.get_builder();
assert_eq!(builder.buffer.0.len(), 0);
builder.push_char('H');
builder.push_char('i');
builder.push_char('↩'); assert_eq!(builder.buffer.0.len(), 5);
let str_ref = builder.finish(&arena);
assert_eq!(str_ref.len(), 5);
assert_eq!(str_ref, "Hi↩");
}
struct CycleParticipant<'a> {
val: i32,
next: Option<&'a mut CycleParticipant<'a>>,
}
#[test]
fn test_arena_with_cycle() {
let arena = DroplessArena::default();
let a = arena.alloc(CycleParticipant { val: 1, next: None });
let b = arena.alloc(CycleParticipant { val: 2, next: None });
a.next = Some(b);
let c = arena.alloc(CycleParticipant { val: 3, next: None });
a.next.as_mut().unwrap().next = Some(c);
assert_eq!(a.val, 1);
assert_eq!(a.next.as_ref().unwrap().val, 2);
assert_eq!(a.next.as_ref().unwrap().next.as_ref().unwrap().val, 3);
}
}