Skip to main content

math_core_renderer_internal/
arena.rs

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