Fix test for bug #32625
[gnash.git] / libbase / zlib_adapter.cpp
blob93353a73ee4c589693bd5354617d8a5b1a9b8254
1 // zlib_adapter.cpp -- Thatcher Ulrich 2003
3 // This source code has been donated to the Public Domain. Do
4 // whatever you want with it.
6 // Code to wrap zlib compression/decompression around a IOChannel
7 // stream.
10 #include "zlib_adapter.h"
12 #include <algorithm>
13 #include <sstream>
14 #include <memory>
16 #include "IOChannel.h" // for inheritance
17 #include "log.h"
18 #include "GnashException.h"
20 namespace gnash {
22 #ifndef HAVE_ZLIB_H
25 // Stubs, in case client doesn't want to link to zlib.
26 namespace zlib_adapter
28 std::auto_ptr<IOChannel> make_inflater(std::auto_ptr<IOChannel> /*in*/) {
29 std::abort();
33 #else // HAVE_ZLIB_H
35 extern "C" {
36 # include <zlib.h>
39 namespace zlib_adapter {
41 class InflaterIOChannel : public IOChannel
43 public:
45 /// Constructor.
46 InflaterIOChannel(std::auto_ptr<IOChannel> in);
48 ~InflaterIOChannel() {
49 rewind_unused_bytes();
50 inflateEnd(&(m_zstream));
53 // See dox in IOChannel
54 virtual bool seek(std::streampos pos);
56 // See dox in IOChannel
57 virtual std::streamsize read(void* dst, std::streamsize bytes) {
58 if (m_error) return 0;
59 return inflate_from_stream(dst, bytes);
62 // See dox in IOChannel
63 virtual void go_to_end();
65 // See dox in IOChannel
66 virtual std::streampos tell() const {
67 return m_logical_stream_pos;
70 // See dox in IOChannel
71 virtual bool eof() const {
72 return m_at_eof;
75 // See dox in IOChannel
76 virtual bool bad() const {
77 return m_error;
80 private:
82 static const int ZBUF_SIZE = 4096;
84 std::auto_ptr<IOChannel> m_in;
86 // position of the input stream where we started inflating.
87 std::streampos m_initial_stream_pos;
89 unsigned char m_rawdata[ZBUF_SIZE];
91 z_stream m_zstream;
93 // current stream position of uncompressed data.
94 std::streampos m_logical_stream_pos;
96 bool m_at_eof;
97 bool m_error;
99 /// Discard current results and rewind to the beginning.
102 /// Necessary in order to seek backwards.
104 /// might throw a ParserException if unable to reset the uderlying
105 /// stream to original position.
107 void reset();
109 std::streamsize inflate_from_stream(void* dst, std::streamsize bytes);
111 // If we have unused bytes in our input buffer, rewind
112 // to before they started.
113 void rewind_unused_bytes();
117 const int InflaterIOChannel::ZBUF_SIZE;
119 void
120 InflaterIOChannel::rewind_unused_bytes()
122 if (m_zstream.avail_in > 0) {
123 const int pos = m_in->tell();
124 const int rewound_pos = pos - m_zstream.avail_in;
125 assert(pos >= 0);
126 assert(pos >= m_initial_stream_pos);
127 assert(rewound_pos >= 0);
128 assert(rewound_pos >= m_initial_stream_pos);
130 m_in->seek(rewound_pos);
134 void
135 InflaterIOChannel::reset()
137 m_error = 0;
138 m_at_eof = 0;
139 const int err = inflateReset(&m_zstream);
140 if (err != Z_OK) {
141 log_error("inflater_impl::reset() inflateReset() returned %d", err);
142 m_error = 1;
143 return;
146 m_zstream.next_in = 0;
147 m_zstream.avail_in = 0;
149 m_zstream.next_out = 0;
150 m_zstream.avail_out = 0;
152 // Rewind the underlying stream.
153 if (!m_in->seek(m_initial_stream_pos))
155 std::stringstream ss;
156 ss << "inflater_impl::reset: unable to seek underlying "
157 "stream to position " << m_initial_stream_pos;
158 throw ParserException(ss.str());
161 m_logical_stream_pos = m_initial_stream_pos;
164 std::streamsize
165 InflaterIOChannel::inflate_from_stream(void* dst, std::streamsize bytes)
168 assert(bytes);
170 if (m_error) return 0;
172 m_zstream.next_out = static_cast<unsigned char*>(dst);
173 m_zstream.avail_out = bytes;
175 for (;;) {
176 if (m_zstream.avail_in == 0) {
177 // Get more raw data.
178 const int new_bytes = m_in->read(m_rawdata, ZBUF_SIZE);
179 if (new_bytes == 0) {
180 // The cupboard is bare! We have nothing to feed to inflate().
181 break;
183 else {
184 m_zstream.next_in = m_rawdata;
185 m_zstream.avail_in = new_bytes;
189 const int err = inflate(&m_zstream, Z_SYNC_FLUSH);
190 if (err == Z_STREAM_END) {
191 m_at_eof = true;
192 break;
194 if (err == Z_BUF_ERROR) {
195 std::ostringstream ss;
196 ss << __FILE__ << ":" << __LINE__ << ": " << m_zstream.msg;
197 log_error("%s", ss.str());
198 break;
200 if (err == Z_DATA_ERROR) {
201 std::ostringstream ss;
202 ss << __FILE__ << ":" << __LINE__ << ": " << m_zstream.msg;
203 throw ParserException(ss.str());
204 break;
206 if (err == Z_MEM_ERROR) {
207 std::ostringstream ss;
208 ss << __FILE__ << ":" << __LINE__ << ": " << m_zstream.msg;
209 throw ParserException(ss.str());
210 break;
212 if (err != Z_OK) {
213 // something's wrong.
214 std::ostringstream ss;
215 ss << __FILE__ << ":" << __LINE__ << ": " << m_zstream.msg;
216 throw ParserException(ss.str());
217 break;
220 if (m_zstream.avail_out == 0) {
221 break;
225 if (m_error) return 0;
227 const int bytes_read = bytes - m_zstream.avail_out;
228 m_logical_stream_pos += bytes_read;
230 return bytes_read;
233 void
234 InflaterIOChannel::go_to_end()
236 if (m_error) {
237 throw IOException("InflaterIOChannel is in error condition, "
238 "can't seek to end");
241 // Keep reading until we can't read any more.
243 unsigned char temp[ZBUF_SIZE];
245 // Seek forwards.
246 for (;;) {
247 const std::streamsize bytes_read = inflate_from_stream(temp, ZBUF_SIZE);
248 if (!bytes_read) {
249 // We've seeked as far as we can.
250 break;
255 bool
256 InflaterIOChannel::seek(std::streampos pos)
258 if (m_error) {
259 log_debug("Inflater is in error condition");
260 return false;
263 // If we're seeking backwards, then restart from the beginning.
264 if (pos < m_logical_stream_pos) {
265 log_debug("inflater reset due to seek back from %d to %d",
266 m_logical_stream_pos, pos );
267 reset();
270 unsigned char temp[ZBUF_SIZE];
272 // Now seek forwards, by just reading data in blocks.
273 while (m_logical_stream_pos < pos) {
274 std::streamsize to_read = pos - m_logical_stream_pos;
275 assert(to_read > 0);
277 std::streamsize readNow = std::min<std::streamsize>(to_read, ZBUF_SIZE);
278 assert(readNow > 0);
280 std::streamsize bytes_read = inflate_from_stream(temp, readNow);
281 assert(bytes_read <= readNow);
282 if (bytes_read == 0) {
283 log_debug("Trouble: can't seek any further.. ");
284 return false;
288 assert(m_logical_stream_pos == pos);
290 return true;
293 InflaterIOChannel::InflaterIOChannel(std::auto_ptr<IOChannel> in)
295 m_in(in),
296 m_initial_stream_pos(m_in->tell()),
297 m_zstream(),
298 m_logical_stream_pos(m_initial_stream_pos),
299 m_at_eof(false),
300 m_error(0)
302 assert(m_in.get());
304 const int err = inflateInit(&m_zstream);
305 if (err != Z_OK) {
306 log_error("inflateInit() returned %d", err);
307 m_error = 1;
308 return;
312 std::auto_ptr<IOChannel> make_inflater(std::auto_ptr<IOChannel> in)
314 assert(in.get());
315 return std::auto_ptr<IOChannel>(new InflaterIOChannel(in));
320 #endif // HAVE_ZLIB_H
322 } // namespace gnash
324 // Local Variables:
325 // mode: C++
326 // c-basic-offset: 8
327 // tab-width: 8
328 // indent-tabs-mode: t
329 // End: