blob: f04536fa24d7b1f00fbc6be73dbaa50da1e3486d [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
26using namespace rdr;
27
28enum { DEFAULT_BUF_SIZE = 16384 };
29
30ZlibOutStream::ZlibOutStream(OutStream* os, int bufSize_, int compressLevel)
31 : underlying(os), compressionLevel(compressLevel), newLevel(compressLevel),
DRCff5ca2d2011-08-09 20:19:59 +000032 bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0),
33 newBehavior(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000034{
35 zs = new z_stream;
36 zs->zalloc = Z_NULL;
37 zs->zfree = Z_NULL;
38 zs->opaque = Z_NULL;
39 if (deflateInit(zs, compressLevel) != Z_OK) {
40 delete zs;
41 throw Exception("ZlibOutStream: deflateInit failed");
42 }
43 ptr = start = new U8[bufSize];
44 end = start + bufSize;
DRCff5ca2d2011-08-09 20:19:59 +000045 const char *version = zlibVersion();
46 if (strcmp(version, "1.2.3") > 0) newBehavior = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000047}
48
49ZlibOutStream::~ZlibOutStream()
50{
51 try {
52 flush();
53 } catch (Exception&) {
54 }
55 delete [] start;
56 deflateEnd(zs);
57 delete zs;
58}
59
60void ZlibOutStream::setUnderlying(OutStream* os)
61{
62 underlying = os;
63}
64
65void ZlibOutStream::setCompressionLevel(int level)
66{
67 if (level < -1 || level > 9)
68 level = -1; // Z_DEFAULT_COMPRESSION
69
70 newLevel = level;
71}
72
73int ZlibOutStream::length()
74{
75 return offset + ptr - start;
76}
77
78void ZlibOutStream::flush()
79{
80 zs->next_in = start;
81 zs->avail_in = ptr - start;
82
83// fprintf(stderr,"zos flush: avail_in %d\n",zs->avail_in);
84
DRCff5ca2d2011-08-09 20:19:59 +000085 if (!underlying)
86 throw Exception("ZlibOutStream: underlying OutStream has not been set");
87
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000088 while (zs->avail_in != 0) {
89
90 do {
91 underlying->check(1);
92 zs->next_out = underlying->getptr();
93 zs->avail_out = underlying->getend() - underlying->getptr();
94
95// fprintf(stderr,"zos flush: calling deflate, avail_in %d, avail_out %d\n",
96// zs->avail_in,zs->avail_out);
97 checkCompressionLevel();
DRCf4a341b2011-08-09 11:12:55 +000098 if (zs->avail_in != 0) {
99 int rc = deflate(zs, Z_SYNC_FLUSH);
100 if (rc != Z_OK) throw Exception("ZlibOutStream: deflate failed");
101 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000102
103// fprintf(stderr,"zos flush: after deflate: %d bytes\n",
104// zs->next_out-underlying->getptr());
105
106 underlying->setptr(zs->next_out);
107 } while (zs->avail_out == 0);
108 }
109
110 offset += ptr - start;
111 ptr = start;
112}
113
114int ZlibOutStream::overrun(int itemSize, int nItems)
115{
116// fprintf(stderr,"ZlibOutStream overrun\n");
117
118 if (itemSize > bufSize)
119 throw Exception("ZlibOutStream overrun: max itemSize exceeded");
120
DRCff5ca2d2011-08-09 20:19:59 +0000121 if (!underlying)
122 throw Exception("ZlibOutStream: underlying OutStream has not been set");
123
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000124 while (end - ptr < itemSize) {
125 zs->next_in = start;
126 zs->avail_in = ptr - start;
127
128 do {
129 underlying->check(1);
130 zs->next_out = underlying->getptr();
131 zs->avail_out = underlying->getend() - underlying->getptr();
132
133// fprintf(stderr,"zos overrun: calling deflate, avail_in %d, avail_out %d\n",
134// zs->avail_in,zs->avail_out);
135
DRCff5ca2d2011-08-09 20:19:59 +0000136 checkCompressionLevel();
137 if (zs->avail_in != 0) {
138 int rc = deflate(zs, 0);
139 if (rc != Z_OK) throw Exception("ZlibOutStream: deflate failed");
140 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000141
142// fprintf(stderr,"zos overrun: after deflate: %d bytes\n",
143// zs->next_out-underlying->getptr());
144
145 underlying->setptr(zs->next_out);
146 } while (zs->avail_out == 0);
147
148 // output buffer not full
149
150 if (zs->avail_in == 0) {
151 offset += ptr - start;
152 ptr = start;
153 } else {
154 // but didn't consume all the data? try shifting what's left to the
155 // start of the buffer.
156 fprintf(stderr,"z out buf not full, but in data not consumed\n");
157 memmove(start, zs->next_in, ptr - zs->next_in);
158 offset += zs->next_in - start;
159 ptr -= zs->next_in - start;
160 }
161 }
162
163 if (itemSize * nItems > end - ptr)
164 nItems = (end - ptr) / itemSize;
165
166 return nItems;
167}
168
169void ZlibOutStream::checkCompressionLevel()
170{
171 if (newLevel != compressionLevel) {
DRCff5ca2d2011-08-09 20:19:59 +0000172
173 // This is a horrible hack, but after many hours of trying, I couldn't find
174 // a better way to make this class work properly with both Zlib 1.2.3 and
175 // 1.2.5. 1.2.3 does a Z_PARTIAL_FLUSH in the body of deflateParams() if
176 // the compression level has changed, and 1.2.5 does a Z_BLOCK flush.
177
178 if (newBehavior) {
179 int rc = deflate(zs, Z_SYNC_FLUSH);
180 if (rc != Z_OK) throw Exception("ZlibOutStream: deflate failed");
181 }
182
DRCf4a341b2011-08-09 11:12:55 +0000183 if (deflateParams (zs, newLevel, Z_DEFAULT_STRATEGY) != Z_OK) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000184 throw Exception("ZlibOutStream: deflateParams failed");
DRCf4a341b2011-08-09 11:12:55 +0000185 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000186 compressionLevel = newLevel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000187 }
188}