From 95239fea87250aea3def686ce74c8d34baaf6a82 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Sun, 13 Jun 2021 20:39:48 +0100 Subject: [PATCH] simgear/io/iostreams/zlibstream.*: improved support for reading compressed data. Added support for seeking (forwards only) - in ZlibAbstractIStreambuf, added seekoff(). Also disabled assert that checked that we reach EOF compressed data at same time as EOF uncompressed data; was breaking things when using embedded compressed streams within fgtape recording. Added ZLibCompressionFormat::ZLIB_RAW, uses windowBits=-15, for zlib compressed streams without header or trailer. Fixed clang build error with std::min(). --- simgear/io/iostreams/zlibstream.cxx | 30 ++++++++++++++++++++++++++--- simgear/io/iostreams/zlibstream.hxx | 10 +++++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/simgear/io/iostreams/zlibstream.cxx b/simgear/io/iostreams/zlibstream.cxx index 2a4c36e1..7011bbd8 100644 --- a/simgear/io/iostreams/zlibstream.cxx +++ b/simgear/io/iostreams/zlibstream.cxx @@ -157,7 +157,6 @@ ZlibAbstractIStreambuf::ZlibAbstractIStreambuf(std::istream& iStream, _putbackSize(putbackSize) { assert(_inBufSize > 0); - assert(_putbackSize >= 0); // guaranteed unless the type is changed... assert(_putbackSize < _outBufSize); if (_inBuf == nullptr) { @@ -249,6 +248,28 @@ int ZlibAbstractIStreambuf::underflow() return (gptr() == egptr()) ? traits::eof() : traits::to_int_type(*gptr()); } +std::streampos ZlibAbstractIStreambuf::seekoff(std::streamoff off, + std::ios_base::seekdir way, + std::ios_base::openmode which) +{ + if (way != std::ios_base::cur || off < 0) return -1; + for(;;) { + if (!off) break; + char* g = gptr(); + char* eg = egptr(); + if (eg > g) { + size_t delta = (off < eg - g) ? off : eg - g; + setg(g + delta, g + delta, eg); + off -= delta; + } + else { + if (underflow() == EOF) return -1; + } + } + return 0; +} + + // Simple utility method for fillOutputBuffer(), used to improve readability. // Return the remaining space available in the output buffer, where zlib can // write. @@ -320,7 +341,7 @@ char* ZlibAbstractIStreambuf::fillOutputBuffer() if (retCode == Z_BUF_ERROR) { handleZ_BUF_ERROR(); // doesn't return } else if (retCode == Z_STREAM_END) { - assert(_zstream.avail_in == 0); // all of _inBuf must have been used + //assert(_zstream.avail_in == 0); // all of _inBuf must have been used _allFinished = true; break; } else if (retCode < 0) { // negative codes are errors @@ -481,7 +502,7 @@ std::streamsize ZlibAbstractIStreambuf::xsgetn(char* dest, std::streamsize n) if (retCode == Z_BUF_ERROR) { handleZ_BUF_ERROR(); // doesn't return } else if (retCode == Z_STREAM_END) { - assert(_zstream.avail_in == 0); // all of _inBuf must have been used + //assert(_zstream.avail_in == 0); // all of _inBuf must have been used _allFinished = true; break; } else if (retCode < 0) { // negative codes are errors @@ -730,6 +751,9 @@ void ZlibDecompressorIStreambuf::zStreamInit(ZLibCompressionFormat format) case ZLibCompressionFormat::AUTODETECT: windowBits = 47; // 47 = 32 + 15 break; + case ZLibCompressionFormat::ZLIB_RAW: + windowBits = -15; + break; default: throw std::logic_error("Unexpected compression format: " + std::to_string(enumValue(format))); diff --git a/simgear/io/iostreams/zlibstream.hxx b/simgear/io/iostreams/zlibstream.hxx index e4a2621c..357d5de0 100644 --- a/simgear/io/iostreams/zlibstream.hxx +++ b/simgear/io/iostreams/zlibstream.hxx @@ -103,7 +103,8 @@ namespace simgear enum class ZLibCompressionFormat { ZLIB = 0, GZIP, - AUTODETECT + AUTODETECT, + ZLIB_RAW, // No zlib header or trailer. }; enum class ZLibMemoryStrategy { @@ -196,6 +197,13 @@ private: // Callback whose role is to refill the output buffer when it's empty and // the “client” tries to read more. virtual int underflow() override; + + // We only support seeking forwards, with way==std::ios_base::cur and + // off>=0, returning 0. Otherwise we return -1. + std::streampos seekoff(std::streamoff off, + std::ios_base::seekdir way, + std::ios_base::openmode which) override; + // Optional override when subclassing std::streambuf. This is the most // efficient way of reading several characters (as soon as we've emptied the // output buffer, data is written by zlib directly to the destination