[coding] ZLib wrappers.

This commit is contained in:
Yuri Gorshenin 2016-12-08 01:57:10 +03:00
parent fce37cc9a0
commit d6816bb4ee
7 changed files with 265 additions and 0 deletions

View file

@ -90,6 +90,8 @@ set(
zip_creator.hpp
zip_reader.cpp
zip_reader.hpp
zlib.cpp
zlib.hpp
)
add_library(${PROJECT_NAME} ${SRC})

View file

@ -30,6 +30,7 @@ SOURCES += \
# varint_vector.cpp \
zip_creator.cpp \
zip_reader.cpp \
zlib.cpp \
HEADERS += \
$$ROOT_DIR/3party/expat/expat_impl.h \
@ -93,3 +94,4 @@ HEADERS += \
writer.hpp \
zip_creator.hpp \
zip_reader.hpp \
zlib.hpp \

View file

@ -41,6 +41,7 @@ set(
writer_test.cpp
zip_creator_test.cpp
zip_reader_test.cpp
zlib_test.cpp
)
omim_add_test(${PROJECT_NAME} ${SRC})

View file

@ -48,6 +48,7 @@ SOURCES += ../../testing/testingmain.cpp \
writer_test.cpp \
zip_creator_test.cpp \
zip_reader_test.cpp \
zlib_test.cpp \
HEADERS += \
coder_test.hpp \

View file

@ -0,0 +1,50 @@
#include "testing/testing.hpp"
#include "coding/zlib.hpp"
#include "std/iterator.hpp"
#include "std/sstream.hpp"
#include "std/string.hpp"
using namespace coding;
namespace
{
void TestInflateDeflate(string const & original)
{
string compressed;
TEST(ZLib::Deflate(original, ZLib::Level::BestCompression, back_inserter(compressed)), ());
string decompressed;
TEST(ZLib::Inflate(compressed, back_inserter(decompressed)), ());
TEST_EQUAL(original, decompressed, ());
}
UNIT_TEST(ZLib_Smoke)
{
{
string s;
TEST(!ZLib::Deflate(nullptr, 0, ZLib::Level::BestCompression, back_inserter(s)), ());
TEST(!ZLib::Deflate(nullptr, 4, ZLib::Level::BestCompression, back_inserter(s)), ());
TEST(!ZLib::Inflate(nullptr, 0, back_inserter(s)), ());
TEST(!ZLib::Inflate(nullptr, 4, back_inserter(s)), ());
}
TestInflateDeflate("");
TestInflateDeflate("Hello, World");
}
UNIT_TEST(ZLib_Large)
{
string original;
{
ostringstream os;
for (size_t i = 0; i < 1000; ++i)
os << i;
original = os.str();
}
TestInflateDeflate(original);
}
} // namespace

83
coding/zlib.cpp Normal file
View file

