bmf_parse/
macro.rs

1use crate::base::*;
2use std::fmt::Debug;
3
4// MP4 impl helpers
5pub(crate) trait Mp4BoxTrait: Debug {
6    const TYPE: u32; // Should be 4 bytes/ASCII chars
7
8    fn parse_full(input: &[u8], state: &mut ParserState) -> Self;
9    fn parse(input: &[u8], state: &mut ParserState, header: &Option<(u8, u32)>) -> Self;
10
11    fn write_full(&self, output: &mut Vec<u8>);
12    fn write(&self, output: &mut Vec<u8>);
13}
14
15pub(crate) fn read_header<'a>(input: &'a [u8], state: &mut ParserState) -> (u32, &'a [u8]) {
16    let size = read(input, state, 4).unwrap();
17    let size = u32::from_be_bytes([size[0], size[1], size[2], size[3]]);
18
19    let type_ = read(input, state, 4).unwrap();
20    let type_ = u32::from_ne_bytes([type_[0], type_[1], type_[2], type_[3]]);
21
22    (type_, read(input, state, size as usize - 8).unwrap())
23}
24
25pub(crate) fn read_fullbox_header(input: &[u8], state: &mut ParserState) -> (u8, u32) {
26    let version = read(input, state, 1).unwrap()[0];
27    let flags = read(input, state, 3).unwrap();
28    let flags = u32::from_be_bytes([0, flags[0], flags[1], flags[2]]);
29
30    (version, flags)
31}
32
33macro_rules! mp4box_gen {
34    // Read types
35    { @read $input:ident $state:ident $header:ident; u8 } => {
36        read($input, $state, 1).unwrap()[0]
37    };
38    { @read $input:ident $state:ident $header:ident; u16 } => {
39        {
40            let slice = read($input, $state, 2).unwrap();
41            u16::from_be_bytes([slice[0], slice[1]])
42        }
43    };
44    { @read $input:ident $state:ident $header:ident; u32 } => {
45        {
46            let slice = read($input, $state, 4).unwrap();
47            u32::from_be_bytes([slice[0], slice[1], slice[2], slice[3]])
48        }
49    };
50    { @read $input:ident $state:ident $header:ident; u64 } => {
51        {
52            let slice = read($input, $state, 8).unwrap();
53            u64::from_be_bytes([slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7]])
54        }
55    };
56    { @read $input:ident $state:ident $header:ident; i8 } => {
57        read($input, $state, 1).unwrap()[0] as i8
58    };
59    { @read $input:ident $state:ident $header:ident; i16 } => {
60        {
61            let slice = read($input,$state, 2).unwrap();
62            i16::from_be_bytes([slice[0], slice[1]])
63        }
64    };
65    { @read $input:ident $state:ident $header:ident; i32 } => {
66        {
67            let slice = read($input, $state, 4).unwrap();
68            i32::from_be_bytes([slice[0], slice[1], slice[2], slice[3]])
69        }
70    };
71    { @read $input:ident $state:ident $header:ident; i64 } => {
72        {
73            let slice = read($input, $state, 8).unwrap();
74            i64::from_be_bytes([slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7]])
75        }
76    };
77    { @read $input:ident $state:ident $header:ident; f32 } => {
78        {
79            let slice = read($input, $state, 4).unwrap();
80            f32::from_be_bytes([slice[0], slice[1], slice[2], slice[3]])
81        }
82    };
83    { @read $input:ident $state:ident $header:ident; f64 } => {
84        {
85            let slice = read($input, $state, 8).unwrap();
86            f64::from_be_bytes([slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7]])
87        }
88    };
89    { @read $input:ident $state:ident $header:ident; [$type:tt; $n:expr] } => { // Might also work with sizes defined by variables
90        {
91            let mut array = [0; $n];
92            for i in 0..$n {
93                array[i] = mp4box_gen! { @read $input $state $header; $type };
94            }
95            array
96        }
97    };
98    { @read $input:ident $state:ident $header:ident; Mp4Box } => {
99        parse_box($input, $state).unwrap()
100    };
101    { @read $input:ident $state:ident $header:ident; String } => {
102        {
103            // Read CString in utf8
104            let mut string = String::new();
105
106            let mut bytes = Vec::new();
107            while let Some(byte) = read($input, $state, 1) {
108                if byte[0] == 0 {
109                    break;
110                }
111
112                // Read utf8 character
113                match byte[0] {
114                    0..=0x7F => {
115                        // 1 byte
116                        bytes.push(byte[0]);
117                    },
118                    0xC0..=0xDF => {
119                        // 2 bytes
120                        bytes.push(byte[0]);
121                        bytes.push(read($input, $state, 1).unwrap()[0]);
122                    },
123                    0xE0..=0xEF => {
124                        // 3 bytes
125                        bytes.push(byte[0]);
126                        bytes.push(read($input, $state, 1).unwrap()[0]);
127                        bytes.push(read($input, $state, 1).unwrap()[0]);
128                    },
129                    0xF0..=0xF7 => {
130                        // 4 bytes
131                        bytes.push(byte[0]);
132                        bytes.push(read($input, $state, 1).unwrap()[0]);
133                        bytes.push(read($input, $state, 1).unwrap()[0]);
134                        bytes.push(read($input, $state, 1).unwrap()[0]);
135                    },
136                    _ => panic!("Invalid utf8 character"),
137                }
138            }
139
140            // Convert to string
141            string.push_str(std::str::from_utf8(&bytes).unwrap());
142
143            string
144        }
145    };
146
147
148    // Generic catch-all for metastructs
149    { @read $input:ident $state:ident $header:ident; $($type:tt)* } => {
150        //panic!("Unsupported type: {}", stringify!($type))
151        <$($type)*>::parse($input, $state, &$header)
152    };
153
154    // Write Types
155    { @write $output:ident $($item:ident).+; &u8 } => {
156        $output.push(*$($item).+);
157    };
158    { @write $output:ident $($item:ident).+; u8 } => {
159        $output.push($($item).+);
160    };
161    { @write $output:ident $($item:ident).+; &u16 } => {
162        $output.extend_from_slice(&u16::to_be_bytes(*$($item).+))
163    };
164    { @write $output:ident $($item:ident).+; u16 } => {
165        $output.extend_from_slice(&u16::to_be_bytes($($item).+))
166    };
167    { @write $output:ident $($item:ident).+; &u32 } => {
168        $output.extend_from_slice(&u32::to_be_bytes(*$($item).+))
169    };
170    { @write $output:ident $($item:ident).+; u32 } => {
171        $output.extend_from_slice(&u32::to_be_bytes($($item).+))
172    };
173    { @write $output:ident $($item:ident).+; &u64 } => {
174        $output.extend_from_slice(&u64::to_be_bytes(*$($item).+))
175    };
176    { @write $output:ident $($item:ident).+; u64 } => {
177        $output.extend_from_slice(&u64::to_be_bytes($($item).+))
178    };
179    { @write $output:ident $($item:ident).+; i8 } => {
180        $output.push($($item).+ as u8)
181    };
182    { @write $output:ident $($item:ident).+; i16 } => {
183        $output.extend_from_slice(&i16::to_be_bytes($($item).+))
184    };
185    { @write $output:ident $($item:ident).+; &i32 } => {
186        $output.extend_from_slice(&i32::to_be_bytes(*$($item).+))
187    };
188    { @write $output:ident $($item:ident).+; i32 } => {
189        $output.extend_from_slice(&i32::to_be_bytes($($item).+))
190    };
191    { @write $output:ident $($item:ident).+; i64 } => {
192        $output.extend_from_slice(&i64::to_be_bytes($($item).+))
193    };
194    { @write $output:ident $($item:ident).+; f32 } => {
195        $output.extend_from_slice(&f32::to_be_bytes($($item).+))
196    };
197    { @write $output:ident $($item:ident).+; f64 } => {
198        $output.extend_from_slice(&f64::to_be_bytes($($item).+))
199    };
200    { @write $output:ident $($item:ident).+; [$type:tt; $n:expr] } => { // Might also work with sizes defined by variables
201        for entry in $($item).+ {
202            mp4box_gen! { @write $output entry; $type };
203        }
204    };
205    { @write $output:ident $($item:ident).+; &[$type:tt; $n:expr] } => { // Might also work with sizes defined by variables
206        for entry in $($item).+ {
207            let entry = *entry;
208            mp4box_gen! { @write $output entry; $type };
209        }
210    };
211    { @write $output:ident $($item:ident).+; String } => {
212        $output.extend_from_slice($($item).+.as_bytes());
213    };
214
215    // Generic catch-all for metastructs
216    { @write $output:ident $($item:ident).+; $($type:tt)* } => {
217        $($item).+.write($output);
218    };
219
220    // Condition expansion
221    // Final layer Read
222    { // vec w/ no length
223        @cond read
224        $input:ident $state:ident $header:ident;
225        [Vec<$type:tt, Remain>],
226    } => {
227        {
228            let length = ($input.len() - $state.offset) / std::mem::size_of::<$type>();
229            let mut vec = Vec::with_capacity(length);
230            for _ in 0..length {
231                vec.push(mp4box_gen! { @read $input $state $header; $type });
232            }
233            vec
234        }
235    };
236    { // vec w/ non-option length
237        @cond read
238        $input:ident $state:ident $header:ident;
239        [Vec<$type:tt, $length:ident>],
240    } => {
241        {
242            let length = $length as usize;
243            let mut vec = Vec::with_capacity(length);
244            for _ in 0..length {
245                vec.push(mp4box_gen! { @read $input $state $header; $type });
246            }
247            vec
248        }
249    };
250    { // vec w/ option length
251        @cond read
252        $input:ident $state:ident $header:ident;
253        [Vec<$type:tt, Option<[$($length:tt)*]>>],
254    } => {
255        {
256            let length = ($($length)*) as usize;
257            let mut vec = Vec::with_capacity(length);
258            for _ in 0..length {
259                vec.push(mp4box_gen! { @read $input $state $header; $type });
260            }
261            vec
262        }
263    };
264    { // Simple type
265        @cond read $($opt:ident)*;
266        [$type:tt],
267    } => {
268        mp4box_gen! { @read $($opt)*; $type }
269    };
270
271    // Iterate
272    { // Either
273        @cond read $($opt:ident)*;
274        [Either<$type:tt, [$($btype:tt)*]>],
275        $cond:expr,
276        $($rest:tt)*
277    } => {
278        if $cond {
279            Either::A(mp4box_gen! { @cond read $($opt)*; [$type], })
280        } else {
281            Either::B(mp4box_gen! { @cond read $($opt)*; [$($btype)*], $($rest)* })
282        }
283    };
284    { // Option
285        @cond read $($opt:ident)*;
286        [Option<[$($type:tt)*]>],
287        $cond:expr,
288        $($rest:tt)*
289    } => {
290        if $cond {
291            Some(mp4box_gen! { @cond read $($opt)*; [$($type)*], $($rest)* })
292        } else {
293            None
294        }
295    };
296
297    // Write
298    { // borrow vec w/ non-option length
299        @cond write
300        $output:ident $($item:ident).+;
301        [&Vec<$type:tt, $length:ident>],
302    } => {
303        for entry in $($item).+ {
304            mp4box_gen! { @write $output entry; $type };
305        }
306    };
307    { // borrow vec w/ option length
308        @cond write
309        $output:ident $($item:ident).+;
310        [&Vec<$type:tt, Option<[$($length:tt)*]>>],
311    } => {
312        for entry in $($item).+ {
313            mp4box_gen! { @write $output entry; $type };
314        }
315    };
316    { // vec w/ non-option length
317        @cond write
318        $output:ident $($item:ident).+;
319        [Vec<$type:tt, $length:ident>],
320    } => {
321        for entry in &$($item).+ {
322            mp4box_gen! { @write $output entry; &$type };
323        }
324    };
325    { // vec w/ option length
326        @cond write
327        $output:ident $($item:ident).+;
328        [Vec<$type:tt, Option<[$($length:tt)*]>>],
329    } => {
330        for entry in $($item).+ {
331            mp4box_gen! { @write $output entry; $type };
332        }
333    };
334    { // Simple type
335        @cond write
336        $output:ident $($item:ident).+;
337        [$type:tt],
338    } => {
339        mp4box_gen! { @write $output $($item).+; $type }
340    };
341    { // Simple type
342        @cond write
343        $output:ident $($item:ident).+;
344        [&$type:tt],
345    } => {
346        mp4box_gen! { @write $output $($item).+; &$type }
347    };
348
349    // Iterate
350    { // borrow Either
351        @cond write
352        $output:ident $($item:ident).+;
353        [&Either<$type:tt, [$($btype:tt)*]>],
354        $cond:expr,
355        $($rest:tt)*
356    } => {
357        match $($item).+ {
358            Either::A(item) => mp4box_gen! { @cond write $output item; [&$type], },
359            Either::B(item) => mp4box_gen! { @cond write $output item; [&$($btype)*], $($rest)* },
360        }
361    };
362    { // Either
363        @cond write
364        $output:ident $($item:ident).+;
365        [Either<$type:tt, [$($btype:tt)*]>],
366        $cond:expr,
367        $($rest:tt)*
368    } => {
369        match $($item).+ {
370            Either::A(item) => mp4box_gen! { @cond write $output item; [$type], },
371            Either::B(item) => mp4box_gen! { @cond write $output item; [$($btype)*], $($rest)* },
372        }
373    };
374    { // borrow Option
375        @cond write
376        $output:ident $($item:ident).+;
377        [&Option<[$($type:tt)*]>],
378        $cond:expr,
379        $($rest:tt)*
380    } => {
381        if let Some(item) = $($item).+ {
382            mp4box_gen! { @cond write $output item; [&$($type)*], $($rest)* }
383        }
384    };
385    { // Option
386        @cond write
387        $output:ident $($item:ident).+;
388        [Option<[$($type:tt)*]>],
389        $cond:expr,
390        $($rest:tt)*
391    } => {
392        if let Some(item) = &$($item).+ {
393            mp4box_gen! { @cond write $output item; [&$($type)*], $($rest)* }
394        }
395    };
396
397    // Struct construction
398    // Full Type
399    {
400        @expand $version:ident $flags:ident;
401        $name:ident Full {}; // No fields remaining
402        [
403            $([
404                $field:ident, [$($ftype:tt)*]&[$($ctype:tt)*]; $($cond:expr,)*
405            ],)+ // Expanded fields
406        ]
407    } => {
408        use $crate::base::*;
409        use $crate::r#macro::*;
410
411        paste::paste! {
412            #[derive(Debug)]
413            pub struct [<Box $name>] {
414                pub header: Option<(u8, u32)>,
415                $(
416                    pub $field: $($ftype)*,
417                )+
418            }
419            impl [<Box $name>] {
420                const IDSTR: &[u8] = bstringify::bstringify!([<$name:lower>]);
421            }
422            impl Mp4BoxTrait for [<Box $name>] {
423                const TYPE: u32 = u32::from_ne_bytes([Self::IDSTR[0], Self::IDSTR[1], Self::IDSTR[2], Self::IDSTR[3]]);
424
425                fn parse_full(input: &[u8], state: &mut ParserState) -> Self {
426                    let (version, flags) = read_fullbox_header(input, state);
427                    let header = Some((version, flags));
428
429                    let mut instance = Self::parse(input, state, &header);
430                    instance.header = header;
431                    instance
432                }
433
434                fn parse(input: &[u8], state: &mut ParserState, header: &Option<(u8, u32)>) -> Self {
435                    let uwh = header.unwrap();
436                    let ($version, $flags) = uwh;
437
438                    // Split out into fields so they can reference each other
439                    $(
440                        let $field = mp4box_gen! {
441                            @cond read input state header;
442                            [$($ctype)*],
443                            $($cond,)*
444                        };
445                    )+
446
447                    Self {
448                        header: None,
449                        $(
450                            $field,
451                        )+
452                    }
453                }
454
455                fn write_full(&self, output: &mut Vec<u8>) {
456                    let mut data = Vec::new();
457                    self.write(&mut data);
458
459                    // Write header
460                    let size = data.len() as u32 + 12;
461                    output.extend_from_slice(&u32::to_be_bytes(size)); // Size
462                    output.extend_from_slice(&u32::to_ne_bytes(Self::TYPE)); // Type
463
464                    // Version
465                    let (version, flags) = self.header.unwrap();
466                    output.push(version); // Version (1 byte)
467                    output.extend_from_slice(&u32::to_be_bytes(flags)[1..]); // Flags (3 bytes)
468
469                    output.extend(data);
470                }
471
472                fn write(&self, output: &mut Vec<u8>) {
473                    $(
474                        mp4box_gen! {
475                            @cond write output self.$field;
476                            [$($ctype)*],
477                            $($cond,)*
478                        };
479                    )+
480                }
481            }
482        }
483    };
484
485    // Base Type
486    {
487        @expand $version:ident $flags:ident;
488        $name:ident {}; // No fields remaining
489        [
490            $([
491                $field:ident, [$($ftype:tt)*]&[$($ctype:tt)*]; $($cond:expr,)*
492            ],)+ // Expanded fields
493        ]
494    } => {
495        use $crate::base::*;
496        use $crate::r#macro::*;
497
498        paste::paste! {
499            #[derive(Debug)]
500            pub struct [<Box $name>] {
501                $(
502                    pub $field: $($ftype)*,
503                )+
504            }
505            impl [<Box $name>] {
506                const IDSTR: &[u8] = bstringify::bstringify!([<$name:lower>]);
507            }
508            impl Mp4BoxTrait for [<Box $name>] {
509                const TYPE: u32 = u32::from_ne_bytes([Self::IDSTR[0], Self::IDSTR[1], Self::IDSTR[2], Self::IDSTR[3]]);
510
511                fn parse_full(input: &[u8], state: &mut ParserState) -> Self {
512                    Self::parse(input, state, &None)
513                }
514
515                fn parse(input: &[u8], state: &mut ParserState, _: &Option<(u8, u32)>) -> Self {
516                    // Split out into fields so they can reference each other
517                    $(
518                        let $field = mp4box_gen! {
519                            @cond read input state header;
520                            [$($ctype)*],
521                            $($cond,)*
522                        };
523                    )+
524
525                    Self {
526                        $(
527                            $field,
528                        )+
529                    }
530                }
531
532                fn write_full(&self, output: &mut Vec<u8>) {
533                    let mut data = Vec::new();
534                    self.write(&mut data);
535
536                    // Write header
537                    let size = data.len() as u32 + 8;
538                    output.extend_from_slice(&u32::to_be_bytes(size)); // Size
539                    output.extend_from_slice(&u32::to_ne_bytes(Self::TYPE)); // Type
540                    output.extend(data);
541                }
542
543                fn write(&self, output: &mut Vec<u8>) {
544                    $(
545                        mp4box_gen! {
546                            @cond write output self.$field;
547                            [$($ctype)*],
548                            $($cond,)*
549                        };
550                    )+
551                }
552            }
553        }
554    };
555
556    // Conditional expansion
557    { // Complete
558        @cond_expand $version:ident $flags:ident;
559        $name:ident $($stype:ident)? {
560            [],
561            $($rest:tt)* // Remaining fields
562        }; [$($prev:tt)*], // Already expanded fields
563        [$field:ident, [$($ftype:tt)*]&[$($ctype:tt)*]; $($done:tt)*] // Current
564    } => {
565        mp4box_gen! {
566            @expand $version $flags;
567            $name $($stype)? {
568                $($rest)* // Remaining fields
569            }; [
570                $($prev)* // Already expanded fields
571                [$field, [$($ftype)*]&[$($ctype)*]; $($done)*],
572            ]
573        }
574    };
575    { // Complete with condition
576        @cond_expand $version:ident $flags:ident;
577        $name:ident $($stype:ident)? {
578            [] [if $cond:expr],
579            $($rest:tt)* // Remaining fields
580        }; [$($prev:tt)*], // Already expanded fields
581        [$field:ident, [$($ftype:tt)*]&[$($ctype:tt)*]; $($done:tt)*] // Current
582    } => {
583        mp4box_gen! {
584            @expand $version $flags;
585            $name $($stype)? {
586                $($rest)* // Remaining fields
587            }; [
588                $($prev)* // Already expanded fields
589                [$field, [Option<$($ftype)*>]&[Option<[$($ctype)*]>]; $cond, $($done)*],
590            ]
591        }
592    };
593    { // Iterate
594        @cond_expand $version:ident $flags:ident;
595        $name:ident $($stype:ident)? {
596            [$type:tt, $($rtype:tt,)*] [if $cond:expr] $([if $rcond:expr])*,
597            $($rest:tt)* // Remaining fields
598        }; [$($prev:tt)*], // Already expanded fields
599        [$field:ident, [$($ftype:tt)*]&[$($ctype:tt)*]; $($done:tt)*] // Current
600    } => {
601        mp4box_gen! {
602            @cond_expand $version $flags;
603            $name $($stype)? {
604                [$($rtype,)*] $([if $rcond])*,
605                $($rest)* // Remaining fields
606            }; [$($prev)*], // Already expanded fields
607            [$field, [Either<$type,$($ftype)*>]&[Either<$type,[$($ctype)*]>]; $cond, $($done)*]
608        }
609    };
610
611    // Expansion TT
612    // Struct
613    { // Condition
614        @expand $version:ident $flags:ident;
615        $name:ident $($stype:ident)? {
616            $field:ident: [$($type:tt)*] {
617                $(
618                    $ifield:ident: $iftype:tt $({$($ifsdef:tt)+})? $([if $ifcond:expr])*
619                ),+ $(,)?
620            } [if $cond:expr],
621            $($rest:tt)* // Remaining fields
622        }; [$($prev:tt)*] // Already expanded fields
623    } => {
624        paste::paste! {
625            mp4box_gen! {
626                @expand $version $flags;
627                [<$name:camel $field:camel Type>] $($stype)? {
628                    $(
629                        $ifield: $iftype $({$($ifsdef)+})? $([if $ifcond])*,
630                    )+
631                }; []
632            }
633            mp4box_gen! {
634                @expand $version $flags;
635                $name $($stype)? {
636                    $($rest)* // Remaining fields
637                }; [
638                    $($prev)* // Already expanded fields
639                    [$field, [Option<Vec<[<Box $name:camel $field:camel Type>]>>]&[Option<[Vec<[<Box $name:camel $field:camel Type>], Option<[$($type)*]>>]>]; $cond,],
640                ]
641            }
642        }
643    };
644    { // No condition
645        @expand $version:ident $flags:ident;
646        $name:ident $($stype:ident)? {
647            $field:ident: [$type:ident] {
648                $(
649                    $ifield:ident: $iftype:tt $({$($ifsdef:tt)+})? $([if $ifcond:expr])*
650                ),+ $(,)?
651            },
652            $($rest:tt)* // Remaining fields
653        }; [$($prev:tt)*] // Already expanded fields
654    } => {
655        paste::paste! {
656            mp4box_gen! {
657                @expand $version $flags;
658                [<$name:camel $field:camel Type>] $($stype)? {
659                    $(
660                        $ifield: $iftype $({$($ifsdef)+})? $([if $ifcond])*,
661                    )+
662                }; []
663            }
664            mp4box_gen! {
665                @expand $version $flags;
666                $name $($stype)? {
667                    $($rest)* // Remaining fields
668                }; [
669                    $($prev)* // Already expanded fields
670                    [$field, [Vec<[<Box $name:camel $field:camel Type>]>]&[Vec<[<Box $name:camel $field:camel Type>], $type>]; ],
671                ]
672            }
673        }
674    };
675
676    // Non-struct
677    { // Multi-Condition
678        @expand $version:ident $flags:ident;
679        $name:ident $($stype:ident)? {
680            $field:ident: [$ftype:tt, $($type:tt),+ $(,)?] $([if $cond:expr])+,
681            $($rest:tt)* // Remaining fields
682        }; [$($prev:tt)*] // Already expanded fields
683    } => {
684        mp4box_gen! {
685            @cond_expand $version $flags;
686            $name $($stype)? {
687                [$($type,)*] $([if $cond])+,
688                $($rest)* // Remaining fields
689            }; [
690                $($prev)* // Already expanded fields
691            ], [$field, [$ftype]&[$ftype]; ]
692        }
693    };
694    { // Condition
695        @expand $version:ident $flags:ident;
696        $name:ident $($stype:ident)? {
697            $field:ident: $type:tt [if $cond:expr],
698            $($rest:tt)* // Remaining fields
699        }; [$($prev:tt)*] // Already expanded fields
700    } => {
701        mp4box_gen! {
702            @expand $version $flags;
703            $name $($stype)? {
704                $($rest)* // Remaining fields
705            }; [
706                $($prev)* // Already expanded fields
707                [$field, [Option<$type>]&[Option<[$type]>]; $cond,],
708            ]
709        }
710    };
711    { // No condition
712        @expand $version:ident $flags:ident;
713        $name:ident $($stype:ident)? {
714            $field:ident: Vec<$type:tt>,
715            $($rest:tt)* // Remaining fields
716        }; [$($prev:tt)*] // Already expanded fields
717    } => {
718        mp4box_gen! {
719            @expand $version $flags;
720            $name $($stype)? {
721                $($rest)* // Remaining fields
722            }; [
723                $($prev)* // Already expanded fields
724                [$field, [Vec<$type>]&[Vec<$type, Remain>];],
725            ]
726        }
727    };
728    { // No condition
729        @expand $version:ident $flags:ident;
730        $name:ident $($stype:ident)? {
731            $field:ident: $type:tt,
732            $($rest:tt)* // Remaining fields
733        }; [$($prev:tt)*] // Already expanded fields
734    } => {
735        mp4box_gen! {
736            @expand $version $flags;
737            $name $($stype)? {
738                $($rest)* // Remaining fields
739            }; [
740                $($prev)* // Already expanded fields
741                [$field, [$type]&[$type];],
742            ]
743        }
744    };
745
746    // Container parsing
747    {
748        @expand $version:ident $flags:ident;
749        $name:ident Container $type:tt
750    } => {
751        use $crate::base::*;
752        use $crate::r#macro::*;
753
754        paste::paste! {
755            pub struct [<Box $name>] {
756                pub data: Vec<$type>,
757            }
758            impl std::fmt::Debug for [<Box $name>] {
759                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
760                    if !f.alternate() {
761                        write!(f, "{}: {:?}", stringify!($name), self.data)
762                    } else {
763                        write!(f, "{}: [", stringify!($name))?;
764                        for item in &self.data {
765                            write!(f, "{:#?}, ", item)?;
766                        }
767                        write!(f, "]")
768                    }
769                }
770            }
771            impl Mp4BoxTrait for [<Box $name>] {
772                const TYPE: u32 = u32::from_ne_bytes(*bstringify::bstringify!([<$name:lower>]));
773
774                fn parse_full(input: &[u8], state: &mut ParserState) -> Self {
775                    Self::parse(input, state, &None)
776                }
777
778                fn parse(input: &[u8], state: &mut ParserState, _header: &Option<(u8, u32)>) -> Self {
779                    let mut data = vec![];
780                    while !is_empty(input, &state) {
781                        data.push(mp4box_gen!{ @read input state _header; $type });
782                    }
783
784                    Self { data }
785                }
786
787                fn write_full(&self, output: &mut Vec<u8>) {
788                    let mut data = Vec::new();
789                    self.write(&mut data);
790
791                    // Write header
792                    let size = data.len() as u32 + 8;
793                    output.extend_from_slice(&u32::to_be_bytes(size));
794                    output.extend_from_slice(&u32::to_ne_bytes(Self::TYPE));
795                    output.extend(data);
796                }
797
798                fn write(&self, output: &mut Vec<u8>) {
799                    for item in &self.data {
800                        mp4box_gen! { @write output item; &$type }
801                    }
802                }
803            }
804        }
805    };
806    {
807        @expand $version:ident $flags:ident;
808        $name:ident Container
809    } => {
810        mp4box_gen!{@expand $version $flags; $name Container Mp4Box}
811    };
812
813    // Skip box
814    {
815        @expand $version:ident $flags:ident;
816        $name:ident Skip
817    } => {
818        use $crate::base::*;
819        use $crate::r#macro::*;
820
821        paste::paste! {
822            pub struct [<Box $name>] {
823                pub data: Vec<u8>,
824            }
825            impl std::fmt::Debug for [<Box $name>] {
826                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
827                    write!(f, "{}: {:?}", stringify!($name), self.data)
828                }
829            }
830            impl Mp4BoxTrait for [<Box $name>] {
831                const TYPE: u32 = u32::from_ne_bytes(*bstringify::bstringify!([<$name:lower>]));
832
833                fn parse_full(input: &[u8], state: &mut ParserState) -> Self {
834                    Self::parse(input, state, &None)
835                }
836
837                fn parse(input: &[u8], state: &mut ParserState, _: &Option<(u8, u32)>) -> Self {
838                    // Read all data in box into data
839                    let data = read(input, state, input.len() - state.offset).unwrap().to_vec();
840
841                    Self { data }
842                }
843
844                fn write_full(&self, output: &mut Vec<u8>) {
845                    let mut data = Vec::new();
846                    self.write(&mut data);
847
848                    // Write header
849                    let size = data.len() as u32 + 8;
850                    output.extend_from_slice(&u32::to_be_bytes(size));
851                    output.extend_from_slice(&u32::to_ne_bytes(Self::TYPE));
852                    output.extend(data);
853                }
854
855                fn write(&self, output: &mut Vec<u8>) {
856                    output.extend(&self.data);
857                }
858            }
859        }
860    };
861
862    {
863        $version:ident $flags:ident;
864        $($sname:ident $(: $stype:ident $(= $svtype:tt)?)? $({
865            $(
866                $field:ident: $ftype:tt$(<$iftype:tt>)? $({$($fsdef:tt)+})? $([if $fcond:expr])*
867            ),+ $(,)?
868        })?),* $(,)? // Trailing comma may be omitted
869    } => {
870        $(mp4box_gen! {
871            @expand $version $flags;
872            $sname $($stype $($svtype)?)? $({
873                $(
874                    $field: $ftype$(<$iftype>)? $({$($fsdef)+})? $([if $fcond])*,
875                )+
876            }; [])?
877        })*
878
879        paste::paste! {
880            pub enum Mp4Box {
881                $( $sname(Box<[<Box $sname>]>), )*
882            }
883            impl std::fmt::Debug for Mp4Box {
884                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
885                    match self {
886                        $( Mp4Box::$sname(box_) => write!(f, "{:?}", box_), )*
887                    }
888                }
889            }
890
891            impl Mp4Box {
892                pub(crate) fn write(&self, output: &mut Vec<u8>) {
893                    match self {
894                        $( Mp4Box::$sname(box_) => box_.write_full(output), )*
895                    }
896                }
897            }
898
899            pub(crate) fn parse_box(input: &[u8], state: &mut ParserState) -> Option<Mp4Box> {
900                assert!(!is_empty(input, state));
901                let data = read_header(input, state);
902
903                match data.0 {
904                    $([<Box $sname>]::TYPE => {
905                        Some(Mp4Box::$sname(Box::new([<Box $sname>]::parse_full(data.1, &mut ParserState { offset: 0 }))))
906                    })*
907                    _ => panic!("Unknown box type: {}", String::from_utf8_lossy(&u32::to_ne_bytes(data.0))),
908                }
909            }
910            pub(crate) fn is_box_type(box_: &Mp4Box, type_: u32) -> bool {
911                match box_ {
912                    $(Mp4Box::$sname(_) => [<Box $sname>]::TYPE == type_),*
913                }
914            }
915            pub(crate) fn get_box_type(box_: &Mp4Box) -> String {
916                let int = match box_ {
917                    $(Mp4Box::$sname(_) => [<Box $sname>]::TYPE),*
918                };
919                String::from_utf8(u32::to_ne_bytes(int).to_vec()).unwrap()
920            }
921        }
922    };
923}
924
925// crate visibility
926pub(crate) use mp4box_gen;