Skip to content

Instantly share code, notes, and snippets.

@m4rw3r
Created August 20, 2015 15:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save m4rw3r/6370f617199af2d6ca78 to your computer and use it in GitHub Desktop.
Save m4rw3r/6370f617199af2d6ca78 to your computer and use it in GitHub Desktop.
Version of the attoparsec example using the parser combinator Combine.
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