Skip to content

Commit d783b3c

Browse files
author
Van-nam Do
committed
wc command
1 parent e3b7de0 commit d783b3c

File tree

4 files changed

+72
-74
lines changed

4 files changed

+72
-74
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/target
2-
.idea
2+
.idea
3+
.DS_Store

challenges/wc-command/src/lib.rs

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,70 @@
1-
use core::num;
2-
use std::sync::atomic::AtomicUsize;
3-
use rand::Rng;
4-
use rayon::{prelude::*, vec};
5-
use rust_coding_challenges::utils::read_text_file_from_args;
1+
use core::str;
62
use std::cmp::min;
7-
use std::{fs::*, os};
8-
use std::io::{BufRead, BufReader, Read};
9-
use std::sync::{mpsc, Arc, Mutex};
10-
use std::thread;
11-
use std::time::Instant;
3+
use std::fs::File;
4+
use std::sync::atomic::AtomicUsize;
125
use std::sync::atomic::Ordering;
6+
use std::sync::Arc;
7+
use std::thread;
8+
use memmap2::MmapOptions;
139

14-
pub fn solve(text: &str) -> usize {
15-
let num_threads = std::thread::available_parallelism().unwrap().get();
16-
count_words_parallel(text, num_threads)
10+
pub fn read_file_as_string(file_path: &str) -> String {
11+
let file = File::open(file_path).expect("Path is not valid");
12+
let mmap = unsafe { MmapOptions::new().map(&file).expect("Cannot map from file") };
13+
let text = unsafe { str::from_utf8_unchecked(&mmap).to_string() };
14+
text
1715
}
1816

17+
pub fn count_words(text: &str) -> usize {
18+
count_in_paralell(text, count_words_in_chunk)
19+
}
1920

21+
pub fn count_word_occurrences(text: &str, word: String) -> usize {
22+
count_in_paralell(text, move |chunk| count_word_occurrences_in_chunk(chunk, &word))
23+
}
2024

21-
fn count_words_parallel(text: &str, num_threads: usize) -> usize {
25+
fn count_in_paralell<F>(text: &str, count_fn: F) -> usize
26+
where
27+
F: Fn(&[String]) -> usize + Send + Sync + 'static,
28+
{
29+
let num_threads: usize = std::thread::available_parallelism().unwrap().get();
2230
let lines: Vec<String> = text.lines().map(|line| line.to_string()).collect();
2331
let lines_per_thread = (lines.len() + num_threads - 1) / num_threads;
2432
let total_word_count = Arc::new(AtomicUsize::new(0));
2533
let mut handles = Vec::new();
34+
let count_fn = Arc::new(count_fn);
2635

2736
for i in 0..num_threads {
2837
let start = lines_per_thread * i;
2938
let end = min(start + lines_per_thread, lines.len());
30-
31-
let total_word_count_clone = Arc::clone(&total_word_count);
3239
let chunk = lines[start..end].to_vec();
40+
let count_fn_clone = Arc::clone(&count_fn);
41+
let total_word_count_clone = total_word_count.clone();
3342

3443
let handle = thread::spawn(move || {
35-
let count = count_words_in_chunk(&chunk);
36-
total_word_count_clone.fetch_add(count, Ordering::Relaxed)
44+
let count = count_fn_clone(&chunk);
45+
total_word_count_clone.fetch_add(count, Ordering::Relaxed);
3746
});
3847
handles.push(handle);
3948
}
49+
4050
for handle in handles {
4151
handle.join().unwrap();
4252
}
4353
total_word_count.load(Ordering::Relaxed)
4454
}
4555

56+
4657
fn count_words_in_chunk(chunk: &[String]) -> usize {
4758
chunk
4859
.iter()
4960
.flat_map(|line| line.split_whitespace())
5061
.count()
5162
}
5263

53-
54-
// fn count_words_in_chunk(chunk: &str) -> usize {
55-
// chunk.split_whitespace().count()
56-
// }
64+
fn count_word_occurrences_in_chunk(chunk: &[String], word: &str) -> usize {
65+
chunk
66+
.iter()
67+
.flat_map(|line| line.split_whitespace())
68+
.filter(|&w| w == word)
69+
.count()
70+
}

challenges/wc-command/src/main.rs

Lines changed: 34 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,41 @@
11

2-
use memmap2::MmapOptions;
3-
use wc_command::solve;
4-
use std::{fs::File, io::{self, BufRead, BufReader, Read, Write}, str, time::Instant};
2+
use wc_command::{count_word_occurrences, count_words, read_file_as_string};
3+
use std::env;
54

6-
fn main() -> std::io::Result<()> {
7-
let start = Instant::now();
8-
// let file_path = "/Users/namdo/Desktop/learntocodetogether/random_1m_lines.txt";
9-
let file_path = "/Users/namdo/Desktop/learntocodetogether/random_10m_lines.txt";
10-
let text = read_file_to_string(file_path)?;
11-
let wc = solve(&text);
12-
println!("total words: {:?}", wc);
13-
println!("total time: {:?}", start.elapsed());
14-
println!("OK: {}", 8503930 == wc);
5+
fn main() {
6+
let args: Vec<String> = env::args().collect();
157

16-
let time = Instant::now();
17-
let file = File::open(file_path)?;
18-
19-
let mmap = unsafe { MmapOptions::new().map(&file)? };
20-
let text = unsafe { str::from_utf8_unchecked(&mmap) };
21-
let str_time = Instant::now();
22-
println!("finish converting to string: {:?}", str_time.elapsed());
23-
solve(text);
24-
println!("Elapsed time for mmap: {:?}", time.elapsed());
25-
Ok(())
26-
}
27-
28-
29-
fn million_lines(n: usize, file_path: &str, new_file_path: &str) -> io::Result<()> {
30-
let mut src = File::open(file_path)?;
31-
32-
let mut content = String::new();
33-
src.read_to_string(&mut content)?;
34-
35-
let mut des = File::create(new_file_path)?;
36-
37-
for i in 0..n {
38-
des.write_all(content.as_bytes())?;
8+
if args.len() < 2 || args[1] != "rccwc" {
9+
eprintln!("Usage: rccwc -w <file_path> or rccwc -wo <word> <file_path>");
10+
return;
3911
}
4012

41-
Ok(())
42-
}
43-
44-
fn read_file_to_string(file_path: &str) -> io::Result<String>{
45-
let file = File::open(file_path)?;
46-
let mut reader = BufReader::new(file);
47-
let mut buffer = String::new();
48-
let mut content = String::new();
49-
while reader.read_line(&mut buffer)? > 0 {
50-
content.push_str(&buffer);
51-
buffer.clear();
13+
match args[2].as_str() {
14+
"-w" => {
15+
if args.len() != 4 {
16+
eprintln!("Usage: rccwc -w <file>");
17+
return;
18+
}
19+
let file_path = &args[3];
20+
let text = read_file_as_string(&file_path);
21+
let total_words = count_words(&text);
22+
println!("Total words: {}", total_words);
23+
}
24+
"-wo" => {
25+
if args.len() != 5 {
26+
eprintln!("Usage: rccwc -wc <word> <file_path>");
27+
return;
28+
}
29+
let word = &args[3];
30+
let file_path = &args[4];
31+
let text = read_file_as_string(&file_path);
32+
let total_occurrences = count_word_occurrences(&text, word.to_string());
33+
println!("Total occurrences of '{}': {} ", word, total_occurrences);
34+
}
35+
_ => {
36+
eprintln!("Invalid option. Usage: rccwc -w <file_path> or -wo <word> <file_path>")
37+
}
5238
}
53-
Ok(content)
39+
5440
}
41+

challenges/wc-command/tests/test.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
#[cfg(test)]
22
mod tests {
3-
use wc_command::solve;
43

54

65
#[test]
76
fn test_solve() {
87
// Call the solve function and check for expected results
9-
let result = solve(str);
10-
assert!(result.is_ok());
11-
// Add more assertions based on expected behavior
128
}
139
}

0 commit comments

Comments
 (0)