VLC handles most audio, video and streaming formats
fuzzing
enum Option<T> {
Some(T),
None
}
enum Result<T, E> {
Ok(T),
Err(E)
}
fn f(i: u8) -> Option<u8> { ... }
match f(1) {
Some(x) => println!("got int: {}", x),
None => println!("got None")
}
if let Some(x) = f(2) {
...
}
let x = 5;
x = 6; // error
//////
let mut x = 5;
x = 6; // ok
fn to_vec(a:u8, b: u8) -> Vec<u8> {
let mut v = Vec::new();
v.push(a);
v.push(b);
v
}
pub struct Slice<T> {
pub data: *const T,
pub len: usize,
}
let vec = vec![1, 2, 3];
let int_slice = &vec[1..];
let v = vec![1, 2, 3];
let v2 = v;
println!("v[0] is: {}", v[0]);
//error: use of moved value: `v`
//println!("v[0] is: {}", v[0]);
// ^
fn take<'a>(x: &'a [u8], count: usize) -> &'a [u8]{ }
a bet that we can make zero copy parsers in Rust
pub enum IResult<'a,I,O> {
Done(I,O),
Error(Err<'a>),
Incomplete(Needed)
}
pub enum Err<'a>{
Code(u32),
Node(u32, Box<Err<'a>>),
Position(u32, &'a [u8]),
NodePosition(u32, &'a [u8], Box<Err<'a>>)
}
pub enum Needed {
Unknown,
Size(u32)
}
IResult<&[u8], &[u8]>
IResult<&[u8], &str>
IResult<&str, MyStruct>
pub fn digit<'a>(input:&'a [u8]) -> IResult<'a,&'a [u8], &[u8]> {
for idx in 0..input.len() {
if !is_digit(input[idx]) {
if idx == 0 {
return Error(Position(ErrorCode::Digit as u32, input))
} else {
return Done(&input[idx..], &input[0..idx])
}
}
}
Done(b"", input)
}
named!( not_space, is_not!( " \t\r\n" ) );
let r = not_space(&b"abcdefgh\nijkl"[..]);
// -> Done(&b"\nijkl"[..], &b"abcdefgh"[..])
named! defines a function
macros make it easy to define complex parsers
struct FileType<'a> {
major_brand: &'a str,
major_brand_version: &'a [u8],
compatible_brands: Vec<&'a str>
}
named!(brand_name<&[u8],&str>, map_res!(take!(4), str::from_utf8));
named!(mp4_filetype<&[u8], FileType>,
chain!(
m: brand_name ~
v: take!(4) ~
c: many0!(brand_name) ,
|| { FileType {
major_brand: m,
major_brand_version: v,
compatible_brands: c
} }
)
);
small.mp4 (375 kB) | bigbuckbunny.mp4 (5.3 MB) | |
---|---|---|
hammer | 32424 ns/iter | 26523 ns/iter |
attoparsec | 1138 ns/iter (+/- 55.2) | 1124 ns/iter (+/- 62.3) |
cereal | 189 ns/iter (+/- 9.9) | 193 ns/iter (+/- 12.4) |
nom | 240 ns/iter (+/- 56) | 195 ns/iter (+/- 69) |
pub enum Needed {
Unknown,
Size(u32)
}
pub enum IResult<'a,I,O> {
Done(I,O),
Error(Err<'a>),
Incomplete(Needed)
}
pub fn be_u16<'a>(i: &[u8]) -> IResult<'a,&[u8], u16> {
if i.len() < 2 {
Incomplete(Needed::Size(2))
} else {
let res = ((i[0] as u16) << 8) + i[1] as u16;
Done(&i[2..], res)
}
}
pub enum ProducerState<O> {
Eof(O),
Continue,
Data(O),
ProducerError(u32),
}
pub trait Producer {
fn produce(&mut self) -> ProducerState<&[u8]>;
fn seek(&mut self, position:SeekFrom) -> Option<u64>;
}
fn local_print<'a, T: Debug>(input: T) -> IResult<'a, T, ()> {
println!("{:?}", input);
Done(input, ())
}
FileProducer::new("links.txt", 20).map(|producer: FileProducer| {
let mut p = producer;
pusher!(pipeline, local_print);
pipeline(&mut p);
});
pub enum ConsumerState {
Await(
usize, // consumed
usize // needed buffer size
),
Seek(
usize, // consumed
SeekFrom, // new position
usize // needed buffer size
),
Incomplete,
ConsumerDone,
ConsumerError(u32)
}
pub trait Consumer {
fn consume(&mut self, input: &[u8]) -> ConsumerState;
fn end(&mut self);
fn run(&mut self, producer: &mut Producer) { ... }
}
enum State {
Beginning,
Middle,
End,
Done
}
struct TestConsumer {
state: State,
counter: usize,
}
named!(om_parser, tag!("om"));
named!(nomnom_parser<&[u8],Vec<&[u8]> >, many1!(tag!("nom")));
named!(end_parser, tag!("kthxbye"));
impl Consumer for TestConsumer {
fn consume(&mut self, input: &[u8]) -> ConsumerState {
match self.state {
State::Beginning => {
match om_parser(input) {
IResult::Error(_) => ConsumerState::ConsumerError(0),
IResult::Incomplete(_) => ConsumerState::Await(0, 2),
IResult::Done(_,_) => {
self.state = State::Middle;
ConsumerState::Await(2, 3)
}
}
},
[ ... ]
}
}
fn end(&mut self) {
println!("counted {} noms", self.counter);
}
}
machine!(TrafficLight {
attributes { cars: u8 }
impl { }
states {
Green { } => {
next => Orange;
};
Orange { } => {
next => Red;
};
Red { } => {
next => Green;
};
}
});
impl TrafficLight<Green> {
pub fn pass_car(&mut self) {
self.cars = self.cars + 1;
}
}
let mut t = TrafficLight { state: Green, cars: 0 };
t.pass_car();
let t = t.next();
t.pass_car();
// error: no method named `pass_car` found for type `TrafficLight<Orange>` in the current scope
// t.pass_car();
// ^~~~~~~~~~
actually, would be more interesting to handle errors at runtime
slice.to_hex(8);
ftyp header:
00000000 6d 70 34 32 00 00 00 00 mp42....
00000008 6d 70 34 32 69 73 6f 6d mp42isom
00000010 61 76 63 31 avc1
named!(f, dbg!( tag!( "abcd" ) ) );
let a = &b"efgh"[..];
f(a);
// -> Error(Position(0, [101, 102, 103, 104])) at l.1 by ' tag ! ( "abcd" ) '
named!(f, dbg_dmp!( tag!( "abcd" ) ) );
let a = &b"efghijkl"[..];
f(a);
// -> Error(Position(0, [101, 102, 103, 104, 105, 106, 107, 108]))
// at l.1 by ' tag ! ( "abcd" ) '
// -> 00000000 65 66 67 68 69 6a 6b 6c efghijkl
pub enum Err<'a>{
Code(u32),
Node(u32, Box<Err<'a>>),
Position(u32, &'a [u8]),
NodePosition(u32, &'a [u8], Box<Err<'a>>)
}
pub enum IResult<'a,I,O> {
Done(I,O),
Error(Err<'a>),
Incomplete(Needed)
}
named!(err_test, alt!(
tag!("abcd")
| preceded!(tag!("efgh"), error!(42,
chain!(
tag!("ijkl") ~
res: error!(128, tag!("mnop")) ,
|| { res }
)
)
)
));
let a = &b"efghblah"[..];
let err = err_test(a);
// -> Error(NodePosition(42, blah, Box::new(Position(0, &b"blah"[..]))))
named!(err_test, alt!(
tag!("abcd")
| preceded!(tag!("efgh"), error!(42,
chain!(
tag!("ijkl") ~
res: error!(128, tag!("mnop")) ,
|| { res }
)
)
)
));
let b = &b"efghijklblah"[..];
let blah = &b"blah"[..];
let err = err_test(b);
// -> Error(
// NodePosition(42, &b"ijklblah"[..],
// Box::new(NodePosition(128, blah,
// Box::new(Position(0, blah))
// ))
// )
// )
what can we do with that?
use nom::util::error_to_list;
fn error_to_string(e: Err) -> &str {
let v:Vec<u32> = error_to_list(e);
match &v[..] {
[42, 0] => "missing `ijkl` tag",
[42, 128, 0] => "missing `mnop` tag after `ijkl`",
_ => "unrecognized error"
}
}
let mut err_map = collections::HashMap::new();
add_error_pattern(
&mut err_map,
err_test(&b"efghaaaa"[..]),
"missing `ijkl` tag"
);
add_error_pattern(
&mut err_map,
err_test(&b"efghijklaaaa"[..]),
"missing `mnop` tag after `ijkl`"
);
if let IResult::Error(e) = err_test(&b"efghblah"[..]) {
let msg = err_map.get(&error_to_list(e));
// -> "missing `ijkl` tag"
};
if let IResult::Error(e) = err_test(&b"efghijklblah"[..]) {
let msg = err_map.get(&error_to_list(e))
// -> "missing `mnop` tag after `ijkl`"
};
idea: generate error patterns from known bad inputs
named!(err_test, alt!(
tag!("abcd") |
error!(12, preceded!(tag!("efgh"),
error!(42, chain!(
tag!("ijk") ~
res: error!(128, tag!("mnop")) ,
|| { res }
))
))
));
remove dependance on the standard library
can do static or dynamic lib