@@ -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.
169169func 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.
268310func 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
0 commit comments