| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,363 @@ | ||
| // (C) Copyright Reimar Döffinger 2018. | ||
| // Based on zstd.hpp by: | ||
| // (C) Copyright Milan Svoboda 2008. | ||
| // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) | ||
|
|
||
| // See http://www.boost.org/libs/iostreams for documentation. | ||
|
|
||
| #ifndef BOOST_IOSTREAMS_ZSTD_HPP_INCLUDED | ||
| #define BOOST_IOSTREAMS_ZSTD_HPP_INCLUDED | ||
|
|
||
| #if defined(_MSC_VER) | ||
| # pragma once | ||
| #endif | ||
|
|
||
| #include <cassert> | ||
| #include <iosfwd> // streamsize. | ||
| #include <memory> // allocator, bad_alloc. | ||
| #include <new> | ||
| #include <boost/config.hpp> // MSVC, STATIC_CONSTANT, DEDUCED_TYPENAME, DINKUM. | ||
| #include <boost/detail/workaround.hpp> | ||
| #include <boost/iostreams/constants.hpp> // buffer size. | ||
| #include <boost/iostreams/detail/config/auto_link.hpp> | ||
| #include <boost/iostreams/detail/config/dyn_link.hpp> | ||
| #include <boost/iostreams/detail/config/wide_streams.hpp> | ||
| #include <boost/iostreams/detail/ios.hpp> // failure, streamsize. | ||
| #include <boost/iostreams/filter/symmetric.hpp> | ||
| #include <boost/iostreams/pipeline.hpp> | ||
| #include <boost/type_traits/is_same.hpp> | ||
|
|
||
| // Must come last. | ||
| #ifdef BOOST_MSVC | ||
| # pragma warning(push) | ||
| # pragma warning(disable:4251 4231 4660) // Dependencies not exported. | ||
| #endif | ||
| #include <boost/config/abi_prefix.hpp> | ||
|
|
||
| namespace boost { namespace iostreams { | ||
|
|
||
| namespace zstd { | ||
|
|
||
| typedef void* (*alloc_func)(void*, size_t, size_t); | ||
| typedef void (*free_func)(void*, void*); | ||
|
|
||
| // Compression levels | ||
|
|
||
| BOOST_IOSTREAMS_DECL extern const uint32_t best_speed; | ||
| BOOST_IOSTREAMS_DECL extern const uint32_t best_compression; | ||
| BOOST_IOSTREAMS_DECL extern const uint32_t default_compression; | ||
|
|
||
| // Status codes | ||
|
|
||
| BOOST_IOSTREAMS_DECL extern const int okay; | ||
| BOOST_IOSTREAMS_DECL extern const int stream_end; | ||
|
|
||
| // Flush codes | ||
|
|
||
| BOOST_IOSTREAMS_DECL extern const int finish; | ||
| BOOST_IOSTREAMS_DECL extern const int flush; | ||
| BOOST_IOSTREAMS_DECL extern const int run; | ||
|
|
||
| // Code for current OS | ||
|
|
||
| // Null pointer constant. | ||
|
|
||
| const int null = 0; | ||
|
|
||
| // Default values | ||
|
|
||
| } // End namespace zstd. | ||
|
|
||
| // | ||
| // Class name: zstd_params. | ||
| // Description: Encapsulates the parameters passed to zstddec_init | ||
| // to customize compression and decompression. | ||
| // | ||
| struct zstd_params { | ||
|
|
||
| // Non-explicit constructor. | ||
| zstd_params( uint32_t level = zstd::default_compression ) | ||
| : level(level) | ||
| { } | ||
| uint32_t level; | ||
| }; | ||
|
|
||
| // | ||
| // Class name: zstd_error. | ||
| // Description: Subclass of std::ios::failure thrown to indicate | ||
| // zstd errors other than out-of-memory conditions. | ||
| // | ||
| class BOOST_IOSTREAMS_DECL zstd_error : public BOOST_IOSTREAMS_FAILURE { | ||
| public: | ||
| explicit zstd_error(size_t error); | ||
| int error() const { return error_; } | ||
| static void check BOOST_PREVENT_MACRO_SUBSTITUTION(size_t error); | ||
| private: | ||
| size_t error_; | ||
| }; | ||
|
|
||
| namespace detail { | ||
|
|
||
| template<typename Alloc> | ||
| struct zstd_allocator_traits { | ||
| #ifndef BOOST_NO_STD_ALLOCATOR | ||
| #if defined(BOOST_NO_CXX11_ALLOCATOR) | ||
| typedef typename Alloc::template rebind<char>::other type; | ||
| #else | ||
| typedef typename std::allocator_traits<Alloc>::template rebind_alloc<char> type; | ||
| #endif | ||
| #else | ||
| typedef std::allocator<char> type; | ||
| #endif | ||
| }; | ||
|
|
||
| template< typename Alloc, | ||
| typename Base = // VC6 workaround (C2516) | ||
| BOOST_DEDUCED_TYPENAME zstd_allocator_traits<Alloc>::type > | ||
| struct zstd_allocator : private Base { | ||
| private: | ||
| #if defined(BOOST_NO_CXX11_ALLOCATOR) || defined(BOOST_NO_STD_ALLOCATOR) | ||
| typedef typename Base::size_type size_type; | ||
| #else | ||
| typedef typename std::allocator_traits<Base>::size_type size_type; | ||
| #endif | ||
| public: | ||
| BOOST_STATIC_CONSTANT(bool, custom = | ||
| (!is_same<std::allocator<char>, Base>::value)); | ||
| typedef typename zstd_allocator_traits<Alloc>::type allocator_type; | ||
| static void* allocate(void* self, size_t items, size_t size); | ||
| static void deallocate(void* self, void* address); | ||
| }; | ||
|
|
||
| class BOOST_IOSTREAMS_DECL zstd_base { | ||
| public: | ||
| typedef char char_type; | ||
| protected: | ||
| zstd_base(); | ||
| ~zstd_base(); | ||
| template<typename Alloc> | ||
| void init( const zstd_params& p, | ||
| bool compress, | ||
| zstd_allocator<Alloc>& zalloc ) | ||
| { | ||
| bool custom = zstd_allocator<Alloc>::custom; | ||
| do_init( p, compress, | ||
| custom ? zstd_allocator<Alloc>::allocate : 0, | ||
| custom ? zstd_allocator<Alloc>::deallocate : 0, | ||
| &zalloc ); | ||
| } | ||
| void before( const char*& src_begin, const char* src_end, | ||
| char*& dest_begin, char* dest_end ); | ||
| void after( const char*& src_begin, char*& dest_begin, | ||
| bool compress ); | ||
| int deflate(int action); | ||
| int inflate(int action); | ||
| void reset(bool compress, bool realloc); | ||
| private: | ||
| void do_init( const zstd_params& p, bool compress, | ||
| zstd::alloc_func, | ||
| zstd::free_func, | ||
| void* derived ); | ||
| void* cstream_; // Actual type: ZSTD_CStream * | ||
| void* dstream_; // Actual type: ZSTD_DStream * | ||
| void* in_; // Actual type: ZSTD_inBuffer * | ||
| void* out_; // Actual type: ZSTD_outBuffer * | ||
| int eof_; | ||
| uint32_t level; | ||
| }; | ||
|
|
||
| // | ||
| // Template name: zstd_compressor_impl | ||
| // Description: Model of C-Style Filter implementing compression by | ||
| // delegating to the zstd function deflate. | ||
| // | ||
| template<typename Alloc = std::allocator<char> > | ||
| class zstd_compressor_impl : public zstd_base, public zstd_allocator<Alloc> { | ||
| public: | ||
| zstd_compressor_impl(const zstd_params& = zstd::default_compression); | ||
| ~zstd_compressor_impl(); | ||
| bool filter( const char*& src_begin, const char* src_end, | ||
| char*& dest_begin, char* dest_end, bool flush ); | ||
| void close(); | ||
| }; | ||
|
|
||
| // | ||
| // Template name: zstd_compressor_impl | ||
| // Description: Model of C-Style Filte implementing decompression by | ||
| // delegating to the zstd function inflate. | ||
| // | ||
| template<typename Alloc = std::allocator<char> > | ||
| class zstd_decompressor_impl : public zstd_base, public zstd_allocator<Alloc> { | ||
| public: | ||
| zstd_decompressor_impl(const zstd_params&); | ||
| zstd_decompressor_impl(); | ||
| ~zstd_decompressor_impl(); | ||
| bool filter( const char*& begin_in, const char* end_in, | ||
| char*& begin_out, char* end_out, bool flush ); | ||
| void close(); | ||
| }; | ||
|
|
||
| } // End namespace detail. | ||
|
|
||
| // | ||
| // Template name: zstd_compressor | ||
| // Description: Model of InputFilter and OutputFilter implementing | ||
| // compression using zstd. | ||
| // | ||
| template<typename Alloc = std::allocator<char> > | ||
| struct basic_zstd_compressor | ||
| : symmetric_filter<detail::zstd_compressor_impl<Alloc>, Alloc> | ||
| { | ||
| private: | ||
| typedef detail::zstd_compressor_impl<Alloc> impl_type; | ||
| typedef symmetric_filter<impl_type, Alloc> base_type; | ||
| public: | ||
| typedef typename base_type::char_type char_type; | ||
| typedef typename base_type::category category; | ||
| basic_zstd_compressor( const zstd_params& = zstd::default_compression, | ||
| std::streamsize buffer_size = default_device_buffer_size ); | ||
| }; | ||
| BOOST_IOSTREAMS_PIPABLE(basic_zstd_compressor, 1) | ||
|
|
||
| typedef basic_zstd_compressor<> zstd_compressor; | ||
|
|
||
| // | ||
| // Template name: zstd_decompressor | ||
| // Description: Model of InputFilter and OutputFilter implementing | ||
| // decompression using zstd. | ||
| // | ||
| template<typename Alloc = std::allocator<char> > | ||
| struct basic_zstd_decompressor | ||
| : symmetric_filter<detail::zstd_decompressor_impl<Alloc>, Alloc> | ||
| { | ||
| private: | ||
| typedef detail::zstd_decompressor_impl<Alloc> impl_type; | ||
| typedef symmetric_filter<impl_type, Alloc> base_type; | ||
| public: | ||
| typedef typename base_type::char_type char_type; | ||
| typedef typename base_type::category category; | ||
| basic_zstd_decompressor( std::streamsize buffer_size = default_device_buffer_size ); | ||
| basic_zstd_decompressor( const zstd_params& p, | ||
| std::streamsize buffer_size = default_device_buffer_size ); | ||
| }; | ||
| BOOST_IOSTREAMS_PIPABLE(basic_zstd_decompressor, 1) | ||
|
|
||
| typedef basic_zstd_decompressor<> zstd_decompressor; | ||
|
|
||
| //----------------------------------------------------------------------------// | ||
|
|
||
| //------------------Implementation of zstd_allocator--------------------------// | ||
|
|
||
| namespace detail { | ||
|
|
||
| template<typename Alloc, typename Base> | ||
| void* zstd_allocator<Alloc, Base>::allocate | ||
| (void* self, size_t items, size_t size) | ||
| { | ||
| size_type len = items * size; | ||
| char* ptr = | ||
| static_cast<allocator_type*>(self)->allocate | ||
| (len + sizeof(size_type) | ||
| #if BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB, == 1) | ||
| , (char*)0 | ||
| #endif | ||
| ); | ||
| *reinterpret_cast<size_type*>(ptr) = len; | ||
| return ptr + sizeof(size_type); | ||
| } | ||
|
|
||
| template<typename Alloc, typename Base> | ||
| void zstd_allocator<Alloc, Base>::deallocate(void* self, void* address) | ||
| { | ||
| char* ptr = reinterpret_cast<char*>(address) - sizeof(size_type); | ||
| size_type len = *reinterpret_cast<size_type*>(ptr) + sizeof(size_type); | ||
| static_cast<allocator_type*>(self)->deallocate(ptr, len); | ||
| } | ||
|
|
||
| //------------------Implementation of zstd_compressor_impl--------------------// | ||
|
|
||
| template<typename Alloc> | ||
| zstd_compressor_impl<Alloc>::zstd_compressor_impl(const zstd_params& p) | ||
| { init(p, true, static_cast<zstd_allocator<Alloc>&>(*this)); } | ||
|
|
||
| template<typename Alloc> | ||
| zstd_compressor_impl<Alloc>::~zstd_compressor_impl() | ||
| { reset(true, false); } | ||
|
|
||
| template<typename Alloc> | ||
| bool zstd_compressor_impl<Alloc>::filter | ||
| ( const char*& src_begin, const char* src_end, | ||
| char*& dest_begin, char* dest_end, bool flush ) | ||
| { | ||
| before(src_begin, src_end, dest_begin, dest_end); | ||
| int result = deflate(flush ? zstd::finish : zstd::run); | ||
| after(src_begin, dest_begin, true); | ||
| return result != zstd::stream_end; | ||
| } | ||
|
|
||
| template<typename Alloc> | ||
| void zstd_compressor_impl<Alloc>::close() { reset(true, true); } | ||
|
|
||
| //------------------Implementation of zstd_decompressor_impl------------------// | ||
|
|
||
| template<typename Alloc> | ||
| zstd_decompressor_impl<Alloc>::zstd_decompressor_impl(const zstd_params& p) | ||
| { init(p, false, static_cast<zstd_allocator<Alloc>&>(*this)); } | ||
|
|
||
| template<typename Alloc> | ||
| zstd_decompressor_impl<Alloc>::~zstd_decompressor_impl() | ||
| { reset(false, false); } | ||
|
|
||
| template<typename Alloc> | ||
| zstd_decompressor_impl<Alloc>::zstd_decompressor_impl() | ||
| { | ||
| zstd_params p; | ||
| init(p, false, static_cast<zstd_allocator<Alloc>&>(*this)); | ||
| } | ||
|
|
||
| template<typename Alloc> | ||
| bool zstd_decompressor_impl<Alloc>::filter | ||
| ( const char*& src_begin, const char* src_end, | ||
| char*& dest_begin, char* dest_end, bool flush ) | ||
| { | ||
| before(src_begin, src_end, dest_begin, dest_end); | ||
| int result = inflate(flush ? zstd::finish : zstd::run); | ||
| after(src_begin, dest_begin, false); | ||
| return result != zstd::stream_end; | ||
| } | ||
|
|
||
| template<typename Alloc> | ||
| void zstd_decompressor_impl<Alloc>::close() { reset(false, true); } | ||
|
|
||
| } // End namespace detail. | ||
|
|
||
| //------------------Implementation of zstd_compressor-----------------------// | ||
|
|
||
| template<typename Alloc> | ||
| basic_zstd_compressor<Alloc>::basic_zstd_compressor | ||
| (const zstd_params& p, std::streamsize buffer_size) | ||
| : base_type(buffer_size, p) { } | ||
|
|
||
| //------------------Implementation of zstd_decompressor-----------------------// | ||
|
|
||
| template<typename Alloc> | ||
| basic_zstd_decompressor<Alloc>::basic_zstd_decompressor | ||
| (std::streamsize buffer_size) | ||
| : base_type(buffer_size) { } | ||
|
|
||
| template<typename Alloc> | ||
| basic_zstd_decompressor<Alloc>::basic_zstd_decompressor | ||
| (const zstd_params& p, std::streamsize buffer_size) | ||
| : base_type(buffer_size, p) { } | ||
|
|
||
| //----------------------------------------------------------------------------// | ||
|
|
||
| } } // End namespaces iostreams, boost. | ||
|
|
||
| #include <boost/config/abi_suffix.hpp> // Pops abi_suffix.hpp pragmas. | ||
| #ifdef BOOST_MSVC | ||
| # pragma warning(pop) | ||
| #endif | ||
|
|
||
| #endif // #ifndef BOOST_IOSTREAMS_ZSTD_HPP_INCLUDED |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| // (C) Copyright Reimar Döffinger 2018. | ||
| // Based on zstd.cpp by: | ||
| // (C) Copyright Milan Svoboda 2008. | ||
| // (C) Copyright Jonathan Turkanis 2003. | ||
| // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) | ||
|
|
||
| // See http://www.boost.org/libs/iostreams for documentation. | ||
|
|
||
| // Define BOOST_IOSTREAMS_SOURCE so that <boost/iostreams/detail/config.hpp> | ||
| // knows that we are building the library (possibly exporting code), rather | ||
| // than using it (possibly importing code). | ||
| #define BOOST_IOSTREAMS_SOURCE | ||
|
|
||
| #include <zstd.h> | ||
|
|
||
| #include <boost/throw_exception.hpp> | ||
| #include <boost/iostreams/detail/config/dyn_link.hpp> | ||
| #include <boost/iostreams/filter/zstd.hpp> | ||
|
|
||
| namespace boost { namespace iostreams { | ||
|
|
||
| namespace zstd { | ||
| // Compression levels | ||
|
|
||
| const uint32_t best_speed = 1; | ||
| const uint32_t best_compression = 19; | ||
| const uint32_t default_compression = 3; | ||
|
|
||
| // Status codes | ||
|
|
||
| const int okay = 0; | ||
| const int stream_end = 1; | ||
|
|
||
| // Flush codes | ||
|
|
||
| const int finish = 0; | ||
| const int flush = 1; | ||
| const int run = 2; | ||
| } // End namespace zstd. | ||
|
|
||
| //------------------Implementation of zstd_error------------------------------// | ||
|
|
||
| zstd_error::zstd_error(size_t error) | ||
| : BOOST_IOSTREAMS_FAILURE(ZSTD_getErrorName(error)), error_(error) | ||
| { } | ||
|
|
||
| void zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(size_t error) | ||
| { | ||
| if (ZSTD_isError(error)) | ||
| boost::throw_exception(zstd_error(error)); | ||
| } | ||
|
|
||
| //------------------Implementation of zstd_base-------------------------------// | ||
|
|
||
| namespace detail { | ||
|
|
||
| zstd_base::zstd_base() | ||
| : cstream_(ZSTD_createCStream()), dstream_(ZSTD_createDStream()), in_(new ZSTD_inBuffer), out_(new ZSTD_outBuffer), eof_(0) | ||
| { } | ||
|
|
||
| zstd_base::~zstd_base() | ||
| { | ||
| ZSTD_freeCStream(static_cast<ZSTD_CStream *>(cstream_)); | ||
| ZSTD_freeDStream(static_cast<ZSTD_DStream *>(dstream_)); | ||
| delete static_cast<ZSTD_inBuffer*>(in_); | ||
| delete static_cast<ZSTD_outBuffer*>(out_); | ||
| } | ||
|
|
||
| void zstd_base::before( const char*& src_begin, const char* src_end, | ||
| char*& dest_begin, char* dest_end ) | ||
| { | ||
| ZSTD_inBuffer *in = static_cast<ZSTD_inBuffer *>(in_); | ||
| ZSTD_outBuffer *out = static_cast<ZSTD_outBuffer *>(out_); | ||
| in->src = src_begin; | ||
| in->size = static_cast<size_t>(src_end - src_begin); | ||
| in->pos = 0; | ||
| out->dst = dest_begin; | ||
| out->size = static_cast<size_t>(dest_end - dest_begin); | ||
| out->pos = 0; | ||
| } | ||
|
|
||
| void zstd_base::after(const char*& src_begin, char*& dest_begin, bool) | ||
| { | ||
| ZSTD_inBuffer *in = static_cast<ZSTD_inBuffer *>(in_); | ||
| ZSTD_outBuffer *out = static_cast<ZSTD_outBuffer *>(out_); | ||
| src_begin = reinterpret_cast<const char*>(in->src) + in->pos; | ||
| dest_begin = reinterpret_cast<char*>(out->dst) + out->pos; | ||
| } | ||
|
|
||
| int zstd_base::deflate(int action) | ||
| { | ||
| ZSTD_CStream *s = static_cast<ZSTD_CStream *>(cstream_); | ||
| ZSTD_inBuffer *in = static_cast<ZSTD_inBuffer *>(in_); | ||
| ZSTD_outBuffer *out = static_cast<ZSTD_outBuffer *>(out_); | ||
| // Ignore spurious extra calls. | ||
| // Note size > 0 will trigger an error in this case. | ||
| if (eof_ && in->size == 0) return zstd::stream_end; | ||
| size_t result = ZSTD_compressStream(s, out, in); | ||
| zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(result); | ||
| if (action != zstd::run) | ||
| { | ||
| result = action == zstd::finish ? ZSTD_endStream(s, out) : ZSTD_flushStream(s, out); | ||
| zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(result); | ||
| eof_ = action == zstd::finish && result == 0; | ||
| return result == 0 ? zstd::stream_end : zstd::okay; | ||
| } | ||
| return zstd::okay; | ||
| } | ||
|
|
||
| int zstd_base::inflate(int action) | ||
| { | ||
| ZSTD_DStream *s = static_cast<ZSTD_DStream *>(dstream_); | ||
| ZSTD_inBuffer *in = static_cast<ZSTD_inBuffer *>(in_); | ||
| ZSTD_outBuffer *out = static_cast<ZSTD_outBuffer *>(out_); | ||
| // need loop since iostream code cannot handle short reads | ||
| do { | ||
| size_t result = ZSTD_decompressStream(s, out, in); | ||
| zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(result); | ||
| } while (in->pos < in->size && out->pos < out->size); | ||
| return action == zstd::finish && in->size == 0 && out->pos == 0 ? zstd::stream_end : zstd::okay; | ||
| } | ||
|
|
||
| void zstd_base::reset(bool compress, bool realloc) | ||
| { | ||
| ZSTD_inBuffer *in = static_cast<ZSTD_inBuffer *>(in_); | ||
| ZSTD_outBuffer *out = static_cast<ZSTD_outBuffer *>(out_); | ||
| if (realloc) | ||
| { | ||
| memset(in, 0, sizeof(*in)); | ||
| memset(out, 0, sizeof(*out)); | ||
| eof_ = 0; | ||
|
|
||
| zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION( | ||
| compress ? | ||
| ZSTD_initCStream(static_cast<ZSTD_CStream *>(cstream_), level) : | ||
| ZSTD_initDStream(static_cast<ZSTD_DStream *>(dstream_)) | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| void zstd_base::do_init | ||
| ( const zstd_params& p, bool compress, | ||
| zstd::alloc_func, zstd::free_func, | ||
| void* ) | ||
| { | ||
| ZSTD_inBuffer *in = static_cast<ZSTD_inBuffer *>(in_); | ||
| ZSTD_outBuffer *out = static_cast<ZSTD_outBuffer *>(out_); | ||
|
|
||
| memset(in, 0, sizeof(*in)); | ||
| memset(out, 0, sizeof(*out)); | ||
| eof_ = 0; | ||
|
|
||
| level = p.level; | ||
| zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION( | ||
| compress ? | ||
| ZSTD_initCStream(static_cast<ZSTD_CStream *>(cstream_), level) : | ||
| ZSTD_initDStream(static_cast<ZSTD_DStream *>(dstream_)) | ||
| ); | ||
| } | ||
|
|
||
| } // End namespace detail. | ||
|
|
||
| //----------------------------------------------------------------------------// | ||
|
|
||
| } } // End namespaces iostreams, boost. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| // (C) COPYRIGHT 2018 Reimar Döffinger | ||
| // Based on zstd_test.cpp by: | ||
| // (C) COPYRIGHT 2017 ARM Limited | ||
| // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) | ||
| // (C) Copyright 2004-2007 Jonathan Turkanis | ||
| // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) | ||
|
|
||
| // See http://www.boost.org/libs/iostreams for documentation. | ||
|
|
||
| // Note: basically a copy-paste of the gzip test | ||
|
|
||
| #include <cstddef> | ||
| #include <string> | ||
| #include <boost/iostreams/copy.hpp> | ||
| #include <boost/iostreams/device/array.hpp> | ||
| #include <boost/iostreams/device/back_inserter.hpp> | ||
| #include <boost/iostreams/filter/zstd.hpp> | ||
| #include <boost/iostreams/filter/test.hpp> | ||
| #include <boost/iostreams/filtering_stream.hpp> | ||
| #include <boost/ref.hpp> | ||
| #include <boost/range/iterator_range.hpp> | ||
| #include <boost/test/test_tools.hpp> | ||
| #include <boost/test/unit_test.hpp> | ||
| #include "detail/sequence.hpp" | ||
| #include "detail/verification.hpp" | ||
|
|
||
| using namespace boost; | ||
| using namespace boost::iostreams; | ||
| using namespace boost::iostreams::test; | ||
| namespace io = boost::iostreams; | ||
| using boost::unit_test::test_suite; | ||
|
|
||
| struct zstd_alloc : std::allocator<char> { | ||
| zstd_alloc() { } | ||
| zstd_alloc(const zstd_alloc& other) { } | ||
| template<typename T> | ||
| zstd_alloc(const std::allocator<T>& other) { } | ||
| }; | ||
|
|
||
| void compression_test() | ||
| { | ||
| text_sequence data; | ||
|
|
||
| // Test compression and decompression with custom allocator | ||
| BOOST_CHECK( | ||
| test_filter_pair( basic_zstd_compressor<zstd_alloc>(), | ||
| basic_zstd_decompressor<zstd_alloc>(), | ||
| std::string(data.begin(), data.end()) ) | ||
| ); | ||
| } | ||
|
|
||
| void multiple_member_test() | ||
| { | ||
| text_sequence data; | ||
| std::vector<char> temp, dest; | ||
|
|
||
| // Write compressed data to temp, twice in succession | ||
| filtering_ostream out; | ||
| out.push(zstd_compressor()); | ||
| out.push(io::back_inserter(temp)); | ||
| io::copy(make_iterator_range(data), out); | ||
| out.push(io::back_inserter(temp)); | ||
| io::copy(make_iterator_range(data), out); | ||
| BOOST_CHECK(std::equal(temp.begin(), temp.begin() + temp.size()/2, temp.begin() + temp.size()/2)); | ||
|
|
||
| // Read compressed data from temp into dest | ||
| filtering_istream in; | ||
| in.push(zstd_decompressor()); | ||
| in.push(array_source(&temp[0], temp.size())); | ||
| io::copy(in, io::back_inserter(dest)); | ||
|
|
||
| // Check that dest consists of two copies of data | ||
| BOOST_REQUIRE_EQUAL(data.size() * 2, dest.size()); | ||
| BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin())); | ||
| BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin() + dest.size() / 2)); | ||
|
|
||
| dest.clear(); | ||
| io::copy( | ||
| array_source(&temp[0], temp.size()), | ||
| io::compose(zstd_decompressor(), io::back_inserter(dest))); | ||
|
|
||
| // Check that dest consists of two copies of data | ||
| BOOST_REQUIRE_EQUAL(data.size() * 2, dest.size()); | ||
| BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin())); | ||
| BOOST_CHECK(std::equal(data.begin(), data.end(), dest.begin() + dest.size() / 2)); | ||
| } | ||
|
|
||
| void array_source_test() | ||
| { | ||
| std::string data = "simple test string."; | ||
| std::string encoded; | ||
|
|
||
| filtering_ostream out; | ||
| out.push(zstd_compressor()); | ||
| out.push(io::back_inserter(encoded)); | ||
| io::copy(make_iterator_range(data), out); | ||
|
|
||
| std::string res; | ||
| io::array_source src(encoded.data(),encoded.length()); | ||
| io::copy(io::compose(io::zstd_decompressor(), src), io::back_inserter(res)); | ||
|
|
||
| BOOST_CHECK_EQUAL(data, res); | ||
| } | ||
|
|
||
| void empty_file_test() | ||
| { | ||
| // This test is in response to https://svn.boost.org/trac/boost/ticket/5237 | ||
| // The previous implementation of gzip_compressor only wrote the gzip file | ||
| // header when the first bytes of uncompressed input were processed, causing | ||
| // incorrect behavior for empty files | ||
| BOOST_CHECK( | ||
| test_filter_pair( zstd_compressor(), | ||
| zstd_decompressor(), | ||
| std::string() ) | ||
| ); | ||
| } | ||
|
|
||
| void multipart_test() | ||
| { | ||
| // This test verifies that the zstd_decompressor properly handles a file | ||
| // that consists of multiple concatenated files (matches unzstd behaviour) | ||
| static const char multipart_file[] = { | ||
| '\x28', '\xb5', '\x2f', '\xfd', '\x24', '\x15', '\x95', '\x00', '\x00', '\x50', '\x4c', '\x69', | ||
| '\x6e', '\x65', '\x20', '\x31', '\x0a', '\x32', '\x33', '\x0a', '\x02', '\x00', '\x60', '\x84', | ||
| '\xae', '\x62', '\x04', '\x19', '\xf8', '\xe1', '\x2d', '\x28', '\xb5', '\x2f', '\xfd', '\x24', | ||
| '\x15', '\x95', '\x00', '\x00', '\x50', '\x4c', '\x69', '\x6e', '\x65', '\x20', '\x34', '\x0a', | ||
| '\x35', '\x36', '\x0a', '\x02', '\x00', '\x60', '\x84', '\xae', '\x62', '\x04', '\x5f', '\xcf', | ||
| '\xd5', '\xb8', '\x28', '\xb5', '\x2f', '\xfd', '\x24', '\x00', '\x01', '\x00', '\x00', '\x99', | ||
| '\xe9', '\xd8', '\x51', '\x28', '\xb5', '\x2f', '\xfd', '\x24', '\x15', '\x95', '\x00', '\x00', | ||
| '\x50', '\x4c', '\x69', '\x6e', '\x65', '\x20', '\x37', '\x0a', '\x38', '\x39', '\x0a', '\x02', | ||
| '\x00', '\x60', '\x84', '\xae', '\x62', '\x04', '\x94', '\x13', '\xdb', '\xae' | ||
| }; | ||
|
|
||
| filtering_istream in; | ||
| std::string line; | ||
|
|
||
| in.push(zstd_decompressor()); | ||
| in.push(io::array_source(multipart_file, sizeof(multipart_file))); | ||
|
|
||
| // First part | ||
| std::getline(in, line); | ||
| BOOST_CHECK_EQUAL("Line 1", line); | ||
| std::getline(in, line); | ||
| BOOST_CHECK_EQUAL("Line 2", line); | ||
| std::getline(in, line); | ||
| BOOST_CHECK_EQUAL("Line 3", line); | ||
|
|
||
| // Second part immediately follows | ||
| std::getline(in, line); | ||
| BOOST_CHECK_EQUAL("Line 4", line); | ||
| std::getline(in, line); | ||
| BOOST_CHECK_EQUAL("Line 5", line); | ||
| std::getline(in, line); | ||
| BOOST_CHECK_EQUAL("Line 6", line); | ||
|
|
||
| // Then an empty part, followed by one last 3-line part. | ||
| std::getline(in, line); | ||
| BOOST_CHECK_EQUAL("Line 7", line); | ||
| std::getline(in, line); | ||
| BOOST_CHECK_EQUAL("Line 8", line); | ||
| std::getline(in, line); | ||
| BOOST_CHECK_EQUAL("Line 9", line); | ||
|
|
||
| // Check for zstd errors too. | ||
| BOOST_CHECK(!in.bad()); | ||
| } | ||
|
|
||
| test_suite* init_unit_test_suite(int, char* []) | ||
| { | ||
| test_suite* test = BOOST_TEST_SUITE("zstd test"); | ||
| test->add(BOOST_TEST_CASE(&compression_test)); | ||
| test->add(BOOST_TEST_CASE(&multiple_member_test)); | ||
| test->add(BOOST_TEST_CASE(&array_source_test)); | ||
| test->add(BOOST_TEST_CASE(&empty_file_test)); | ||
| test->add(BOOST_TEST_CASE(&multipart_test)); | ||
| return test; | ||
| } |