
// Copyright (C) 2001-2006 by Digital Mars
// All Rights Reserved
// Written by Walter Bright
// www.digitalmars.com

// GC tester program

import std.c.stdio;
import std.c.stdlib;
import std.c.string;
import std.string;

import gcstats;
import std.gc;
import gcx;
import std.random;

alias GC gc_t;
//alias GC* gc_t;

gc_t newGC()
{
    version (all)
    {	void* p;
	ClassInfo ci = GC.classinfo;

	p = std.c.stdlib.malloc(ci.init.length);
	(cast(byte*)p)[0 .. ci.init.length] = ci.init[];
	return cast(GC)p;
    }
    else
    {
	return cast(gc_t)std.c.stdlib.calloc(1, GC.sizeof);
    }
}

void deleteGC(gc_t gc)
{
    gc.Dtor();
    std.c.stdlib.free(cast(void*)gc);
}

void printStats(gc_t gc)
{
    GCStats stats;

    //gc.getStats(stats);
    printf("poolsize = x%x, usedsize = x%x, freelistsize = x%x, freeblocks = %d, pageblocks = %d\n",
	stats.poolsize, stats.usedsize, stats.freelistsize, stats.freeblocks, stats.pageblocks);
}

uint PERMUTE(uint key)
{
    return key + 1;
}

void fill(void *p, uint key, uint size)
{
    uint i;
    byte *q = cast(byte *)p;

    for (i = 0; i < size; i++)
    {
	key = PERMUTE(key);
	q[i] = cast(byte)key;
    }
}

void verify(void *p, uint key, uint size)
{
    uint i;
    byte *q = cast(byte *)p;

    for (i = 0; i < size; i++)
    {
	key = PERMUTE(key);
	assert(q[i] == cast(byte)key);
    }
}

long desregs()
{
    return strlen("foo");
}

/* ---------------------------- */

void smoke()
{
    gc_t gc;

    printf("--------------------------smoke()\n");

    gc = newGC();
    deleteGC(gc);
printf("smoke.1\n");

    gc = newGC();
    gc.initialize();
    deleteGC(gc);
printf("smoke.2\n");

    gc = newGC();
    gc.initialize();
    char *p = cast(char *)gc.malloc(10);
    assert(p);
    strcpy(p, "Hello!");
//    char *p2 = gc.strdup(p);
//    printf("p2 = %x, '%s'\n", p2, p2);
//    int result = strcmp(p, p2);
//    assert(result == 0);
//    gc.strdup(p);

    printf("p  = %x\n", p);
    p = null;
    gc.fullCollect();
    printStats(gc);

    deleteGC(gc);
}

/* ---------------------------- */

void finalizer(void *p, void *dummy)
{
}

void smoke2()
{
    gc_t gc;
    int *p;
    int i;

    const int SMOKE2_SIZE = 100;
    int *foo[SMOKE2_SIZE];

    printf("--------------------------smoke2()\n");

    gc = newGC();
    gc.initialize();

    for (i = 0; i < SMOKE2_SIZE; i++)
    {
	p = cast(int *)gc.calloc(i + 1, 500);
	p[0] = i * 3;
	foo[i] = p;
	gc.setFinalizer(cast(void *)p, &finalizer);
    }

    for (i = 0; i < SMOKE2_SIZE; i += 2)
    {
	p = foo[i];
	if (p[0] != i * 3)
	{
	    printf("p = %x, i = %d, p[0] = %d\n", p, i, p[0]);
	    //c.stdio.fflush(stdout);
	}
	assert(p[0] == i * 3);
	gc.free(p);
    }

    p = null;
    foo[] = null;

    gc.fullCollect();
    printStats(gc);

    deleteGC(gc);
}

/* ---------------------------- */

void smoke3()
{
    gc_t gc;
    int *p;
    int i;

    printf("--------------------------smoke3()\n");

    gc = newGC();
    gc.initialize();

//    for (i = 0; i < 1000000; i++)
    for (i = 0; i < 1000; i++)
    {
	uint size = std.random.rand() % 2048;
	p = cast(int *)gc.malloc(size);
	memset(p, i, size);

	size = std.random.rand() % 2048;
	p = cast(int *)gc.realloc(p, size);
	memset(p, i + 1, size);
    }

    p = null;
    desregs();
    gc.fullCollect();
    printStats(gc);

    deleteGC(gc);
}

