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