view lphobos/internal/adi.d @ 1638:0de4525a9ed6

Apply workaround for #395 by klickverbot.
author Christian Kamm <kamm incasoftware de>
date Mon, 08 Mar 2010 20:06:08 +0100
parents 88e23f8c2354
children
line wrap: on
line source

//_ adi.d

/**
 * Part of the D programming language runtime library.
 * Dynamic array property support routines
 */

/*
 *  Copyright (C) 2000-2006 by Digital Mars, www.digitalmars.com
 *  Written by Walter Bright
 *
 *  This software is provided 'as-is', without any express or implied
 *  warranty. In no event will the authors be held liable for any damages
 *  arising from the use of this software.
 *
 *  Permission is granted to anyone to use this software for any purpose,
 *  including commercial applications, and to alter it and redistribute it
 *  freely, in both source and binary form, subject to the following
 *  restrictions:
 *
 *  o  The origin of this software must not be misrepresented; you must not
 *     claim that you wrote the original software. If you use this software
 *     in a product, an acknowledgment in the product documentation would be
 *     appreciated but is not required.
 *  o  Altered source versions must be plainly marked as such, and must not
 *     be misrepresented as being the original software.
 *  o  This notice may not be removed or altered from any source
 *     distribution.
 */

//debug=adi;        // uncomment to turn on debugging printf's

//import std.stdio;
import std.c.stdio;
import std.c.stdlib;
import std.c.string;
//import std.string;
import std.outofmemory;
import std.utf;

pragma(no_typeinfo)
struct Array
{
    size_t length;
    void* ptr;
}

/**********************************************
 * Reverse array of chars.
 * Handled separately because embedded multibyte encodings should not be
 * reversed.
 */

extern (C) char[] _adReverseChar(char[] a)
{
    if (a.length > 1)
    {
    char[6] tmp;
    char[6] tmplo;
    char* lo = a.ptr;
    char* hi = &a[length - 1];

    while (lo < hi)
    {   auto clo = *lo;
        auto chi = *hi;

        //printf("lo = %d, hi = %d\n", lo, hi);
        if (clo <= 0x7F && chi <= 0x7F)
        {
        //printf("\tascii\n");
        *lo = chi;
        *hi = clo;
        lo++;
        hi--;
        continue;
        }

        uint stridelo = std.utf.UTF8stride[clo];

        uint stridehi = 1;
        while ((chi & 0xC0) == 0x80)
        {
        chi = *--hi;
        stridehi++;
        assert(hi >= lo);
        }
        if (lo == hi)
        break;

        //printf("\tstridelo = %d, stridehi = %d\n", stridelo, stridehi);
        if (stridelo == stridehi)
        {

        memcpy(tmp.ptr, lo, stridelo);
        memcpy(lo, hi, stridelo);
        memcpy(hi, tmp.ptr, stridelo);
        lo += stridelo;
        hi--;
        continue;
        }

        /* Shift the whole array. This is woefully inefficient
         */
        memcpy(tmp.ptr, hi, stridehi);
        memcpy(tmplo.ptr, lo, stridelo);
        memmove(lo + stridehi, lo + stridelo , (hi - lo) - stridelo);
        memcpy(lo, tmp.ptr, stridehi);
        memcpy(hi + stridehi - stridelo, tmplo.ptr, stridelo);

        lo += stridehi;
        hi = hi - 1 + (stridehi - stridelo);
    }
    }
    return a;
}

unittest
{
    string a = "abcd";
    string r;

    r = a.dup.reverse;
    //writefln(r);
    assert(r == "dcba");

    a = "a\u1235\u1234c";
    //writefln(a);
    r = a.dup.reverse;
    //writefln(r);
    assert(r == "c\u1234\u1235a");

    a = "ab\u1234c";
    //writefln(a);
    r = a.dup.reverse;
    //writefln(r);
    assert(r == "c\u1234ba");

    a = "\u3026\u2021\u3061\n";
    r = a.dup.reverse;
    assert(r == "\n\u3061\u2021\u3026");
}


