A fully functional file compression tool implementing Huffman coding algorithm from scratch in Go. This project demonstrates lossless data compression using binary trees, priority queues, and bit manipulation.
- About
- Features
- How It Works
- Installation
- Usage
- Technical Implementation
- Performance
- Learning Journey
- Future Enhancements
- Acknowledgments
This project was inspired by Coding Challenges and implements the classic Huffman coding algorithm for lossless data compression. Built entirely from scratch without using compression libraries, this tool showcases fundamental computer science concepts including:
- Binary tree data structures
- Priority queues (min-heap)
- Greedy algorithms
- Bit manipulation
- File I/O streaming
- Lossless Compression: Perfectly reconstructs original files
- Streaming Architecture: Memory-efficient processing for large files
- Binary Tree Implementation: Custom Huffman tree with optimal prefix codes
- Priority Queue: Hand-rolled min-heap using generics
- Bit-Level Operations: Efficient bit packing and unpacking
- Comprehensive Testing: 30+ unit and integration tests
- Edge Case Handling: Single character files, empty files, binary data
Input File
β
1. Analyze character frequencies
β
2. Build Huffman tree (greedy algorithm)
β
3. Generate variable-length prefix codes
β
4. Write header (frequencies + metadata)
β
5. Encode data using generated codes
β
6. Pack bits into bytes
β
Compressed File (.hf)
Compressed File (.hf)
β
1. Read and parse header
β
2. Rebuild Huffman tree from frequencies
β
3. Read compressed bits
β
4. Traverse tree bit-by-bit
β
5. Decode characters at leaf nodes
β
Original File Restored
Prerequisites:
- Go 1.21 or higher
Clone and Build:
# Clone the repository
git clone https://github.com/yourusername/huffman-compressor.git
cd huffman-compressor
# Build the binary
go build -o huffman cmd/main.go
# Or install directly
go install# Basic compression
./huffman -compress -input file.txt -output file.hf
# The tool will display compression statistics:
# === Compression Statistics ===
# Original size: 1000 bytes
# Compressed size: 650 bytes
# Compression ratio: 65.00%
# Space saved: 350 bytes# Decompress back to original
./huffman -decompress -input file.hf -output restored.txt
# Verify files are identical
diff file.txt restored.txt # Should show no differences# Compress a text file with repetitive content (best compression)
./huffman -compress -input alice.txt -output alice.hf
# Compress a binary file
./huffman -compress -input image.bin -output image.hf
# Decompress
./huffman -decompress -input alice.hf -output alice_restored.txtCompressed File Structure (.hf):
[HEADER]
- Magic Number (2 bytes): "HF"
- Original Size (8 bytes): uint64
- Unique Characters (1 byte): uint8 (max 255)
- Padding Bits (1 byte): uint8 (0-7)
- Frequency Entries (N Γ 5 bytes):
- Character (1 byte)
- Frequency (4 bytes): uint32
[COMPRESSED DATA]
- Variable-length encoded bits packed into bytes
1. Priority Queue (Min-Heap)
// Generic implementation with custom comparators
type PriorityQueue[T any] struct {
heap *MinHeap[T]
}2. Huffman Tree Node
type HuffmanNode struct {
char byte
frequency int
left *HuffmanNode
right *HuffmanNode
isLeaf bool
}3. Huffman Code
type HuffmanCode struct {
bits uint64 // LSB-first storage
length int // Number of bits
}4. Bit Buffer (Writer)
// Streams bits to file, auto-flushing complete bytes
type BitBuffer struct {
currentByte byte
bitPosition int
writer io.Writer
}5. Bit Reader
// Reads individual bits from byte stream
type BitReader struct {
currentByte byte
bitPosition int
reader io.Reader
}- Tree Building: O(n log n) where n = unique characters
- Code Generation: O(n) tree traversal
- Compression: O(m) where m = file size
- Decompression: O(m Γ log n) for tree traversal
- Space: O(n) for tree + O(1) for streaming
| File Type | Size | Compressed | Ratio | Time |
|---|---|---|---|---|
| Text (repetitive) | 1 MB | 450 KB | 45% | ~50ms |
| Source code | 500 KB | 380 KB | 76% | ~30ms |
| Random data | 1 MB | 1.01 MB | 101% | ~60ms |
| Alice in Wonderland | 167 KB | 93 KB | 56% | ~15ms |
Note: Compression effectiveness depends on data repetition. Random data may slightly expand due to header overhead.
- Streaming I/O: Reads and writes in 1KB chunks
- Constant memory: O(1) additional space during encode/decode
- Tree overhead: ~5 bytes per unique character in header
This project was built as a learning exercise to understand:
-
Data Structures
- Binary trees (construction and traversal)
- Priority queues (heap implementation)
- Hash tables (frequency counting)
-
Algorithms
- Greedy algorithms (Huffman's approach)
- Tree traversal (DFS for code generation)
- Prefix-free encoding
-
Systems Programming
- File I/O streaming
- Bit manipulation
- Memory management
- Error handling
-
Go Language Features
- Generics (for priority queue)
- Interfaces (io.Reader/Writer)
- Defer statements
- Table-driven tests
- LSB-first storage with MSB-first writing: Implemented bit reversal in WriteBits
- Single character edge case: Added dummy node for tree construction
- Padding handling: Track padding bits in header for accurate decompression
- Streaming architecture: Process large files without loading into memory
- Uint8 overflow: Limited unique characters to 255 (practical constraint)
- Adaptive Huffman Coding: Update tree on-the-fly
- Dictionary Encoding: Combine with LZ77 for better compression
- Parallel Processing: Multi-threaded compression for large files
- Progress Indicators: Show progress for large file operations
- Compression Levels: Trade speed for ratio (like gzip -1 to -9)
- Directory Compression: Archive multiple files (like tar)
- GUI Interface: Desktop app with drag-and-drop
- Benchmark Suite: Automated performance testing
- 256 Character Support: Use uint16 for NumChars field
- Streaming API: Library interface for programmatic use
- Canonical Huffman: Simplify tree serialization
- Length-Limited Huffman: Bound code length for hardware
- Adaptive Huffman: Dynamic tree updates (LZSS + Huffman)
Run the comprehensive test suite:
# Run all tests
go test ./... -v
# Run with coverage
go test ./... -cover
# Run specific test package
go test ./test/compress_test.go -v
# Run benchmarks
go test ./... -bench=. -benchmemTest Coverage:
- Unit tests: Priority queue, tree building, code generation
- Integration tests: Full compress/decompress round-trips
- Edge cases: Empty files, single characters, binary data
- Error handling: Corrupted files, invalid headers
huffman-compressor/
βββ cmd/
β βββ main.go # CLI entry point
βββ internal/
β βββ huffman.go # Frequency analysis
β βββ tree.go # Tree construction
β βββ encoder.go # Code generation
β βββ bitbuffer.go # Bit writing
β βββ bitreader.go # Bit reading
β βββ header.go # File format handling
β βββ compress.go # Main compression logic
β βββ decompress.go # Main decompression logic
β βββ priority_queue.go # Min-heap implementation
βββ test/
β βββ *_test.go # Comprehensive test suites
βββ README.md
This is a learning project, but contributions are welcome! Feel free to:
- Report bugs
- Suggest features
- Submit pull requests
- Share improvements
MIT License - feel free to use this code for learning and projects.
- Coding Challenges by John Crickett for the project inspiration
- David Huffman for the algorithm (1952)
- Introduction to Algorithms (CLRS) for theoretical foundation
- The Go community for excellent documentation
Created by Syed Sibteali Baqar - sibteali786@gmail.com
Project Link: https://github.com/sibteali786/huffman-compressor
β If you found this project helpful for learning, please star it on GitHub!