/* ---------------------------- */

void smoke4()
{
    gc_t gc;
    int *p;
    int i;

    printf("--------------------------smoke4()\n");

    gc = newGC();
    gc.initialize();

    for (i = 0; i < 80000; i++)
    {
	uint size = i;
	p = cast(int *)gc.malloc(size);
	memset(p, i, size);

	size = std.random.rand() % 2048;
	gc.check(p);
	p = cast(int *)gc.realloc(p, size);
	memset(p, i + 1, size);
    }

    p = null;
    desregs();
    gc.fullCollect();
    printStats(gc);

    deleteGC(gc);
}

/* ---------------------------- */

void smoke5(gc_t gc)
{
    byte *p;
    int i;
    int j;
    const int SMOKE5_SIZE = 1000;
    byte *array[SMOKE5_SIZE];
    uint offset[SMOKE5_SIZE];

    printf("--------------------------smoke5()\n");
    //printf("gc = %p\n", gc);
    //printf("gc = %p, gcx = %p, self = %x\n", gc, gc.gcx, gc.gcx.self);

    for (j = 0; j < 20; j++)
    {
	for (i = 0; i < 2000 /*4000*/; i++)
	{
	    uint size = (std.random.rand() % 2048) + 1;
	    uint index = std.random.rand() % SMOKE5_SIZE;

	    //printf("index = %d, size = %d\n", index, size);
	    p = array[index] - offset[index];
	    p = cast(byte *)gc.realloc(p, size);
	    if (array[index])
	    {	uint s;

		//printf("\tverify = %d\n", p[0]);
		s = offset[index];
		if (size < s)
		    s = size;
		verify(p, index, s);
	    }
	    array[index] = p;
	    fill(p, index, size);
	    offset[index] = std.random.rand() % size;
	    array[index] += offset[index];

	    //printf("p[0] = %d\n", p[0]);
	}
	gc.fullCollect();
    }

    p = null;
    array[] = null;
    gc.fullCollect();
    printStats(gc);
}

/* ---------------------------- */

void test1()
{
    printf("test1()\n");
    char[] a=new char[0];
    uint c = 200000;
    while (c--)
	a ~= 'x';
    //printf("a = '%.*s'\n", a);
    printf("test1() done\n");
}

/* ---------------------------- */

void test2()
{
    char[] str;

    for (int i = 0; i < 10_000; i++)
	str = str ~ "ABCDEFGHIJKLMNOPQRST";
}

/* ---------------------------- */

/* The Great Computer Language Shootout
   http://shootout.alioth.debian.org/

   http://www.bagley.org/~doug/shootout/

   converted to D by Dave Fladebo
   compile: dmd -O -inline -release hash.d
*/


void test3()
{
    int n = 1000;

    char[32]    str;
    int[char[]] X;

    for(int i = 1; i <= n; i++) {
        int len = sprintf(str.ptr,"%x",i);
        X[str[0..len].dup] = i;
    }

    int c;
    for(int i = n; i > 0; i--) {
        int len = sprintf(str.ptr,"%d",i);
        if(str[0..len] in X) c++;
    }

    printf("%d\n", c);
}

/* ---------------------------- */

void test4()
{
    const int iters = 1_000_000;
    C[] c = new C[iters];
    int i;
    for(i = 0; i < iters; i++)
    {
        c[i] = new C;
        delete c[i];
    }
    printf("%d\n", i);
}

class C
{
    int i, j, k;
    real l, m, n;
}

/* ---------------------------- */

/* The Computer Language Shootout Benchmarks
   http://shootout.alioth.debian.org/

   contributed by Dave Fladebo
   compile: dmd -O -inline -release binarytrees.d
*/


