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