glyph_core/
format.rs

1use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
2use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};
3use std::io::{Read, Write};
4
5use crate::error::GlyphError;
6
7const MAGIC: [u8; 4] = *b"GLYF";
8const VERSION: u32 = 1;
9
10// Constants for performance tuning
11const COMPRESSION_LEVEL: Compression = Compression::fast();
12const INITIAL_BUFFER_SIZE: usize = 8192;
13const CHANGE_BUFFER_CAPACITY: usize = 2048;
14const LINE_BUFFER_CAPACITY: usize = 256;
15
16#[derive(Debug, Clone)]
17pub struct Frame {
18    pub content: String,
19    pub duration_ms: Option<u32>,
20}
21
22impl Frame {
23    pub fn new(content: String) -> Self {
24        Self {
25            content,
26            duration_ms: None,
27        }
28    }
29
30    pub fn with_duration(content: String, duration_ms: u32) -> Self {
31        Self {
32            content,
33            duration_ms: Some(duration_ms),
34        }
35    }
36
37    // Optimize validation by using bytes directly
38    fn validate_dimensions(&self, width: u32, height: u32) -> Result<(), GlyphError> {
39        let bytes = self.content.as_bytes();
40        let mut line_count = 0;
41        let mut pos = 0;
42        let mut line_width = 0;
43
44        while pos < bytes.len() {
45            match bytes[pos] {
46                b'\n' => {
47                    if line_width as u32 != width {
48                        return Err(GlyphError::InvalidDimensions {
49                            width: line_width as u32,
50                            height,
51                        });
52                    }
53                    line_count += 1;
54                    line_width = 0;
55                }
56                _ => line_width += 1,
57            }
58            pos += 1;
59        }
60
61        // Handle last line if it doesn't end with newline
62        if line_width > 0 {
63            if line_width as u32 != width {
64                return Err(GlyphError::InvalidDimensions {
65                    width: line_width as u32,
66                    height,
67                });
68            }
69            line_count += 1;
70        }
71
72        if line_count as u32 != height {
73            return Err(GlyphError::InvalidDimensions {
74                width,
75                height: line_count as u32,
76            });
77        }
78
79        Ok(())
80    }
81}
82
83#[derive(Debug)]
84pub struct Glyph {
85    pub width: u32,
86    pub height: u32,
87    pub default_duration_ms: u32,
88    pub frames: Vec<Frame>,
89}
90
91impl Glyph {
92    pub fn new(width: u32, height: u32, default_duration_ms: u32) -> Self {
93        Self {
94            width,
95            height,
96            default_duration_ms,
97            frames: Vec::with_capacity(LINE_BUFFER_CAPACITY),
98        }
99    }
100
101    pub fn add_frame(&mut self, frame: Frame) -> Result<(), GlyphError> {
102        frame.validate_dimensions(self.width, self.height)?;
103        self.frames.push(frame);
104        Ok(())
105    }
106
107    pub fn frames(&self) -> &[Frame] {
108        &self.frames
109    }
110
111    pub fn write<W: Write>(&self, writer: &mut W) -> Result<(), GlyphError> {
112        // Write header
113        writer.write_all(&MAGIC)?;
114        writer.write_u32::<LittleEndian>(VERSION)?;
115        writer.write_u32::<LittleEndian>(self.frames.len() as u32)?;
116        writer.write_u32::<LittleEndian>(self.width)?;
117        writer.write_u32::<LittleEndian>(self.height)?;
118        writer.write_u32::<LittleEndian>(self.default_duration_ms)?;
119        writer.write_all(&[0u8; 16])?; // Reserved bytes
120
121        // Pre-allocate buffers for reuse
122        let mut changes = Vec::with_capacity(CHANGE_BUFFER_CAPACITY);
123        let mut changed_chars = Vec::with_capacity(CHANGE_BUFFER_CAPACITY);
124        let mut compressed_buffer = Vec::with_capacity(INITIAL_BUFFER_SIZE);
125        let mut utf8_buf = [0u8; 4];
126
127        // Write frames
128        for (i, frame) in self.frames.iter().enumerate() {
129            // Write duration override flag and value if present
130            writer.write_u8(frame.duration_ms.is_some() as u8)?;
131            if let Some(duration) = frame.duration_ms {
132                writer.write_u32::<LittleEndian>(duration)?;
133            }
134
135            if i == 0 {
136                // First frame: full compressed data
137                let mut encoder = ZlibEncoder::new(&mut compressed_buffer, COMPRESSION_LEVEL);
138                encoder.write_all(frame.content.as_bytes())?;
139                encoder.finish()?;
140
141                writer.write_u32::<LittleEndian>(compressed_buffer.len() as u32)?;
142                writer.write_all(&compressed_buffer)?;
143                compressed_buffer.clear();
144            } else {
145                // Delta encoding for subsequent frames
146                let prev_frame = &self.frames[i - 1];
147                changes.clear();
148                changed_chars.clear();
149
150                // Compare bytes directly for better performance
151                let prev_bytes = prev_frame.content.as_bytes();
152                let curr_bytes = frame.content.as_bytes();
153                let mut pos = 0;
154                let mut byte_pos = 0;
155
156                while byte_pos < curr_bytes.len() {
157                    if byte_pos >= prev_bytes.len() || curr_bytes[byte_pos] != prev_bytes[byte_pos]
158                    {
159                        // Found a difference, decode the character
160                        let mut char_len = 1;
161                        while byte_pos + char_len < curr_bytes.len()
162                            && (curr_bytes[byte_pos + char_len] & 0xC0) == 0x80
163                        {
164                            char_len += 1;
165                        }
166                        if let Some(c) =
167                            std::str::from_utf8(&curr_bytes[byte_pos..byte_pos + char_len])
168                                .ok()
169                                .and_then(|s| s.chars().next())
170                        {
171                            changes.push(pos as u32);
172                            changed_chars.push(c);
173                        }
174                    }
175
176                    // Move to next character
177                    if byte_pos < prev_bytes.len() {
178                        let mut len = 1;
179                        while byte_pos + len < prev_bytes.len()
180                            && (prev_bytes[byte_pos + len] & 0xC0) == 0x80
181                        {
182                            len += 1;
183                        }
184                        byte_pos += len;
185                    } else {
186                        byte_pos += 1;
187                    }
188                    pos += 1;
189                }
190
191                // Compress changed characters efficiently
192                let mut encoder = ZlibEncoder::new(&mut compressed_buffer, COMPRESSION_LEVEL);
193                for &c in &changed_chars {
194                    let len = c.encode_utf8(&mut utf8_buf).len();
195                    encoder.write_all(&utf8_buf[..len])?;
196                }
197                encoder.finish()?;
198
199                // Write delta data
200                writer.write_u32::<LittleEndian>(compressed_buffer.len() as u32)?;
201                writer.write_u32::<LittleEndian>(changes.len() as u32)?;
202                writer.write_all(&compressed_buffer)?;
203                compressed_buffer.clear();
204
205                // Write change positions in a batch
206                for chunk in changes.chunks(1024) {
207                    for &pos in chunk {
208                        writer.write_u32::<LittleEndian>(pos)?;
209                    }
210                }
211            }
212        }
213
214        Ok(())
215    }
216
217    pub fn read<R: Read>(reader: &mut R) -> Result<Self, GlyphError> {
218        // Pre-allocate fixed-size buffers
219        let mut magic = [0u8; 4];
220        let mut reserved = [0u8; 16];
221        let mut u32_buf = [0u8; 4];
222
223        // Read and verify magic number
224        reader.read_exact(&mut magic)?;
225        if magic != MAGIC {
226            return Err(GlyphError::InvalidMagic);
227        }
228
229        // Read header using fixed buffer
230        reader.read_exact(&mut u32_buf)?;
231        let version = u32::from_le_bytes(u32_buf);
232        if version != VERSION {
233            return Err(GlyphError::InvalidVersion);
234        }
235
236        reader.read_exact(&mut u32_buf)?;
237        let frame_count = u32::from_le_bytes(u32_buf);
238        reader.read_exact(&mut u32_buf)?;
239        let width = u32::from_le_bytes(u32_buf);
240        reader.read_exact(&mut u32_buf)?;
241        let height = u32::from_le_bytes(u32_buf);
242        reader.read_exact(&mut u32_buf)?;
243        let default_duration_ms = u32::from_le_bytes(u32_buf);
244
245        // Skip reserved bytes
246        reader.read_exact(&mut reserved)?;
247
248        // Pre-calculate buffer sizes based on dimensions
249        let frame_size = (width * height) as usize;
250        let mut glyph = Glyph::new(width, height, default_duration_ms);
251        let mut compressed_buffer = Vec::with_capacity(frame_size * 2);
252        let mut content_buffer = String::with_capacity(frame_size);
253        let mut prev_content = String::with_capacity(frame_size);
254        let mut char_buf = Vec::with_capacity(frame_size);
255
256        // Read frames
257        for i in 0..frame_count {
258            // Read duration override
259            let has_duration = reader.read_u8()? != 0;
260            let duration = if has_duration {
261                reader.read_exact(&mut u32_buf)?;
262                Some(u32::from_le_bytes(u32_buf))
263            } else {
264                None
265            };
266
267            content_buffer.clear();
268            if i == 0 {
269                // First frame: read full compressed data
270                reader.read_exact(&mut u32_buf)?;
271                let compressed_len = u32::from_le_bytes(u32_buf) as usize;
272                compressed_buffer.resize(compressed_len, 0);
273                reader.read_exact(&mut compressed_buffer)?;
274
275                let mut decoder = ZlibDecoder::new(&compressed_buffer[..]);
276                decoder
277                    .read_to_string(&mut content_buffer)
278                    .map_err(|e| GlyphError::DecompressionError(e.to_string()))?;
279
280                prev_content.clear();
281                prev_content.push_str(&content_buffer);
282            } else {
283                // Delta encoded frame
284                reader.read_exact(&mut u32_buf)?;
285                let compressed_len = u32::from_le_bytes(u32_buf) as usize;
286                reader.read_exact(&mut u32_buf)?;
287                let change_count = u32::from_le_bytes(u32_buf) as usize;
288
289                // Read and decompress changed characters
290                compressed_buffer.resize(compressed_len, 0);
291                reader.read_exact(&mut compressed_buffer)?;
292
293                let mut decoder = ZlibDecoder::new(&compressed_buffer[..]);
294                char_buf.clear();
295                decoder
296                    .read_to_end(&mut char_buf)
297                    .map_err(|e| GlyphError::DecompressionError(e.to_string()))?;
298
299                let changed_chars = std::str::from_utf8(&char_buf)
300                    .map_err(|e| GlyphError::DecompressionError(e.to_string()))?;
301
302                if changed_chars.chars().count() != change_count {
303                    return Err(GlyphError::FrameDataError(
304                        "Mismatch in change count".to_string(),
305                    ));
306                }
307
308                // Apply changes to previous frame efficiently
309                content_buffer.push_str(&prev_content);
310                let mut content_chars: Vec<char> = content_buffer.chars().collect();
311
312                // Read and apply changes in chunks for better cache utilization
313                let changed_chars_iter = changed_chars.chars();
314                let mut positions = Vec::with_capacity(change_count);
315                for _ in 0..change_count {
316                    reader.read_exact(&mut u32_buf)?;
317                    positions.push(u32::from_le_bytes(u32_buf) as usize);
318                }
319
320                for (pos, new_char) in positions.into_iter().zip(changed_chars_iter) {
321                    if pos >= content_chars.len() {
322                        return Err(GlyphError::FrameDataError(
323                            "Invalid change position".to_string(),
324                        ));
325                    }
326                    content_chars[pos] = new_char;
327                }
328
329                content_buffer.clear();
330                content_buffer.extend(content_chars);
331                prev_content.clear();
332                prev_content.push_str(&content_buffer);
333            }
334
335            let frame = Frame {
336                content: content_buffer.clone(),
337                duration_ms: duration,
338            };
339
340            glyph.add_frame(frame)?;
341        }
342
343        Ok(glyph)
344    }
345}