/**********************************************
 * Reverse array of wchars.
 * Handled separately because embedded multiword encodings should not be
 * reversed.
 */

extern (C) wchar[] _adReverseWchar(wchar[] a)
{
    if (a.length > 1)
    {
    wchar[2] tmp;
    wchar* lo = a.ptr;
    wchar* hi = &a[length - 1];

    while (lo < hi)
    {   auto clo = *lo;
        auto chi = *hi;

        if ((clo < 0xD800 || clo > 0xDFFF) &&
        (chi < 0xD800 || chi > 0xDFFF))
        {
        *lo = chi;
        *hi = clo;
        lo++;
        hi--;
        continue;
        }

        int stridelo = 1 + (clo >= 0xD800 && clo <= 0xDBFF);

        int stridehi = 1;
        if (chi >= 0xDC00 && chi <= 0xDFFF)
        {
        chi = *--hi;
        stridehi++;
        assert(hi >= lo);
        }
        if (lo == hi)
        break;

        if (stridelo == stridehi)
        {   int stmp;

        assert(stridelo == 2);
        assert(stmp.sizeof == 2 * (*lo).sizeof);
        stmp = *cast(int*)lo;
        *cast(int*)lo = *cast(int*)hi;
        *cast(int*)hi = stmp;
        lo += stridelo;
        hi--;
        continue;
        }

        /* Shift the whole array. This is woefully inefficient
         */
        memcpy(tmp.ptr, hi, stridehi * wchar.sizeof);
        memcpy(hi + stridehi - stridelo, lo, stridelo * wchar.sizeof);
        memmove(lo + stridehi, lo + stridelo , (hi - (lo + stridelo)) * wchar.sizeof);
        memcpy(lo, tmp.ptr, stridehi * wchar.sizeof);

        lo += stridehi;
        hi = hi - 1 + (stridehi - stridelo);
    }
    }
    return a;
}

unittest
{
    wstring a = "abcd";
    wstring r;

    r = a.dup.reverse;
    assert(r == "dcba");

    a = "a\U00012356\U00012346c";
    r = a.dup.reverse;
    assert(r == "c\U00012346\U00012356a");

    a = "ab\U00012345c";
    r = a.dup.reverse;
    assert(r == "c\U00012345ba");
}


/**********************************************
 * Support for array.reverse property.
 */

extern (C) Array _adReverse(Array a, size_t szelem)
{
    if (a.length >= 2)
    {
        byte* tmp;
        byte[16] buffer;

        void* lo = a.ptr;
        void* hi = a.ptr + (a.length - 1) * szelem;

        tmp = buffer.ptr;
        if (szelem > 16)
        {
        //version (Win32)
            //tmp = cast(byte*) alloca(szelem);
        //else
            tmp = (new byte[szelem]).ptr;
        }

        for (; lo < hi; lo += szelem, hi -= szelem)
        {
        memcpy(tmp, lo,  szelem);
        memcpy(lo,  hi,  szelem);
        memcpy(hi,  tmp, szelem);
        }

        version (Win32)
        {
        }
        else
        {
        //if (szelem > 16)
            // BUG: bad code is generate for delete pointer, tries
            // to call delclass.
            //delete tmp;
        }
    }
    return a;
}

unittest
{
    debug(adi) printf("array.reverse.unittest\n");

    int[] a = new int[5];
    int[] b;
    size_t i;

    for (i = 0; i < 5; i++)
    a[i] = i;
    b = a.reverse;
    assert(b is a);
    for (i = 0; i < 5; i++)
    assert(a[i] == 4 - i);

    struct X20
    {   // More than 16 bytes in size
    int a;
    int b, c, d, e;
    }

    X20[] c = new X20[5];
    X20[] d;

    for (i = 0; i < 5; i++)
    {   c[i].a = i;
    c[i].e = 10;
    }
    d = c.reverse;
    assert(d is c);
    for (i = 0; i < 5; i++)
    {
    assert(c[i].a == 4 - i);
    assert(c[i].e == 10);
    }
}

