1 //_ adi.d
2 
3 /**
4  * Part of the D programming language runtime library.
5  * Dynamic array property support routines
6  */
7 
8 /*
9  *  Copyright (C) 2000-2006 by Digital Mars, www.digitalmars.com
10  *  Written by Walter Bright
11  *
12  *  This software is provided 'as-is', without any express or implied
13  *  warranty. In no event will the authors be held liable for any damages
14  *  arising from the use of this software.
15  *
16  *  Permission is granted to anyone to use this software for any purpose,
17  *  including commercial applications, and to alter it and redistribute it
18  *  freely, in both source and binary form, subject to the following
19  *  restrictions:
20  *
21  *  o  The origin of this software must not be misrepresented; you must not
22  *     claim that you wrote the original software. If you use this software
23  *     in a product, an acknowledgment in the product documentation would be
24  *     appreciated but is not required.
25  *  o  Altered source versions must be plainly marked as such, and must not
26  *     be misrepresented as being the original software.
27  *  o  This notice may not be removed or altered from any source
28  *     distribution.
29  */
30 
31 /*
32  *  Modified by Sean Kelly <sean@f4.ca> for use with Tango.
33  */
34 
35 module rt.compiler.dmd.rt.adi;
36 
37 //debug=adi;            // uncomment to turn on debugging printf's
38 
39 private
40 {
41     import tango.stdc.string : memcpy, memmove, memcmp;
42     import tango.stdc.stdlib : alloca;
43     import rt.compiler.util.utf;
44 
45     enum BlkAttr : uint
46     {
47         FINALIZE = 0b0000_0001,
48         NO_SCAN  = 0b0000_0010,
49         NO_MOVE  = 0b0000_0100,
50         ALL_BITS = 0b1111_1111
51     }
52 
53     extern (C) void*  gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
54     extern (C) void*  gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
55     extern (C) void  gc_free( void* p );
56 }
57 
58 
59 struct Array
60 {
61     size_t  length;
62     void*   ptr;
63 }
64 
65 /**********************************************
66  * Reverse array of chars.
67  * Handled separately because embedded multibyte encodings should not be
68  * reversed.
69  */
70 
71 extern (C) char[] _adReverseChar(char[] a)
72 {
73     bool hadErrors=false;
74     if (a.length > 1)
75     {
76         char[6] tmp;
77         char[6] tmplo;
78         char* lo = a.ptr;
79         char* hi = &a[length - 1];
80 
81         while (lo < hi)
82         {   auto clo = *lo;
83             auto chi = *hi;
84 
85             debug(adi) printf("lo = %d, hi = %d\n", lo, hi);
86             if (clo <= 0x7F && chi <= 0x7F)
87             {
88                 debug(adi) printf("\tascii\n");
89                 *lo = chi;
90                 *hi = clo;
91                 lo++;
92                 hi--;
93                 continue;
94             }
95 
96             uint stridelo = UTF8stride[clo];
97             if (stridelo>6) { // invalid UTF-8 0xFF
98                 stridelo=1;
99                 hadErrors=true;
100             }
101 
102             uint stridehi = 1;
103             while ((chi & 0xC0) == 0x80)
104             {
105                 chi = *--hi;
106                 stridehi++;
107             }
108             if (lo == hi)
109             {
110                 if (lo>hi){
111                     hadErrors=true;
112                 }
113                 break;
114             }
115             if (stridehi>6){
116                 hadErrors=true;
117                 stridehi=6;
118             }
119 
120             debug(adi) printf("\tstridelo = %d, stridehi = %d\n", stridelo, stridehi);
121             if (stridelo == stridehi)
122             {
123                 memcpy(tmp.ptr, lo, stridelo);
124                 memcpy(lo, hi, stridelo);
125                 memcpy(hi, tmp.ptr, stridelo);
126                 lo += stridelo;
127                 hi--;
128                 continue;
129             }
130 
131             /* Shift the whole array. This is woefully inefficient
132              */
133             memcpy(tmp.ptr, hi, stridehi);
134             memcpy(tmplo.ptr, lo, stridelo);
135             memmove(lo + stridehi, lo + stridelo , cast(size_t)((hi - lo) - stridelo));
136             memcpy(lo, tmp.ptr, stridehi);
137             memcpy(hi + stridehi - stridelo, tmplo.ptr, stridelo);
138 
139             lo += stridehi;
140             hi = hi - 1 + cast(int)(stridehi - stridelo);
141         }
142     }
143     if (hadErrors)
144         throw new Exception("invalid UTF-8 sequence",__FILE__,__LINE__);
145     return a;
146 }
147 
148 unittest
149 {
150     char[] a = "abcd";
151 
152     auto r = a.dup.reverse;
153     //writefln(r);
154     assert(r == "dcba");
155 
156     a = "a\u1235\u1234c";
157     //writefln(a);
158     r = a.dup.reverse;
159     //writefln(r);
160     assert(r == "c\u1234\u1235a");
161 
162     a = "ab\u1234c";
163     //writefln(a);
164     r = a.dup.reverse;
165     //writefln(r);
166     assert(r == "c\u1234ba");
167 
168     a = "\u3026\u2021\u3061\n";
169     r = a.dup.reverse;
170     assert(r == "\n\u3061\u2021\u3026");
171 }
172 
173 
174 /**********************************************
175  * Reverse array of wchars.
176  * Handled separately because embedded multiword encodings should not be
177  * reversed.
178  */
179 
180 extern (C) long _adReverseWchar(wchar[] a)
181 {
182     bool hadErrors=false;
183     if (a.length > 1)
184     {
185         wchar[2] tmp;
186         wchar* lo = a.ptr;
187         wchar* hi = &a[length - 1];
188 
189         while (lo < hi)
190         {   auto clo = *lo;
191             auto chi = *hi;
192 
193             if ((clo < 0xD800 || clo > 0xDFFF) &&
194                 (chi < 0xD800 || chi > 0xDFFF))
195             {
196                 *lo = chi;
197                 *hi = clo;
198                 lo++;
199                 hi--;
200                 continue;
201             }
202 
203             int stridelo = 1 + (clo >= 0xD800 && clo <= 0xDBFF);
204 
205             int stridehi = 1;
206             if (chi >= 0xDC00 && chi <= 0xDFFF)
207             {
208                 chi = *--hi;
209                 stridehi++;
210             }
211             if (lo >= hi){
212                 if (lo>hi){
213                     hadErrors=true;
214                 }
215                 break;
216             }
217 
218             if (stridelo == stridehi)
219             {   int stmp;
220 
221                 assert(stridelo == 2);
222                 assert(stmp.sizeof == 2 * (*lo).sizeof);
223                 stmp = *cast(int*)lo;
224                 *cast(int*)lo = *cast(int*)hi;
225                 *cast(int*)hi = stmp;
226                 lo += stridelo;
227                 hi--;
228                 continue;
229             }
230 
231             /* Shift the whole array. This is woefully inefficient
232              */
233             memcpy(tmp.ptr, hi, stridehi * wchar.sizeof);
234             memcpy(hi + stridehi - stridelo, lo, stridelo * wchar.sizeof);
235             memmove(lo + stridehi, lo + stridelo , (hi - (lo + stridelo)) * wchar.sizeof);
236             memcpy(lo, tmp.ptr, stridehi * wchar.sizeof);
237 
238             lo += stridehi;
239             hi = hi - 1 + (stridehi - stridelo);
240         }
241     }
242     if (hadErrors)
243         throw new Exception("invalid UTF-16 sequence",__FILE__,__LINE__);
244     return *cast(long*)(&a);
245 }
246 
247 unittest
248 {
249     wchar[] a = "abcd";
250     wchar[] r;
251 
252     r = a.dup.reverse;
253     assert(r == "dcba");
254 
255     a = "a\U00012356\U00012346c";
256     r = a.dup.reverse;
257     assert(r == "c\U00012346\U00012356a");
258 
259     a = "ab\U00012345c";
260     r = a.dup.reverse;
261     assert(r == "c\U00012345ba");
262 }
263 
264 
265 /**********************************************
266  * Support for array.reverse property.
267  */
268 
269 extern (C) long _adReverse(Array a, size_t szelem)
270     out (result)
271     {
272         assert(result is *cast(long*)(&a));
273     }
274     body
275     {
276         if (a.length >= 2)
277         {
278             byte*    tmp;
279             byte[16] buffer;
280 
281             void* lo = a.ptr;
282             void* hi = a.ptr + (a.length - 1) * szelem;
283 
284             tmp = buffer.ptr;
285             if (szelem > 16)
286             {
287                 //version (Win32)
288                     tmp = cast(byte*) alloca(szelem);
289                 //else
290                     //tmp = gc_malloc(szelem);
291             }
292 
293             for (; lo < hi; lo += szelem, hi -= szelem)
294             {
295                 memcpy(tmp, lo,  szelem);
296                 memcpy(lo,  hi,  szelem);
297                 memcpy(hi,  tmp, szelem);
298             }
299 
300             version (Win32)
301             {
302             }
303             else
304             {
305                 //if (szelem > 16)
306                     // BUG: bad code is generate for delete pointer, tries
307                     // to call delclass.
308                     //gc_free(tmp);
309             }
310         }
311         return *cast(long*)(&a);
312     }
313 
314 unittest
315 {
316     debug(adi) printf("array.reverse.unittest\n");
317 
318     int[] a = new int[5];
319     int[] b;
320     size_t i;
321 
322     for (i = 0; i < 5; i++)
323         a[i] = i;
324     b = a.reverse;
325     assert(b is a);
326     for (i = 0; i < 5; i++)
327         assert(a[i] == 4 - i);
328 
329     struct X20
330     {   // More than 16 bytes in size
331         int a;
332         int b, c, d, e;
333     }
334 
335     X20[] c = new X20[5];
336     X20[] d;
337 
338     for (i = 0; i < 5; i++)
339     {   c[i].a = i;
340         c[i].e = 10;
341     }
342     d = c.reverse;
343     assert(d is c);
344     for (i = 0; i < 5; i++)
345     {
346         assert(c[i].a == 4 - i);
347         assert(c[i].e == 10);
348     }
349 }
350 
351 /**********************************************
352  * Sort array of chars.
353  */
354 
355 extern (C) long _adSortChar(char[] a)
356 {
357     if (a.length > 1)
358     {
359         dchar[] da = toUTF32(a);
360         da.sort;
361         size_t i = 0;
362         foreach (dchar d; da)
363         {   char[4] buf;
364             auto t = toUTF8(buf, d);
365             a[i .. i + t.length] = t[];
366             i += t.length;
367         }
368         delete da;
369     }
370     return *cast(long*)(&a);
371 }
372 
373 /**********************************************
374  * Sort array of wchars.
375  */
376 
377 extern (C) long _adSortWchar(wchar[] a)
378 {
379     if (a.length > 1)
380     {
381         dchar[] da = toUTF32(a);
382         da.sort;
383         size_t i = 0;
384         foreach (dchar d; da)
385         {   wchar[2] buf;
386             auto t = toUTF16(buf, d);
387             a[i .. i + t.length] = t[];
388             i += t.length;
389         }
390         delete da;
391     }
392     return *cast(long*)(&a);
393 }
394 
395 /***************************************
396  * Support for array equality test.
397  * Returns:
398  *      1       equal
399  *      0       not equal
400  */
401 
402 extern (C) int _adEq(Array a1, Array a2, TypeInfo ti)
403 {
404     debug(adi) printf("_adEq(a1.length = %d, a2.length = %d)\n", a1.length, a2.length);
405     if (a1.length != a2.length)
406         return 0; // not equal
407     auto sz = ti.tsize();
408     auto p1 = a1.ptr;
409     auto p2 = a2.ptr;
410 
411     if (sz == 1)
412         // We should really have a ti.isPOD() check for this
413         return (memcmp(p1, p2, a1.length) == 0);
414 
415     for (size_t i = 0; i < a1.length; i++)
416     {
417         if (!ti.equals(p1 + i * sz, p2 + i * sz))
418             return 0; // not equal
419     }
420     return 1; // equal
421 }
422 
423 extern (C) int _adEq2(Array a1, Array a2, TypeInfo ti)
424 {
425     debug(adi) printf("_adEq2(a1.length = %d, a2.length = %d)\n", a1.length, a2.length);
426     if (a1.length != a2.length)
427         return 0;               // not equal
428     if (!ti.equals(&a1, &a2))
429         return 0;
430     return 1;
431 }
432 unittest
433 {
434     debug(adi) printf("array.Eq unittest\n");
435 
436     auto a = "hello"c;
437 
438     assert(a != "hel");
439     assert(a != "helloo");
440     assert(a != "betty");
441     assert(a == "hello");
442     assert(a != "hxxxx");
443 }
444 
445 /***************************************
446  * Support for array compare test.
447  */
448 
449 extern (C) int _adCmp(Array a1, Array a2, TypeInfo ti)
450 {
451     debug(adi) printf("adCmp()\n");
452     auto len = a1.length;
453     if (a2.length < len)
454         len = a2.length;
455     auto sz = ti.tsize();
456     void *p1 = a1.ptr;
457     void *p2 = a2.ptr;
458 
459     if (sz == 1)
460     {   // We should really have a ti.isPOD() check for this
461         auto c = memcmp(p1, p2, len);
462         if (c)
463             return c;
464     }
465     else
466     {
467         for (size_t i = 0; i < len; i++)
468         {
469             auto c = ti.compare(p1 + i * sz, p2 + i * sz);
470             if (c)
471                 return c;
472         }
473     }
474     if (a1.length == a2.length)
475         return 0;
476     return (a1.length > a2.length) ? 1 : -1;
477 }
478 
479 extern (C) int _adCmp2(Array a1, Array a2, TypeInfo ti)
480 {
481     debug(adi) printf("_adCmp2(a1.length = %d, a2.length = %d)\n", a1.length, a2.length);
482     return ti.compare(&a1, &a2);
483 }
484 unittest
485 {
486     debug(adi) printf("array.Cmp unittest\n");
487 
488     auto a = "hello"c;
489 
490     assert(a >  "hel");
491     assert(a >= "hel");
492     assert(a <  "helloo");
493     assert(a <= "helloo");
494     assert(a >  "betty");
495     assert(a >= "betty");
496     assert(a == "hello");
497     assert(a <= "hello");
498     assert(a >= "hello");
499 }
500 
501 /***************************************
502  * Support for array compare test.
503  */
504 
505 extern (C) int _adCmpChar(Array a1, Array a2)
506 {
507   version (X86)
508   {
509     asm
510     {   naked                   ;
511 
512         push    EDI             ;
513         push    ESI             ;
514 
515         mov    ESI,a1+4[4+ESP]  ;
516         mov    EDI,a2+4[4+ESP]  ;
517 
518         mov    ECX,a1[4+ESP]    ;
519         mov    EDX,a2[4+ESP]    ;
520 
521         cmp     ECX,EDX         ;
522         jb      GotLength       ;
523 
524         mov     ECX,EDX         ;
525 
526 GotLength:
527         cmp    ECX,4            ;
528         jb    DoBytes           ;
529 
530         // Do alignment if neither is dword aligned
531         test    ESI,3           ;
532         jz    Aligned           ;
533 
534         test    EDI,3           ;
535         jz    Aligned           ;
536 DoAlign:
537         mov    AL,[ESI]         ; //align ESI to dword bounds
538         mov    DL,[EDI]         ;
539 
540         cmp    AL,DL            ;
541         jnz    Unequal          ;
542 
543         inc    ESI              ;
544         inc    EDI              ;
545 
546         test    ESI,3           ;
547 
548         lea    ECX,[ECX-1]      ;
549         jnz    DoAlign          ;
550 Aligned:
551         mov    EAX,ECX          ;
552 
553         // do multiple of 4 bytes at a time
554 
555         shr    ECX,2            ;
556         jz    TryOdd            ;
557 
558         repe                    ;
559         cmpsd                   ;
560 
561         jnz    UnequalQuad      ;
562 
563 TryOdd:
564         mov    ECX,EAX          ;
565 DoBytes:
566         // if still equal and not end of string, do up to 3 bytes slightly
567         // slower.
568 
569         and    ECX,3            ;
570         jz    Equal             ;
571 
572         repe                    ;
573         cmpsb                   ;
574 
575         jnz    Unequal          ;
576 Equal:
577         mov    EAX,a1[4+ESP]    ;
578         mov    EDX,a2[4+ESP]    ;
579 
580         sub    EAX,EDX          ;
581         pop    ESI              ;
582 
583         pop    EDI              ;
584         ret                     ;
585 
586 UnequalQuad:
587         mov    EDX,[EDI-4]      ;
588         mov    EAX,[ESI-4]      ;
589 
590         cmp    AL,DL            ;
591         jnz    Unequal          ;
592 
593         cmp    AH,DH            ;
594         jnz    Unequal          ;
595 
596         shr    EAX,16           ;
597 
598         shr    EDX,16           ;
599 
600         cmp    AL,DL            ;
601         jnz    Unequal          ;
602 
603         cmp    AH,DH            ;
604 Unequal:
605         sbb    EAX,EAX          ;
606         pop    ESI              ;
607 
608         or     EAX,1            ;
609         pop    EDI              ;
610 
611         ret                     ;
612     }
613   }
614   else
615   {
616     int len;
617     int c;
618 
619     debug(adi) printf("adCmpChar()\n");
620     len = a1.length;
621     if (a2.length < len)
622         len = a2.length;
623     c = memcmp(cast(char *)a1.ptr, cast(char *)a2.ptr, len);
624     if (!c)
625         c = cast(int)a1.length - cast(int)a2.length;
626     return c;
627   }
628 }
629 
630 unittest
631 {
632     debug(adi) printf("array.CmpChar unittest\n");
633 
634     auto a = "hello"c;
635 
636     assert(a >  "hel");
637     assert(a >= "hel");
638     assert(a <  "helloo");
639     assert(a <= "helloo");
640     assert(a >  "betty");
641     assert(a >= "betty");
642     assert(a == "hello");
643     assert(a <= "hello");
644     assert(a >= "hello");
645 }