📄 zlib_adapter.cpp
字号:
// zlib_adapter.cpp -- Thatcher Ulrich 2003
// This source code has been donated to the Public Domain. Do
// whatever you want with it.
// Code to wrap zlib compression/decompression around a tu_file
// stream.
#include "base/zlib_adapter.h"
#include "base/tu_file.h"
#include "base/utility.h"
#if !TU_CONFIG_LINK_TO_ZLIB
// Stubs, in case client doesn't want to link to zlib.
namespace zlib_adapter
{
tu_file* make_inflater(tu_file* in) { return NULL; }
tu_file* make_deflater(tu_file* out) { return NULL; }
}
#else // TU_CONFIG_LINK_TO_ZLIB
#include <zlib.h>
namespace zlib_adapter
{
const int ZBUF_SIZE = 4096;
struct inflater_impl
{
tu_file* m_in;
z_stream m_zstream;
int m_initial_stream_pos; // position of the input stream where we started inflating.
int m_logical_stream_pos; // current stream position of uncompressed data.
bool m_at_eof;
unsigned char m_rawdata[ZBUF_SIZE];
int m_error;
inflater_impl(tu_file* in)
// Constructor.
:
m_in(in),
m_initial_stream_pos(in->get_position()),
m_logical_stream_pos(0),
m_at_eof(false),
m_error(0)
{
assert(m_in);
m_zstream.zalloc = (alloc_func)0;
m_zstream.zfree = (free_func)0;
m_zstream.opaque = (voidpf)0;
m_zstream.next_in = 0;
m_zstream.avail_in = 0;
m_zstream.next_out = 0;
m_zstream.avail_out = 0;
int err = inflateInit(&m_zstream);
if (err != Z_OK) {
//log_error("error: inflater_impl::ctor() inflateInit() returned %d\n", err);
m_error = 1;
return;
}
// Ready to go!
}
void reset()
// Discard current results and rewind to the beginning.
// Necessary in order to seek backwards.
{
m_error = 0;
m_at_eof = 0;
int err = inflateReset(&m_zstream);
if (err != Z_OK) {
m_error = 1;
return;
}
m_zstream.next_in = 0;
m_zstream.avail_in = 0;
m_zstream.next_out = 0;
m_zstream.avail_out = 0;
// Rewind the underlying stream.
m_in->set_position(m_initial_stream_pos);
m_logical_stream_pos = 0;
}
int inflate_from_stream(void* dst, int bytes)
{
if (m_error)
{
return 0;
}
m_zstream.next_out = (unsigned char*) dst;
m_zstream.avail_out = bytes;
for (;;)
{
if (m_zstream.avail_in == 0)
{
// Get more raw data.
int new_bytes = m_in->read_bytes(m_rawdata, ZBUF_SIZE);
if (new_bytes == 0)
{
// The cupboard is bare! We have nothing to feed to inflate().
break;
}
else
{
m_zstream.next_in = m_rawdata;
m_zstream.avail_in = new_bytes;
}
}
int err = inflate(&m_zstream, Z_SYNC_FLUSH);
if (err == Z_STREAM_END)
{
m_at_eof = true;
break;
}
if (err != Z_OK)
{
// something's wrong.
m_error = 1;
break;
}
if (m_zstream.avail_out == 0)
{
break;
}
}
int bytes_read = bytes - m_zstream.avail_out;
m_logical_stream_pos += bytes_read;
return bytes_read;
}
void rewind_unused_bytes()
// If we have unused bytes in our input buffer, rewind
// to before they started.
{
if (m_zstream.avail_in > 0)
{
int pos = m_in->get_position();
int rewound_pos = pos - m_zstream.avail_in;
assert(pos >= 0);
assert(pos >= m_initial_stream_pos);
assert(rewound_pos >= 0);
assert(rewound_pos >= m_initial_stream_pos);
m_in->set_position(rewound_pos);
}
}
};
int inflate_read(void* dst, int bytes, void* appdata)
// Return number of bytes actually read.
{
inflater_impl* inf = (inflater_impl*) appdata;
if (inf->m_error)
{
return 0;
}
return inf->inflate_from_stream(dst, bytes);
}
int inflate_write(const void* src, int bytes, void* appdata)
// Return number of bytes actually written.
{
// *In*flaters can't write!!!
assert(0);
return 0;
}
int inflate_seek(int pos, void* appdata)
// Try to go to pos. Return actual pos.
{
inflater_impl* inf = (inflater_impl*) appdata;
if (inf->m_error)
{
return inf->m_logical_stream_pos;
}
// If we're seeking backwards, then restart from the beginning.
if (pos < inf->m_logical_stream_pos)
{
inf->reset();
}
unsigned char temp[ZBUF_SIZE];
// Now seek forwards, by just reading data in blocks.
while (inf->m_logical_stream_pos < pos)
{
int to_read = pos - inf->m_logical_stream_pos;
int to_read_this_time = imin(to_read, ZBUF_SIZE);
assert(to_read_this_time > 0);
int bytes_read = inf->inflate_from_stream(temp, to_read_this_time);
assert(bytes_read <= to_read_this_time);
if (bytes_read == 0)
{
// Trouble; can't seek any further.
break;
}
}
assert(inf->m_logical_stream_pos <= pos);
return inf->m_logical_stream_pos;
}
int inflate_seek_to_end(void* appdata)
{
inflater_impl* inf = (inflater_impl*) appdata;
if (inf->m_error)
{
return inf->m_logical_stream_pos;
}
// Keep reading until we can't read any more.
unsigned char temp[ZBUF_SIZE];
// Seek forwards.
for (;;)
{
int bytes_read = inf->inflate_from_stream(temp, ZBUF_SIZE);
if (bytes_read == 0)
{
// We've seeked as far as we can.
break;
}
}
return inf->m_logical_stream_pos;
}
int inflate_tell(const void* appdata)
{
inflater_impl* inf = (inflater_impl*) appdata;
return inf->m_logical_stream_pos;
}
bool inflate_get_eof(void* appdata)
{
inflater_impl* inf = (inflater_impl*) appdata;
return inf->m_at_eof;
}
int inflate_close(void* appdata)
{
inflater_impl* inf = (inflater_impl*) appdata;
inf->rewind_unused_bytes();
int err = inflateEnd(&(inf->m_zstream));
delete inf;
if (err != Z_OK)
{
return TU_FILE_CLOSE_ERROR;
}
return 0;
}
tu_file* make_inflater(tu_file* in)
{
assert(in);
inflater_impl* inflater = new inflater_impl(in);
return new tu_file(
inflater,
inflate_read,
inflate_write,
inflate_seek,
inflate_seek_to_end,
inflate_tell,
inflate_get_eof,
inflate_close);
}
// @@ TODO
// tu_file* make_deflater(tu_file* out) { ... }
}
#endif // TU_CONFIG_LINK_TO_ZLIB
// Local Variables:
// mode: C++
// c-basic-offset: 8
// tab-width: 8
// indent-tabs-mode: t
// End:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -