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