blob: 9d9f8ba1fd98d1eeca6925ee5e71a11c3307da3d [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
DRCf4a341b2011-08-09 11:12:55 +00002 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
Pierre Ossman5ad4d062014-07-07 14:13:46 +020020#include <stdio.h>
21
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000022#include <rdr/ZlibOutStream.h>
23#include <rdr/Exception.h>
Adam Tkac20e0d712008-11-14 14:48:21 +000024
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000025#include <zlib.h>
26
Pierre Ossmanb5822f32011-10-18 14:27:07 +000027#undef ZLIBOUT_DEBUG
28
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000029using namespace rdr;
30
31enum { DEFAULT_BUF_SIZE = 16384 };
32
33ZlibOutStream::ZlibOutStream(OutStream* os, int bufSize_, int compressLevel)
34 : underlying(os), compressionLevel(compressLevel), newLevel(compressLevel),
Pierre Ossmanb5822f32011-10-18 14:27:07 +000035 bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000036{
37 zs = new z_stream;
38 zs->zalloc = Z_NULL;
39 zs->zfree = Z_NULL;
40 zs->opaque = Z_NULL;
Pierre Ossmanb5822f32011-10-18 14:27:07 +000041 zs->next_in = Z_NULL;
42 zs->avail_in = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000043 if (deflateInit(zs, compressLevel) != Z_OK) {
44 delete zs;
45 throw Exception("ZlibOutStream: deflateInit failed");
46 }
47 ptr = start = new U8[bufSize];
48 end = start + bufSize;
49}
50
51ZlibOutStream::~ZlibOutStream()
52{
53 try {
54 flush();
55 } catch (Exception&) {
56 }
57 delete [] start;
58 deflateEnd(zs);
59 delete zs;
60}
61
62void ZlibOutStream::setUnderlying(OutStream* os)
63{
64 underlying = os;
65}
66
67void ZlibOutStream::setCompressionLevel(int level)
68{
69 if (level < -1 || level > 9)
70 level = -1; // Z_DEFAULT_COMPRESSION
71
72 newLevel = level;
73}
74
75int ZlibOutStream::length()
76{
77 return offset + ptr - start;
78}
79
80void ZlibOutStream::flush()
81{
Pierre Ossmanb5822f32011-10-18 14:27:07 +000082 checkCompressionLevel();
83
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000084 zs->next_in = start;
85 zs->avail_in = ptr - start;
86
Pierre Ossmanb5822f32011-10-18 14:27:07 +000087#ifdef ZLIBOUT_DEBUG
88 fprintf(stderr,"zos flush: avail_in %d\n",zs->avail_in);
89#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000090
Pierre Ossmanb5822f32011-10-18 14:27:07 +000091 // Force out everything from the zlib encoder
92 deflate(Z_SYNC_FLUSH);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000093
94 offset += ptr - start;
95 ptr = start;
96}
97
98int ZlibOutStream::overrun(int itemSize, int nItems)
99{
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000100#ifdef ZLIBOUT_DEBUG
101 fprintf(stderr,"zos overrun\n");
102#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000103
104 if (itemSize > bufSize)
105 throw Exception("ZlibOutStream overrun: max itemSize exceeded");
106
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000107 checkCompressionLevel();
DRCff5ca2d2011-08-09 20:19:59 +0000108
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000109 while (end - ptr < itemSize) {
110 zs->next_in = start;
111 zs->avail_in = ptr - start;
112
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000113 deflate(Z_NO_FLUSH);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000114
115 // output buffer not full
116
117 if (zs->avail_in == 0) {
118 offset += ptr - start;
119 ptr = start;
120 } else {
121 // but didn't consume all the data? try shifting what's left to the
122 // start of the buffer.
123 fprintf(stderr,"z out buf not full, but in data not consumed\n");
124 memmove(start, zs->next_in, ptr - zs->next_in);
125 offset += zs->next_in - start;
126 ptr -= zs->next_in - start;
127 }
128 }
129
130 if (itemSize * nItems > end - ptr)
131 nItems = (end - ptr) / itemSize;
132
133 return nItems;
134}
135
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000136void ZlibOutStream::deflate(int flush)
137{
138 int rc;
139
140 if (!underlying)
141 throw Exception("ZlibOutStream: underlying OutStream has not been set");
142
143 if ((flush == Z_NO_FLUSH) && (zs->avail_in == 0))
144 return;
145
146 do {
147 underlying->check(1);
148 zs->next_out = underlying->getptr();
149 zs->avail_out = underlying->getend() - underlying->getptr();
150
151#ifdef ZLIBOUT_DEBUG
152 fprintf(stderr,"zos: calling deflate, avail_in %d, avail_out %d\n",
153 zs->avail_in,zs->avail_out);
154#endif
155
156 rc = ::deflate(zs, flush);
157 if (rc != Z_OK) {
158 // Silly zlib returns an error if you try to flush something twice
159 if ((rc == Z_BUF_ERROR) && (flush != Z_NO_FLUSH))
160 break;
161
162 throw Exception("ZlibOutStream: deflate failed");
163 }
164
165#ifdef ZLIBOUT_DEBUG
166 fprintf(stderr,"zos: after deflate: %d bytes\n",
167 zs->next_out-underlying->getptr());
168#endif
169
170 underlying->setptr(zs->next_out);
171 } while (zs->avail_out == 0);
172}
173
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000174void ZlibOutStream::checkCompressionLevel()
175{
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000176 int rc;
177
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000178 if (newLevel != compressionLevel) {
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000179#ifdef ZLIBOUT_DEBUG
180 fprintf(stderr,"zos change: avail_in %d\n",zs->avail_in);
181#endif
DRCff5ca2d2011-08-09 20:19:59 +0000182
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000183 // zlib is just horribly stupid. It does an implicit flush on
184 // parameter changes, but the flush it does is not one that forces
185 // out all the data. And since you cannot flush things again, we
186 // cannot force out our data after the parameter change. Hence we
187 // need to do a more proper flush here first.
188 deflate(Z_SYNC_FLUSH);
DRCff5ca2d2011-08-09 20:19:59 +0000189
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000190 rc = deflateParams (zs, newLevel, Z_DEFAULT_STRATEGY);
191 if (rc != Z_OK) {
192 // The implicit flush can result in this error, caused by the
193 // explicit flush we did above. It should be safe to ignore though
194 // as the first flush should have left things in a stable state...
195 if (rc != Z_BUF_ERROR)
196 throw Exception("ZlibOutStream: deflateParams failed");
DRCff5ca2d2011-08-09 20:19:59 +0000197 }
198
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000199 compressionLevel = newLevel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000200 }
201}