Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/coding
diff options
context:
space:
mode:
authorYuri Gorshenin <y@maps.me>2016-12-08 01:57:10 +0300
committerYuri Gorshenin <y@maps.me>2016-12-08 02:01:45 +0300
commitd6816bb4ee5fc53f706e243e2e0e52d1d18a463a (patch)
tree6018dcb41e8cc2893f5d6457d25728daa0b40768 /coding
parentfce37cc9a0483aed41aaa15ad5b20ee64bd4c3e0 (diff)
[coding] ZLib wrappers.
Diffstat (limited to 'coding')
-rw-r--r--coding/CMakeLists.txt2
-rw-r--r--coding/coding.pro2
-rw-r--r--coding/coding_tests/CMakeLists.txt1
-rw-r--r--coding/coding_tests/coding_tests.pro1
-rw-r--r--coding/coding_tests/zlib_test.cpp50
-rw-r--r--coding/zlib.cpp83
-rw-r--r--coding/zlib.hpp126
7 files changed, 265 insertions, 0 deletions
diff --git a/coding/CMakeLists.txt b/coding/CMakeLists.txt
index 6278e3c1b7..d09d6002b1 100644
--- a/coding/CMakeLists.txt
+++ b/coding/CMakeLists.txt
@@ -90,6 +90,8 @@ set(
zip_creator.hpp
zip_reader.cpp
zip_reader.hpp
+ zlib.cpp
+ zlib.hpp
)
add_library(${PROJECT_NAME} ${SRC})
diff --git a/coding/coding.pro b/coding/coding.pro
index 123247a2ff..264abb1cc5 100644
--- a/coding/coding.pro
+++ b/coding/coding.pro
@@ -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 \
diff --git a/coding/coding_tests/CMakeLists.txt b/coding/coding_tests/CMakeLists.txt
index 7aa4610344..c4a2d1eb81 100644
--- a/coding/coding_tests/CMakeLists.txt
+++ b/coding/coding_tests/CMakeLists.txt
@@ -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})
diff --git a/coding/coding_tests/coding_tests.pro b/coding/coding_tests/coding_tests.pro
index 899346a795..232d5eac79 100644
--- a/coding/coding_tests/coding_tests.pro
+++ b/coding/coding_tests/coding_tests.pro
@@ -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 \
diff --git a/coding/coding_tests/zlib_test.cpp b/coding/coding_tests/zlib_test.cpp
new file mode 100644
index 0000000000..6694ad30bd
--- /dev/null
+++ b/coding/coding_tests/zlib_test.cpp
@@ -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
diff --git a/coding/zlib.cpp b/coding/zlib.cpp
new file mode 100644
index 0000000000..61979359f4
--- /dev/null
+++ b/coding/zlib.cpp
@@ -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
diff --git a/coding/zlib.hpp b/coding/zlib.hpp
new file mode 100644
index 0000000000..86743bb2c0
--- /dev/null
+++ b/coding/zlib.hpp
@@ -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