change bzr to git
[gnash.git] / libbase / zlib_adapter.cpp
blob8fb3b53895c02f56dcee39cee709bd4b2a5f4244
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"
11 #include "IOChannel.h" // for inheritance
12 #include "log.h"
13 #include "GnashException.h"
14 #include <algorithm> // std::min
16 #include <sstream>
17 #include <memory>
19 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*/)
30 abort(); // callers should check this themselves
31 return std::auto_ptr<IOChannel>(NULL);
34 IOChannel* make_deflater(IOChannel* /*out*/)
36 abort(); // callers should check this themselves
37 return NULL;
42 #else // HAVE_ZLIB_H
44 extern "C" {
45 #include <zlib.h>
48 namespace zlib_adapter
51 class InflaterIOChannel : public IOChannel
53 public:
55 /// Constructor.
56 InflaterIOChannel(std::auto_ptr<IOChannel> in);
58 ~InflaterIOChannel() {
59 rewind_unused_bytes();
60 inflateEnd(&(m_zstream));
63 // See dox in IOChannel
64 virtual bool seek(std::streampos pos);
66 // See dox in IOChannel
67 virtual std::streamsize read(void* dst, std::streamsize bytes)
69 if (m_error) return 0;
70 return inflate_from_stream(dst, bytes);
73 // See dox in IOChannel
74 virtual void go_to_end();
76 // See dox in IOChannel
77 virtual std::streampos tell() const
79 return m_logical_stream_pos;
82 // See dox in IOChannel
83 virtual bool eof() const
85 return m_at_eof;
88 // See dox in IOChannel
89 virtual bool bad() const
91 return m_error;
94 private:
96 static const int ZBUF_SIZE = 4096;
98 std::auto_ptr<IOChannel> m_in;
100 // position of the input stream where we started inflating.
101 std::streampos m_initial_stream_pos;
103 unsigned char m_rawdata[ZBUF_SIZE];
105 z_stream m_zstream;
107 // current stream position of uncompressed data.
108 std::streampos m_logical_stream_pos;
110 bool m_at_eof;
111 bool m_error;
113 /// Discard current results and rewind to the beginning.
116 /// Necessary in order to seek backwards.
118 /// might throw a ParserException if unable to reset the uderlying
119 /// stream to original position.
121 void reset();
123 std::streamsize inflate_from_stream(void* dst, std::streamsize bytes);
125 // If we have unused bytes in our input buffer, rewind
126 // to before they started.
127 void rewind_unused_bytes();
131 const int InflaterIOChannel::ZBUF_SIZE;
133 void
134 InflaterIOChannel::rewind_unused_bytes()
136 if (m_zstream.avail_in > 0)
138 int pos = m_in->tell();
139 int rewound_pos = pos - m_zstream.avail_in;
140 assert(pos >= 0);
141 assert(pos >= m_initial_stream_pos);
142 assert(rewound_pos >= 0);
143 assert(rewound_pos >= m_initial_stream_pos);
145 m_in->seek(rewound_pos);
149 void
150 InflaterIOChannel::reset()
152 m_error = 0;
153 m_at_eof = 0;
154 int err = inflateReset(&m_zstream);
155 if (err != Z_OK) {
156 log_error("inflater_impl::reset() inflateReset() returned %d", err);
157 m_error = 1;
158 return;
161 m_zstream.next_in = 0;
162 m_zstream.avail_in = 0;
164 m_zstream.next_out = 0;
165 m_zstream.avail_out = 0;
167 // Rewind the underlying stream.
168 if (!m_in->seek(m_initial_stream_pos))
170 std::stringstream ss;
171 ss << "inflater_impl::reset: unable to seek underlying "
172 "stream to position " << m_initial_stream_pos;
173 throw ParserException(ss.str());
176 m_logical_stream_pos = m_initial_stream_pos;
179 std::streamsize
180 InflaterIOChannel::inflate_from_stream(void* dst, std::streamsize bytes)
183 assert(bytes);
185 if (m_error) return 0;
187 m_zstream.next_out = static_cast<unsigned char*>(dst);
188 m_zstream.avail_out = bytes;
190 for (;;)
192 if (m_zstream.avail_in == 0)
194 // Get more raw data.
195 int new_bytes = m_in->read(m_rawdata, ZBUF_SIZE);
196 if (new_bytes == 0)
198 // The cupboard is bare! We have nothing to feed to inflate().
199 break;
201 else
203 m_zstream.next_in = m_rawdata;
204 m_zstream.avail_in = new_bytes;
208 int err = inflate(&m_zstream, Z_SYNC_FLUSH);
209 if (err == Z_STREAM_END)
211 m_at_eof = true;
212 break;
214 if (err == Z_BUF_ERROR)
216 std::ostringstream ss;
217 ss << __FILE__ << ":" << __LINE__ << ": " << m_zstream.msg;
218 log_error("%s", ss.str());
219 break;
221 if (err == Z_DATA_ERROR)
223 std::ostringstream ss;
224 ss << __FILE__ << ":" << __LINE__ << ": " << m_zstream.msg;
225 throw ParserException(ss.str());
226 break;
228 if (err == Z_MEM_ERROR)
230 std::ostringstream ss;
231 ss << __FILE__ << ":" << __LINE__ << ": " << m_zstream.msg;
232 throw ParserException(ss.str());
233 break;
235 if (err != Z_OK)
237 // something's wrong.
238 std::ostringstream ss;
239 ss << __FILE__ << ":" << __LINE__ << ": " << m_zstream.msg;
240 throw ParserException(ss.str());
241 //m_error = 1;
242 break;
245 if (m_zstream.avail_out == 0)
247 break;
251 if (m_error)
253 return 0;
256 int bytes_read = bytes - m_zstream.avail_out;
257 m_logical_stream_pos += bytes_read;
259 return bytes_read;
262 void
263 InflaterIOChannel::go_to_end()
265 if (m_error)
267 throw IOException("InflaterIOChannel is in error condition, can't seek to end");
270 // Keep reading until we can't read any more.
272 unsigned char temp[ZBUF_SIZE];
274 // Seek forwards.
275 for (;;)
277 std::streamsize bytes_read = inflate_from_stream(temp, ZBUF_SIZE);
278 if (bytes_read == 0)
280 // We've seeked as far as we can.
281 break;
286 bool
287 InflaterIOChannel::seek(std::streampos pos)
289 if (m_error)
291 log_debug("Inflater is in error condition");
292 return false;
295 // If we're seeking backwards, then restart from the beginning.
296 if (pos < m_logical_stream_pos)
298 log_debug("inflater reset due to seek back from %d to %d",
299 m_logical_stream_pos, pos );
300 reset();
303 unsigned char temp[ZBUF_SIZE];
305 // Now seek forwards, by just reading data in blocks.
306 while (m_logical_stream_pos < pos)
308 std::streamsize to_read = pos - m_logical_stream_pos;
309 assert(to_read > 0);
311 std::streamsize readNow = std::min<std::streamsize>(to_read, ZBUF_SIZE);
312 assert(readNow > 0);
314 std::streamsize bytes_read = inflate_from_stream(temp, readNow);
315 assert(bytes_read <= readNow);
316 if (bytes_read == 0)
318 // Trouble; can't seek any further.
319 log_debug("Trouble: can't seek any further.. ");
320 return false;
321 break;
325 assert(m_logical_stream_pos == pos);
327 return true;
330 InflaterIOChannel::InflaterIOChannel(std::auto_ptr<IOChannel> in)
332 m_in(in),
333 m_initial_stream_pos(m_in->tell()),
334 m_logical_stream_pos(m_initial_stream_pos),
335 m_at_eof(false),
336 m_error(0)
338 assert(m_in.get());
340 m_zstream.zalloc = (alloc_func)0;
341 m_zstream.zfree = (free_func)0;
342 m_zstream.opaque = (voidpf)0;
344 m_zstream.next_in = 0;
345 m_zstream.avail_in = 0;
347 m_zstream.next_out = 0;
348 m_zstream.avail_out = 0;
350 int err = inflateInit(&m_zstream);
351 if (err != Z_OK) {
352 log_error("inflater_impl::ctor() inflateInit() returned %d", err);
353 m_error = 1;
354 return;
357 // Ready to go!
362 std::auto_ptr<IOChannel> make_inflater(std::auto_ptr<IOChannel> in)
364 assert(in.get());
365 return std::auto_ptr<IOChannel> (new InflaterIOChannel(in));
369 // @@ TODO
370 // IOChannel* make_deflater(IOChannel* out) { ... }
374 #endif // HAVE_ZLIB_H
376 } // namespace gnash
378 // Local Variables:
379 // mode: C++
380 // c-basic-offset: 8
381 // tab-width: 8
382 // indent-tabs-mode: t
383 // End: