// (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.



#ifndef BOOST_IOSTREAMS_DETAIL_BUFFERS_HPP_INCLUDED

#define BOOST_IOSTREAMS_DETAIL_BUFFERS_HPP_INCLUDED



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

# pragma once

#endif              



#include <algorithm>                           // swap.

#include <memory>                              // allocator.

#include <boost/config.hpp>                    // member templates.

#include <boost/iostreams/char_traits.hpp>

#include <boost/iostreams/detail/ios.hpp>      // streamsize.

#include <boost/iostreams/read.hpp>

#include <boost/iostreams/traits.hpp>          // int_type_of.

#include <boost/iostreams/checked_operations.hpp>

#include <boost/mpl/if.hpp>

#include <boost/type_traits/is_same.hpp>



namespace boost { namespace iostreams { namespace detail {



//----------------Buffers-----------------------------------------------------//



//

// Template name: buffer

// Description: Character buffer.

// Template paramters:

//     Ch - The character type.

//     Alloc - The Allocator type.

//

template< typename Ch,

          typename Alloc = std::allocator<Ch> >

class basic_buffer {

private:

#ifndef BOOST_NO_STD_ALLOCATOR

    typedef typename Alloc::template rebind<Ch>::other allocator_type;

#else

    typedef std::allocator<Ch> allocator_type;

#endif

public:

    basic_buffer();

    basic_buffer(int buffer_size);

    ~basic_buffer();

    void resize(int buffer_size);

    Ch* begin() const { return buf_; }

    Ch* end() const { return buf_ + size_; }

    Ch* data() const { return buf_; }

    std::streamsize size() const { return size_; }

    void swap(basic_buffer& rhs);

private:

    // Disallow copying and assignment.

    basic_buffer(const basic_buffer&);

    basic_buffer& operator=(const basic_buffer&);

    Ch*              buf_;

    std::streamsize  size_;

};



template<typename Ch, typename Alloc>

void swap(basic_buffer<Ch, Alloc>& lhs, basic_buffer<Ch, Alloc>& rhs)

{ lhs.swap(rhs); }



//

// Template name: buffer

// Description: Character buffer with two pointers accessible via ptr() and

//      eptr().

// Template paramters:

//     Ch - A character type.

//

template< typename Ch,

          typename Alloc = std::allocator<Ch> >

class buffer : public basic_buffer<Ch, Alloc> {

private:

    typedef basic_buffer<Ch, Alloc> base;

public:

    typedef iostreams::char_traits<Ch> traits_type;

    using base::resize; 

    using base::data; 

    using base::size;

    typedef Ch* const const_pointer;

    buffer(int buffer_size);

    Ch* & ptr() { return ptr_; }

    const_pointer& ptr() const { return ptr_; }

    Ch* & eptr() { return eptr_; }

    const_pointer& eptr() const { return eptr_; }

    void set(std::streamsize ptr, std::streamsize end);

    void swap(buffer& rhs);



    // Returns an int_type as a status code.

    template<typename Source>

    typename int_type_of<Source>::type fill(Source& src) 

    {

        using namespace std;

        std::streamsize keep;

        if ((keep = static_cast<std::streamsize>(eptr_ - ptr_)) > 0)

            traits_type::move(this->data(), ptr_, keep);

        set(0, keep);

        std::streamsize result = 

            iostreams::read(src, this->data() + keep, this->size() - keep);

        if (result != -1)

            this->set(0, keep + result);

        //return result == this->size() - keep ?

        //    traits_type::good() :

        //    keep == -1 ?

        //        traits_type::eof() :

        //        traits_type::would_block();

        return result == -1 ?

            traits_type::eof() :

                result == 0 ?

                    traits_type::would_block() :

                    traits_type::good();



    }



    // Returns true if one or more characters were written.

    template<typename Sink>

    bool flush(Sink& dest) 

    {

        using namespace std;

        std::streamsize amt = static_cast<std::streamsize>(eptr_ - ptr_);

        std::streamsize result = iostreams::write_if(dest, ptr_, amt);

        if (result < amt) {

            traits_type::move( this->data(), 

                               ptr_ + result, 

                               amt - result );

        }

        this->set(0, amt - result);

        return result != 0;

    }

private:

    Ch *ptr_, *eptr_;

};



template<typename Ch, typename Alloc>

void swap(buffer<Ch, Alloc>& lhs, buffer<Ch, Alloc>& rhs)

{ lhs.swap(rhs); }



//--------------Implementation of basic_buffer--------------------------------//



template<typename Ch, typename Alloc>

basic_buffer<Ch, Alloc>::basic_buffer() : buf_(0), size_(0) { }



template<typename Ch, typename Alloc>

basic_buffer<Ch, Alloc>::basic_buffer(int buffer_size)

    : buf_(static_cast<Ch*>(allocator_type().allocate(buffer_size, 0))), 

      size_(buffer_size) // Cast for SunPro 5.3.

    { }



template<typename Ch, typename Alloc>

inline basic_buffer<Ch, Alloc>::~basic_buffer()

{ if (buf_) allocator_type().deallocate(buf_, size_); }



template<typename Ch, typename Alloc>

inline void basic_buffer<Ch, Alloc>::resize(int buffer_size)

{

    if (size_ != buffer_size) {

        basic_buffer<Ch, Alloc> temp(buffer_size);

        std::swap(size_, temp.size_);

        std::swap(buf_, temp.buf_);

    }

}



template<typename Ch, typename Alloc>

void basic_buffer<Ch, Alloc>::swap(basic_buffer& rhs) 

{ 

    std::swap(buf_, rhs.buf_); 

    std::swap(size_, rhs.size_); 

}



//--------------Implementation of buffer--------------------------------------//



template<typename Ch, typename Alloc>

buffer<Ch, Alloc>::buffer(int buffer_size)

    : basic_buffer<Ch, Alloc>(buffer_size) { }



template<typename Ch, typename Alloc>

inline void buffer<Ch, Alloc>::set(std::streamsize ptr, std::streamsize end)

{ 

    ptr_ = data() + ptr; 

    eptr_ = data() + end; 

}



template<typename Ch, typename Alloc>

inline void buffer<Ch, Alloc>::swap(buffer& rhs) 

{ 

    base::swap(rhs); 

    std::swap(ptr_, rhs.ptr_); 

    std::swap(eptr_, rhs.eptr_); 

}



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



} } } // End namespaces detail, iostreams, boost.



#endif // #ifndef BOOST_IOSTREAMS_DETAIL_BUFFERS_HPP_INCLUDED

