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
10 #include "zlib_adapter.h"
11 #include "IOChannel.h" // for inheritance
13 #include "GnashException.h"
14 #include <algorithm> // std::min
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
48 namespace zlib_adapter
51 class InflaterIOChannel
: public IOChannel
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
88 // See dox in IOChannel
89 virtual bool bad() const
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
];
107 // current stream position of uncompressed data.
108 std::streampos m_logical_stream_pos
;
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.
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
;
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
;
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
);
150 InflaterIOChannel::reset()
154 int err
= inflateReset(&m_zstream
);
156 log_error("inflater_impl::reset() inflateReset() returned %d", err
);
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
;
180 InflaterIOChannel::inflate_from_stream(void* dst
, std::streamsize bytes
)
185 if (m_error
) return 0;
187 m_zstream
.next_out
= static_cast<unsigned char*>(dst
);
188 m_zstream
.avail_out
= bytes
;
192 if (m_zstream
.avail_in
== 0)
194 // Get more raw data.
195 int new_bytes
= m_in
->read(m_rawdata
, ZBUF_SIZE
);
198 // The cupboard is bare! We have nothing to feed to inflate().
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
)
214 if (err
== Z_BUF_ERROR
)
216 std::ostringstream ss
;
217 ss
<< __FILE__
<< ":" << __LINE__
<< ": " << m_zstream
.msg
;
218 log_error("%s", ss
.str());
221 if (err
== Z_DATA_ERROR
)
223 std::ostringstream ss
;
224 ss
<< __FILE__
<< ":" << __LINE__
<< ": " << m_zstream
.msg
;
225 throw ParserException(ss
.str());
228 if (err
== Z_MEM_ERROR
)
230 std::ostringstream ss
;
231 ss
<< __FILE__
<< ":" << __LINE__
<< ": " << m_zstream
.msg
;
232 throw ParserException(ss
.str());
237 // something's wrong.
238 std::ostringstream ss
;
239 ss
<< __FILE__
<< ":" << __LINE__
<< ": " << m_zstream
.msg
;
240 throw ParserException(ss
.str());
245 if (m_zstream
.avail_out
== 0)
256 int bytes_read
= bytes
- m_zstream
.avail_out
;
257 m_logical_stream_pos
+= bytes_read
;
263 InflaterIOChannel::go_to_end()
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
];
277 std::streamsize bytes_read
= inflate_from_stream(temp
, ZBUF_SIZE
);
280 // We've seeked as far as we can.
287 InflaterIOChannel::seek(std::streampos pos
)
291 log_debug("Inflater is in error condition");
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
);
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
;
311 std::streamsize readNow
= std::min
<std::streamsize
>(to_read
, ZBUF_SIZE
);
314 std::streamsize bytes_read
= inflate_from_stream(temp
, readNow
);
315 assert(bytes_read
<= readNow
);
318 // Trouble; can't seek any further.
319 log_debug("Trouble: can't seek any further.. ");
325 assert(m_logical_stream_pos
== pos
);
330 InflaterIOChannel::InflaterIOChannel(std::auto_ptr
<IOChannel
> in
)
333 m_initial_stream_pos(m_in
->tell()),
334 m_logical_stream_pos(m_initial_stream_pos
),
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
);
352 log_error("inflater_impl::ctor() inflateInit() returned %d", err
);
362 std::auto_ptr
<IOChannel
> make_inflater(std::auto_ptr
<IOChannel
> in
)
365 return std::auto_ptr
<IOChannel
> (new InflaterIOChannel(in
));
370 // IOChannel* make_deflater(IOChannel* out) { ... }
374 #endif // HAVE_ZLIB_H
382 // indent-tabs-mode: t