blob: 260bd1a4cb8b7cfecefea5208fa35da148358d42 [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
20#include <rdr/ZlibOutStream.h>
21#include <rdr/Exception.h>
Adam Tkac20e0d712008-11-14 14:48:21 +000022#include <os/print.h>
23
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000024#include <zlib.h>
25
Pierre Ossmanb5822f32011-10-18 14:27:07 +000026#undef ZLIBOUT_DEBUG
27
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000028using namespace rdr;
29
30enum { DEFAULT_BUF_SIZE = 16384 };
31
32ZlibOutStream::ZlibOutStream(OutStream* os, int bufSize_, int compressLevel)
33 : underlying(os), compressionLevel(compressLevel), newLevel(compressLevel),
Pierre Ossmanb5822f32011-10-18 14:27:07 +000034 bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000035{
36 zs = new z_stream;
37 zs->zalloc = Z_NULL;
38 zs->zfree = Z_NULL;
39 zs->opaque = Z_NULL;
Pierre Ossmanb5822f32011-10-18 14:27:07 +000040 zs->next_in = Z_NULL;
41 zs->avail_in = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000042 if (deflateInit(zs, compressLevel) != Z_OK) {
43 delete zs;
44 throw Exception("ZlibOutStream: deflateInit failed");
45 }
46 ptr = start = new U8[bufSize];
47 end = start + bufSize;
48}
49
50ZlibOutStream::~ZlibOutStream()
51{
52 try {
53 flush();
54 } catch (Exception&) {
55 }
56 delete [] start;
57 deflateEnd(zs);
58 delete zs;
59}
60
61void ZlibOutStream::setUnderlying(OutStream* os)
62{
63 underlying = os;
64}
65
66void ZlibOutStream::setCompressionLevel(int level)
67{
68 if (level < -1 || level > 9)
69 level = -1; // Z_DEFAULT_COMPRESSION
70
71 newLevel = level;
72}
73
74int ZlibOutStream::length()
75{
76 return offset + ptr - start;
77}
78
79void ZlibOutStream::flush()
80{
Pierre Ossmanb5822f32011-10-18 14:27:07 +000081 checkCompressionLevel();
82
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000083 zs->next_in = start;
84 zs->avail_in = ptr - start;
85
Pierre Ossmanb5822f32011-10-18 14:27:07 +000086#ifdef ZLIBOUT_DEBUG
87 fprintf(stderr,"zos flush: avail_in %d\n",zs->avail_in);
88#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000089
Pierre Ossmanb5822f32011-10-18 14:27:07 +000090 // Force out everything from the zlib encoder
91 deflate(Z_SYNC_FLUSH);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000092
93 offset += ptr - start;
94 ptr = start;
95}
96
97int ZlibOutStream::overrun(int itemSize, int nItems)
98{
Pierre Ossmanb5822f32011-10-18 14:27:07 +000099#ifdef ZLIBOUT_DEBUG
100 fprintf(stderr,"zos overrun\n");
101#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000102
103 if (itemSize > bufSize)
104 throw Exception("ZlibOutStream overrun: max itemSize exceeded");
105
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000106 checkCompressionLevel();
DRCff5ca2d2011-08-09 20:19:59 +0000107
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000108 while (end - ptr < itemSize) {
109 zs->next_in = start;
110 zs->avail_in = ptr - start;
111
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000112 deflate(Z_NO_FLUSH);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000113
114 // output buffer not full
115
116 if (zs->avail_in == 0) {
117 offset += ptr - start;
118 ptr = start;
119 } else {
120 // but didn't consume all the data? try shifting what's left to the
121 // start of the buffer.
122 fprintf(stderr,"z out buf not full, but in data not consumed\n");
123 memmove(start, zs->next_in, ptr - zs->next_in);
124 offset += zs->next_in - start;
125 ptr -= zs->next_in - start;
126 }
127 }
128
129 if (itemSize * nItems > end - ptr)
130 nItems = (end - ptr) / itemSize;
131
132 return nItems;
133}
134
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000135void ZlibOutStream::deflate(int flush)
136{
137 int rc;
138
139 if (!underlying)
140 throw Exception("ZlibOutStream: underlying OutStream has not been set");
141
142 if ((flush == Z_NO_FLUSH) && (zs->avail_in == 0))
143 return;
144
145 do {
146 underlying->check(1);
147 zs->next_out = underlying->getptr();
148 zs->avail_out = underlying->getend() - underlying->getptr();
149
150#ifdef ZLIBOUT_DEBUG
151 fprintf(stderr,"zos: calling deflate, avail_in %d, avail_out %d\n",
152 zs->avail_in,zs->avail_out);
153#endif
154
155 rc = ::deflate(zs, flush);
156 if (rc != Z_OK) {
157 // Silly zlib returns an error if you try to flush something twice
158 if ((rc == Z_BUF_ERROR) && (flush != Z_NO_FLUSH))
159 break;
160
161 throw Exception("ZlibOutStream: deflate failed");
162 }
163
164#ifdef ZLIBOUT_DEBUG
165 fprintf(stderr,"zos: after deflate: %d bytes\n",
166 zs->next_out-underlying->getptr());
167#endif
168
169 underlying->setptr(zs->next_out);
170 } while (zs->avail_out == 0);
171}
172
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000173void ZlibOutStream::checkCompressionLevel()
174{
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000175 int rc;
176
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000177 if (newLevel != compressionLevel) {
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000178#ifdef ZLIBOUT_DEBUG
179 fprintf(stderr,"zos change: avail_in %d\n",zs->avail_in);
180#endif
DRCff5ca2d2011-08-09 20:19:59 +0000181
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000182 // zlib is just horribly stupid. It does an implicit flush on
183 // parameter changes, but the flush it does is not one that forces
184 // out all the data. And since you cannot flush things again, we
185 // cannot force out our data after the parameter change. Hence we
186 // need to do a more proper flush here first.
187 deflate(Z_SYNC_FLUSH);
DRCff5ca2d2011-08-09 20:19:59 +0000188
Pierre Ossmanb5822f32011-10-18 14:27:07 +0000189 rc = deflateParams (zs, newLevel, Z_DEFAULT_STRATEGY);
190 if (rc != Z_OK) {
191 // The implicit flush can result in this error, caused by the
192 // explicit flush we did above. It should be safe to ignore though
193 // as the first flush should have left things in a stable state...
194 if (rc != Z_BUF_ERROR)
195 throw Exception("ZlibOutStream: deflateParams failed");
DRCff5ca2d2011-08-09 20:19:59 +0000196 }
197
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000198 compressionLevel = newLevel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000199 }
200}