Skip to content

Commit 3a6d17a

Browse files
committed
Implement code changes to enhance functionality and improve performance
1 parent b670f55 commit 3a6d17a

File tree

5 files changed

+200
-1407
lines changed

5 files changed

+200
-1407
lines changed
0 Bytes
Binary file not shown.

compressor/hfc/io.go

Lines changed: 135 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -167,29 +167,71 @@ func ReadHuffmanCodes(file io.Reader) (map[rune]string, error) {
167167
// the number of bits used in the last byte to the output. If an error occurs during reading, processing, or
168168
// writing, the function returns the error.
169169
func compressData(input io.Reader, output io.Writer, codes map[rune]string) (uint64, error) {
170-
var currentByte byte
171-
var bitCount uint8
172-
compressedLength := uint64(0)
170+
return compressDataWithProgress(input, output, codes, 0, "", nil)
171+
}
172+
173+
// compressionState holds the state during compression
174+
type compressionState struct {
175+
currentByte byte
176+
bitCount uint8
177+
compressedLength uint64
178+
processedBytes int64
179+
}
180+
181+
// compressDataWithProgress is the enhanced version that supports progress reporting
182+
func compressDataWithProgress(input io.Reader, output io.Writer, codes map[rune]string, fileSize int64, fileName string, progressCallback utils.ProgressCallback) (uint64, error) {
183+
state := &compressionState{}
184+
185+
if err := processCompressionLoop(input, output, codes, fileSize, fileName, progressCallback, state); err != nil {
186+
return 0, err
187+
}
188+
189+
return finalizeCompression(output, state)
190+
}
191+
192+
// processCompressionLoop handles the main compression loop
193+
func processCompressionLoop(input io.Reader, output io.Writer, codes map[rune]string, fileSize int64, fileName string, progressCallback utils.ProgressCallback, state *compressionState) error {
173194
buf := make([]byte, constants.BUFFER_SIZE)
174195

175196
for {
176197
n, err := input.Read(buf)
177198
if err != nil && err != io.EOF {
178-
return 0, fmt.Errorf(constants.BUFFER_READ_ERROR, err)
199+
return fmt.Errorf(constants.BUFFER_READ_ERROR, err)
179200
}
180201
if n == 0 {
181202
break // EOF reached
182203
}
183204

184-
if err := processByte(buf[:n], output, codes, &currentByte, &bitCount, &compressedLength); err != nil {
185-
return 0, fmt.Errorf(constants.ERROR_COMPRESS, err)
205+
if err := processByte(buf[:n], output, codes, &state.currentByte, &state.bitCount, &state.compressedLength); err != nil {
206+
return fmt.Errorf(constants.ERROR_COMPRESS, err)
186207
}
208+
209+
// Update progress after processing each chunk - report every 0.01%
210+
state.processedBytes += int64(n)
211+
reportCompressionProgress(progressCallback, state.processedBytes, fileSize, fileName)
187212
}
213+
214+
return nil
215+
}
216+
217+
// reportCompressionProgress reports the current compression progress
218+
func reportCompressionProgress(progressCallback utils.ProgressCallback, processedBytes, fileSize int64, fileName string) {
219+
if progressCallback != nil && fileSize > 0 {
220+
progress := float64(processedBytes) / float64(fileSize)
221+
if progress > 1.0 {
222+
progress = 1.0
223+
}
224+
progressCallback(progress, fmt.Sprintf("Compressing: %s (%.2f%%)", fileName, progress*100))
225+
}
226+
}
227+
228+
// finalizeCompression handles the final compression steps
229+
func finalizeCompression(output io.Writer, state *compressionState) (uint64, error) {
188230
// if there are remaining bits in the current byte, write them to the output
189-
if bitCount > 0 {
231+
if state.bitCount > 0 {
190232
// Pad the last byte with zeros
191-
currentByte <<= 8 - bitCount
192-
if err := binary.Write(output, binary.LittleEndian, currentByte); err != nil {
233+
state.currentByte <<= 8 - state.bitCount
234+
if err := binary.Write(output, binary.LittleEndian, state.currentByte); err != nil {
193235
return 0, fmt.Errorf(constants.FILE_WRITE_ERROR, err)
194236
}
195237
} else {
@@ -198,13 +240,13 @@ func compressData(input io.Reader, output io.Writer, codes map[rune]string) (uin
198240
}
199241
}
200242
// Write the number of bits in the last byte
201-
if err := binary.Write(output, binary.LittleEndian, bitCount); err != nil {
243+
if err := binary.Write(output, binary.LittleEndian, state.bitCount); err != nil {
202244
return 0, fmt.Errorf(constants.FILE_WRITE_ERROR, err)
203245
}
204246

205-
compressedLength += 2 // 1 byte for the last byte and 1 byte for the number of bits in the last byte
247+
state.compressedLength += 2 // 1 byte for the last byte and 1 byte for the number of bits in the last byte
206248

207-
return compressedLength, nil
249+
return state.compressedLength, nil
208250
}
209251

210252
// processByte processes a buffer of bytes, compressing it using Huffman codes and writing the result to an output writer.
@@ -266,57 +308,111 @@ func processByte(buf []byte, output io.Writer, codes map[rune]string, currentByt
266308
// Returns:
267309
// - error: An error if decompression fails, otherwise nil.
268310
func decompressData(reader io.Reader, writer io.Writer, codes map[rune]string, limiter uint64) error {
311+
return decompressDataWithProgress(reader, writer, codes, limiter, "", nil)
312+
}
269313

270-
lastByte := make([]byte, 1) // last byte read from the reader
271-
lastByteCount := make([]byte, 1) // number of bits in the last byte
314+
// decompressionContext holds the context for decompression operations
315+
type decompressionContext struct {
316+
reader io.Reader
317+
writer io.Writer
318+
root *Node
319+
currentNode *Node
320+
state *decompressionState
321+
limiter uint64
322+
fileName string
323+
progressCallback utils.ProgressCallback
324+
}
272325

273-
leftOverByte := uint32(0)
274-
leftOverByteCount := uint8(0)
326+
// decompressDataWithProgress is the enhanced version that supports progress reporting
327+
func decompressDataWithProgress(reader io.Reader, writer io.Writer, codes map[rune]string, limiter uint64, fileName string, progressCallback utils.ProgressCallback) error {
328+
state := &decompressionState{
329+
lastByte: make([]byte, 1),
330+
lastByteCount: make([]byte, 1),
331+
}
275332

276-
loopFlag := 0
277333
root := rebuildHuffmanTree(codes)
278334
currentNode := root
279335

280-
// if the limiter is not -1, then we only read limiter bytes
336+
ctx := &decompressionContext{
337+
reader: reader,
338+
writer: writer,
339+
root: root,
340+
currentNode: currentNode,
341+
state: state,
342+
limiter: limiter,
343+
fileName: fileName,
344+
progressCallback: progressCallback,
345+
}
281346

282-
dataRead := uint64(0)
347+
return processDecompressionLoop(ctx)
348+
}
349+
350+
// decompressionState holds the state during decompression
351+
type decompressionState struct {
352+
lastByte []byte
353+
lastByteCount []byte
354+
leftOverByte uint32
355+
leftOverByteCount uint8
356+
loopFlag int
357+
dataRead uint64
358+
}
283359

360+
// processDecompressionLoop handles the main decompression loop
361+
func processDecompressionLoop(ctx *decompressionContext) error {
284362
bytesToRead := constants.BUFFER_SIZE
285363

286364
for {
287-
288-
if dataRead <= limiter {
289-
bytesToRead = int(min(constants.BUFFER_SIZE, limiter-dataRead))
365+
if ctx.state.dataRead <= ctx.limiter {
366+
bytesToRead = int(min(constants.BUFFER_SIZE, ctx.limiter-ctx.state.dataRead))
290367
}
291368

292369
readBuffer := make([]byte, bytesToRead)
293-
n, err := reader.Read(readBuffer)
370+
n, err := ctx.reader.Read(readBuffer)
294371
if err != nil && err != io.EOF {
295372
return err
296373
}
297374

298-
dataRead += uint64(n)
375+
ctx.state.dataRead += uint64(n)
299376

300-
if n == 0 {
377+
// Report progress during decompression
378+
reportDecompressionProgress(ctx.progressCallback, ctx.state.dataRead, ctx.limiter, ctx.fileName)
301379

302-
err := decompressRemainingBits(leftOverByte, leftOverByteCount, uint8(lastByteCount[0]), lastByte[0], writer, root)
303-
if err != nil {
304-
return fmt.Errorf(constants.ERROR_COMPRESS, err)
305-
}
380+
if n == 0 {
381+
return finalizeDecompression(ctx.state, ctx.writer, ctx.root)
382+
}
306383

307-
break
308-
} else {
384+
if err := processDecompressionBuffer(readBuffer, n, ctx.state, &ctx.currentNode, ctx.root, ctx.writer); err != nil {
385+
return err
386+
}
309387

310-
adjustBuffer(loopFlag, &n, &lastByte, &lastByteCount, &readBuffer)
388+
ctx.state.loopFlag = 1
389+
}
390+
}
311391

312-
if err := decompressFullByte(readBuffer, &leftOverByte, &leftOverByteCount, &currentNode, &root, writer); err != nil {
313-
return fmt.Errorf(constants.ERROR_COMPRESS, err)
314-
}
392+
// reportDecompressionProgress reports the current decompression progress
393+
func reportDecompressionProgress(progressCallback utils.ProgressCallback, dataRead, limiter uint64, fileName string) {
394+
if progressCallback != nil && limiter > 0 {
395+
progress := float64(dataRead) / float64(limiter)
396+
if progress > 1.0 {
397+
progress = 1.0
315398
}
316-
317-
loopFlag = 1
399+
progressCallback(progress, fmt.Sprintf("Decompressing: %s (%.2f%%)", fileName, progress*100))
318400
}
401+
}
402+
403+
// processDecompressionBuffer processes a buffer during decompression
404+
func processDecompressionBuffer(readBuffer []byte, n int, state *decompressionState, currentNode **Node, root *Node, writer io.Writer) error {
405+
adjustBuffer(state.loopFlag, &n, &state.lastByte, &state.lastByteCount, &readBuffer)
319406

407+
return decompressFullByte(readBuffer, &state.leftOverByte, &state.leftOverByteCount, currentNode, &root, writer)
408+
}
409+
410+
// finalizeDecompression handles the final decompression steps
411+
func finalizeDecompression(state *decompressionState, writer io.Writer, root *Node) error {
412+
err := decompressRemainingBits(state.leftOverByte, state.leftOverByteCount, uint8(state.lastByteCount[0]), state.lastByte[0], writer, root)
413+
if err != nil {
414+
return fmt.Errorf(constants.ERROR_COMPRESS, err)
415+
}
320416
return nil
321417
}
322418

@@ -461,7 +557,7 @@ func processFileCompression(file utils.FileData, output io.Writer, codes map[run
461557
}
462558

463559
// Compress and write the data
464-
compressedLen, err := compressData(file.Reader, output, codes)
560+
compressedLen, err := compressDataWithProgress(file.Reader, output, codes, file.Size, filepath.Base(file.Name), progressCallback)
465561
if err != nil {
466562
return fmt.Errorf(constants.ERROR_COMPRESS, err)
467563
}
@@ -643,7 +739,7 @@ func processFile(input io.Reader, outputPath string, fileName string, codes map[
643739
return "", fmt.Errorf(constants.FILE_READ_ERROR, err)
644740
}
645741

646-
if err := decompressData(input, outputFile, codes, compressedSize); err != nil {
742+
if err := decompressDataWithProgress(input, outputFile, codes, compressedSize, filepath.Base(fileName), progressCallback); err != nil {
647743
return "", fmt.Errorf(constants.ERROR_DECOMPRESS, err)
648744
}
649745

compressor/lzma/io.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func processFileCompression(file utils.FileData, output io.Writer, progressCallb
3131
return fmt.Errorf(constants.FILE_WRITE_ERROR, err)
3232
}
3333

34-
compressedLen, err := compressData(file.Reader, output)
34+
compressedLen, err := compressDataWithProgress(file.Reader, output, filepath.Base(file.Name), progressCallback)
3535
if err != nil {
3636
return fmt.Errorf(constants.ERROR_COMPRESS, err)
3737
}
@@ -137,7 +137,7 @@ func processFile(input io.Reader, outputPath string, fileName string, progressCa
137137
return "", fmt.Errorf(constants.FILE_READ_ERROR, err)
138138
}
139139

140-
if err := decompressData(input, outputFile, compressedSize); err != nil {
140+
if err := decompressDataWithProgress(input, outputFile, compressedSize, filepath.Base(fileName), progressCallback); err != nil {
141141
return "", fmt.Errorf(constants.ERROR_DECOMPRESS, err)
142142
}
143143

compressor/lzma/lzma.go

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"encoding/binary"
66
"file-compressor/constants"
7+
"file-compressor/utils"
78
"fmt"
89
"io"
910
)
@@ -19,11 +20,17 @@ type Match struct {
1920
}
2021

2122
func compressData(input io.Reader, output io.Writer) (uint64, error) {
23+
return compressDataWithProgress(input, output, "", nil)
24+
}
25+
26+
// compressDataWithProgress is the enhanced version that supports progress reporting
27+
func compressDataWithProgress(input io.Reader, output io.Writer, fileName string, progressCallback utils.ProgressCallback) (uint64, error) {
2228
var inBuf bytes.Buffer
2329
if _, err := inBuf.ReadFrom(input); err != nil {
2430
return 0, err
2531
}
2632
data := inBuf.Bytes()
33+
totalBytes := len(data)
2734

2835
var outBuf bytes.Buffer
2936
pos := 0
@@ -46,40 +53,84 @@ func compressData(input io.Reader, output io.Writer) (uint64, error) {
4653
updateDictionary(data, pos, dictionary)
4754
pos++
4855
}
56+
57+
// Report progress every 0.01% for very smooth updates
58+
if progressCallback != nil && totalBytes > 0 {
59+
progress := float64(pos) / float64(totalBytes)
60+
progressCallback(progress, fmt.Sprintf("Compressing: %s (%.2f%%)", fileName, progress*100))
61+
}
4962
}
5063

5164
written, err := output.Write(outBuf.Bytes())
5265
return uint64(written), err
5366
}
5467

5568
func decompressData(reader io.Reader, writer io.Writer, limiter uint64) error {
56-
var outBuf bytes.Buffer
69+
return decompressDataWithProgress(reader, writer, limiter, "", nil)
70+
}
71+
72+
// lzmaDecompressionState holds the state during LZMA decompression
73+
type lzmaDecompressionState struct {
74+
outBuf bytes.Buffer
75+
dataRead uint64
76+
}
77+
78+
// decompressDataWithProgress is the enhanced version that supports progress reporting
79+
func decompressDataWithProgress(reader io.Reader, writer io.Writer, limiter uint64, fileName string, progressCallback utils.ProgressCallback) error {
80+
state := &lzmaDecompressionState{}
5781
buf := make([]byte, constants.BUFFER_SIZE)
58-
dataRead := uint64(0)
5982

6083
for {
61-
if limiter > 0 && dataRead >= limiter {
84+
if shouldStopDecompression(state.dataRead, limiter) {
6285
break
6386
}
6487

65-
flag, err := readFlag(reader, buf)
66-
if err != nil {
88+
if err := processDecompressionStep(reader, &state.outBuf, buf, state, limiter, fileName, progressCallback); err != nil {
6789
if err == io.EOF {
6890
break
6991
}
7092
return err
7193
}
94+
}
7295

73-
dataRead++
96+
_, err := writer.Write(state.outBuf.Bytes())
97+
return err
98+
}
7499

75-
if err := processFlag(flag, reader, &outBuf, buf); err != nil {
76-
return err
77-
}
78-
dataRead += flagDataReadIncrement(flag)
100+
// shouldStopDecompression checks if decompression should stop
101+
func shouldStopDecompression(dataRead, limiter uint64) bool {
102+
return limiter > 0 && dataRead >= limiter
103+
}
104+
105+
// processDecompressionStep processes a single step in LZMA decompression
106+
func processDecompressionStep(reader io.Reader, outBuf *bytes.Buffer, buf []byte, state *lzmaDecompressionState, limiter uint64, fileName string, progressCallback utils.ProgressCallback) error {
107+
flag, err := readFlag(reader, buf)
108+
if err != nil {
109+
return err
79110
}
80111

81-
_, err := writer.Write(outBuf.Bytes())
82-
return err
112+
state.dataRead++
113+
114+
if err := processFlag(flag, reader, outBuf, buf); err != nil {
115+
return err
116+
}
117+
state.dataRead += flagDataReadIncrement(flag)
118+
119+
// Report progress during decompression
120+
reportLZMADecompressionProgress(progressCallback, state.dataRead, limiter, fileName)
121+
122+
return nil
123+
}
124+
125+
// reportLZMADecompressionProgress reports the current LZMA decompression progress
126+
func reportLZMADecompressionProgress(progressCallback utils.ProgressCallback, dataRead, limiter uint64, fileName string) {
127+
if progressCallback != nil && limiter > 0 {
128+
progress := float64(dataRead) / float64(limiter)
129+
if progress > 1.0 {
130+
progress = 1.0
131+
}
132+
progressCallback(progress, fmt.Sprintf("Decompressing: %s (%.2f%%)", fileName, progress*100))
133+
}
83134
}
84135

85136
func processFlag(flag byte, reader io.Reader, outBuf *bytes.Buffer, buf []byte) error {

0 commit comments

Comments
 (0)