更新libclamav库1.0.0版本

This commit is contained in:
2023-01-14 18:28:39 +08:00
parent b879ee0b2e
commit 45fe15f472
8531 changed files with 1222046 additions and 177272 deletions

View File

@@ -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)));
}

View File

@@ -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)")))
);
}

View File

@@ -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,
}
))
);
}

View File

@@ -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)
}

View File

@@ -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)))
);
}

View File

@@ -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)));
}

View File

@@ -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);
}

View File

@@ -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)));
}

View File

@@ -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)));
}

View File

@@ -0,0 +1,216 @@
//#![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')));
}

View File

@@ -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()
)
))
);
}

View File

@@ -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 }))
}

View File

@@ -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);
}

View File

@@ -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)))
);
}

View File

@@ -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)
}