c++compressionzlibinflatedeflate

C++ zlib deflate/inflate and z_stream parameters


After I understood (with some help...) how work the compress and uncompress functions of the zlib library, I'm now trying to understand how deflate and inflate work. As far as I understand, compress is used in a single call, whereas deflate can be called several times.

Having a simple program with a Particle struct (coordinate x, y, z), I can deflate my data without errors (getting a Z_STREAM_END response) and then inflate them with another z_stream object (Z_STREAM_END response too). But when I tried to display back my data from the inflate response, I can get the x and y coordinates of my struct but not the third one (z).

I think it's due to incorrect parameters that I gave to my z_stream object for inflate, but I can't find which ones. As far as I understand reading the docs and example, this is how I think z_stream works (this is just an example) :

// Here i give a total memory size for the output buffer used by deflate func
#define CHUNK 16384

struct Particle
{
    float x;
    float y;
    float z;
};

...

// An element to get a single particle and give it to deflate func
Bytef *dataOriginal = (Bytef*)malloc( sizeof(Particle) );

// This var will be used to pass compressed data
Bytef *dataCompressed = (Bytef*)malloc( CHUNK );

z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
deflateInit(&strm, Z_DEFAULT_COMPRESSION);

strm.avail_out = CHUNK;
strm.next_out = dataCompressed;

int nbrLoop = 2;
int spaceUsed = 0;
int flush;
Particle p;

for (var i = 0; i<nbrLoop; i++){
    // set all values equal to 0
    memset( &p, 0, sizeof(Particle) );

    // insert some random values
    p.x = (i+1) * 1;
    p.y = (i+1) * 3;
    p.z = (i+1) * 7;

    // copy this values in a Bytef* elements
    memcpy( dataOriginal, &p, sizeof(Particle) );


    strm.avail_in = sizeof(dataOriginal);
    strm.next_in = dataOriginal;

    // If it's the last particle:
    if(i == nbrLoop - 1){
    flush = Z_FINISH;
    }
    else{
        flush = Z_NO_FLUSH;
    }

    int response = deflate(&strm, flush);

    // I don't get any errors here
    // EDIT : Get Z_OK at first loop, then Z_STREAM_END at second (last)
    if( res == Z_STREAM_END ){
        spaceUsed = CHUNK - strm.avail_out;
    }
}

deflateEnd(&strm);

// Trying to get back my data
Bytef *decomp = (Bytef*)malloc( sizeof(Particle) );

z_stream strmInflate;
strmInflate.zalloc = Z_NULL;
strmInflate.zfree = Z_NULL;
strmInflate.opaque = Z_NULL;
inflateInit(&strmInflate);

// data I want to get at the next inflate
strmInflate.avail_in = sizeof(Particle);
strmInflate.next_in = dataCompressed;

// Two particles were compressed, so I need to get back two
strmInflate.avail_out = sizeof(Particle) * 2;
strmInflate.next_out = decomp;

int response = inflate( &strmInflate, Z_NO_FLUSH );
// No error here,
// EDIT : Get Z_OK

inflateEnd( &strmInflate );

Particle testP;
memset( &testP, 0, sizeof(Particle) );
memcpy( &testP, decomp, sizeof(Particle) );

std::cout << testP.x << std::endl; // display 1 OK
std::cout << testP.y << std::endl; // display 3 OK
std::cout << testP.z << std::endl; // display 0 NOT OK

Moreover, I thought that calling inflate a second time will allow me to recover the data of my second particle that was created in my for loop but I can't retrieve it.

Thanks in advance for any help!


Solution

  • strmInflate.avail_in = sizeof(Particle); needs to be strmInflate.avail_in = spaceUsed; You have to provide inflate all of the data produced by deflate.

    At the end you want to get Z_STREAM_END from inflate(), not Z_OK. Otherwise you have not decompressed the entire generated stream.

    Note that per the documentation in zlib.h, you need to also set next_in and avail_in (to Z_NULL and 0 if you like) before calling inflateInit()

    Depending on the size of the input and output buffers you will be using in the final application, you may need more loops to assure that deflate() and inflate() can finish their jobs. Please see the example of how to use zlib.