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