| extern crate combine; | |
| use combine::*; | |
| use std::fs::File; | |
| use std::env; | |
| // Seems like combine does not support any kind of zero-copy parsing | |
| #[derive(Debug, Clone)] | |
| struct Request { | |
| method: Vec<u8>, | |
| uri: Vec<u8>, | |
| version: Vec<u8>, | |
| } | |
| #[derive(Debug)] | |
| struct Header { | |
| name: Vec<u8>, | |
| value: Vec<Vec<u8>>, | |
| } | |
| fn is_token(c: u8) -> bool { | |
| c < 128 && c > 31 && b"()<>@,;:\\\"/[]?={} \t".iter().position(|&i| i == c).is_none() | |
| } | |
| fn is_horizontal_space(c: u8) -> bool { c == b' ' || c == b'\t' } | |
| fn is_space(c: u8) -> bool { c == b' ' } | |
| fn is_not_space(c: u8) -> bool { c != b' ' } | |
| fn is_http_version(c: u8) -> bool { c >= b'0' && c <= b'9' || c == b'.' } | |
| fn main() { | |
| let mut contents: Vec<u8> = Vec::new(); | |
| { | |
| use std::io::Read; | |
| let mut file = File::open(env::args().nth(1).expect("File to read")).ok().expect("Failed to open file"); | |
| let _ = file.read_to_end(&mut contents).unwrap(); | |
| } | |
| // Making a closure, because parser instances cannot be reused | |
| let end_of_line = || (satisfy(|&c| c == b'\r').skip(satisfy(|&c| c == b'\n'))).or(satisfy(|&c| c == b'\n')); | |
| // Cannot use char() as it requires a char, instead use satisfy and dereference the pointer to | |
| // the item | |
| let http_version = satisfy(|&c| c == b'H') | |
| .skip(satisfy(|&c| c == b'T')) | |
| .skip(satisfy(|&c| c == b'T')) | |
| .skip(satisfy(|&c| c == b'P')) | |
| .skip(satisfy(|&c| c == b'/')) | |
| // Need a map() here to be able to use FromIterator<u8> | |
| .with(many1(satisfy(|&c| is_http_version(c)).map(|t: &u8| *t))); | |
| let request_line = ( | |
| // Yet again, dereferencing pointers before checking if it is a token | |
| many1(satisfy(|&c: &u8| is_token(c)).map(|t: &u8| *t)), | |
| skip_many1(satisfy(|&c: &u8| is_space(c)).map(|t: &u8| *t)), | |
| many1(satisfy(|&c: &u8| is_not_space(c)).map(|t: &u8| *t)), | |
| skip_many1(satisfy(|&c: &u8| is_space(c)).map(|t: &u8| *t)), | |
| http_version, | |
| ).map(|(method, _, uri, _, version)| Request { | |
| method: method, | |
| uri: uri, | |
| version: version, | |
| }); | |
| let message_header_line = ( | |
| skip_many1(satisfy(|&c| is_horizontal_space(c))), | |
| many1(satisfy(|&c| c != b'\r' && c != b'\n').map(|t: &u8| *t)), | |
| end_of_line()) | |
| .map(|(_, line, _)| line); | |
| let message_header = ( | |
| many1(satisfy(|&c| is_token(c)).map(|t: &u8| *t)), | |
| satisfy(|&c| c == b':'), | |
| many1(message_header_line) | |
| ).map(|(name, _, value)| Header { | |
| name: name, | |
| value: value, | |
| }); | |
| let mut request = ( | |
| request_line, | |
| end_of_line(), | |
| many(message_header), | |
| end_of_line() | |
| ).map(|(request, _, headers, _)| (request, headers)); | |
| let mut i = 0; | |
| let mut buf = &contents[..]; | |
| loop { | |
| // Needed for inferrence for many(message_header) | |
| let r: Result<((Request, Vec<Header>), _), _> = request.parse(buf); | |
| match r { | |
| Ok(((_, _), b)) => { | |
| i = i + 1; | |
| buf = b | |
| }, | |
| Err(e) => { | |
| panic!("{:?}", e); | |
| } | |
| } | |
| if buf.is_empty() { | |
| break; | |
| } | |
| } | |
| println!("{:?}", i); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment