Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
22.locale.collate.cpp
Go to the documentation of this file.
1
29#include <locale> // for collate, locale
30#include <string> // for string
31
32#include <algorithm> // for sort and unique
33#include <climits> // for UCHAR_MAX
34#include <clocale> // for LC_COLLATE, setlocale
35#include <cstdlib> // for exit()
36#include <cstdio> // for fprintf()
37#include <cstring> // for strcmp(), strcoll(), ...
38#include <cwchar> // for wcscoll()
39
40#include <rw_driver.h>
41#include <rw_environ.h>
42#include <rw_file.h>
43#include <rw_locale.h>
44#include <rw_process.h>
45
46
47#if _RWSTD_PATH_SEP == '/'
48# define SLASH "/"
49#else
50# define SLASH "\\"
51#endif
52
53// strings declared extern to work around a SunPro bug (PR #28124)
54// get the source root
55#define RELPATH "etc" SLASH "nls"
56#define TESTS_ETC_PATH "tests" SLASH "etc"
57
58// the root of the locale directory (RWSTD_LOCALE_ROOT)
59#define LOCALE_ROOT "RWSTD_LOCALE_ROOT"
60const char* locale_root;
61
62#define LC_COLLATE_SRC "LC_COLLATE.src"
63#define LC_COLLATE_CM "LC_COLLATE.cm"
64#define TEST_LOCALE_NAME "test.locale"
65
66
68// These overloads are necessary in our template
69// functions so that we can make a single function call reguardless
70// of the charT we are using
71
72int c_strcoll (const char* s1, const char* s2)
73{
74 const int ret = std::strcoll(s1, s2);
75 return ret ? ret > 0 ? 1 : -1 : 0;
76}
77
78std::size_t c_xfrm (char* to, const char* from, std::size_t size)
79{
80 char safety_buf [8];
81 if (0 == to && 0 == size) {
82 // prevent buggy implementations (such as MSVC 8) from trying
83 // to write to the destination buffer even though it's 0 and
84 // its size is zero (see stdcxx-69)
85 to = safety_buf;
86 *to = '\0';
87 }
88
89 std::size_t n = std::strxfrm (to, from, size);
90
91 if (to)
92 n = std::strlen (to);
93
94 return n;
95}
96
97std::size_t c_strlen (const char* s1)
98{
99 return std::strlen (s1);
100}
101
102const char* narrow (char* dst, const char* src)
103{
104 if (src == dst || !src || !dst)
105 return src;
106
107 std::memcpy (dst, src, std::strlen (src) + 1);
108 return dst;
109}
110
111const char* widen (char* dst, const char* src)
112{
113 if (src == dst || !src || !dst)
114 return src;
115
116 std::memcpy (dst, src, std::strlen (src) + 1);
117 return dst;
118}
119
120#ifndef _RWSTD_NO_WCHAR_T
121
122int c_strcoll (const wchar_t* s1, const wchar_t* s2)
123{
124 const int ret = std::wcscoll(s1, s2);
125 return ret ? ret > 0 ? 1 : -1 : 0;
126}
127
128std::size_t c_xfrm (wchar_t* to, const wchar_t* from, std::size_t size)
129{
130#if !defined (_MSC_VER) || _MSC_VER > 1200
131
132 wchar_t safety_buf [8];
133 if (0 == to && 0 == size) {
134 // prevent buggy implementations (such as MSVC 8) from trying
135 // to write to the destination buffer even though it's 0 and
136 // its size is zero (see stdcxx-69)
137 to = safety_buf;
138 *to = L'\0';
139 }
140
141 std::size_t n = std::wcsxfrm (to, from, size);
142
143 if (to)
144 n = std::wcslen (to);
145
146#else // MSVC 6 and prior
147
148 // working around an MSVC 6.0 libc bug (PR #26437)
149
150 if (to) {
151 std::size_t n = std::wcsxfrm (to, from, size);
152
153 n = std::wcslen (to);
154
155 return n;
156 }
157
158 wchar_t tmp [1024];
159
160 std::size_t n = std::wcslen (from);
161
162 _RWSTD_ASSERT (n < sizeof tmp / sizeof *tmp);
163
164 std::wcscpy (tmp, from);
165
166 std::wcsxfrm (tmp, from, sizeof tmp / sizeof *tmp);
167
168 n = std::wcslen (tmp);
169
170#endif // MSVC 6
171
172 return n;
173}
174
175std::size_t c_strlen (const wchar_t* s1)
176{
177 return std::wcslen (s1);
178}
179
180const wchar_t* widen (wchar_t* dst, const char* src)
181{
182 static wchar_t buf [4096];
183
184 if (!src)
185 return 0;
186
187 if (!dst)
188 dst = buf;
189
190 std::size_t len = std::strlen (src);
191
192 _RWSTD_ASSERT (len < sizeof buf /sizeof *buf);
193
194 len = std::mbstowcs (dst, src, sizeof buf / sizeof *buf);
195
196 if (std::size_t (-1) == len)
197 *dst = 0;
198
199 return dst;
200}
201
202const char* narrow (char* dst, const wchar_t* src)
203{
204 static char buf [4096];
205
206 if (!src)
207 return 0;
208
209 if (!dst)
210 dst = buf;
211
212 std::size_t len = std::wcslen (src);
213
214 _RWSTD_ASSERT (len < sizeof buf);
215
216 len = std::wcstombs (dst, src, sizeof buf / sizeof *buf);
217
218 if (std::size_t (-1) == len)
219 *dst = 0;
220
221 return dst;
222}
223
224#endif //_RWSTD_NO_WCHAR_T
225
226
228template <class charT>
229/*static*/ void
230gen_str (charT* str, std::size_t size)
231{
232 // generate a random string with the given size
233 if (!size)
234 return;
235
236 // use ASCII characters in the printable range
237 for (std::size_t i = 0; i != size - 1; ++i)
238 str [i] = ' ' + std::rand () % ('~' - ' ');
239
240 str [size - 1] = charT ();
241}
242
243
245template <class charT>
246/*static*/ void
247check_libc (const char* charTname)
248{
249 // the libc implementation of the library should act the same as
250 // the c-library. Go through all the locales, generate some random
251 // strings and make sure that the following holds true:
252 // transform acts like strxfrm and wcsxfrm,
253 // compare acts like strcoll and wcscoll
254
255 int nfail [3] = { 0 };
256
257 rw_info (0, __FILE__, __LINE__,
258 "libc std::collate<%s>::transform ()", charTname);
259
260 rw_info (0, __FILE__, __LINE__,
261 "libc std::collate<%s>::compare ()", charTname);
262
263 rw_info (0, __FILE__, __LINE__,
264 "std::collate<%s>::hash ()", charTname);
265
266 for (const char* locname = rw_locales (LC_COLLATE);
267 *locname; locname += std::strlen (locname) + 1) {
268
269 _TRY {
270 std::setlocale (LC_COLLATE, locname);
271 int max = MB_CUR_MAX;
272 if (max > 1)
273 continue;
274
275 std::locale loc;
276
277 _TRY {
278 loc = std::locale (locname);
279 }
280 _CATCH (...) {
281 rw_assert (false, __FILE__, __LINE__,
282 "std::locale(\"%s\") unexpectedly threw "
283 "an exception", locname);
284 continue;
285 }
286
287 const std::collate<charT> &co =
288 _STD_USE_FACET (std::collate<charT>, loc);
289 co._C_opts |= co._C_use_libc;
290 co._C_opts &= ~co._C_use_libstd;
291
292 // now the locale is set up so lets test the transform and
293 // compare functions
294
295 for (int loop_cntrl = 0; loop_cntrl < 10; loop_cntrl++) {
296
297#define STR_SIZE 16
298
299 charT str1 [STR_SIZE] = { 0 };
300 charT str2 [STR_SIZE] = { 0 };
301
302 // generate two random NUL-terminated strings
303 gen_str (str1, sizeof str1 / sizeof *str1);
304 gen_str (str2, sizeof str2 / sizeof *str2);
305
306 // call transform on the generated string
307 // not including the terminating NUL
308 const std::basic_string <charT, std::char_traits<charT>,
309 std::allocator<charT> > out =
310 co.transform (str1, str1 + sizeof str1 / sizeof *str1 - 1);
311
312 // get the size of the buffer needed to hold the
313 // transformed string (with the terminating NUL)
314 std::size_t size = 1U + c_xfrm (0, str1, 0);
315
316 // prevent errors caused by huge return values (e.g., MSVC)
317 if (size > STR_SIZE * 64)
318 size = 0;
319
320 std::basic_string <charT, std::char_traits<charT>,
321 std::allocator<charT> > c_out;
322
323 if (size) {
324 c_out.resize (size);
325
326 // call the C-library transform function
327 size = c_xfrm (&c_out [0], str1, size);
328
329 if (size > STR_SIZE * 64)
330 size = 0;
331
332 // shrink to fit (chop off the terminating NUL)
333 c_out.resize (size);
334 }
335
336 // make sure the output is the same
337 if (out != c_out) {
338 nfail[0]++;
339 rw_assert (false, __FILE__, __LINE__,
340 "%d. collate<%s>::transform(%s, ...) "
341 "== %{S}, got %{S} in locale(\"%s\")",
342 loop_cntrl, charTname, str1,
343 &c_out, &out, locname);
344 }
345
346 // now call compare on the two generated strings
347 int ret1 = co.compare (str1, str1 + sizeof str1 / sizeof *str1,
348 str2, str2 + sizeof str2 / sizeof *str2);
349
350 // call the C-library comparison function
351 int ret2 = c_strcoll (str1, str2);
352
353 // make sure the results are the same
354 if (ret1 != ret2) {
355 nfail [1]++;
356 rw_assert (false, __FILE__, __LINE__,
357 "%d. collate<%s>::compare(%s, ..., %s, ...) "
358 "== %d, got %d in locale(\"%s\")",
359 loop_cntrl, charTname, str1,
360 str2, ret2, ret1, locname);
361 }
362
363 // two strings that compare identically must hash
364 // identically as well. Calling hash on the same string is
365 // not very conclusive but generating strings that have exactly
366 // the same weights is not possible without knowing all the
367 // weight orderings
368 const long hashNum1 =
369 co.hash (str1, str1 + sizeof str1 / sizeof *str1);
370
371 const long hashNum2 =
372 co.hash (str1, str1 + sizeof str1 / sizeof *str1);
373
374 if (hashNum1 != hashNum2) {
375 nfail[2]++;
376 rw_assert (false, __FILE__, __LINE__,
377 "%d. collate<%s>::hash(%s, ...) == %d, "
378 "got %d in locale(\"%s\")",
379 loop_cntrl, charTname, str1,
380 hashNum1, hashNum2, locname);
381 }
382
383
384 }
385 }
386 _CATCH (...) {
387 rw_assert (false, __FILE__, __LINE__,
388 "locale(\"%s\") threw an exception", locname);
389 }
390 }
391
392 rw_assert (0 == nfail [0], __FILE__, __LINE__,
393 "collate<%s>::transform () failed %d times",
394 charTname, nfail [0]);
395
396 rw_assert (0 == nfail [1], __FILE__, __LINE__,
397 "collate<%s>::compare () failed %d times",
398 charTname, nfail [1]);
399
400 rw_assert (0 == nfail [2], __FILE__, __LINE__,
401 "collate<%s>::hash () failed %d times",
402 charTname, nfail [2]);
403}
404
405
407static const char*
408make_test_locale ()
409{
410 // create a temporary locale definition file that exercises as
411 // many different parts of the collate standard as possible
412
413 char lc_collate_src_path [L_tmpnam + sizeof LC_COLLATE_SRC + 2];
414 std::strcpy (lc_collate_src_path, locale_root);
415 std::strcat (lc_collate_src_path, SLASH);
416 std::strcat (lc_collate_src_path, LC_COLLATE_SRC);
417
418 std::FILE *fout = std::fopen (lc_collate_src_path, "w");
419
420 const char lc_collate_file[] = {
421 "LC_COLLATE\n"
422 "script <ALL_FORWARD>\n"
423 "collating-element <er> from \"<e><r>\"\n"
424 "collating-element <ic> from \"ic\"\n"
425 "collating-symbol <LETTER>\n"
426 "collating-symbol <COLLATING_ELEMENT>\n"
427 "collating-symbol <DIGIT>\n"
428
429 "order_start forward;backward;forward,position\n"
430 "<LETTER>\n"
431 "<COLLATING_ELEMENT>\n"
432 "<DIGIT>\n"
433
434 "<a> <a> <LETTER> IGNORE\n"
435 "<b> <b> <LETTER> IGNORE\n"
436
437 // "<c>" will have a non-ignored position ordering
438 "<c> <c> <LETTER> <c>\n"
439
440 // try giving "<d>" a many-to-one weight
441 "<d> \"<d><a>\" <LETTER> IGNORE\n"
442
443 // try giving "<e>" a decimal value weight
444 "<e> \\d139 <LETTER> IGNORE\n"
445
446 // try giving "<f>" an octal value weight
447 "<f> \\36 <LETTER> IGNORE\n"
448
449 // try giving "<g>" a hex value weight
450 "<g> \\x3A <LETTER> IGNORE\n"
451
452 "<zero> <zero> <DIGIT> IGNORE\n"
453 "<one> <one> <DIGIT> <zero>\n"
454 "<two> <two> <DIGIT> IGNORE\n"
455 "<three> <three> <DIGIT> IGNORE\n"
456 "<er> <a> <COLLATING_ELEMENT> IGNORE\n"
457
458 // the <ic> collating element will be equivalent to the letter <c>
459 "<ic> <c> <LETTER> <c>\n"
460 "UNDEFINED IGNORE IGNORE IGNORE\n"
461
462 "order_end\n"
463
464 // define a section in which all of the orders are forward orders
465 "order_start <ALL_FORWARD>;forward;forward;forward\n"
466 "<h>\n<i>\n<j>\n<k>\n"
467 "order_end\n"
468
469 // reorder the elementes in the <ALL_FORWARD> section to appear
470 // after the letter "<g>"
471 "reorder-after <g>\n"
472 "<h>\n<i>\n<j>\n<k>\n"
473
474 // try to reorder "<a>" after "<b>"
475 "reorder-after <b>\n"
476 "<a> <a> <LETTER> IGNORE\n"
477 "reorder-end\n"
478
479 "\nEND LC_COLLATE\n"
480 };
481
482 std::fputs (lc_collate_file, fout);
483
484 std::fclose (fout);
485
486 // create a temporary character map file
487
488 char lc_collate_cm_path [L_tmpnam + sizeof LC_COLLATE_CM + 2];
489 std::strcpy (lc_collate_cm_path, locale_root);
490 std::strcat (lc_collate_cm_path, SLASH);
491 std::strcat (lc_collate_cm_path, LC_COLLATE_CM);
492
493 fout = std::fopen (lc_collate_cm_path, "w");
494 pcs_write (fout, 0);
495
496 std::fclose (fout);
497
498 return rw_localedef ("-w", lc_collate_src_path,
499 lc_collate_cm_path,
501}
502
503
506template <class charT>
507/*static*/ void
508check_libstd_test_locale (const char* charTname)
509{
510 rw_info (0, __FILE__, __LINE__,
511 "libstd std::collate<%s>::transform () "
512 "collate test database", charTname);
513 rw_info (0, __FILE__, __LINE__,
514 "libstd std::collate<%s>::compare () collate test "
515 "database", charTname);
516 rw_info (0, __FILE__, __LINE__,
517 "libstd std::collate<%s>::hash () collate test "
518 "database", charTname);
519
520 const char* const locname = make_test_locale ();
521 if (locname) {
522
523 std::locale loc;
524
525 _TRY {
526 loc = std::locale (locname);
527 }
528 _CATCH (...) {
529 const char* const var = std::getenv (LOCALE_ROOT);
530
531 rw_assert (false, __FILE__, __LINE__,
532 "std::locale(\"%s\") unexpectedly threw "
533 "an exception; " LOCALE_ROOT "=%s",
534 locname, var ? var : "(null)");
535 return;
536 }
537
538 const std::collate<charT> &co =
539 _STD_USE_FACET (std::collate<charT>, loc);
540 co._C_opts |= co._C_use_libstd;
541 co._C_opts &= ~co._C_use_libc;
542
543#define IGNORE 0
544
545 // first lets make sure that each character was given the
546 // correct weight for each level.
547
548#undef TEST
549#define TEST(ch, w0, w1, w2, w3, w3_is_fp) \
550 test_weight_val (charTname, co, charT (ch), w0, w1, w2, w3, w3_is_fp)
551
552 TEST ('a', 6, IGNORE, 2, IGNORE, true);
553 TEST ('b', 5, IGNORE, 2, IGNORE, true);
554 TEST ('c', 7, IGNORE, 2, 7, true);
555 TEST ('d', 8, 6, 2, IGNORE, true);
556 TEST ('e', 139, IGNORE, 2, IGNORE, true);
557 TEST ('f', 30, IGNORE, 2, IGNORE, true);
558 TEST ('g', 58, IGNORE, 2, IGNORE, true);
559 TEST ('h', 12, IGNORE, 12, 12, false);
560 TEST ('i', 13, IGNORE, 13, 13, false);
561 TEST ('j', 14, IGNORE, 14, 14, false);
562 TEST ('k', 15, IGNORE, 15, 15, false);
563 TEST ('0', 16, IGNORE, 4, IGNORE, true);
564 TEST ('1', 17, IGNORE, 4, 16, true);
565 TEST ('2', 18, IGNORE, 4, IGNORE, true);
566 TEST ('3', 19, IGNORE, 4, IGNORE, true);
567 TEST ('l', IGNORE, IGNORE, IGNORE, IGNORE, true);
568
569 // make sure that strings collate the way we expect them to
570
571 // a should collate greater then b
572 test_string (charTname, co, "a", "b", 1) ;
573
574 // the collating element "er" should collate after 'a' and 'b'
575 // but before 'c'
576 test_string (charTname, co, "er", "a", 1);
577 test_string (charTname, co, "er", "b", 1);
578 test_string (charTname, co, "er", "c", -1);
579
580 // the collating element "ic" should be equivalent to the letter 'c'
581 test_string (charTname, co, "ic", "c", 0);
582
583
584 // two strings that compare identically must hash
585 // identically as well.
586 // since ic and c are equivalent elements string they should hash
587 // the same
588 test_hash (charTname, co, "c", "ic");
589 }
590 else
591 rw_assert (false, __FILE__, __LINE__,
592 "unable to create a locale database");
593}
594
595
597enum { bufsiz = 256 };
598
599template <class charT>
600/*static*/ void
601test_hash (const char* charTname, const std::collate<charT>& co,
602 const char* str1, const char* str2)
603{
604 // convert narrow string to a (possibly) wide representation
605 charT wstrbuf [bufsiz];
606 charT wstrbuf2 [bufsiz];
607
608 const charT* const wstr = widen (wstrbuf, str1);
609 const charT* const wstr2 = widen (wstrbuf2, str2);
610
611 long hashNum1 = co.hash (wstr, wstr + c_strlen (wstr));
612 long hashNum2 = co.hash (wstr2, wstr2 + c_strlen (wstr2));
613
614 if (hashNum1 != hashNum2) {
615 rw_assert (false, __FILE__, __LINE__,
616 "collate<%s>::hash(%s, ...) returned %d and\n "
617 "collate<%s>::hash(%s, ...) returned %d",
618 charTname, str1,
619 hashNum1, charTname, str2, hashNum2);
620 }
621}
622
623
625template <class charT>
626/*static*/ void
627test_string (const char* charTname, const std::collate<charT>& co,
628 const char* str1, const char* str2,
629 int expected_val)
630{
631 // convert narrow string to a (possibly) wide representation
632 charT wstrbuf [bufsiz];
633 charT wstrbuf2 [bufsiz];
634
635 const charT* const wstr = widen (wstrbuf, str1);
636 const charT* const wstr2 = widen (wstrbuf2, str2);
637
638 int ret = co.compare (wstr, wstr + c_strlen (wstr),
639 wstr2, wstr2 + c_strlen(wstr2));
640 if (ret != expected_val)
641 rw_assert (false, __FILE__, __LINE__,
642 "libstd std::collate<%s>::compare"
643 "(%s, ..., %s, ...) == %d, got %d",
644 charTname, str1, str2, expected_val, ret);
645}
646
647
649template <class charT>
650/*static*/ void
651test_weight_val (const char* charTname, const std::collate<charT>& co,
652 charT ch, int w1a, int w1b, int w2, int w3, bool w3_is_fp)
653{
654 int w [3][2] = { { w1a, w1b }, { w2, IGNORE }, { w3, IGNORE } };
655
656 typedef std::char_traits<charT> Traits;
657 typedef std::allocator<charT> Alloc;
658 typedef std::basic_string <charT, Traits, Alloc> String;
659
660 // construct an expected transformed string out of the weight arguments
661 String expected;
662
663 if (sizeof (charT) == sizeof (char)) {
664 for (int i = 0; i < 3; ++i) {
665 for (int k = 0; k < 2; ++k) {
666 if (w [i][k] != IGNORE) {
667 while (w [i][k] > _RWSTD_CHAR_MAX) {
668 expected += charT (_RWSTD_CHAR_MAX);
669 w [i][k] -= _RWSTD_CHAR_MAX;
670 }
671 expected += charT (w [i][k]);
672 }
673 else if (i == 2 && k == 0 && w3_is_fp)
674 expected += charT (_RWSTD_CHAR_MAX);
675 }
676
677 // mark the end of the pass
678 expected += charT (1);
679 }
680 }
681 else {
682 for (int i = 0; i < 3; ++i) {
683 for (int k = 0; k < 2; ++k) {
684 if (w [i][k] != IGNORE) {
685 expected += charT (w [i][k]);
686 }
687 else if (i == 2 && k == 0 && w3_is_fp)
688 expected += charT (_RWSTD_WCHAR_MAX);
689 }
690
691 expected += charT (1);
692 }
693 }
694
695 // get the transformed string
696 const String actual = co.transform (&ch, &ch + 1);
697
698 // make sure the strings are equal
699 rw_assert (expected != actual, __FILE__, __LINE__,
700 "collate<%s>::transform (\"%c\", ...) == %{S}, "
701 "got %{S}", charTname, ch, &expected, &actual);
702}
703
704
706template <class charT>
707/*static*/ void
708check_libstd (const char* charTname)
709{
710 rw_info (0, __FILE__, __LINE__,
711 "libstd std::collate<%s>::transform () sorting "
712 "file test", charTname);
713
714 rw_info (0, __FILE__, __LINE__,
715 "libstd std::collate<%s>::compare () sorting "
716 "file test", charTname);
717
718
719 // This test works by using a series of sorted input files
720 // we randomize the words in the input files and sort them using
721 // the proper locale's collate facet. This test will automatically
722 // generate the required locales.
723
724 static const char* const locales[][3] = {
725 //
726 // +-- locale name
727 // | +-- character set
728 // | | +-- input file name
729 // | | |
730 // V V V
731 { "cs_CZ", "ISO-8859-2", "collate.cs_CZ.in" }, // Czech, Czech Rep.
732 { "da_DK", "ISO-8859-1", "collate.da_DK.in" }, // Danish, Denmark
733 { "en_US", "ISO-8859-1", "collate.en_US.in" }, // English, US
734 { "hr_HR", "ISO-8859-2", "collate.hr_HR.in" }, // Hungarian, Hungary
735 { "sv_SE", "ISO-8859-1", "collate.sv_SE.in" }, // Swedish, Sweden
736 { "th_TH", "TIS-620", "collate.th_TH.in" } // Thai, Thailand
737 };
738
739 const std::size_t nlocales = sizeof locales / sizeof *locales;
740
741 typedef std::char_traits<charT> Traits;
742 typedef std::allocator<charT> Allocator;
743 typedef std::basic_string<charT, Traits, Allocator> String;
744
745 for (std::size_t i = 0; i < nlocales; ++i) {
746
747 const char* const locname =
748 rw_localedef ("-w --no_position",
749 locales [i][0], locales [i][1], 0);
750
751 if (locname) {
752
753 std::locale loc;
754
755 _TRY {
756 loc = std::locale (locname);
757 }
758 _CATCH (...) {
759 const char* const var = std::getenv (LOCALE_ROOT);
760
761 rw_assert (false, __FILE__, __LINE__,
762 "std::locale(\"%s\") unexpectedly threw "
763 "an exception; " LOCALE_ROOT "=%s",
764 locname, var ? var : "(null)");
765 continue;
766 }
767
768 const std::collate<charT> &co =
769 _STD_USE_FACET (std::collate<charT>, loc);
770
771 co._C_opts |= co._C_use_libstd;
772 co._C_opts &= ~co._C_use_libc;
773
774 typedef std::codecvt<charT, char, std::mbstate_t> CodeCvt;
775
777
778 cvt._C_opts |= cvt._C_use_libstd;
779 cvt._C_opts &= ~cvt._C_use_libc;
780
781 // 'in' holds the strings from the input file and is there
782 // sorting will take place.
783 String in [1000];
784
785 // out holds the strings located in the output file
786 String out [1000];
787
788#define TOPDIR "TOPDIR" /* the TOPDIR environment variable */
789
790 const char* in_path = std::getenv (TOPDIR);
791 if (!in_path || !*in_path) {
792 std::fprintf (stderr, "TOPDIR not defined or empty");
793
794 std::exit (1);
795 }
796
797 std::string path (in_path);
799 path += locales [i][2];
800
801 std::FILE* const f = std::fopen (path.c_str (), "r");
802 if (!f) {
803 rw_assert (false, __FILE__, __LINE__,
804 "file \"%s\" could not be opened", path.c_str ());
805 break;
806 }
807
808 std::size_t j = 0;
809 while (1) {
810 char next_line [bufsiz];
811
812 if (0 != std::fgets (next_line, bufsiz, f)) {
813
814 std::size_t line_len = std::strlen (next_line);
815
816 // get rid of the newline character
817 next_line [--line_len] = '\0';
818
819 // convert from external to internal encoding
820 // (both of which might be the same type)
821 charT to [bufsiz];
822 const char* from_next;
823 charT* to_next;
824
825 static std::mbstate_t initial;
826 std::mbstate_t mbs = initial;
827
828 const std::codecvt_base::result res =
829 cvt.in (mbs,
830 next_line, next_line + line_len + 1,
831 from_next,
832 to, to + sizeof to / sizeof *to,
833 to_next);
834
835 if (cvt.ok == res) {
836 in [j] = to;
837 out [j] = to;
838 }
839 else if (cvt.noconv == res) {
840 in [j] = (charT*)next_line;
841 out [j] = (charT*)next_line;
842 }
843
844 j++;
845 }
846 else
847 break;
848 }
849 // close the file
850 std::fclose (f);
851
852 // now bubble sort the items in the array
853 std::size_t idx;
854 std::size_t idx2;
855 String tmp;
856 String tmp2;
857
858 bool flipped;
859
860 if (j > 1) {
861 idx = 1;
862 do {
863 flipped = false;
864 for (idx2 = j - 1; idx2 >= idx; --idx2) {
865
866 const std::size_t idx1 = idx2 - 1;
867
868 if (co.compare (in [idx1].c_str (),
869 in [idx1].c_str () + in [idx1].size (),
870 in [idx2].c_str (),
871 in [idx2].c_str () + in [idx2].size ())
872 > 0) {
873 in [idx1].swap (in [idx2]);
874 flipped = true;
875 }
876 }
877 } while (++idx < j && flipped);
878 }
879
880 // the items are sorted now lets make sure that they are sorted
881 // the same way they are sorted in the output file.
882 std::size_t nfail = 0;
883
884 for (std::size_t k = 0; k < j; ++k) {
885
886 if (in [k] != out [k]) {
887
888 nfail++;
889
890 rw_assert (false, __FILE__, __LINE__,
891 "%{S} != %{S} at line %u of %s",
892 &out [k], &in [k],
893 k + 1, locales [i][2]);
894
895 }
896 }
897
898 rw_assert (!nfail, __FILE__, __LINE__,
899 "collate<%s>::compare() failed %d times",
900 charTname, nfail);
901 }
902 }
903}
904
905
908template <class charT>
909/*static*/ void
910check_hash_eff (const char* charTname)
911{
912 // test effectiveness of hash function
913 rw_info (0, __FILE__, __LINE__,
914 "std::collate<%s>::hash () -- effectiveness", charTname);
915
916 // since the same hash algorithm is used for both byname and non-byname
917 // facets, simply set up a std::locale that uses the "C" locale
918 std::locale loc ("C");
919 const std::collate<charT> &co =
920 _STD_USE_FACET (std::collate<charT>, loc);
921
922
923 int nfail = 0;
924
925 charT s[100];
926 bool next = true;
927
928 // generate `N' unique strings and hash them, storing each value
929 static const std::size_t N = 100;
930 long hashed [N] = { 0 };
931
932 std::size_t k;
933 for (k = 1; k != N && next; ++k) {
934 // generate a unique string
935 gen_str (s, k);
936
937 // compute hash value
938 hashed [k] = co.hash (s, s + std::char_traits<charT>::length(s));
939 }
940
941 // sort hashed values, then remove all duplicates
942 std::sort (hashed, hashed + k);
943 k = std::unique (hashed, hashed + k) - hashed;
944
945 // assert that the probability of a collision is less than 1%
946 // according to 22.2.4.1, p3, the likelihood should be very small,
947 // approaching 1.0 / numeric_limits<unsigned long>::max()
948 if (N - k > N /100) {
949 nfail++;
950 rw_assert (false, __FILE__, __LINE__,
951 "collate<%s>::do_hash (const char_type*, "
952 "const char_type*); "
953 "probability of collision %f",
954 charTname, double (N - k) / N);
955 }
956
957 rw_assert (!nfail, __FILE__, __LINE__,
958 "collate<%s>::do_hash () failed %d times", charTname,
959 nfail);
960
961}
962
963
966template <class charT>
967/*static*/ void
968check_NUL (const char* charTname)
969{
970 rw_info (0, __FILE__, __LINE__,
971 "std::collate<%s>::compare() with embedded NULs", charTname);
972
973 // verify that the collate facet correctly handles
974 // character sequences with embedded NULs
975
976 charT buf_1 [STR_SIZE];
977 charT buf_2 [STR_SIZE];
978
979 bool fail = false;
980
981 unsigned i = 0;
982
983 for (const char* locname = rw_locales (LC_COLLATE);
984 *locname && !fail; locname += std::strlen (locname) + 1, ++i) {
985
986 std::locale loc;
987
988 _TRY {
989 loc = std::locale (locname);
990 }
991 _CATCH (...) {
992 continue;
993 }
994
995 const std::size_t buflen = sizeof buf_1 / sizeof *buf_1 - 1;
996
997 gen_str (buf_1, sizeof buf_1 / sizeof *buf_1);
998 std::memcpy (buf_2, buf_1, sizeof buf_2);
999
1000 // compute a random index into the character buffers
1001 // at which to set the element to NUL; the indices
1002 // are such that (inx_1 > inx_2) always holds
1003 const std::size_t inx_2 = std::rand () % (buflen - 1);
1004 const std::size_t inx_1 =
1005 inx_2 + 1 + std::rand () % (buflen - inx_2 - 1);
1006
1007 buf_2 [inx_2] = charT ();
1008
1009 typedef std::collate<charT> CollateT;
1010
1011 const CollateT &col = std::use_facet<CollateT>(loc);
1012
1013 int cmp = col.compare (buf_1, buf_1 + buflen, buf_2, buf_2 + buflen);
1014
1015 if (!cmp) {
1016 typedef typename CollateT::string_type StringT;
1017
1018 const StringT str_1 (buf_1, buflen);
1019 const StringT str_2 (buf_2, buflen);
1020
1021 fail = true;
1022
1023 rw_assert (false, __FILE__, __LINE__,
1024 "collate<%s>::compare(%{S}, ..., %{S}, ...) "
1025 "!= 0, got 0 in locale(\"%s\")", charTname,
1026 &str_1, &str_2, locname);
1027 }
1028
1029 // set the character at the smaller index in both buffers to
1030 // NUL, then set a character at the larger index in the first
1031 // buffer to NUL, compare the two, and verify that the buffers
1032 // compare unequal (buf_1 probably less)
1033 buf_1 [inx_1] = charT ();
1034 buf_1 [inx_2] = charT ();
1035
1036 cmp = col.compare (buf_1, buf_1 + buflen, buf_2, buf_2 + buflen);
1037
1038 if (!cmp) {
1039 typedef typename CollateT::string_type StringT;
1040
1041 const StringT str_1 (buf_1, buflen);
1042 const StringT str_2 (buf_2, buflen);
1043
1044 fail = true;
1045
1046 rw_assert (false, __FILE__, __LINE__,
1047 "collate<%s>::compare(%{S}, ..., %{S}, ...) "
1048 "!= 0, got 0 in locale(\"%s\")", charTname,
1049 &str_1, &str_2, locname);
1050 }
1051 }
1052}
1053
1054
1056template <class charT>
1057/*static*/ void
1058do_test (const char* charTname)
1059{
1060 check_libstd_test_locale<charT> (charTname);
1061 check_libstd<charT> (charTname);
1062 check_libc<charT> (charTname);
1063 check_NUL<charT> (charTname);
1064 check_hash_eff<charT> (charTname);
1065}
1066
1067
1068#if _RWSTD_PATH_SEP == '/'
1069# define RM_RF "rm -rf "
1070#else
1071# define RM_RF "rmdir /Q /S "
1072#endif // _RWSTD_PATH_SEP == '/'
1073
1074
1075static int
1076run_test (int /*argc*/, char* /*argv*/ [])
1077{
1078 // set any additional environment variables defined in
1079 // the RW_PUTENV environment variable (if it exists)
1080 rw_putenv (0);
1081
1082 // create a temporary directory for files created by the test
1083 char namebuf [L_tmpnam];
1084 locale_root = std::tmpnam (namebuf);
1085
1086 char envvar [sizeof LOCALE_ROOT + L_tmpnam] = LOCALE_ROOT "=";
1087 std::strcat (envvar, locale_root);
1088
1089 rw_system ("mkdir %s", locale_root);
1090
1091 // set the LOCALE_ROOT variable where std::locale looks
1092 // for locale database files
1093 rw_putenv (envvar);
1094
1095 do_test<char> ("char");
1096
1097#ifndef _RWSTD_NO_WCHAR_T
1098
1099 do_test<wchar_t> ("wchar_t");
1100
1101#endif // _RWSTD_NO_WCHAR_T
1102
1103 // remove temporary locale databases created by the test
1104 rw_system (RM_RF "%s", locale_root);
1105
1106 return 0;
1107}
1108
1109
1110/*extern*/ int
1111main (int argc, char* argv [])
1112{
1113 return rw_test (argc, argv, __FILE__,
1114 "[lib.category.collate]",
1115 "22.2.4 The collate category",
1116 run_test, "", 0);
1117}
1118
int fail
#define N
Definition 17.names.cpp:157
#define i
Definition 17.names.cpp:126
#define s1
Definition 17.names.cpp:242
#define k
Definition 17.names.cpp:128
#define var
Definition 17.names.cpp:573
#define cmp
Definition 17.names.cpp:458
#define s2
Definition 17.names.cpp:268
#define w2
Definition 17.names.cpp:272
#define res
Definition 17.names.cpp:531
#define loc
Definition 17.names.cpp:492
#define w
Definition 17.names.cpp:139
#define f
Definition 17.names.cpp:119
#define Allocator
Definition 17.names.cpp:440
#define Traits
Definition 17.names.cpp:556
#define Alloc
Definition 17.names.cpp:439
#define buf
Definition 17.names.cpp:452
#define j
Definition 17.names.cpp:127
#define charT
Definition 17.names.cpp:456
#define tmp
Definition 17.names.cpp:553
#define s
#define len
Definition 17.names.cpp:488
#define n
Definition 17.names.cpp:130
void check_libstd_test_locale(const char *charTname)
void check_libc(const char *charTname)
#define RM_RF
#define STR_SIZE
void test_string(const char *charTname, const std::collate< charT > &co, const char *str1, const char *str2, int expected_val)
const char * widen(char *dst, const char *src)
#define TEST_LOCALE_NAME
#define SLASH
#define LC_COLLATE_CM
#define TOPDIR
#define LC_COLLATE_SRC
void test_weight_val(const char *charTname, const std::collate< charT > &co, charT ch, int w1a, int w1b, int w2, int w3, bool w3_is_fp)
#define TEST(ch, w0, w1, w2, w3, w3_is_fp)
void check_NUL(const char *charTname)
const char * narrow(char *dst, const char *src)
std::size_t c_strlen(const char *s1)
#define IGNORE
#define TESTS_ETC_PATH
int c_strcoll(const char *s1, const char *s2)
#define LOCALE_ROOT
const char * locale_root
void check_hash_eff(const char *charTname)
std::size_t c_xfrm(char *to, const char *from, std::size_t size)
void gen_str(charT *str, std::size_t size)
void test_hash(const char *charTname, const std::collate< charT > &co, const char *str1, const char *str2)
void check_libstd(const char *charTname)
void do_test(int line, const char *src, std::size_t resoff, ForwardIterator dummy_iter, const T *, const char *predname)
void run_test(const ForwardIterator &dummy, T *, Predicate *pred)
const int out
const int in
#define main
const unsigned nlocales
#define LC_COLLATE
#define stderr
#define L_tmpnam
#define MB_CUR_MAX
#define _TRY
Definition _defs.h:850
#define _STD_USE_FACET(type, arg)
Definition _defs.h:920
#define _RWSTD_ASSERT(ignore)
Definition _defs.h:1108
#define _CATCH(ex)
Definition _defs.h:851
A TOML path.
Definition path.hpp:209
const struct @19 locales[]
const char * str
Definition exec.cpp:170
_TEST_EXPORT int rw_test(int argc, char *argv[], const char *filename, const char *clause, const char *comment, int(*testfun)(int, char **), const char *optspec,...)
_TEST_EXPORT int rw_info(int, const char *, int, const char *,...)
Definition driver.cpp:1681
_TEST_EXPORT int rw_assert(int, const char *, int, const char *,...)
Definition driver.cpp:1630
_TEST_EXPORT int rw_putenv(const char *, int=-1)
Definition environ.cpp:69
_TEST_EXPORT void pcs_write(void *, const char *)
Definition file.cpp:89
_TEST_EXPORT char * rw_locales(int=_UNUSED_CAT, const char *=0, bool=true)
Definition locale.cpp:368
_TEST_EXPORT const char * rw_localedef(const char *, const char *, const char *, const char *)
Definition locale.cpp:154
_TEST_EXPORT int rw_system(const char *,...)
Definition process.cpp:328
#define max(a, b)