New!

Visual Basic

ZLib Compression

Download

Filesize~49 kBytes
Versionv1.0
Screenshotsnone
DownloadMain Server (Belgium)

Introduction

Compressing text, audio, images and video has always been a popular research subject. Compressed files have several advantages; they use less space on your hard drive, less disk activity is needed to open them (saving battery- and access time) and they are transferred faster to other people via the Internet, (W)LAN or PAN.

This tutorial explains how to compress text using the latest ZLib library (v1.2.3), modified to work for Visual Basic by Gilles Vollant. Special thanks to Mike D Sutton, who helped me write a great deal of this project.

Compressing

Let's have a look at the compress function we're going to use...

Private Declare Function compress2 Lib "ZLibWAPI.dll" ( _
    ByRef dest As Any, ByRef destLen As Long, _
    ByRef Source As Any, ByVal sourceLen As Long, _
    ByVal level As Long) As Long

It's pretty straightforward, except for the destination lenght, which is actually the buffersize for the actual destination. But how can we know how big this buffer is supposed to be? ZLibWAPI has a function that returns the maximum size for the destination, which is what we're going to use as a buffersize.

Private Declare Function compressBound Lib "ZLibWAPI.dll" ( _
    ByVal sourceLen As Long) As Long

Very easy. But looking back at 'compress2', can you guess what you have to feed the function? Don't let the 'As Any' fool you, 'compress2' only takes pointers to existing arrays in the system memory. This is where it gets tricky.

Visual Basic strings are stored in 2 formats: ANSI and Unicode. ANSI uses 1 byte of memory per character, and Unicode uses 2 Bytes per character. Luckily, there is an undocumented function called 'StrPtr', which returns a pointer to the first byte of the Unicode string.

StrPtr(string)

This is what we'll be using as the 'Source'. The source length is not Len(string), but (Len(String) * 2), because as mentioned above, Unicode uses 2 bytes per character. You can also use LenB(string) to get the correct length. LenB is a function that returns the amount of Bytes a variable is using.

Now for the destination. Seeing as a compressed string is not readable, but actually just a pile of Bytes, it makes sense to just store the compressed string in a byte array. Before we do that we're going to have a look at the 'uncompress' function.

Private Declare Function uncompress Lib "ZLibWAPI.dll" ( _
    ByRef dest As Any, ByRef destLen As Long, _
    ByRef Source As Any, ByVal sourceLen As Long) As Long

As you can see, it requires a destination length, which is the length of the original string we are trying to compress. This means we need to somehow store the length of the original string in the compressed byte array.

We can use the following functions to achieve that end.

Private Declare Sub PutDWord Lib "MSVBVM60.dll" Alias "PutMem4" ( _
    ByRef inDst As Any, _
    ByVal inSrc As Long)
Private Declare Sub GetDWord Lib "MSVBVM60.dll" Alias "GetMem4" ( _
    ByRef inSrc As Any, _
    ByRef inDst As Long)

A DWord consists of 4 Bytes (hence the name Put- and GetMem4). In case you did not know this already, a byte is a number from 0 to 255. Using 4 bytes to store the length of the string allows for 256^4 combinations, which leaves us with a maximum of 4294967296 Bytes, or 4 gigabytes for the original string size. That should be enough :).

Public Function ZCompress(sSrc As String, bytDst() As Byte, Optional lLevel As ZCompression = Z_DEFAULT_COMPRESSION) As Boolean

    Dim lRet        As Long
    Dim lSrcLen     As Long
    Dim lDstLen     As Long

    lSrcLen = LenB(sSrc)
    lDstLen = compressBound(lSrcLen)

    'allocate buffer memory + 4 bytes to save the original size in
    ReDim bytDst(0 To (lDstLen + 3)) As Byte
    'write original size in the first 4 bytes
    Call PutDWord(bytDst(0), lSrcLen)

    'StrPtr returns the pointer of the first byte of the Unicode string
    lRet = compress2(bytDst(4), lDstLen, ByVal StrPtr(sSrc), lSrcLen, lLevel)

    If lRet = Z_OK Then
        ZCompress = True
        'remove obsolete array elements (lDstLen is updated by compress2)
        ReDim Preserve bytDst(0 To (lDstLen + 3)) As Byte
    Else
        ZCompress = False
        Call Err.Raise(vbObjectError Or ZLibError Or lRet)
    End If

End Function

As you can see, first the original string length and the buffer length are saved in a couple of longs. Then the destination byte array is buffered up to the buffersize plus 4 extra bytes to store the original string size in.

After the compress function has done its work, the destination length (lDstLen) is updated to reflect the actual size of the compressed byte array. Usually, the size of the compressed byte array is bigger than the updated lDstLen because we buffered it to quite a large size before, so we best remove those obsolete elements to save some memory.

Decompressing

Decompressing strings is so similar to compressing strings that it hardly requires any explanation.

Public Function ZDeCompress(bytSrc() As Byte) As String

    Dim lRet     As Long

    Dim lSrcLB   As Long
    Dim lSrcUB   As Long
    Dim lSrcLen  As Long

    Dim lDstLen  As Long
    Dim sDst     As String

    On Error Resume Next
    lSrcLB = LBound(bytSrc)
    lSrcUB = UBound(bytSrc) + 1
    lSrcLen = lSrcUB - lSrcLB - 4
    On Error GoTo 0

    If lSrcLen > 0 Then

        'get the destination length
        Call GetDWord(bytSrc(0), lDstLen)
        'create a buffer
        sDst = Space$(lDstLen \ 2)
        'uncompress starting from byte 4 (0-3 are used by the dword)
        lRet = uncompress(ByVal StrPtr(sDst), lDstLen, bytSrc(4), lSrcLen)

        If lRet = Z_OK Then
            ZDeCompress = sDst
        Else
            Call Err.Raise(vbObjectError Or ZLibError Or lRet)
        End If

    End If

End Function

This time, the destination is not a byte array, but a Unicode string array, using the same 'StrPtr' function as before. One thing you must remember to do is to buffer the destination string using the function 'Space'.

Well that concludes this tutorial on ZLib compression, stay tuned for more articles!