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
Post a Comment