blob: c86a5a5585a1e21c1dc6751c34d6d8ccf554ff80 [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),
32 bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
33{
34 zs = new z_stream;
35 zs->zalloc = Z_NULL;
36 zs->zfree = Z_NULL;
37 zs->opaque = Z_NULL;
38 if (deflateInit(zs, compressLevel) != Z_OK) {
39 delete zs;
40 throw Exception("ZlibOutStream: deflateInit failed");
41 }
42 ptr = start = new U8[bufSize];
43 end = start + bufSize;
44}
45
46ZlibOutStream::~ZlibOutStream()
47{
48 try {
49 flush();
50 } catch (Exception&) {
51 }
52 delete [] start;
53 deflateEnd(zs);
54 delete zs;
55}
56
57void ZlibOutStream::setUnderlying(OutStream* os)
58{
59 underlying = os;
60}
61
62void ZlibOutStream::setCompressionLevel(int level)
63{
64 if (level < -1 || level > 9)
65 level = -1; // Z_DEFAULT_COMPRESSION
66
67 newLevel = level;
68}
69
70int ZlibOutStream::length()
71{
72 return offset + ptr - start;
73}
74
75void ZlibOutStream::flush()
76{
77 zs->next_in = start;
78 zs->avail_in = ptr - start;
79
80// fprintf(stderr,"zos flush: avail_in %d\n",zs->avail_in);
81
82 while (zs->avail_in != 0) {
83
84 do {
85 underlying->check(1);
86 zs->next_out = underlying->getptr();
87 zs->avail_out = underlying->getend() - underlying->getptr();
88
89// fprintf(stderr,"zos flush: calling deflate, avail_in %d, avail_out %d\n",
90// zs->avail_in,zs->avail_out);
91 checkCompressionLevel();
DRCf4a341b2011-08-09 11:12:55 +000092 if (zs->avail_in != 0) {
93 int rc = deflate(zs, Z_SYNC_FLUSH);
94 if (rc != Z_OK) throw Exception("ZlibOutStream: deflate failed");
95 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000096
97// fprintf(stderr,"zos flush: after deflate: %d bytes\n",
98// zs->next_out-underlying->getptr());
99
100 underlying->setptr(zs->next_out);
101 } while (zs->avail_out == 0);
102 }
103
104 offset += ptr - start;
105 ptr = start;
106}
107
108int ZlibOutStream::overrun(int itemSize, int nItems)
109{
110// fprintf(stderr,"ZlibOutStream overrun\n");
111
112 if (itemSize > bufSize)
113 throw Exception("ZlibOutStream overrun: max itemSize exceeded");
114
115 while (end - ptr < itemSize) {
116 zs->next_in = start;
117 zs->avail_in = ptr - start;
118
119 do {
120 underlying->check(1);
121 zs->next_out = underlying->getptr();
122 zs->avail_out = underlying->getend() - underlying->getptr();
123
124// fprintf(stderr,"zos overrun: calling deflate, avail_in %d, avail_out %d\n",
125// zs->avail_in,zs->avail_out);
126
DRCf4a341b2011-08-09 11:12:55 +0000127 checkCompressionLevel();
128 if (zs->avail_in != 0) {
129 int rc = deflate(zs, 0);
130 if (rc != Z_OK) throw Exception("ZlibOutStream: deflate failed");
131 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000132
133// fprintf(stderr,"zos overrun: after deflate: %d bytes\n",
134// zs->next_out-underlying->getptr());
135
136 underlying->setptr(zs->next_out);
137 } while (zs->avail_out == 0);
138
139 // output buffer not full
140
141 if (zs->avail_in == 0) {
142 offset += ptr - start;
143 ptr = start;
144 } else {
145 // but didn't consume all the data? try shifting what's left to the
146 // start of the buffer.
147 fprintf(stderr,"z out buf not full, but in data not consumed\n");
148 memmove(start, zs->next_in, ptr - zs->next_in);
149 offset += zs->next_in - start;
150 ptr -= zs->next_in - start;
151 }
152 }
153
154 if (itemSize * nItems > end - ptr)
155 nItems = (end - ptr) / itemSize;
156
157 return nItems;
158}
159
160void ZlibOutStream::checkCompressionLevel()
161{
162 if (newLevel != compressionLevel) {
DRCf4a341b2011-08-09 11:12:55 +0000163 if (deflateParams (zs, newLevel, Z_DEFAULT_STRATEGY) != Z_OK) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000164 throw Exception("ZlibOutStream: deflateParams failed");
DRCf4a341b2011-08-09 11:12:55 +0000165 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000166 compressionLevel = newLevel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000167 }
168}