@ -0,0 +1,83 @@
#include "coding/zlib.hpp"
namespace coding
{
namespace
{
int LevelToInt(ZLib::Level level)
{
switch (level)
{
case ZLib::Level::NoCompression: return Z_NO_COMPRESSION;
case ZLib::Level::BestSpeed: return Z_BEST_SPEED;
case ZLib::Level::BestCompression: return Z_BEST_COMPRESSION;
case ZLib::Level::DefaultCompression: return Z_DEFAULT_COMPRESSION;
}
}
} // namespace
// ZLib::Processor ---------------------------------------------------------------------------------
ZLib::Processor::Processor(char const * data, size_t size) : m_init(false)
{
m_stream.next_in = const_cast<unsigned char *>(reinterpret_cast<unsigned char const *>(data));
m_stream.avail_in = size;
m_stream.next_out = reinterpret_cast<unsigned char *>(m_buffer);
m_stream.avail_out = kBufferSize;
m_stream.zalloc = Z_NULL;
m_stream.zfree = Z_NULL;
m_stream.opaque = Z_NULL;
}
bool ZLib::Processor::ConsumedAll() const
{
ASSERT(IsInit(), ());
return m_stream.avail_in == 0;
}
bool ZLib::Processor::BufferIsFull() const
{
ASSERT(IsInit(), ());
return m_stream.avail_out == 0;
}
// ZLib::Deflate -----------------------------------------------------------------------------------
ZLib::DeflateProcessor::DeflateProcessor(char const * data, size_t size, ZLib::Level level)
: Processor(data, size)
{
int const ret = deflateInit(&m_stream, LevelToInt(level));
m_init = (ret == Z_OK);
}
ZLib::DeflateProcessor::~DeflateProcessor()
{
if (m_init)
deflateEnd(&m_stream);
}
int ZLib::DeflateProcessor::Process(int flush)
{
ASSERT(IsInit(), ());
return deflate(&m_stream, flush);
}
// ZLib::Inflate -----------------------------------------------------------------------------------
ZLib::InflateProcessor::InflateProcessor(char const * data, size_t size) : Processor(data, size)
{
int const ret = inflateInit(&m_stream);
m_init = (ret == Z_OK);
}
ZLib::InflateProcessor::~InflateProcessor()
{
if (m_init)
inflateEnd(&m_stream);
}
int ZLib::InflateProcessor::Process(int flush)
{
ASSERT(IsInit(), ());
return inflate(&m_stream, flush);
}
} // namespace coding

126
coding/zlib.hpp Normal file
View file

@ -0,0 +1,126 @@
#include "base/assert.hpp"
#include "std/algorithm.hpp"
#include "std/string.hpp"
#include "zlib.h"
namespace coding
{
// Following class is a wrapper 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 its own roll-back
// strategy.
class ZLib
{
public:
enum class Level
{
NoCompression,
BestSpeed,
BestCompression,
DefaultCompression
};
template <typename OutIt>
static bool Deflate(char const * data, size_t size, Level level, OutIt out)
{
if (!data)
return false;
DeflateProcessor processor(data, size, level);
return Process(processor, out);
}
template <typename OutIt>
static bool Deflate(string const & s, Level level, OutIt out)
{
return Deflate(s.c_str(), s.size(), level, out);
}
template <typename OutIt>
static bool Inflate(char const * data, size_t size, OutIt out)
{
if (!data)
return false;
InflateProcessor processor(data, size);
return Process(processor, out);
}
template <typename OutIt>
static bool Inflate(string const & s, OutIt out)
{
return Inflate(s.c_str(), s.size(), out);
}
private:
class Processor
{
public:
static size_t constexpr kBufferSize = 1024;
Processor(char const * data, size_t size);
inline bool IsInit() const { return m_init; }
bool ConsumedAll() const;
bool BufferIsFull() const;
template <typename OutIt>
void MoveOut(OutIt out)
{
ASSERT(IsInit(), ());
copy(m_buffer, m_buffer + kBufferSize - m_stream.avail_out, out);
m_stream.next_out = reinterpret_cast<unsigned char *>(m_buffer);
m_stream.avail_out = kBufferSize;
}
protected:
z_stream m_stream;
bool m_init;
char m_buffer[kBufferSize];
};
class DeflateProcessor : public Processor
{
public:
DeflateProcessor(char const * data, size_t size, Level level);
~DeflateProcessor();
int Process(int flush);
};
class InflateProcessor : public Processor
{
public:
InflateProcessor(char const * data, size_t size);
~InflateProcessor();
int Process(int flush);
};
template <typename Processor, typename OutIt>
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 const ret = processor.Process(flush);
if (ret != Z_OK && ret != Z_STREAM_END)
return false;
if (processor.BufferIsFull())
processor.MoveOut(out);
if (flush == Z_FINISH && ret == Z_STREAM_END)
break;
}
processor.MoveOut(out);
return true;
}
};
} // namespace coding