/**********************************************
 * Support for array.reverse property for bit[].
 */

version (none)
{
extern (C) bit[] _adReverseBit(bit[] a)
    out (result)
    {
    assert(result is a);
    }
    body
    {
    if (a.length >= 2)
    {
        bit t;
        int lo, hi;

        lo = 0;
        hi = a.length - 1;
        for (; lo < hi; lo++, hi--)
        {
        t = a[lo];
        a[lo] = a[hi];
        a[hi] = t;
        }
    }
    return a;
    }

unittest
{
    debug(adi) printf("array.reverse_Bit[].unittest\n");

    bit[] b;
    b = new bit[5];
    static bit[5] data = [1,0,1,1,0];
    int i;

    b[] = data[];
    b.reverse;
    for (i = 0; i < 5; i++)
    {
    assert(b[i] == data[4 - i]);
    }
}
}

/**********************************************
 * Sort array of chars.
 */

extern (C) char[] _adSortChar(char[] a)
{
    if (a.length > 1)
    {
    dstring da = toUTF32(a);
    da.sort;
    size_t i = 0;
    foreach (dchar d; da)
    {   char[4] buf;
        string t = toUTF8(buf, d);
        a[i .. i + t.length] = t[];
        i += t.length;
    }
    delete da;
    }
    return a;
}

/**********************************************
 * Sort array of wchars.
 */

extern (C) wchar[] _adSortWchar(wchar[] a)
{
    if (a.length > 1)
    {
    dstring da = toUTF32(a);
    da.sort;
    size_t i = 0;
    foreach (dchar d; da)
    {   wchar[2] buf;
        wstring t = toUTF16(buf, d);
        a[i .. i + t.length] = t[];
        i += t.length;
    }
    delete da;
    }
    return a;
}

/**********************************************
 * Support for array.sort property for bit[].
 */

version (none)
{
extern (C) bit[] _adSortBit(bit[] a)
    out (result)
    {
    assert(result is a);
    }
    body
    {
    if (a.length >= 2)
    {
        size_t lo, hi;

        lo = 0;
        hi = a.length - 1;
        while (1)
        {
        while (1)
        {
            if (lo >= hi)
            goto Ldone;
            if (a[lo] == true)
            break;
            lo++;
        }

        while (1)
        {
            if (lo >= hi)
            goto Ldone;
            if (a[hi] == false)
            break;
            hi--;
        }

        a[lo] = false;
        a[hi] = true;

        lo++;
        hi--;
        }
    Ldone:
        ;
    }
    return a;
    }

unittest
{
    debug(adi) printf("array.sort_Bit[].unittest\n");
}
}

/***************************************
 * Support for array equality test.
 */

extern (C) int _adEq(Array a1, Array a2, TypeInfo ti)
{
    // printf("_adEq(a1.length = %d, a2.length = %d)\n", a1.length, a2.length);
    if (a1.length != a2.length)
    return 0;       // not equal
    auto sz = ti.next.tsize();
    auto p1 = a1.ptr;
    auto p2 = a2.ptr;
    
/+
    for (int i = 0; i < a1.length; i++)
    {
    printf("%4x %4x\n", (cast(short*)p1)[i], (cast(short*)p2)[i]);
    }
    printf("sz = %u\n", sz);
+/

    if (sz == 1)
    // We should really have a ti.isPOD() check for this
    return (memcmp(p1, p2, a1.length) == 0);
    
    for (size_t i = 0; i < a1.length; i++)
    {
    if (!ti.next.equals(p1 + i * sz, p2 + i * sz))
        return 0;       // not equal
    }
    return 1;           // equal
}

unittest
{
    debug(adi) printf("array.Eq unittest\n");

    string a = "hello";

    assert(a != "hel");
    assert(a != "helloo");
    assert(a != "betty");
    assert(a == "hello");
    assert(a != "hxxxx");
}

