c++ - zlib inflate stream and avail_in -


part of application i'm working on involves receiving compressed data stream in zlib (deflate) format, piece piece on socket. routine receive compressed data in chunks, , pass inflate more data becomes available. when inflate returns z_stream_end know full object has arrived.

a simplified version of basic c++ inflater function follows:

void inflater::inflate_next_chunk(void* chunk, std::size_t size) {    m_strm.avail_in = size;    m_strm.next_in = chunk;    m_strm.next_out = m_buffer;     int ret = inflate(&m_strm, z_no_flush);    /* ... check errors, etc. ... */ } 

except strangely, every like... 40 or times, inflate fail z_data_error.

according zlib manual, z_data_error indicates "corrupt or incomplete" stream. obviously, there number of ways data getting corrupted in application way beyond scope of question - after tinkering around, realized call inflate return z_data_error if m_strm.avail_in not 0 before set size. in other words, seems inflate failing because there already data in stream before set avail_in.

but understanding every call inflate should empty input stream, meaning when call inflate again, shouldn't have worry if didn't finish last call. understanding correct here? or need check strm.avail_in see if there pending input?

also, why there ever pending input? why doesn't inflate consume available input with each call?

inflate() can return because has filled output buffer not consumed of input data. if happens need provide new output buffer , call inflate() again until m_strm.avail.in == 0.

the zlib manual has say...

the detailed semantics follows. inflate performs 1 or both of following actions:

decompress more input starting @ next_in , update next_in , avail_in accordingly. if not input can processed (because there not enough room in output buffer), next_in updated , processing resume @ point next call of inflate().

you appear assuming compressed input fit in output buffer space, that's not case...

my wrapper code looks this...

bool cdatainflator::inflate(    const byte * const pdatain,    dword &datainsize,    byte *pdataout,    dword &dataoutsize) {    if (pdatain)    {       if (m_stream.avail_in == 0)       {          m_stream.avail_in = datainsize;          m_stream.next_in = const_cast<byte * const>(pdatain);       }       else       {          throw cexception(             _t("cdatainflator::inflate()"),             _t("no space input data"));       }    }     m_stream.avail_out = dataoutsize;    m_stream.next_out = pdataout;     bool done = false;        {       int result = inflate(&m_stream, z_block);        if (result < 0)       {          throwonfailure(_t("cdatainflator::inflate()"), result);       }        done = (m_stream.avail_in == 0 ||               (dataoutsize != m_stream.avail_out &&               m_stream.avail_out != 0));    }    while (!done && m_stream.avail_out == dataoutsize);     datainsize = m_stream.avail_in;     dataoutsize = dataoutsize - m_stream.avail_out;     return done; } 

note loop , fact caller relies on datainsize know when of current input data has been consumed. if output space filled caller calls again using inflate(0, 0, pnewbuffer, newbuffersize); provide more buffer space...


Comments

Popular posts from this blog

node.js - Mongoose: Cast to ObjectId failed for value on newly created object after setting the value -

gradle error "Cannot convert the provided notation to a File or URI" -

python - NameError: name 'subprocess' is not defined -