int test5()
{
    TreeNode*   stretchTree, longLivedTree, tempTree;
    int         depth, minDepth, maxDepth, stretchDepth, N = 1;

    minDepth = 4;
    maxDepth = (minDepth + 2) > N ? minDepth + 2 : N;
    stretchDepth = maxDepth + 1;

    stretchTree = TreeNode.BottomUpTree(0, stretchDepth);
    printf("stretch tree of depth %d\t check: %d\n", stretchDepth, stretchTree.ItemCheck);
    //TreeNode.DeleteTree(stretchTree);

    longLivedTree = TreeNode.BottomUpTree(0, maxDepth);

    for(depth = minDepth; depth <= maxDepth; depth += 2)
    {
        int check, iterations = 1 << (maxDepth - depth + minDepth);

        for(int i = 0; i < iterations; i++)
        {
            tempTree = TreeNode.BottomUpTree(i, depth);
            check += tempTree.ItemCheck;
            //TreeNode.DeleteTree(tempTree);

            tempTree = TreeNode.BottomUpTree(-i, depth);
            check += tempTree.ItemCheck;
            //TreeNode.DeleteTree(tempTree);
        }

        //printf(iterations * 2,"\t trees of depth ",depth,"\t check: ",check);
    }

    //writefln("long lived tree of depth ",maxDepth,"\t check: ",longLivedTree.ItemCheck);

    return 0;
}

struct TreeNode
{
public:
    static TreeNode* BottomUpTree(int item, int depth)
    {
        if(depth > 0)
            return TreeNode(item
                           ,BottomUpTree(2 * item - 1, depth - 1)
                           ,BottomUpTree(2 * item, depth - 1));
        return TreeNode(item);
    }

    int ItemCheck()
    {
        if(left)
            return item + left.ItemCheck() - right.ItemCheck();
        return item;
    }

    static void DeleteTree(TreeNode* tree)
    {
        if(tree.left)
        {
            DeleteTree(tree.left);
            DeleteTree(tree.right);
        }

        delete tree;
    }

private:
    TreeNode*           left, right;
    int                 item;

    static TreeNode* opCall(int item, TreeNode* left = null, TreeNode* right = null)
    {
        TreeNode* t = new TreeNode;
        t.left = left;
        t.right = right;
        t.item = item;
        return t;
    }

    //new(uint sz)
    //{
    //    return std.c.stdlib.malloc(sz);
    //}

    //delete(void* p)
    //{
    //    free(p);
    //}
}

/* ---------------------------- */

void test6()
{
    printf("test6\n");

    gc_t gc;

    gc = newGC();
    gc.initialize();

    auto p = gc.malloc(4096);
    assert(gc.capacity(p) == 4096);
    memset(p, 3, 4096);

    auto q = gc.realloc(p, 4096*4);
    assert(q == p);
    assert(gc.capacity(p) == 4096*4);
    memset(p, 4, 4096*4);

    q = gc.realloc(p, 4096*2);
    assert(q == p);
    assert(gc.capacity(p) == 4096*2);
    memset(p, 5, 4096*2);

    q = gc.realloc(p, 4096*2 + 1000);
    assert(q == p);
    assert(gc.capacity(p) == 4096*3);
    memset(p, 6, 4096*2 + 1000);

    q = gc.realloc(p, 4096*4);
    assert(q == p);
    assert(gc.capacity(p) == 4096*4);
    memset(p, 7, 4096*4);

    q = gc.realloc(p, 0);
    assert(q == null);
    assert(gc.capacity(p) == 0);

    gc.fullCollect();
    deleteGC(gc);
}

/* ---------------------------- */

void test7()
{
    printf("test7\n");

    gc_t gc;

    gc = newGC();
    gc.initialize();

    auto p = gc.malloc(4096);
    assert(gc.capacity(p) == 4096);
    memset(p, 3, 4096);

    auto q = gc.extend(p, 4096, 4096*2);
    assert(q == 4096*2 || q == 4096*3);

    auto s = gc.malloc(4096);
    q = gc.extend(p, 4096, 4096);
    assert(q == 0);

    gc.fullCollect();
    deleteGC(gc);
}

/* ---------------------------- */

int main(char[][] args)
{
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();

    gc_t gc;

    printf("GC test start\n");

    gc = newGC();
printf("gc = %p\n", gc);
    gc.initialize();

    smoke();
    smoke2();
    smoke3();
    smoke4();
printf("gc = %p\n", gc);
    smoke5(gc);

    deleteGC(gc);

    printf("GC test success\n");
    return EXIT_SUCCESS;
}
