| 1 | // (C) Copyright Jonathan Turkanis 2003. | 
|---|
| 2 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | 
|---|
| 3 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) | 
|---|
| 4 |  | 
|---|
| 5 | // See http://www.boost.org/libs/iostreams for documentation. | 
|---|
| 6 |  | 
|---|
| 7 | // Contains machinery for performing code conversion. | 
|---|
| 8 |  | 
|---|
| 9 | #ifndef BOOST_IOSTREAMS_CODE_CONVERTER_HPP_INCLUDED | 
|---|
| 10 | #define BOOST_IOSTREAMS_CODE_CONVERTER_HPP_INCLUDED | 
|---|
| 11 |  | 
|---|
| 12 | #if defined(_MSC_VER) && (_MSC_VER >= 1020) | 
|---|
| 13 | # pragma once | 
|---|
| 14 | #endif | 
|---|
| 15 |  | 
|---|
| 16 | #include <boost/iostreams/detail/config/wide_streams.hpp> | 
|---|
| 17 | #if defined(BOOST_IOSTREAMS_NO_WIDE_STREAMS) || \ | 
|---|
| 18 |     defined(BOOST_IOSTREAMS_NO_LOCALE) \ | 
|---|
| 19 |     /**/ | 
|---|
| 20 | # error code conversion not supported on this platform | 
|---|
| 21 | #endif | 
|---|
| 22 |  | 
|---|
| 23 | #include <algorithm>                       // max. | 
|---|
| 24 | #include <cstring>                         // memcpy. | 
|---|
| 25 | #include <exception> | 
|---|
| 26 | #include <boost/config.hpp>                // DEDUCED_TYPENAME,  | 
|---|
| 27 | #include <boost/iostreams/char_traits.hpp> | 
|---|
| 28 | #include <boost/iostreams/constants.hpp>   // default_filter_buffer_size. | 
|---|
| 29 | #include <boost/iostreams/detail/adapter/concept_adapter.hpp> | 
|---|
| 30 | #include <boost/iostreams/detail/adapter/direct_adapter.hpp> | 
|---|
| 31 | #include <boost/iostreams/detail/buffer.hpp> | 
|---|
| 32 | #include <boost/iostreams/detail/call_traits.hpp> | 
|---|
| 33 | #include <boost/iostreams/detail/codecvt_holder.hpp> | 
|---|
| 34 | #include <boost/iostreams/detail/codecvt_helper.hpp> | 
|---|
| 35 | #include <boost/iostreams/detail/double_object.hpp> | 
|---|
| 36 | #include <boost/iostreams/detail/forward.hpp> | 
|---|
| 37 | #include <boost/iostreams/detail/ios.hpp> // failure, openmode, int types. | 
|---|
| 38 | #include <boost/iostreams/detail/select.hpp> | 
|---|
| 39 | #include <boost/iostreams/traits.hpp> | 
|---|
| 40 | #include <boost/iostreams/operations.hpp> | 
|---|
| 41 | #include <boost/optional.hpp> | 
|---|
| 42 | #include <boost/shared_ptr.hpp> | 
|---|
| 43 | #include <boost/static_assert.hpp> | 
|---|
| 44 | #include <boost/type_traits/is_convertible.hpp> | 
|---|
| 45 | #include <boost/type_traits/is_same.hpp> | 
|---|
| 46 |  | 
|---|
| 47 | // Must come last. | 
|---|
| 48 | #include <boost/iostreams/detail/config/disable_warnings.hpp> // Borland 5.x | 
|---|
| 49 |  | 
|---|
| 50 | namespace boost { namespace iostreams { | 
|---|
| 51 |  | 
|---|
| 52 | struct code_conversion_error : BOOST_IOSTREAMS_FAILURE { | 
|---|
| 53 |     code_conversion_error()  | 
|---|
| 54 |         : BOOST_IOSTREAMS_FAILURE("code conversion error") | 
|---|
| 55 |         { } | 
|---|
| 56 | }; | 
|---|
| 57 |  | 
|---|
| 58 | namespace detail { | 
|---|
| 59 |  | 
|---|
| 60 | //--------------Definition of strncpy_if_same---------------------------------// | 
|---|
| 61 |  | 
|---|
| 62 | // Helper template for strncpy_if_same, below. | 
|---|
| 63 | template<bool B> | 
|---|
| 64 | struct strncpy_if_same_impl; | 
|---|
| 65 |  | 
|---|
| 66 | template<> | 
|---|
| 67 | struct strncpy_if_same_impl<true> { | 
|---|
| 68 |     template<typename Ch> | 
|---|
| 69 |     static Ch* copy(Ch* tgt, const Ch* src, std::streamsize n) | 
|---|
| 70 |     { return BOOST_IOSTREAMS_CHAR_TRAITS(Ch)::copy(tgt, src, n); } | 
|---|
| 71 | }; | 
|---|
| 72 |  | 
|---|
| 73 | template<> | 
|---|
| 74 | struct strncpy_if_same_impl<false> { | 
|---|
| 75 |     template<typename Src, typename Tgt> | 
|---|
| 76 |     static Tgt* copy(Tgt* tgt, const Src*, std::streamsize) { return tgt; } | 
|---|
| 77 | }; | 
|---|
| 78 |  | 
|---|
| 79 | template<typename Src, typename Tgt> | 
|---|
| 80 | Tgt* strncpy_if_same(Tgt* tgt, const Src* src, std::streamsize n) | 
|---|
| 81 | { | 
|---|
| 82 |     typedef strncpy_if_same_impl<is_same<Src, Tgt>::value> impl; | 
|---|
| 83 |     return impl::copy(tgt, src, n); | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 86 | //--------------Definition of conversion_buffer-------------------------------// | 
|---|
| 87 |  | 
|---|
| 88 | // Buffer and conversion state for reading. | 
|---|
| 89 | template<typename Codecvt, typename Alloc> | 
|---|
| 90 | class conversion_buffer  | 
|---|
| 91 |     : public buffer< | 
|---|
| 92 |                  BOOST_DEDUCED_TYPENAME detail::codecvt_extern<Codecvt>::type, | 
|---|
| 93 |                  Alloc | 
|---|
| 94 |              >  | 
|---|
| 95 | { | 
|---|
| 96 | public: | 
|---|
| 97 |     typedef typename Codecvt::state_type state_type; | 
|---|
| 98 |     conversion_buffer()  | 
|---|
| 99 |         : buffer< | 
|---|
| 100 |               BOOST_DEDUCED_TYPENAME detail::codecvt_extern<Codecvt>::type, | 
|---|
| 101 |               Alloc | 
|---|
| 102 |           >(0)  | 
|---|
| 103 |     {  | 
|---|
| 104 |         reset();  | 
|---|
| 105 |     } | 
|---|
| 106 |     state_type& state() { return state_; } | 
|---|
| 107 |     void reset()  | 
|---|
| 108 |     {  | 
|---|
| 109 |         if (this->size())  | 
|---|
| 110 |             this->set(0, 0); | 
|---|
| 111 |         state_ = state_type();  | 
|---|
| 112 |     } | 
|---|
| 113 | private: | 
|---|
| 114 |     state_type state_; | 
|---|
| 115 | }; | 
|---|
| 116 |  | 
|---|
| 117 | //--------------Definition of converter_impl----------------------------------// | 
|---|
| 118 |  | 
|---|
| 119 | // Contains member data, open/is_open/close and buffer management functions. | 
|---|
| 120 | template<typename Device, typename Codecvt, typename Alloc> | 
|---|
| 121 | struct code_converter_impl { | 
|---|
| 122 |     typedef typename codecvt_extern<Codecvt>::type          extern_type; | 
|---|
| 123 |     typedef typename category_of<Device>::type              device_category; | 
|---|
| 124 |     typedef is_convertible<device_category, input>          can_read; | 
|---|
| 125 |     typedef is_convertible<device_category, output>         can_write; | 
|---|
| 126 |     typedef is_convertible<device_category, bidirectional>  is_bidir; | 
|---|
| 127 |     typedef typename  | 
|---|
| 128 |             iostreams::select<  // Disambiguation for Tru64. | 
|---|
| 129 |                 is_bidir, bidirectional, | 
|---|
| 130 |                 can_read, input, | 
|---|
| 131 |                 can_write, output | 
|---|
| 132 |             >::type                                         mode;       | 
|---|
| 133 |     typedef typename | 
|---|
| 134 |             mpl::if_< | 
|---|
| 135 |                 is_direct<Device>, | 
|---|
| 136 |                 direct_adapter<Device>, | 
|---|
| 137 |                 Device | 
|---|
| 138 |             >::type                                         policy_type; | 
|---|
| 139 |     typedef optional< concept_adapter<policy_type> >        storage_type; | 
|---|
| 140 |     typedef is_convertible<device_category, two_sequence>   is_double; | 
|---|
| 141 |     typedef conversion_buffer<Codecvt, Alloc>               buffer_type; | 
|---|
| 142 |  | 
|---|
| 143 |     code_converter_impl() : cvt_(), flags_(0) { } | 
|---|
| 144 |  | 
|---|
| 145 |     ~code_converter_impl() | 
|---|
| 146 |     {  | 
|---|
| 147 |         try {  | 
|---|
| 148 |             if (flags_ & f_open) close();  | 
|---|
| 149 |         } catch (std::exception&) { /* */ }  | 
|---|
| 150 |     } | 
|---|
| 151 |  | 
|---|
| 152 |     void open(const Device& dev, int buffer_size) | 
|---|
| 153 |     { | 
|---|
| 154 |         if (flags_ & f_open) | 
|---|
| 155 |             throw BOOST_IOSTREAMS_FAILURE("already open"); | 
|---|
| 156 |         if (buffer_size == -1) | 
|---|
| 157 |             buffer_size = default_filter_buffer_size; | 
|---|
| 158 |         int max_length = cvt_.get().max_length(); | 
|---|
| 159 |         buffer_size = (std::max)(buffer_size, 2 * max_length); | 
|---|
| 160 |         if (can_read::value) { | 
|---|
| 161 |             buf_.first().resize(buffer_size); | 
|---|
| 162 |             buf_.first().set(0, 0); | 
|---|
| 163 |         } | 
|---|
| 164 |         if (can_write::value && !is_double::value) { | 
|---|
| 165 |             buf_.second().resize(buffer_size); | 
|---|
| 166 |             buf_.second().set(0, 0); | 
|---|
| 167 |         } | 
|---|
| 168 |         dev_.reset(concept_adapter<policy_type>(dev)); | 
|---|
| 169 |         flags_ |= f_open; | 
|---|
| 170 |     } | 
|---|
| 171 |  | 
|---|
| 172 |     void close(BOOST_IOS::openmode which = BOOST_IOS::in | BOOST_IOS::out) | 
|---|
| 173 |     { | 
|---|
| 174 |         if (which & BOOST_IOS::in) { | 
|---|
| 175 |             iostreams::close(dev(), BOOST_IOS::in); | 
|---|
| 176 |             flags_ |= f_input_closed; | 
|---|
| 177 |         } | 
|---|
| 178 |         if (which & BOOST_IOS::out) { | 
|---|
| 179 |             buf_.second().flush(dev()); | 
|---|
| 180 |             iostreams::close(dev(), BOOST_IOS::out); | 
|---|
| 181 |             flags_ |= f_output_closed; | 
|---|
| 182 |         } | 
|---|
| 183 |         if ( !is_double::value ||  | 
|---|
| 184 |              (flags_ & f_input_closed) != 0 &&  | 
|---|
| 185 |              (flags_ & f_output_closed) != 0 ) | 
|---|
| 186 |         { | 
|---|
| 187 |             dev_.reset(); | 
|---|
| 188 |             buf_.first().reset(); | 
|---|
| 189 |             buf_.second().reset(); | 
|---|
| 190 |             flags_ = 0; | 
|---|
| 191 |         } | 
|---|
| 192 |     } | 
|---|
| 193 |  | 
|---|
| 194 |     bool is_open() const { return (flags_ & f_open) != 0;} | 
|---|
| 195 |  | 
|---|
| 196 |     policy_type& dev() { return **dev_; } | 
|---|
| 197 |  | 
|---|
| 198 |     enum flag_type { | 
|---|
| 199 |         f_open             = 1, | 
|---|
| 200 |         f_input_closed     = f_open << 1, | 
|---|
| 201 |         f_output_closed    = f_input_closed << 1 | 
|---|
| 202 |     }; | 
|---|
| 203 |  | 
|---|
| 204 |     codecvt_holder<Codecvt>  cvt_; | 
|---|
| 205 |     storage_type             dev_; | 
|---|
| 206 |     double_object< | 
|---|
| 207 |         buffer_type,  | 
|---|
| 208 |         is_double | 
|---|
| 209 |     >                        buf_; | 
|---|
| 210 |     int                      flags_; | 
|---|
| 211 | }; | 
|---|
| 212 |  | 
|---|
| 213 | } // End namespace detail. | 
|---|
| 214 |  | 
|---|
| 215 | //--------------Definition of converter---------------------------------------// | 
|---|
| 216 |  | 
|---|
| 217 | #define BOOST_IOSTREAMS_CONVERTER_PARAMS() , int buffer_size = -1 | 
|---|
| 218 | #define BOOST_IOSTREAMS_CONVERTER_ARGS() , buffer_size | 
|---|
| 219 |  | 
|---|
| 220 | template<typename Device, typename Codecvt, typename Alloc> | 
|---|
| 221 | struct code_converter_base { | 
|---|
| 222 |     typedef detail::code_converter_impl< | 
|---|
| 223 |                 Device, Codecvt, Alloc | 
|---|
| 224 |             > impl_type; | 
|---|
| 225 |     code_converter_base() : pimpl_(new impl_type) { } | 
|---|
| 226 |     shared_ptr<impl_type> pimpl_; | 
|---|
| 227 | }; | 
|---|
| 228 |  | 
|---|
| 229 | template< typename Device,  | 
|---|
| 230 |           typename Codecvt = detail::default_codecvt,  | 
|---|
| 231 |           typename Alloc = std::allocator<char> > | 
|---|
| 232 | class code_converter  | 
|---|
| 233 |     : protected code_converter_base<Device, Codecvt, Alloc> | 
|---|
| 234 | { | 
|---|
| 235 | private: | 
|---|
| 236 |     typedef detail::code_converter_impl< | 
|---|
| 237 |                 Device, Codecvt, Alloc | 
|---|
| 238 |             >                                                       impl_type; | 
|---|
| 239 |     typedef typename impl_type::policy_type                         policy_type; | 
|---|
| 240 |     typedef typename impl_type::buffer_type                         buffer_type; | 
|---|
| 241 |     typedef typename detail::codecvt_holder<Codecvt>::codecvt_type  codecvt_type; | 
|---|
| 242 |     typedef typename detail::codecvt_intern<Codecvt>::type          intern_type; | 
|---|
| 243 |     typedef typename detail::codecvt_extern<Codecvt>::type          extern_type; | 
|---|
| 244 |     typedef typename detail::codecvt_state<Codecvt>::type           state_type; | 
|---|
| 245 | public: | 
|---|
| 246 |     typedef intern_type                                             char_type;     | 
|---|
| 247 |     struct category  | 
|---|
| 248 |         : impl_type::mode, device_tag, closable_tag, localizable_tag | 
|---|
| 249 |         { }; | 
|---|
| 250 |     BOOST_STATIC_ASSERT(( | 
|---|
| 251 |         is_same< | 
|---|
| 252 |             extern_type,  | 
|---|
| 253 |             BOOST_DEDUCED_TYPENAME char_type_of<Device>::type | 
|---|
| 254 |         >::value | 
|---|
| 255 |     )); | 
|---|
| 256 | public: | 
|---|
| 257 |     code_converter() { } | 
|---|
| 258 | #if BOOST_WORKAROUND(__GNUC__, < 3) | 
|---|
| 259 |     code_converter(code_converter& rhs)  | 
|---|
| 260 |         : code_converter_base<Device, Codecvt, Alloc>(rhs) | 
|---|
| 261 |         { } | 
|---|
| 262 |     code_converter(const code_converter& rhs)  | 
|---|
| 263 |         : code_converter_base<Device, Codecvt, Alloc>(rhs) | 
|---|
| 264 |         { } | 
|---|
| 265 | #endif | 
|---|
| 266 |     BOOST_IOSTREAMS_FORWARD( code_converter, open_impl, Device, | 
|---|
| 267 |                              BOOST_IOSTREAMS_CONVERTER_PARAMS,  | 
|---|
| 268 |                              BOOST_IOSTREAMS_CONVERTER_ARGS ) | 
|---|
| 269 |  | 
|---|
| 270 |         // fstream-like interface. | 
|---|
| 271 |  | 
|---|
| 272 |     bool is_open() const { return this->pimpl_->is_open(); } | 
|---|
| 273 |     void close(BOOST_IOS::openmode which = BOOST_IOS::in | BOOST_IOS::out ) | 
|---|
| 274 |     { impl().close(which); } | 
|---|
| 275 |  | 
|---|
| 276 |         // Device interface. | 
|---|
| 277 |  | 
|---|
| 278 |     std::streamsize read(char_type*, std::streamsize); | 
|---|
| 279 |     std::streamsize write(const char_type*, std::streamsize); | 
|---|
| 280 |     void imbue(const std::locale& loc) { impl().cvt_.imbue(loc); } | 
|---|
| 281 |  | 
|---|
| 282 |         // Direct device access. | 
|---|
| 283 |  | 
|---|
| 284 |     Device& operator*() { return detail::unwrap_direct(dev()); } | 
|---|
| 285 |     Device* operator->() { return &detail::unwrap_direct(dev()); } | 
|---|
| 286 | private: | 
|---|
| 287 |     template<typename T> // Used for forwarding. | 
|---|
| 288 |     void open_impl(const T& t BOOST_IOSTREAMS_CONVERTER_PARAMS())  | 
|---|
| 289 |     {  | 
|---|
| 290 |         impl().open(t BOOST_IOSTREAMS_CONVERTER_ARGS());  | 
|---|
| 291 |     } | 
|---|
| 292 |  | 
|---|
| 293 |     const codecvt_type& cvt() { return impl().cvt_.get(); } | 
|---|
| 294 |     policy_type& dev() { return impl().dev(); } | 
|---|
| 295 |     buffer_type& in() { return impl().buf_.first(); } | 
|---|
| 296 |     buffer_type& out() { return impl().buf_.second(); } | 
|---|
| 297 |     impl_type& impl() { return *this->pimpl_; } | 
|---|
| 298 | }; | 
|---|
| 299 |  | 
|---|
| 300 | //--------------Implementation of converter-----------------------------------// | 
|---|
| 301 |  | 
|---|
| 302 | // Implementation note: if end of stream contains a partial character, | 
|---|
| 303 | // it is ignored. | 
|---|
| 304 | template<typename Device, typename Codevt, typename Alloc> | 
|---|
| 305 | std::streamsize code_converter<Device, Codevt, Alloc>::read | 
|---|
| 306 |     (char_type* s, std::streamsize n) | 
|---|
| 307 | { | 
|---|
| 308 |     using namespace std; | 
|---|
| 309 |     const extern_type*   next;        // Next external char. | 
|---|
| 310 |     intern_type*         nint;        // Next internal char. | 
|---|
| 311 |     streamsize           total = 0;   // Characters read. | 
|---|
| 312 |     int                  status = iostreams::char_traits<char>::good(); | 
|---|
| 313 |     bool                 partial = false; | 
|---|
| 314 |     buffer_type&         buf = in(); | 
|---|
| 315 |  | 
|---|
| 316 |     do { | 
|---|
| 317 |  | 
|---|
| 318 |         // Fill buffer. | 
|---|
| 319 |         if (buf.ptr() == buf.eptr() || partial) { | 
|---|
| 320 |             status = buf.fill(dev()); | 
|---|
| 321 |             if (buf.ptr() == buf.eptr()) | 
|---|
| 322 |                 break; | 
|---|
| 323 |             partial = false; | 
|---|
| 324 |         } | 
|---|
| 325 |  | 
|---|
| 326 |         // Convert. | 
|---|
| 327 |         codecvt_base::result result = | 
|---|
| 328 |             cvt().in( buf.state(), | 
|---|
| 329 |                       buf.ptr(), buf.eptr(), next, | 
|---|
| 330 |                       s + total, s + n, nint ); | 
|---|
| 331 |         buf.ptr() += next - buf.ptr(); | 
|---|
| 332 |         total = static_cast<streamsize>(nint - s); | 
|---|
| 333 |  | 
|---|
| 334 |         switch (result) { | 
|---|
| 335 |         case codecvt_base::partial: | 
|---|
| 336 |             partial = true; | 
|---|
| 337 |             break; | 
|---|
| 338 |         case codecvt_base::ok: | 
|---|
| 339 |             break; | 
|---|
| 340 |         case codecvt_base::noconv: | 
|---|
| 341 |             { | 
|---|
| 342 |                 streamsize amt =  | 
|---|
| 343 |                     std::min<streamsize>(next - buf.ptr(), n - total); | 
|---|
| 344 |                 detail::strncpy_if_same(s + total, buf.ptr(), amt); | 
|---|
| 345 |                 total += amt; | 
|---|
| 346 |             } | 
|---|
| 347 |             break; | 
|---|
| 348 |         case codecvt_base::error: | 
|---|
| 349 |         default: | 
|---|
| 350 |             buf.state() = state_type(); | 
|---|
| 351 |             throw code_conversion_error(); | 
|---|
| 352 |         } | 
|---|
| 353 |  | 
|---|
| 354 |     } while (total < n && status != EOF && status != WOULD_BLOCK); | 
|---|
| 355 |  | 
|---|
| 356 |     return total == 0 && status == EOF ? -1 : total; | 
|---|
| 357 | } | 
|---|
| 358 |  | 
|---|
| 359 | template<typename Device, typename Codevt, typename Alloc> | 
|---|
| 360 | std::streamsize code_converter<Device, Codevt, Alloc>::write | 
|---|
| 361 |     (const char_type* s, std::streamsize n) | 
|---|
| 362 | { | 
|---|
| 363 |     using namespace std; | 
|---|
| 364 |     buffer_type&        buf = out(); | 
|---|
| 365 |     extern_type*        next;              // Next external char. | 
|---|
| 366 |     const intern_type*  nint;              // Next internal char. | 
|---|
| 367 |     streamsize          total = 0;         // Characters written. | 
|---|
| 368 |     bool                partial = false; | 
|---|
| 369 |  | 
|---|
| 370 |     while (total < n) { | 
|---|
| 371 |  | 
|---|
| 372 |         // Empty buffer. | 
|---|
| 373 |         if (buf.eptr() == buf.end() || partial) { | 
|---|
| 374 |             if (!buf.flush(dev())) | 
|---|
| 375 |                 break; | 
|---|
| 376 |             partial = false; | 
|---|
| 377 |         } | 
|---|
| 378 |         | 
|---|
| 379 |         // Convert. | 
|---|
| 380 |         codecvt_base::result result = | 
|---|
| 381 |             cvt().out( buf.state(), | 
|---|
| 382 |                        s + total, s + n, nint, | 
|---|
| 383 |                        buf.eptr(), buf.end(), next ); | 
|---|
| 384 |         int progress = (int) (next - buf.eptr()); | 
|---|
| 385 |         buf.eptr() += progress; | 
|---|
| 386 |  | 
|---|
| 387 |         switch (result) { | 
|---|
| 388 |         case codecvt_base::partial: | 
|---|
| 389 |             partial = true; // Fall through. | 
|---|
| 390 |         case codecvt_base::ok: | 
|---|
| 391 |             total = static_cast<streamsize>(nint - s); | 
|---|
| 392 |             break; | 
|---|
| 393 |         case codecvt_base::noconv: | 
|---|
| 394 |             { | 
|---|
| 395 |                 streamsize amt =  | 
|---|
| 396 |                     std::min<streamsize>( nint - total - s,  | 
|---|
| 397 |                                           buf.end() - buf.eptr() ); | 
|---|
| 398 |                 detail::strncpy_if_same(buf.eptr(), s + total, amt); | 
|---|
| 399 |                 total += amt; | 
|---|
| 400 |             } | 
|---|
| 401 |             break; | 
|---|
| 402 |         case codecvt_base::error: | 
|---|
| 403 |         default: | 
|---|
| 404 |             buf.state() = state_type(); | 
|---|
| 405 |             throw code_conversion_error(); | 
|---|
| 406 |         } | 
|---|
| 407 |     } | 
|---|
| 408 |     return total; | 
|---|
| 409 | } | 
|---|
| 410 |  | 
|---|
| 411 | //----------------------------------------------------------------------------// | 
|---|
| 412 |  | 
|---|
| 413 | } } // End namespaces iostreams, boost. | 
|---|
| 414 |  | 
|---|
| 415 | #include <boost/iostreams/detail/config/enable_warnings.hpp> // Borland 5.x | 
|---|
| 416 |  | 
|---|
| 417 | #endif // #ifndef BOOST_IOSTREAMS_CODE_CONVERTER_HPP_INCLUDED | 
|---|