1use compact_str::{CompactString, format_compact};
2
3use crate::{CellSize, FontAtlasData, FontStyle, Glyph, LineDecoration};
4
5const ATLAS_HEADER: [u8; 4] = [0xBA, 0xB1, 0xF0, 0xA7];
6const ATLAS_VERSION: u8 = 0x03; #[derive(Debug, thiserror::Error)]
10#[error("{message}")]
11pub struct SerializationError {
12 pub message: CompactString,
14}
15
16impl SerializationError {
17 pub fn new(message: impl Into<CompactString>) -> Self {
19 Self { message: message.into() }
20 }
21}
22
23pub(crate) trait Serializable {
24 fn serialize(&self) -> Result<Vec<u8>, SerializationError>;
25
26 fn deserialize(deser: &mut Deserializer) -> Result<Self, SerializationError>
27 where
28 Self: Sized;
29}
30
31pub(crate) struct Deserializer<'a> {
32 data: &'a [u8],
33 position: usize,
34}
35
36struct Serializer {
37 data: Vec<u8>,
38}
39
40impl Serializer {
41 pub fn new() -> Self {
42 Self { data: Vec::new() }
43 }
44
45 pub fn write_u8(&mut self, value: u8) {
46 self.data.push(value);
47 }
48
49 pub fn write_u16(&mut self, value: u16) {
50 self.data.extend(&value.to_le_bytes());
51 }
52
53 pub fn write_u32(&mut self, value: u32) {
54 self.data.extend(&value.to_le_bytes());
55 }
56
57 pub fn write_f32(&mut self, value: f32) {
58 self.data.extend(&value.to_le_bytes());
59 }
60
61 pub fn write_i32(&mut self, value: i32) {
62 self.data.extend(&value.to_le_bytes());
63 }
64
65 pub fn write_u8_slice(&mut self, value: &[u8]) {
66 self.write_u32(value.len() as u32);
67 self.data.extend_from_slice(value);
68 }
69
70 pub fn write_string(&mut self, value: &str) -> Result<(), SerializationError> {
71 if value.len() > u8::MAX as usize {
72 return Err(SerializationError::new(format!(
73 "String too long: {} bytes (max 255)",
74 value.len()
75 )));
76 }
77
78 let length = value.len() as u8;
79 self.write_u8(length);
80 self.data.extend(value.as_bytes());
81
82 Ok(())
83 }
84}
85
86impl<'a> Deserializer<'a> {
87 pub fn new(data: &'a [u8]) -> Self {
88 Self { data, position: 0 }
89 }
90
91 pub fn read_u8(&mut self) -> Result<u8, SerializationError> {
92 self.verify_offset_in_bounds(1)?;
93
94 let byte = self.data[self.position];
95 self.position += 1;
96
97 Ok(byte)
98 }
99
100 pub fn read_u16(&mut self) -> Result<u16, SerializationError> {
101 self.verify_offset_in_bounds(2)?;
102
103 let bytes = &self.data[self.position..self.position + 2];
104 self.position += 2;
105
106 Ok(u16::from_le_bytes(bytes.try_into().unwrap()))
107 }
108
109 pub fn read_f32(&mut self) -> Result<f32, SerializationError> {
110 self.verify_offset_in_bounds(4)?;
111
112 let bytes = &self.data[self.position..self.position + 4];
113 self.position += 4;
114
115 Ok(f32::from_le_bytes(bytes.try_into().unwrap()))
116 }
117
118 pub fn read_u32(&mut self) -> Result<u32, SerializationError> {
119 self.verify_offset_in_bounds(4)?;
120
121 let bytes = &self.data[self.position..self.position + 4];
122 self.position += 4;
123
124 Ok(u32::from_le_bytes(bytes.try_into().unwrap()))
125 }
126
127 pub fn read_u8_slice(&mut self) -> Result<Vec<u8>, SerializationError> {
128 let length = self.read_u32()? as usize;
129 self.verify_offset_in_bounds(length)?;
130
131 let slice = &self.data[self.position..self.position + length];
132 self.position += length;
133
134 Ok(slice.to_vec())
135 }
136
137 pub fn read_i32(&mut self) -> Result<i32, SerializationError> {
138 self.verify_offset_in_bounds(4)?;
139
140 let bytes = &self.data[self.position..self.position + 4];
141 self.position += 4;
142
143 Ok(i32::from_le_bytes(bytes.try_into().unwrap()))
144 }
145
146 pub fn read_string(&mut self) -> Result<CompactString, SerializationError> {
147 let length = self.read_u8()? as usize;
148 self.verify_offset_in_bounds(length)?;
149
150 let bytes = &self.data[self.position..self.position + length];
151 self.position += length;
152
153 Ok(CompactString::from_utf8_lossy(bytes))
154 }
155
156 fn verify_offset_in_bounds(&self, length: usize) -> Result<(), SerializationError> {
157 if (self.position + length) > self.data.len() {
158 return Err(SerializationError { message: CompactString::from("Out of bounds read") });
159 }
160 Ok(())
161 }
162}
163
164impl Serializable for CompactString {
165 fn serialize(&self) -> Result<Vec<u8>, SerializationError> {
166 let mut ser = Serializer::new();
167 ser.write_string(self)?;
168 Ok(ser.data)
169 }
170
171 fn deserialize(serialized: &mut Deserializer) -> Result<Self, SerializationError> {
172 serialized.read_string()
173 }
174}
175
176impl Serializable for Glyph {
177 fn serialize(&self) -> Result<Vec<u8>, SerializationError> {
178 let mut ser = Serializer::new();
179 ser.write_u16(self.id);
180 ser.write_u8(self.style.ordinal() as u8);
181 ser.write_u8(self.is_emoji as u8);
182 ser.write_i32(self.pixel_coords.0);
183 ser.write_i32(self.pixel_coords.1);
184 ser.write_string(&self.symbol)?;
185 Ok(ser.data)
186 }
187
188 fn deserialize(serialized: &mut Deserializer) -> Result<Self, SerializationError> {
189 let id = serialized.read_u16()?;
190 let style = serialized.read_u8()?;
191 let is_emoji = serialized.read_u8()? != 0;
192 let x = serialized.read_i32()?;
193 let y = serialized.read_i32()?;
194 let symbol = serialized.read_string()?;
195
196 Ok(Glyph {
197 id,
198 style: FontStyle::from_ordinal(style)?,
199 is_emoji,
200 pixel_coords: (x, y),
201 symbol,
202 })
203 }
204}
205
206impl Serializable for FontAtlasData {
207 fn serialize(&self) -> Result<Vec<u8>, SerializationError> {
208 let mut ser = Serializer::new();
209 ser.write_u8(ATLAS_HEADER[0]);
210 ser.write_u8(ATLAS_HEADER[1]);
211 ser.write_u8(ATLAS_HEADER[2]);
212 ser.write_u8(ATLAS_HEADER[3]);
213
214 ser.write_u8(ATLAS_VERSION);
215
216 ser.write_string(&self.font_name)?;
217 ser.write_f32(self.font_size);
218 ser.write_u16(self.max_halfwidth_base_glyph_id);
219
220 ser.write_i32(self.texture_dimensions.0);
221 ser.write_i32(self.texture_dimensions.1);
222 ser.write_i32(self.texture_dimensions.2);
223
224 ser.write_i32(self.cell_size.width);
225 ser.write_i32(self.cell_size.height);
226
227 ser.write_f32(self.underline.position);
228 ser.write_f32(self.underline.thickness);
229 ser.write_f32(self.strikethrough.position);
230 ser.write_f32(self.strikethrough.thickness);
231
232 ser.write_u16(self.glyphs.len() as u16);
234 for glyph in &self.glyphs {
235 ser.data.extend(glyph.serialize()?);
236 }
237
238 let packed_texture_data = miniz_oxide::deflate::compress_to_vec(&self.texture_data, 9);
240 ser.write_u8_slice(&packed_texture_data);
241
242 Ok(ser.data)
243 }
244
245 fn deserialize(deser: &mut Deserializer) -> Result<Self, SerializationError> {
246 let header = [deser.read_u8()?, deser.read_u8()?, deser.read_u8()?, deser.read_u8()?];
247 if header != ATLAS_HEADER {
248 return Err(SerializationError {
249 message: CompactString::const_new("Invalid font atlas header (wrong file format?)"),
250 });
251 }
252
253 let version = deser.read_u8()?;
254 if version != ATLAS_VERSION {
255 return Err(SerializationError {
256 message: format_compact!(
257 "Atlas version mismatch: expected v{}, found v{}. \
258 Please regenerate atlas with current beamterm-atlas version.",
259 ATLAS_VERSION,
260 version
261 ),
262 });
263 }
264
265 let font_name = deser.read_string()?;
266 let font_size = deser.read_f32()?;
267 let halfwidth_glyphs_per_layer = deser.read_u16()?;
268
269 let texture_dimensions = (deser.read_i32()?, deser.read_i32()?, deser.read_i32()?);
270 let cell_size = CellSize::new(deser.read_i32()?, deser.read_i32()?);
271
272 let underline = LineDecoration::new(deser.read_f32()?, deser.read_f32()?);
273 let strikethrough = LineDecoration::new(deser.read_f32()?, deser.read_f32()?);
274
275 let glyph_count = deser.read_u16()? as usize;
277 let mut glyphs = Vec::with_capacity(glyph_count);
278 for _ in 0..glyph_count {
279 glyphs.push(Glyph::deserialize(deser)?);
280 }
281
282 let packed_texture_data = deser.read_u8_slice()?;
284 let texture_data =
285 miniz_oxide::inflate::decompress_to_vec(&packed_texture_data).map_err(|_| {
286 SerializationError {
287 message: CompactString::const_new("Failed to decompress texture data"),
288 }
289 })?;
290
291 Ok(FontAtlasData {
292 font_name,
293 font_size,
294 max_halfwidth_base_glyph_id: halfwidth_glyphs_per_layer,
295 texture_dimensions,
296 cell_size,
297 underline,
298 strikethrough,
299 glyphs,
300 texture_data,
301 })
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn test_serialized_read_u8() {
311 let data = [42, 100, 255];
312 let mut serialized = Deserializer::new(&data);
313
314 assert_eq!(serialized.read_u8().unwrap(), 42);
315 assert_eq!(serialized.read_u8().unwrap(), 100);
316 assert_eq!(serialized.read_u8().unwrap(), 255);
317 }
318
319 #[test]
320 fn test_serialized_read_u8_bounds_error() {
321 let data = [42];
322 let mut serialized = Deserializer::new(&data);
323
324 assert_eq!(serialized.read_u8().unwrap(), 42);
326
327 let result = serialized.read_u8();
329 assert!(result.is_err());
330 assert_eq!(result.unwrap_err().message, "Out of bounds read");
331 }
332
333 #[test]
334 fn test_serialized_read_u16() {
335 let data = [0x34, 0x12, 0xFF, 0x00];
337 let mut serialized = Deserializer::new(&data);
338
339 assert_eq!(serialized.read_u16().unwrap(), 0x1234);
340 assert_eq!(serialized.read_u16().unwrap(), 0x00FF);
341 }
342
343 #[test]
344 fn test_serialized_read_u16_bounds_error() {
345 let data = [0x34]; let mut serialized = Deserializer::new(&data);
347
348 let result = serialized.read_u16();
349 assert!(result.is_err());
350 assert_eq!(result.unwrap_err().message, "Out of bounds read");
351 }
352
353 #[test]
354 fn test_serialized_read_u32() {
355 let data = [0x78, 0x56, 0x34, 0x12, 0xFF, 0xFF, 0xFF, 0xFF];
357 let mut serialized = Deserializer::new(&data);
358
359 assert_eq!(serialized.read_u32().unwrap(), 0x12345678);
360 assert_eq!(serialized.read_u32().unwrap(), 0xFFFFFFFF);
361 }
362
363 #[test]
364 fn test_serialized_read_u32_bounds_error() {
365 let data = [0x78, 0x56, 0x34]; let mut serialized = Deserializer::new(&data);
367
368 let result = serialized.read_u32();
369 assert!(result.is_err());
370 assert_eq!(result.unwrap_err().message, "Out of bounds read");
371 }
372
373 #[test]
374 fn test_compact_string_serialize_empty() {
375 let s = CompactString::new("");
376 let serialized = s.serialize().unwrap();
377
378 assert_eq!(serialized, vec![0]); }
380
381 #[test]
382 fn test_compact_string_serialize_short() {
383 let s = CompactString::from("Hello");
384 let serialized = s.serialize().unwrap();
385
386 let mut expected = vec![5]; expected.extend(b"Hello");
388 assert_eq!(serialized, expected);
389 }
390
391 #[test]
392 fn test_compact_string_serialize_max_length() {
393 let s = CompactString::from("A".repeat(255));
394 let serialized = s.serialize().unwrap();
395
396 assert_eq!(serialized[0], 255); assert_eq!(serialized.len(), 256); assert_eq!(&serialized[1..], "A".repeat(255).as_bytes());
399 }
400
401 #[test]
402 fn test_compact_string_deserialize_empty() {
403 let data = [0]; let mut serialized = Deserializer::new(&data);
405
406 let result = CompactString::deserialize(&mut serialized).unwrap();
407 assert_eq!(result, "");
408 }
409
410 #[test]
411 fn test_compact_string_deserialize_short() {
412 let mut data = vec![5]; data.extend(b"Hello");
414 let mut serialized = Deserializer::new(&data);
415
416 let result = CompactString::deserialize(&mut serialized).unwrap();
417 assert_eq!(result, "Hello");
418 }
419
420 #[test]
421 fn test_compact_string_round_trip() {
422 let aaaaaa = "A".repeat(255);
423 let test_cases = ["", "Hello", "World!", "🚀 Unicode works! 🎉", aaaaaa.as_str()];
424
425 for original in &test_cases {
426 let compact_str = CompactString::from(*original);
427 let serialized = compact_str.serialize().unwrap();
428 let mut serialized_reader = Deserializer::new(&serialized);
429 let deserialized = CompactString::deserialize(&mut serialized_reader).unwrap();
430
431 assert_eq!(compact_str, deserialized);
432 assert_eq!(*original, deserialized.as_str());
433 }
434 }
435
436 #[test]
437 fn test_serialized_position_tracking() {
438 let data = [1, 2, 3, 4, 5, 6, 7, 8];
439 let mut serialized = Deserializer::new(&data);
440
441 assert_eq!(serialized.read_u8().unwrap(), 1);
443
444 assert_eq!(serialized.read_u16().unwrap(), 0x0302); assert_eq!(serialized.read_u32().unwrap(), 0x07060504); assert_eq!(serialized.read_u8().unwrap(), 8);
452
453 assert!(serialized.read_u8().is_err());
455 }
456
457 #[test]
458 fn test_mixed_serialization() {
459 let mut data = Vec::new();
461 data.push(42u8); data.extend(&100u16.to_le_bytes()); data.extend(&0x12345678u32.to_le_bytes()); data.push(5); data.extend(b"Hello"); let mut serialized = Deserializer::new(&data);
468
469 assert_eq!(serialized.read_u8().unwrap(), 42);
470 assert_eq!(serialized.read_u16().unwrap(), 100);
471 assert_eq!(serialized.read_u32().unwrap(), 0x12345678);
472 assert_eq!(serialized.read_string().unwrap(), "Hello");
473 }
474
475 #[test]
476 fn test_font_atlas_config_round_trip() {
477 let glyphs = vec![
479 Glyph {
480 id: 65, style: FontStyle::Normal,
482 symbol: CompactString::from("A"),
483 pixel_coords: (0, 0),
484 is_emoji: false,
485 },
486 Glyph {
487 id: 66, style: FontStyle::Normal,
489 symbol: CompactString::from("B"),
490 pixel_coords: (16, 0),
491 is_emoji: false,
492 },
493 Glyph {
494 id: 8364, style: FontStyle::Normal,
496 symbol: CompactString::from("€"),
497 pixel_coords: (32, 0),
498 is_emoji: false,
499 },
500 Glyph {
501 id: 10000, style: FontStyle::Normal,
503 symbol: CompactString::from("🚀"),
504 pixel_coords: (48, 0),
505 is_emoji: true,
506 },
507 ];
508
509 let original = FontAtlasData {
511 font_name: CompactString::from("TestFont"),
512 font_size: 16.5,
513 max_halfwidth_base_glyph_id: 328,
514 texture_dimensions: (512, 256, 256),
515 cell_size: CellSize::new(12, 18),
516 underline: LineDecoration::new(0.85, 5.0 / 100.0),
517 strikethrough: LineDecoration::new(0.5, 5.0 / 100.0),
518 glyphs,
519 texture_data: Vec::new(),
520 };
521
522 let serialized = original.serialize().unwrap();
524
525 let mut deserializer = Deserializer::new(&serialized);
527 let deserialized = FontAtlasData::deserialize(&mut deserializer).unwrap();
528
529 assert_eq!(original.font_size, deserialized.font_size);
531 assert_eq!(
532 original.max_halfwidth_base_glyph_id,
533 deserialized.max_halfwidth_base_glyph_id
534 );
535 assert_eq!(original.texture_dimensions, deserialized.texture_dimensions);
536 assert_eq!(original.cell_size, deserialized.cell_size);
537 assert_eq!(original.underline, deserialized.underline);
538 assert_eq!(original.strikethrough, deserialized.strikethrough);
539 assert_eq!(original.glyphs.len(), deserialized.glyphs.len());
540
541 for (orig_glyph, deser_glyph) in original
543 .glyphs
544 .iter()
545 .zip(deserialized.glyphs.iter())
546 {
547 assert_eq!(orig_glyph.id, deser_glyph.id);
548 assert_eq!(orig_glyph.symbol, deser_glyph.symbol);
549 assert_eq!(orig_glyph.pixel_coords, deser_glyph.pixel_coords);
550 }
551 }
552}