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"
16 #include "IOChannel.h" // for inheritance
18 #include "GnashException.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*/) {
39 namespace zlib_adapter
{
41 class InflaterIOChannel
: public IOChannel
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 {
75 // See dox in IOChannel
76 virtual bool bad() const {
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
];
93 // current stream position of uncompressed data.
94 std::streampos m_logical_stream_pos
;
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.
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
;
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
;
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
);
135 InflaterIOChannel::reset()
139 const int err
= inflateReset(&m_zstream
);
141 log_error("inflater_impl::reset() inflateReset() returned %d", err
);
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
;
165 InflaterIOChannel::inflate_from_stream(void* dst
, std::streamsize bytes
)
170 if (m_error
) return 0;
172 m_zstream
.next_out
= static_cast<unsigned char*>(dst
);
173 m_zstream
.avail_out
= bytes
;
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().
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
) {
194 if (err
== Z_BUF_ERROR
) {
195 std::ostringstream ss
;
196 ss
<< __FILE__
<< ":" << __LINE__
<< ": " << m_zstream
.msg
;
197 log_error("%s", ss
.str());
200 if (err
== Z_DATA_ERROR
) {
201 std::ostringstream ss
;
202 ss
<< __FILE__
<< ":" << __LINE__
<< ": " << m_zstream
.msg
;
203 throw ParserException(ss
.str());
206 if (err
== Z_MEM_ERROR
) {
207 std::ostringstream ss
;
208 ss
<< __FILE__
<< ":" << __LINE__
<< ": " << m_zstream
.msg
;
209 throw ParserException(ss
.str());
213 // something's wrong.
214 std::ostringstream ss
;
215 ss
<< __FILE__
<< ":" << __LINE__
<< ": " << m_zstream
.msg
;
216 throw ParserException(ss
.str());
220 if (m_zstream
.avail_out
== 0) {
225 if (m_error
) return 0;
227 const int bytes_read
= bytes
- m_zstream
.avail_out
;
228 m_logical_stream_pos
+= bytes_read
;
234 InflaterIOChannel::go_to_end()
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
];
247 const std::streamsize bytes_read
= inflate_from_stream(temp
, ZBUF_SIZE
);
249 // We've seeked as far as we can.
256 InflaterIOChannel::seek(std::streampos pos
)
259 log_debug("Inflater is in error condition");
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
);
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
;
277 std::streamsize readNow
= std::min
<std::streamsize
>(to_read
, ZBUF_SIZE
);
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.. ");
288 assert(m_logical_stream_pos
== pos
);
293 InflaterIOChannel::InflaterIOChannel(std::auto_ptr
<IOChannel
> in
)
296 m_initial_stream_pos(m_in
->tell()),
298 m_logical_stream_pos(m_initial_stream_pos
),
304 const int err
= inflateInit(&m_zstream
);
306 log_error("inflateInit() returned %d", err
);
312 std::auto_ptr
<IOChannel
> make_inflater(std::auto_ptr
<IOChannel
> in
)
315 return std::auto_ptr
<IOChannel
>(new InflaterIOChannel(in
));
320 #endif // HAVE_ZLIB_H
328 // indent-tabs-mode: t