xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/libdruntime/rt/aApplyR.d (revision 3587d6f89c746bbb4f886219ddacd41ace480ecf)
1 /**
2  * This code handles decoding UTF strings for foreach_reverse loops.  There are
3  * 6 combinations of conversions between char, wchar, and dchar, and 2 of each
4  * of those.
5  *
6  * Copyright: Copyright Digital Mars 2004 - 2010.
7  * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
8  * Authors:   Walter Bright, Sean Kelly
9  * Source: $(DRUNTIMESRC rt/_aApplyR.d)
10  */
11 module rt.aApplyR;
12 
13 import core.internal.utf;
14 
15 /**********************************************/
16 /* 1 argument versions */
17 
18 // dg is D, but _aApplyRcd() is C
19 extern (D) alias int delegate(void *) dg_t;
20 
21 extern (C) int _aApplyRcd1(in char[] aa, dg_t dg)
22 {   int result;
23 
24     debug(apply) printf("_aApplyRcd1(), len = %d\n", aa.length);
25     for (size_t i = aa.length; i != 0; )
26     {   dchar d;
27 
28         i--;
29         d = aa[i];
30         if (d & 0x80)
31         {   char c = cast(char)d;
32             uint j;
33             uint m = 0x3F;
34             d = 0;
35             while ((c & 0xC0) != 0xC0)
36             {   if (i == 0)
37                     onUnicodeError("Invalid UTF-8 sequence", 0);
38                 i--;
39                 d |= (c & 0x3F) << j;
40                 j += 6;
41                 m >>= 1;
42                 c = aa[i];
43             }
44             d |= (c & m) << j;
45         }
46         result = dg(cast(void *)&d);
47         if (result)
48             break;
49     }
50     return result;
51 }
52 
53 unittest
54 {
55     debug(apply) printf("_aApplyRcd1.unittest\n");
56 
57     auto s = "hello"c[];
58     int i;
59 
60     foreach_reverse (dchar d; s)
61     {
62         switch (i)
63         {
64             case 0:     assert(d == 'o'); break;
65             case 1:     assert(d == 'l'); break;
66             case 2:     assert(d == 'l'); break;
67             case 3:     assert(d == 'e'); break;
68             case 4:     assert(d == 'h'); break;
69             default:    assert(0);
70         }
71         i++;
72     }
73     assert(i == 5);
74 
75     s = "a\u1234\U000A0456b";
76     i = 0;
77     foreach_reverse (dchar d; s)
78     {
79         //printf("i = %d, d = %x\n", i, d);
80         switch (i)
81         {
82             case 0:     assert(d == 'b'); break;
83             case 1:     assert(d == '\U000A0456'); break;
84             case 2:     assert(d == '\u1234'); break;
85             case 3:     assert(d == 'a'); break;
86             default:    assert(0);
87         }
88         i++;
89     }
90     assert(i == 4);
91 }
92 
93 /*****************************/
94 
95 extern (C) int _aApplyRwd1(in wchar[] aa, dg_t dg)
96 {   int result;
97 
98     debug(apply) printf("_aApplyRwd1(), len = %d\n", aa.length);
99     for (size_t i = aa.length; i != 0; )
100     {   dchar d;
101 
102         i--;
103         d = aa[i];
104         if (d >= 0xDC00 && d <= 0xDFFF)
105         {   if (i == 0)
106                 onUnicodeError("Invalid UTF-16 sequence", 0);
107             i--;
108             d = ((aa[i] - 0xD7C0) << 10) + (d - 0xDC00);
109         }
110         result = dg(cast(void *)&d);
111         if (result)
112             break;
113     }
114     return result;
115 }
116 
117 unittest
118 {
119     debug(apply) printf("_aApplyRwd1.unittest\n");
120 
121     auto s = "hello"w[];
122     int i;
123 
124     foreach_reverse (dchar d; s)
125     {
126         switch (i)
127         {
128             case 0:     assert(d == 'o'); break;
129             case 1:     assert(d == 'l'); break;
130             case 2:     assert(d == 'l'); break;
131             case 3:     assert(d == 'e'); break;
132             case 4:     assert(d == 'h'); break;
133             default:    assert(0);
134         }
135         i++;
136     }
137     assert(i == 5);
138 
139     s = "a\u1234\U000A0456b";
140     i = 0;
141     foreach_reverse (dchar d; s)
142     {
143         //printf("i = %d, d = %x\n", i, d);
144         switch (i)
145         {
146             case 0:     assert(d == 'b'); break;
147             case 1:     assert(d == '\U000A0456'); break;
148             case 2:     assert(d == '\u1234'); break;
149             case 3:     assert(d == 'a'); break;
150             default:    assert(0);
151         }
152         i++;
153     }
154     assert(i == 4);
155 }
156 
157 /*****************************/
158 
159 extern (C) int _aApplyRcw1(in char[] aa, dg_t dg)
160 {   int result;
161 
162     debug(apply) printf("_aApplyRcw1(), len = %d\n", aa.length);
163     for (size_t i = aa.length; i != 0; )
164     {   dchar d;
165         wchar w;
166 
167         i--;
168         w = aa[i];
169         if (w & 0x80)
170         {   char c = cast(char)w;
171             uint j;
172             uint m = 0x3F;
173             d = 0;
174             while ((c & 0xC0) != 0xC0)
175             {   if (i == 0)
176                     onUnicodeError("Invalid UTF-8 sequence", 0);
177                 i--;
178                 d |= (c & 0x3F) << j;
179                 j += 6;
180                 m >>= 1;
181                 c = aa[i];
182             }
183             d |= (c & m) << j;
184 
185             if (d <= 0xFFFF)
186                 w = cast(wchar) d;
187             else
188             {
189                 w = cast(wchar) ((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
190                 result = dg(cast(void *)&w);
191                 if (result)
192                     break;
193                 w = cast(wchar) (((d - 0x10000) & 0x3FF) + 0xDC00);
194             }
195         }
196         result = dg(cast(void *)&w);
197         if (result)
198             break;
199     }
200     return result;
201 }
202 
203 unittest
204 {
205     debug(apply) printf("_aApplyRcw1.unittest\n");
206 
207     auto s = "hello"c[];
208     int i;
209 
210     foreach_reverse (wchar d; s)
211     {
212         switch (i)
213         {
214             case 0:     assert(d == 'o'); break;
215             case 1:     assert(d == 'l'); break;
216             case 2:     assert(d == 'l'); break;
217             case 3:     assert(d == 'e'); break;
218             case 4:     assert(d == 'h'); break;
219             default:    assert(0);
220         }
221         i++;
222     }
223     assert(i == 5);
224 
225     s = "a\u1234\U000A0456b";
226     i = 0;
227     foreach_reverse (wchar d; s)
228     {
229         //printf("i = %d, d = %x\n", i, d);
230         switch (i)
231         {
232             case 0:     assert(d == 'b'); break;
233             case 1:     assert(d == 0xDA41); break;
234             case 2:     assert(d == 0xDC56); break;
235             case 3:     assert(d == 0x1234); break;
236             case 4:     assert(d == 'a'); break;
237             default:    assert(0);
238         }
239         i++;
240     }
241     assert(i == 5);
242 }
243 
244 /*****************************/
245 
246 extern (C) int _aApplyRwc1(in wchar[] aa, dg_t dg)
247 {   int result;
248 
249     debug(apply) printf("_aApplyRwc1(), len = %d\n", aa.length);
250     for (size_t i = aa.length; i != 0; )
251     {   dchar d;
252         char c;
253 
254         i--;
255         d = aa[i];
256         if (d >= 0xDC00 && d <= 0xDFFF)
257         {   if (i == 0)
258                 onUnicodeError("Invalid UTF-16 sequence", 0);
259             i--;
260             d = ((aa[i] - 0xD7C0) << 10) + (d - 0xDC00);
261         }
262 
263         if (d & ~0x7F)
264         {
265             char[4] buf = void;
266 
267             auto b = toUTF8(buf, d);
268             foreach (char c2; b)
269             {
270                 result = dg(cast(void *)&c2);
271                 if (result)
272                     return result;
273             }
274             continue;
275         }
276         c = cast(char)d;
277         result = dg(cast(void *)&c);
278         if (result)
279             break;
280     }
281     return result;
282 }
283 
284 unittest
285 {
286     debug(apply) printf("_aApplyRwc1.unittest\n");
287 
288     auto s = "hello"w[];
289     int i;
290 
291     foreach_reverse (char d; s)
292     {
293         switch (i)
294         {
295             case 0:     assert(d == 'o'); break;
296             case 1:     assert(d == 'l'); break;
297             case 2:     assert(d == 'l'); break;
298             case 3:     assert(d == 'e'); break;
299             case 4:     assert(d == 'h'); break;
300             default:    assert(0);
301         }
302         i++;
303     }
304     assert(i == 5);
305 
306     s = "a\u1234\U000A0456b";
307     i = 0;
308     foreach_reverse (char d; s)
309     {
310         //printf("i = %d, d = %x\n", i, d);
311         switch (i)
312         {
313             case 0:     assert(d == 'b'); break;
314             case 1:     assert(d == 0xF2); break;
315             case 2:     assert(d == 0xA0); break;
316             case 3:     assert(d == 0x91); break;
317             case 4:     assert(d == 0x96); break;
318             case 5:     assert(d == 0xE1); break;
319             case 6:     assert(d == 0x88); break;
320             case 7:     assert(d == 0xB4); break;
321             case 8:     assert(d == 'a'); break;
322             default:    assert(0);
323         }
324         i++;
325     }
326     assert(i == 9);
327 }
328 
329 /*****************************/
330 
331 extern (C) int _aApplyRdc1(in dchar[] aa, dg_t dg)
332 {   int result;
333 
334     debug(apply) printf("_aApplyRdc1(), len = %d\n", aa.length);
335     for (size_t i = aa.length; i != 0;)
336     {   dchar d = aa[--i];
337         char c;
338 
339         if (d & ~0x7F)
340         {
341             char[4] buf = void;
342 
343             auto b = toUTF8(buf, d);
344             foreach (char c2; b)
345             {
346                 result = dg(cast(void *)&c2);
347                 if (result)
348                     return result;
349             }
350             continue;
351         }
352         else
353         {
354             c = cast(char)d;
355         }
356         result = dg(cast(void *)&c);
357         if (result)
358             break;
359     }
360     return result;
361 }
362 
363 unittest
364 {
365     debug(apply) printf("_aApplyRdc1.unittest\n");
366 
367     auto s = "hello"d[];
368     int i;
369 
370     foreach_reverse (char d; s)
371     {
372         switch (i)
373         {
374             case 0:     assert(d == 'o'); break;
375             case 1:     assert(d == 'l'); break;
376             case 2:     assert(d == 'l'); break;
377             case 3:     assert(d == 'e'); break;
378             case 4:     assert(d == 'h'); break;
379             default:    assert(0);
380         }
381         i++;
382     }
383     assert(i == 5);
384 
385     s = "a\u1234\U000A0456b";
386     i = 0;
387     foreach_reverse (char d; s)
388     {
389         //printf("i = %d, d = %x\n", i, d);
390         switch (i)
391         {
392             case 0:     assert(d == 'b'); break;
393             case 1:     assert(d == 0xF2); break;
394             case 2:     assert(d == 0xA0); break;
395             case 3:     assert(d == 0x91); break;
396             case 4:     assert(d == 0x96); break;
397             case 5:     assert(d == 0xE1); break;
398             case 6:     assert(d == 0x88); break;
399             case 7:     assert(d == 0xB4); break;
400             case 8:     assert(d == 'a'); break;
401             default:    assert(0);
402         }
403         i++;
404     }
405     assert(i == 9);
406 }
407 
408 /*****************************/
409 
410 extern (C) int _aApplyRdw1(in dchar[] aa, dg_t dg)
411 {   int result;
412 
413     debug(apply) printf("_aApplyRdw1(), len = %d\n", aa.length);
414     for (size_t i = aa.length; i != 0; )
415     {   dchar d = aa[--i];
416         wchar w;
417 
418         if (d <= 0xFFFF)
419             w = cast(wchar) d;
420         else
421         {
422             w = cast(wchar) ((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
423             result = dg(cast(void *)&w);
424             if (result)
425                 break;
426             w = cast(wchar) (((d - 0x10000) & 0x3FF) + 0xDC00);
427         }
428         result = dg(cast(void *)&w);
429         if (result)
430             break;
431     }
432     return result;
433 }
434 
435 unittest
436 {
437     debug(apply) printf("_aApplyRdw1.unittest\n");
438 
439     auto s = "hello"d[];
440     int i;
441 
442     foreach_reverse (wchar d; s)
443     {
444         switch (i)
445         {
446             case 0:     assert(d == 'o'); break;
447             case 1:     assert(d == 'l'); break;
448             case 2:     assert(d == 'l'); break;
449             case 3:     assert(d == 'e'); break;
450             case 4:     assert(d == 'h'); break;
451             default:    assert(0);
452         }
453         i++;
454     }
455     assert(i == 5);
456 
457     s = "a\u1234\U000A0456b";
458     i = 0;
459     foreach_reverse (wchar d; s)
460     {
461         //printf("i = %d, d = %x\n", i, d);
462         switch (i)
463         {
464             case 0:     assert(d == 'b'); break;
465             case 1:     assert(d == 0xDA41); break;
466             case 2:     assert(d == 0xDC56); break;
467             case 3:     assert(d == 0x1234); break;
468             case 4:     assert(d == 'a'); break;
469             default:    assert(0);
470         }
471         i++;
472     }
473     assert(i == 5);
474 }
475 
476 
477 /****************************************************************************/
478 /* 2 argument versions */
479 
480 // dg is D, but _aApplyRcd2() is C
481 extern (D) alias int delegate(void *, void *) dg2_t;
482 
483 extern (C) int _aApplyRcd2(in char[] aa, dg2_t dg)
484 {   int result;
485     size_t i;
486     size_t len = aa.length;
487 
488     debug(apply) printf("_aApplyRcd2(), len = %d\n", len);
489     for (i = len; i != 0; )
490     {   dchar d;
491 
492         i--;
493         d = aa[i];
494         if (d & 0x80)
495         {   char c = cast(char)d;
496             uint j;
497             uint m = 0x3F;
498             d = 0;
499             while ((c & 0xC0) != 0xC0)
500             {   if (i == 0)
501                     onUnicodeError("Invalid UTF-8 sequence", 0);
502                 i--;
503                 d |= (c & 0x3F) << j;
504                 j += 6;
505                 m >>= 1;
506                 c = aa[i];
507             }
508             d |= (c & m) << j;
509         }
510         result = dg(&i, cast(void *)&d);
511         if (result)
512             break;
513     }
514     return result;
515 }
516 
517 unittest
518 {
519     debug(apply) printf("_aApplyRcd2.unittest\n");
520 
521     auto s = "hello"c[];
522     int i;
523 
524     foreach_reverse (k, dchar d; s)
525     {
526         assert(k == 4 - i);
527         switch (i)
528         {
529             case 0:     assert(d == 'o'); break;
530             case 1:     assert(d == 'l'); break;
531             case 2:     assert(d == 'l'); break;
532             case 3:     assert(d == 'e'); break;
533             case 4:     assert(d == 'h'); break;
534             default:    assert(0);
535         }
536         i++;
537     }
538     assert(i == 5);
539 
540     s = "a\u1234\U000A0456b";
541     i = 0;
542     foreach_reverse (k, dchar d; s)
543     {
544         //printf("i = %d, k = %d, d = %x\n", i, k, d);
545         switch (i)
546         {
547             case 0:     assert(d == 'b'); assert(k == 8); break;
548             case 1:     assert(d == '\U000A0456'); assert(k == 4); break;
549             case 2:     assert(d == '\u1234'); assert(k == 1); break;
550             case 3:     assert(d == 'a'); assert(k == 0); break;
551             default:    assert(0);
552         }
553         i++;
554     }
555     assert(i == 4);
556 }
557 
558 /*****************************/
559 
560 extern (C) int _aApplyRwd2(in wchar[] aa, dg2_t dg)
561 {   int result;
562 
563     debug(apply) printf("_aApplyRwd2(), len = %d\n", aa.length);
564     for (size_t i = aa.length; i != 0; )
565     {   dchar d;
566 
567         i--;
568         d = aa[i];
569         if (d >= 0xDC00 && d <= 0xDFFF)
570         {   if (i == 0)
571                 onUnicodeError("Invalid UTF-16 sequence", 0);
572             i--;
573             d = ((aa[i] - 0xD7C0) << 10) + (d - 0xDC00);
574         }
575         result = dg(&i, cast(void *)&d);
576         if (result)
577             break;
578     }
579     return result;
580 }
581 
582 unittest
583 {
584     debug(apply) printf("_aApplyRwd2.unittest\n");
585 
586     auto s = "hello"w[];
587     int i;
588 
589     foreach_reverse (k, dchar d; s)
590     {
591         //printf("i = %d, k = %d, d = %x\n", i, k, d);
592         assert(k == 4 - i);
593         switch (i)
594         {
595             case 0:     assert(d == 'o'); break;
596             case 1:     assert(d == 'l'); break;
597             case 2:     assert(d == 'l'); break;
598             case 3:     assert(d == 'e'); break;
599             case 4:     assert(d == 'h'); break;
600             default:    assert(0);
601         }
602         i++;
603     }
604     assert(i == 5);
605 
606     s = "a\u1234\U000A0456b";
607     i = 0;
608     foreach_reverse (k, dchar d; s)
609     {
610         //printf("i = %d, k = %d, d = %x\n", i, k, d);
611         switch (i)
612         {
613             case 0:     assert(k == 4); assert(d == 'b'); break;
614             case 1:     assert(k == 2); assert(d == '\U000A0456'); break;
615             case 2:     assert(k == 1); assert(d == '\u1234'); break;
616             case 3:     assert(k == 0); assert(d == 'a'); break;
617             default:    assert(0);
618         }
619         i++;
620     }
621     assert(i == 4);
622 }
623 
624 /*****************************/
625 
626 extern (C) int _aApplyRcw2(in char[] aa, dg2_t dg)
627 {   int result;
628 
629     debug(apply) printf("_aApplyRcw2(), len = %d\n", aa.length);
630     for (size_t i = aa.length; i != 0; )
631     {   dchar d;
632         wchar w;
633 
634         i--;
635         w = aa[i];
636         if (w & 0x80)
637         {   char c = cast(char)w;
638             uint j;
639             uint m = 0x3F;
640             d = 0;
641             while ((c & 0xC0) != 0xC0)
642             {   if (i == 0)
643                     onUnicodeError("Invalid UTF-8 sequence", 0);
644                 i--;
645                 d |= (c & 0x3F) << j;
646                 j += 6;
647                 m >>= 1;
648                 c = aa[i];
649             }
650             d |= (c & m) << j;
651 
652             if (d <= 0xFFFF)
653                 w = cast(wchar) d;
654             else
655             {
656                 w = cast(wchar) ((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
657                 result = dg(&i, cast(void *)&w);
658                 if (result)
659                     break;
660                 w = cast(wchar) (((d - 0x10000) & 0x3FF) + 0xDC00);
661             }
662         }
663         result = dg(&i, cast(void *)&w);
664         if (result)
665             break;
666     }
667     return result;
668 }
669 
670 unittest
671 {
672     debug(apply) printf("_aApplyRcw2.unittest\n");
673 
674     auto s = "hello"c[];
675     int i;
676 
677     foreach_reverse (k, wchar d; s)
678     {
679         //printf("i = %d, k = %d, d = %x\n", i, k, d);
680         assert(k == 4 - i);
681         switch (i)
682         {
683             case 0:     assert(d == 'o'); break;
684             case 1:     assert(d == 'l'); break;
685             case 2:     assert(d == 'l'); break;
686             case 3:     assert(d == 'e'); break;
687             case 4:     assert(d == 'h'); break;
688             default:    assert(0);
689         }
690         i++;
691     }
692     assert(i == 5);
693 
694     s = "a\u1234\U000A0456b";
695     i = 0;
696     foreach_reverse (k, wchar d; s)
697     {
698         //printf("i = %d, k = %d, d = %x\n", i, k, d);
699         switch (i)
700         {
701             case 0:     assert(k == 8); assert(d == 'b'); break;
702             case 1:     assert(k == 4); assert(d == 0xDA41); break;
703             case 2:     assert(k == 4); assert(d == 0xDC56); break;
704             case 3:     assert(k == 1); assert(d == 0x1234); break;
705             case 4:     assert(k == 0); assert(d == 'a'); break;
706             default:    assert(0);
707         }
708         i++;
709     }
710     assert(i == 5);
711 }
712 
713 /*****************************/
714 
715 extern (C) int _aApplyRwc2(in wchar[] aa, dg2_t dg)
716 {   int result;
717 
718     debug(apply) printf("_aApplyRwc2(), len = %d\n", aa.length);
719     for (size_t i = aa.length; i != 0; )
720     {   dchar d;
721         char c;
722 
723         i--;
724         d = aa[i];
725         if (d >= 0xDC00 && d <= 0xDFFF)
726         {   if (i == 0)
727                 onUnicodeError("Invalid UTF-16 sequence", 0);
728             i--;
729             d = ((aa[i] - 0xD7C0) << 10) + (d - 0xDC00);
730         }
731 
732         if (d & ~0x7F)
733         {
734             char[4] buf = void;
735 
736             auto b = toUTF8(buf, d);
737             foreach (char c2; b)
738             {
739                 result = dg(&i, cast(void *)&c2);
740                 if (result)
741                     return result;
742             }
743             continue;
744         }
745         c = cast(char)d;
746         result = dg(&i, cast(void *)&c);
747         if (result)
748             break;
749     }
750     return result;
751 }
752 
753 unittest
754 {
755     debug(apply) printf("_aApplyRwc2.unittest\n");
756 
757     auto s = "hello"w[];
758     int i;
759 
760     foreach_reverse (k, char d; s)
761     {
762         //printf("i = %d, k = %d, d = %x\n", i, k, d);
763         assert(k == 4 - i);
764         switch (i)
765         {
766             case 0:     assert(d == 'o'); break;
767             case 1:     assert(d == 'l'); break;
768             case 2:     assert(d == 'l'); break;
769             case 3:     assert(d == 'e'); break;
770             case 4:     assert(d == 'h'); break;
771             default:    assert(0);
772         }
773         i++;
774     }
775     assert(i == 5);
776 
777     s = "a\u1234\U000A0456b";
778     i = 0;
779     foreach_reverse (k, char d; s)
780     {
781         //printf("i = %d, k = %d, d = %x\n", i, k, d);
782         switch (i)
783         {
784             case 0:     assert(k == 4); assert(d == 'b'); break;
785             case 1:     assert(k == 2); assert(d == 0xF2); break;
786             case 2:     assert(k == 2); assert(d == 0xA0); break;
787             case 3:     assert(k == 2); assert(d == 0x91); break;
788             case 4:     assert(k == 2); assert(d == 0x96); break;
789             case 5:     assert(k == 1); assert(d == 0xE1); break;
790             case 6:     assert(k == 1); assert(d == 0x88); break;
791             case 7:     assert(k == 1); assert(d == 0xB4); break;
792             case 8:     assert(k == 0); assert(d == 'a'); break;
793             default:    assert(0);
794         }
795         i++;
796     }
797     assert(i == 9);
798 }
799 
800 /*****************************/
801 
802 extern (C) int _aApplyRdc2(in dchar[] aa, dg2_t dg)
803 {   int result;
804 
805     debug(apply) printf("_aApplyRdc2(), len = %d\n", aa.length);
806     for (size_t i = aa.length; i != 0; )
807     {   dchar d = aa[--i];
808         char c;
809 
810         if (d & ~0x7F)
811         {
812             char[4] buf = void;
813 
814             auto b = toUTF8(buf, d);
815             foreach (char c2; b)
816             {
817                 result = dg(&i, cast(void *)&c2);
818                 if (result)
819                     return result;
820             }
821             continue;
822         }
823         else
824         {   c = cast(char)d;
825         }
826         result = dg(&i, cast(void *)&c);
827         if (result)
828             break;
829     }
830     return result;
831 }
832 
833 unittest
834 {
835     debug(apply) printf("_aApplyRdc2.unittest\n");
836 
837     auto s = "hello"d[];
838     int i;
839 
840     foreach_reverse (k, char d; s)
841     {
842         //printf("i = %d, k = %d, d = %x\n", i, k, d);
843         assert(k == 4 - i);
844         switch (i)
845         {
846             case 0:     assert(d == 'o'); break;
847             case 1:     assert(d == 'l'); break;
848             case 2:     assert(d == 'l'); break;
849             case 3:     assert(d == 'e'); break;
850             case 4:     assert(d == 'h'); break;
851             default:    assert(0);
852         }
853         i++;
854     }
855     assert(i == 5);
856 
857     s = "a\u1234\U000A0456b";
858     i = 0;
859     foreach_reverse (k, char d; s)
860     {
861         //printf("i = %d, k = %d, d = %x\n", i, k, d);
862         switch (i)
863         {
864             case 0:     assert(k == 3); assert(d == 'b'); break;
865             case 1:     assert(k == 2); assert(d == 0xF2); break;
866             case 2:     assert(k == 2); assert(d == 0xA0); break;
867             case 3:     assert(k == 2); assert(d == 0x91); break;
868             case 4:     assert(k == 2); assert(d == 0x96); break;
869             case 5:     assert(k == 1); assert(d == 0xE1); break;
870             case 6:     assert(k == 1); assert(d == 0x88); break;
871             case 7:     assert(k == 1); assert(d == 0xB4); break;
872             case 8:     assert(k == 0); assert(d == 'a'); break;
873             default:    assert(0);
874         }
875         i++;
876     }
877     assert(i == 9);
878 }
879 
880 /*****************************/
881 
882 extern (C) int _aApplyRdw2(in dchar[] aa, dg2_t dg)
883 {   int result;
884 
885     debug(apply) printf("_aApplyRdw2(), len = %d\n", aa.length);
886     for (size_t i = aa.length; i != 0; )
887     {   dchar d = aa[--i];
888         wchar w;
889 
890         if (d <= 0xFFFF)
891             w = cast(wchar) d;
892         else
893         {
894             w = cast(wchar) ((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
895             result = dg(&i, cast(void *)&w);
896             if (result)
897                 break;
898             w = cast(wchar) (((d - 0x10000) & 0x3FF) + 0xDC00);
899         }
900         result = dg(&i, cast(void *)&w);
901         if (result)
902             break;
903     }
904     return result;
905 }
906 
907 unittest
908 {
909     debug(apply) printf("_aApplyRdw2.unittest\n");
910 
911     auto s = "hello"d[];
912     int i;
913 
914     foreach_reverse (k, wchar d; s)
915     {
916         //printf("i = %d, k = %d, d = %x\n", i, k, d);
917         assert(k == 4 - i);
918         switch (i)
919         {
920             case 0:     assert(d == 'o'); break;
921             case 1:     assert(d == 'l'); break;
922             case 2:     assert(d == 'l'); break;
923             case 3:     assert(d == 'e'); break;
924             case 4:     assert(d == 'h'); break;
925             default:    assert(0);
926         }
927         i++;
928     }
929     assert(i == 5);
930 
931     s = "a\u1234\U000A0456b";
932     i = 0;
933     foreach_reverse (k, wchar d; s)
934     {
935         //printf("i = %d, k = %d, d = %x\n", i, k, d);
936         switch (i)
937         {
938             case 0:     assert(k == 3); assert(d == 'b'); break;
939             case 1:     assert(k == 2); assert(d == 0xDA41); break;
940             case 2:     assert(k == 2); assert(d == 0xDC56); break;
941             case 3:     assert(k == 1); assert(d == 0x1234); break;
942             case 4:     assert(k == 0); assert(d == 'a'); break;
943             default:    assert(0);
944         }
945         i++;
946     }
947     assert(i == 5);
948 }
949