summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--includes/ZHandle.h37
-rw-r--r--src/ZHandle.cc140
3 files changed, 178 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index 28207c1..8e9091f 100644
--- a/Makefile
+++ b/Makefile
@@ -129,6 +129,7 @@ Output.cc \
Socket.cc \
Buffer.cc \
BStream.cc \
+ZHandle.cc \
\
Task.cc \
TaskMan.cc \
diff --git a/includes/ZHandle.h b/includes/ZHandle.h
new file mode 100644
index 0000000..63da59a
--- /dev/null
+++ b/includes/ZHandle.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <zlib.h>
+#include <Handle.h>
+
+namespace Balau {
+
+class ZStream : public Handle {
+ public:
+ typedef enum {
+ ZLIB,
+ GZIP,
+ RAW,
+ } header_t;
+ ZStream(const IO<Handle> & h, int level = Z_BEST_COMPRESSION, header_t header = ZLIB);
+ virtual void close() throw (GeneralException);
+ virtual bool isClosed();
+ virtual bool isEOF();
+ virtual bool canRead();
+ virtual bool canWrite();
+ virtual const char * getName();
+ virtual ssize_t read(void * buf, size_t count) throw (GeneralException);
+ virtual ssize_t write(const void * buf, size_t count) throw (GeneralException);
+ virtual off_t getSize();
+ void detach() { m_detached = true; }
+ void flush() { doFlush(false); }
+ private:
+ void finish() { doFlush(true); }
+ void doFlush(bool finish);
+ IO<Handle> m_h;
+ z_stream m_zin, m_zout;
+ bool m_detached, m_closed, m_eof;
+ String m_name;
+ uint8_t * m_in;
+};
+
+};
diff --git a/src/ZHandle.cc b/src/ZHandle.cc
new file mode 100644
index 0000000..7e1d139
--- /dev/null
+++ b/src/ZHandle.cc
@@ -0,0 +1,140 @@
+#include "ZHandle.h"
+#include "Task.h"
+
+Balau::ZStream::ZStream(const IO<Handle> & h, int level, header_t header) : m_h(h), m_detached(false), m_closed(false), m_eof(false), m_in(NULL) {
+ m_zin.zalloc = m_zout.zalloc = NULL;
+ m_zin.zfree = m_zout.zfree = NULL;
+ m_zin.opaque = m_zout.opaque = NULL;
+ m_zin.next_in = NULL;
+ m_zin.avail_in = 0;
+ int window = 0;
+ switch (header) {
+ case ZLIB: window = 15; break;
+ case GZIP: window = 31; break;
+ case RAW: window = -15; break;
+ }
+ int r;
+ r = inflateInit2(&m_zin, window);
+ EAssert(r == Z_OK, "inflateInit2 returned %i", r);
+ r = deflateInit2(&m_zout, level, Z_DEFLATED, window, 9, Z_DEFAULT_STRATEGY);
+ EAssert(r == Z_OK, "deflateInit2 returned %i", r);
+ m_name.set("ZStream(%s)", m_h->getName());
+}
+
+void Balau::ZStream::close() throw (GeneralException) {
+ if (m_in) {
+ free(m_in);
+ m_in = NULL;
+ }
+ if (m_h->canWrite())
+ finish();
+ inflateEnd(&m_zin);
+ deflateEnd(&m_zout);
+ if (!m_detached)
+ m_h->close();
+ m_closed = true;
+}
+
+bool Balau::ZStream::isClosed() {
+ return m_closed;
+}
+
+bool Balau::ZStream::isEOF() {
+ if (m_closed || m_eof)
+ return true;
+ return m_h->isEOF();
+}
+
+bool Balau::ZStream::canRead() {
+ return m_h->canRead();
+}
+
+bool Balau::ZStream::canWrite() {
+ return m_h->canWrite();
+}
+
+const char * Balau::ZStream::getName() {
+ return m_name.to_charp();
+}
+
+static const int BLOCK_SIZE = 1024;
+
+ssize_t Balau::ZStream::read(void * buf, size_t count) throw (GeneralException) {
+ if (m_closed || m_eof)
+ return 0;
+
+ AAssert(m_h->canRead(), "Can't call ZStream::read on a non-readable handle.");
+
+ size_t readTotal = 0;
+ m_zin.next_out = (Bytef *) buf;
+ m_zin.avail_out = count;
+ if (!m_in) {
+ m_zin.next_in = m_in = (uint8_t *) malloc(BLOCK_SIZE);
+ m_zin.avail_in = 0;
+ }
+ while ((count != 0) && !m_h->isClosed() && !m_h->isEOF()) {
+ if (m_zin.avail_in == 0) {
+ m_zin.next_in = m_in;
+ size_t r = m_h->read(m_in, BLOCK_SIZE);
+ if (r <= 0)
+ return readTotal;
+ }
+ Task::yield(NULL);
+ int r = inflate(&m_zin, Z_SYNC_FLUSH);
+ size_t didRead = count - m_zin.avail_out;
+ readTotal += didRead;
+ count -= didRead;
+ if (r == Z_STREAM_END) {
+ m_eof = true;
+ break;
+ }
+ EAssert(r == Z_OK, "inflate() didn't return Z_OK but %i", r);
+ }
+ return readTotal;
+}
+
+ssize_t Balau::ZStream::write(const void * buf, size_t count) throw (GeneralException) {
+ if (m_closed || m_eof)
+ return 0;
+
+ AAssert(m_h->canWrite(), "Can't call ZStream::write on a non-writable handle.");
+
+ size_t wroteTotal = 0;
+ m_zout.next_in = (Bytef *) const_cast<void *>(buf);
+ m_zout.avail_in = count;
+ void * obuf = alloca(BLOCK_SIZE);
+ while ((count != 0) && !m_h->isClosed() && !m_h->isEOF()) {
+ m_zout.next_out = (Bytef *) obuf;
+ m_zout.avail_out = BLOCK_SIZE;
+ int r = deflate(&m_zout, Z_NO_FLUSH);
+ EAssert(r == Z_OK, "deflate() didn't return Z_OK but %i", r);
+ Task::yield(NULL);
+ size_t compressed = BLOCK_SIZE - m_zout.avail_out;
+ if (compressed) {
+ size_t w = m_h->write(obuf, compressed);
+ if (w <= 0)
+ return wroteTotal;
+ }
+ wroteTotal += compressed;
+ count -= compressed;
+ }
+ return wroteTotal;
+}
+
+void Balau::ZStream::doFlush(bool finish) {
+ void * buf = alloca(BLOCK_SIZE);
+ m_zout.next_in = NULL;
+ m_zout.avail_in = 0;
+ int r;
+ do {
+ m_zout.next_out = (Bytef *) buf;
+ m_zout.avail_out = BLOCK_SIZE;
+ r = deflate(&m_zout, finish ? Z_FINISH : Z_SYNC_FLUSH);
+ size_t compressed = BLOCK_SIZE - m_zout.avail_out;
+ if (compressed) {
+ size_t w = m_h->write(buf, compressed);
+ if (w <= 0)
+ return;
+ }
+ } while (r == Z_OK && finish);
+}