Skip to main content

math_core_renderer_internal/
arena.rs

1use std::fmt::Debug;
2
3use stable_arena::DroplessArena;
4
5use crate::{
6    ast::{AHref, MultiscriptPair, Node},
7    super_char::SuperChar,
8    table::{ArraySpec, ColumnSpec, RowLabelInfo},
9};
10
11pub struct Arena {
12    inner: DroplessArena,
13}
14
15impl Arena {
16    pub fn new() -> Self {
17        Arena {
18            inner: DroplessArena::default(),
19        }
20    }
21
22    pub fn push<'arena>(&'arena self, node: Node<'arena>) -> &'arena mut Node<'arena> {
23        self.inner.alloc(node)
24    }
25
26    pub fn push_slice<'arena>(
27        &'arena self,
28        nodes: &[&'arena Node<'arena>],
29    ) -> &'arena [&'arena Node<'arena>] {
30        // `DroplessArena::alloc_slice()` panics on empty slices.
31        if nodes.is_empty() {
32            &[]
33        } else {
34            self.inner.alloc_slice(nodes)
35        }
36    }
37
38    pub fn alloc_str(&self, src: &str) -> &str {
39        // `DroplessArena::alloc_str()` panics on empty strings.
40        if src.is_empty() {
41            ""
42        } else {
43            self.inner.alloc_str(src)
44        }
45    }
46
47    pub fn alloc_column_specs<'arena>(
48        &'arena self,
49        column_specs: &[ColumnSpec],
50    ) -> &'arena [ColumnSpec] {
51        // `DroplessArena::alloc_slice()` panics on empty slices.
52        if column_specs.is_empty() {
53            &[]
54        } else {
55            self.inner.alloc_slice(column_specs)
56        }
57    }
58
59    pub fn alloc_array_spec<'arena>(
60        &'arena self,
61        array_spec: ArraySpec<'arena>,
62    ) -> &'arena ArraySpec<'arena> {
63        self.inner.alloc(array_spec)
64    }
65
66    pub fn alloc_row_label_info<'arena>(
67        &'arena self,
68        info: RowLabelInfo<'arena>,
69    ) -> &'arena RowLabelInfo<'arena> {
70        self.inner.alloc(info)
71    }
72
73    pub fn alloc_ahref<'arena, 'attrs>(
74        &'arena self,
75        ahref: AHref<'attrs>,
76    ) -> &'arena AHref<'attrs> {
77        self.inner.alloc(ahref)
78    }
79
80    pub fn alloc_multiscript_pairs<'arena, 'pairs>(
81        &'arena self,
82        pairs: &[MultiscriptPair<'pairs>],
83    ) -> &'arena &'arena [MultiscriptPair<'pairs>] {
84        let fat = self.inner.alloc_slice(pairs);
85        self.inner.alloc(&*fat)
86    }
87}
88
89impl Default for Arena {
90    fn default() -> Self {
91        Self::new()
92    }
93}
94
95#[derive(Debug)]
96#[repr(transparent)]
97pub struct Buffer(String);
98
99impl Buffer {
100    pub fn new(size_hint: usize) -> Self {
101        Buffer(String::with_capacity(size_hint))
102    }
103
104    pub fn get_builder(&mut self) -> StringBuilder<'_> {
105        StringBuilder::new(self)
106    }
107}
108
109/// A helper type to safely build a string in the buffer from multiple pieces.
110///
111/// It takes an exclusive reference to the buffer and clears everything in the
112/// buffer before we start building. This guarantees that upon finishing, the
113/// buffer contains only what we wrote to it.
114#[derive(Debug)]
115pub struct StringBuilder<'buffer> {
116    buffer: &'buffer mut Buffer,
117}
118
119impl<'buffer> StringBuilder<'buffer> {
120    pub fn new(buffer: &'buffer mut Buffer) -> Self {
121        // Clear the buffer before we start building.
122        buffer.0.clear();
123        StringBuilder { buffer }
124    }
125
126    #[inline]
127    pub fn push_str(&mut self, src: &str) {
128        self.buffer.0.push_str(src)
129    }
130
131    pub fn push_char(&mut self, c: char) {
132        self.buffer.0.push(c)
133    }
134
135    #[inline]
136    pub fn push_superchar(&mut self, sc: SuperChar) {
137        self.buffer.0.extend(sc.chars());
138    }
139
140    pub fn finish(self, arena: &Arena) -> &str {
141        arena.alloc_str(&self.buffer.0)
142    }
143
144    pub fn is_empty(&self) -> bool {
145        self.buffer.0.is_empty()
146    }
147}
148
149impl std::fmt::Write for StringBuilder<'_> {
150    #[inline]
151    fn write_str(&mut self, s: &str) -> std::fmt::Result {
152        self.push_str(s);
153        Ok(())
154    }
155
156    #[inline]
157    fn write_char(&mut self, c: char) -> std::fmt::Result {
158        self.push_char(c);
159        Ok(())
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use crate::ast::RowAttrs;
166
167    use super::*;
168
169    #[test]
170    fn test_arena() {
171        let arena = Arena::new();
172        let node = Node::EMPTY_ROW;
173        let reference = arena.push(node);
174        assert!(matches!(
175            reference,
176            Node::Row {
177                nodes: [],
178                attrs: RowAttrs::DEFAULT,
179            },
180        ));
181    }
182
183    #[test]
184    fn test_buffer_extend() {
185        let arena = Arena::new();
186        let mut buffer = Buffer::new(0);
187        let mut builder = buffer.get_builder();
188        builder.push_char('H');
189        builder.push_char('i');
190        let str_ref = builder.finish(&arena);
191        assert_eq!(str_ref, "Hi");
192    }
193
194    #[test]
195    fn test_buffer_manual_reference() {
196        let arena = Arena::new();
197        let mut buffer = Buffer::new(0);
198        let mut builder = buffer.get_builder();
199        assert_eq!(builder.buffer.0.len(), 0);
200        builder.push_char('H');
201        builder.push_char('i');
202        builder.push_char('↩'); // This is a multi-byte character.
203        assert_eq!(builder.buffer.0.len(), 5);
204        let str_ref = builder.finish(&arena);
205        assert_eq!(str_ref.len(), 5);
206        assert_eq!(str_ref, "Hi↩");
207    }
208
209    struct CycleParticipant<'a> {
210        val: i32,
211        next: Option<&'a mut CycleParticipant<'a>>,
212    }
213
214    #[test]
215    fn test_arena_with_cycle() {
216        let arena = DroplessArena::default();
217
218        let a = arena.alloc(CycleParticipant { val: 1, next: None });
219        let b = arena.alloc(CycleParticipant { val: 2, next: None });
220        a.next = Some(b);
221        let c = arena.alloc(CycleParticipant { val: 3, next: None });
222        a.next.as_mut().unwrap().next = Some(c);
223
224        // for (i, node) in arena.iter_mut().enumerate() {
225        //     match i {
226        //         0 => assert_eq!(node.val, 1),
227        //         1 => assert_eq!(node.val, 2),
228        //         2 => assert_eq!(node.val, 3),
229        //         _ => panic!("Too many nodes"),
230        //     }
231        // }
232
233        assert_eq!(a.val, 1);
234        assert_eq!(a.next.as_ref().unwrap().val, 2);
235        assert_eq!(a.next.as_ref().unwrap().next.as_ref().unwrap().val, 3);
236    }
237}