code_converter.hpp 14 KB

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