2020-09-20 14:16:44 +02:00
|
|
|
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
|
|
|
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* This is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This software is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this software; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
|
|
* USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include <rdr/ZlibOutStream.h>
|
|
|
|
#include <rdr/Exception.h>
|
|
|
|
|
|
|
|
#include <zlib.h>
|
|
|
|
|
|
|
|
#undef ZLIBOUT_DEBUG
|
|
|
|
|
|
|
|
using namespace rdr;
|
|
|
|
|
|
|
|
enum { DEFAULT_BUF_SIZE = 16384 };
|
|
|
|
|
2019-09-23 11:00:17 +02:00
|
|
|
ZlibOutStream::ZlibOutStream(OutStream* os, size_t bufSize_, int compressLevel)
|
2020-09-20 14:16:44 +02:00
|
|
|
: underlying(os), compressionLevel(compressLevel), newLevel(compressLevel),
|
|
|
|
bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
|
|
|
|
{
|
|
|
|
zs = new z_stream;
|
|
|
|
zs->zalloc = Z_NULL;
|
|
|
|
zs->zfree = Z_NULL;
|
|
|
|
zs->opaque = Z_NULL;
|
|
|
|
zs->next_in = Z_NULL;
|
|
|
|
zs->avail_in = 0;
|
|
|
|
if (deflateInit(zs, compressLevel) != Z_OK) {
|
|
|
|
delete zs;
|
|
|
|
throw Exception("ZlibOutStream: deflateInit failed");
|
|
|
|
}
|
|
|
|
ptr = start = new U8[bufSize];
|
|
|
|
end = start + bufSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
ZlibOutStream::~ZlibOutStream()
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
flush();
|
|
|
|
} catch (Exception&) {
|
|
|
|
}
|
|
|
|
delete [] start;
|
|
|
|
deflateEnd(zs);
|
|
|
|
delete zs;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ZlibOutStream::setUnderlying(OutStream* os)
|
|
|
|
{
|
|
|
|
underlying = os;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ZlibOutStream::setCompressionLevel(int level)
|
|
|
|
{
|
|
|
|
if (level < -1 || level > 9)
|
|
|
|
level = -1; // Z_DEFAULT_COMPRESSION
|
|
|
|
|
|
|
|
newLevel = level;
|
|
|
|
}
|
|
|
|
|
2019-09-23 11:00:17 +02:00
|
|
|
size_t ZlibOutStream::length()
|
2020-09-20 14:16:44 +02:00
|
|
|
{
|
|
|
|
return offset + ptr - start;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ZlibOutStream::flush()
|
|
|
|
{
|
|
|
|
checkCompressionLevel();
|
|
|
|
|
|
|
|
zs->next_in = start;
|
|
|
|
zs->avail_in = ptr - start;
|
|
|
|
|
|
|
|
#ifdef ZLIBOUT_DEBUG
|
|
|
|
fprintf(stderr,"zos flush: avail_in %d\n",zs->avail_in);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Force out everything from the zlib encoder
|
|
|
|
deflate(Z_SYNC_FLUSH);
|
|
|
|
|
|
|
|
offset += ptr - start;
|
|
|
|
ptr = start;
|
|
|
|
}
|
|
|
|
|
2019-09-23 11:00:17 +02:00
|
|
|
size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems)
|
2020-09-20 14:16:44 +02:00
|
|
|
{
|
|
|
|
#ifdef ZLIBOUT_DEBUG
|
|
|
|
fprintf(stderr,"zos overrun\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (itemSize > bufSize)
|
|
|
|
throw Exception("ZlibOutStream overrun: max itemSize exceeded");
|
|
|
|
|
|
|
|
checkCompressionLevel();
|
|
|
|
|
2019-09-23 11:00:17 +02:00
|
|
|
while ((size_t)(end - ptr) < itemSize) {
|
2020-09-20 14:16:44 +02:00
|
|
|
zs->next_in = start;
|
|
|
|
zs->avail_in = ptr - start;
|
|
|
|
|
|
|
|
deflate(Z_NO_FLUSH);
|
|
|
|
|
|
|
|
// output buffer not full
|
|
|
|
|
|
|
|
if (zs->avail_in == 0) {
|
|
|
|
offset += ptr - start;
|
|
|
|
ptr = start;
|
|
|
|
} else {
|
|
|
|
// but didn't consume all the data? try shifting what's left to the
|
|
|
|
// start of the buffer.
|
|
|
|
fprintf(stderr,"z out buf not full, but in data not consumed\n");
|
|
|
|
memmove(start, zs->next_in, ptr - zs->next_in);
|
|
|
|
offset += zs->next_in - start;
|
|
|
|
ptr -= zs->next_in - start;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-24 09:41:07 +02:00
|
|
|
size_t nAvail;
|
|
|
|
nAvail = (end - ptr) / itemSize;
|
|
|
|
if (nAvail < nItems)
|
|
|
|
return nAvail;
|
2020-09-20 14:16:44 +02:00
|
|
|
|
|
|
|
return nItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ZlibOutStream::deflate(int flush)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!underlying)
|
|
|
|
throw Exception("ZlibOutStream: underlying OutStream has not been set");
|
|
|
|
|
|
|
|
if ((flush == Z_NO_FLUSH) && (zs->avail_in == 0))
|
|
|
|
return;
|
|
|
|
|
|
|
|
do {
|
|
|
|
underlying->check(1);
|
|
|
|
zs->next_out = underlying->getptr();
|
|
|
|
zs->avail_out = underlying->getend() - underlying->getptr();
|
|
|
|
|
|
|
|
#ifdef ZLIBOUT_DEBUG
|
|
|
|
fprintf(stderr,"zos: calling deflate, avail_in %d, avail_out %d\n",
|
|
|
|
zs->avail_in,zs->avail_out);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
rc = ::deflate(zs, flush);
|
2020-02-27 13:35:41 +01:00
|
|
|
if (rc < 0) {
|
2020-09-20 14:16:44 +02:00
|
|
|
// Silly zlib returns an error if you try to flush something twice
|
|
|
|
if ((rc == Z_BUF_ERROR) && (flush != Z_NO_FLUSH))
|
|
|
|
break;
|
|
|
|
|
|
|
|
throw Exception("ZlibOutStream: deflate failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ZLIBOUT_DEBUG
|
|
|
|
fprintf(stderr,"zos: after deflate: %d bytes\n",
|
|
|
|
zs->next_out-underlying->getptr());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
underlying->setptr(zs->next_out);
|
|
|
|
} while (zs->avail_out == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ZlibOutStream::checkCompressionLevel()
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (newLevel != compressionLevel) {
|
|
|
|
#ifdef ZLIBOUT_DEBUG
|
|
|
|
fprintf(stderr,"zos change: avail_in %d\n",zs->avail_in);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// zlib is just horribly stupid. It does an implicit flush on
|
|
|
|
// parameter changes, but the flush it does is not one that forces
|
|
|
|
// out all the data. And since you cannot flush things again, we
|
|
|
|
// cannot force out our data after the parameter change. Hence we
|
|
|
|
// need to do a more proper flush here first.
|
|
|
|
deflate(Z_SYNC_FLUSH);
|
|
|
|
|
|
|
|
rc = deflateParams (zs, newLevel, Z_DEFAULT_STRATEGY);
|
2020-02-27 13:35:41 +01:00
|
|
|
if (rc < 0) {
|
2020-09-20 14:16:44 +02:00
|
|
|
// The implicit flush can result in this error, caused by the
|
|
|
|
// explicit flush we did above. It should be safe to ignore though
|
|
|
|
// as the first flush should have left things in a stable state...
|
|
|
|
if (rc != Z_BUF_ERROR)
|
|
|
|
throw Exception("ZlibOutStream: deflateParams failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
compressionLevel = newLevel;
|
|
|
|
}
|
|
|
|
}
|