Skip to main content

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
120impl std::fmt::Write for StringBuilder<'_> {
121    #[inline]
122    fn write_str(&mut self, s: &str) -> std::fmt::Result {
123        self.push_str(s);
124        Ok(())
125    }
126
127    #[inline]
128    fn write_char(&mut self, c: char) -> std::fmt::Result {
129        self.push_char(c);
130        Ok(())
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn test_arena() {
140        let arena = Arena::new();
141        let node = Node::HardcodedMathML("Hello, world!");
142        let reference = arena.push(node);
143        assert!(matches!(reference, Node::HardcodedMathML("Hello, world!")));
144    }
145
146    #[test]
147    fn test_buffer_extend() {
148        let arena = Arena::new();
149        let mut buffer = Buffer::new(0);
150        let mut builder = buffer.get_builder();
151        builder.push_char('H');
152        builder.push_char('i');
153        let str_ref = builder.finish(&arena);
154        assert_eq!(str_ref, "Hi");
155    }
156
157    #[test]
158    fn test_buffer_manual_reference() {
159        let arena = Arena::new();
160        let mut buffer = Buffer::new(0);
161        let mut builder = buffer.get_builder();
162        assert_eq!(builder.buffer.0.len(), 0);
163        builder.push_char('H');
164        builder.push_char('i');
165        builder.push_char('↩'); // This is a multi-byte character.
166        assert_eq!(builder.buffer.0.len(), 5);
167        let str_ref = builder.finish(&arena);
168        assert_eq!(str_ref.len(), 5);
169        assert_eq!(str_ref, "Hi↩");
170    }
171
172    struct CycleParticipant<'a> {
173        val: i32,
174        next: Option<&'a mut CycleParticipant<'a>>,
175    }
176
177    #[test]
178    fn test_arena_with_cycle() {
179        let arena = DroplessArena::default();
180
181        let a = arena.alloc(CycleParticipant { val: 1, next: None });
182        let b = arena.alloc(CycleParticipant { val: 2, next: None });
183        a.next = Some(b);
184        let c = arena.alloc(CycleParticipant { val: 3, next: None });
185        a.next.as_mut().unwrap().next = Some(c);
186
187        // for (i, node) in arena.iter_mut().enumerate() {
188        //     match i {
189        //         0 => assert_eq!(node.val, 1),
190        //         1 => assert_eq!(node.val, 2),
191        //         2 => assert_eq!(node.val, 3),
192        //         _ => panic!("Too many nodes"),
193        //     }
194        // }
195
196        assert_eq!(a.val, 1);
197        assert_eq!(a.next.as_ref().unwrap().val, 2);
198        assert_eq!(a.next.as_ref().unwrap().next.as_ref().unwrap().val, 3);
199    }
200}