
module traits_getPointerBitmap;

import std.stdio;
static import std.traits;

// version = RTInfo;
// debug = LOG;

version(RTInfo)
    import gc.rtinfo;
else
    enum bool RTInfoMark__Monitor = false; // is __monitor GC allocated?


enum bytesPerPtr = (size_t.sizeof);
enum bytesPerBitmapWord = bytesPerPtr * bytesPerPtr * 8;

template allocatedSize(T)
{
    static if (is (T == class))
        enum allocatedSize = __traits(classInstanceSize, T);
    else
        enum allocatedSize = T.sizeof;
}

bool testBit(const(size_t)* p, size_t biti)
{
    enum BITS_SHIFT = (size_t.sizeof == 8 ? 6 : 5);
    enum BITS_MASK = (bytesPerPtr - 1);

    return (p[biti >> BITS_SHIFT] & (1 << (biti & BITS_MASK))) != 0;
}

void __testType(T)(size_t[] expected)
{
    // check compile time info
    enum bits  = (T.sizeof + bytesPerPtr - 1) / bytesPerPtr;
    enum words = (T.sizeof + bytesPerBitmapWord - 1) / bytesPerBitmapWord;
    version(RTInfo)
        enum info = RTInfoImpl2!(Unqual!T); // we want the array, not the pointer
    else
        enum info = __traits(getPointerBitmap,T); // we want the array, not the pointer

    debug(LOG) writef("%-20s:", T.stringof);
    debug(LOG) writef(" CT:%s", info);
    debug(LOG) writef(" EXP:%d %s", allocatedSize!T, expected);
    assert(info[0] == allocatedSize!T);
    assert(info[1..$] == expected);
    assert(words == expected.length);

    debug(LOG) writeln();
}

///////////////////////////////////////
struct S(T, aliasTo = void)
{
    static if(!is(aliasTo == void))
    {
        aliasTo a;
        alias a this;
    }

    size_t x;
    T t = void;
    void* p;

}

template tOff(T)
{
    enum tOff = T.t.offsetof / bytesPerPtr;
}

template pOff(T)
{
    enum pOff = T.p.offsetof / bytesPerPtr;
}

class C(T, aliasTo = void)
{
    static if(!is(aliasTo == void))
    {
        aliasTo a;
        alias a this;
    }

    size_t x;
    T t = void;
    void* p;
}

///////////////////////////////////////

void _testType(T)(size_t[] expected)
{
    __testType!(T)(expected);
    __testType!(const(T))(expected);
    __testType!(immutable(T))(expected);
    version(RTInfo) {} else // Unqual does not work with shared(T[N])
        __testType!(shared(T))(expected);
}

void testType(T)(size_t[] expected)
{
    _testType!(T)(expected);

    // generate bit pattern for S!T
    assert(expected.length == 1);
    size_t[] sexp;

    sexp ~= (expected[0] << tOff!(S!T)) | (1 << pOff!((S!T)));
    _testType!(S!T)(sexp);

    // prepend Object
    sexp[0] = (expected[0] << tOff!(S!(T, Object))) | (1 << pOff!(S!(T, Object))) | 1;
    _testType!(S!(T, Object))(sexp);

    // prepend string
    sexp[0] = (expected[0] << tOff!(S!(T, string))) | (1 << pOff!(S!(T, string))) | 2; // arr ptr
    _testType!(S!(T, string))(sexp);

    // generate bit pattern for C!T
    C!T ct = null;
    size_t mutexBit = (RTInfoMark__Monitor ? 2 : 0);
    size_t ctpOff = ct.p.offsetof / bytesPerPtr;
    size_t cttOff = ct.t.offsetof / bytesPerPtr;
    sexp[0] = (expected[0] << cttOff) | (1 << ctpOff) | mutexBit;
    _testType!(C!(T))(sexp);

    C!(T, string) cts = null;
    size_t ctspOff = cts.p.offsetof / bytesPerPtr;
    size_t ctstOff = cts.t.offsetof / bytesPerPtr;
    // generate bit pattern for C!T
    sexp[0] = (expected[0] << ctstOff) | (1 << ctspOff) | mutexBit | 0b1000; // arr ptr
    _testType!(C!(T, string))(sexp);
}

///////////////////////////////////////
alias void[2*size_t.sizeof] void2;
alias size_t[3] int3;
alias size_t*[3] pint3;
alias string[3] sint3;
alias string[3][2] sint3_2;
alias int delegate() dg;
alias int function() fn;
alias typeof(null) NullType;

// span multiple bitmap elements
struct Large
{
    size_t[30] data1;
    void* p1;
    size_t[1] val1;

    size_t[28] data2;
    void* p2;
    size_t[3] val2;

    size_t[16] data3;
    void* p3;
    size_t[15] val3;
}

class N
{
    struct Nested
    {
        // no outer for structs
        size_t x;
        void* p1;
        Large* s;

        void foo() {} // need member fnction to not be POD
    }
    class CNested
    {
        // implicit vtptr,monitor
        size_t x;
        void* p1;
        size_t y;
        // implicit outer
    }
    class CNestedDerived : CNested
    {
        size_t[3] z;
        void* p;
    }
}

union U
{
    size_t data[4];
    Large*[] arr; // { length, ptr }

    struct
    {
        size_t d1;
        size_t d2;
        size_t d3;
        void* p;
    }
}

void testRTInfo()
{
    testType!(bool)         ([ 0b0 ]);
    testType!(ubyte)        ([ 0b0 ]);
    testType!(short)        ([ 0b0 ]);
    testType!(int)          ([ 0b0 ]);
    testType!(long)         ([ 0b00 ]);
    testType!(double)       ([ 0b00 ]);
    testType!(ifloat)       ([ 0b0 ]);
    testType!(cdouble)      ([ 0b0000 ]);
    testType!(dg)           ([ 0b01 ]);
    testType!(fn)           ([ 0b0 ]);
    testType!(S!fn)         ([ 0b100 ]);
    testType!(NullType)     ([ 0b0 ]);
    version(D_LP64)
        testType!(__vector(float[4]))  ([ 0b00 ]);

    testType!(Object[int])       ([ 0b1 ]);
    testType!(Object[])       ([ 0b10 ]);
    testType!(string)         ([ 0b10 ]);

    testType!(int3)           ([ 0b000 ]);
    testType!(pint3)          ([ 0b111 ]);
    testType!(sint3)          ([ 0b101010 ]);
    testType!(sint3_2)        ([ 0b101010101010 ]);
    testType!(void2)          ([ 0b11 ]);
    testType!(U)              ([ 0b1010 ]);

    version(D_LP64)
        _testType!(Large)          ([ 0x1000_0000__4000_0000, 0x0001_0000 ]);
    else
        _testType!(Large)          ([ 0x4000_0000, 0x1000_0000, 0x0001_0000 ]);

    _testType!(N.CNested)     ([ 0b101000 ]);
    _testType!(N.CNestedDerived) ([ 0b1000101000 ]);

    testType!(N.Nested)       ([ 0b110 ]);

    struct SFNested
    {
        size_t[2] d;
        void* p1;
        fn f;
        // implicite outer

        void foo() {} // need member fnction to not be POD
    }

    class CFNested
    {
        // implicit vtptr,monitor
        size_t[2] d;
        void* p1;
        // implicite outer
    }

    testType!(SFNested)      ([ 0b10100 ]);
    _testType!(CFNested)     ([ 0b110000 ]);
}

void main()
{
    testRTInfo();
}
