• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <string>
2 
3 #if !defined (STLPORT) || !defined (_STLP_USE_NO_IOSTREAMS)
4 #  include <fstream>
5 #  include <locale>
6 #  include <stdexcept>
7 #  include <cstdio> // for WEOF
8 
9 #  include "cppunit/cppunit_proxy.h"
10 
11 #  if !defined (STLPORT) || defined(_STLP_USE_NAMESPACES)
12 using namespace std;
13 #  endif
14 
15 //
16 // TestCase class
17 //
18 class CodecvtTest : public CPPUNIT_NS::TestCase
19 {
20   CPPUNIT_TEST_SUITE(CodecvtTest);
21 #if defined (STLPORT) && defined (_STLP_NO_MEMBER_TEMPLATES)
22   CPPUNIT_IGNORE;
23 #endif
24   CPPUNIT_TEST(variable_encoding);
25   CPPUNIT_STOP_IGNORE;
26 #if defined (STLPORT) && (defined (_STLP_NO_WCHAR_T) || !defined (_STLP_USE_EXCEPTIONS))
27   CPPUNIT_IGNORE;
28 #endif
29   CPPUNIT_TEST(in_out_test);
30   CPPUNIT_TEST(length_test);
31   CPPUNIT_TEST(imbue_while_reading);
32   CPPUNIT_TEST(special_encodings);
33   CPPUNIT_TEST_SUITE_END();
34 
35 protected:
36   void variable_encoding();
37   void in_out_test();
38   void length_test();
39   void imbue_while_reading();
40   void special_encodings();
41 };
42 
43 CPPUNIT_TEST_SUITE_REGISTRATION(CodecvtTest);
44 
45 #if defined (STLPORT)
46 #  define __NO_THROW _STLP_NOTHROW
47 #else
48 #  define __NO_THROW throw()
49 #endif
50 
51 
52 /* Codecvt facet eating some characters from the external buffer.
53  * Transform '01' in 'a'
54  */
55 struct eater_codecvt : public codecvt<char, char, mbstate_t> {
56   typedef codecvt<char,char,mbstate_t> base;
57 
eater_codecvteater_codecvt58   explicit eater_codecvt(size_t refs = 0) : base(refs) {}
59 
60   // primitive conversion
61   virtual base::result
do_ineater_codecvt62   do_in(mbstate_t& mb,
63         const char* ebegin, const char* eend, const char*& ecur,
64         char* ibegin, char* iend, char*& icur) const __NO_THROW {
65       char *state = (char*)&mb;
66       ecur = ebegin;
67       icur = ibegin;
68 
69       while (ecur != eend) {
70           if (icur == iend)
71               return partial;
72           if (*ecur == '0' || *state == 1) {
73             if (*state != 1) {
74               ++ecur;
75             }
76             if (ecur == eend) {
77               *state = 1;
78               return ok;
79             }
80 
81             if (*ecur == '1') {
82               *icur = 'a';
83             }
84             else {
85               *(icur++) = '0';
86               if (icur == iend) {
87                 if (*state != 1) {
88                   --ecur;
89                 }
90                 return partial;
91               }
92               *icur = *ecur;
93             }
94           }
95           else {
96             *icur = *ecur;
97           }
98 
99           *state = 0;
100           ++icur;
101           ++ecur;
102       }
103 
104       return ok;
105   }
106 
107   // claim it's not a null-conversion
do_always_noconveater_codecvt108   virtual bool do_always_noconv() const __NO_THROW
109   { return false; }
110 
111   // claim it doesn't have a fixed-length encoding
do_encodingeater_codecvt112   virtual int do_encoding() const __NO_THROW
113   { return 0; }
114 
115   // implemented for consistency with do_in overload
do_lengtheater_codecvt116   virtual int do_length(mbstate_t &state,
117                         const char *efrom, const char *eend, size_t m) const {
118     char *ibegin = new char[m];
119     const char *ecur = efrom;
120     char *icur = ibegin;
121     mbstate_t tmp = state;
122     do_in(tmp, efrom, eend, ecur, ibegin, ibegin + m, icur);
123     delete[] ibegin;
124     return ecur - efrom;
125   }
126 
do_max_lengtheater_codecvt127   virtual int do_max_length() const __NO_THROW
128   { return 2; }
129 
130 #ifdef __DMC__
131   static locale::id id;
132 #endif
133 };
134 
135 #ifdef __DMC__
136 locale::id eater_codecvt::id;
137 
_GetFacetId(const eater_codecvt *)138 locale::id& _GetFacetId(const eater_codecvt*)
139 { return eater_codecvt::id; }
140 #endif
141 
142 /* Codecvt facet generating more characters than the ones read from the
143  * external buffer, transform '01' in 'abc'
144  * This kind of facet do not allow systematical positionning in the external
145  * buffer (tellg -> -1), when you just read a 'a' you are at an undefined
146  * external buffer position.
147  */
148 struct generator_codecvt : public codecvt<char, char, mbstate_t> {
149   typedef codecvt<char,char,mbstate_t> base;
150 
generator_codecvtgenerator_codecvt151   explicit generator_codecvt(size_t refs = 0) : base(refs) {}
152 
153   // primitive conversion
154   virtual base::result
do_ingenerator_codecvt155   do_in(mbstate_t& mb,
156         const char* ebegin, const char* eend, const char*& ecur,
157         char* ibegin, char* iend, char*& icur) const __NO_THROW {
158       //Access the mbstate information in a portable way:
159       char *state = (char*)&mb;
160       ecur = ebegin;
161       icur = ibegin;
162 
163       if (icur == iend) return ok;
164 
165       if (*state == 2) {
166         *(icur++) = 'b';
167         if (icur == iend) {
168           *state = 3;
169           return ok;
170         }
171         *(icur++) = 'c';
172         *state = 0;
173       }
174       else if (*state == 3) {
175         *(icur++) = 'c';
176         *state = 0;
177       }
178 
179       while (ecur != eend) {
180           if (icur == iend)
181               return ok;
182           if (*ecur == '0' || *state == 1) {
183             if (*state != 1) {
184               ++ecur;
185             }
186             if (ecur == eend) {
187               *state = 1;
188               return partial;
189             }
190 
191             if (*ecur == '1') {
192               *(icur++) = 'a';
193               if (icur == iend) {
194                 *state = 2;
195                 return ok;
196               }
197               *(icur++) = 'b';
198               if (icur == iend) {
199                 *state = 3;
200                 return ok;
201               }
202               *icur = 'c';
203             }
204             else {
205               *(icur++) = '0';
206               if (icur == iend) {
207                 if (*state != 1) {
208                   --ecur;
209                 }
210                 return ok;
211               }
212               *icur = *ecur;
213             }
214           }
215           else {
216             *icur = *ecur;
217           }
218 
219           *state = 0;
220           ++icur;
221           ++ecur;
222       }
223 
224       return ok;
225   }
226 
227   // claim it's not a null-conversion
do_always_noconvgenerator_codecvt228   virtual bool do_always_noconv() const __NO_THROW
229   { return false; }
230 
231   // claim it doesn't have a fixed-length encoding
do_encodinggenerator_codecvt232   virtual int do_encoding() const __NO_THROW
233   { return 0; }
234 
235   // implemented for consistency with do_in overload
do_lengthgenerator_codecvt236   virtual int do_length(mbstate_t &mb,
237                         const char *efrom, const char *eend, size_t m) const {
238     const char *state = (const char*)&mb;
239     int offset = 0;
240     if (*state == 2)
241       offset = 2;
242     else if (*state == 3)
243       offset = 1;
244 
245     char *ibegin = new char[m + offset];
246     const char *ecur = efrom;
247     char *icur = ibegin;
248     mbstate_t tmpState = mb;
249     do_in(tmpState, efrom, eend, ecur, ibegin, ibegin + m + offset, icur);
250     /*
251     char *state = (char*)&tmpState;
252     if (*state != 0) {
253       if (*state == 1)
254         --ecur;
255       else if (*state == 2 || *state == 3) {
256         //Undefined position, we return -1:
257         ecur = efrom - 1;
258       }
259     }
260     else {
261       if (*((char*)&mb) != 0) {
262         //We take into account the character that hasn't been counted yet in
263         //the previous decoding step:
264         ecur++;
265       }
266     }
267     */
268     delete[] ibegin;
269     return (int)min((size_t)(ecur - efrom), m);
270   }
271 
do_max_lengthgenerator_codecvt272   virtual int do_max_length() const __NO_THROW
273   { return 0; }
274 #ifdef __DMC__
275   static locale::id id;
276 #endif
277 };
278 
279 #ifdef __DMC__
280 locale::id generator_codecvt::id;
281 
_GetFacetId(const generator_codecvt *)282 locale::id& _GetFacetId(const generator_codecvt*)
283 { return generator_codecvt::id; }
284 #endif
285 
286 //
287 // tests implementation
288 //
variable_encoding()289 void CodecvtTest::variable_encoding()
290 {
291 #if !defined (STLPORT) || !defined (_STLP_NO_MEMBER_TEMPLATES)
292   //We first generate the file used for test:
293   const char* fileName = "test_file.txt";
294   {
295     ofstream ostr(fileName);
296     //Maybe we simply do not have write access to repository
297     CPPUNIT_ASSERT( ostr.good() );
298     for (int i = 0; i < 2048; ++i) {
299       ostr << "0123456789";
300     }
301     CPPUNIT_ASSERT( ostr.good() );
302   }
303 
304   {
305     ifstream istr(fileName);
306     CPPUNIT_ASSERT( istr.good() );
307     CPPUNIT_ASSERT( !istr.eof() );
308 
309     eater_codecvt codec(1);
310     locale loc(locale::classic(), &codec);
311 
312     istr.imbue(loc);
313     CPPUNIT_ASSERT( istr.good() );
314     CPPUNIT_ASSERT( (int)istr.tellg() == 0 );
315 
316     int theoricalPos = 0;
317     do {
318       int c = istr.get();
319       if (char_traits<char>::eq_int_type(c, char_traits<char>::eof())) {
320         break;
321       }
322       ++theoricalPos;
323       if (c == 'a') {
324         ++theoricalPos;
325       }
326 
327       CPPUNIT_ASSERT( (int)istr.tellg() == theoricalPos );
328     }
329     while (!istr.eof());
330 
331     CPPUNIT_ASSERT( istr.eof() );
332   }
333 
334 #  if 0
335   /* This test is broken, not sure if it is really possible to get a position in
336    * a locale having a codecvt such as generator_codecvt. Maybe generator_codecvt
337    * is not a valid theorical example of codecvt implementation. */
338   {
339     ifstream istr(fileName);
340     CPPUNIT_ASSERT( istr.good() );
341     CPPUNIT_ASSERT( !istr.eof() );
342 
343     generator_codecvt codec(1);
344     locale loc(locale::classic(), &codec);
345 
346     istr.imbue(loc);
347     CPPUNIT_ASSERT( istr.good() );
348     CPPUNIT_ASSERT( (int)istr.tellg() == 0 );
349 
350     int theoricalPos = 0;
351     int theoricalTellg;
352     do {
353       char c = istr.get();
354       if (c == char_traits<char>::eof()) {
355         break;
356       }
357       switch (c) {
358         case 'a':
359         case 'b':
360           theoricalTellg = -1;
361           break;
362         case 'c':
363           ++theoricalPos;
364         default:
365           ++theoricalPos;
366           theoricalTellg = theoricalPos;
367           break;
368       }
369 
370       if ((int)istr.tellg() != theoricalTellg) {
371         CPPUNIT_ASSERT( (int)istr.tellg() == theoricalTellg );
372       }
373     }
374     while (!istr.eof());
375 
376     CPPUNIT_ASSERT( istr.eof() );
377   }
378 #  endif
379 #endif
380 }
381 
in_out_test()382 void CodecvtTest::in_out_test()
383 {
384 #if !defined (STLPORT) || !(defined (_STLP_NO_WCHAR_T) || !defined (_STLP_USE_EXCEPTIONS))
385   try {
386     locale loc("");
387 
388     typedef codecvt<wchar_t, char, mbstate_t> cdecvt_type;
389     if (has_facet<cdecvt_type>(loc)) {
390       cdecvt_type const& cdect = use_facet<cdecvt_type>(loc);
391       {
392         cdecvt_type::state_type state;
393         memset(&state, 0, sizeof(cdecvt_type::state_type));
394         string from("abcdef");
395         const char* next_from;
396         wchar_t to[1];
397         wchar_t *next_to;
398         cdecvt_type::result res = cdect.in(state, from.data(), from.data() + from.size(), next_from,
399                                            to, to + sizeof(to) / sizeof(wchar_t), next_to);
400         CPPUNIT_ASSERT( res == cdecvt_type::ok );
401         CPPUNIT_ASSERT( next_from == from.data() + 1 );
402         CPPUNIT_ASSERT( next_to == &to[0] + 1 );
403         CPPUNIT_ASSERT( to[0] == L'a');
404       }
405       {
406         cdecvt_type::state_type state;
407         memset(&state, 0, sizeof(cdecvt_type::state_type));
408         wstring from(L"abcdef");
409         const wchar_t* next_from;
410         char to[1];
411         char *next_to;
412         cdecvt_type::result res = cdect.out(state, from.data(), from.data() + from.size(), next_from,
413                                             to, to + sizeof(to) / sizeof(char), next_to);
414         CPPUNIT_ASSERT( res == cdecvt_type::ok );
415         CPPUNIT_ASSERT( next_from == from.data() + 1 );
416         CPPUNIT_ASSERT( next_to == &to[0] + 1 );
417         CPPUNIT_ASSERT( to[0] == 'a');
418       }
419     }
420   }
421   catch (runtime_error const&) {
422   }
423   catch (...) {
424     CPPUNIT_FAIL;
425   }
426 #endif
427 }
428 
length_test()429 void CodecvtTest::length_test()
430 {
431 #if !defined (STLPORT) || !(defined (_STLP_NO_WCHAR_T) || !defined (_STLP_USE_EXCEPTIONS))
432   try {
433     locale loc("");
434 
435     typedef codecvt<wchar_t, char, mbstate_t> cdecvt_type;
436     if (has_facet<cdecvt_type>(loc)) {
437       cdecvt_type const& cdect = use_facet<cdecvt_type>(loc);
438       {
439         cdecvt_type::state_type state;
440         memset(&state, 0, sizeof(cdecvt_type::state_type));
441         string from("abcdef");
442         int res = cdect.length(state, from.data(), from.data() + from.size(), from.size());
443         CPPUNIT_ASSERT( (size_t)res == from.size() );
444       }
445     }
446   }
447   catch (runtime_error const&) {
448   }
449   catch (...) {
450     CPPUNIT_FAIL;
451   }
452 #endif
453 }
454 
455 #if !defined (STLPORT) || !(defined (_STLP_NO_WCHAR_T) || !defined (_STLP_USE_EXCEPTIONS))
456 typedef std::codecvt<wchar_t, char, mbstate_t> my_codecvt_base;
457 
458 class my_codecvt : public my_codecvt_base {
459 public:
my_codecvt(size_t r=0)460   explicit my_codecvt(size_t r = 0)
461    : my_codecvt_base(r) {}
462 
463 protected:
do_in(state_type &,const extern_type * first1,const extern_type * last1,const extern_type * & next1,intern_type * first2,intern_type * last2,intern_type * & next2) const464   virtual result do_in(state_type& /*state*/, const extern_type* first1,
465                        const extern_type* last1, const extern_type*& next1,
466                        intern_type* first2, intern_type* last2,
467                        intern_type*& next2) const {
468     for ( next1 = first1, next2 = first2; next1 < last1; next1 += 2 ) {
469       if ( (last1 - next1) < 2 || (last2 - next2) < 1 )
470         return partial;
471       *next2++ = (intern_type)((*(next1 + 1) << 8) | (*next1 & 255));
472     }
473     return ok;
474   }
do_always_noconv() const475   virtual bool do_always_noconv() const __NO_THROW
476   { return false; }
do_max_length() const477   virtual int do_max_length() const __NO_THROW
478   { return 2; }
do_encoding() const479   virtual int do_encoding() const __NO_THROW
480   { return 2; }
481 };
482 #endif
483 
imbue_while_reading()484 void CodecvtTest::imbue_while_reading()
485 {
486 #if !defined (STLPORT) || !(defined (_STLP_NO_WCHAR_T) || !defined (_STLP_USE_EXCEPTIONS))
487   {
488     wofstream ofs( "test.txt" );
489     const wchar_t buf[] = L" ";
490     for ( int i = 0; i < 4098; ++i ) {
491       ofs << buf[0];
492     }
493   }
494 
495   wifstream ifs("test.txt"); // a file containing 4098 wchars
496 
497   ifs.imbue( locale(locale(), new my_codecvt) );
498   ifs.get();
499   ifs.seekg(0);
500   ifs.imbue( locale() );
501   ifs.ignore(4096);
502   int ch = ifs.get();
503   CPPUNIT_CHECK( ch != (int)WEOF );
504 #endif
505 }
506 
special_encodings()507 void CodecvtTest::special_encodings()
508 {
509 #if !defined (STLPORT) || (!defined (_STLP_NO_WCHAR_T) && defined (_STLP_USE_EXCEPTIONS))
510   {
511     locale loc(locale::classic(), new codecvt_byname<wchar_t, char, mbstate_t>("C"));
512     codecvt<wchar_t, char, mbstate_t> const& cvt = use_facet<codecvt<wchar_t, char, mbstate_t> >(loc);
513     mbstate_t state;
514     memset(&state, 0, sizeof(mbstate_t));
515     char c = '0';
516     const char *from_next;
517     wchar_t wc;
518     wchar_t *to_next;
519     CPPUNIT_ASSERT( cvt.in(state, &c, &c + 1, from_next, &wc, &wc, to_next) == codecvt_base::ok );
520     CPPUNIT_ASSERT( to_next == &wc );
521     CPPUNIT_ASSERT( cvt.in(state, &c, &c + 1, from_next, &wc, &wc + 1, to_next) == codecvt_base::ok );
522     CPPUNIT_ASSERT( wc == L'0' );
523     CPPUNIT_ASSERT( to_next == &wc + 1 );
524   }
525   try
526   {
527     wstring cp936_wstr;
528     const string cp936_str = "\xd6\xd0\xb9\xfa\xc9\xe7\xbb\xe1\xbf\xc6\xd1\xa7\xd4\xba\xb7\xa2\xb2\xbc\x32\x30\x30\x38\xc4\xea\xa1\xb6\xbe\xad\xbc\xc3\xc0\xb6\xc6\xa4\xca\xe9\xa1\xb7\xd6\xb8\xb3\xf6\xa3\xac\x32\x30\x30\x37\xc4\xea\xd6\xd0\xb9\xfa\xbe\xad\xbc\xc3\xd4\xf6\xb3\xa4\xd3\xc9\xc6\xab\xbf\xec\xd7\xaa\xcf\xf2\xb9\xfd\xc8\xc8\xb5\xc4\xc7\xf7\xca\xc6\xc3\xf7\xcf\xd4\xd4\xa4\xbc\xc6\xc8\xab\xc4\xea\x47\x44\x50\xd4\xf6\xcb\xd9\xbd\xab\xb4\xef\x31\x31\x2e\x36\x25\xa1\xa3";
529     locale loc(locale::classic(), ".936", locale::ctype);
530     codecvt<wchar_t, char, mbstate_t> const& cvt = use_facet<codecvt<wchar_t, char, mbstate_t> >(loc);
531     mbstate_t state;
532     memset(&state, 0, sizeof(mbstate_t));
533 
534     codecvt_base::result res;
535 
536     {
537       wchar_t wbuf[4096];
538       // Check we will have enough room for the generated wide string generated from the whole char buffer:
539       int len = cvt.length(state, cp936_str.data(), cp936_str.data() + cp936_str.size(), sizeof(wbuf) / sizeof(wchar_t));
540       CPPUNIT_ASSERT( cp936_str.size() == (size_t)len );
541 
542       const char *from_next;
543       wchar_t *to_next;
544       res = cvt.in(state, cp936_str.data(), cp936_str.data() + cp936_str.size(), from_next,
545                           wbuf, wbuf + sizeof(wbuf) / sizeof(wchar_t), to_next);
546       CPPUNIT_ASSERT( res == codecvt_base::ok );
547       CPPUNIT_ASSERT( from_next == cp936_str.data() + cp936_str.size() );
548       cp936_wstr.assign(wbuf, to_next);
549     }
550 
551     {
552       const wchar_t *from_next;
553       char buf[4096];
554       char *to_next;
555       res = cvt.out(state, cp936_wstr.data(), cp936_wstr.data() + cp936_wstr.size(), from_next,
556                            buf, buf + sizeof(buf), to_next);
557       CPPUNIT_ASSERT( res == codecvt_base::ok );
558       CPPUNIT_CHECK( string(buf, to_next) == cp936_str );
559     }
560   }
561   catch (const runtime_error&)
562   {
563     CPPUNIT_MESSAGE("Not enough platform localization support to check 936 code page encoding.");
564   }
565   try
566   {
567     const string utf8_str = "\xe4\xb8\xad\xe5\x9b\xbd\xe7\xa4\xbe\xe4\xbc\x9a\xe7\xa7\x91\xe5\xad\xa6\xe9\x99\xa2\xe5\x8f\x91\xe5\xb8\x83\x32\x30\x30\x38\xe5\xb9\xb4\xe3\x80\x8a\xe7\xbb\x8f\xe6\xb5\x8e\xe8\x93\x9d\xe7\x9a\xae\xe4\xb9\xa6\xe3\x80\x8b\xe6\x8c\x87\xe5\x87\xba\xef\xbc\x8c\x32\x30\x30\x37\xe5\xb9\xb4\xe4\xb8\xad\xe5\x9b\xbd\xe7\xbb\x8f\xe6\xb5\x8e\xe5\xa2\x9e\xe9\x95\xbf\xe7\x94\xb1\xe5\x81\x8f\xe5\xbf\xab\xe8\xbd\xac\xe5\x90\x91\xe8\xbf\x87\xe7\x83\xad\xe7\x9a\x84\xe8\xb6\x8b\xe5\x8a\xbf\xe6\x98\x8e\xe6\x98\xbe\xe9\xa2\x84\xe8\xae\xa1\xe5\x85\xa8\xe5\xb9\xb4\x47\x44\x50\xe5\xa2\x9e\xe9\x80\x9f\xe5\xb0\x86\xe8\xbe\xbe\x31\x31\x2e\x36\x25\xe3\x80\x82";
568     wstring utf8_wstr;
569     locale loc(locale::classic(), new codecvt_byname<wchar_t, char, mbstate_t>(".utf8"));
570     codecvt<wchar_t, char, mbstate_t> const& cvt = use_facet<codecvt<wchar_t, char, mbstate_t> >(loc);
571     mbstate_t state;
572     memset(&state, 0, sizeof(mbstate_t));
573 
574     codecvt_base::result res;
575 
576     {
577       wchar_t wbuf[4096];
578       // Check we will have enough room for the wide string generated from the whole char buffer:
579       int len = cvt.length(state, utf8_str.data(), utf8_str.data() + utf8_str.size(), sizeof(wbuf) / sizeof(wchar_t));
580       CPPUNIT_ASSERT( utf8_str.size() == (size_t)len );
581 
582       const char *from_next;
583       wchar_t *to_next;
584       res = cvt.in(state, utf8_str.data(), utf8_str.data() + utf8_str.size(), from_next,
585                           wbuf, wbuf + sizeof(wbuf) / sizeof(wchar_t), to_next);
586       CPPUNIT_ASSERT( res == codecvt_base::ok );
587       CPPUNIT_ASSERT( from_next == utf8_str.data() + utf8_str.size() );
588       utf8_wstr.assign(wbuf, to_next);
589 
590       // Try to read one char after the other:
591       wchar_t wc;
592       const char* from = utf8_str.data();
593       const char* from_end = from + utf8_str.size();
594       from_next = utf8_str.data();
595       size_t length = 1;
596       size_t windex = 0;
597       while (from + length <= from_end) {
598         res = cvt.in(state, from, from + length, from_next,
599                             &wc, &wc + 1, to_next);
600         switch (res) {
601           case codecvt_base::ok:
602             // reset length:
603             from = from_next;
604             length = 1;
605             CPPUNIT_ASSERT( wc == utf8_wstr[windex++] );
606             wc = 0;
607             break;
608           case codecvt_base::partial:
609             if (from_next == from)
610               // from_next hasn't move so we have to pass more chars
611               ++length;
612             else
613               // char between from and from_next has been eaten, we simply restart
614               // conversion from from_next:
615               from = from_next;
616             continue;
617           case codecvt_base::error:
618           case codecvt_base::noconv:
619             CPPUNIT_FAIL;
620             //break;
621         }
622       }
623       CPPUNIT_ASSERT( windex == utf8_wstr.size() );
624     }
625 
626     {
627       const wchar_t *from_next;
628       char buf[4096];
629       char *to_next;
630       res = cvt.out(state, utf8_wstr.data(), utf8_wstr.data() + utf8_wstr.size(), from_next,
631                            buf, buf + sizeof(buf), to_next);
632       CPPUNIT_ASSERT( res == codecvt_base::ok );
633       CPPUNIT_CHECK( string(buf, to_next) == utf8_str );
634     }
635 
636     {
637       // Check that an obviously wrong UTF8 encoded string is correctly detected:
638       const string bad_utf8_str("\xdf\xdf\xdf\xdf\xdf");
639       wchar_t wc;
640       const char *from_next;
641       wchar_t *to_next;
642       res = cvt.in(state, bad_utf8_str.data(), bad_utf8_str.data() + bad_utf8_str.size(), from_next,
643                           &wc, &wc + 1, to_next);
644       CPPUNIT_ASSERT( res == codecvt_base::error );
645     }
646   }
647   catch (const runtime_error&)
648   {
649     CPPUNIT_MESSAGE("Not enough platform localization support to check UTF8 encoding.");
650   }
651 #endif
652 }
653 
654 #endif
655