1use arrayvec::ArrayVec;
11use nom::{
12 branch::alt,
13 bytes::complete::tag,
14 character::complete::{char, digit1, line_ending, multispace0, multispace1, space0, space1},
15 combinator::{eof, map_res, opt, recognize},
16 error::{Error as NomError, ErrorKind, FromExternalError, ParseError},
17 multi::{many1, separated_list0, separated_list1},
18 sequence::{delimited, preceded},
19 AsChar, Compare, Err as NomErr, IResult, Input, Parser,
20};
21
22use crate::{GrainTableSegment, NUM_UV_COEFFS, NUM_UV_POINTS, NUM_Y_COEFFS, NUM_Y_POINTS};
23
24#[inline]
51pub fn parse_grain_table(input: &str) -> anyhow::Result<Vec<GrainTableSegment>> {
52 let (input, _) = grain_table_header(input).map_err(|e| anyhow::anyhow!(e.to_string()))?;
53 let (_, segments) = many1(grain_table_segment)
54 .parse(input)
55 .map_err(|e| anyhow::anyhow!(e.to_string()))?;
56 Ok(segments.into_iter().flatten().collect())
57}
58
59fn grain_table_header(input: &str) -> IResult<&str, ()> {
60 let (input, _) = delimited(multispace0, tag("filmgrn1"), line_ending).parse(input)?;
61 Ok((input, ()))
62}
63
64fn line<I, O, E: ParseError<I>, F>(parser: F) -> impl Parser<I, Output = O, Error = E>
65where
66 I: Input + Clone + Compare<&'static str>,
67 <I as Input>::Item: AsChar + Clone,
68 F: Parser<I, Output = O, Error = E>,
69{
70 delimited(multispace0, parser, alt((line_ending, eof)))
71}
72
73fn grain_table_segment(input: &str) -> IResult<&str, Option<GrainTableSegment>> {
74 let (input, e_params) = e_params(input)?;
75 if !e_params.apply {
76 return Ok((input, None));
79 }
80
81 let (input, p_params) = p_params(input)?;
82 let (input, s_y_params) = s_y_params(input)?;
83 let (input, s_cb_params) = s_cb_params(input)?;
84 let (input, s_cr_params) = s_cr_params(input)?;
85 let coeff_count = (2 * p_params.ar_coeff_lag * (p_params.ar_coeff_lag + 1)) as usize;
86 let (input, c_y_params) = c_y_params(input, coeff_count)?;
87 let (input, c_cb_params) = c_cb_params(input, coeff_count)?;
88 let (input, c_cr_params) = c_cr_params(input, coeff_count)?;
89
90 Ok((
91 input,
92 Some(GrainTableSegment {
93 start_time: e_params.start,
94 end_time: e_params.end,
95 scaling_points_y: s_y_params,
96 scaling_points_cb: s_cb_params,
97 scaling_points_cr: s_cr_params,
98 scaling_shift: p_params.scaling_shift,
99 ar_coeff_lag: p_params.ar_coeff_lag,
100 ar_coeffs_y: c_y_params,
101 ar_coeffs_cb: c_cb_params,
102 ar_coeffs_cr: c_cr_params,
103 ar_coeff_shift: p_params.ar_coeff_shift,
104 cb_mult: p_params.cb_mult,
105 cb_luma_mult: p_params.cb_luma_mult,
106 cb_offset: p_params.cb_offset,
107 cr_mult: p_params.cr_mult,
108 cr_luma_mult: p_params.cr_luma_mult,
109 cr_offset: p_params.cr_offset,
110 overlap_flag: p_params.overlap_flag,
111 chroma_scaling_from_luma: p_params.chroma_scaling_from_luma,
112 grain_scale_shift: p_params.grain_scale_shift,
113 random_seed: e_params.seed,
114 }),
115 ))
116}
117
118#[derive(Debug, Clone, Copy)]
119struct EParams {
120 pub start: u64,
121 pub end: u64,
122 pub apply: bool,
123 pub seed: u16,
124}
125
126fn e_params(input: &str) -> IResult<&str, EParams> {
127 let (input, params) = map_res(
128 line(preceded(
129 tag("E"),
130 preceded(space1, separated_list1(space1, digit1)),
131 )),
132 |items: Vec<&str>| {
133 if items.len() != 5 {
134 return Err(NomErr::<NomError<&str>>::Failure(
135 NomError::from_external_error(
136 input,
137 ErrorKind::Verify,
138 "Expected 5 values on E line",
139 ),
140 ));
141 }
142 let parsed = EParams {
143 start: items[0].parse().map_err(|_e| {
144 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
145 input,
146 ErrorKind::Digit,
147 "Failed to parse start_time",
148 ))
149 })?,
150 end: items[1].parse().map_err(|_e| {
151 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
152 input,
153 ErrorKind::Digit,
154 "Failed to parse end_time",
155 ))
156 })?,
157 apply: items[2].parse::<u8>().map_err(|_e| {
158 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
159 input,
160 ErrorKind::Digit,
161 "Failed to parse apply_grain",
162 ))
163 })? > 0,
164 seed: items[3].parse().map_err(|_e| {
165 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
166 input,
167 ErrorKind::Digit,
168 "Failed to parse random_seed",
169 ))
170 })?,
171 };
172 Ok::<_, NomErr<NomError<&str>>>(parsed)
173 },
174 )
175 .parse(input)?;
176
177 if params.end < params.start {
178 return Err(NomErr::Failure(NomError::from_external_error(
179 input,
180 ErrorKind::Verify,
181 "Start time must be before end time",
182 )));
183 }
184
185 Ok((input, params))
186}
187
188#[derive(Debug, Clone, Copy)]
189struct PParams {
190 ar_coeff_lag: u8,
191 ar_coeff_shift: u8,
192 grain_scale_shift: u8,
193 scaling_shift: u8,
194 chroma_scaling_from_luma: bool,
195 overlap_flag: bool,
196 cb_mult: u8,
197 cb_luma_mult: u8,
198 cb_offset: u16,
199 cr_mult: u8,
200 cr_luma_mult: u8,
201 cr_offset: u16,
202}
203
204#[allow(clippy::too_many_lines)]
205fn p_params(input: &str) -> IResult<&str, PParams> {
206 let (input, params) = map_res(
207 line(preceded(
208 tag("p"),
209 preceded(space1, separated_list1(space1, digit1)),
210 )),
211 |items: Vec<&str>| {
212 if items.len() != 12 {
213 return Err(NomErr::<NomError<&str>>::Failure(
214 NomError::from_external_error(
215 input,
216 ErrorKind::Verify,
217 "Expected 12 values on p line",
218 ),
219 ));
220 }
221
222 let parsed = PParams {
223 ar_coeff_lag: items[0].parse().map_err(|_e| {
224 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
225 input,
226 ErrorKind::Digit,
227 "Failed to parse ar_coeff_lag",
228 ))
229 })?,
230 ar_coeff_shift: items[1].parse().map_err(|_e| {
231 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
232 input,
233 ErrorKind::Digit,
234 "Failed to parse ar_coeff_shift",
235 ))
236 })?,
237 grain_scale_shift: items[2].parse().map_err(|_e| {
238 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
239 input,
240 ErrorKind::Digit,
241 "Failed to parse grain_scale_shift",
242 ))
243 })?,
244 scaling_shift: items[3].parse().map_err(|_e| {
245 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
246 input,
247 ErrorKind::Digit,
248 "Failed to parse scaling_shift",
249 ))
250 })?,
251 chroma_scaling_from_luma: items[4].parse::<u8>().map_err(|_e| {
252 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
253 input,
254 ErrorKind::Digit,
255 "Failed to parse chroma_scaling_from_luma",
256 ))
257 })? > 0,
258 overlap_flag: items[5].parse::<u8>().map_err(|_e| {
259 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
260 input,
261 ErrorKind::Digit,
262 "Failed to parse overlap_flag",
263 ))
264 })? > 0,
265 cb_mult: items[6].parse().map_err(|_e| {
266 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
267 input,
268 ErrorKind::Digit,
269 "Failed to parse cb_mult",
270 ))
271 })?,
272 cb_luma_mult: items[7].parse().map_err(|_e| {
273 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
274 input,
275 ErrorKind::Digit,
276 "Failed to parse cb_luma_mult",
277 ))
278 })?,
279 cb_offset: items[8].parse().map_err(|_e| {
280 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
281 input,
282 ErrorKind::Digit,
283 "Failed to parse cb_offset",
284 ))
285 })?,
286 cr_mult: items[9].parse().map_err(|_e| {
287 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
288 input,
289 ErrorKind::Digit,
290 "Failed to parse cr_mult",
291 ))
292 })?,
293 cr_luma_mult: items[10].parse().map_err(|_e| {
294 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
295 input,
296 ErrorKind::Digit,
297 "Failed to parse cr_luma_mult",
298 ))
299 })?,
300 cr_offset: items[11].parse().map_err(|_e| {
301 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
302 input,
303 ErrorKind::Digit,
304 "Failed to parse cr_offset",
305 ))
306 })?,
307 };
308 Ok::<_, NomErr<NomError<&str>>>(parsed)
309 },
310 )
311 .parse(input)?;
312
313 if params.scaling_shift < 8 || params.scaling_shift > 11 {
314 return Err(NomErr::Failure(NomError::from_external_error(
315 input,
316 ErrorKind::Verify,
317 "scaling_shift must be between 8 and 11",
318 )));
319 }
320 if params.ar_coeff_lag > 3 {
321 return Err(NomErr::Failure(NomError::from_external_error(
322 input,
323 ErrorKind::Verify,
324 "ar_coeff_lag must be between 0 and 3",
325 )));
326 }
327 if params.ar_coeff_shift < 6 || params.ar_coeff_shift > 9 {
328 return Err(NomErr::Failure(NomError::from_external_error(
329 input,
330 ErrorKind::Verify,
331 "ar_coeff_shift must be between 6 and 9",
332 )));
333 }
334
335 Ok((input, params))
336}
337
338fn s_y_params(input: &str) -> IResult<&str, ArrayVec<[u8; 2], NUM_Y_POINTS>> {
339 let (input, values) = map_res(
340 line(preceded(
341 tag("sY"),
342 preceded(space1, separated_list1(space1, digit1)),
343 )),
344 |items: Vec<&str>| {
345 let mut parsed = Vec::with_capacity(items.len());
346 for item in items {
347 parsed.push(item.parse::<u8>().map_err(|_e| {
348 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
349 input,
350 ErrorKind::Digit,
351 "Failed to parse Y-plane points",
352 ))
353 })?);
354 }
355 Ok::<_, NomErr<NomError<&str>>>(parsed)
356 },
357 )
358 .parse(input)?;
359
360 let len = values[0] as usize;
361 if values.len() != len * 2 + 1 {
362 return Err(NomErr::Failure(NomError::from_external_error(
363 input,
364 ErrorKind::Verify,
365 format!(
366 "Expected {} Y-plane points, got {}",
367 len * 2,
368 values.len() - 1
369 ),
370 )));
371 }
372
373 Ok((
374 input,
375 values[1..]
376 .chunks_exact(2)
377 .map(|chunk| [chunk[0], chunk[1]])
378 .collect(),
379 ))
380}
381
382fn s_cb_params(input: &str) -> IResult<&str, ArrayVec<[u8; 2], NUM_UV_POINTS>> {
383 let (input, values) = map_res(
384 line(preceded(
385 tag("sCb"),
386 preceded(space1, separated_list1(space1, digit1)),
387 )),
388 |items: Vec<&str>| {
389 let mut parsed = Vec::with_capacity(items.len());
390 for item in items {
391 parsed.push(item.parse::<u8>().map_err(|_e| {
392 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
393 input,
394 ErrorKind::Digit,
395 "Failed to parse Cb-plane points",
396 ))
397 })?);
398 }
399 Ok::<_, NomErr<NomError<&str>>>(parsed)
400 },
401 )
402 .parse(input)?;
403
404 let len = values[0] as usize;
405 if values.len() != len * 2 + 1 {
406 return Err(NomErr::Failure(NomError::from_external_error(
407 input,
408 ErrorKind::Verify,
409 format!(
410 "Expected {} Cb-plane points, got {}",
411 len * 2,
412 values.len() - 1
413 ),
414 )));
415 }
416
417 Ok((
418 input,
419 values[1..]
420 .chunks_exact(2)
421 .map(|chunk| [chunk[0], chunk[1]])
422 .collect(),
423 ))
424}
425
426fn s_cr_params(input: &str) -> IResult<&str, ArrayVec<[u8; 2], NUM_UV_POINTS>> {
427 let (input, values) = map_res(
428 line(preceded(
429 tag("sCr"),
430 preceded(space1, separated_list1(space1, digit1)),
431 )),
432 |items: Vec<&str>| {
433 let mut parsed = Vec::with_capacity(items.len());
434 for item in items {
435 parsed.push(item.parse::<u8>().map_err(|_e| {
436 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
437 input,
438 ErrorKind::Digit,
439 "Failed to parse Cr-plane points",
440 ))
441 })?);
442 }
443 Ok::<_, NomErr<NomError<&str>>>(parsed)
444 },
445 )
446 .parse(input)?;
447
448 let len = values[0] as usize;
449 if values.len() != len * 2 + 1 {
450 return Err(NomErr::Failure(NomError::from_external_error(
451 input,
452 ErrorKind::Verify,
453 format!(
454 "Expected {} Cr-plane points, got {}",
455 len * 2,
456 values.len() - 1
457 ),
458 )));
459 }
460
461 Ok((
462 input,
463 values[1..]
464 .chunks_exact(2)
465 .map(|chunk| [chunk[0], chunk[1]])
466 .collect(),
467 ))
468}
469
470fn integer(input: &str) -> IResult<&str, &str> {
471 recognize(preceded(opt(char('-')), digit1)).parse(input)
472}
473
474fn c_y_params(input: &str, count: usize) -> IResult<&str, ArrayVec<i8, NUM_Y_COEFFS>> {
475 let (input, values) = map_res(
476 line(preceded(
477 tag("cY"),
478 preceded(space0, separated_list0(multispace1, integer)),
479 )),
480 |items: Vec<&str>| {
481 let mut parsed = Vec::with_capacity(items.len());
482 for item in items {
483 parsed.push(item.parse::<i8>().map_err(|_e| {
484 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
485 input,
486 ErrorKind::Digit,
487 "Failed to parse Y-plane coeffs",
488 ))
489 })?);
490 }
491 Ok::<_, NomErr<NomError<&str>>>(parsed)
492 },
493 )
494 .parse(input)?;
495
496 if values.len() != count {
497 return Err(NomErr::Failure(NomError::from_external_error(
498 input,
499 ErrorKind::Verify,
500 format!("Expected {} Y-plane coeffs, got {}", count, values.len()),
501 )));
502 }
503
504 Ok((input, values.into_iter().collect()))
505}
506
507fn c_cb_params(input: &str, count: usize) -> IResult<&str, ArrayVec<i8, NUM_UV_COEFFS>> {
508 let (input, values) = map_res(
509 line(preceded(
510 tag("cCb"),
511 preceded(space1, separated_list1(multispace1, integer)),
512 )),
513 |items: Vec<&str>| {
514 let mut parsed = Vec::with_capacity(items.len());
515 for item in items {
516 parsed.push(item.parse::<i8>().map_err(|_e| {
517 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
518 input,
519 ErrorKind::Digit,
520 "Failed to parse Cb-plane coeffs",
521 ))
522 })?);
523 }
524 Ok::<_, NomErr<NomError<&str>>>(parsed)
525 },
526 )
527 .parse(input)?;
528
529 if values.len() != count + 1 {
530 return Err(NomErr::Failure(NomError::from_external_error(
531 input,
532 ErrorKind::Verify,
533 format!(
534 "Expected {} Cb-plane coeffs, got {}",
535 count + 1,
536 values.len()
537 ),
538 )));
539 }
540
541 Ok((input, values.into_iter().collect()))
542}
543
544fn c_cr_params(input: &str, count: usize) -> IResult<&str, ArrayVec<i8, NUM_UV_COEFFS>> {
545 let (input, values) = map_res(
546 line(preceded(
547 tag("cCr"),
548 preceded(space1, separated_list1(multispace1, integer)),
549 )),
550 |items: Vec<&str>| {
551 let mut parsed = Vec::with_capacity(items.len());
552 for item in items {
553 parsed.push(item.parse::<i8>().map_err(|_e| {
554 NomErr::<NomError<&str>>::Failure(NomError::from_external_error(
555 input,
556 ErrorKind::Digit,
557 "Failed to parse Cr-plane coeffs",
558 ))
559 })?);
560 }
561 Ok::<_, NomErr<NomError<&str>>>(parsed)
562 },
563 )
564 .parse(input)?;
565
566 if values.len() != count + 1 {
567 return Err(NomErr::Failure(NomError::from_external_error(
568 input,
569 ErrorKind::Verify,
570 format!(
571 "Expected {} Cr-plane coeffs, got {}",
572 count + 1,
573 values.len()
574 ),
575 )));
576 }
577
578 Ok((input, values.into_iter().collect()))
579}
580
581#[cfg(test)]
582mod tests {
583 use super::*;
584
585 #[test]
586 fn parse_luma_only_table() {
587 let input = r#"filmgrn1
590E 0 9223372036854775807 1 7391 1
591 p 0 6 0 8 0 1 0 0 0 0 0 0
592 sY 14 0 20 20 5 39 4 59 3 78 3 98 3 118 3 137 3 157 3 177 3 196 3 216 4 235 4 255 4
593 sCb 0
594 sCr 0
595 cY
596 cCb 0
597 cCr 0
598"#;
599 let expected = GrainTableSegment {
600 start_time: 0,
601 end_time: 9_223_372_036_854_775_807,
602 scaling_points_y: ArrayVec::from([
603 [0, 20],
604 [20, 5],
605 [39, 4],
606 [59, 3],
607 [78, 3],
608 [98, 3],
609 [118, 3],
610 [137, 3],
611 [157, 3],
612 [177, 3],
613 [196, 3],
614 [216, 4],
615 [235, 4],
616 [255, 4],
617 ]),
618 scaling_points_cb: ArrayVec::new(),
619 scaling_points_cr: ArrayVec::new(),
620 scaling_shift: 8,
621 ar_coeff_lag: 0,
622 ar_coeffs_y: ArrayVec::new(),
623 ar_coeffs_cb: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity"),
624 ar_coeffs_cr: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity"),
625 ar_coeff_shift: 6,
626 cb_mult: 0,
627 cb_luma_mult: 0,
628 cb_offset: 0,
629 cr_mult: 0,
630 cr_luma_mult: 0,
631 cr_offset: 0,
632 overlap_flag: true,
633 chroma_scaling_from_luma: false,
634 grain_scale_shift: 0,
635 random_seed: 7391,
636 };
637 let output = parse_grain_table(input).expect("Test failed");
638 assert_eq!(vec![expected], output);
639 }
640
641 #[test]
642 fn parse_luma_chroma_table() {
643 let input = r#"filmgrn1
646E 0 9223372036854775807 1 7391 1
647 p 0 6 0 8 0 1 128 192 256 128 192 256
648 sY 14 0 0 20 4 39 3 59 3 78 3 98 3 118 4 137 4 157 4 177 4 196 4 216 5 235 5 255 5
649 sCb 10 0 0 28 0 57 0 85 0 113 0 142 0 170 0 198 0 227 0 255 1
650 sCr 10 0 0 28 0 57 0 85 0 113 0 142 0 170 0 198 0 227 0 255 1
651 cY
652 cCb 0
653 cCr 0
654"#;
655 let expected = GrainTableSegment {
656 start_time: 0,
657 end_time: 9_223_372_036_854_775_807,
658 scaling_points_y: ArrayVec::from([
659 [0, 0],
660 [20, 4],
661 [39, 3],
662 [59, 3],
663 [78, 3],
664 [98, 3],
665 [118, 4],
666 [137, 4],
667 [157, 4],
668 [177, 4],
669 [196, 4],
670 [216, 5],
671 [235, 5],
672 [255, 5],
673 ]),
674 scaling_points_cb: ArrayVec::from([
675 [0, 0],
676 [28, 0],
677 [57, 0],
678 [85, 0],
679 [113, 0],
680 [142, 0],
681 [170, 0],
682 [198, 0],
683 [227, 0],
684 [255, 1],
685 ]),
686 scaling_points_cr: ArrayVec::from([
687 [0, 0],
688 [28, 0],
689 [57, 0],
690 [85, 0],
691 [113, 0],
692 [142, 0],
693 [170, 0],
694 [198, 0],
695 [227, 0],
696 [255, 1],
697 ]),
698 scaling_shift: 8,
699 ar_coeff_lag: 0,
700 ar_coeffs_y: ArrayVec::new(),
701 ar_coeffs_cb: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity"),
702 ar_coeffs_cr: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity"),
703 ar_coeff_shift: 6,
704 cb_mult: 128,
705 cb_luma_mult: 192,
706 cb_offset: 256,
707 cr_mult: 128,
708 cr_luma_mult: 192,
709 cr_offset: 256,
710 overlap_flag: true,
711 chroma_scaling_from_luma: false,
712 grain_scale_shift: 0,
713 random_seed: 7391,
714 };
715 let output = parse_grain_table(input).expect("Test failed");
716 assert_eq!(vec![expected], output);
717 }
718
719 #[test]
720 fn parse_complex_table() {
721 let input = r#"filmgrn1
722E 0 417083 1 7391 1
723 p 3 7 0 11 0 1 128 192 256 128 192 256
724 sY 6 0 53 13 53 40 64 94 49 121 46 255 46
725 sCb 2 0 14 255 13
726 sCr 2 0 12 255 14
727 cY 1 -4 1 4 8 3 -2 -6 9 14 -27 -25 -2 4 5 15 -80 94 28 -3 -2 6 -47 121
728 cCb -3 1 -4 6 -1 2 -2 1 11 -10 -2 -16 -1 3 -2 -14 -26 65 19 -3 -5 2 -6 75 -1
729 cCr 0 0 -4 8 -1 0 1 2 -1 -9 4 -7 -5 -2 -5 -14 0 45 18 3 -3 4 8 49 5
730E 417083 7090416 1 0 1
731 p 3 7 0 11 0 1 128 192 256 128 192 256
732 sY 4 0 46 40 54 108 39 255 38
733 sCb 2 0 14 255 14
734 sCr 2 0 12 255 14
735 cY 1 -4 1 5 8 4 -2 -6 9 13 -28 -28 -5 5 5 13 -76 91 32 -1 -3 7 -50 124
736 cCb -2 1 -3 3 -2 1 -1 2 8 -10 0 -12 -2 2 -1 -14 -20 61 18 -1 -4 -2 -1 70 -1
737 cCr 0 0 -3 6 -1 -1 0 1 -2 -8 6 -4 -5 -2 -6 -12 4 41 17 4 -2 3 13 44 5
738E 7090416 7507500 1 0 1
739 p 3 7 0 11 0 1 128 192 256 128 192 256
740 sY 4 0 54 40 64 108 46 255 44
741 sCb 2 0 14 255 13
742 sCr 2 0 12 255 14
743 cY 1 -4 2 3 7 3 -2 -6 9 14 -26 -25 -3 5 6 15 -81 95 27 -3 -3 5 -46 121
744 cCb -2 1 -4 4 -2 1 -1 2 9 -12 3 -13 -1 2 -2 -16 -26 66 17 -2 -5 -1 1 73 0
745 cCr 1 -1 -5 8 -1 -1 1 1 -3 -9 9 -5 -6 -2 -7 -14 1 44 17 3 -3 5 15 46 4
746E 7507500 10010000 1 0 1
747 p 3 7 0 11 0 1 128 192 256 128 192 256
748 sY 4 0 49 40 59 108 43 255 41
749 sCb 2 0 14 255 14
750 sCr 2 0 13 255 15
751 cY 1 -4 0 6 8 3 -2 -5 8 14 -29 -26 -3 4 3 15 -76 92 29 -2 -3 8 -49 121
752 cCb -3 0 -3 6 0 1 -2 1 10 -9 -4 -15 -1 2 -1 -13 -22 62 20 -3 -4 2 -7 73 -1
753 cCr -1 0 -3 6 0 0 0 2 0 -9 2 -7 -5 -1 -4 -14 0 45 19 2 -2 3 7 50 4
754E 10010000 13346666 1 0 1
755 p 3 7 0 11 0 1 128 192 256 128 192 256
756 sY 6 0 33 27 39 40 53 54 55 108 52 255 52
757 sCb 2 0 16 255 14
758 sCr 2 0 11 255 12
759 cY 1 -4 1 5 9 4 -2 -7 12 11 -27 -30 -5 5 6 10 -73 89 35 -1 -3 6 -49 124
760 cCb -2 0 -2 1 -2 1 -2 0 9 -9 -2 -14 -1 2 0 -11 -26 65 18 -2 -4 -2 -8 75 -5
761 cCr 0 0 -4 5 -2 0 1 3 -1 -9 6 -5 -5 -1 -6 -14 1 43 18 4 -3 3 13 49 3
762E 13346666 16683333 1 0 1
763 p 3 7 0 11 0 1 128 192 256 128 192 256
764 sY 6 0 36 27 42 40 58 54 60 108 57 255 57
765 sCb 2 0 15 255 14
766 sCr 4 0 11 40 17 94 13 255 13
767 cY 1 -4 1 5 8 3 -2 -6 10 12 -27 -27 -4 4 5 12 -73 90 32 -2 -3 6 -47 121
768 cCb -2 0 -3 4 -1 1 -2 0 10 -9 -2 -14 1 3 -1 -10 -24 62 16 -2 -4 0 -6 72 -7
769 cCr 0 0 -3 6 -1 0 1 3 1 -9 3 -7 -5 -1 -5 -14 -2 46 19 2 -3 3 7 54 3
770E 16683333 17100416 1 0 1
771 p 3 7 0 11 0 1 128 192 256 128 192 256
772 sY 7 0 41 13 41 27 49 40 66 54 68 108 65 255 65
773 sCb 2 0 18 255 14
774 sCr 4 0 11 40 18 67 14 255 13
775 cY 0 -3 1 4 7 3 -2 -5 7 13 -27 -23 -3 4 5 15 -79 94 26 -3 -2 5 -45 120
776 cCb -1 -2 -1 1 0 0 -3 -2 12 -6 -3 -15 3 2 2 -8 -42 75 12 -3 -4 -2 -8 82 -3
777 cCr 0 0 -5 7 -2 0 1 3 0 -11 6 -7 -5 -1 -6 -15 -5 48 18 2 -3 3 10 55 2
778E 17100416 20020000 1 0 1
779 p 3 7 0 11 0 1 128 192 256 128 192 256
780 sY 6 0 37 27 44 40 61 54 63 108 60 255 60
781 sCb 2 0 14 255 14
782 sCr 4 0 11 40 18 94 13 255 13
783 cY 1 -3 0 6 7 2 -1 -5 7 13 -28 -25 -2 3 3 13 -73 91 29 -2 -2 7 -47 119
784 cCb -2 -1 -3 4 0 1 -2 -1 11 -7 -6 -15 1 2 -1 -9 -25 63 16 -3 -4 2 -11 73 -8
785 cCr -1 1 -2 6 0 1 0 2 3 -9 -2 -10 -4 0 -3 -14 -6 50 20 0 -3 3 -1 59 3
786E 20020000 9223372036854775807 1 0 1
787 p 3 6 0 11 0 1 128 192 256 128 192 256
788 sY 6 0 32 27 37 40 50 54 52 121 49 255 49
789 sCb 4 0 21 40 23 81 17 255 15
790 sCr 2 0 11 255 12
791 cY 1 -3 1 2 5 3 -2 -6 8 6 -12 -18 -2 3 5 7 -42 44 21 -3 -1 4 -29 67
792 cCb -1 0 1 0 -1 0 -1 0 5 -4 -3 -9 1 1 2 -4 -21 39 10 -2 -3 -2 -7 44 1
793 cCr 1 0 -3 2 -3 -1 0 1 -1 -4 5 -2 -1 -1 -5 -6 3 20 10 4 -2 0 9 23 -1"#;
794 let output = parse_grain_table(input);
795 assert!(output.is_ok());
796 }
797}