/***************************************
 * Support for bit array equality test for bit arrays.
 */

version (none)
{
extern (C) int _adEqBit(Array a1, Array a2)
{   size_t i;

    if (a1.length != a2.length)
    return 0;       // not equal
    auto p1 = cast(byte*)a1.ptr;
    auto p2 = cast(byte*)a2.ptr;
    auto n = a1.length / 8;
    for (i = 0; i < n; i++)
    {
    if (p1[i] != p2[i])
        return 0;       // not equal
    }

    ubyte mask;

    n = a1.length & 7;
    mask = cast(ubyte)((1 << n) - 1);
    //printf("i = %d, n = %d, mask = %x, %x, %x\n", i, n, mask, p1[i], p2[i]);
    return (mask == 0) || (p1[i] & mask) == (p2[i] & mask);
}

unittest
{
    debug(adi) printf("array.EqBit unittest\n");

    static bit[] a = [1,0,1,0,1];
    static bit[] b = [1,0,1];
    static bit[] c = [1,0,1,0,1,0,1];
    static bit[] d = [1,0,1,1,1];
    static bit[] e = [1,0,1,0,1];

    assert(a != b);
    assert(a != c);
    assert(a != d);
    assert(a == e);
}
}

/***************************************
 * Support for array compare test.
 */

extern (C) int _adCmp(Array a1, Array a2, TypeInfo ti)
{
    //printf("adCmp()\n");
    auto len = a1.length;
    if (a2.length < len)
    len = a2.length;
    auto sz = ti.tsize();
    void *p1 = a1.ptr;
    void *p2 = a2.ptr;

    if (sz == 1)
    {   // We should really have a ti.isPOD() check for this
    auto c = memcmp(p1, p2, len);
    if (c)
        return c;
    }
    else
    {
    for (size_t i = 0; i < len; i++)
    {
        auto c = ti.compare(p1 + i * sz, p2 + i * sz);
        if (c)
        return c;
    }
    }
    if (a1.length == a2.length)
    return 0;
    return (a1.length > a2.length) ? 1 : -1;
}

unittest
{
    debug(adi) printf("array.Cmp unittest\n");

    string a = "hello";

    assert(a >  "hel");
    assert(a >= "hel");
    assert(a <  "helloo");
    assert(a <= "helloo");
    assert(a >  "betty");
    assert(a >= "betty");
    assert(a == "hello");
    assert(a <= "hello");
    assert(a >= "hello");
}

/***************************************
 * Support for char array compare test.
 */

extern (C) int _adCmpChar(Array a1, Array a2)
{
version (D_InlineAsm_X86)
{
    asm
    {   naked           ;

        push    EDI     ;
        push    ESI     ;

        mov    ESI,a1+4[4+ESP]  ;
        mov    EDI,a2+4[4+ESP]  ;

        mov    ECX,a1[4+ESP]    ;
        mov    EDX,a2[4+ESP]    ;

    cmp ECX,EDX     ;
    jb  GotLength   ;

    mov ECX,EDX     ;

GotLength:
        cmp    ECX,4        ;
        jb    DoBytes       ;

        // Do alignment if neither is dword aligned
        test    ESI,3       ;
        jz    Aligned       ;

        test    EDI,3       ;
        jz    Aligned       ;
DoAlign:
        mov    AL,[ESI]     ; //align ESI to dword bounds
        mov    DL,[EDI]     ;

        cmp    AL,DL        ;
        jnz    Unequal      ;

        inc    ESI      ;
        inc    EDI      ;

        test    ESI,3       ;

        lea    ECX,[ECX-1]  ;
        jnz    DoAlign      ;
Aligned:
        mov    EAX,ECX      ;

    // do multiple of 4 bytes at a time

        shr    ECX,2        ;
        jz    TryOdd        ;

        repe            ;
    cmpsd           ;

        jnz    UnequalQuad  ;

TryOdd:
        mov    ECX,EAX      ;
DoBytes:
    // if still equal and not end of string, do up to 3 bytes slightly
    // slower.

        and    ECX,3        ;
        jz    Equal     ;

        repe            ;
    cmpsb           ;

        jnz    Unequal      ;
Equal:
        mov    EAX,a1[4+ESP]    ;
        mov    EDX,a2[4+ESP]    ;

        sub    EAX,EDX      ;
        pop    ESI      ;

        pop    EDI      ;
        ret         ;

UnequalQuad:
        mov    EDX,[EDI-4]  ;
        mov    EAX,[ESI-4]  ;

        cmp    AL,DL        ;
        jnz    Unequal      ;

        cmp    AH,DH        ;
        jnz    Unequal      ;

        shr    EAX,16       ;

        shr    EDX,16       ;

        cmp    AL,DL        ;
        jnz    Unequal      ;

        cmp    AH,DH        ;
Unequal:
        sbb    EAX,EAX      ;
        pop    ESI      ;

        or     EAX,1        ;
        pop    EDI      ;

        ret         ;
    }
}
else
{
    int len;
    int c;

    //printf("adCmpChar()\n");
    len = a1.length;
    if (a2.length < len)
    len = a2.length;
    c = memcmp(cast(char *)a1.ptr, cast(char *)a2.ptr, len);
    if (!c)
    c = cast(int)a1.length - cast(int)a2.length;
    return c;
}
}

