mkv_element/
lacer.rs

1//! Handler for lacing and delacing operations on frame data.
2
3use crate::{Error, base::VInt64, functional::Encode, io::blocking_impl::ReadFrom};
4
5// https://www.matroska.org/technical/notes.html
6/// Handler for lacing and delacing operations on frame data.
7pub enum Lacer {
8    /// Xiph lacing (variable-size frames with size prefixes)
9    ///
10    /// The Xiph lacing uses the same coding of size as found in the Ogg container \[@?RFC3533\]. The bits 5-6 of the Block Header flags are set to 01.
11    /// The Block data with laced frames is stored as follows:
12    ///     Lacing Head on 1 Octet: Number of frames in the lace minus 1.
13    ///     Lacing size of each frame except the last one.
14    ///     Binary data of each frame consecutively.
15    /// The lacing size is split into 255 values, stored as unsigned octets – for example, 500 is coded 255;245 or [0xFF 0xF5]. A frame with a size multiple of 255 is coded with a 0 at the end of the size – for example, 765 is coded 255;255;255;0 or [0xFF 0xFF 0xFF 0x00].
16    /// The size of the last frame is deduced from the size remaining in the Block after the other frames.
17    Xiph,
18
19    /// Fixed-size lacing (all frames have the same size)
20    FixedSize,
21    /// EBML lacing (variable-size frames with EBML-encoded sizes)
22    ///
23    /// The EBML lacing encodes the frame size with an EBML-like encoding \[@!RFC8794\]. The bits 5-6 of the Block Header flags are set to 11.
24    ///
25    /// The Block data with laced frames is stored as follows:
26    ///     Lacing Head on 1 Octet: Number of frames in the lace minus 1.
27    ///     Lacing size of each frame except the last one.
28    ///     Binary data of each frame consecutively.
29    ///
30    /// The first frame size is encoded as an EBML Variable-Size Integer value, also known as VINT in \[@!RFC8794\].
31    /// The remaining frame sizes are encoded as signed values using the difference between the frame size and the previous frame size.
32    /// These signed values are encoded as VINT, with a mapping from signed to unsigned numbers.
33    /// Decoding the unsigned number stored in the VINT to a signed number is done by subtracting 2^((7*n)-1)-1, where n is the octet size of the VINT.
34    Ebml,
35}
36
37impl Lacer {
38    /// Encode multiple frames into a single laced block
39    pub fn lace(&self, frames: &[&[u8]]) -> Vec<u8> {
40        if frames.is_empty() {
41            return vec![];
42        }
43        let num_frames = frames.len();
44        let mut output = vec![];
45        output.push((num_frames - 1) as u8); // Number of frames - 1
46
47        match self {
48            Lacer::Xiph => {
49                for frame in &frames[..num_frames - 1] {
50                    let mut size = frame.len();
51                    while size >= 0xFF {
52                        output.push(0xFF);
53                        size -= 0xFF;
54                    }
55                    output.push(size as u8);
56                }
57                for frame in frames {
58                    output.extend_from_slice(frame);
59                }
60                output
61            }
62            Lacer::FixedSize => {
63                let frame_size = frames[0].len();
64                if let Some((idx, bad_frame)) = frames
65                    .iter()
66                    .enumerate()
67                    .find(|(_, f)| f.len() != frame_size)
68                {
69                    panic!(
70                        "All frames must have the same size for FixedSize lacing: expected size {}, but frame at index {} has size {}",
71                        frame_size,
72                        idx,
73                        bad_frame.len()
74                    );
75                }
76                for frame in frames {
77                    output.extend_from_slice(frame);
78                }
79                output
80            }
81            Lacer::Ebml => {
82                if num_frames == 1 {
83                    output.extend_from_slice(frames[0]);
84                    return output;
85                }
86                let sizes = frames.iter().map(|f| f.len() as u64).collect::<Vec<_>>();
87                // except first size, other sizes are stored as diffs to the previous size
88                let diff_sizes = std::iter::once(
89                    // first
90                    VInt64::new(sizes[0]),
91                )
92                .chain(sizes.windows(2).map(|w| {
93                    let diff = w[1] as i64 - w[0] as i64;
94
95                    //-(2^6^-1) to 2^6^
96                    let n = if diff > -(2i64.pow(6) - 1) && diff < (2i64.pow(6)) {
97                        1
98                    } else if diff > -(2i64.pow(13) - 1) && diff < (2i64.pow(13)) {
99                        2
100                    } else if diff > -(2i64.pow(20) - 1) && diff < (2i64.pow(20)) {
101                        3
102                    } else if diff > -(2i64.pow(27) - 1) && diff < (2i64.pow(27)) {
103                        4
104                    } else if diff > -(2i64.pow(34) - 1) && diff < (2i64.pow(34)) {
105                        5
106                    } else if diff > -(2i64.pow(41) - 1) && diff < (2i64.pow(41)) {
107                        6
108                    } else if diff > -(2i64.pow(48) - 1) && diff < (2i64.pow(48)) {
109                        7
110                    } else {
111                        panic!("Frame size diff too large for EBML lacing: diff = {}", diff);
112                    };
113
114                    // map to unsigned
115                    let diff_unsigned = diff + (2i64.pow(7 * n as u32 - 1) - 1);
116                    VInt64::new(diff_unsigned as u64)
117                }))
118                // dont include last size, it is deduced from remaining data
119                .take(num_frames - 1);
120
121                for size in diff_sizes {
122                    size.encode(&mut output).unwrap();
123                }
124                for frame in frames {
125                    output.extend_from_slice(frame);
126                }
127                output
128            }
129        }
130    }
131
132    /// Decode a laced block into individual frames
133    pub fn delace<'a>(&self, data: &'a [u8]) -> crate::Result<Vec<&'a [u8]>> {
134        // TODO(perf): avoid heap allocations ideally
135        // we should be able to return a `impl Iterator<Item = crate::Result<&'a [u8]>>` here
136        // can make it work using nightly features like `generators`.
137        // but not sure how to do that with the current stable Rust.
138
139        if data.is_empty() {
140            return Ok(vec![]);
141        }
142        let num_frames = data[0] as usize + 1;
143        if num_frames == 1 {
144            return Ok(vec![&data[1..]]);
145        }
146
147        match self {
148            Lacer::Xiph => {
149                let mut out = Vec::with_capacity(num_frames);
150
151                let data_start_pos = data
152                    .iter()
153                    .enumerate()
154                    .skip(1)
155                    .filter(|(_, b)| **b != 0xFF)
156                    .nth(num_frames - 2)
157                    .map(|(i, _)| i)
158                    .ok_or(Error::MalformedLacingData)?
159                    + 1;
160
161                let laced_data = data
162                    .get(data_start_pos..)
163                    .ok_or(Error::MalformedLacingData)?;
164
165                let mut start = 0;
166                for size in data[1..data_start_pos]
167                    .split_inclusive(|b| *b != 0xFF)
168                    .map(|chunk| chunk.iter().map(|b| *b as usize).sum::<usize>())
169                {
170                    out.push(
171                        laced_data
172                            .get(start..start + size)
173                            .ok_or(Error::MalformedLacingData)?,
174                    );
175                    start += size;
176                }
177                out.push(laced_data.get(start..).ok_or(Error::MalformedLacingData)?);
178                Ok(out)
179            }
180            Lacer::FixedSize => {
181                let data_len = data.len() - 1;
182
183                // all frames must have the same size
184                if !data_len.is_multiple_of(num_frames) {
185                    return Err(Error::MalformedLacingData);
186                }
187
188                Ok(data[1..].chunks(data_len / num_frames).collect())
189            }
190            Lacer::Ebml => {
191                let mut data_buf = &data[1..];
192                let mut out_sizes = Vec::with_capacity(num_frames - 1);
193                let first_size = VInt64::read_from(&mut data_buf)?;
194                out_sizes.push(*first_size as usize);
195                for _ in 1..(num_frames - 1) {
196                    let oct_size = data_buf
197                        .first()
198                        .ok_or(Error::MalformedLacingData)?
199                        .leading_zeros()
200                        + 1;
201                    let current_encoded_vint = VInt64::read_from(&mut data_buf)?;
202                    // unsigned to signed
203                    let diff = *current_encoded_vint as i64 - (2i64.pow(7 * oct_size - 1) - 1);
204                    let new_size = out_sizes
205                        .last()
206                        .unwrap()
207                        .checked_add_signed(diff as isize)
208                        .ok_or(Error::MalformedLacingData)?;
209                    out_sizes.push(new_size);
210                }
211
212                let mut out = Vec::with_capacity(num_frames);
213
214                let mut start = 0;
215                for size in out_sizes {
216                    out.push(
217                        data_buf
218                            .get(start..start + size)
219                            .ok_or(Error::MalformedLacingData)?,
220                    );
221                    start += size;
222                }
223                out.push(data_buf.get(start..).ok_or(Error::MalformedLacingData)?);
224                Ok(out)
225            }
226        }
227    }
228}
229
230#[cfg(test)]
231mod lacer_tests {
232    use super::*;
233    #[test]
234    fn test_xiph_lacing() {
235        // 0 frames
236        let laced = Lacer::Xiph.lace(&[]);
237        assert_eq!(laced, vec![]);
238        let frames: Vec<_> = Lacer::Xiph.delace(&[]).unwrap();
239        assert_eq!(frames.len(), 0);
240
241        // 4 frames, sizes: 255, 256, 1, remaining
242        let len = vec![0x03, 0xFF, 0x00, 0xFF, 0x1, 0x1];
243        let frame0 = vec![2u8; 255];
244        let frame1 = vec![42u8; 256];
245        let frame2 = vec![38u8; 1];
246        let frame3 = vec![100u8; 1];
247
248        let laced = Lacer::Xiph.lace(&[&frame0, &frame1, &frame2, &frame3]);
249        let data = [len, frame0, frame1, frame2, frame3].concat();
250        assert_eq!(laced, data);
251
252        let frames: Vec<_> = Lacer::Xiph.delace(&data).unwrap();
253        assert_eq!(frames.len(), 4);
254        assert_eq!(frames[0], &[2u8; 255]);
255        assert_eq!(frames[1], &[42u8; 256]);
256        assert_eq!(frames[2], &[38u8; 1]);
257        assert_eq!(frames[3], &[100u8; 1]);
258
259        // 1 frame, size: remaining
260        let len = vec![0x00];
261        let frame0 = vec![2u8; 255];
262
263        let laced = Lacer::Xiph.lace(&[&frame0]);
264        let data = [len, frame0].concat();
265        assert_eq!(laced, data);
266
267        let frames: Vec<_> = Lacer::Xiph.delace(&data).unwrap();
268        assert_eq!(frames.len(), 1);
269        assert_eq!(frames[0], &[2u8; 255]);
270
271        // 2 frames, sizes: 32, remaining
272        let len = vec![0x01, 0x20];
273        let frame0 = vec![2u8; 32];
274        let frame1 = vec![42u8; 256];
275
276        let laced = Lacer::Xiph.lace(&[&frame0, &frame1]);
277        let data = [len, frame0, frame1].concat();
278        assert_eq!(laced, data);
279
280        let frames: Vec<_> = Lacer::Xiph.delace(&data).unwrap();
281        assert_eq!(frames.len(), 2);
282        assert_eq!(frames[0], &[2u8; 32]);
283        assert_eq!(frames[1], &[42u8; 256]);
284
285        // 4 frames, sizes: 600, 3, 520, remaining
286        let len = vec![0x03, 0xFF, 0xFF, 0x5A, 0x3, 0xFF, 0xFF, 0xA];
287        assert_eq!(0xff + 0xff + 0x5A, 600);
288        assert_eq!(0xff + 0xff + 0xA, 520);
289        let frame0 = vec![2u8; 600];
290        let frame1 = vec![42u8; 3];
291        let frame2 = vec![38u8; 520];
292        let frame3 = vec![100u8; 1];
293
294        let laced = Lacer::Xiph.lace(&[&frame0, &frame1, &frame2, &frame3]);
295        let data = [len, frame0, frame1, frame2, frame3].concat();
296        assert_eq!(laced, data);
297
298        let frames: Vec<_> = Lacer::Xiph.delace(&data).unwrap();
299        assert_eq!(frames.len(), 4);
300        assert_eq!(frames[0], &[2u8; 600]);
301        assert_eq!(frames[1], &[42u8; 3]);
302        assert_eq!(frames[2], &[38u8; 520]);
303        assert_eq!(frames[3], &[100u8; 1]);
304    }
305
306    #[test]
307    fn test_ebml_lacing() {
308        // 0 frames
309        let laced = Lacer::Ebml.lace(&[]);
310        assert_eq!(laced, vec![]);
311        let frames: Vec<_> = Lacer::Ebml.delace(&[]).unwrap();
312        assert_eq!(frames.len(), 0);
313
314        // 3 frames, sizes: 800, 500, remaining(1000)
315
316        // store as size diffs: 800, -300
317
318        // offset = 2**(7*n - 1) - 1
319        // n = 2 -> 2**13 - 1 = 8191
320        // convert to uint: 800, 7891(-300+8191)
321
322        // encode as VInt:
323        // 0x4320(800), 0x5ED3(7891)
324
325        let len = vec![0x02, 0x43, 0x20, 0x5E, 0xD3];
326        let frame0 = vec![2u8; 800];
327        let frame1 = vec![42u8; 500];
328        let frame2 = vec![38u8; 1000];
329        let laced = Lacer::Ebml.lace(&[&frame0, &frame1, &frame2]);
330        let data = [len, frame0, frame1, frame2].concat();
331        assert_eq!(laced, data);
332
333        let frames: Vec<_> = Lacer::Ebml.delace(&data).unwrap();
334        assert_eq!(frames.len(), 3);
335        assert_eq!(frames[0], &[2u8; 800]);
336        assert_eq!(frames[1], &[42u8; 500]);
337        assert_eq!(frames[2], &[38u8; 1000]);
338
339        // 7 frames, sizes      2, 5000, 4980, 400, 20, 2000, remaining(300)
340        // store as size diffs: 2, 4998, -20, -4580, -380, 1980
341        let len = vec![
342            0x06, 0x82, 0x73, 0x85, 0xAB, 0x4E, 0x1B, 0x5E, 0x83, 0x67, 0xBB,
343        ];
344        let frame0 = vec![2u8; 2];
345        let frame1 = vec![42u8; 5000];
346        let frame2 = vec![38u8; 4980];
347        let frame3 = vec![100u8; 400];
348        let frame4 = vec![7u8; 20];
349        let frame5 = vec![8u8; 2000];
350        let frame6 = vec![9u8; 300];
351        let laced = Lacer::Ebml.lace(&[
352            &frame0, &frame1, &frame2, &frame3, &frame4, &frame5, &frame6,
353        ]);
354        let data = [len, frame0, frame1, frame2, frame3, frame4, frame5, frame6].concat();
355        assert_eq!(laced, data);
356        let frames: Vec<_> = Lacer::Ebml.delace(&data).unwrap();
357        assert_eq!(frames.len(), 7);
358        assert_eq!(frames[0], &[2u8; 2]);
359        assert_eq!(frames[1], &[42u8; 5000]);
360        assert_eq!(frames[2], &[38u8; 4980]);
361        assert_eq!(frames[3], &[100u8; 400]);
362        assert_eq!(frames[4], &[7u8; 20]);
363        assert_eq!(frames[5], &[8u8; 2000]);
364        assert_eq!(frames[6], &[9u8; 300]);
365    }
366
367    #[test]
368    fn test_fixed_size_lacing() {
369        // 0 frames
370        let laced = Lacer::FixedSize.lace(&[]);
371        assert_eq!(laced, vec![]);
372        let frames: Vec<_> = Lacer::FixedSize.delace(&[]).unwrap();
373        assert_eq!(frames.len(), 0);
374
375        // 3 frames, sizes: 500, 500, 500
376        let len = vec![0x02];
377        let frame0 = vec![2u8; 500];
378        let frame1 = vec![42u8; 500];
379        let frame2 = vec![38u8; 500];
380        let laced = Lacer::FixedSize.lace(&[&frame0, &frame1, &frame2]);
381        let data = [len, frame0, frame1, frame2].concat();
382        assert_eq!(laced, data);
383
384        let frames: Vec<_> = Lacer::FixedSize.delace(&data).unwrap();
385        assert_eq!(frames.len(), 3);
386        assert_eq!(frames[0], &[2u8; 500]);
387        assert_eq!(frames[1], &[42u8; 500]);
388        assert_eq!(frames[2], &[38u8; 500]);
389    }
390}