font_test_data/
bebuffer.rs

1//! small utilities used in tests
2
3use font_types::Scalar;
4use std::collections::HashMap;
5
6/// A convenience type for generating a buffer of big-endian bytes.
7#[derive(Debug, Clone, Default)]
8pub struct BeBuffer {
9    data: Vec<u8>,
10    tagged_locations: HashMap<String, usize>,
11}
12
13impl BeBuffer {
14    pub fn new() -> Self {
15        Default::default()
16    }
17
18    /// The current length of the buffer in bytes.
19    pub fn len(&self) -> usize {
20        self.data.len()
21    }
22
23    /// Returns `true` if the buffer contains zero bytes.
24    pub fn is_empty(&self) -> bool {
25        self.data.is_empty()
26    }
27
28    /// Return a reference to the contents of the buffer
29    pub fn as_slice(&self) -> &[u8] {
30        &self.data
31    }
32
33    /// Write any scalar to this buffer.
34    pub fn push(mut self, item: impl Scalar) -> Self {
35        self.data.extend(item.to_raw().as_ref());
36        self
37    }
38
39    pub fn push_with_tag(mut self, item: impl Scalar, tag: &str) -> Self {
40        self.tagged_locations
41            .insert(tag.to_string(), self.data.len());
42        self.data.extend(item.to_raw().as_ref());
43        self
44    }
45
46    /// Write multiple scalars into the buffer
47    pub fn extend<T: Scalar>(mut self, iter: impl IntoIterator<Item = T>) -> Self {
48        for item in iter {
49            self.data.extend(item.to_raw().as_ref());
50        }
51        self
52    }
53
54    pub fn offset_for(&self, tag: &str) -> usize {
55        // panic on unrecognized tags
56        self.tagged_locations.get(tag).copied().unwrap()
57    }
58
59    fn data_for(&mut self, tag: &str) -> &mut [u8] {
60        let offset = self.offset_for(tag);
61        &mut self.data[offset..]
62    }
63
64    pub fn write_at(&mut self, tag: &str, item: impl Scalar) {
65        let data = self.data_for(tag);
66        let raw = item.to_raw();
67        let new_data: &[u8] = raw.as_ref();
68
69        if data.len() < new_data.len() {
70            panic!("not enough room left in buffer for the requested write.");
71        }
72
73        for (left, right) in data.iter_mut().zip(new_data) {
74            *left = *right
75        }
76    }
77
78    pub fn data(&self) -> &[u8] {
79        &self.data
80    }
81}
82
83impl std::ops::Deref for BeBuffer {
84    type Target = [u8];
85    fn deref(&self) -> &Self::Target {
86        &self.data
87    }
88}
89
90/// be_buffer_add!(buffer, value) - Add an item to a be_buffer.
91///
92/// Will call the appropriate insertion method on BeBuffer depending on the values format.
93///
94/// Values can be one of three types:
95/// 1. Single numeric value, ie: 10u16
96/// 2. Array of numeric values, ie: [1u8, 2, 3]
97/// 3. Tagged value: {10u16, "tag_name"}. This will insert the numeric value
98///    and attach the provide string tag to the insertion location in the buffer.
99#[macro_export]
100macro_rules! be_buffer_add {
101    ($b:ident, $v:literal) => {
102        let $b = $b.push($v);
103    };
104    ($b:ident, {$v:tt : $tag:literal}) => {
105        let $b = $b.push_with_tag($v, $tag);
106    };
107    ($b:ident, [$($v:literal),+]) => {
108        let $b = $b.extend([$($v),*]);
109    };
110    ($b:ident, $v:tt) => {
111        let $b = $b.push($v);
112    };
113}
114
115/// be_buffer!(val1, ..., valn) - Constructs a BeBuffer from the provided list of values:
116///
117/// Values can be one of three types:
118/// 1. Single numeric value, ie: 10u16
119/// 2. Array of numeric values, ie: [1u8, 2, 3]
120/// 3. Tagged value: {10u16, "tag_name"}. This will insert the numeric value
121///    and attach the provide string tag to the insertion location in the buffer.
122#[macro_export]
123macro_rules! be_buffer {
124    ( $( $x:tt ),+ ) => {
125        {
126            let builder = BeBuffer::new();
127            $(
128                $crate::be_buffer_add!(builder, $x);
129            )*
130            builder
131        }
132    };
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn be_buffer_macro() {
141        let data: &[u8] = &be_buffer! {
142            1u8,
143            2u16,
144            3u32
145        };
146
147        assert_eq!([1, 0, 2, 0, 0, 0, 3], data);
148    }
149
150    #[test]
151    fn be_buffer_macro_array() {
152        let data: &[u8] = &be_buffer! {
153            1u8,
154            [2u8, 3, 4, 5, 6]
155        };
156
157        assert_eq!([1, 2, 3, 4, 5, 6], data);
158    }
159
160    #[test]
161    fn be_buffer_macro_tagged() {
162        let builder = be_buffer! {
163            1u8,
164            {2u16: "foo"},
165            {(u32::from(3u16)): "bar"}
166        };
167        let data: &[u8] = &builder;
168
169        assert_eq!([1, 0, 2, 0, 0, 0, 3], data);
170        assert_eq!(builder.offset_for("foo"), 1);
171        assert_eq!(builder.offset_for("bar"), 3);
172    }
173}