From: David Reiss Date: Mon, 17 Sep 2007 23:20:38 +0000 (+0000) Subject: Thrift: Re-committing zlib. X-Git-Tag: 0.2.0~1203 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=faebedd356985c3b2df5e39e1cf5b152c29fc179;p=common%2Fthrift.git Thrift: Re-committing zlib. Summary: Same as the last (reverted) zlib patch, but this time with way more awesome support for building with no zlib headers installed. Reviewed By: mcslee Test Plan: - Did lots of really pathological stuff in my VMware. - On devrs004: ./bootstrap.sh && ./configure && make && make install DESTDIR=/tmp/tzinst && echo "Yay" Revert Plan: ok git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665269 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/lib/cpp/Makefile.am b/lib/cpp/Makefile.am index b80bcc38..7b1f1b01 100644 --- a/lib/cpp/Makefile.am +++ b/lib/cpp/Makefile.am @@ -4,11 +4,14 @@ lib_LTLIBRARIES = libthrift.la if AMX_HAVE_LIBEVENT lib_LTLIBRARIES += libthriftnb.la endif +if AMX_HAVE_ZLIB +lib_LTLIBRARIES += libthriftz.la +endif common_cxxflags = -Wall -Isrc $(BOOST_CPPFLAGS) common_ldflags = -Wall $(BOOST_LDFLAGS) -# Define the source file for the module +# Define the source files for the module libthrift_la_SOURCES = src/Thrift.cpp \ src/reflection_limited_types.cpp \ @@ -32,11 +35,21 @@ libthrift_la_SOURCES = src/Thrift.cpp \ src/server/TThreadedServer.cpp \ src/processor/PeekProcessor.cpp - libthriftnb_la_SOURCES = src/server/TNonblockingServer.cpp +libthriftz_la_SOURCES = src/transport/TZlibTransport.cpp + + +# Flags for the various libraries + libthrift_la_CXXFLAGS = $(common_cxxflags) -libthriftnb_la_CXXFLAGS = $(common_cxxflags) $(LIBEVENT_CPPFLAGS) + +libthriftnb_la_CXXFLAGS = $(common_cxxflags) +libthriftnb_la_CPPFLAGS = $(LIBEVENT_CPPFLAGS) + +libthriftz_la_CXXFLAGS = $(common_cxxflags) +libthriftz_la_CPPFLAGS = $(ZLIB_CPPFLAGS) + include_thriftdir = $(includedir)/thrift include_thrift_HEADERS = \ @@ -76,7 +89,8 @@ include_transport_HEADERS = \ src/transport/TSocketPool.h \ src/transport/TTransport.h \ src/transport/TTransportException.h \ - src/transport/TTransportUtils.h + src/transport/TTransportUtils.h \ + src/transport/TZlibTransport.h include_serverdir = $(include_thriftdir)/server include_server_HEADERS = \ diff --git a/lib/cpp/aclocal/ax_lib_event.m4 b/lib/cpp/aclocal/ax_lib_event.m4 index 14f115e4..9266df4f 100644 --- a/lib/cpp/aclocal/ax_lib_event.m4 +++ b/lib/cpp/aclocal/ax_lib_event.m4 @@ -1,6 +1,6 @@ dnl @synopsis AX_LIB_EVENT([MINIMUM-VERSION]) dnl -dnl Test for the libz library of a particular version (or newer). +dnl Test for the libevent library of a particular version (or newer). dnl dnl If no path to the installed libevent is given, the macro will first try dnl using no -I or -L flags, then searches under /usr, /usr/local, /opt, diff --git a/lib/cpp/aclocal/ax_lib_zlib.m4 b/lib/cpp/aclocal/ax_lib_zlib.m4 new file mode 100644 index 00000000..40e96df9 --- /dev/null +++ b/lib/cpp/aclocal/ax_lib_zlib.m4 @@ -0,0 +1,169 @@ +dnl @synopsis AX_LIB_ZLIB([MINIMUM-VERSION]) +dnl +dnl Test for the libz library of a particular version (or newer). +dnl +dnl If no path to the installed zlib is given, the macro will first try +dnl using no -I or -L flags, then searches under /usr, /usr/local, /opt, +dnl and /opt/zlib. +dnl If these all fail, it will try the $ZLIB_ROOT environment variable. +dnl +dnl This macro calls: +dnl AC_SUBST(ZLIB_CPPFLAGS) +dnl AC_SUBST(ZLIB_LDFLAGS) +dnl AC_SUBST(ZLIB_LIBS) +dnl +dnl And (if zlib is found): +dnl AC_DEFINE(HAVE_ZLIB) +dnl +dnl It also leaves the shell variables "success" and "ax_have_zlib" +dnl set to "yes" or "no". +dnl +dnl NOTE: This macro does not currently work for cross-compiling, +dnl but it can be easily modified to allow it. (grep "cross"). +dnl +dnl @category InstalledPackages +dnl @category C +dnl @author David Reiss +dnl @version 2007-09-12 +dnl @license AllPermissive + +dnl Input: ax_zlib_path, WANT_ZLIB_VERSION +dnl Output: success=yes/no +AC_DEFUN([AX_LIB_ZLIB_DO_CHECK], + [ + # Save our flags. + CPPFLAGS_SAVED="$CPPFLAGS" + LDFLAGS_SAVED="$LDFLAGS" + LIBS_SAVED="$LIBS" + LD_LIBRARY_PATH_SAVED="$LD_LIBRARY_PATH" + + # Set our flags if we are checking a specific directory. + if test -n "$ax_zlib_path" ; then + ZLIB_CPPFLAGS="-I$ax_zlib_path/include" + ZLIB_LDFLAGS="-L$ax_zlib_path/lib" + LD_LIBRARY_PATH="$ax_zlib_path/lib:$LD_LIBRARY_PATH" + else + ZLIB_CPPFLAGS="" + ZLIB_LDFLAGS="" + fi + + # Required flag for zlib. + ZLIB_LIBS="-lz" + + # Prepare the environment for compilation. + CPPFLAGS="$CPPFLAGS $ZLIB_CPPFLAGS" + LDFLAGS="$LDFLAGS $ZLIB_LDFLAGS" + LIBS="$LIBS $ZLIB_LIBS" + export CPPFLAGS + export LDFLAGS + export LIBS + export LD_LIBRARY_PATH + + success=no + + # Compile, link, and run the program. This checks: + # - zlib.h is available for including. + # - zlibVersion() is available for linking. + # - ZLIB_VERNUM is greater than or equal to the desired version. + # - ZLIB_VERSION (defined in zlib.h) matches zlibVersion() + # (defined in the library). + AC_LANG_PUSH([C]) + dnl This can be changed to AC_LINK_IFELSE if you are cross-compiling. + AC_RUN_IFELSE([AC_LANG_PROGRAM([[ + #include + #if ZLIB_VERNUM >= 0x$WANT_ZLIB_VERSION + #else + # error zlib is too old + #endif + ]], [[ + const char* lib_version = zlibVersion(); + const char* hdr_version = ZLIB_VERSION; + for (;;) { + if (*lib_version != *hdr_version) { + /* If this happens, your zlib header doesn't match your zlib */ + /* library. That is really bad. */ + return 1; + } + if (*lib_version == '\0') { + break; + } + lib_version++; + hdr_version++; + } + return 0; + ]])], [ + success=yes + ]) + AC_LANG_POP([C]) + + # Restore flags. + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + LIBS="$LIBS_SAVED" + LD_LIBRARY_PATH="$LD_LIBRARY_PATH_SAVED" + ]) + + +AC_DEFUN([AX_LIB_ZLIB], + [ + + dnl Allow search path to be overridden on the command line. + AC_ARG_WITH([zlib], + AS_HELP_STRING([--with-zlib@<:@=DIR@:>@], [use zlib (default is yes) - it is possible to specify an alternate root directory for zlib]), + [ + if test "$withval" = "xno"; then + want_zlib="no" + elif test "$withval" = "xyes"; then + want_zlib="yes" + ax_zlib_path="" + else + want_zlib="yes" + ax_zlib_path="$withval" + fi + ], + [want_zlib="yes" ; ax_zlib_path="" ]) + + + if test "$want_zlib" = "yes"; then + # Parse out the version. + zlib_version_req=ifelse([$1], ,1.2.3,$1) + zlib_version_req_major=`expr $zlib_version_req : '\([[0-9]]*\)'` + zlib_version_req_minor=`expr $zlib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` + zlib_version_req_patch=`expr $zlib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` + if test -z "$zlib_version_req_patch" ; then + zlib_version_req_patch="0" + fi + WANT_ZLIB_VERSION=`expr $zlib_version_req_major \* 1000 \+ $zlib_version_req_minor \* 100 \+ $zlib_version_req_patch \* 10` + + AC_MSG_CHECKING(for zlib >= $zlib_version_req) + + # Run tests. + if test -n "$ax_zlib_path"; then + AX_LIB_ZLIB_DO_CHECK + else + for ax_zlib_path in "" /usr /usr/local /opt /opt/zlib "$ZLIB_ROOT" ; do + AX_LIB_ZLIB_DO_CHECK + if test "$success" = "yes"; then + break; + fi + done + fi + + if test "$success" != "yes" ; then + AC_MSG_RESULT(no) + ZLIB_CPPFLAGS="" + ZLIB_LDFLAGS="" + ZLIB_LIBS="" + else + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_ZLIB,,[define if zlib is available]) + fi + + ax_have_zlib="$success" + + AC_SUBST(ZLIB_CPPFLAGS) + AC_SUBST(ZLIB_LDFLAGS) + AC_SUBST(ZLIB_LIBS) + fi + + ]) diff --git a/lib/cpp/configure.ac b/lib/cpp/configure.ac index 5e336173..d9a66daf 100644 --- a/lib/cpp/configure.ac +++ b/lib/cpp/configure.ac @@ -71,6 +71,9 @@ AX_BOOST_BASE([1.33.1]) AX_LIB_EVENT([1.0]) AM_CONDITIONAL([AMX_HAVE_LIBEVENT], [test "$success" = "yes"]) +AX_LIB_ZLIB([1.2.3]) +AM_CONDITIONAL([AMX_HAVE_ZLIB], [test "$success" = "yes"]) + AC_CHECK_LIB(pthread, pthread_create) AC_CHECK_LIB(rt, sched_get_priority_min) diff --git a/lib/cpp/src/Thrift.h b/lib/cpp/src/Thrift.h index 0b13bae2..ad8d46c3 100644 --- a/lib/cpp/src/Thrift.h +++ b/lib/cpp/src/Thrift.h @@ -26,8 +26,8 @@ namespace facebook { namespace thrift { -class TOutput{ -public: +class TOutput { + public: TOutput() : f_(&perrorTimeWrapper) {} inline void setOutputFunction(void (*function)(const char *)){ @@ -47,7 +47,7 @@ public: fprintf(stderr, "%s ", dbgtime); perror(msg); } -private: + private: void (*f_)(const char *); }; @@ -58,7 +58,7 @@ namespace protocol { } class TException : public std::exception { -public: + public: TException() {} TException(const std::string& message) : @@ -74,13 +74,13 @@ public: } } -protected: + protected: std::string message_; }; class TApplicationException : public TException { -public: + public: /** * Error codes for the various types of exceptions. @@ -134,7 +134,7 @@ public: uint32_t read(protocol::TProtocol* iprot); uint32_t write(protocol::TProtocol* oprot) const; -protected: + protected: /** * Error code */ diff --git a/lib/cpp/src/transport/TTransportException.h b/lib/cpp/src/transport/TTransportException.h index bb8b8a9f..9b75826e 100644 --- a/lib/cpp/src/transport/TTransportException.h +++ b/lib/cpp/src/transport/TTransportException.h @@ -34,7 +34,9 @@ class TTransportException : public facebook::thrift::TException { TIMED_OUT = 3, END_OF_FILE = 4, INTERRUPTED = 5, - BAD_ARGS = 6 + BAD_ARGS = 6, + CORRUPTED_DATA = 7, + INTERNAL_ERROR = 8, }; TTransportException() : diff --git a/lib/cpp/src/transport/TZlibTransport.cpp b/lib/cpp/src/transport/TZlibTransport.cpp new file mode 100644 index 00000000..59e3a364 --- /dev/null +++ b/lib/cpp/src/transport/TZlibTransport.cpp @@ -0,0 +1,285 @@ +// Copyright (c) 2006- Facebook +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/ + +#include +#include +#include +#include + +using std::string; + +namespace facebook { namespace thrift { namespace transport { + +// Don't call this outside of the constructor. +void TZlibTransport::initZlib() { + int rv; + bool r_init = false; + try { + rstream_ = new z_stream; + wstream_ = new z_stream; + + rstream_->zalloc = Z_NULL; + wstream_->zalloc = Z_NULL; + rstream_->zfree = Z_NULL; + wstream_->zfree = Z_NULL; + rstream_->opaque = Z_NULL; + wstream_->opaque = Z_NULL; + + rstream_->next_in = crbuf_; + wstream_->next_in = uwbuf_; + rstream_->next_out = urbuf_; + wstream_->next_out = cwbuf_; + rstream_->avail_in = 0; + wstream_->avail_in = 0; + rstream_->avail_out = urbuf_size_; + wstream_->avail_out = cwbuf_size_; + + rv = inflateInit(rstream_); + checkZlibRv(rv, rstream_->msg); + + // Have to set this flag so we know whether to de-initialize. + r_init = true; + + rv = deflateInit(wstream_, Z_DEFAULT_COMPRESSION); + checkZlibRv(rv, wstream_->msg); + } + + catch (...) { + if (r_init) { + rv = inflateEnd(rstream_); + checkZlibRvNothrow(rv, rstream_->msg); + } + // There is no way we can get here if wstream_ was initialized. + + throw; + } +} + +inline void TZlibTransport::checkZlibRv(int status, const char* message) { + if (status != Z_OK) { + throw TZlibTransportException(status, message); + } +} + +inline void TZlibTransport::checkZlibRvNothrow(int status, const char* message) { + if (status != Z_OK) { + string output = "TZlibTransport: zlib failure in destructor: " + + TZlibTransportException::errorMessage(status, message); + GlobalOutput(output.c_str()); + } +} + +TZlibTransport::~TZlibTransport() { + int rv; + rv = inflateEnd(rstream_); + checkZlibRvNothrow(rv, rstream_->msg); + rv = deflateEnd(wstream_); + checkZlibRvNothrow(rv, wstream_->msg); + + delete[] urbuf_; + delete[] crbuf_; + delete[] uwbuf_; + delete[] cwbuf_; + delete rstream_; + delete wstream_; +} + +bool TZlibTransport::isOpen() { + return (readAvail() > 0) || transport_->isOpen(); +} + +// READING STRATEGY +// +// We have two buffers for reading: one containing the compressed data (crbuf_) +// and one containing the uncompressed data (urbuf_). When read is called, +// we repeat the following steps until we have satisfied the request: +// - Copy data from urbuf_ into the caller's buffer. +// - If we had enough, return. +// - If urbuf_ is empty, read some data into it from the underlying transport. +// - Inflate data from crbuf_ into urbuf_. +// +// In standalone objects, we set input_ended_ to true when inflate returns +// Z_STREAM_END. This allows to make sure that a checksum was verified. + +inline int TZlibTransport::readAvail() { + return urbuf_size_ - rstream_->avail_out - urpos_; +} + +uint32_t TZlibTransport::read(uint8_t* buf, uint32_t len) { + int need = len; + + // TODO(dreiss): Skip urbuf on big reads. + + while (true) { + // Copy out whatever we have available, then give them the min of + // what we have and what they want, then advance indices. + int give = std::min(readAvail(), need); + memcpy(buf, urbuf_ + urpos_, give); + need -= give; + buf += give; + urpos_ += give; + + // If they were satisfied, we are done. + if (need == 0) { + return len; + } + + // If we get to this point, we need to get some more data. + + // If zlib has reported the end of a stream, we can't really do any more. + if (input_ended_) { + return len - need; + } + + // The uncompressed read buffer is empty, so reset the stream fields. + rstream_->next_out = urbuf_; + rstream_->avail_out = urbuf_size_; + urpos_ = 0; + + // If we don't have any more compressed data available, + // read some from the underlying transport. + if (rstream_->avail_in == 0) { + uint32_t got = transport_->read(crbuf_, crbuf_size_); + if (got == 0) { + return len - need; + } + rstream_->next_in = crbuf_; + rstream_->avail_in = got; + } + + // We have some compressed data now. Uncompress it. + int zlib_rv = inflate(rstream_, Z_SYNC_FLUSH); + + if (zlib_rv == Z_STREAM_END) { + if (standalone_) { + input_ended_ = true; + } + } else { + checkZlibRv(zlib_rv, rstream_->msg); + } + + // Okay. The read buffer should have whatever we can give it now. + // Loop back to the start and try to give some more. + } +} + + +// WRITING STRATEGY +// +// We buffer up small writes before sending them to zlib, so our logic is: +// - Is the write big? +// - Send the buffer to zlib. +// - Send this data to zlib. +// - Is the write small? +// - Is there insufficient space in the buffer for it? +// - Send the buffer to zlib. +// - Copy the data to the buffer. +// +// We have two buffers for writing also: the uncompressed buffer (mentioned +// above) and the compressed buffer. When sending data to zlib we loop over +// the following until the source (uncompressed buffer or big write) is empty: +// - Is there no more space in the compressed buffer? +// - Write the compressed buffer to the underlying transport. +// - Deflate from the source into the compressed buffer. + +void TZlibTransport::write(const uint8_t* buf, uint32_t len) { + // zlib's "deflate" function has enough logic in it that I think + // we're better off (performance-wise) buffering up small writes. + if ((int)len > MIN_DIRECT_DEFLATE_SIZE) { + flushToZlib(uwbuf_, uwpos_); + uwpos_ = 0; + flushToZlib(buf, len); + } else if (len > 0) { + if (uwbuf_size_ - uwpos_ < (int)len) { + flushToZlib(uwbuf_, uwpos_); + uwpos_ = 0; + } + memcpy(uwbuf_ + uwpos_, buf, len); + uwpos_ += len; + } +} + +void TZlibTransport::flush() { + flushToZlib(uwbuf_, uwpos_, true); + assert((int)wstream_->avail_out != cwbuf_size_); + transport_->write(cwbuf_, cwbuf_size_ - wstream_->avail_out); + transport_->flush(); +} + +void TZlibTransport::flushToZlib(const uint8_t* buf, int len, bool finish) { + int flush = (finish ? Z_FINISH : Z_NO_FLUSH); + + wstream_->next_in = const_cast(buf); + wstream_->avail_in = len; + + while (wstream_->avail_in > 0 || finish) { + // If our ouput buffer is full, flush to the underlying transport. + if (wstream_->avail_out == 0) { + transport_->write(cwbuf_, cwbuf_size_); + wstream_->next_out = cwbuf_; + wstream_->avail_out = cwbuf_size_; + } + + int zlib_rv = deflate(wstream_, flush); + + if (finish && zlib_rv == Z_STREAM_END) { + assert(wstream_->avail_in == 0); + break; + } + + checkZlibRv(zlib_rv, wstream_->msg); + } +} + +bool TZlibTransport::borrow(uint8_t* buf, uint32_t len) { + // Don't try to be clever with shifting buffers. + // If we have enough data, give it, otherwise + // let the protcol use its slow path. + if (readAvail() >= (int)len) { + memcpy(buf, urbuf_ + urpos_, len); + return true; + } + return false; +} + +void TZlibTransport::consume(uint32_t len) { + if (readAvail() >= (int)len) { + urpos_ += len; + } else { + throw TTransportException(TTransportException::BAD_ARGS, + "consume did not follow a borrow."); + } +} + +void TZlibTransport::verifyChecksum() { + if (!standalone_) { + throw TTransportException( + TTransportException::BAD_ARGS, + "TZLibTransport can only verify checksums for standalone objects."); + } + + if (!input_ended_) { + // This should only be called when reading is complete, + // but it's possible that the whole checksum has not been fed to zlib yet. + // We try to read an extra byte here to force zlib to finish the stream. + // It might not always be easy to "unread" this byte, + // but we throw an exception if we get it, which is not really + // a recoverable error, so it doesn't matter. + uint8_t buf[1]; + uint32_t got = this->read(buf, sizeof(buf)); + if (got || !input_ended_) { + throw TTransportException( + TTransportException::CORRUPTED_DATA, + "Zlib stream not complete."); + } + } + + // If the checksum had been bad, we would have gotten an error while + // inflating. +} + + +}}} // facebook::thrift::transport diff --git a/lib/cpp/src/transport/TZlibTransport.h b/lib/cpp/src/transport/TZlibTransport.h new file mode 100644 index 00000000..cc2522d9 --- /dev/null +++ b/lib/cpp/src/transport/TZlibTransport.h @@ -0,0 +1,207 @@ +// Copyright (c) 2006- Facebook +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/ + +#ifndef _THRIFT_TRANSPORT_TZLIBTRANSPORT_H_ +#define _THRIFT_TRANSPORT_TZLIBTRANSPORT_H_ 1 + +#include +#include + +struct z_stream_s; + +namespace facebook { namespace thrift { namespace transport { + +class TZlibTransportException : public TTransportException { + public: + TZlibTransportException(int status, const char* msg) : + TTransportException(TTransportException::INTERNAL_ERROR, + errorMessage(status, msg)), + zlib_status_(status), + zlib_msg_(msg == NULL ? "(null)" : msg) {} + + virtual ~TZlibTransportException() throw() {} + + int getZlibStatus() { return zlib_status_; } + std::string getZlibMessage() { return zlib_msg_; } + + static std::string errorMessage(int status, const char* msg) { + std::string rv = "zlib error: "; + if (msg) { + rv += msg; + } else { + rv += "(no message)"; + } + rv += " (status = "; + rv += boost::lexical_cast(status); + rv += ")"; + return rv; + } + + int zlib_status_; + std::string zlib_msg_; +}; + +/** + * This transport uses zlib's compressed format on the "far" side. + * + * There are two kinds of TZlibTransport objects: + * - Standalone objects are used to encode self-contained chunks of data + * (like structures). They include checksums. + * - Non-standalone transports are used for RPC. They are not implemented yet. + * + * TODO(dreiss): Don't do an extra copy of the compressed data if + * the underlying transport is TBuffered or TMemory. + * + * @author David Reiss + */ +class TZlibTransport : public TTransport { + public: + + /** + * @param transport The transport to read compressed data from + * and write compressed data to. + * @param use_for_rpc True if this object will be used for RPC, + * false if this is a standalone object. + * @param urbuf_size Uncompressed buffer size for reading. + * @param crbuf_size Compressed buffer size for reading. + * @param uwbuf_size Uncompressed buffer size for writing. + * @param cwbuf_size Compressed buffer size for writing. + * + * TODO(dreiss): Write a constructor that isn't a pain. + */ + TZlibTransport(boost::shared_ptr transport, + bool use_for_rpc, + int urbuf_size = DEFAULT_URBUF_SIZE, + int crbuf_size = DEFAULT_CRBUF_SIZE, + int uwbuf_size = DEFAULT_UWBUF_SIZE, + int cwbuf_size = DEFAULT_CWBUF_SIZE) : + transport_(transport), + standalone_(!use_for_rpc), + urpos_(0), + uwpos_(0), + input_ended_(false), + output_flushed_(false), + urbuf_size_(urbuf_size), + crbuf_size_(crbuf_size), + uwbuf_size_(uwbuf_size), + cwbuf_size_(cwbuf_size), + urbuf_(NULL), + crbuf_(NULL), + uwbuf_(NULL), + cwbuf_(NULL), + rstream_(NULL), + wstream_(NULL) + { + + if (!standalone_) { + throw TTransportException( + TTransportException::BAD_ARGS, + "TZLibTransport has not been tested for RPC."); + } + + if (uwbuf_size_ < MIN_DIRECT_DEFLATE_SIZE) { + // Have to copy this into a local because of a linking issue. + int minimum = MIN_DIRECT_DEFLATE_SIZE; + throw TTransportException( + TTransportException::BAD_ARGS, + "TZLibTransport: uncompressed write buffer must be at least" + + boost::lexical_cast(minimum) + "."); + } + + try { + urbuf_ = new uint8_t[urbuf_size]; + crbuf_ = new uint8_t[crbuf_size]; + uwbuf_ = new uint8_t[uwbuf_size]; + cwbuf_ = new uint8_t[cwbuf_size]; + + // Don't call this outside of the constructor. + initZlib(); + + } catch (...) { + delete[] urbuf_; + delete[] crbuf_; + delete[] uwbuf_; + delete[] cwbuf_; + throw; + } + } + + // Don't call this outside of the constructor. + void initZlib(); + + ~TZlibTransport(); + + bool isOpen(); + + void open() { + transport_->open(); + } + + void close() { + transport_->close(); + } + + uint32_t read(uint8_t* buf, uint32_t len); + + void write(const uint8_t* buf, uint32_t len); + + void flush(); + + bool borrow(uint8_t* buf, uint32_t len); + + void consume(uint32_t len); + + void verifyChecksum(); + + /** + * TODO(someone_smart): Choose smart defaults. + */ + static const int DEFAULT_URBUF_SIZE = 128; + static const int DEFAULT_CRBUF_SIZE = 1024; + static const int DEFAULT_UWBUF_SIZE = 128; + static const int DEFAULT_CWBUF_SIZE = 1024; + + protected: + + inline void checkZlibRv(int status, const char* msg); + inline void checkZlibRvNothrow(int status, const char* msg); + inline int readAvail(); + void flushToZlib(const uint8_t* buf, int len, bool finish = false); + + // Writes smaller than this are buffered up. + // Larger (or equal) writes are dumped straight to zlib. + static const int MIN_DIRECT_DEFLATE_SIZE = 32; + + boost::shared_ptr transport_; + bool standalone_; + + int urpos_; + int uwpos_; + + /// True iff zlib has reached the end of a stream. + /// This is only ever true in standalone protcol objects. + bool input_ended_; + /// True iff we have flushed the output stream. + /// This is only ever true in standalone protcol objects. + bool output_flushed_; + + int urbuf_size_; + int crbuf_size_; + int uwbuf_size_; + int cwbuf_size_; + + uint8_t* urbuf_; + uint8_t* crbuf_; + uint8_t* uwbuf_; + uint8_t* cwbuf_; + + struct z_stream_s* rstream_; + struct z_stream_s* wstream_; +}; + +}}} // facebook::thrift::transport + +#endif // #ifndef _THRIFT_TRANSPORT_TZLIBTRANSPORT_H_ diff --git a/test/DebugProtoTest.thrift b/test/DebugProtoTest.thrift index 090418c7..e669fb3d 100644 --- a/test/DebugProtoTest.thrift +++ b/test/DebugProtoTest.thrift @@ -73,3 +73,12 @@ service PartiallyReflectable { OneOfEach refl1(1: list arg1), OneOfEach refl2(2: list arg1, 1: Bonk arg2); } + +// The only purpose of this thing is to increase the size of the generated code +// so that ZlibTest has more highly compressible data to play with. +struct BlowUp { + 1: map,set>> b1; + 2: map,set>> b2; + 3: map,set>> b3; + 4: map,set>> b4; +} diff --git a/test/ZlibTest.cpp b/test/ZlibTest.cpp new file mode 100644 index 00000000..1782c586 --- /dev/null +++ b/test/ZlibTest.cpp @@ -0,0 +1,291 @@ +/* +thrift -cpp DebugProtoTest.thrift +g++ -Wall -g -I../lib/cpp/src -I/usr/local/include/boost-1_33_1 \ + ZlibTest.cpp \ + ../lib/cpp/.libs/libthriftz.a ../lib/cpp/.libs/libthrift.a \ + -lz -o ZlibTest +./ZlibTest +*/ + +#include +#include +#include +#include +#include +#include + + +// Distributions of reads and writes meant to approximate a real load, +// mixing up small and large while also hitting various boundary conditions. +// Generated by Python: int(random.lognormvariate(2.5, 1)) +unsigned int dist[][5000] = { + { 1<<15 }, + + { + 5,13,9,1,8,9,11,13,18,48,24,13,21,13,5,11,35,2,4,20,17,72,27,14,15,4,7,26, + 12,1,14,9,2,16,29,41,7,24,4,27,14,4,1,4,25,3,6,34,10,8,50,2,14,13,55,29,3, + 43,53,49,14,4,10,32,27,48,1,3,1,11,5,17,16,51,17,30,15,11,9,2,2,11,52,12,2, + 13,94,1,19,1,38,2,8,43,8,33,7,30,8,17,22,2,15,14,12,34,2,12,6,37,29,74,3, + 165,16,11,17,5,14,3,10,7,37,11,24,7,1,3,12,37,8,9,34,17,12,8,21,13,37,1,4, + 30,14,78,4,15,2,40,37,17,12,36,82,14,4,1,4,7,17,11,16,88,77,2,3,15,3,34,11, + 5,79,22,34,8,4,4,40,22,24,28,9,13,3,34,27,9,16,39,16,39,13,2,4,3,41,26,10,4, + 33,4,7,12,5,6,3,10,30,8,21,16,58,19,9,0,47,7,13,11,19,15,7,53,57,2,13,28,22, + 3,16,9,25,33,12,40,7,12,64,7,14,24,44,9,2,14,11,2,58,1,26,30,11,9,5,24,7,9, + 94,2,10,21,5,5,4,5,6,179,9,18,2,7,13,31,41,17,4,36,3,21,6,26,8,15,18,44,27, + 11,9,25,7,0,14,2,12,20,23,13,2,163,9,5,15,65,2,14,6,8,98,11,15,14,34,2,3,10, + 22,9,92,7,10,32,67,13,3,4,35,8,2,1,5,0,26,381,7,27,8,2,16,93,4,19,5,8,25,9, + 31,14,4,21,5,3,9,22,56,4,18,3,11,18,6,4,3,40,12,16,110,8,35,14,1,18,40,9,12, + 14,3,11,7,57,13,18,116,53,19,22,7,16,11,5,8,21,16,1,75,21,20,1,28,2,6,1,7, + 19,38,5,6,9,9,4,1,7,55,36,62,5,4,4,24,15,1,12,35,48,20,5,17,1,5,26,15,4,54, + 13,5,5,15,5,19,32,29,31,7,6,40,7,80,11,18,8,128,48,6,12,84,13,4,7,2,13,9,16, + 17,3,254,1,4,181,8,44,7,6,24,27,9,23,14,34,16,22,25,10,3,3,4,4,12,2,12,6,7, + 13,58,13,6,11,19,53,11,66,18,19,10,4,13,2,5,49,58,1,67,7,21,64,14,11,14,8,3, + 26,33,91,31,20,7,9,42,39,4,3,55,11,10,0,7,4,75,8,12,0,27,3,8,9,0,12,12,23, + 28,23,20,4,13,30,2,22,20,19,30,6,22,2,6,4,24,7,19,55,86,5,33,2,161,6,7,1,62, + 13,3,72,12,12,9,7,12,10,5,10,29,1,5,22,13,13,5,2,12,3,7,14,18,2,3,46,21,17, + 15,19,3,27,5,16,45,31,10,8,17,18,18,3,7,24,6,55,9,3,6,12,10,12,8,91,9,4,4,4, + 27,29,16,5,7,22,43,28,11,14,8,11,28,109,55,71,40,3,8,22,26,15,44,3,25,29,5, + 3,32,17,12,3,29,27,25,15,11,8,40,39,38,17,3,9,11,2,32,11,6,20,48,75,27,3,7, + 54,12,95,12,7,24,23,2,13,8,15,16,5,12,4,17,7,19,88,2,6,13,115,45,12,21,2,86, + 74,9,7,5,16,32,16,2,21,18,6,34,5,18,260,7,12,16,44,19,92,31,7,8,2,9,0,0,15, + 8,38,4,8,20,18,2,83,3,3,4,9,5,3,10,3,5,29,15,7,11,8,48,17,23,2,17,4,11,22, + 21,64,8,8,4,19,95,0,17,28,9,11,20,71,5,11,18,12,13,45,49,4,1,33,32,23,13,5, + 52,2,2,16,3,4,7,12,2,1,12,6,24,1,22,155,21,3,45,4,12,44,26,5,40,36,9,9,8,20, + 35,31,3,2,32,50,10,8,37,2,75,35,22,15,192,8,11,23,1,4,29,6,8,8,5,12,18,32,4, + 7,12,2,0,0,9,5,48,11,35,3,1,123,6,29,8,11,8,23,51,16,6,63,12,2,5,4,14,2,15, + 7,14,3,2,7,17,32,8,8,10,1,23,62,2,49,6,49,47,23,3,20,7,11,39,10,24,6,15,5,5, + 11,8,16,36,8,13,20,3,10,44,7,52,7,10,36,6,15,10,5,11,4,14,19,17,10,12,3,6, + 23,4,13,94,70,7,36,7,38,7,28,8,4,15,3,19,4,33,39,21,109,4,80,6,40,4,432,4,4, + 7,8,3,31,8,28,37,34,10,2,21,5,22,0,7,36,14,12,6,24,1,21,5,9,2,29,20,54,113, + 13,31,39,27,6,0,27,4,5,2,43,7,8,57,8,62,7,9,12,22,90,30,6,19,7,10,20,6,5,58, + 32,30,41,4,10,25,13,3,8,7,10,2,9,6,151,44,16,12,16,20,8,3,18,11,17,4,10,45, + 15,8,56,38,52,25,40,14,4,17,15,8,2,19,7,8,26,30,2,3,180,8,26,17,38,35,5,16, + 28,5,15,56,13,14,18,9,15,83,27,3,9,4,11,8,27,27,44,10,12,8,3,48,14,7,9,4,4, + 8,4,5,9,122,8,14,12,19,17,21,4,29,63,21,17,10,12,18,47,10,10,53,4,18,16,4,8, + 118,9,5,12,9,11,9,3,12,32,3,23,2,15,3,3,30,3,17,235,15,22,9,299,14,17,1,5, + 16,8,3,7,3,13,2,7,6,4,8,66,2,13,6,15,16,47,3,36,5,7,10,24,1,9,9,8,13,16,26, + 12,7,24,21,18,49,23,39,10,41,4,13,4,27,11,12,12,19,4,147,8,10,9,40,21,2,83, + 10,5,6,11,25,9,50,57,40,12,12,21,1,3,24,23,9,3,9,13,2,3,12,57,8,11,13,15,26, + 15,10,47,36,4,25,1,5,8,5,4,0,12,49,5,19,4,6,16,14,6,10,69,10,33,29,7,8,61, + 12,4,0,3,7,6,3,16,29,27,38,4,21,0,24,3,2,1,19,16,22,2,8,138,11,7,7,3,12,22, + 3,16,5,7,3,53,9,10,32,14,5,7,3,6,22,9,59,26,8,7,58,5,16,11,55,7,4,11,146,91, + 8,13,18,14,6,8,8,31,26,22,6,11,30,11,30,15,18,31,3,48,17,7,6,4,9,2,25,3,35, + 13,13,7,8,4,31,10,8,10,4,3,45,10,23,2,7,259,17,21,13,14,3,26,3,8,27,4,18,9, + 66,7,12,5,8,17,4,23,55,41,51,2,32,26,66,4,21,14,12,65,16,22,17,5,14,2,29,24, + 7,3,36,2,43,53,86,5,28,4,58,13,49,121,6,2,73,2,1,47,4,2,27,10,35,28,27,10, + 17,10,56,7,10,14,28,20,24,40,7,4,7,3,10,11,32,6,6,3,15,11,54,573,2,3,6,2,3, + 14,64,4,16,12,16,42,10,26,4,6,11,69,18,27,2,2,17,22,9,13,22,11,6,1,15,49,3, + 14,1 + }, + + { + 11,11,11,15,47,1,3,1,23,5,8,18,3,23,15,21,1,7,19,10,26,1,17,11,31,21,41,18, + 34,4,9,58,19,3,3,36,5,18,13,3,14,4,9,10,4,19,56,15,3,5,3,11,27,9,4,10,13,4, + 11,6,9,2,18,3,10,19,11,4,53,4,2,2,3,4,58,16,3,0,5,30,2,11,93,10,2,14,10,6,2, + 115,2,25,16,22,38,101,4,18,13,2,145,51,45,15,14,15,13,20,7,24,5,13,14,30,40, + 10,4,107,12,24,14,39,12,6,13,20,7,7,11,5,18,18,45,22,6,39,3,2,1,51,9,11,4, + 13,9,38,44,8,11,9,15,19,9,23,17,17,17,13,9,9,1,10,4,18,6,2,9,5,27,32,72,8, + 37,9,4,10,30,17,20,15,17,66,10,4,73,35,37,6,4,16,117,45,13,4,75,5,24,65,10, + 4,9,4,13,46,5,26,29,10,4,4,52,3,13,18,63,6,14,9,24,277,9,88,2,48,27,123,14, + 61,7,5,10,8,7,90,3,10,3,3,48,17,13,10,18,33,2,19,36,6,21,1,16,12,5,6,2,16, + 15,29,88,28,2,15,6,11,4,6,11,3,3,4,18,9,53,5,4,3,33,8,9,8,6,7,36,9,62,14,2, + 1,10,1,16,7,32,7,23,20,11,10,23,2,1,0,9,16,40,2,81,5,22,8,5,4,37,51,37,10, + 19,57,11,2,92,31,6,39,10,13,16,8,20,6,9,3,10,18,25,23,12,30,6,2,26,7,64,18, + 6,30,12,13,27,7,10,5,3,33,24,99,4,23,4,1,27,7,27,49,8,20,16,3,4,13,9,22,67, + 28,3,10,16,3,2,10,4,8,1,8,19,3,85,6,21,1,9,16,2,30,10,33,12,4,9,3,1,60,38,6, + 24,32,3,14,3,40,8,34,115,5,9,27,5,96,3,40,6,15,5,8,22,112,5,5,25,17,58,2,7, + 36,21,52,1,3,95,12,21,4,11,8,59,24,5,21,4,9,15,8,7,21,3,26,5,11,6,7,17,65, + 14,11,10,2,17,5,12,22,4,4,2,21,8,112,3,34,63,35,2,25,1,2,15,65,23,0,3,5,15, + 26,27,9,5,48,11,15,4,9,5,33,20,15,1,18,19,11,24,40,10,21,74,6,6,32,30,40,5, + 4,7,44,10,25,46,16,12,5,40,7,18,5,18,9,12,8,4,25,5,6,36,4,43,8,9,12,35,17,4, + 8,9,11,27,5,10,17,40,8,12,4,18,9,18,12,20,25,39,42,1,24,13,22,15,7,112,35,3, + 7,17,33,2,5,5,19,8,4,12,24,14,13,2,1,13,6,5,19,11,7,57,0,19,6,117,48,14,8, + 10,51,17,12,14,2,5,8,9,15,4,48,53,13,22,4,25,12,11,19,45,5,2,6,54,22,9,15,9, + 13,2,7,11,29,82,16,46,4,26,14,26,40,22,4,26,6,18,13,4,4,20,3,3,7,12,17,8,9, + 23,6,20,7,25,23,19,5,15,6,23,15,11,19,11,3,17,59,8,18,41,4,54,23,44,75,13, + 20,6,11,2,3,1,13,10,3,7,12,3,4,7,8,30,6,6,7,3,32,9,5,28,6,114,42,13,36,27, + 59,6,93,13,74,8,69,140,3,1,17,48,105,6,11,5,15,1,10,10,14,8,53,0,8,24,60,2, + 6,35,2,12,32,47,16,17,75,2,5,4,37,28,10,5,9,57,4,59,5,12,13,7,90,5,11,5,24, + 22,13,30,1,2,10,9,6,19,3,18,47,2,5,7,9,35,15,3,6,1,21,14,14,18,14,9,12,8,73, + 6,19,3,32,9,14,17,17,5,55,23,6,16,28,3,11,48,4,6,6,6,12,16,30,10,30,27,51, + 18,29,2,3,15,1,76,0,16,33,4,27,3,62,4,10,2,4,8,15,9,41,26,22,2,4,20,4,49,0, + 8,1,57,13,12,39,3,63,10,19,34,35,2,7,8,29,72,4,10,0,77,8,6,7,9,15,21,9,4,1, + 20,23,1,9,18,9,15,36,4,7,6,15,5,7,7,40,2,9,22,2,3,20,4,12,34,13,6,18,15,1, + 38,20,12,7,16,3,19,85,12,16,18,16,2,17,1,13,8,6,12,15,97,17,12,9,3,21,15,12, + 23,44,81,26,30,2,5,17,6,6,0,22,42,19,6,19,41,14,36,7,3,56,7,9,3,2,6,9,69,3, + 15,4,30,28,29,7,9,15,17,17,6,1,6,153,9,33,5,12,14,16,28,3,8,7,14,12,4,6,36, + 9,24,13,13,4,2,9,15,19,9,53,7,13,4,150,17,9,2,6,12,7,3,5,58,19,58,28,8,14,3, + 20,3,0,32,56,7,5,4,27,1,68,4,29,13,5,58,2,9,65,41,27,16,15,12,14,2,10,9,24, + 3,2,9,2,2,3,14,32,10,22,3,13,11,4,6,39,17,0,10,5,5,10,35,16,19,14,1,8,63,19, + 14,8,56,10,2,12,6,12,6,7,16,2,9,9,12,20,73,25,13,21,17,24,5,32,8,12,25,8,14, + 16,5,23,3,7,6,3,11,24,6,30,4,21,13,28,4,6,29,15,5,17,6,26,8,15,8,3,7,7,50, + 11,30,6,2,28,56,16,24,25,23,24,89,31,31,12,7,22,4,10,17,3,3,8,11,13,5,3,27, + 1,12,1,14,8,10,29,2,5,2,2,20,10,0,31,10,21,1,48,3,5,43,4,5,18,13,5,18,25,34, + 18,3,5,22,16,3,4,20,3,9,3,25,6,6,44,21,3,12,7,5,42,3,2,14,4,36,5,3,45,51,15, + 9,11,28,9,7,6,6,12,26,5,14,10,11,42,55,13,21,4,28,6,7,23,27,11,1,41,36,0,32, + 15,26,2,3,23,32,11,2,15,7,29,26,144,33,20,12,7,21,10,7,11,65,46,10,13,20,32, + 4,4,5,19,2,19,15,49,41,1,75,10,11,25,1,2,45,11,8,27,18,10,60,28,29,12,30,19, + 16,4,24,11,19,27,17,49,18,7,40,13,19,22,8,55,12,11,3,6,5,11,8,10,22,5,9,9, + 25,7,17,7,64,1,24,2,12,17,44,4,12,27,21,11,10,7,47,5,9,13,12,38,27,21,7,29, + 7,1,17,3,3,5,48,62,10,3,11,17,15,15,6,3,8,10,8,18,19,13,3,9,7,6,44,9,10,4, + 43,8,6,6,14,20,38,24,2,4,5,5,7,5,9,39,8,44,40,9,19,7,3,15,25,2,37,18,15,9,5, + 8,32,10,5,18,4,7,46,20,17,23,4,11,16,18,31,11,3,11,1,14,1,25,4,27,13,13,39, + 14,6,6,35,6,16,13,11,122,21,15,20,24,10,5,152,15,39,5,20,16,9,14,7,53,6,3,8, + 19,63,32,6,2,3,20,1,19,5,13,42,15,4,6,68,31,46,11,38,10,24,5,5,8,9,12,3,35, + 46,26,16,2,8,4,74,16,44,4,5,1,16,4,14,23,16,69,15,42,31,14,7,7,6,97,14,40,1, + 8,7,34,9,39,19,13,15,10,21,18,10,5,15,38,7,5,12,7,20,15,4,11,6,14,5,17,7,39, + 35,36,18,20,26,22,4,2,36,21,64,0,5,9,10,6,4,1,7,3,1,3,3,4,10,20,90,2,22,48, + 16,23,2,33,40,1,21,21,17,20,8,8,12,4,83,14,48,4,21,3,9,27,5,11,40,15,9,3,16, + 17,9,11,4,24,31,17,3,4,2,11,1,8,4,8,6,41,17,4,13,3,7,17,8,27,5,13,6,10,7,13, + 12,18,13,60,18,3,8,1,12,125,2,7,16,2,11,2,4,7,26,5,9,14,14,16,8,14,7,14,6,9, + 13,9,6,4,26,35,49,36,55,3,9,6,40,26,23,31,19,41,2,10,31,6,54,5,69,16,7,8,16, + 1,5,7,4,22,7,7,5,4,48,11,13,3,98,4,11,19,4,2,14,7,34,7,10,3,2,12,7,6,2,5,118 + }, +}; + + +int main() { + using namespace std; + using namespace boost; + using namespace facebook::thrift::transport; + + char *file_names[] = { + // Highly compressible. + "./gen-cpp/DebugProtoTest_types.cpp", + // Uncompressible. + "/dev/urandom", + // Null-terminated. + NULL, + }; + + + for (char** fnamep = &file_names[0]; *fnamep != NULL; fnamep++) { + ifstream file(*fnamep); + char buf[32*1024]; + file.read(buf, sizeof(buf)); + vector content(buf, buf+file.gcount()); + vector mirror; + file.close(); + + assert(content.size() == 32*1024); + + // Let's just start with the big dog! + { + mirror.clear(); + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf, false)); + zlib_trans->write(&content[0], content.size()); + zlib_trans->flush(); + mirror.resize(content.size()); + uint32_t got = zlib_trans->read(&mirror[0], mirror.size()); + assert(got == content.size()); + assert(mirror == content); + zlib_trans->verifyChecksum(); + } + + // This one is tricky. I separate the last byte of the stream out + // into a separate crbuf_. The last byte is part of the checksum, + // so the entire read goes fine, but when I go to verify the checksum + // it isn't there. The original implementation complained that + // the stream was not complete. I'm about to go fix that. + // It worked. Awesome. + { + mirror.clear(); + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf, false)); + zlib_trans->write(&content[0], content.size()); + zlib_trans->flush(); + string tmp_buf; + membuf->appendBufferToString(tmp_buf); + zlib_trans.reset(new TZlibTransport(membuf, false, + TZlibTransport::DEFAULT_URBUF_SIZE, + tmp_buf.length()-1)); + mirror.resize(content.size()); + uint32_t got = zlib_trans->read(&mirror[0], mirror.size()); + assert(got == content.size()); + assert(mirror == content); + zlib_trans->verifyChecksum(); + } + + // Make sure we still get that "not complete" error if + // it really isn't complete. + { + mirror.clear(); + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf, false)); + zlib_trans->write(&content[0], content.size()); + zlib_trans->flush(); + string tmp_buf; + membuf->appendBufferToString(tmp_buf); + tmp_buf.erase(tmp_buf.length() - 1); + membuf->resetFromString(tmp_buf); + mirror.resize(content.size()); + uint32_t got = zlib_trans->read(&mirror[0], mirror.size()); + assert(got == content.size()); + assert(mirror == content); + try { + zlib_trans->verifyChecksum(); + assert(false); + } catch (TTransportException& ex) { + assert(ex.getType() == TTransportException::CORRUPTED_DATA); + } + } + + // Try it with a mix of read/write sizes. + for (int d1 = 0; d1 < 3; d1++) { + for (int d2 = 0; d2 < 3; d2++) { + mirror.clear(); + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf, false)); + int idx; + unsigned int tot; + + idx = 0; + tot = 0; + while (tot < content.size()) { + zlib_trans->write(&content[tot], dist[d1][idx]); + tot += dist[d1][idx]; + idx++; + } + + zlib_trans->flush(); + mirror.resize(content.size()); + + idx = 0; + tot = 0; + while (tot < mirror.size()) { + uint32_t got = zlib_trans->read(&mirror[tot], dist[d2][idx]); + assert(got == dist[d2][idx]); + tot += dist[d2][idx]; + idx++; + } + + assert(mirror == content); + zlib_trans->verifyChecksum(); + } + } + + // Verify checksum checking. + { + mirror.clear(); + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf, false)); + zlib_trans->write(&content[0], content.size()); + zlib_trans->flush(); + string tmp_buf; + membuf->appendBufferToString(tmp_buf); + tmp_buf[57]++; + membuf->resetFromString(tmp_buf); + mirror.resize(content.size()); + try { + zlib_trans->read(&mirror[0], mirror.size()); + zlib_trans->verifyChecksum(); + assert(false); + } catch (TZlibTransportException& ex) { + assert(ex.getType() == TTransportException::INTERNAL_ERROR); + } + } + } + + return 0; +}