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