Skip to content

Commit

Permalink
compression done
Browse files Browse the repository at this point in the history
  • Loading branch information
Murat Yildirim committed Dec 1, 2024
1 parent f530abc commit 12ff73d
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 16 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target/
.vscode/
.vscode/
*.dat
65 changes: 59 additions & 6 deletions src/compression.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::huffman::{HuffmanLeafNode, HuffmanInternalNode, HuffmanNode};
use std::collections::{BinaryHeap, HashMap};
use std::{collections::{BinaryHeap, HashMap}, fs::File, io::{self, Write}};

pub struct CompressionTool {
input: String, // todo: should also support streams
Expand All @@ -12,20 +12,52 @@ impl CompressionTool {
}
}

pub fn compress(&mut self) -> Result<HuffmanNode, String> {
fn generate_frequency_map(&self) -> HashMap<char, i32> {
let mut map: HashMap<char, i32> = HashMap::new();

for ch in self.input.chars() {
let counter: &mut i32 = map.entry(ch).or_insert(0);
*counter += 1;
}

map
}

// Method to write header with frequency map to the output file
fn write_header(&self, file: &mut File, frequency_map: HashMap<char, i32>) -> io::Result<()> {
// Write the number of unique characters (for future decoding)
let num_chars: u32 = frequency_map.len() as u32;
file.write_all(&num_chars.to_le_bytes())?;

// Write the frequency table to the file
for (ch, count) in frequency_map {
file.write_all(&ch.to_string().as_bytes())?;
file.write_all(&count.to_le_bytes())?;
}

// Write a delimiter to indicate the end of the header
file.write_all(&[0x00])?;

Ok(())
}

pub fn compress(&mut self, output_file: &str) -> Result<(), String> {
let mut file: File = File::create(output_file).map_err(|e| e.to_string())?;

let frequency_map: HashMap<char, i32> = self.generate_frequency_map();

self.write_header(&mut file, frequency_map.clone())
.map_err(|e| format!("Error writing header: {}", e))?;



let mut heap: BinaryHeap<HuffmanNode> = BinaryHeap::new();
for (ch, count) in map {
for (ch, count) in frequency_map {
let leaf: HuffmanLeafNode = HuffmanLeafNode::new(count, ch);
heap.push(HuffmanNode::Leaf(leaf));
}

// Build the Huffman tree
while heap.len() > 1 {
// Pop the two nodes with the smallest frequencies
let left: HuffmanNode = heap.pop().unwrap();
Expand All @@ -39,12 +71,33 @@ impl CompressionTool {

let root = heap.pop().unwrap();

// Generate the prefix codes for each character
let mut codes: HashMap<char, String> = HashMap::new();
root.generate_prefix_codes(&mut codes);
for (ch, code) in &codes {
println!("Character: '{}' -> code: {}", ch, code);

// Now write the compressed data after the header
let compressed_data = self.compressed_data(&codes);
file.write_all(&compressed_data).map_err(|e| e.to_string())?;


Ok(())
}

fn compressed_data(&self, codes: &HashMap<char, String>) -> Vec<u8> {
let mut compressed_bits = String::new();
for ch in self.input.chars() {
compressed_bits.push_str(&codes[&ch]);
}

// Convert the binary string to byte vector
let mut result = Vec::new();
for chunk in compressed_bits.as_bytes().chunks(8) {
let byte = chunk.iter().fold(0, |acc, &bit| (acc <<1) | (bit - b'0') as u8);
result.push(byte);
}

Ok(root)
result
}


}
19 changes: 10 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ use compression_tool::compression::CompressionTool;

fn main() {
let args: Vec<String> = std::env::args().collect();
let file_path: String = args[1].to_string();
if args.len() != 3 {
println!("Usage: cargo run <input_file> <output_file>");
return;
}
let input_file: &String = &args[1];
let output_file: &String = &args[2];

let mut file: File = File::open(file_path).unwrap();

let mut file: File = File::open(input_file).unwrap();
let mut content: String = String::new();
let _ = file.read_to_string(&mut content);

let mut tool = CompressionTool::new(&content);
match tool.compress() {
Ok(root) => {
root.print_tree();
println!("{:?}", root)
},
let mut tool: CompressionTool = CompressionTool::new(&content);
match tool.compress(&output_file) {
Ok(_) => println!("Compression successfull, file written to '{}'", output_file),
Err(e) => println!("Error: {}", e),
}
}

0 comments on commit 12ff73d

Please sign in to comment.