Code archives/Algorithms/Huffman Compression

This code has been declared by its author to be Public Domain code.

Download source code

Huffman Compression by Murilo2002
Sorry about the lame example (included in the code below).

Now I don't profess to be a master number cruncher, but the routines have served me well. I have actually developed these routines further (and given variables more specific names (huff_), removed those horrid "Exit" instructions etc), but as they form part of my "external data protection" I'm reluctant to release them at the moment.
; Title:		Curve Compression (COMPRESS)
; Author:		Leigh Bowers
;			(c) Copyright 2001 Curve Software
; Version:	1.0
;
; Email:		leigh.bowers@curvesoftware.co.uk
; WWW:		www.curvesoftware.co.uk/blitz

Type TreeNode
    Field Weight%
    Field SavedWeight%
    Field Child1%
    Field Child0%
End Type

Type CharCodes
    Field Code%
    Field CodeBits%
End Type

Type BitFile
    Field Mask%
    Field Rack%
    Field OutputByteCount%
End Type

Const SRCCOPY% = $CC0020
Const ENDOFSTREAM% = 256
Const ENDWEIGHTSTREAM% = $FFF

Dim Counters%(256)
Dim Nodes.TreeNode(514)
Dim Codes.CharCodes(257)

Global Bitio.BitFile

; --------------
; Example Usage
; --------------

s$ = "source.bmp"
Print "Curve Compression - Compress"
Print ""
Print "Source length:" + FileSize(s)
Start# = MilliSecs()
;
CompressFile s, "packed.dat" ; The extention can be anything (.dat, .pak, .abc etc)
;
Finish# = MilliSecs()
Print "Packed length:" + FileSize("packed.dat")
Print ((Finish - Start)/1000) + " seconds taken."
WaitKey

End

; --------------

Function InitHuffman()

	Local i%

	For i = 0 To 514 : Nodes.TreeNode(i) = New TreeNode : Next
	For i = 0 To 257 : Codes.CharCodes(i) = New CharCodes : Next
	bitio.bitfile = New bitfile
	Bitio\Mask = $80

End Function

Function CountOccurrences(lSourceBank%, lSourceSize%)

	Local bChar%, l%

	For l = 0 To (lSourceSize - 1)
		bChar = PeekByte(lSourceBank, l)
    		Counters(bChar) = Counters(bChar) + 1
	Next

End Function

Function ScaleCounts()

	Local MaxCount%, i%
    
    	MaxCount = 0
    	For i = 0 To 256
        	If Counters(i) > MaxCount Then MaxCount = Counters(i)
    	Next
        
    	MaxCount = (MaxCount / 255) + 1
    	For i = 0 To 256
        	Nodes(i)\Weight = Counters(i) / MaxCount
        	If Nodes(i)\Weight = 0 And Counters(i) <> 0 Then Nodes(i)\Weight = 1
    	Next
    	Nodes(ENDOFSTREAM)\Weight = 1

End Function

Function BuildTree%()

	Local NextFree%, i%, Min1%, Min2%
    
	Nodes(513)\Weight = $7FFF
	For NextFree = (EndOfStream + 1) To 514
	    	Min1 = 513
	    	Min2 = 513
	    	For i = 0 To (NextFree - 1)
	    		If Nodes(i)\Weight <> 0 Then
	        		If Nodes(i)\Weight < Nodes(Min1)\Weight Then
		            		Min2 = Min1
		            		Min1 = i
	        		Else
					If Nodes(i)\Weight < Nodes(Min2)\Weight Then Min2 = i
		         	End If
	    		End If
		Next
	
		If Min2 = 513 Then Exit

		Nodes(NextFree)\Weight = Nodes(Min1)\Weight + Nodes(Min2)\Weight
		Nodes(Min1)\SavedWeight = Nodes(Min1)\Weight
	      	Nodes(Min1)\Weight = 0
	      	Nodes(Min2)\SavedWeight = Nodes(Min2)\Weight
	      	Nodes(Min2)\Weight = 0
		Nodes(NextFree)\Child0 = Min1
		Nodes(NextFree)\Child1 = Min2
	Next
	
	NextFree = NextFree - 1
	Nodes(NextFree)\SavedWeight = Nodes(NextFree)\Weight

	Return NextFree

End Function