unittest
{
    debug(adi) printf("array.CmpChar unittest\n");

    string a = "hello";

    assert(a >  "hel");
    assert(a >= "hel");
    assert(a <  "helloo");
    assert(a <= "helloo");
    assert(a >  "betty");
    assert(a >= "betty");
    assert(a == "hello");
    assert(a <= "hello");
    assert(a >= "hello");
}

/***************************************
 * Support for bit array compare test.
 */

version (none)
{
extern (C) int _adCmpBit(Array a1, Array a2)
{
    int len;
    uint i;

    len = a1.length;
    if (a2.length < len)
    len = a2.length;
    ubyte *p1 = cast(ubyte*)a1.ptr;
    ubyte *p2 = cast(ubyte*)a2.ptr;
    uint n = len / 8;
    for (i = 0; i < n; i++)
    {
    if (p1[i] != p2[i])
        break;      // not equal
    }
    for (uint j = i * 8; j < len; j++)
    {   ubyte mask = cast(ubyte)(1 << j);
    int c;

    c = cast(int)(p1[i] & mask) - cast(int)(p2[i] & mask);
    if (c)
        return c;
    }
    return cast(int)a1.length - cast(int)a2.length;
}

unittest
{
    debug(adi) printf("array.CmpBit unittest\n");

    static bit[] a = [1,0,1,0,1];
    static bit[] b = [1,0,1];
    static bit[] c = [1,0,1,0,1,0,1];
    static bit[] d = [1,0,1,1,1];
    static bit[] e = [1,0,1,0,1];

    assert(a >  b);
    assert(a >= b);
    assert(a <  c);
    assert(a <= c);
    assert(a <  d);
    assert(a <= d);
    assert(a == e);
    assert(a <= e);
    assert(a >= e);
}
}

/**********************************
 * Support for array.dup property.
 */

extern(C)
void* _d_realloc(void*, size_t);

extern(C)
Array _adDupT(TypeInfo ti, Array a)
{
    Array r;
    if (a.length)
    {
        auto sizeelem = ti.next.tsize();        // array element size
        auto size = a.length * sizeelem;
        r.ptr = _d_realloc(null,size);
        r.length = a.length;
        memcpy(r.ptr, a.ptr, size);
    }
    return r;
}

unittest
{
    int[] a;
    int[] b;
    int i;

    debug(adi) printf("array.dup.unittest\n");

    a = new int[3];
    a[0] = 1; a[1] = 2; a[2] = 3;
    b = a.dup;
    assert(b.length == 3);
    for (i = 0; i < 3; i++)
    assert(b[i] == i + 1);
}