Skip to content

Commit 6701da7

Browse files
authored
Merge pull request #39 from rubytogether/segiddins/stop-allocating-strings-for-each-line
Stop allocating strings for each line
2 parents bbada7c + 16cbe00 commit 6701da7

File tree

1 file changed

+61
-14
lines changed

1 file changed

+61
-14
lines changed

src/lib.rs

+61-14
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ pub fn print_unknown_user_agents(path: &str, opts: &Options) {
133133
});
134134
}
135135

136-
pub fn count_line(times: &mut TimeMap, line: String) {
136+
pub fn count_line(times: &mut TimeMap, line: &str) {
137137
let r: request::Request = serde_json::from_str(&line).unwrap();
138138

139139
if duplicate_request(&r) {
@@ -158,11 +158,25 @@ pub fn count_line(times: &mut TimeMap, line: String) {
158158
}
159159
}
160160

161-
pub fn stream_stats(stream: Box<dyn BufRead>, opts: &Options) -> TimeMap {
161+
pub fn stream_stats<'a>(mut stream: Box<dyn BufRead + 'a>, opts: &Options) -> TimeMap {
162162
let mut times = TimeMap::default();
163163
let mut lineno = 0;
164164

165-
stream.lines().for_each(|line| {
165+
let mut line = String::with_capacity(1024*1024);
166+
167+
loop {
168+
line.clear();
169+
match stream.read_line(&mut line) {
170+
Ok(0) => break,
171+
Ok(_) => {}
172+
Err(e) => {
173+
if opts.verbose {
174+
eprintln!("Failed to read line:\n {}", e);
175+
}
176+
continue;
177+
}
178+
}
179+
166180
if opts.verbose {
167181
lineno += 1;
168182
if lineno % 100_000 == 0 {
@@ -171,17 +185,8 @@ pub fn stream_stats(stream: Box<dyn BufRead>, opts: &Options) -> TimeMap {
171185
}
172186
}
173187

174-
match line {
175-
Ok(l) => {
176-
count_line(&mut times, l);
177-
}
178-
Err(e) => {
179-
if opts.verbose {
180-
eprintln!("Failed to read line:\n {}", e);
181-
}
182-
}
183-
}
184-
});
188+
count_line(&mut times, line.as_str());
189+
}
185190

186191
if opts.verbose {
187192
println!();
@@ -195,3 +200,45 @@ pub fn file_stats(path: &str, opts: &Options) -> TimeMap {
195200
let file_stream = file::reader(path, opts);
196201
stream_stats(file_stream, opts)
197202
}
203+
204+
205+
#[cfg(test)]
206+
mod tests {
207+
extern crate test;
208+
209+
use super::*;
210+
use std::fs::File;
211+
use std::io::BufReader;
212+
use test::Bencher;
213+
214+
#[test]
215+
fn test_stream_stats() {
216+
let file = File::open("test/sample_500.log").unwrap();
217+
let reader = BufReader::new(file);
218+
let opts = Options {
219+
verbose: false,
220+
unknown: false,
221+
paths: vec![],
222+
};
223+
let times = stream_stats(Box::new(reader), &opts);
224+
assert_eq!(times.len(), 45);
225+
}
226+
227+
#[bench]
228+
fn bench_stream_stats_sample_500(b: &mut Bencher) {
229+
let mut logs = Vec::new();
230+
File::open("test/sample_500.log")
231+
.unwrap()
232+
.read_to_end(&mut logs)
233+
.unwrap();
234+
let opts = Options {
235+
verbose: false,
236+
unknown: false,
237+
paths: vec![],
238+
};
239+
b.iter(|| {
240+
let reader = Box::new(BufReader::new(logs.as_slice()));
241+
stream_stats(reader, &opts);
242+
});
243+
}
244+
}

0 commit comments

Comments
 (0)