Function ConvertTreeToCode(CodeSoFar%, Bits%, node%)

	If node <= ENDOFSTREAM Then
		Codes(node)\Code = CodeSoFar
		Codes(node)\CodeBits = Bits
		Return
	End If
	CodeSoFar = CodeSoFar * 2
	Bits = Bits + 1
	ConvertTreeToCode CodeSoFar, Bits, Nodes(node)\Child0
	ConvertTreeToCode (CodeSoFar Or 1), Bits, Nodes(node)\Child1
	CodeSoFar = CodeSoFar / 2
	Bits = Bits - 1

End Function

Function OutputCounts%(lDestBank%, lDestSize%)

	Local i%, FirstNode%, LastNode%, NextNode%
	
	FirstNode = 0
	While (FirstNode < 256)
		If Nodes(FirstNode)\SavedWeight <> 0 Then
			For LastNode = FirstNode + 1 To 255
			    If Nodes(LastNode)\SavedWeight = 0 Then Exit
			Next
			LastNode = LastNode - 1
			For NextNode = LastNode + 1 To 255
				If Nodes(NextNode)\SavedWeight = 0 Then
					If (NextNode > 255) Or (NextNode - LastNode > 3) Then Exit
				Else
					LastNode = NextNode
				End If
			Next
			PokeShort lDestBank, lDestSize, FirstNode : lDestSize = lDestSize + 2
			PokeShort lDestBank, lDestSize, LastNode : lDestSize = lDestSize + 2
			For i = FirstNode To LastNode
				PokeShort lDestBank, lDestSize, Nodes(i)\SavedWeight : lDestSize = lDestSize + 2
			Next
			FirstNode = NextNode + 1
		Else
			FirstNode = FirstNode + 1
		End If
	Wend
	PokeShort lDestBank, lDestSize, ENDWEIGHTSTREAM : lDestSize = lDestSize + 2
	
	Return lDestSize

End Function

Function CompressFile(sSource$, sDest$)

	Local iSymbol%, iBitcount%, bChar%
	Local lSourceBank%, lDestBank%, lSourceSize%, lDestSize%
	
	InitHuffman

	; Create Banks

	lSourceSize = FileSize(sSource)

	lSourceBank = CreateBank(lSourceSize)
	lDestBank = CreateBank((lSourceSize + (lSourceSize * 0.01)) + 1000 ) ; Allow for 1% file increase

	; Read source file in to bank

	hFileIn% = ReadFile(sSource)
	ReadBytes lSourceBank, hFileIn, 0, lSourceSize
	CloseFile hFileIn
	
	; Pre-Processing
	
	CountOccurrences lSourceBank, lSourceSize
	ScaleCounts()

	iRootNode% = BuildTree()
	ConvertTreeToCode 0, 0, iRootNode
	
	; Main Compression
	
	PokeInt lDestBank, 0, lSourceSize ; Store the original (uncompressed) file size at the head of the file

	lDestSize = OutputCounts(lDestBank, 4)

	For l% = 0 To (lSourceSize - 1)
		bChar = PeekByte(lSourceBank, l)
		iSymbol = Codes(bChar)\Code
		iBitcount = Codes(bChar)\CodeBits
		lDestSize = BitHandler(lDestBank, iSymbol, iBitcount, lDestSize)
	Next

	iSymbol = Codes(ENDOFSTREAM)\Code
	iBitcount = Codes(ENDOFSTREAM)\CodeBits
	lDestSize = BitHandler(lDestBank, iSymbol, iBitcount, lDestSize)
	If Bitio\Mask <> $80 Then PokeByte lDestBank, lDestSize, Bitio\Rack : lDestSize = lDestSize + 1

	FreeBank lSourceBank

	; Write the compressed file

	hFileOut% = WriteFile(sDest)
	WriteBytes lDestBank, hFileOut, 0, lDestSize
	CloseFile hFileOut

	FreeBank lDestBank

End Function

Function BitHandler%(lDestBank%, iSymbol%, iBitcount%, lDestSize%)

	Local SymbolMask%, i%

	SymbolMask = 1
	For i = 1 To (iBitcount - 1) : SymbolMask = SymbolMask * 2 : Next
	While (SymbolMask <> 0)
		If (iSymbol And SymbolMask) Then Bitio\Rack = Bitio\Rack Or Bitio\Mask
		SymbolMask = SymbolMask / 2
		Bitio\Mask = Bitio\Mask / 2
		If Bitio\Mask = 0 Then
			PokeByte lDestBank, lDestSize, Bitio\Rack : lDestSize = lDestSize + 1
			Bitio\Mask = $80
			Bitio\Rack = 0
			Bitio\OutputByteCount = Bitio\OutputByteCount + 2
		End If
	Wend
	
	Return lDestSize

End Function

Comments

None.

Code Archives Forum