Chris@16: // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) Chris@16: // (C) Copyright 2003-2007 Jonathan Turkanis Chris@16: // Distributed under the Boost Software License, Version 1.0. (See accompanying Chris@16: // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) Chris@16: Chris@16: // See http://www.boost.org/libs/iostreams for documentation. Chris@16: Chris@16: // Contains machinery for performing code conversion. Chris@16: Chris@16: #ifndef BOOST_IOSTREAMS_CODE_CONVERTER_HPP_INCLUDED Chris@16: #define BOOST_IOSTREAMS_CODE_CONVERTER_HPP_INCLUDED Chris@16: Chris@16: #if defined(_MSC_VER) && (_MSC_VER >= 1020) Chris@16: # pragma once Chris@16: #endif Chris@16: Chris@16: #include Chris@16: #if defined(BOOST_IOSTREAMS_NO_WIDE_STREAMS) || \ Chris@16: defined(BOOST_IOSTREAMS_NO_LOCALE) \ Chris@16: /**/ Chris@16: # error code conversion not supported on this platform Chris@16: #endif Chris@16: Chris@16: #include // max. Chris@16: #include // memcpy. Chris@16: #include Chris@16: #include // DEDUCED_TYPENAME, Chris@16: #include Chris@16: #include // default_filter_buffer_size. Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include // failure, openmode, int types. Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: // Must come last. Chris@16: #include // Borland 5.x Chris@16: Chris@16: namespace boost { namespace iostreams { Chris@16: Chris@16: struct code_conversion_error : BOOST_IOSTREAMS_FAILURE { Chris@16: code_conversion_error() Chris@16: : BOOST_IOSTREAMS_FAILURE("code conversion error") Chris@16: { } Chris@16: }; Chris@16: Chris@16: namespace detail { Chris@16: Chris@16: //--------------Definition of strncpy_if_same---------------------------------// Chris@16: Chris@16: // Helper template for strncpy_if_same, below. Chris@16: template Chris@16: struct strncpy_if_same_impl; Chris@16: Chris@16: template<> Chris@16: struct strncpy_if_same_impl { Chris@16: template Chris@16: static Ch* copy(Ch* tgt, const Ch* src, std::streamsize n) Chris@16: { return BOOST_IOSTREAMS_CHAR_TRAITS(Ch)::copy(tgt, src, n); } Chris@16: }; Chris@16: Chris@16: template<> Chris@16: struct strncpy_if_same_impl { Chris@16: template Chris@16: static Tgt* copy(Tgt* tgt, const Src*, std::streamsize) { return tgt; } Chris@16: }; Chris@16: Chris@16: template Chris@16: Tgt* strncpy_if_same(Tgt* tgt, const Src* src, std::streamsize n) Chris@16: { Chris@16: typedef strncpy_if_same_impl::value> impl; Chris@16: return impl::copy(tgt, src, n); Chris@16: } Chris@16: Chris@16: //--------------Definition of conversion_buffer-------------------------------// Chris@16: Chris@16: // Buffer and conversion state for reading. Chris@16: template Chris@16: class conversion_buffer Chris@16: : public buffer< Chris@16: BOOST_DEDUCED_TYPENAME detail::codecvt_extern::type, Chris@16: Alloc Chris@16: > Chris@16: { Chris@16: public: Chris@16: typedef typename Codecvt::state_type state_type; Chris@16: conversion_buffer() Chris@16: : buffer< Chris@16: BOOST_DEDUCED_TYPENAME detail::codecvt_extern::type, Chris@16: Alloc Chris@16: >(0) Chris@16: { Chris@16: reset(); Chris@16: } Chris@16: state_type& state() { return state_; } Chris@16: void reset() Chris@16: { Chris@16: if (this->size()) Chris@16: this->set(0, 0); Chris@16: state_ = state_type(); Chris@16: } Chris@16: private: Chris@16: state_type state_; Chris@16: }; Chris@16: Chris@16: //--------------Definition of converter_impl----------------------------------// Chris@16: Chris@16: // Contains member data, open/is_open/close and buffer management functions. Chris@16: template Chris@16: struct code_converter_impl { Chris@16: typedef typename codecvt_extern::type extern_type; Chris@16: typedef typename category_of::type device_category; Chris@16: typedef is_convertible can_read; Chris@16: typedef is_convertible can_write; Chris@16: typedef is_convertible is_bidir; Chris@16: typedef typename Chris@16: iostreams::select< // Disambiguation for Tru64. Chris@16: is_bidir, bidirectional, Chris@16: can_read, input, Chris@16: can_write, output Chris@16: >::type mode; Chris@16: typedef typename Chris@16: mpl::if_< Chris@16: is_direct, Chris@16: direct_adapter, Chris@16: Device Chris@16: >::type device_type; Chris@16: typedef optional< concept_adapter > storage_type; Chris@16: typedef is_convertible is_double; Chris@16: typedef conversion_buffer buffer_type; Chris@16: Chris@16: code_converter_impl() : cvt_(), flags_(0) { } Chris@16: Chris@16: ~code_converter_impl() Chris@16: { Chris@16: try { Chris@16: if (flags_ & f_open) close(); Chris@16: } catch (...) { /* */ } Chris@16: } Chris@16: Chris@16: template Chris@16: void open(const T& dev, int buffer_size) Chris@16: { Chris@16: if (flags_ & f_open) Chris@16: boost::throw_exception(BOOST_IOSTREAMS_FAILURE("already open")); Chris@16: if (buffer_size == -1) Chris@16: buffer_size = default_filter_buffer_size; Chris@16: int max_length = cvt_.get().max_length(); Chris@16: buffer_size = (std::max)(buffer_size, 2 * max_length); Chris@16: if (can_read::value) { Chris@16: buf_.first().resize(buffer_size); Chris@16: buf_.first().set(0, 0); Chris@16: } Chris@16: if (can_write::value && !is_double::value) { Chris@16: buf_.second().resize(buffer_size); Chris@16: buf_.second().set(0, 0); Chris@16: } Chris@16: dev_.reset(concept_adapter(dev)); Chris@16: flags_ = f_open; Chris@16: } Chris@16: Chris@16: void close() Chris@16: { Chris@16: detail::execute_all( Chris@16: detail::call_member_close(*this, BOOST_IOS::in), Chris@16: detail::call_member_close(*this, BOOST_IOS::out) Chris@16: ); Chris@16: } Chris@16: Chris@16: void close(BOOST_IOS::openmode which) Chris@16: { Chris@16: if (which == BOOST_IOS::in && (flags_ & f_input_closed) == 0) { Chris@16: flags_ |= f_input_closed; Chris@16: iostreams::close(dev(), BOOST_IOS::in); Chris@16: } Chris@16: if (which == BOOST_IOS::out && (flags_ & f_output_closed) == 0) { Chris@16: flags_ |= f_output_closed; Chris@16: detail::execute_all( Chris@16: detail::flush_buffer(buf_.second(), dev(), can_write::value), Chris@16: detail::call_close(dev(), BOOST_IOS::out), Chris@16: detail::call_reset(dev_), Chris@16: detail::call_reset(buf_.first()), Chris@16: detail::call_reset(buf_.second()) Chris@16: ); Chris@16: } Chris@16: } Chris@16: Chris@16: bool is_open() const { return (flags_ & f_open) != 0;} Chris@16: Chris@16: device_type& dev() { return **dev_; } Chris@16: Chris@16: enum flag_type { Chris@16: f_open = 1, Chris@16: f_input_closed = f_open << 1, Chris@16: f_output_closed = f_input_closed << 1 Chris@16: }; Chris@16: Chris@16: codecvt_holder cvt_; Chris@16: storage_type dev_; Chris@16: double_object< Chris@16: buffer_type, Chris@16: is_double Chris@16: > buf_; Chris@16: int flags_; Chris@16: }; Chris@16: Chris@16: } // End namespace detail. Chris@16: Chris@16: //--------------Definition of converter---------------------------------------// Chris@16: Chris@16: #define BOOST_IOSTREAMS_CONVERTER_PARAMS() , int buffer_size = -1 Chris@16: #define BOOST_IOSTREAMS_CONVERTER_ARGS() , buffer_size Chris@16: Chris@16: template Chris@16: struct code_converter_base { Chris@16: typedef detail::code_converter_impl< Chris@16: Device, Codecvt, Alloc Chris@16: > impl_type; Chris@16: code_converter_base() : pimpl_(new impl_type) { } Chris@16: shared_ptr pimpl_; Chris@16: }; Chris@16: Chris@16: template< typename Device, Chris@16: typename Codecvt = detail::default_codecvt, Chris@16: typename Alloc = std::allocator > Chris@16: class code_converter Chris@16: : protected code_converter_base Chris@16: { Chris@16: private: Chris@16: typedef detail::code_converter_impl< Chris@16: Device, Codecvt, Alloc Chris@16: > impl_type; Chris@16: typedef typename impl_type::device_type device_type; Chris@16: typedef typename impl_type::buffer_type buffer_type; Chris@16: typedef typename detail::codecvt_holder::codecvt_type codecvt_type; Chris@16: typedef typename detail::codecvt_intern::type intern_type; Chris@16: typedef typename detail::codecvt_extern::type extern_type; Chris@16: typedef typename detail::codecvt_state::type state_type; Chris@16: public: Chris@16: typedef intern_type char_type; Chris@16: struct category Chris@16: : impl_type::mode, device_tag, closable_tag, localizable_tag Chris@16: { }; Chris@16: BOOST_STATIC_ASSERT(( Chris@16: is_same< Chris@16: extern_type, Chris@16: BOOST_DEDUCED_TYPENAME char_type_of::type Chris@16: >::value Chris@16: )); Chris@16: public: Chris@16: code_converter() { } Chris@16: #if BOOST_WORKAROUND(__GNUC__, < 3) Chris@16: code_converter(code_converter& rhs) Chris@16: : code_converter_base(rhs) Chris@16: { } Chris@16: code_converter(const code_converter& rhs) Chris@16: : code_converter_base(rhs) Chris@16: { } Chris@16: #endif Chris@16: BOOST_IOSTREAMS_FORWARD( code_converter, open_impl, Device, Chris@16: BOOST_IOSTREAMS_CONVERTER_PARAMS, Chris@16: BOOST_IOSTREAMS_CONVERTER_ARGS ) Chris@16: Chris@16: // fstream-like interface. Chris@16: Chris@16: bool is_open() const { return this->pimpl_->is_open(); } Chris@16: void close(BOOST_IOS::openmode which = BOOST_IOS::in | BOOST_IOS::out ) Chris@16: { impl().close(which); } Chris@16: Chris@16: // Device interface. Chris@16: Chris@16: std::streamsize read(char_type*, std::streamsize); Chris@16: std::streamsize write(const char_type*, std::streamsize); Chris@16: void imbue(const std::locale& loc) { impl().cvt_.imbue(loc); } Chris@16: Chris@16: // Direct device access. Chris@16: Chris@16: Device& operator*() { return detail::unwrap_direct(dev()); } Chris@16: Device* operator->() { return &detail::unwrap_direct(dev()); } Chris@16: private: Chris@16: template // Used for forwarding. Chris@16: void open_impl(const T& t BOOST_IOSTREAMS_CONVERTER_PARAMS()) Chris@16: { Chris@16: impl().open(t BOOST_IOSTREAMS_CONVERTER_ARGS()); Chris@16: } Chris@16: Chris@16: const codecvt_type& cvt() { return impl().cvt_.get(); } Chris@16: device_type& dev() { return impl().dev(); } Chris@16: buffer_type& in() { return impl().buf_.first(); } Chris@16: buffer_type& out() { return impl().buf_.second(); } Chris@16: impl_type& impl() { return *this->pimpl_; } Chris@16: }; Chris@16: Chris@16: //--------------Implementation of converter-----------------------------------// Chris@16: Chris@16: // Implementation note: if end of stream contains a partial character, Chris@16: // it is ignored. Chris@16: template Chris@16: std::streamsize code_converter::read Chris@16: (char_type* s, std::streamsize n) Chris@16: { Chris@16: const extern_type* next; // Next external char. Chris@16: intern_type* nint; // Next internal char. Chris@16: std::streamsize total = 0; // Characters read. Chris@16: int status = iostreams::char_traits::good(); Chris@16: bool partial = false; Chris@16: buffer_type& buf = in(); Chris@16: Chris@16: do { Chris@16: Chris@16: // Fill buffer. Chris@16: if (buf.ptr() == buf.eptr() || partial) { Chris@16: status = buf.fill(dev()); Chris@16: if (buf.ptr() == buf.eptr()) Chris@16: break; Chris@16: partial = false; Chris@16: } Chris@16: Chris@16: // Convert. Chris@16: std::codecvt_base::result result = Chris@16: cvt().in( buf.state(), Chris@16: buf.ptr(), buf.eptr(), next, Chris@16: s + total, s + n, nint ); Chris@16: buf.ptr() += next - buf.ptr(); Chris@16: total = static_cast(nint - s); Chris@16: Chris@16: switch (result) { Chris@16: case std::codecvt_base::partial: Chris@16: partial = true; Chris@16: break; Chris@16: case std::codecvt_base::ok: Chris@16: break; Chris@16: case std::codecvt_base::noconv: Chris@16: { Chris@16: std::streamsize amt = Chris@16: std::min(next - buf.ptr(), n - total); Chris@16: detail::strncpy_if_same(s + total, buf.ptr(), amt); Chris@16: total += amt; Chris@16: } Chris@16: break; Chris@16: case std::codecvt_base::error: Chris@16: default: Chris@16: buf.state() = state_type(); Chris@16: boost::throw_exception(code_conversion_error()); Chris@16: } Chris@16: Chris@16: } while (total < n && status != EOF && status != WOULD_BLOCK); Chris@16: Chris@16: return total == 0 && status == EOF ? -1 : total; Chris@16: } Chris@16: Chris@16: template Chris@16: std::streamsize code_converter::write Chris@16: (const char_type* s, std::streamsize n) Chris@16: { Chris@16: buffer_type& buf = out(); Chris@16: extern_type* next; // Next external char. Chris@16: const intern_type* nint; // Next internal char. Chris@16: std::streamsize total = 0; // Characters written. Chris@16: bool partial = false; Chris@16: Chris@16: while (total < n) { Chris@16: Chris@16: // Empty buffer. Chris@16: if (buf.eptr() == buf.end() || partial) { Chris@16: if (!buf.flush(dev())) Chris@16: break; Chris@16: partial = false; Chris@16: } Chris@16: Chris@16: // Convert. Chris@16: std::codecvt_base::result result = Chris@16: cvt().out( buf.state(), Chris@16: s + total, s + n, nint, Chris@16: buf.eptr(), buf.end(), next ); Chris@16: int progress = (int) (next - buf.eptr()); Chris@16: buf.eptr() += progress; Chris@16: Chris@16: switch (result) { Chris@16: case std::codecvt_base::partial: Chris@16: partial = true; Chris@16: BOOST_FALLTHROUGH; Chris@16: case std::codecvt_base::ok: Chris@16: total = static_cast(nint - s); Chris@16: break; Chris@16: case std::codecvt_base::noconv: Chris@16: { Chris@16: std::streamsize amt = Chris@16: std::min( nint - total - s, Chris@16: buf.end() - buf.eptr() ); Chris@16: detail::strncpy_if_same(buf.eptr(), s + total, amt); Chris@16: total += amt; Chris@16: } Chris@16: break; Chris@16: case std::codecvt_base::error: Chris@16: default: Chris@16: buf.state() = state_type(); Chris@16: boost::throw_exception(code_conversion_error()); Chris@16: } Chris@16: } Chris@16: return total; Chris@16: } Chris@16: Chris@16: //----------------------------------------------------------------------------// Chris@16: Chris@16: } } // End namespaces iostreams, boost. Chris@16: Chris@16: #include // Borland 5.x Chris@16: Chris@16: #endif // #ifndef BOOST_IOSTREAMS_CODE_CONVERTER_HPP_INCLUDED