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, 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
81impl Default for Arena {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87#[derive(Debug)]
88#[repr(transparent)]
89pub struct Buffer(String);
90
91impl Buffer {
92    pub fn new(size_hint: usize) -> Self {
93        Buffer(String::with_capacity(size_hint))
94    }
95
96    pub fn get_builder(&mut self) -> StringBuilder<'_> {
97        StringBuilder::new(self)
98    }
99}
100
101/// A helper type to safely build a string in the buffer from multiple pieces.
102///
103/// It takes an exclusive reference to the buffer and clears everything in the
104/// buffer before we start building. This guarantees that upon finishing, the
105/// buffer contains only what we wrote to it.
106#[derive(Debug)]
107pub struct StringBuilder<'buffer> {
108    buffer: &'buffer mut Buffer,
109}
110
111impl<'buffer> StringBuilder<'buffer> {
112    pub fn new(buffer: &'buffer mut Buffer) -> Self {
113        // Clear the buffer before we start building.
114        buffer.0.clear();
115        StringBuilder { buffer }
116    }
117
118    #[inline]
119    pub fn push_str(&mut self, src: &str) {
120        self.buffer.0.push_str(src)
121    }
122
123    pub fn push_char(&mut self, c: char) {
124        self.buffer.0.push(c)
125    }
126
127    #[inline]
128    pub fn push_superchar(&mut self, sc: SuperChar) {
129        self.buffer.0.extend(sc.chars());
130    }
131
132    pub fn finish(self, arena: &Arena) -> &str {
133        arena.alloc_str(&self.buffer.0)
134    }
135
136    pub fn is_empty(&self) -> bool {
137        self.buffer.0.is_empty()
138    }
139}
140
141impl std::fmt::Write for StringBuilder<'_> {
142    #[inline]
143    fn write_str(&mut self, s: &str) -> std::fmt::Result {
144        self.push_str(s);
145        Ok(())
146    }
147
148    #[inline]
149    fn write_char(&mut self, c: char) -> std::fmt::Result {
150        self.push_char(c);
151        Ok(())
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn test_arena() {
161        let arena = Arena::new();
162        let node = Node::HardcodedMathML("Hello, world!");
163        let reference = arena.push(node);
164        assert!(matches!(reference, Node::HardcodedMathML("Hello, world!")));
165    }
166
167    #[test]
168    fn test_buffer_extend() {
169        let arena = Arena::new();
170        let mut buffer = Buffer::new(0);
171        let mut builder = buffer.get_builder();
172        builder.push_char('H');
173        builder.push_char('i');
174        let str_ref = builder.finish(&arena);
175        assert_eq!(str_ref, "Hi");
176    }
177
178    #[test]
179    fn test_buffer_manual_reference() {
180        let arena = Arena::new();
181        let mut buffer = Buffer::new(0);
182        let mut builder = buffer.get_builder();
183        assert_eq!(builder.buffer.0.len(), 0);
184        builder.push_char('H');
185        builder.push_char('i');
186        builder.push_char('↩'); // This is a multi-byte character.
187        assert_eq!(builder.buffer.0.len(), 5);
188        let str_ref = builder.finish(&arena);
189        assert_eq!(str_ref.len(), 5);
190        assert_eq!(str_ref, "Hi↩");
191    }
192
193    struct CycleParticipant<'a> {
194        val: i32,
195        next: Option<&'a mut CycleParticipant<'a>>,
196    }
197
198    #[test]
199    fn test_arena_with_cycle() {
200        let arena = DroplessArena::default();
201
202        let a = arena.alloc(CycleParticipant { val: 1, next: None });
203        let b = arena.alloc(CycleParticipant { val: 2, next: None });
204        a.next = Some(b);
205        let c = arena.alloc(CycleParticipant { val: 3, next: None });
206        a.next.as_mut().unwrap().next = Some(c);
207
208        // for (i, node) in arena.iter_mut().enumerate() {
209        //     match i {
210        //         0 => assert_eq!(node.val, 1),
211        //         1 => assert_eq!(node.val, 2),
212        //         2 => assert_eq!(node.val, 3),
213        //         _ => panic!("Too many nodes"),
214        //     }
215        // }
216
217        assert_eq!(a.val, 1);
218        assert_eq!(a.next.as_ref().unwrap().val, 2);
219        assert_eq!(a.next.as_ref().unwrap().next.as_ref().unwrap().val, 3);
220    }
221}