#pragma once #include "base/assert.hpp" #include "base/macros.hpp" #include "std/algorithm.hpp" #include "std/string.hpp" #include "zlib.h" namespace coding { // Following classes are wrappers around ZLib routines. // // *NOTE* All Inflate() and Deflate() methods may return false in case // of errors. In this case the output sequence may be already // partially formed, so the user needs to implement their own // roll-back strategy. class ZLib { public: class Inflate { public: enum class Format { ZLib, GZip, Both }; explicit Inflate(Format format) noexcept : m_format(format) {} template bool operator()(void const * data, size_t size, OutIt out) const { if (data == nullptr) return false; InflateProcessor processor(m_format, data, size); return Process(processor, out); } template bool operator()(string const & s, OutIt out) const { return (*this)(s.c_str(), s.size(), out); } private: Format const m_format; }; class Deflate { public: enum class Format { ZLib, GZip }; enum class Level { NoCompression, BestSpeed, BestCompression, DefaultCompression }; Deflate(Format format, Level level) noexcept : m_format(format), m_level(level) {} template bool operator()(void const * data, size_t size, OutIt out) const { if (data == nullptr) return false; DeflateProcessor processor(m_format, m_level, data, size); return Process(processor, out); } template bool operator()(string const & s, OutIt out) const { return (*this)(s.c_str(), s.size(), out); } private: Format const m_format; Level const m_level; }; private: class Processor { public: static size_t constexpr kBufferSize = 1024; Processor(void const * data, size_t size) noexcept; virtual ~Processor() noexcept = default; inline bool IsInit() const noexcept { return m_init; } bool ConsumedAll() const; bool BufferIsFull() const; template void MoveOut(OutIt out) { ASSERT(IsInit(), ()); copy(m_buffer, m_buffer + kBufferSize - m_stream.avail_out, out); m_stream.next_out = m_buffer; m_stream.avail_out = kBufferSize; } protected: z_stream m_stream; bool m_init; unsigned char m_buffer[kBufferSize]; DISALLOW_COPY_AND_MOVE(Processor); }; class DeflateProcessor final : public Processor { public: DeflateProcessor(Deflate::Format format, Deflate::Level level, void const * data, size_t size) noexcept; virtual ~DeflateProcessor() noexcept override; int Process(int flush); DISALLOW_COPY_AND_MOVE(DeflateProcessor); }; class InflateProcessor final : public Processor { public: InflateProcessor(Inflate::Format format, void const * data, size_t size) noexcept; virtual ~InflateProcessor() noexcept override; int Process(int flush); DISALLOW_COPY_AND_MOVE(InflateProcessor); }; template static bool Process(Processor & processor, OutIt out) { if (!processor.IsInit()) return false; while (true) { int const flush = processor.ConsumedAll() ? Z_FINISH : Z_NO_FLUSH; int ret = Z_OK; while (true) { ret = processor.Process(flush); if (ret != Z_OK && ret != Z_STREAM_END) return false; if (!processor.BufferIsFull()) break; processor.MoveOut(out); } if (flush == Z_FINISH && ret == Z_STREAM_END) break; } processor.MoveOut(out); return true; } }; } // namespace coding