diff options
| author | ljfa-ag <ljfa-ag@web.de> | 2015-09-15 14:01:35 +0200 |
|---|---|---|
| committer | ljfa-ag <ljfa-ag@web.de> | 2015-09-15 14:01:35 +0200 |
| commit | c782034564ed422de61235917917963c5e5c3fca (patch) | |
| tree | 403a69650fe94324f2f64e404b6e01c6dcd563dd | |
| parent | ec792d93a73e49738d4ded361f23acfae3a00e94 (diff) | |
| parent | 7a8a1833dc766abc1626a1e590e3f39a7d503062 (diff) | |
| download | Project-Tick-c782034564ed422de61235917917963c5e5c3fca.tar.gz Project-Tick-c782034564ed422de61235917917963c5e5c3fca.zip | |
Merge branch 'zlibstream'
| -rw-r--r-- | CMakeLists.txt | 4 | ||||
| -rw-r--r-- | include/io/izlibstream.h | 96 | ||||
| -rw-r--r-- | include/io/ozlibstream.h | 12 | ||||
| -rw-r--r-- | include/io/zlib_error.h | 19 | ||||
| -rw-r--r-- | src/io/izlibstream.cpp | 95 | ||||
| -rw-r--r-- | test/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | test/testfiles/bigtest_corrupt.nbt | bin | 0 -> 561 bytes | |||
| -rw-r--r-- | test/testfiles/bigtest_eof.nbt | bin | 0 -> 426 bytes | |||
| -rw-r--r-- | test/zlibstream_test.h | 115 |
9 files changed, 332 insertions, 12 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a2eb71cb8..c16860a8ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,12 +14,14 @@ add_library(nbt++ STATIC src/value.cpp src/value_initializer.cpp + src/io/izlibstream.cpp src/io/stream_reader.cpp src/io/stream_writer.cpp src/text/json_formatter.cpp) -find_package(CxxTest) +find_package(zlib REQUIRED) +find_package(CxxTest) option(NBT_BUILD_TESTS "Build the unit tests. Requires CxxTest." ${CXXTEST_FOUND}) diff --git a/include/io/izlibstream.h b/include/io/izlibstream.h new file mode 100644 index 0000000000..edf632eddf --- /dev/null +++ b/include/io/izlibstream.h @@ -0,0 +1,96 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef IZLIBSTREAM_H_INCLUDED +#define IZLIBSTREAM_H_INCLUDED + +#include <istream> +#include <vector> +#include <zlib.h> + +namespace zlib +{ + +/** + * @brief Stream buffer used by zlib::izlibstream + * @sa izlibstream + */ +class inflate_streambuf : public std::streambuf +{ +public: + /** + * @param input the istream to wrap + * @param bufsize the size of the internal buffers + * @param window_bits the base two logarithm of the maximum window size that + * zlib will use. This parameter also determines which type of input to expect. + * The default argument will autodetect between zlib and gzip data. + * Refer to the zlib documentation of inflateInit2 for more details. + * + * @throw zlib_error if zlib encounters a problem during initialization + */ + explicit inflate_streambuf(std::istream& input, size_t bufsize = 32768, int window_bits = 32 + 15); + ~inflate_streambuf() noexcept; + + //No copying or moving + inflate_streambuf(const inflate_streambuf&) = delete; + inflate_streambuf& operator=(const inflate_streambuf&) = delete; + + ///@return the wrapped istream + std::istream& get_istr() const { return is; } + +private: + std::istream& is; + std::vector<char> in; + std::vector<char> out; + z_stream zstr; + bool stream_end; + + int_type underflow() override; +}; + +/** + * @brief An istream adapter that decompresses data using zlib + * + * This istream wraps another istream. The izlibstream will read compressed + * data from the wrapped istream and inflate (decompress) it with zlib. + * + * @sa inflate_streambuf + */ +class izlibstream : public std::istream +{ +public: + /** + * @param input the istream to wrap + * @param bufsize the size of the internal buffers + */ + explicit izlibstream(std::istream& input, size_t bufsize = 32768): + buf(input, bufsize) + { + init(&buf); + } + ///@return the wrapped istream + std::istream& get_istr() const { return buf.get_istr(); } + +private: + inflate_streambuf buf; +}; + +} + +#endif // IZLIBSTREAM_H_INCLUDED diff --git a/include/io/ozlibstream.h b/include/io/ozlibstream.h index c427281763..6ebd3c0dd0 100644 --- a/include/io/ozlibstream.h +++ b/include/io/ozlibstream.h @@ -20,6 +20,7 @@ #ifndef OZLIBSTREAM_H_INCLUDED #define OZLIBSTREAM_H_INCLUDED +#include "io/zlib_error.h" #include <ostream> #include <vector> #include <zlib.h> @@ -27,17 +28,6 @@ namespace zlib { -///Exception thrown in case zlib encounters a problem -class zlib_error : public std::runtime_error -{ -public: - const int errcode; - - explicit zlib_error(const char* what_arg, int errcode = Z_ERRNO): - std::runtime_error(what_arg), errcode(errcode) - {} -}; - /** * @brief Stream buffer used by zlib::ozlibstream * @see ozlibstream diff --git a/include/io/zlib_error.h b/include/io/zlib_error.h new file mode 100644 index 0000000000..ce375a6ccd --- /dev/null +++ b/include/io/zlib_error.h @@ -0,0 +1,19 @@ +#ifndef ZLIB_ERROR_H_INCLUDED +#define ZLIB_ERROR_H_INCLUDED + +#include <stdexcept> +#include <zlib.h> + +///Exception thrown in case zlib encounters a problem +class zlib_error : public std::runtime_error +{ +public: + const int errcode; + + explicit zlib_error(const char* msg, int errcode): + std::runtime_error(std::string(zError(errcode)) + ": " + msg), + errcode(errcode) + {} +}; + +#endif // ZLIB_ERROR_H_INCLUDED diff --git a/src/io/izlibstream.cpp b/src/io/izlibstream.cpp new file mode 100644 index 0000000000..db69e0e626 --- /dev/null +++ b/src/io/izlibstream.cpp @@ -0,0 +1,95 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see <http://www.gnu.org/licenses/>. + */ +#include "io/izlibstream.h" +#include "io/zlib_error.h" + +namespace zlib +{ + +inflate_streambuf::inflate_streambuf(std::istream& input, size_t bufsize, int window_bits): + is(input), in(bufsize), out(bufsize), stream_end(false) +{ + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + zstr.next_in = Z_NULL; + zstr.avail_in = 0; + int ret = inflateInit2(&zstr, window_bits); + if(ret != Z_OK) + throw zlib_error(zstr.msg, ret); + + char* end = out.data() + out.size(); + setg(end, end, end); +} + +inflate_streambuf::~inflate_streambuf() +{ + inflateEnd(&zstr); +} + +inflate_streambuf::int_type inflate_streambuf::underflow() +{ + if(gptr() < egptr()) + return traits_type::to_int_type(*gptr()); + + size_t have; + do + { + //Read if input buffer is empty + if(zstr.avail_in <= 0) + { + is.read(in.data(), in.size()); + if(is.bad()) + throw std::ios_base::failure("Input stream is bad"); + size_t count = is.gcount(); + if(count == 0 && !stream_end) + throw zlib_error("Unexpected end of stream", Z_DATA_ERROR); + + zstr.next_in = reinterpret_cast<Bytef*>(in.data()); + zstr.avail_in = count; + } + + zstr.next_out = reinterpret_cast<Bytef*>(out.data()); + zstr.avail_out = out.size(); + + int ret = inflate(&zstr, Z_NO_FLUSH); + have = out.size() - zstr.avail_out; + switch(ret) + { + case Z_NEED_DICT: + case Z_DATA_ERROR: + throw zlib_error(zstr.msg, ret); + + case Z_MEM_ERROR: + throw std::bad_alloc(); + + case Z_STREAM_END: + stream_end = true; + if(have == 0) + return traits_type::eof(); + break; + } + } while(have == 0); + + setg(out.data(), out.data(), out.data() + have); + return traits_type::to_int_type(*gptr()); +} + +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c13f09f2da..c640ef054e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,9 @@ add_custom_command(TARGET read_test POST_BUILD CXXTEST_ADD_TEST(write_test write_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/write_test.h) target_link_libraries(write_test nbt++) +CXXTEST_ADD_TEST(zlibstream_test zlibstream_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/zlibstream_test.h) +target_link_libraries(zlibstream_test nbt++ z) + add_executable(format_test format_test.cpp) target_link_libraries(format_test nbt++) add_test(format_test format_test) diff --git a/test/testfiles/bigtest_corrupt.nbt b/test/testfiles/bigtest_corrupt.nbt Binary files differnew file mode 100644 index 0000000000..71eba42a7b --- /dev/null +++ b/test/testfiles/bigtest_corrupt.nbt diff --git a/test/testfiles/bigtest_eof.nbt b/test/testfiles/bigtest_eof.nbt Binary files differnew file mode 100644 index 0000000000..eeedb9d26d --- /dev/null +++ b/test/testfiles/bigtest_eof.nbt diff --git a/test/zlibstream_test.h b/test/zlibstream_test.h new file mode 100644 index 0000000000..f37dcdbd91 --- /dev/null +++ b/test/zlibstream_test.h @@ -0,0 +1,115 @@ +/* + * libnbt++ - A library for the Minecraft Named Binary Tag format. + * Copyright (C) 2013, 2015 ljfa-ag + * + * This file is part of libnbt++. + * + * libnbt++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libnbt++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libnbt++. If not, see <http://www.gnu.org/licenses/>. + */ +#include <cxxtest/TestSuite.h> +#include "io/izlibstream.h" +#include "io/ozlibstream.h" +#include <fstream> +#include <sstream> + +using namespace zlib; + +class zlibstream_test : public CxxTest::TestSuite +{ +private: + std::stringbuf bigtest; + +public: + zlibstream_test() + { + std::ifstream bigtest_f("bigtest_uncompr", std::ios::binary); + bigtest_f >> &bigtest; + if(!bigtest_f || bigtest.str().size() == 0) + throw std::runtime_error("Could not read bigtest_uncompr file"); + } + + void test_inflate_gzip() + { + std::ifstream gzip_in("bigtest.nbt", std::ios::binary); + TS_ASSERT(gzip_in); + + std::stringbuf data; + //Small buffer so not all fits at once (the compressed file is 561 bytes) + { + izlibstream igzs(gzip_in, 256); + igzs.exceptions(std::ios::failbit); + TS_ASSERT(igzs.good()); + + TS_ASSERT_THROWS_NOTHING(igzs >> &data); + TS_ASSERT(igzs); + TS_ASSERT(igzs.eof()); + TS_ASSERT_EQUALS(data.str(), bigtest.str()); + } + + //Clear and reuse buffers + data.str(""); + gzip_in.clear(); + gzip_in.seekg(0); + //Now try the same with larger buffer (but not large enough for all output, uncompressed size 1561 bytes) + { + izlibstream igzs(gzip_in, 1000); + igzs.exceptions(std::ios::failbit); + TS_ASSERT(igzs.good()); + + TS_ASSERT_THROWS_NOTHING(igzs >> &data); + TS_ASSERT(igzs); + TS_ASSERT(igzs.eof()); + TS_ASSERT_EQUALS(data.str(), bigtest.str()); + } + + data.str(""); + gzip_in.clear(); + gzip_in.seekg(0); + //Now with large buffer + { + izlibstream igzs(gzip_in, 4000); + igzs.exceptions(std::ios::failbit); + TS_ASSERT(igzs.good()); + + TS_ASSERT_THROWS_NOTHING(igzs >> &data); + TS_ASSERT(igzs); + TS_ASSERT(igzs.eof()); + TS_ASSERT_EQUALS(data.str(), bigtest.str()); + } + } + + void test_inflate_corrupt() + { + std::ifstream gzip_in("bigtest_corrupt.nbt", std::ios::binary); + TS_ASSERT(gzip_in); + + std::stringbuf data; + { + izlibstream igzs(gzip_in); + igzs.exceptions(std::ios::failbit); + TS_ASSERT_THROWS(igzs >> &data, zlib_error); + } + + gzip_in.close(); + gzip_in.open("bigtest_eof.nbt", std::ios::binary); + TS_ASSERT(gzip_in); + + data.str(""); + { + izlibstream igzs(gzip_in); + igzs.exceptions(std::ios::failbit); + TS_ASSERT_THROWS(igzs >> &data, zlib_error); + } + } +}; |
