diff options
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-src/src/ZLibOutputStream.hpp')
-rw-r--r-- | Build/source/texk/dvisvgm/dvisvgm-src/src/ZLibOutputStream.hpp | 181 |
1 files changed, 121 insertions, 60 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-src/src/ZLibOutputStream.hpp b/Build/source/texk/dvisvgm/dvisvgm-src/src/ZLibOutputStream.hpp index 4a0b2fa52e2..c03db71742d 100644 --- a/Build/source/texk/dvisvgm/dvisvgm-src/src/ZLibOutputStream.hpp +++ b/Build/source/texk/dvisvgm/dvisvgm-src/src/ZLibOutputStream.hpp @@ -21,100 +21,161 @@ #ifndef ZLIBOUTPUTSTREAM_HPP #define ZLIBOUTPUTSTREAM_HPP +#include <fstream> #include <ostream> +#include <vector> #include <zlib.h> +#include "MessageException.hpp" -class ZLibOutputStream; +#ifdef _WIN32 +# include <fcntl.h> +# include <io.h> +#endif + +struct ZLibException : public MessageException { + ZLibException (const std::string &msg) : MessageException(msg) {} +}; -class ZLibOutputBuffer : public std::streambuf -{ - friend class ZLibOutputStream; +enum ZLibCompressionFormat {ZLIB_DEFLATE=0, ZLIB_GZIP=16}; + +class ZLibOutputBuffer : public std::streambuf { public: - ~ZLibOutputBuffer () {close();} + ZLibOutputBuffer () {} + + ZLibOutputBuffer (std::streambuf *sbuf, ZLibCompressionFormat format, int zipLevel) { + open(sbuf, format, zipLevel); + } - /** Opens the buffer object for writing. - * @param[in] fname name of gzip file + ~ZLibOutputBuffer () { + close(); + } + + /** Opens the buffer for writing. + * @param[in] sink stream buffer taking the compressed data + * @param[in] format compression format (deflate or gzip) * @param[in] zipLevel compression level (1-9) - * @return true on success */ - bool open (const char *fname, int zipLevel) { - if (_opened) - return false; - zipLevel = std::max(1, std::min(9, zipLevel)); - std::string modestr = "wb0"; - modestr[2] += zipLevel; - _gzfile = gzopen(fname, modestr.c_str()); - return _opened = (_gzfile != nullptr); + * @return true if buffer is ready for writing */ + bool open (std::streambuf *sink, ZLibCompressionFormat format, int zipLevel) { + if (sink) { + _inbuf.reserve(4096); + _zbuf.resize(4096); + _zstream.zalloc = Z_NULL; + _zstream.zfree = Z_NULL; + _zstream.opaque = Z_NULL; + zipLevel = std::max(1, std::min(9, zipLevel)); + if (deflateInit2(&_zstream, zipLevel, Z_DEFLATED, 15+format, 8, Z_DEFAULT_STRATEGY) != Z_OK) + throw ZLibException("failed to initialize deflate compression"); + _sink = sink; + _opened = true; + } + return _opened; } - /** Closes the buffer object. */ - bool close () { - if (!_opened) - return false; - sync(); - _opened = false; - return gzclose(_gzfile) == Z_OK; + /** Flushes the remaining data, finishes the compression process, and + * closes the buffer so that further output doesn't reach the sink. */ + void close () { + close(true); } - int overflow (int c) override { - if (!_opened) - return EOF; - if (c != EOF) { - *pptr() = c; - pbump(1); + int_type overflow (int_type c) override { + if (c == traits_type::eof()) { + close(); + } + else { + if (_inbuf.size() == _inbuf.capacity()) + flush(Z_NO_FLUSH); + _inbuf.push_back(c); } - return (flush() == EOF) ? EOF : c; + return c; } int sync () override { - return (pptr() && pptr() > pbase() && flush() == EOF) ? -1 : 0; + flush(Z_NO_FLUSH); + return 0; } protected: - ZLibOutputBuffer () : _gzfile(nullptr), _opened(false) { - setp(_buffer, _buffer+SIZE-1); + /** Compresses the chunk of data present in the input buffer + * and writes it to the assigned output stream. + * @param[in] flushmode flush mode of deflate function (Z_NO_FLUSH or Z_FINISH) + * @throws ZLibException if compression failed */ + void flush (int flushmode) { + if (_opened) { + _zstream.avail_in = _inbuf.size(); + _zstream.next_in = _inbuf.data(); + do { + _zstream.avail_out = _zbuf.size(); + _zstream.next_out = _zbuf.data(); + int ret = deflate(&_zstream, flushmode); + if (ret == Z_STREAM_ERROR) { + close(false); + throw ZLibException("stream error during data compression"); + } + auto have = _zbuf.size()-_zstream.avail_out; + _sink->sputn(reinterpret_cast<char*>(_zbuf.data()), have); + } while (_zstream.avail_out == 0); + } + _inbuf.clear(); } - /** Forces to write the buffer data to the output file. */ - int flush () { - int w = pptr()-pbase(); - if (gzwrite(_gzfile, pbase(), w) != w) - return EOF; - pbump(-w); - return w; + /** Closes the buffer so that further output doesn't reach the sink. + * @param[in] finish if true, flushes the remaining data and finishes the compression process */ + void close (bool finish) { + if (_opened) { + if (finish) + flush(Z_FINISH); + deflateEnd(&_zstream); + _sink = nullptr; + _opened = false; + } } private: - static constexpr int SIZE = 512; - gzFile _gzfile; - char _buffer[SIZE]; - bool _opened; + z_stream _zstream; + std::streambuf *_sink = nullptr; ///< target buffer where the compressded data is flushed to + std::vector<Bytef> _inbuf; ///< buffer holding a chunk of data to be compressed + std::vector<Bytef> _zbuf; ///< buffer holding a chunk of compressed data + bool _opened = false; ///< true if ready to process the incoming data correctly }; -class ZLibOutputStream : public virtual std::ios, public std::ostream -{ +class ZLibOutputStream : private ZLibOutputBuffer, public std::ostream { public: - ZLibOutputStream () : std::ostream(&_buf) {init(&_buf);} - ZLibOutputStream (const char *fname, int zipLevel) : ZLibOutputStream() {open(fname, zipLevel);} - ZLibOutputStream (const std::string &fname, int zipLevel) : ZLibOutputStream(fname.c_str(), zipLevel) {} + ZLibOutputStream () : std::ostream(this) {} + + ZLibOutputStream (std::ostream &os, ZLibCompressionFormat format, int zipLevel) + : ZLibOutputBuffer(os.rdbuf(), format, zipLevel), std::ostream(this) {} + ~ZLibOutputStream () {close();} - ZLibOutputBuffer* rdbuf() {return &_buf;} - - /** Opens the output stream for writing. - * @param[in] fname name of gzip file - * @param[in] zipLevel compression level (1-9) */ - void open (const char *name, int zipLevel) { - if (!_buf.open(name, zipLevel)) - clear(rdstate()|std::ios::badbit); + + bool open (std::ostream &os, ZLibCompressionFormat format, int zipLevel) { + ZLibOutputBuffer::close(); + return ZLibOutputBuffer::open(os.rdbuf(), format, zipLevel); } void close () { - if (!_buf.close()) - clear(rdstate()|std::ios::badbit); + ZLibOutputBuffer::close(); } +}; + + +class ZLibOutputFileStream : public ZLibOutputStream { + public: + ZLibOutputFileStream (const std::string &fname, ZLibCompressionFormat format, int zipLevel) + : _ofs(fname, std::ios::binary) + { + if (_ofs) { + if (_ofs.rdbuf()) + open(_ofs, format, zipLevel); + else + _ofs.close(); + } + } + + ~ZLibOutputFileStream () {close();} private: - ZLibOutputBuffer _buf; + std::ofstream _ofs; }; #endif |