// (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com)

// (C) Copyright 2003-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: custom allocators are not supported on VC6, since that compiler

// had trouble finding the function zlib_base::do_init.



#ifndef BOOST_IOSTREAMS_ZLIB_HPP_INCLUDED

#define BOOST_IOSTREAMS_ZLIB_HPP_INCLUDED



#if defined(_MSC_VER) && (_MSC_VER >= 1020)

# 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/config/zlib.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 zlib {

                    // Typedefs



typedef unsigned int uint;

typedef unsigned char byte;

typedef unsigned long ulong;



typedef void* (*alloc_func)(void*, zlib::uint, zlib::uint);

typedef void (*free_func)(void*, void*);



                    // Compression levels



BOOST_IOSTREAMS_DECL extern const int no_compression;

BOOST_IOSTREAMS_DECL extern const int best_speed;

BOOST_IOSTREAMS_DECL extern const int best_compression;

BOOST_IOSTREAMS_DECL extern const int default_compression;



                    // Compression methods



BOOST_IOSTREAMS_DECL extern const int deflated;



                    // Compression strategies



BOOST_IOSTREAMS_DECL extern const int default_strategy;

BOOST_IOSTREAMS_DECL extern const int filtered;

BOOST_IOSTREAMS_DECL extern const int huffman_only;



                    // Status codes



BOOST_IOSTREAMS_DECL extern const int okay;

BOOST_IOSTREAMS_DECL extern const int stream_end;

BOOST_IOSTREAMS_DECL extern const int stream_error;

BOOST_IOSTREAMS_DECL extern const int version_error;

BOOST_IOSTREAMS_DECL extern const int data_error;

BOOST_IOSTREAMS_DECL extern const int mem_error;

BOOST_IOSTREAMS_DECL extern const int buf_error;



                    // Flush codes



BOOST_IOSTREAMS_DECL extern const int finish;

BOOST_IOSTREAMS_DECL extern const int no_flush;

BOOST_IOSTREAMS_DECL extern const int sync_flush;



                    // Code for current OS



//BOOST_IOSTREAMS_DECL extern const int os_code;



                    // Null pointer constant.



const int null                               = 0;



                    // Default values



const int default_window_bits                = 15;

const int default_mem_level                  = 8;

const bool default_crc                       = false;

const bool default_noheader                  = false;



} // End namespace zlib. 



//

// Class name: zlib_params.

// Description: Encapsulates the parameters passed to deflateInit2

//      and inflateInit2 to customize compression and decompression.

//

struct zlib_params {



    // Non-explicit constructor.

    zlib_params( int level           = zlib::default_compression,

                 int method          = zlib::deflated,

                 int window_bits     = zlib::default_window_bits, 

                 int mem_level       = zlib::default_mem_level, 

                 int strategy        = zlib::default_strategy,

                 bool noheader       = zlib::default_noheader,

                 bool calculate_crc  = zlib::default_crc )

        : level(level), method(method), window_bits(window_bits),

          mem_level(mem_level), strategy(strategy),  

          noheader(noheader), calculate_crc(calculate_crc)

        { }

    int level;

    int method;

    int window_bits;

    int mem_level;

    int strategy;

    bool noheader;

    bool calculate_crc;

};



//

// Class name: zlib_error.

// Description: Subclass of std::ios::failure thrown to indicate

//     zlib errors other than out-of-memory conditions.

//

class BOOST_IOSTREAMS_DECL zlib_error : public BOOST_IOSTREAMS_FAILURE {

public:

    explicit zlib_error(int error);

    int error() const { return error_; }

    static void check(int error);

private:

    int error_;

};



namespace detail {



template<typename Alloc>

struct zlib_allocator_traits {

#ifndef BOOST_NO_STD_ALLOCATOR

    typedef typename Alloc::template rebind<char>::other type;

#else

    typedef std::allocator<char> type;

#endif

};



template< typename Alloc,

          typename Base = // VC6 workaround (C2516)

              BOOST_DEDUCED_TYPENAME zlib_allocator_traits<Alloc>::type >

struct zlib_allocator : private Base {

private:

    typedef typename Base::size_type size_type;

public:

    BOOST_STATIC_CONSTANT(bool, custom = 

        (!is_same<std::allocator<char>, Base>::value));

    typedef typename zlib_allocator_traits<Alloc>::type allocator_type;

    static void* allocate(void* self, zlib::uint items, zlib::uint size);

    static void deallocate(void* self, void* address);

};



class BOOST_IOSTREAMS_DECL zlib_base { 

public:

    typedef char char_type;

protected:

    zlib_base();

    ~zlib_base();

    void* stream() { return stream_; }

    template<typename Alloc> 

    void init( const zlib_params& p, 

               bool compress,

               zlib_allocator<Alloc>& zalloc )

        {

            bool custom = zlib_allocator<Alloc>::custom;

            do_init( p, compress,

                     #if !BOOST_WORKAROUND(BOOST_MSVC, < 1300)

                         custom ? zlib_allocator<Alloc>::allocate : 0,

                         custom ? zlib_allocator<Alloc>::deallocate : 0,

                     #endif

                     &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 flush);

    int inflate(int flush);

    void reset(bool compress, bool realloc);

public:

    zlib::ulong crc() const { return crc_; }

    int total_in() const { return total_in_; }

    int total_out() const { return total_out_; }

private:

    void do_init( const zlib_params& p, bool compress, 

                  #if !BOOST_WORKAROUND(BOOST_MSVC, < 1300)

                      zlib::alloc_func, 

                      zlib::free_func, 

                  #endif

                  void* derived );

    void*        stream_;         // Actual type: z_stream*.

    bool         calculate_crc_;

    zlib::ulong  crc_;

    int          total_in_;

    int          total_out_;

};



//

// Template name: zlib_compressor_impl

// Description: Model of C-Style Filte implementing compression by

//      delegating to the zlib function deflate.

//

template<typename Alloc = std::allocator<char> >

class zlib_compressor_impl : public zlib_base, public zlib_allocator<Alloc> { 

public: 

