Last active
August 26, 2016 19:48
-
-
Save malleusinferni/2d7a51687f399c82747db20e724a76ba to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate rand; | |
extern crate time; | |
use std::path::Path; | |
use std::iter::Peekable; | |
use std::slice; | |
struct Parser<'input> { | |
input: Peekable<slice::Iter<'input, u8>>, | |
line: usize, | |
} | |
#[derive(Debug)] | |
struct Row(u32, u32, f32); | |
#[derive(Debug)] | |
struct LineNr(usize); | |
impl<'input> Parser<'input> { | |
fn new(input: &'input [u8]) -> Self { | |
Parser { | |
input: input.iter().peekable(), | |
line: 1, | |
} | |
} | |
#[inline(always)] | |
fn parse_row(&mut self) -> Result<Row, LineNr> { | |
let src = try!(self.parse_int()); | |
try!(self.expect(b' ')); | |
try!(self.expect(b'-')); | |
try!(self.expect(b' ')); | |
let dst = try!(self.parse_int()); | |
try!(self.expect(b',')); | |
let magnitude = try!(self.parse_float()); | |
try!(self.parse_eol()); | |
Ok(Row(src, dst, magnitude)) | |
} | |
#[inline(always)] | |
fn parse_int(&mut self) -> Result<u32, LineNr> { | |
let mut accum = 0; | |
while let Some(n) = self.parse_digit() { | |
accum *= 10; | |
accum += n; | |
} | |
Ok(accum) | |
} | |
#[inline(always)] | |
fn parse_float(&mut self) -> Result<f32, LineNr> { | |
let a = try!(self.parse_int()) as f32; | |
try!(self.expect(b'.')); | |
let mut b = 0.0f32; | |
let mut pow = 1; | |
while let Some(n) = self.parse_digit() { | |
b = b.mul_add(10.0, n as f32); | |
pow += 1; | |
} | |
Ok(a + b * (10.0f32).powi(-pow + 1)) | |
} | |
#[inline(always)] | |
fn parse_digit(&mut self) -> Option<u32> { | |
match self.input.peek() { | |
Some(&&u) if b'0' <= u && u <= b'9' => { | |
self.input.next(); | |
Some((u - b'0') as u32) | |
}, | |
_ => None, | |
} | |
} | |
#[inline(always)] | |
fn parse_eol(&mut self) -> Result<(), LineNr> { | |
match self.input.next() { | |
None => Ok(()), | |
Some(&b'\n') => { self.line += 1; Ok(()) }, | |
_ => Err(LineNr(self.line)), | |
} | |
} | |
#[inline(always)] | |
fn expect(&mut self, byte: u8) -> Result<(), LineNr> { | |
match self.input.next() { | |
Some(&n) if n == byte => Ok(()), | |
_ => Err(LineNr(self.line)), | |
} | |
} | |
} | |
impl<'input> Iterator for Parser<'input> { | |
type Item = Result<Row, LineNr>; | |
fn next(&mut self) -> Option<Self::Item> { | |
if self.input.peek().is_some() { | |
Some(self.parse_row()) | |
} else { | |
None | |
} | |
} | |
} | |
#[test] | |
fn test_parse() { | |
let mut parser = Parser::new(b"2224 - 26466,79.2503490704\n"); | |
let Row(src, dst, magnitude) = parser.parse_row().expect("Bad parse"); | |
assert_eq!(src, 2224); | |
assert_eq!(dst, 26466); | |
assert_eq!(magnitude, 79.2503490704); | |
} | |
fn write_test_data(path: &Path) { | |
use std::io::Write; | |
use std::fs::File; | |
use rand::{Rng, StdRng}; | |
println!("Generating test data..."); | |
let mut rng = StdRng::new().expect("Couldn't start RNG"); | |
let mut outfile = File::create(path).expect("Couln't open"); | |
for _ in 0 .. 100_000 { | |
let src: u32 = rng.gen(); | |
let dst: u32 = rng.gen(); | |
let magnitude: f64 = rng.gen::<f64>() * 10.0 + 25.0; | |
writeln!(&mut outfile, "{} - {},{}", src, dst, magnitude) | |
.expect("Couldn't write"); | |
} | |
println!("Done."); | |
} | |
fn read_test_data(path: &Path) { | |
use std::io::Read; | |
use std::fs::File; | |
let mut infile = File::open(path).expect("Couldn't open"); | |
let mut buf = Vec::with_capacity(100_000); | |
infile.read_to_end(&mut buf).expect("Oh no"); | |
for result in Parser::new(&buf) { | |
result.expect("Parse failed"); | |
} | |
} | |
fn main() { | |
let path = "test.dat"; | |
write_test_data(path.as_ref()); | |
let start_s = time::precise_time_s(); | |
let start_ns = time::precise_time_ns(); | |
read_test_data(path.as_ref()); | |
let end_s = time::precise_time_s(); | |
let end_ns = time::precise_time_ns(); | |
let duration_ns = end_ns - start_ns; | |
let per_iter = duration_ns / 100_000; | |
println!("Execution took {} seconds", end_s - start_s); | |
println!("{} ns per iteration", per_iter); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment