--- /dev/null
+#include <msp/core/application.h>
+#include <msp/core/getopt.h>
+#include <msp/io/console.h>
+#include <msp/io/file.h>
+#include <msp/io/zlibcompressed.h>
+
+using namespace std;
+using namespace Msp;
+
+class Z: public RegisteredApplication<Z>
+{
+private:
+ IO::ZlibCompressed *zlib;
+ IO::File *input_file;
+ IO::Base *input;
+ IO::Base *output;
+ bool decompress;
+
+public:
+ Z(int, char **);
+ ~Z();
+
+ virtual int main();
+};
+
+Z::Z(int argc, char **argv):
+ input_file(0)
+{
+ GetOpt getopt;
+ getopt.add_option('d', "decompress", decompress, GetOpt::NO_ARG);
+ getopt(argc, argv);
+
+ const vector<string> &args = getopt.get_args();
+ if(!args.empty())
+ input_file = new IO::File(args[0]);
+
+ input = (input_file ? static_cast<IO::Base *>(input_file) : static_cast<IO::Base *>(&IO::cin));
+ output = &IO::cout;
+ if(decompress)
+ {
+ zlib = new IO::ZlibCompressed(*input);
+ input = zlib;
+ }
+ else
+ {
+ zlib = new IO::ZlibCompressed(*output);
+ output = zlib;
+ }
+}
+
+Z::~Z()
+{
+ delete zlib;
+ delete input_file;
+}
+
+int Z::main()
+{
+ char buffer[1024];
+ while(1)
+ {
+ unsigned len = input->read(buffer, sizeof(buffer));
+ if(!len)
+ break;
+ output->write(buffer, len);
+ }
+ return 0;
+}
--- /dev/null
+#ifdef WITH_ZLIB
+#include <zlib.h>
+#endif
+#include "zlibcompressed.h"
+
+using namespace std;
+
+namespace Msp {
+namespace IO {
+
+zlib_error::zlib_error(const std::string &w, int c):
+#ifdef WITH_ZLIB
+ runtime_error(w+": "+zError(c)),
+#else
+ runtime_error(w),
+#endif
+ code_(c)
+{ }
+
+
+struct ZlibCompressed::Private
+{
+#ifdef WITH_ZLIB
+ z_stream stream;
+#endif
+
+ Private();
+};
+
+ZlibCompressed::Private::Private()
+{
+#ifdef WITH_ZLIB
+ stream.zalloc = 0;
+ stream.zfree = 0;
+ stream.opaque = 0;
+#endif
+}
+
+
+ZlibCompressed::ZlibCompressed(Base &b, unsigned level):
+ below(b),
+ buffer_size(1024),
+ in_buffer(0),
+ out_buffer(0),
+ priv(0)
+{
+#ifdef WITH_ZLIB
+ mode = below.get_mode()&M_RDWR;
+ if(mode!=M_READ && mode!=M_WRITE)
+ throw invalid_access(mode);
+
+ priv = new Private;
+
+ if(mode==M_WRITE)
+ {
+ int ret = deflateInit(&priv->stream, level);
+ if(ret!=Z_OK)
+ throw zlib_error("deflateInit", ret);
+ }
+ else
+ {
+ int ret = inflateInit(&priv->stream);
+ if(ret!=Z_OK)
+ throw zlib_error("inflateInit", ret);
+ }
+
+ in_buffer = new unsigned char[buffer_size];
+ out_buffer = new unsigned char[buffer_size];
+
+ priv->stream.next_in = in_buffer;
+ priv->stream.avail_in = 0;
+ priv->stream.next_out = out_buffer;
+ priv->stream.avail_out = buffer_size;
+
+ below.signal_flush_required.connect(sigc::mem_fun(this, &ZlibCompressed::flush));
+#else
+ (void)level;
+ throw zlib_error("unsupported", -1);
+#endif
+}
+
+ZlibCompressed::~ZlibCompressed()
+{
+#ifdef WITH_ZLIB
+ if(mode==M_WRITE)
+ {
+ while(compress_data(Z_FINISH)) ;
+ deflateEnd(&priv->stream);
+ }
+ else
+ inflateEnd(&priv->stream);
+#endif
+
+ delete[] in_buffer;
+ delete[] out_buffer;
+ delete priv;
+}
+
+void ZlibCompressed::flush()
+{
+#ifdef WITH_ZLIB
+ if(mode==M_WRITE)
+ {
+ while(1)
+ {
+ if(!compress_data(Z_SYNC_FLUSH))
+ break;
+
+ // The flush is done when all input data has been consumed
+ if(!priv->stream.avail_in)
+ break;
+ }
+ }
+#endif
+}
+
+unsigned ZlibCompressed::do_write(const char *data, unsigned size)
+{
+ check_access(M_WRITE);
+
+ unsigned processed = 0;
+#ifdef WITH_ZLIB
+ while(processed<size)
+ {
+ unsigned free_in = (in_buffer+buffer_size-priv->stream.next_in);
+ if(free_in<size && priv->stream.next_in>in_buffer)
+ {
+ // Not all of the data fits in the buffer, so make some more room
+ copy(priv->stream.next_in, priv->stream.next_in+priv->stream.avail_in, in_buffer);
+ priv->stream.next_in = in_buffer;
+ free_in = buffer_size-priv->stream.avail_in;
+ }
+
+ if(free_in)
+ {
+ // Copy as much data into the input buffer as possible
+ unsigned len = min(free_in, size-processed);
+ copy(data+processed, data+processed+len, priv->stream.next_in+priv->stream.avail_in);
+ priv->stream.avail_in += len;
+ processed += len;
+ }
+
+ bool stalled = false;
+ while(priv->stream.avail_in && !stalled)
+ stalled = !compress_data(Z_NO_FLUSH);
+ if(stalled)
+ break;
+ }
+#else
+ (void)data;
+ (void)size;
+#endif
+
+ return processed;
+}
+
+bool ZlibCompressed::compress_data(int flush_mode)
+{
+#ifdef WITH_ZLIB
+ bool can_deflate = ((priv->stream.avail_in || flush_mode) && priv->stream.avail_out);
+ bool finished = false;
+ if(can_deflate)
+ {
+ int ret = deflate(&priv->stream, flush_mode);
+ if(flush_mode==Z_FINISH && ret==Z_STREAM_END)
+ finished = true;
+ else if(ret!=Z_OK)
+ throw zlib_error("deflate", ret);
+ }
+
+ // Write compressed data into the underlying object
+ unsigned len = 0;
+ if(priv->stream.next_out>out_buffer)
+ len = below.write(reinterpret_cast<char *>(out_buffer), priv->stream.next_out-out_buffer);
+ if(len>0)
+ {
+ if(len<static_cast<unsigned>(priv->stream.next_out-out_buffer))
+ copy(out_buffer+len, priv->stream.next_out, out_buffer);
+ priv->stream.avail_out += len;
+ priv->stream.next_out -= len;
+ }
+ else if(!can_deflate)
+ // We weren't able to do anything
+ return false;
+
+ return !finished;
+#else
+ (void)flush_mode;
+ return false;
+#endif
+}
+
+unsigned ZlibCompressed::do_read(char *data, unsigned size)
+{
+ check_access(M_READ);
+
+ unsigned processed = 0;
+#ifdef WITH_ZLIB
+ while(processed<size)
+ {
+ if(priv->stream.next_out>out_buffer)
+ {
+ // We have some pending output, give it out first
+ unsigned len = min<unsigned>(priv->stream.next_out-out_buffer, size-processed);
+
+ copy(out_buffer, out_buffer+len, data+processed);
+ processed += len;
+
+ if(len<static_cast<unsigned>(priv->stream.next_out-out_buffer))
+ copy(out_buffer+len, priv->stream.next_out, out_buffer);
+ priv->stream.next_out -= len;
+ priv->stream.avail_out += len;
+
+ continue;
+ }
+
+ bool need_more_input = !priv->stream.avail_in;
+ if(priv->stream.avail_in)
+ {
+ int ret = inflate(&priv->stream, Z_NO_FLUSH);
+ if(ret==Z_STREAM_END)
+ set_eof();
+ else if(ret!=Z_OK)
+ throw zlib_error("inflate", ret);
+ need_more_input = (priv->stream.next_out==out_buffer);
+ }
+
+ if(need_more_input)
+ {
+ if(eof_flag)
+ break;
+
+ if(priv->stream.next_in>in_buffer)
+ copy(priv->stream.next_in, priv->stream.next_in+priv->stream.avail_in, in_buffer);
+ priv->stream.next_in = in_buffer;
+
+ unsigned len = below.read(reinterpret_cast<char *>(priv->stream.next_in), in_buffer+buffer_size-priv->stream.next_in);
+ priv->stream.avail_in += len;
+ if(!len && below.eof())
+ set_eof();
+ }
+ }
+#else
+ (void)data;
+ (void)size;
+#endif
+
+ return processed;
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+#ifndef MSP_IO_ZLIBCOMPRESSED_H_
+#define MSP_IO_ZLIBCOMPRESSED_H_
+
+#include <stdexcept>
+#include <string>
+#include "base.h"
+
+namespace Msp {
+namespace IO {
+
+class zlib_error: public std::runtime_error
+{
+private:
+ int code_;
+
+public:
+ zlib_error(const std::string &, int);
+ ~zlib_error() throw() { }
+
+ int code() const throw() { return code_; }
+};
+
+/**
+Compresses or decompresses data with zlib. This class is a filter that
+operates on top of another I/O object.
+
+To ensure proper termination of the compressed data stream, the ZlibCompressed
+object must be destroyed before the underlying object is closed.
+*/
+class ZlibCompressed: public Base
+{
+private:
+ struct Private;
+
+ Base &below;
+ unsigned buffer_size;
+ unsigned char *in_buffer;
+ unsigned char *out_buffer;
+ Private *priv;
+
+public:
+ /** Creates a zlib de/compression object. The underlying object must be
+ open for reading or writing, not both. The level parameter determines
+ compression quality, ranging from 1 (fastest) to 9 (best compression). */
+ ZlibCompressed(Base &, unsigned level = 9);
+
+ virtual ~ZlibCompressed();
+
+ void flush();
+
+protected:
+ virtual unsigned do_write(const char *, unsigned);
+
+private:
+ /** Compresses data and writes it to the underlying object. Returns true if
+ progress was made, false otherwise. flush_mode can be any of zlib's flush
+ modes. If it is Z_FINISH, false is also returned when the stream has been
+ terminated. */
+ bool compress_data(int flush_mode);
+
+public:
+ virtual unsigned do_read(char *, unsigned);
+};
+
+} // namespace IO
+} // namespace Msp
+
+#endif