    zlib_compressor_impl(const zlib_params& = zlib::default_compression);

    ~zlib_compressor_impl();

    bool filter( const char*& src_begin, const char* src_end,

                 char*& dest_begin, char* dest_end, bool flush );

    void close();

};



//

// Template name: zlib_compressor

// Description: Model of C-Style Filte implementing decompression by

//      delegating to the zlib function inflate.

//

template<typename Alloc = std::allocator<char> >

class zlib_decompressor_impl : public zlib_base, public zlib_allocator<Alloc> {

public:

    zlib_decompressor_impl(const zlib_params&);

    zlib_decompressor_impl(int window_bits = zlib::default_window_bits);

    ~zlib_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: zlib_compressor

// Description: Model of InputFilter and OutputFilter implementing

//      compression using zlib.

//

template<typename Alloc = std::allocator<char> >

struct basic_zlib_compressor 

    : symmetric_filter<detail::zlib_compressor_impl<Alloc>, Alloc> 

{

private:

    typedef detail::zlib_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_zlib_compressor( const zlib_params& = zlib::default_compression, 

                           int buffer_size = default_device_buffer_size );

    zlib::ulong crc() { return this->filter().crc(); }

    int total_in() {  return this->filter().total_in(); }

};

BOOST_IOSTREAMS_PIPABLE(basic_zlib_compressor, 1)



typedef basic_zlib_compressor<> zlib_compressor;



//

// Template name: zlib_decompressor

// Description: Model of InputFilter and OutputFilter implementing

//      decompression using zlib.

//

template<typename Alloc = std::allocator<char> >

struct basic_zlib_decompressor 

    : symmetric_filter<detail::zlib_decompressor_impl<Alloc>, Alloc> 

{

private:

    typedef detail::zlib_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_zlib_decompressor( int window_bits = zlib::default_window_bits,

                             int buffer_size = default_device_buffer_size );

    basic_zlib_decompressor( const zlib_params& p,

                             int buffer_size = default_device_buffer_size );

    zlib::ulong crc() { return this->filter().crc(); }

    int total_out() {  return this->filter().total_out(); }

};

BOOST_IOSTREAMS_PIPABLE(basic_zlib_decompressor, 1)



typedef basic_zlib_decompressor<> zlib_decompressor;



//----------------------------------------------------------------------------//



//------------------Implementation of zlib_allocator--------------------------//



namespace detail {



template<typename Alloc, typename Base>

void* zlib_allocator<Alloc, Base>::allocate

    (void* self, zlib::uint items, zlib::uint 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 zlib_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 zlib_compressor_impl--------------------//



template<typename Alloc>

zlib_compressor_impl<Alloc>::zlib_compressor_impl(const zlib_params& p)

{ init(p, true, static_cast<zlib_allocator<Alloc>&>(*this)); }



template<typename Alloc>

zlib_compressor_impl<Alloc>::~zlib_compressor_impl()

{ reset(true, false); }



template<typename Alloc>

bool zlib_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 ? zlib::finish : zlib::no_flush);

    after(src_begin, dest_begin, true);

    zlib_error::check(result);

    return result != zlib::stream_end; 

}



template<typename Alloc>

void zlib_compressor_impl<Alloc>::close() { reset(true, true); }



//------------------Implementation of zlib_decompressor_impl------------------//



template<typename Alloc>

zlib_decompressor_impl<Alloc>::zlib_decompressor_impl(const zlib_params& p)

{ init(p, false, static_cast<zlib_allocator<Alloc>&>(*this)); }



template<typename Alloc>

zlib_decompressor_impl<Alloc>::~zlib_decompressor_impl()

{ reset(false, false); }



template<typename Alloc>

zlib_decompressor_impl<Alloc>::zlib_decompressor_impl(int window_bits)

{ 

    zlib_params p;

    p.window_bits = window_bits;

    init(p, false, static_cast<zlib_allocator<Alloc>&>(*this)); 

}



template<typename Alloc>

bool zlib_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(zlib::sync_flush);

    after(src_begin, dest_begin, false);

    zlib_error::check(result);

    return result != zlib::stream_end;

}



template<typename Alloc>

void zlib_decompressor_impl<Alloc>::close() { reset(false, true); }



} // End namespace detail.



//------------------Implementation of zlib_decompressor-----------------------//



template<typename Alloc>

basic_zlib_compressor<Alloc>::basic_zlib_compressor

    (const zlib_params& p, int buffer_size) 

    : base_type(buffer_size, p) { }



//------------------Implementation of zlib_decompressor-----------------------//



template<typename Alloc>

basic_zlib_decompressor<Alloc>::basic_zlib_decompressor

    (int window_bits, int buffer_size) 

    : base_type(buffer_size, window_bits) { }



template<typename Alloc>

basic_zlib_decompressor<Alloc>::basic_zlib_decompressor

    (const zlib_params& p, int 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_ZLIB_HPP_INCLUDED

