diff options
Diffstat (limited to 'third_party/rust/nom/tests')
-rw-r--r-- | third_party/rust/nom/tests/arithmetic.rs | 94 | ||||
-rw-r--r-- | third_party/rust/nom/tests/arithmetic_ast.rs | 161 | ||||
-rw-r--r-- | third_party/rust/nom/tests/css.rs | 45 | ||||
-rw-r--r-- | third_party/rust/nom/tests/custom_errors.rs | 48 | ||||
-rw-r--r-- | third_party/rust/nom/tests/escaped.rs | 28 | ||||
-rw-r--r-- | third_party/rust/nom/tests/float.rs | 46 | ||||
-rw-r--r-- | third_party/rust/nom/tests/fnmut.rs | 39 | ||||
-rw-r--r-- | third_party/rust/nom/tests/ini.rs | 207 | ||||
-rw-r--r-- | third_party/rust/nom/tests/ini_str.rs | 217 | ||||
-rw-r--r-- | third_party/rust/nom/tests/issues.rs | 242 | ||||
-rw-r--r-- | third_party/rust/nom/tests/json.rs | 236 | ||||
-rw-r--r-- | third_party/rust/nom/tests/mp4.rs | 320 | ||||
-rw-r--r-- | third_party/rust/nom/tests/multiline.rs | 31 | ||||
-rw-r--r-- | third_party/rust/nom/tests/overflow.rs | 145 | ||||
-rw-r--r-- | third_party/rust/nom/tests/reborrow_fold.rs | 31 |
15 files changed, 1890 insertions, 0 deletions
diff --git a/third_party/rust/nom/tests/arithmetic.rs b/third_party/rust/nom/tests/arithmetic.rs new file mode 100644 index 0000000000..5b627a97a5 --- /dev/null +++ b/third_party/rust/nom/tests/arithmetic.rs @@ -0,0 +1,94 @@ +use nom::{ + branch::alt, + bytes::complete::tag, + character::complete::char, + character::complete::{digit1 as digit, space0 as space}, + combinator::map_res, + multi::fold_many0, + sequence::{delimited, pair}, + IResult, +}; + +// Parser definition + +use std::str::FromStr; + +// We parse any expr surrounded by parens, ignoring all whitespaces around those +fn parens(i: &str) -> IResult<&str, i64> { + delimited(space, delimited(tag("("), expr, tag(")")), space)(i) +} + +// We transform an integer string into a i64, ignoring surrounding whitespaces +// We look for a digit suite, and try to convert it. +// If either str::from_utf8 or FromStr::from_str fail, +// we fallback to the parens parser defined above +fn factor(i: &str) -> IResult<&str, i64> { + alt(( + map_res(delimited(space, digit, space), FromStr::from_str), + parens, + ))(i) +} + +// We read an initial factor and for each time we find +// a * or / operator followed by another factor, we do +// the math by folding everything +fn term(i: &str) -> IResult<&str, i64> { + let (i, init) = factor(i)?; + + fold_many0( + pair(alt((char('*'), char('/'))), factor), + move || init, + |acc, (op, val): (char, i64)| { + if op == '*' { + acc * val + } else { + acc / val + } + }, + )(i) +} + +fn expr(i: &str) -> IResult<&str, i64> { + let (i, init) = term(i)?; + + fold_many0( + pair(alt((char('+'), char('-'))), term), + move || init, + |acc, (op, val): (char, i64)| { + if op == '+' { + acc + val + } else { + acc - val + } + }, + )(i) +} + +#[test] +fn factor_test() { + assert_eq!(factor("3"), Ok(("", 3))); + assert_eq!(factor(" 12"), Ok(("", 12))); + assert_eq!(factor("537 "), Ok(("", 537))); + assert_eq!(factor(" 24 "), Ok(("", 24))); +} + +#[test] +fn term_test() { + assert_eq!(term(" 12 *2 / 3"), Ok(("", 8))); + assert_eq!(term(" 2* 3 *2 *2 / 3"), Ok(("", 8))); + assert_eq!(term(" 48 / 3/2"), Ok(("", 8))); +} + +#[test] +fn expr_test() { + assert_eq!(expr(" 1 + 2 "), Ok(("", 3))); + assert_eq!(expr(" 12 + 6 - 4+ 3"), Ok(("", 17))); + assert_eq!(expr(" 1 + 2*3 + 4"), Ok(("", 11))); +} + +#[test] +fn parens_test() { + assert_eq!(expr(" ( 2 )"), Ok(("", 2))); + assert_eq!(expr(" 2* ( 3 + 4 ) "), Ok(("", 14))); + assert_eq!(expr(" 2*2 / ( 5 - 1) + 3"), Ok(("", 4))); +} diff --git a/third_party/rust/nom/tests/arithmetic_ast.rs b/third_party/rust/nom/tests/arithmetic_ast.rs new file mode 100644 index 0000000000..ca15110960 --- /dev/null +++ b/third_party/rust/nom/tests/arithmetic_ast.rs @@ -0,0 +1,161 @@ +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; + +use std::str::FromStr; + +use nom::{ + branch::alt, + bytes::complete::tag, + character::complete::{digit1 as digit, multispace0 as multispace}, + combinator::{map, map_res}, + multi::many0, + sequence::{delimited, preceded}, + IResult, +}; + +pub enum Expr { + Value(i64), + Add(Box<Expr>, Box<Expr>), + Sub(Box<Expr>, Box<Expr>), + Mul(Box<Expr>, Box<Expr>), + Div(Box<Expr>, Box<Expr>), + Paren(Box<Expr>), +} + +#[derive(Debug)] +pub enum Oper { + Add, + Sub, + Mul, + Div, +} + +impl Display for Expr { + fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result { + use self::Expr::*; + match *self { + Value(val) => write!(format, "{}", val), + Add(ref left, ref right) => write!(format, "{} + {}", left, right), + Sub(ref left, ref right) => write!(format, "{} - {}", left, right), + Mul(ref left, ref right) => write!(format, "{} * {}", left, right), + Div(ref left, ref right) => write!(format, "{} / {}", left, right), + Paren(ref expr) => write!(format, "({})", expr), + } + } +} + +impl Debug for Expr { + fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result { + use self::Expr::*; + match *self { + Value(val) => write!(format, "{}", val), + Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right), + Sub(ref left, ref right) => write!(format, "({:?} - {:?})", left, right), + Mul(ref left, ref right) => write!(format, "({:?} * {:?})", left, right), + Div(ref left, ref right) => write!(format, "({:?} / {:?})", left, right), + Paren(ref expr) => write!(format, "[{:?}]", expr), + } + } +} + +fn parens(i: &str) -> IResult<&str, Expr> { + delimited( + multispace, + delimited(tag("("), map(expr, |e| Expr::Paren(Box::new(e))), tag(")")), + multispace, + )(i) +} + +fn factor(i: &str) -> IResult<&str, Expr> { + alt(( + map( + map_res(delimited(multispace, digit, multispace), FromStr::from_str), + Expr::Value, + ), + parens, + ))(i) +} + +fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr { + remainder.into_iter().fold(initial, |acc, pair| { + let (oper, expr) = pair; + match oper { + Oper::Add => Expr::Add(Box::new(acc), Box::new(expr)), + Oper::Sub => Expr::Sub(Box::new(acc), Box::new(expr)), + Oper::Mul => Expr::Mul(Box::new(acc), Box::new(expr)), + Oper::Div => Expr::Div(Box::new(acc), Box::new(expr)), + } + }) +} + +fn term(i: &str) -> IResult<&str, Expr> { + let (i, initial) = factor(i)?; + let (i, remainder) = many0(alt(( + |i| { + let (i, mul) = preceded(tag("*"), factor)(i)?; + Ok((i, (Oper::Mul, mul))) + }, + |i| { + let (i, div) = preceded(tag("/"), factor)(i)?; + Ok((i, (Oper::Div, div))) + }, + )))(i)?; + + Ok((i, fold_exprs(initial, remainder))) +} + +fn expr(i: &str) -> IResult<&str, Expr> { + let (i, initial) = term(i)?; + let (i, remainder) = many0(alt(( + |i| { + let (i, add) = preceded(tag("+"), term)(i)?; + Ok((i, (Oper::Add, add))) + }, + |i| { + let (i, sub) = preceded(tag("-"), term)(i)?; + Ok((i, (Oper::Sub, sub))) + }, + )))(i)?; + + Ok((i, fold_exprs(initial, remainder))) +} + +#[test] +fn factor_test() { + assert_eq!( + factor(" 3 ").map(|(i, x)| (i, format!("{:?}", x))), + Ok(("", String::from("3"))) + ); +} + +#[test] +fn term_test() { + assert_eq!( + term(" 3 * 5 ").map(|(i, x)| (i, format!("{:?}", x))), + Ok(("", String::from("(3 * 5)"))) + ); +} + +#[test] +fn expr_test() { + assert_eq!( + expr(" 1 + 2 * 3 ").map(|(i, x)| (i, format!("{:?}", x))), + Ok(("", String::from("(1 + (2 * 3))"))) + ); + assert_eq!( + expr(" 1 + 2 * 3 / 4 - 5 ").map(|(i, x)| (i, format!("{:?}", x))), + Ok(("", String::from("((1 + ((2 * 3) / 4)) - 5)"))) + ); + assert_eq!( + expr(" 72 / 2 / 3 ").map(|(i, x)| (i, format!("{:?}", x))), + Ok(("", String::from("((72 / 2) / 3)"))) + ); +} + +#[test] +fn parens_test() { + assert_eq!( + expr(" ( 1 + 2 ) * 3 ").map(|(i, x)| (i, format!("{:?}", x))), + Ok(("", String::from("([(1 + 2)] * 3)"))) + ); +} diff --git a/third_party/rust/nom/tests/css.rs b/third_party/rust/nom/tests/css.rs new file mode 100644 index 0000000000..ad3d72b8fa --- /dev/null +++ b/third_party/rust/nom/tests/css.rs @@ -0,0 +1,45 @@ +use nom::bytes::complete::{tag, take_while_m_n}; +use nom::combinator::map_res; +use nom::sequence::tuple; +use nom::IResult; + +#[derive(Debug, PartialEq)] +pub struct Color { + pub red: u8, + pub green: u8, + pub blue: u8, +} + +fn from_hex(input: &str) -> Result<u8, std::num::ParseIntError> { + u8::from_str_radix(input, 16) +} + +fn is_hex_digit(c: char) -> bool { + c.is_digit(16) +} + +fn hex_primary(input: &str) -> IResult<&str, u8> { + map_res(take_while_m_n(2, 2, is_hex_digit), from_hex)(input) +} + +fn hex_color(input: &str) -> IResult<&str, Color> { + let (input, _) = tag("#")(input)?; + let (input, (red, green, blue)) = tuple((hex_primary, hex_primary, hex_primary))(input)?; + + Ok((input, Color { red, green, blue })) +} + +#[test] +fn parse_color() { + assert_eq!( + hex_color("#2F14DF"), + Ok(( + "", + Color { + red: 47, + green: 20, + blue: 223, + } + )) + ); +} diff --git a/third_party/rust/nom/tests/custom_errors.rs b/third_party/rust/nom/tests/custom_errors.rs new file mode 100644 index 0000000000..2021713341 --- /dev/null +++ b/third_party/rust/nom/tests/custom_errors.rs @@ -0,0 +1,48 @@ +#![allow(dead_code)] + +use nom::bytes::streaming::tag; +use nom::character::streaming::digit1 as digit; +use nom::combinator::verify; +use nom::error::{ErrorKind, ParseError}; +#[cfg(feature = "alloc")] +use nom::multi::count; +use nom::sequence::terminated; +use nom::IResult; + +#[derive(Debug)] +pub struct CustomError(String); + +impl<'a> From<(&'a str, ErrorKind)> for CustomError { + fn from(error: (&'a str, ErrorKind)) -> Self { + CustomError(format!("error code was: {:?}", error)) + } +} + +impl<'a> ParseError<&'a str> for CustomError { + fn from_error_kind(_: &'a str, kind: ErrorKind) -> Self { + CustomError(format!("error code was: {:?}", kind)) + } + + fn append(_: &'a str, kind: ErrorKind, other: CustomError) -> Self { + CustomError(format!("{:?}\nerror code was: {:?}", other, kind)) + } +} + +fn test1(input: &str) -> IResult<&str, &str, CustomError> { + //fix_error!(input, CustomError, tag!("abcd")) + tag("abcd")(input) +} + +fn test2(input: &str) -> IResult<&str, &str, CustomError> { + //terminated!(input, test1, fix_error!(CustomError, digit)) + terminated(test1, digit)(input) +} + +fn test3(input: &str) -> IResult<&str, &str, CustomError> { + verify(test1, |s: &str| s.starts_with("abcd"))(input) +} + +#[cfg(feature = "alloc")] +fn test4(input: &str) -> IResult<&str, Vec<&str>, CustomError> { + count(test1, 4)(input) +} diff --git a/third_party/rust/nom/tests/escaped.rs b/third_party/rust/nom/tests/escaped.rs new file mode 100644 index 0000000000..47c6a71e52 --- /dev/null +++ b/third_party/rust/nom/tests/escaped.rs @@ -0,0 +1,28 @@ +use nom::bytes::complete::escaped; +use nom::character::complete::digit1; +use nom::character::complete::one_of; +use nom::{error::ErrorKind, Err, IResult}; + +fn esc(s: &str) -> IResult<&str, &str, (&str, ErrorKind)> { + escaped(digit1, '\\', one_of("\"n\\"))(s) +} + +#[cfg(feature = "alloc")] +fn esc_trans(s: &str) -> IResult<&str, String, (&str, ErrorKind)> { + use nom::bytes::complete::{escaped_transform, tag}; + escaped_transform(digit1, '\\', tag("n"))(s) +} + +#[test] +fn test_escaped() { + assert_eq!(esc("abcd"), Err(Err::Error(("abcd", ErrorKind::Escaped)))); +} + +#[test] +#[cfg(feature = "alloc")] +fn test_escaped_transform() { + assert_eq!( + esc_trans("abcd"), + Err(Err::Error(("abcd", ErrorKind::EscapedTransform))) + ); +} diff --git a/third_party/rust/nom/tests/float.rs b/third_party/rust/nom/tests/float.rs new file mode 100644 index 0000000000..634b189899 --- /dev/null +++ b/third_party/rust/nom/tests/float.rs @@ -0,0 +1,46 @@ +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::character::streaming::digit1 as digit; +use nom::combinator::{map, map_res, opt, recognize}; +use nom::sequence::{delimited, pair}; +use nom::IResult; + +use std::str; +use std::str::FromStr; + +fn unsigned_float(i: &[u8]) -> IResult<&[u8], f32> { + let float_bytes = recognize(alt(( + delimited(digit, tag("."), opt(digit)), + delimited(opt(digit), tag("."), digit), + ))); + let float_str = map_res(float_bytes, str::from_utf8); + map_res(float_str, FromStr::from_str)(i) +} + +fn float(i: &[u8]) -> IResult<&[u8], f32> { + map( + pair(opt(alt((tag("+"), tag("-")))), unsigned_float), + |(sign, value)| { + sign + .and_then(|s| if s[0] == b'-' { Some(-1f32) } else { None }) + .unwrap_or(1f32) + * value + }, + )(i) +} + +#[test] +fn unsigned_float_test() { + assert_eq!(unsigned_float(&b"123.456;"[..]), Ok((&b";"[..], 123.456))); + assert_eq!(unsigned_float(&b"0.123;"[..]), Ok((&b";"[..], 0.123))); + assert_eq!(unsigned_float(&b"123.0;"[..]), Ok((&b";"[..], 123.0))); + assert_eq!(unsigned_float(&b"123.;"[..]), Ok((&b";"[..], 123.0))); + assert_eq!(unsigned_float(&b".123;"[..]), Ok((&b";"[..], 0.123))); +} + +#[test] +fn float_test() { + assert_eq!(float(&b"123.456;"[..]), Ok((&b";"[..], 123.456))); + assert_eq!(float(&b"+123.456;"[..]), Ok((&b";"[..], 123.456))); + assert_eq!(float(&b"-123.456;"[..]), Ok((&b";"[..], -123.456))); +} diff --git a/third_party/rust/nom/tests/fnmut.rs b/third_party/rust/nom/tests/fnmut.rs new file mode 100644 index 0000000000..b1486cbe63 --- /dev/null +++ b/third_party/rust/nom/tests/fnmut.rs @@ -0,0 +1,39 @@ +use nom::{ + bytes::complete::tag, + multi::{many0, many0_count}, +}; + +#[test] +fn parse() { + let mut counter = 0; + + let res = { + let mut parser = many0::<_, _, (), _>(|i| { + counter += 1; + tag("abc")(i) + }); + + parser("abcabcabcabc").unwrap() + }; + + println!("res: {:?}", res); + assert_eq!(counter, 5); +} + +#[test] +fn accumulate() { + let mut v = Vec::new(); + + let (_, count) = { + let mut parser = many0_count::<_, _, (), _>(|i| { + let (i, o) = tag("abc")(i)?; + v.push(o); + Ok((i, ())) + }); + parser("abcabcabcabc").unwrap() + }; + + println!("v: {:?}", v); + assert_eq!(count, 4); + assert_eq!(v.len(), 4); +} diff --git a/third_party/rust/nom/tests/ini.rs b/third_party/rust/nom/tests/ini.rs new file mode 100644 index 0000000000..e556f44a3c --- /dev/null +++ b/third_party/rust/nom/tests/ini.rs @@ -0,0 +1,207 @@ +use nom::{ + bytes::complete::take_while, + character::complete::{ + alphanumeric1 as alphanumeric, char, multispace0 as multispace, space0 as space, + }, + combinator::{map, map_res, opt}, + multi::many0, + sequence::{delimited, pair, separated_pair, terminated, tuple}, + IResult, +}; + +use std::collections::HashMap; +use std::str; + +fn category(i: &[u8]) -> IResult<&[u8], &str> { + map_res( + delimited(char('['), take_while(|c| c != b']'), char(']')), + str::from_utf8, + )(i) +} + +fn key_value(i: &[u8]) -> IResult<&[u8], (&str, &str)> { + let (i, key) = map_res(alphanumeric, str::from_utf8)(i)?; + let (i, _) = tuple((opt(space), char('='), opt(space)))(i)?; + let (i, val) = map_res(take_while(|c| c != b'\n' && c != b';'), str::from_utf8)(i)?; + let (i, _) = opt(pair(char(';'), take_while(|c| c != b'\n')))(i)?; + Ok((i, (key, val))) +} + +fn keys_and_values(i: &[u8]) -> IResult<&[u8], HashMap<&str, &str>> { + map(many0(terminated(key_value, opt(multispace))), |vec| { + vec.into_iter().collect() + })(i) +} + +fn category_and_keys(i: &[u8]) -> IResult<&[u8], (&str, HashMap<&str, &str>)> { + let (i, category) = terminated(category, opt(multispace))(i)?; + let (i, keys) = keys_and_values(i)?; + Ok((i, (category, keys))) +} + +fn categories(i: &[u8]) -> IResult<&[u8], HashMap<&str, HashMap<&str, &str>>> { + map( + many0(separated_pair( + category, + opt(multispace), + map( + many0(terminated(key_value, opt(multispace))), + |vec: Vec<_>| vec.into_iter().collect(), + ), + )), + |vec: Vec<_>| vec.into_iter().collect(), + )(i) +} + +#[test] +fn parse_category_test() { + let ini_file = &b"[category] + +parameter=value +key = value2"[..]; + + let ini_without_category = &b"\n\nparameter=value +key = value2"[..]; + + let res = category(ini_file); + println!("{:?}", res); + match res { + Ok((i, o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_category, "category"))); +} + +#[test] +fn parse_key_value_test() { + let ini_file = &b"parameter=value +key = value2"[..]; + + let ini_without_key_value = &b"\nkey = value2"[..]; + + let res = key_value(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_key_value_with_space_test() { + let ini_file = &b"parameter = value +key = value2"[..]; + + let ini_without_key_value = &b"\nkey = value2"[..]; + + let res = key_value(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_key_value_with_comment_test() { + let ini_file = &b"parameter=value;abc +key = value2"[..]; + + let ini_without_key_value = &b"\nkey = value2"[..]; + + let res = key_value(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_multiple_keys_and_values_test() { + let ini_file = &b"parameter=value;abc + +key = value2 + +[category]"[..]; + + let ini_without_key_value = &b"[category]"[..]; + + let res = keys_and_values(ini_file); + println!("{:?}", res); + match res { + Ok((i, ref o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o), + _ => println!("error"), + } + + let mut expected: HashMap<&str, &str> = HashMap::new(); + expected.insert("parameter", "value"); + expected.insert("key", "value2"); + assert_eq!(res, Ok((ini_without_key_value, expected))); +} + +#[test] +fn parse_category_then_multiple_keys_and_values_test() { + //FIXME: there can be an empty line or a comment line after a category + let ini_file = &b"[abcd] +parameter=value;abc + +key = value2 + +[category]"[..]; + + let ini_after_parser = &b"[category]"[..]; + + let res = category_and_keys(ini_file); + println!("{:?}", res); + match res { + Ok((i, ref o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o), + _ => println!("error"), + } + + let mut expected_h: HashMap<&str, &str> = HashMap::new(); + expected_h.insert("parameter", "value"); + expected_h.insert("key", "value2"); + assert_eq!(res, Ok((ini_after_parser, ("abcd", expected_h)))); +} + +#[test] +fn parse_multiple_categories_test() { + let ini_file = &b"[abcd] + +parameter=value;abc + +key = value2 + +[category] +parameter3=value3 +key4 = value4 +"[..]; + + let ini_after_parser = &b""[..]; + + let res = categories(ini_file); + //println!("{:?}", res); + match res { + Ok((i, ref o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o), + _ => println!("error"), + } + + let mut expected_1: HashMap<&str, &str> = HashMap::new(); + expected_1.insert("parameter", "value"); + expected_1.insert("key", "value2"); + let mut expected_2: HashMap<&str, &str> = HashMap::new(); + expected_2.insert("parameter3", "value3"); + expected_2.insert("key4", "value4"); + let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new(); + expected_h.insert("abcd", expected_1); + expected_h.insert("category", expected_2); + assert_eq!(res, Ok((ini_after_parser, expected_h))); +} diff --git a/third_party/rust/nom/tests/ini_str.rs b/third_party/rust/nom/tests/ini_str.rs new file mode 100644 index 0000000000..3702303527 --- /dev/null +++ b/third_party/rust/nom/tests/ini_str.rs @@ -0,0 +1,217 @@ +use nom::{ + bytes::complete::{is_a, tag, take_till, take_while}, + character::complete::{alphanumeric1 as alphanumeric, char, space0 as space}, + combinator::opt, + multi::many0, + sequence::{delimited, pair, terminated, tuple}, + IResult, +}; + +use std::collections::HashMap; + +fn is_line_ending_or_comment(chr: char) -> bool { + chr == ';' || chr == '\n' +} + +fn not_line_ending(i: &str) -> IResult<&str, &str> { + take_while(|c| c != '\r' && c != '\n')(i) +} + +fn space_or_line_ending(i: &str) -> IResult<&str, &str> { + is_a(" \r\n")(i) +} + +fn category(i: &str) -> IResult<&str, &str> { + terminated( + delimited(char('['), take_while(|c| c != ']'), char(']')), + opt(is_a(" \r\n")), + )(i) +} + +fn key_value(i: &str) -> IResult<&str, (&str, &str)> { + let (i, key) = alphanumeric(i)?; + let (i, _) = tuple((opt(space), tag("="), opt(space)))(i)?; + let (i, val) = take_till(is_line_ending_or_comment)(i)?; + let (i, _) = opt(space)(i)?; + let (i, _) = opt(pair(tag(";"), not_line_ending))(i)?; + let (i, _) = opt(space_or_line_ending)(i)?; + + Ok((i, (key, val))) +} + +fn keys_and_values_aggregator(i: &str) -> IResult<&str, Vec<(&str, &str)>> { + many0(key_value)(i) +} + +fn keys_and_values(input: &str) -> IResult<&str, HashMap<&str, &str>> { + match keys_and_values_aggregator(input) { + Ok((i, tuple_vec)) => Ok((i, tuple_vec.into_iter().collect())), + Err(e) => Err(e), + } +} + +fn category_and_keys(i: &str) -> IResult<&str, (&str, HashMap<&str, &str>)> { + pair(category, keys_and_values)(i) +} + +fn categories_aggregator(i: &str) -> IResult<&str, Vec<(&str, HashMap<&str, &str>)>> { + many0(category_and_keys)(i) +} + +fn categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str>>> { + match categories_aggregator(input) { + Ok((i, tuple_vec)) => Ok((i, tuple_vec.into_iter().collect())), + Err(e) => Err(e), + } +} + +#[test] +fn parse_category_test() { + let ini_file = "[category] + +parameter=value +key = value2"; + + let ini_without_category = "parameter=value +key = value2"; + + let res = category(ini_file); + println!("{:?}", res); + match res { + Ok((i, o)) => println!("i: {} | o: {:?}", i, o), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_category, "category"))); +} + +#[test] +fn parse_key_value_test() { + let ini_file = "parameter=value +key = value2"; + + let ini_without_key_value = "key = value2"; + + let res = key_value(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_key_value_with_space_test() { + let ini_file = "parameter = value +key = value2"; + + let ini_without_key_value = "key = value2"; + + let res = key_value(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_key_value_with_comment_test() { + let ini_file = "parameter=value;abc +key = value2"; + + let ini_without_key_value = "key = value2"; + + let res = key_value(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_multiple_keys_and_values_test() { + let ini_file = "parameter=value;abc + +key = value2 + +[category]"; + + let ini_without_key_value = "[category]"; + + let res = keys_and_values(ini_file); + println!("{:?}", res); + match res { + Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o), + _ => println!("error"), + } + + let mut expected: HashMap<&str, &str> = HashMap::new(); + expected.insert("parameter", "value"); + expected.insert("key", "value2"); + assert_eq!(res, Ok((ini_without_key_value, expected))); +} + +#[test] +fn parse_category_then_multiple_keys_and_values_test() { + //FIXME: there can be an empty line or a comment line after a category + let ini_file = "[abcd] +parameter=value;abc + +key = value2 + +[category]"; + + let ini_after_parser = "[category]"; + + let res = category_and_keys(ini_file); + println!("{:?}", res); + match res { + Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o), + _ => println!("error"), + } + + let mut expected_h: HashMap<&str, &str> = HashMap::new(); + expected_h.insert("parameter", "value"); + expected_h.insert("key", "value2"); + assert_eq!(res, Ok((ini_after_parser, ("abcd", expected_h)))); +} + +#[test] +fn parse_multiple_categories_test() { + let ini_file = "[abcd] + +parameter=value;abc + +key = value2 + +[category] +parameter3=value3 +key4 = value4 +"; + + let res = categories(ini_file); + //println!("{:?}", res); + match res { + Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o), + _ => println!("error"), + } + + let mut expected_1: HashMap<&str, &str> = HashMap::new(); + expected_1.insert("parameter", "value"); + expected_1.insert("key", "value2"); + let mut expected_2: HashMap<&str, &str> = HashMap::new(); + expected_2.insert("parameter3", "value3"); + expected_2.insert("key4", "value4"); + let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new(); + expected_h.insert("abcd", expected_1); + expected_h.insert("category", expected_2); + assert_eq!(res, Ok(("", expected_h))); +} diff --git a/third_party/rust/nom/tests/issues.rs b/third_party/rust/nom/tests/issues.rs new file mode 100644 index 0000000000..7985702f67 --- /dev/null +++ b/third_party/rust/nom/tests/issues.rs @@ -0,0 +1,242 @@ +//#![feature(trace_macros)] +#![allow(dead_code)] +#![cfg_attr(feature = "cargo-clippy", allow(redundant_closure))] + +use nom::{error::ErrorKind, Err, IResult, Needed}; + +#[allow(dead_code)] +struct Range { + start: char, + end: char, +} + +pub fn take_char(input: &[u8]) -> IResult<&[u8], char> { + if !input.is_empty() { + Ok((&input[1..], input[0] as char)) + } else { + Err(Err::Incomplete(Needed::new(1))) + } +} + +#[cfg(feature = "std")] +mod parse_int { + use nom::HexDisplay; + use nom::{ + character::streaming::{digit1 as digit, space1 as space}, + combinator::{complete, map, opt}, + multi::many0, + IResult, + }; + use std::str; + + fn parse_ints(input: &[u8]) -> IResult<&[u8], Vec<i32>> { + many0(spaces_or_int)(input) + } + + fn spaces_or_int(input: &[u8]) -> IResult<&[u8], i32> { + println!("{}", input.to_hex(8)); + let (i, _) = opt(complete(space))(input)?; + let (i, res) = map(complete(digit), |x| { + println!("x: {:?}", x); + let result = str::from_utf8(x).unwrap(); + println!("Result: {}", result); + println!("int is empty?: {}", x.is_empty()); + match result.parse() { + Ok(i) => i, + Err(e) => panic!("UH OH! NOT A DIGIT! {:?}", e), + } + })(i)?; + + Ok((i, res)) + } + + #[test] + fn issue_142() { + let subject = parse_ints(&b"12 34 5689a"[..]); + let expected = Ok((&b"a"[..], vec![12, 34, 5689])); + assert_eq!(subject, expected); + + let subject = parse_ints(&b"12 34 5689 "[..]); + let expected = Ok((&b" "[..], vec![12, 34, 5689])); + assert_eq!(subject, expected) + } +} + +#[test] +fn usize_length_bytes_issue() { + use nom::multi::length_data; + use nom::number::streaming::be_u16; + let _: IResult<&[u8], &[u8], (&[u8], ErrorKind)> = length_data(be_u16)(b"012346"); +} + +#[test] +fn take_till_issue() { + use nom::bytes::streaming::take_till; + + fn nothing(i: &[u8]) -> IResult<&[u8], &[u8]> { + take_till(|_| true)(i) + } + + assert_eq!(nothing(b""), Err(Err::Incomplete(Needed::new(1)))); + assert_eq!(nothing(b"abc"), Ok((&b"abc"[..], &b""[..]))); +} + +#[test] +fn issue_655() { + use nom::character::streaming::{line_ending, not_line_ending}; + fn twolines(i: &str) -> IResult<&str, (&str, &str)> { + let (i, l1) = not_line_ending(i)?; + let (i, _) = line_ending(i)?; + let (i, l2) = not_line_ending(i)?; + let (i, _) = line_ending(i)?; + + Ok((i, (l1, l2))) + } + + assert_eq!(twolines("foo\nbar\n"), Ok(("", ("foo", "bar")))); + assert_eq!(twolines("féo\nbar\n"), Ok(("", ("féo", "bar")))); + assert_eq!(twolines("foé\nbar\n"), Ok(("", ("foé", "bar")))); + assert_eq!(twolines("foé\r\nbar\n"), Ok(("", ("foé", "bar")))); +} + +#[cfg(feature = "alloc")] +fn issue_717(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + use nom::bytes::complete::{is_not, tag}; + use nom::multi::separated_list0; + + separated_list0(tag([0x0]), is_not([0x0u8]))(i) +} + +mod issue_647 { + use nom::bytes::streaming::tag; + use nom::combinator::complete; + use nom::multi::separated_list0; + use nom::{error::Error, number::streaming::be_f64, Err, IResult}; + pub type Input<'a> = &'a [u8]; + + #[derive(PartialEq, Debug, Clone)] + struct Data { + c: f64, + v: Vec<f64>, + } + + fn list<'a, 'b>( + input: Input<'a>, + _cs: &'b f64, + ) -> Result<(Input<'a>, Vec<f64>), Err<Error<&'a [u8]>>> { + separated_list0(complete(tag(",")), complete(be_f64))(input) + } + + fn data(input: Input<'_>) -> IResult<Input<'_>, Data> { + let (i, c) = be_f64(input)?; + let (i, _) = tag("\n")(i)?; + let (i, v) = list(i, &c)?; + Ok((i, Data { c, v })) + } +} + +#[test] +fn issue_848_overflow_incomplete_bits_to_bytes() { + fn take(i: &[u8]) -> IResult<&[u8], &[u8]> { + use nom::bytes::streaming::take; + take(0x2000000000000000_usize)(i) + } + fn parser(i: &[u8]) -> IResult<&[u8], &[u8]> { + use nom::bits::{bits, bytes}; + + bits(bytes(take))(i) + } + assert_eq!( + parser(&b""[..]), + Err(Err::Failure(nom::error_position!( + &b""[..], + ErrorKind::TooLarge + ))) + ); +} + +#[test] +fn issue_942() { + use nom::error::{ContextError, ParseError}; + pub fn parser<'a, E: ParseError<&'a str> + ContextError<&'a str>>( + i: &'a str, + ) -> IResult<&'a str, usize, E> { + use nom::{character::complete::char, error::context, multi::many0_count}; + many0_count(context("char_a", char('a')))(i) + } + assert_eq!(parser::<()>("aaa"), Ok(("", 3))); +} + +#[test] +fn issue_many_m_n_with_zeros() { + use nom::character::complete::char; + use nom::multi::many_m_n; + let mut parser = many_m_n::<_, _, (), _>(0, 0, char('a')); + assert_eq!(parser("aaa"), Ok(("aaa", vec![]))); +} + +#[test] +fn issue_1027_convert_error_panic_nonempty() { + use nom::character::complete::char; + use nom::error::{convert_error, VerboseError}; + use nom::sequence::pair; + + let input = "a"; + + let result: IResult<_, _, VerboseError<&str>> = pair(char('a'), char('b'))(input); + let err = match result.unwrap_err() { + Err::Error(e) => e, + _ => unreachable!(), + }; + + let msg = convert_error(input, err); + assert_eq!( + msg, + "0: at line 1:\na\n ^\nexpected \'b\', got end of input\n\n" + ); +} + +#[test] +fn issue_1231_bits_expect_fn_closure() { + use nom::bits::{bits, complete::take}; + use nom::error::Error; + use nom::sequence::tuple; + pub fn example(input: &[u8]) -> IResult<&[u8], (u8, u8)> { + bits::<_, _, Error<_>, _, _>(tuple((take(1usize), take(1usize))))(input) + } + assert_eq!(example(&[0xff]), Ok((&b""[..], (1, 1)))); +} + +#[test] +fn issue_1282_findtoken_char() { + use nom::character::complete::one_of; + use nom::error::Error; + let parser = one_of::<_, _, Error<_>>(&['a', 'b', 'c'][..]); + assert_eq!(parser("aaa"), Ok(("aa", 'a'))); +} + +#[test] +fn issue_1459_clamp_capacity() { + use nom::character::complete::char; + + // shouldn't panic + use nom::multi::many_m_n; + let mut parser = many_m_n::<_, _, (), _>(usize::MAX, usize::MAX, char('a')); + assert_eq!(parser("a"), Err(nom::Err::Error(()))); + + // shouldn't panic + use nom::multi::count; + let mut parser = count::<_, _, (), _>(char('a'), usize::MAX); + assert_eq!(parser("a"), Err(nom::Err::Error(()))); +} + +#[test] +fn issue_1617_count_parser_returning_zero_size() { + use nom::{bytes::complete::tag, combinator::map, error::Error, multi::count}; + + // previously, `count()` panicked if the parser had type `O = ()` + let parser = map(tag::<_, _, Error<&str>>("abc"), |_| ()); + // shouldn't panic + let result = count(parser, 3)("abcabcabcdef").expect("parsing should succeed"); + assert_eq!(result, ("def", vec![(), (), ()])); +} diff --git a/third_party/rust/nom/tests/json.rs b/third_party/rust/nom/tests/json.rs new file mode 100644 index 0000000000..e8a06fd778 --- /dev/null +++ b/third_party/rust/nom/tests/json.rs @@ -0,0 +1,236 @@ +#![cfg(feature = "alloc")] + +use nom::{ + branch::alt, + bytes::complete::{tag, take}, + character::complete::{anychar, char, multispace0, none_of}, + combinator::{map, map_opt, map_res, value, verify}, + error::ParseError, + multi::{fold_many0, separated_list0}, + number::complete::double, + sequence::{delimited, preceded, separated_pair}, + IResult, Parser, +}; + +use std::collections::HashMap; + +#[derive(Debug, PartialEq, Clone)] +pub enum JsonValue { + Null, + Bool(bool), + Str(String), + Num(f64), + Array(Vec<JsonValue>), + Object(HashMap<String, JsonValue>), +} + +fn boolean(input: &str) -> IResult<&str, bool> { + alt((value(false, tag("false")), value(true, tag("true"))))(input) +} + +fn u16_hex(input: &str) -> IResult<&str, u16> { + map_res(take(4usize), |s| u16::from_str_radix(s, 16))(input) +} + +fn unicode_escape(input: &str) -> IResult<&str, char> { + map_opt( + alt(( + // Not a surrogate + map(verify(u16_hex, |cp| !(0xD800..0xE000).contains(cp)), |cp| { + cp as u32 + }), + // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details + map( + verify( + separated_pair(u16_hex, tag("\\u"), u16_hex), + |(high, low)| (0xD800..0xDC00).contains(high) && (0xDC00..0xE000).contains(low), + ), + |(high, low)| { + let high_ten = (high as u32) - 0xD800; + let low_ten = (low as u32) - 0xDC00; + (high_ten << 10) + low_ten + 0x10000 + }, + ), + )), + // Could be probably replaced with .unwrap() or _unchecked due to the verify checks + std::char::from_u32, + )(input) +} + +fn character(input: &str) -> IResult<&str, char> { + let (input, c) = none_of("\"")(input)?; + if c == '\\' { + alt(( + map_res(anychar, |c| { + Ok(match c { + '"' | '\\' | '/' => c, + 'b' => '\x08', + 'f' => '\x0C', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + _ => return Err(()), + }) + }), + preceded(char('u'), unicode_escape), + ))(input) + } else { + Ok((input, c)) + } +} + +fn string(input: &str) -> IResult<&str, String> { + delimited( + char('"'), + fold_many0(character, String::new, |mut string, c| { + string.push(c); + string + }), + char('"'), + )(input) +} + +fn ws<'a, O, E: ParseError<&'a str>, F: Parser<&'a str, O, E>>(f: F) -> impl Parser<&'a str, O, E> { + delimited(multispace0, f, multispace0) +} + +fn array(input: &str) -> IResult<&str, Vec<JsonValue>> { + delimited( + char('['), + ws(separated_list0(ws(char(',')), json_value)), + char(']'), + )(input) +} + +fn object(input: &str) -> IResult<&str, HashMap<String, JsonValue>> { + map( + delimited( + char('{'), + ws(separated_list0( + ws(char(',')), + separated_pair(string, ws(char(':')), json_value), + )), + char('}'), + ), + |key_values| key_values.into_iter().collect(), + )(input) +} + +fn json_value(input: &str) -> IResult<&str, JsonValue> { + use JsonValue::*; + + alt(( + value(Null, tag("null")), + map(boolean, Bool), + map(string, Str), + map(double, Num), + map(array, Array), + map(object, Object), + ))(input) +} + +fn json(input: &str) -> IResult<&str, JsonValue> { + ws(json_value).parse(input) +} + +#[test] +fn json_string() { + assert_eq!(string("\"\""), Ok(("", "".to_string()))); + assert_eq!(string("\"abc\""), Ok(("", "abc".to_string()))); + assert_eq!( + string("\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""), + Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_string())), + ); + assert_eq!(string("\"\\uD83D\\uDE10\""), Ok(("", "😐".to_string()))); + + assert!(string("\"").is_err()); + assert!(string("\"abc").is_err()); + assert!(string("\"\\\"").is_err()); + assert!(string("\"\\u123\"").is_err()); + assert!(string("\"\\uD800\"").is_err()); + assert!(string("\"\\uD800\\uD800\"").is_err()); + assert!(string("\"\\uDC00\"").is_err()); +} + +#[test] +fn json_object() { + use JsonValue::*; + + let input = r#"{"a":42,"b":"x"}"#; + + let expected = Object( + vec![ + ("a".to_string(), Num(42.0)), + ("b".to_string(), Str("x".to_string())), + ] + .into_iter() + .collect(), + ); + + assert_eq!(json(input), Ok(("", expected))); +} + +#[test] +fn json_array() { + use JsonValue::*; + + let input = r#"[42,"x"]"#; + + let expected = Array(vec![Num(42.0), Str("x".to_string())]); + + assert_eq!(json(input), Ok(("", expected))); +} + +#[test] +fn json_whitespace() { + use JsonValue::*; + + let input = r#" + { + "null" : null, + "true" :true , + "false": false , + "number" : 123e4 , + "string" : " abc 123 " , + "array" : [ false , 1 , "two" ] , + "object" : { "a" : 1.0 , "b" : "c" } , + "empty_array" : [ ] , + "empty_object" : { } + } + "#; + + assert_eq!( + json(input), + Ok(( + "", + Object( + vec![ + ("null".to_string(), Null), + ("true".to_string(), Bool(true)), + ("false".to_string(), Bool(false)), + ("number".to_string(), Num(123e4)), + ("string".to_string(), Str(" abc 123 ".to_string())), + ( + "array".to_string(), + Array(vec![Bool(false), Num(1.0), Str("two".to_string())]) + ), + ( + "object".to_string(), + Object( + vec![ + ("a".to_string(), Num(1.0)), + ("b".to_string(), Str("c".to_string())), + ] + .into_iter() + .collect() + ) + ), + ("empty_array".to_string(), Array(vec![]),), + ("empty_object".to_string(), Object(HashMap::new()),), + ] + .into_iter() + .collect() + ) + )) + ); +} diff --git a/third_party/rust/nom/tests/mp4.rs b/third_party/rust/nom/tests/mp4.rs new file mode 100644 index 0000000000..852bf29555 --- /dev/null +++ b/third_party/rust/nom/tests/mp4.rs @@ -0,0 +1,320 @@ +#![allow(dead_code)] + +use nom::{ + branch::alt, + bytes::streaming::{tag, take}, + combinator::{map, map_res}, + error::ErrorKind, + multi::many0, + number::streaming::{be_f32, be_u16, be_u32, be_u64}, + Err, IResult, Needed, +}; + +use std::str; + +fn mp4_box(input: &[u8]) -> IResult<&[u8], &[u8]> { + match be_u32(input) { + Ok((i, offset)) => { + let sz: usize = offset as usize; + if i.len() >= sz - 4 { + Ok((&i[(sz - 4)..], &i[0..(sz - 4)])) + } else { + Err(Err::Incomplete(Needed::new(offset as usize + 4))) + } + } + Err(e) => Err(e), + } +} + +#[cfg_attr(rustfmt, rustfmt_skip)] +#[derive(PartialEq,Eq,Debug)] +struct FileType<'a> { + major_brand: &'a str, + major_brand_version: &'a [u8], + compatible_brands: Vec<&'a str> +} + +#[cfg_attr(rustfmt, rustfmt_skip)] +#[allow(non_snake_case)] +#[derive(Debug,Clone)] +pub struct Mvhd32 { + version_flags: u32, // actually: + // version: u8, + // flags: u24 // 3 bytes + created_date: u32, + modified_date: u32, + scale: u32, + duration: u32, + speed: f32, + volume: u16, // actually a 2 bytes decimal + /* 10 bytes reserved */ + scaleA: f32, + rotateB: f32, + angleU: f32, + rotateC: f32, + scaleD: f32, + angleV: f32, + positionX: f32, + positionY: f32, + scaleW: f32, + preview: u64, + poster: u32, + selection: u64, + current_time: u32, + track_id: u32 +} + +#[cfg_attr(rustfmt, rustfmt_skip)] +#[allow(non_snake_case)] +#[derive(Debug,Clone)] +pub struct Mvhd64 { + version_flags: u32, // actually: + // version: u8, + // flags: u24 // 3 bytes + created_date: u64, + modified_date: u64, + scale: u32, + duration: u64, + speed: f32, + volume: u16, // actually a 2 bytes decimal + /* 10 bytes reserved */ + scaleA: f32, + rotateB: f32, + angleU: f32, + rotateC: f32, + scaleD: f32, + angleV: f32, + positionX: f32, + positionY: f32, + scaleW: f32, + preview: u64, + poster: u32, + selection: u64, + current_time: u32, + track_id: u32 +} + +#[cfg_attr(rustfmt, rustfmt_skip)] +fn mvhd32(i: &[u8]) -> IResult<&[u8], MvhdBox> { + let (i, version_flags) = be_u32(i)?; + let (i, created_date) = be_u32(i)?; + let (i, modified_date) = be_u32(i)?; + let (i, scale) = be_u32(i)?; + let (i, duration) = be_u32(i)?; + let (i, speed) = be_f32(i)?; + let (i, volume) = be_u16(i)?; // actually a 2 bytes decimal + let (i, _) = take(10_usize)(i)?; + let (i, scale_a) = be_f32(i)?; + let (i, rotate_b) = be_f32(i)?; + let (i, angle_u) = be_f32(i)?; + let (i, rotate_c) = be_f32(i)?; + let (i, scale_d) = be_f32(i)?; + let (i, angle_v) = be_f32(i)?; + let (i, position_x) = be_f32(i)?; + let (i, position_y) = be_f32(i)?; + let (i, scale_w) = be_f32(i)?; + let (i, preview) = be_u64(i)?; + let (i, poster) = be_u32(i)?; + let (i, selection) = be_u64(i)?; + let (i, current_time) = be_u32(i)?; + let (i, track_id) = be_u32(i)?; + + let mvhd_box = MvhdBox::M32(Mvhd32 { + version_flags, + created_date, + modified_date, + scale, + duration, + speed, + volume, + scaleA: scale_a, + rotateB: rotate_b, + angleU: angle_u, + rotateC: rotate_c, + scaleD: scale_d, + angleV: angle_v, + positionX: position_x, + positionY: position_y, + scaleW: scale_w, + preview, + poster, + selection, + current_time, + track_id, + }); + + Ok((i, mvhd_box)) +} + +#[cfg_attr(rustfmt, rustfmt_skip)] +fn mvhd64(i: &[u8]) -> IResult<&[u8], MvhdBox> { + let (i, version_flags) = be_u32(i)?; + let (i, created_date) = be_u64(i)?; + let (i, modified_date) = be_u64(i)?; + let (i, scale) = be_u32(i)?; + let (i, duration) = be_u64(i)?; + let (i, speed) = be_f32(i)?; + let (i, volume) = be_u16(i)?; // actually a 2 bytes decimal + let (i, _) = take(10_usize)(i)?; + let (i, scale_a) = be_f32(i)?; + let (i, rotate_b) = be_f32(i)?; + let (i, angle_u) = be_f32(i)?; + let (i, rotate_c) = be_f32(i)?; + let (i, scale_d) = be_f32(i)?; + let (i, angle_v) = be_f32(i)?; + let (i, position_x) = be_f32(i)?; + let (i, position_y) = be_f32(i)?; + let (i, scale_w) = be_f32(i)?; + let (i, preview) = be_u64(i)?; + let (i, poster) = be_u32(i)?; + let (i, selection) = be_u64(i)?; + let (i, current_time) = be_u32(i)?; + let (i, track_id) = be_u32(i)?; + + let mvhd_box = MvhdBox::M64(Mvhd64 { + version_flags, + created_date, + modified_date, + scale, + duration, + speed, + volume, + scaleA: scale_a, + rotateB: rotate_b, + angleU: angle_u, + rotateC: rotate_c, + scaleD: scale_d, + angleV: angle_v, + positionX: position_x, + positionY: position_y, + scaleW: scale_w, + preview, + poster, + selection, + current_time, + track_id, + }); + + Ok((i, mvhd_box)) +} + +#[derive(Debug, Clone)] +pub enum MvhdBox { + M32(Mvhd32), + M64(Mvhd64), +} + +#[derive(Debug, Clone)] +pub enum MoovBox { + Mdra, + Dref, + Cmov, + Rmra, + Iods, + Mvhd(MvhdBox), + Clip, + Trak, + Udta, +} + +#[derive(Debug)] +enum MP4BoxType { + Ftyp, + Moov, + Mdat, + Free, + Skip, + Wide, + Mdra, + Dref, + Cmov, + Rmra, + Iods, + Mvhd, + Clip, + Trak, + Udta, + Unknown, +} + +#[derive(Debug)] +struct MP4BoxHeader { + length: u32, + tag: MP4BoxType, +} + +fn brand_name(input: &[u8]) -> IResult<&[u8], &str> { + map_res(take(4_usize), str::from_utf8)(input) +} + +fn filetype_parser(input: &[u8]) -> IResult<&[u8], FileType<'_>> { + let (i, name) = brand_name(input)?; + let (i, version) = take(4_usize)(i)?; + let (i, brands) = many0(brand_name)(i)?; + + let ft = FileType { + major_brand: name, + major_brand_version: version, + compatible_brands: brands, + }; + Ok((i, ft)) +} + +fn mvhd_box(input: &[u8]) -> IResult<&[u8], MvhdBox> { + let res = if input.len() < 100 { + Err(Err::Incomplete(Needed::new(100))) + } else if input.len() == 100 { + mvhd32(input) + } else if input.len() == 112 { + mvhd64(input) + } else { + Err(Err::Error(nom::error_position!(input, ErrorKind::TooLarge))) + }; + println!("res: {:?}", res); + res +} + +fn unknown_box_type(input: &[u8]) -> IResult<&[u8], MP4BoxType> { + Ok((input, MP4BoxType::Unknown)) +} + +fn box_type(input: &[u8]) -> IResult<&[u8], MP4BoxType> { + alt(( + map(tag("ftyp"), |_| MP4BoxType::Ftyp), + map(tag("moov"), |_| MP4BoxType::Moov), + map(tag("mdat"), |_| MP4BoxType::Mdat), + map(tag("free"), |_| MP4BoxType::Free), + map(tag("skip"), |_| MP4BoxType::Skip), + map(tag("wide"), |_| MP4BoxType::Wide), + unknown_box_type, + ))(input) +} + +// warning, an alt combinator with 9 branches containing a tag combinator +// can make the compilation very slow. Use functions as sub parsers, +// or split into multiple alt parsers if it gets slow +fn moov_type(input: &[u8]) -> IResult<&[u8], MP4BoxType> { + alt(( + map(tag("mdra"), |_| MP4BoxType::Mdra), + map(tag("dref"), |_| MP4BoxType::Dref), + map(tag("cmov"), |_| MP4BoxType::Cmov), + map(tag("rmra"), |_| MP4BoxType::Rmra), + map(tag("iods"), |_| MP4BoxType::Iods), + map(tag("mvhd"), |_| MP4BoxType::Mvhd), + map(tag("clip"), |_| MP4BoxType::Clip), + map(tag("trak"), |_| MP4BoxType::Trak), + map(tag("udta"), |_| MP4BoxType::Udta), + ))(input) +} + +fn box_header(input: &[u8]) -> IResult<&[u8], MP4BoxHeader> { + let (i, length) = be_u32(input)?; + let (i, tag) = box_type(i)?; + Ok((i, MP4BoxHeader { length, tag })) +} + +fn moov_header(input: &[u8]) -> IResult<&[u8], MP4BoxHeader> { + let (i, length) = be_u32(input)?; + let (i, tag) = moov_type(i)?; + Ok((i, MP4BoxHeader { length, tag })) +} diff --git a/third_party/rust/nom/tests/multiline.rs b/third_party/rust/nom/tests/multiline.rs new file mode 100644 index 0000000000..7378b9e3b4 --- /dev/null +++ b/third_party/rust/nom/tests/multiline.rs @@ -0,0 +1,31 @@ +use nom::{ + character::complete::{alphanumeric1 as alphanumeric, line_ending as eol}, + multi::many0, + sequence::terminated, + IResult, +}; + +pub fn end_of_line(input: &str) -> IResult<&str, &str> { + if input.is_empty() { + Ok((input, input)) + } else { + eol(input) + } +} + +pub fn read_line(input: &str) -> IResult<&str, &str> { + terminated(alphanumeric, end_of_line)(input) +} + +pub fn read_lines(input: &str) -> IResult<&str, Vec<&str>> { + many0(read_line)(input) +} + +#[cfg(feature = "alloc")] +#[test] +fn read_lines_test() { + let res = Ok(("", vec!["Duck", "Dog", "Cow"])); + + assert_eq!(read_lines("Duck\nDog\nCow\n"), res); + assert_eq!(read_lines("Duck\nDog\nCow"), res); +} diff --git a/third_party/rust/nom/tests/overflow.rs b/third_party/rust/nom/tests/overflow.rs new file mode 100644 index 0000000000..ea513bb395 --- /dev/null +++ b/third_party/rust/nom/tests/overflow.rs @@ -0,0 +1,145 @@ +#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] +#![cfg(target_pointer_width = "64")] + +use nom::bytes::streaming::take; +#[cfg(feature = "alloc")] +use nom::multi::{length_data, many0}; +#[cfg(feature = "alloc")] +use nom::number::streaming::be_u64; +use nom::sequence::tuple; +use nom::{Err, IResult, Needed}; + +// Parser definition + +// We request a length that would trigger an overflow if computing consumed + requested +fn parser02(i: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> { + tuple((take(1_usize), take(18446744073709551615_usize)))(i) +} + +#[test] +fn overflow_incomplete_tuple() { + assert_eq!( + parser02(&b"3"[..]), + Err(Err::Incomplete(Needed::new(18446744073709551615))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn overflow_incomplete_length_bytes() { + fn multi(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + many0(length_data(be_u64))(i) + } + + // Trigger an overflow in length_data + assert_eq!( + multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xff"[..]), + Err(Err::Incomplete(Needed::new(18446744073709551615))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn overflow_incomplete_many0() { + fn multi(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + many0(length_data(be_u64))(i) + } + + // Trigger an overflow in many0 + assert_eq!( + multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xef"[..]), + Err(Err::Incomplete(Needed::new(18446744073709551599))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn overflow_incomplete_many1() { + use nom::multi::many1; + + fn multi(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + many1(length_data(be_u64))(i) + } + + // Trigger an overflow in many1 + assert_eq!( + multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xef"[..]), + Err(Err::Incomplete(Needed::new(18446744073709551599))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn overflow_incomplete_many_till() { + use nom::{bytes::complete::tag, multi::many_till}; + + fn multi(i: &[u8]) -> IResult<&[u8], (Vec<&[u8]>, &[u8])> { + many_till(length_data(be_u64), tag("abc"))(i) + } + + // Trigger an overflow in many_till + assert_eq!( + multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xef"[..]), + Err(Err::Incomplete(Needed::new(18446744073709551599))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn overflow_incomplete_many_m_n() { + use nom::multi::many_m_n; + + fn multi(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + many_m_n(2, 4, length_data(be_u64))(i) + } + + // Trigger an overflow in many_m_n + assert_eq!( + multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xef"[..]), + Err(Err::Incomplete(Needed::new(18446744073709551599))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn overflow_incomplete_count() { + use nom::multi::count; + + fn counter(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + count(length_data(be_u64), 2)(i) + } + + assert_eq!( + counter(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xef"[..]), + Err(Err::Incomplete(Needed::new(18446744073709551599))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn overflow_incomplete_length_count() { + use nom::multi::length_count; + use nom::number::streaming::be_u8; + + fn multi(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + length_count(be_u8, length_data(be_u64))(i) + } + + assert_eq!( + multi(&b"\x04\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xee"[..]), + Err(Err::Incomplete(Needed::new(18446744073709551598))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn overflow_incomplete_length_data() { + fn multi(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + many0(length_data(be_u64))(i) + } + + assert_eq!( + multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xff"[..]), + Err(Err::Incomplete(Needed::new(18446744073709551615))) + ); +} diff --git a/third_party/rust/nom/tests/reborrow_fold.rs b/third_party/rust/nom/tests/reborrow_fold.rs new file mode 100644 index 0000000000..486617e427 --- /dev/null +++ b/third_party/rust/nom/tests/reborrow_fold.rs @@ -0,0 +1,31 @@ +#![allow(dead_code)] +// #![allow(unused_variables)] + +use std::str; + +use nom::bytes::complete::is_not; +use nom::character::complete::char; +use nom::combinator::{map, map_res}; +use nom::multi::fold_many0; +use nom::sequence::delimited; +use nom::IResult; + +fn atom<'a>(_tomb: &'a mut ()) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], String> { + move |input| { + map( + map_res(is_not(" \t\r\n"), str::from_utf8), + ToString::to_string, + )(input) + } +} + +// FIXME: should we support the use case of borrowing data mutably in a parser? +fn list<'a>(i: &'a [u8], tomb: &'a mut ()) -> IResult<&'a [u8], String> { + delimited( + char('('), + fold_many0(atom(tomb), String::new, |acc: String, next: String| { + acc + next.as_str() + }), + char(')'), + )(i) +} |