Skip to main content

weakauras_codec_ace_serialize/serialization/
mod.rs

1// Copyright 2020-2025 Velithris
2// SPDX-License-Identifier: MIT
3
4use crate::{error::SerializationError, macros::check_recursion};
5use weakauras_codec_lua_value::LuaValue;
6
7fn f64_to_parts(v: f64) -> (u64, i16, i8) {
8    let bits = v.to_bits();
9    let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 };
10    let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
11    let mantissa = if exponent == 0 {
12        (bits & 0xfffffffffffff) << 1
13    } else {
14        (bits & 0xfffffffffffff) | 0x10000000000000
15    };
16    exponent -= 1023 + 52;
17    (mantissa, exponent, sign)
18}
19
20fn write_integer_to_a_string<I>(string: &mut String, value: I)
21where
22    I: itoa::Integer,
23{
24    let mut buffer = itoa::Buffer::new();
25    let serialized = buffer.format(value);
26    string.push_str(serialized)
27}
28
29/// A structure for serializing [LuaValues](LuaValue).
30///
31/// # Example
32///
33/// ```
34/// use weakauras_codec_ace_serialize::{error::SerializationError, serialization::Serializer};
35///
36/// fn main() -> Result<(), SerializationError> {
37///     assert_eq!(
38///         Serializer::serialize_one(&"Hello, world!".into(), None)?,
39///         "^1^SHello,~`world!^^"
40///     );
41///     Ok(())
42/// }
43/// ```
44pub struct Serializer {
45    remaining_depth: usize,
46    result: String,
47}
48
49impl Serializer {
50    /// Serialize a single value.
51    pub fn serialize_one(
52        value: &LuaValue,
53        approximate_len: Option<usize>,
54    ) -> Result<String, SerializationError> {
55        let mut serializer = Self {
56            remaining_depth: 128,
57            result: String::with_capacity(approximate_len.unwrap_or(1024)),
58        };
59
60        serializer.result.push_str("^1");
61        serializer.serialize_helper(value)?;
62        serializer.result.push_str("^^");
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_str("^Z"),
70            LuaValue::Boolean(b) => self.result.push_str(if b { "^B" } else { "^b" }),
71            LuaValue::String(ref s) => {
72                self.result.push_str("^S");
73                self.serialize_string(s)
74            }
75            LuaValue::Number(n) => self.serialize_number(n)?,
76            LuaValue::Array(ref v) => {
77                self.result.reserve(v.len() * 6 + 4);
78
79                self.result.push_str("^T");
80                for (value, index) in v.iter().zip(1..) {
81                    self.serialize_number(index as f64)?;
82                    self.serialize_helper(value)?;
83                }
84                self.result.push_str("^t");
85            }
86            LuaValue::Map(ref m) => {
87                self.result.reserve(m.len() * 6 + 4);
88
89                self.result.push_str("^T");
90                for (key, value) in m.iter() {
91                    check_recursion!(self, SerializationError, {
92                        self.serialize_helper(key.as_value())?;
93                        self.serialize_helper(value)?;
94                    });
95                }
96                self.result.push_str("^t");
97            }
98        }
99
100        Ok(())
101    }
102
103    fn serialize_number(&mut self, value: f64) -> Result<(), SerializationError> {
104        if value.is_nan() {
105            return Err(SerializationError::NanEncountered);
106        } else if !value.is_finite() {
107            self.result.push_str("^N");
108            self.result
109                .push_str(if value > 0.0 { "1.#INF" } else { "-1.#INF" })
110        } else {
111            let mut buffer = zmij::Buffer::new();
112            let str_value = buffer.format_finite(value);
113
114            if str_value.parse::<f64>().unwrap() == value {
115                self.result.reserve(str_value.len() + 2);
116                self.result.push_str("^N");
117                self.result.push_str(str_value);
118            } else {
119                let (mantissa, exponent, sign) = f64_to_parts(value);
120                self.result.push_str("^F");
121                if sign < 0 {
122                    self.result.push('-');
123                }
124                write_integer_to_a_string(&mut self.result, mantissa);
125                self.result.push_str("^f");
126                write_integer_to_a_string(&mut self.result, exponent);
127            }
128        }
129
130        Ok(())
131    }
132
133    fn serialize_string(&mut self, value: &str) {
134        self.result.reserve(value.len());
135
136        let mut copy_from = 0;
137        for (i, byte) in value.bytes().enumerate() {
138            let replacement = match byte {
139                v @ 0x00..=0x1D | v @ 0x1F..=0x20 => v + 64,
140                0x1E => 0x7A,
141                0x5E => 0x7D,
142                0x7E => 0x7C,
143                0x7F => 0x7B,
144                _ => continue,
145            };
146
147            self.result.push_str(&value[copy_from..i]);
148            self.result.push('~');
149            self.result.push(replacement as char);
150            copy_from = i + 1;
151        }
152
153        self.result.push_str(&value[copy_from..]);
154    }
155}