math_core_renderer_internal/
arena.rs

1use std::fmt::Debug;
2
3use stable_arena::DroplessArena;
4
5use crate::{
6    ast::Node,
7    table::{ArraySpec, ColumnSpec},
8};
9
10pub struct Arena {
11    inner: DroplessArena,
12}
13
14impl Arena {
15    pub fn new() -> Self {
16        Arena {
17            inner: DroplessArena::default(),
18        }
19    }
20
21    pub fn push<'arena>(&'arena self, node: Node<'arena>) -> &'arena mut Node<'arena> {
22        self.inner.alloc(node)
23    }
24
25    pub fn push_slice<'arena>(
26        &'arena self,
27        nodes: &[&'arena Node<'arena>],
28    ) -> &'arena [&'arena Node<'arena>] {
29        // `DroplessArena::alloc_slice()` panics on empty slices.
30        if nodes.is_empty() {
31            &[]
32        } else {
33            self.inner.alloc_slice(nodes)
34        }
35    }
36
37    pub fn alloc_str(&self, src: &str) -> &str {
38        // `DroplessArena::alloc_str()` panics on empty strings.
39        if src.is_empty() {
40            ""
41        } else {
42            self.inner.alloc_str(src)
43        }
44    }
45
46    pub fn alloc_column_specs<'arena>(
47        &'arena self,
48        column_specs: &[ColumnSpec],
49    ) -> &'arena [ColumnSpec] {
50        // `DroplessArena::alloc_slice()` panics on empty slices.
51        if column_specs.is_empty() {
52            &[]
53        } else {
54            self.inner.alloc_slice(column_specs)
55        }
56    }
57
58    pub fn alloc_array_spec<'arena>(
59        &'arena self,
60        array_spec: ArraySpec<'arena>,
61    ) -> &'arena ArraySpec<'arena> {
62        self.inner.alloc(array_spec)
63    }
64}
65
66impl Default for Arena {
67    fn default() -> Self {
68        Self::new()
69    }
70}
71
72#[derive(Debug)]
73#[repr(transparent)]
74pub struct Buffer(String);
75
76impl Buffer {
77    pub fn new(size_hint: usize) -> Self {
78        Buffer(String::with_capacity(size_hint))
79    }
80
81    pub fn get_builder(&mut self) -> StringBuilder<'_> {
82        StringBuilder::new(self)
83    }
84}
85
86/// A helper type to safely build a string in the buffer from multiple pieces.
87///
88/// It takes an exclusive reference to the buffer and clears everything in the
89/// buffer before we start building. This guarantees that upon finishing, the
90/// buffer contains only what we wrote to it.
91pub struct StringBuilder<'buffer> {
92    buffer: &'buffer mut Buffer,
93}
94
95impl<'buffer> StringBuilder<'buffer> {
96    pub fn new(buffer: &'buffer mut Buffer) -> Self {
97        // Clear the buffer before we start building.
98        buffer.0.clear();
99        StringBuilder { buffer }
100    }
101
102    #[inline]
103    pub fn push_str(&mut self, src: &str) {
104        self.buffer.0.push_str(src)
105    }
106
107    pub fn push_char(&mut self, c: char) {
108        self.buffer.0.push(c)
109    }
110
111    pub fn finish(self, arena: &Arena) -> &str {
112        arena.alloc_str(&self.buffer.0)
113    }
114
115    pub fn is_empty(&self) -> bool {
116        self.buffer.0.is_empty()
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn arena_test() {
126        let arena = Arena::new();
127        let node = Node::HardcodedMathML("Hello, world!");
128        let reference = arena.push(node);
129        assert!(matches!(reference, Node::HardcodedMathML("Hello, world!")));
130    }
131
132    #[test]
133    fn buffer_extend() {
134        let arena = Arena::new();
135        let mut buffer = Buffer::new(0);
136        let mut builder = buffer.get_builder();
137        builder.push_char('H');
138        builder.push_char('i');
139        let str_ref = builder.finish(&arena);
140        assert_eq!(str_ref, "Hi");
141    }
142
143    #[test]
144    fn buffer_manual_reference() {
145        let arena = Arena::new();
146        let mut buffer = Buffer::new(0);
147        let mut builder = buffer.get_builder();
148        assert_eq!(builder.buffer.0.len(), 0);
149        builder.push_char('H');
150        builder.push_char('i');
151        builder.push_char('↩'); // This is a multi-byte character.
152        assert_eq!(builder.buffer.0.len(), 5);
153        let str_ref = builder.finish(&arena);
154        assert_eq!(str_ref.len(), 5);
155        assert_eq!(str_ref, "Hi↩");
156    }
157
158    struct CycleParticipant<'a> {
159        val: i32,
160        next: Option<&'a mut CycleParticipant<'a>>,
161    }
162
163    #[test]
164    fn basic_arena() {
165        let arena = DroplessArena::default();
166
167        let a = arena.alloc(CycleParticipant { val: 1, next: None });
168        let b = arena.alloc(CycleParticipant { val: 2, next: None });
169        a.next = Some(b);
170        let c = arena.alloc(CycleParticipant { val: 3, next: None });
171        a.next.as_mut().unwrap().next = Some(c);
172
173        // for (i, node) in arena.iter_mut().enumerate() {
174        //     match i {
175        //         0 => assert_eq!(node.val, 1),
176        //         1 => assert_eq!(node.val, 2),
177        //         2 => assert_eq!(node.val, 3),
178        //         _ => panic!("Too many nodes"),
179        //     }
180        // }
181
182        assert_eq!(a.val, 1);
183        assert_eq!(a.next.as_ref().unwrap().val, 2);
184        assert_eq!(a.next.as_ref().unwrap().next.as_ref().unwrap().val, 3);
185    }
186}