weakauras_codec_lib_serialize/serialization/
mod.rs

1// Based on code from LibSerialize
2// https://github.com/rossnichols/LibSerialize
3// Copyright 2020-2021 Ross Nichols
4// Copyright 2020-2025 Velithris
5// SPDX-License-Identifier: MIT
6
7use crate::{
8    EmbeddedTypeTag, FORMAT_VERSION, TypeTag, error::SerializationError, macros::check_recursion,
9};
10use weakauras_codec_lua_value::{LuaMapKey, LuaValue, Map};
11
12const TYPE_TAG_SHIFT: u8 = 3;
13const EMBEDDED_TYPE_TAG_SHIFT: u8 = 2;
14const EMBEDDED_LEN_SHIFT: u8 = 4;
15
16fn required_bytes(v: u64) -> u8 {
17    match v {
18        0..=255 => 1,
19        256..=65_535 => 2,
20        65_536..=16_777_215 => 3,
21        16_777_216..=4_294_967_295 => 4,
22        _ => 7,
23    }
24}
25
26/// A structure for serializing [LuaValues](LuaValue).
27///
28/// # Example
29///
30/// ```
31/// use weakauras_codec_lib_serialize::{error::SerializationError, serialization::Serializer};
32///
33/// fn main() -> Result<(), SerializationError> {
34///     assert_eq!(
35///         Serializer::serialize_one(&"Hello, world!".into(), None)?,
36///         b"\x01\xd2Hello, world!"
37///     );
38///     Ok(())
39/// }
40/// ```
41pub struct Serializer {
42    remaining_depth: usize,
43    result: Vec<u8>,
44
45    string_refs: Map<String, usize>,
46}
47
48impl Serializer {
49    /// Serialize a single value.
50    pub fn serialize_one(
51        value: &LuaValue,
52        approximate_len: Option<usize>,
53    ) -> Result<Vec<u8>, SerializationError> {
54        let mut serializer = Self {
55            remaining_depth: 128,
56            result: Vec::with_capacity(approximate_len.unwrap_or(1024)),
57
58            string_refs: Map::new(),
59        };
60
61        serializer.result.push(FORMAT_VERSION);
62        serializer.serialize_helper(value)?;
63
64        Ok(serializer.result)
65    }
66
67    fn serialize_helper(&mut self, value: &LuaValue) -> Result<(), SerializationError> {
68        match *value {
69            LuaValue::Null => self.result.push(TypeTag::Null.to_u8() << TYPE_TAG_SHIFT),
70            LuaValue::Boolean(b) => {
71                if b {
72                    self.result.push(TypeTag::True.to_u8() << TYPE_TAG_SHIFT);
73                } else {
74                    self.result.push(TypeTag::False.to_u8() << TYPE_TAG_SHIFT);
75                }
76            }
77            LuaValue::String(ref s) => self.serialize_string(s)?,
78            LuaValue::Number(n) => self.serialize_number(n),
79            LuaValue::Array(ref v) => self.serialize_slice(v)?,
80            LuaValue::Map(ref m) => self.serialize_map(m)?,
81        }
82
83        Ok(())
84    }
85
86    #[allow(clippy::manual_range_contains)]
87    fn serialize_number(&mut self, value: f64) {
88        const MAX_7_BIT: f64 = (2i64.pow(56) - 1) as f64;
89
90        if value.is_nan() {
91            // Serialize any NaN as a positive qNaN:
92            self.result.push(TypeTag::Float.to_u8() << TYPE_TAG_SHIFT);
93            self.result
94                .extend_from_slice(&[0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
95        } else if value.fract() != 0.0 || (value < -MAX_7_BIT || value > MAX_7_BIT) {
96            self.result.push(TypeTag::Float.to_u8() << TYPE_TAG_SHIFT);
97            self.result.extend_from_slice(&value.to_be_bytes());
98        } else {
99            // SAFETY:
100            // 1) NaNs are handled explicitly;
101            // 2) `f64::fract()` returns NaN for infinite values, NaN != 0.0;
102            // 3) `value` is within i64::MIN..=i64::MAX range.
103            let value = unsafe { value.to_int_unchecked::<i64>() };
104
105            if value > -4096 && value < 4096 {
106                if value >= 0 && value < 128 {
107                    self.result.push(((value as u8) << 1) | 1);
108                } else {
109                    let (value, neg_bit) = if value < 0 {
110                        (-value, 1 << TYPE_TAG_SHIFT)
111                    } else {
112                        (value, 0)
113                    };
114
115                    let value = (value << 4) | neg_bit | 4;
116                    self.result.push(value as u8);
117                    self.result.push((value >> 8) as u8);
118                }
119            } else {
120                let (value, neg_bit) = if value < 0 {
121                    ((-value) as u64, 1)
122                } else {
123                    (value as u64, 0)
124                };
125
126                match required_bytes(value) {
127                    2 => {
128                        self.result
129                            .push((TypeTag::Int16Pos.to_u8() + neg_bit) << TYPE_TAG_SHIFT);
130                        self.serialize_int(value, 2);
131                    }
132                    3 => {
133                        self.result
134                            .push((TypeTag::Int24Pos.to_u8() + neg_bit) << TYPE_TAG_SHIFT);
135                        self.serialize_int(value, 3);
136                    }
137                    4 => {
138                        self.result
139                            .push((TypeTag::Int32Pos.to_u8() + neg_bit) << TYPE_TAG_SHIFT);
140                        self.serialize_int(value, 4);
141                    }
142                    _ => {
143                        self.result
144                            .push((TypeTag::Int64Pos.to_u8() + neg_bit) << TYPE_TAG_SHIFT);
145                        self.serialize_int(value, 7);
146                    }
147                }
148            }
149        }
150    }
151
152    fn serialize_int(&mut self, value: u64, len: usize) {
153        let bytes = value.to_be_bytes();
154        self.result.extend_from_slice(&bytes[bytes.len() - len..]);
155    }
156
157    fn serialize_string(&mut self, value: &str) -> Result<(), SerializationError> {
158        match self.string_refs.get(value) {
159            Some(index) => {
160                let index = *index as u64;
161
162                match required_bytes(index) {
163                    1 => {
164                        self.result.push(TypeTag::StrRef8.to_u8() << TYPE_TAG_SHIFT);
165                        self.serialize_int(index, 1);
166                    }
167                    2 => {
168                        self.result
169                            .push(TypeTag::StrRef16.to_u8() << TYPE_TAG_SHIFT);
170                        self.serialize_int(index, 2);
171                    }
172                    3 => {
173                        self.result
174                            .push(TypeTag::StrRef24.to_u8() << TYPE_TAG_SHIFT);
175                        self.serialize_int(index, 3);
176                    }
177                    _ => return Err(SerializationError::TooManyUniqueStrings),
178                }
179            }
180            None => {
181                let len = value.len();
182
183                if len < 16 {
184                    self.result.push(
185                        (EmbeddedTypeTag::Str.to_u8() << EMBEDDED_TYPE_TAG_SHIFT)
186                            | ((len as u8) << EMBEDDED_LEN_SHIFT)
187                            | 2,
188                    );
189                } else {
190                    let len = value.len() as u64;
191
192                    match required_bytes(len) {
193                        1 => {
194                            self.result.push(TypeTag::Str8.to_u8() << TYPE_TAG_SHIFT);
195                            self.serialize_int(len, 1);
196                        }
197                        2 => {
198                            self.result.push(TypeTag::Str16.to_u8() << TYPE_TAG_SHIFT);
199                            self.serialize_int(len, 2);
200                        }
201                        3 => {
202                            self.result.push(TypeTag::Str24.to_u8() << TYPE_TAG_SHIFT);
203                            self.serialize_int(len, 3);
204                        }
205                        _ => return Err(SerializationError::StringIsTooLarge),
206                    }
207                }
208
209                if len > 2 {
210                    self.string_refs
211                        .insert(value.into(), self.string_refs.len() + 1);
212                }
213
214                self.result.extend_from_slice(value.as_bytes());
215            }
216        }
217
218        Ok(())
219    }
220
221    fn serialize_map(&mut self, map: &Map<LuaMapKey, LuaValue>) -> Result<(), SerializationError> {
222        let len = map.len();
223        if len < 16 {
224            self.result.push(
225                (EmbeddedTypeTag::Map.to_u8() << EMBEDDED_TYPE_TAG_SHIFT)
226                    | ((len as u8) << EMBEDDED_LEN_SHIFT)
227                    | 2,
228            );
229        } else {
230            let len = len as u64;
231            match required_bytes(len) {
232                1 => {
233                    self.result.push(TypeTag::Map8.to_u8() << TYPE_TAG_SHIFT);
234                    self.serialize_int(len, 1);
235                }
236                2 => {
237                    self.result.push(TypeTag::Map16.to_u8() << TYPE_TAG_SHIFT);
238                    self.serialize_int(len, 2);
239                }
240                3 => {
241                    self.result.push(TypeTag::Map24.to_u8() << TYPE_TAG_SHIFT);
242                    self.serialize_int(len, 3);
243                }
244                _ => return Err(SerializationError::MapIsTooLarge),
245            }
246        }
247
248        for (key, value) in map {
249            check_recursion!(self, SerializationError, {
250                self.serialize_helper(key.as_value())?;
251                self.serialize_helper(value)?;
252            });
253        }
254
255        Ok(())
256    }
257
258    fn serialize_slice(&mut self, slice: &[LuaValue]) -> Result<(), SerializationError> {
259        let len = slice.len();
260        if len < 16 {
261            self.result.push(
262                (EmbeddedTypeTag::Array.to_u8() << EMBEDDED_TYPE_TAG_SHIFT)
263                    | ((len as u8) << EMBEDDED_LEN_SHIFT)
264                    | 2,
265            );
266        } else {
267            let len = len as u64;
268            match required_bytes(len) {
269                1 => {
270                    self.result.push(TypeTag::Array8.to_u8() << TYPE_TAG_SHIFT);
271                    self.serialize_int(len, 1);
272                }
273                2 => {
274                    self.result.push(TypeTag::Array16.to_u8() << TYPE_TAG_SHIFT);
275                    self.serialize_int(len, 2);
276                }
277                3 => {
278                    self.result.push(TypeTag::Array24.to_u8() << TYPE_TAG_SHIFT);
279                    self.serialize_int(len, 3);
280                }
281                _ => return Err(SerializationError::ArrayIsTooLarge),
282            }
283        }
284
285        for el in slice {
286            check_recursion!(self, SerializationError, {
287                self.serialize_helper(el)?;
288            });
289        }
290
291        Ok(())
292    }
293}