#include <my_assert.h>

/* Start unit tests */



#ifdef WITH_UNITTESTS

#ifdef __cplusplus
extern "C" {
#endif


#if defined(WITH_BTANDCHECK) || defined(WANT_PRINTFSIMPLE)
#define printf printf_simple
#define fflush(s)       do { } while(0)
#else
#include <stdio.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#if defined(WITH_BTANDCHECK) || !defined(NO_ASSERT_CHECKS)

/* ok, we have an assert function */

#else

#undef dfwmalloc_assert
static void assert_abort (const char *msg, const char *file, int line)
{
    printf ("%s:%d: assert fail: (%s)\n", file, line, msg);
    abort ();
}

#define dfwmalloc_assert(x)       do { if (!(x)) assert_abort(#x, __FILE__, __LINE__); } while (0)

#endif



static void dump_compile_options (void)
{
#ifdef DISABLE_INLINE
    printf ("-DDISABLE_INLINE ");
#endif
#ifdef FEATHERWEIGHT
    printf ("-DFEATHERWEIGHT ");
#endif
#ifdef HAVE_PTHREAD
    printf ("-DHAVE_PTHREAD ");
#endif
#ifdef LIGHT
    printf ("-DLIGHT ");
#endif
#ifdef NO_ASSERT_CHECKS
    printf ("-DNO_ASSERT_CHECKS ");
#endif
#ifdef NO_CB_HEAPS
    printf ("-DNO_CB_HEAPS ");
#endif
#ifdef NO_HEAP_TRUNCATION
    printf ("-DNO_HEAP_TRUNCATION ");
#endif
#ifdef NO_INTERNAL_EXPOSE
    printf ("-DNO_INTERNAL_EXPOSE ");
#endif
#ifdef NO_RANKHEAP
    printf ("-DNO_RANKHEAP ");
#endif
#ifdef NULL_MODE
    printf ("-DNULL_MODE ");
#endif
#ifdef USE_TLS
    printf ("-DUSE_TLS ");
#endif
// #ifdef WANTED_ALIGNMENT
//     printf ("-DWANTED_ALIGNMENT=%d ", WANTED_ALIGNMENT);
// #endif
#ifdef WANT_PRINTFSIMPLE
    printf ("-DWANT_PRINTFSIMPLE ");
#endif
#ifdef WITH_ACCOUNTS
    printf ("-DWITH_ACCOUNTS ");
#endif
#ifdef WITH_BTANDCHECK
    printf ("-DWITH_BTANDCHECK ");
#endif
#ifdef WITH_UNITTESTS
    printf ("-DWITH_UNITTESTS ");
#endif
    printf ("\n");
}


STATICFN void speed_test (int n)
{
    unsigned long long t1, s1;
    unsigned long long t2, s2;
    volatile long q;
    volatile char *p;

    p = (char *) malloc (n);
    free ((void *) p);

    s1 = rdtsc ();
    usleep (100000);
    s2 = rdtsc ();

    printf ("start speed test (heap = %ld)\n", (long) dfwmalloc_heap_size ());

    t1 = rdtsc ();
    for (q = 0; ; q++) {
        p = (char *) malloc (n);
        free ((void *) p);
        if (!(q & 0xfff))
            if ((long) (rdtsc() - t1) > (long) (s2 - s1))
                break;
    }
    t2 = rdtsc ();

    printf ("end speed test (heap = %ld)\n", (long) dfwmalloc_heap_size ());

    printf ("q=%ld\n", (long) q);
    printf ("%ld %ld %ld %ld\n", (long) (t1 - s1), (long) (t2 - s1), (long) (s1 - s1), (long) (s2 - s1));

    printf ("        speed = %dfw ns,  %dfw clocks (%d)\n", (0.1 * 1e9 / (double) q) * ((double) (long) (t2 - t1)) / ((double) (long) (s2 - s1)), (double) (long) (t2 - t1) / (double) q, n);
}








#ifdef HAVE_PTHREAD


static pthread_attr_t my_thread_attr;
static pthread_attr_t my_thread_attr_nonjoinable;

static int my_pthread_create (pthread_t * thread, void *(*start_routine) (void *), void *arg)
{
    int r;
    r = pthread_create (thread, &my_thread_attr, start_routine, arg);
    return r;
}

#ifdef USE_TLS
#ifndef NULL_MODE
static int my_pthread_create_joinable (pthread_t * thread, int joinable, void *(*start_routine) (void *), void *arg)
{
    int r;
    if (joinable)
        r = pthread_create (thread, &my_thread_attr, start_routine, arg);
    else
        r = pthread_create (thread, &my_thread_attr_nonjoinable, start_routine, arg);
    return r;
}
#endif
#endif







static const char *retstr = "test";

STATICFN void *tf (void *arg)
{
    char *p;

    (void) arg;

    p = (char *) malloc (13);

    free (p);

#ifdef NO_PTHREAD_EXIT_OVERRIDE
#ifdef USE_TLS
    dfwmalloc_pthread_exit ();
#endif
#endif

    pthread_exit ((void *) retstr);

    return (void *) "WRONG";
}

static char *shared_pointer = NULL;
static char *shared_pointer2 = NULL;
static char *shared_pointer3 = NULL;
static char *shared_pointer4 = NULL;

static char *shared_pointer_bg = NULL;
static char *shared_pointer_bg2 = NULL;

STATICFN void *tf2 (void *arg)
{
    (void) arg;

    shared_pointer_bg = (char *) malloc (13);
    shared_pointer_bg2 = (char *) malloc (27);

    return (void *) retstr;
}

static int sem1 = 0;
static int sem2 = 0;

STATICFN void *tf3 (void *arg)
{
    (void) arg;

    shared_pointer = (char *) malloc (1301);
    printf(" ");
    shared_pointer2 = (char *) malloc (27);
    printf(" ");
    shared_pointer3 = (char *) malloc (51);
    printf(" ");
    shared_pointer4 = (char *) malloc (15);

    sem2 = 1;

    printf(" ");

    while (!sem1) {
        usleep (10000);
    }

    printf(" ");

    return (void *) retstr;
}

#endif



STATICFN void dump_stats (int stats_only)
{
    (void) stats_only;
#ifndef NULL_MODE
    if (!stats_only)
        dfwmalloc_log_leaks ();
#endif

    {
        struct dfwmalloc_stats st;
#ifndef NULL_MODE
        LONG delta = 0L;
#endif

        memset (&st, '\0', sizeof (st));

        dfwmalloc_stats (&st);

#ifndef NULL_MODE
#ifdef WITH_BTANDCHECK
        st.alloced -= st_adjust.alloced;
        st.blocks -= st_adjust.blocks;
        st.alloced_small -= st_adjust.alloced_small;
        st.blocks_small -= st_adjust.blocks_small;

        delta = st_adjust.alloced + st_adjust.alloced_small;
#endif
#endif
    
        printf ("\n");
        printf ("alloced=%ld\n", st.alloced);
        printf ("alloced_small=%ld\n", st.alloced_small);
        printf ("blocks=%ld\n", st.blocks);
        printf ("blocks_small=%ld\n", st.blocks_small);
        printf ("alloced_bt=%ld\n", st.alloced_bt);
        printf ("blocks_bt=%ld\n", st.blocks_bt);
        printf ("heap=%ld\n", st.heap);
        printf ("heap_small=%ld\n", st.heap_small);
        printf ("free_blocks=%ld\n", st.free_blocks);
        printf ("free_blocks_small=%ld\n", st.free_blocks_small);
        printf ("==========\n");
#ifdef FEATHERWEIGHT
        printf ("max_small_area=%ld kBytes\n", st.max_small_area >> 10);
        printf ("max_heap=%ld kBytes\n", st.max_heap >> 10);
#else
        printf ("max_small_area=%ld MBytes\n", st.max_small_area >> 20);
        printf ("max_heap=%ld MBytes\n", st.max_heap >> 20);
#endif
        printf ("==========\n");
        printf ("all_alloced=%ld\n", st.alloced + st.alloced_small + st.alloced_bt);
        printf ("all_blocks=%ld\n", st.blocks + st.blocks_small + st.blocks_bt);
        printf ("all_free=%ld\n", st.free_blocks + st.free_blocks_small);

#ifndef NULL_MODE
        dfwmalloc_assert (dfwmalloc_heap_size () == st.heap + st.heap_small);
        dfwmalloc_assert (dfwmalloc_alloced_bytes () - delta == st.alloced + st.alloced_small + st.alloced_bt);
#endif
    }
}

#if defined(USE_TLS) && !defined(NO_INTERNAL_EXPOSE)
static void check_user_interface (void)
{
    struct dfwmalloc *dfw;
    char *s;
    int leaks;

    dfw = dfwmalloc_new ();

    s = (char *) dfwmalloc_malloc (dfw, 169);
    s = (char *) dfwmalloc_realloc (dfw, s, 273);
    free (s);

    s = (char *) dfwmalloc_malloc (dfw, 168);
    s = (char *) realloc (s, 274);
    free (s);

    leaks = dfwmalloc_destroy (dfw);
    dfwmalloc_assert (leaks == 0);
    dfw = NULL;

    printf ("exported interface successful\n");
}
#endif

#if 0
static void triv(void)
{
struct dfwmalloc *dfw;
char *p, *q;

f = dfwmalloc_new ();

p = malloc(16);
q = malloc(16);
dfwmalloc_realloc (dfw, p, 17);
dfwmalloc_realloc (dfw, q, 17);
p = malloc(16);
exit(0);
}
#endif




#ifdef __cplusplus
}
#endif

#if defined(HAVE_PTHREAD) && defined(USE_TLS)
#ifndef NULL_MODE


/* #define TEST_FOR_REUSING_THREADS */

#ifdef TEST_FOR_REUSING_THREADS

#if 0

static char *conc_p1 = NULL;
static char *conc_p2 = NULL;
static char *conc_p3 = NULL;
static int conc_wait1 = 0;
static int conc_wait2 = 0;
static int conc_wait3 = 0;

static void *force_concurrency_problem_fn (void *use_)
{
    struct dfwmalloc *dfw;
    struct calltrace c;
    (void) use_;

    printf ("slave start %ld\n", pthread_self());

    conc_p1 = (char *) malloc (40000);
    conc_p2 = (char *) malloc (80000);
    conc_p3 = (char *) malloc (120000);
    
    asm volatile ("":::"memory");
    conc_wait1 = 1;
    asm volatile ("":::"memory");
    while (!conc_wait2)
        usleep (20000);


    printf ("slave dfwmalloc_pthread_exit(\n");
    dfwmalloc_pthread_exit ();
    printf (")slave\n");

    usleep (500000);

dfwmalloc_backtrace_if_enabled (&c);
f = dfwmalloc_tls;

printf("-- %d\n", dfw->shared_free_count);
// if (dfw->shared_free_count) {
//     dfwmalloc_free_shared_pointers (dfw, &c);
// }

    while (!conc_wait3)
        usleep (20000);
    printf ("slave pause\n");
    usleep (500000);
    printf ("slave free(\n");
    free (conc_p1);
    printf (")slave done\n");
    sleep (10);
    return NULL;
}

void force_concurrency_problem_master (void)
{
    printf ("master start %ld\n", pthread_self());
    while (!conc_wait1)
        usleep (20000);
    free (conc_p3);
    asm volatile ("":::"memory");
    conc_wait2 = 1;
    usleep (1500000);
    printf ("master free(\n");
    free (conc_p2);
    printf (")master free\n");
    asm volatile ("":::"memory");
    conc_wait3 = 1;
    asm volatile ("":::"memory");
    sleep (3);
    printf ("master done\n");
}

#endif

static void *leaky_fn (void *use_)
{
    int i, j;
    char *r[200];
    char *leak, *p;
    char *use;

    use = (char *) use_;

    if (!strcmp ((char *) use, "pause")) {
        leak = (char *) malloc (5);
        asm volatile ("":::"memory");
        strcpy (use, "cont");
        for (;;)
            sleep (1);
    }

    for (i = 1; i < 200; i++) {
        int l;
        l = i * 200;
        p = r[i] = (char *) malloc (l);
        for (j = 0; j < l; j++)
            p[j] = (i % 123);
    }

    leak = (char *) malloc (50000);

    for (i = 1; i < 200; i++) {
        int l;
        l = i * 200;
        p = r[i];
        for (j = 0; j < l; j++)
            dfwmalloc_assert (p[j] == (i % 123));
        free (r[i]);
    }

    pthread_exit ((void *) leak);
}
#endif

#endif
#endif

#ifdef __cplusplus
#include <iostream> 

class SomeClass {
    char *p;

public:
    SomeClass() {
        p = new char [20];
    }
    ~SomeClass() {
        delete[] p;
    }

};
#endif

int main (int argc, char **argv)
{
    char *p, *q;
    int i;
#ifdef HAVE_PTHREAD
    pthread_t thread_bg;
    void *retval_bg;
#endif

{
    const char *ms;
    ms = "testing log";
    (*dfwmalloc_log_) (ms, (int) strlen (ms));
}

#if 0
printf("%d\n", (int) sizeof (struct control_block));
exit(0);
#endif

#if 0
{
unsigned int u, t, i;
for (i = 0; i < 100000; i++) {
    u = GET_CREATE_TIME;
    t = ((ULONG64) rdtsc () >> (ROUGH_CPU_SPEED_MHZ + 20 /* million */ - 4 /* 16 */));
    printf("%u %u\n", u, t);
    usleep (1000000);
}
}
#endif

#ifndef NULL_MODE
#if defined(USE_TLS) && !defined(NO_CB_HEAPS)
    printf("HEAD_SIZE = %d, max_head = %d, CB_N_SIZES = %d, CB_SIZE_MAX = %d\n", (int) HEAD_SIZE, global->cb_heaps_info.max_head, (int) CB_N_SIZES, (int) CB_SIZE_MAX);
#endif
#endif

#ifdef USE_PRECOMPILED_STATIC_DATA
    printf("using precompiled data : global_free_list_sizes = %ld\n", (long) n_global_free_list_sizes);
#endif

#ifndef NO_CB_HEAPS
#ifndef NULL_MODE
    printf("sizeof(struct control_block) = %d\n", (int) sizeof(struct control_block));
#ifdef USE_TLS
    printf("SIZEOF_CB_SIZE = %d\n", (int) SIZEOF_CB_SIZE);
#endif
#endif
#endif

#ifndef NO_CB_HEAPS
#ifndef NULL_MODE
    {
        int y;
        y = 47 - N_ARENAS_BITS - CB_SIZE_BITS;
        printf("Size of each arena for CB allocs\n");
        printf("47 - N_ARENAS_BITS - CB_SIZE_BITS(%d) = %d (%d MBytes)\n", (int) CB_SIZE_BITS, (int) y, (1<<(y-20)));
    }
#endif
#endif


#ifdef HAVE_PTHREAD
    {
    int pr;
    int joinable = -1;

    pr = pthread_attr_init(&my_thread_attr);
    dfwmalloc_assert (!pr);
    pr = pthread_attr_setstacksize(&my_thread_attr, 16384);
    dfwmalloc_assert (!pr);

    pr = pthread_attr_init(&my_thread_attr_nonjoinable);
    dfwmalloc_assert (!pr);
    pr = pthread_attr_setstacksize(&my_thread_attr_nonjoinable, 16384);
    dfwmalloc_assert (!pr);
    pr = pthread_attr_setdetachstate(&my_thread_attr_nonjoinable, PTHREAD_CREATE_DETACHED);
    dfwmalloc_assert (!pr);
    pr = pthread_attr_getdetachstate(&my_thread_attr_nonjoinable, &joinable);
    dfwmalloc_assert (!pr);
    dfwmalloc_assert (joinable == PTHREAD_CREATE_DETACHED);
    }
#endif

#ifndef NULL_MODE
    if (argc == 2 && !strcmp (argv[1], "backtrace"))
        dfwmalloc_enable_backtrace ();
#endif
#ifndef NULL_MODE
#if defined(USE_TLS) && defined(HAVE_PTHREAD)
{

#ifdef TEST_FOR_REUSING_THREADS

#if 0
{
pthread_t thread;
printf("\n\n");
my_pthread_create_joinable (&thread, 0, force_concurrency_problem_fn, (void *) "the param");
force_concurrency_problem_master ();
exit (0);
}
#endif

    for (i = 0; i < N_ARENAS * 4; i++) {
        int r;
        void *retval;
        pthread_t thread;
        printf ("join i=%d\n", i);
        my_pthread_create_joinable (&thread, 1, leaky_fn, (void *) "the param");
        r = pthread_join (thread, &retval);
        dfwmalloc_assert (!r);
    }
    for (i = 0; i < N_ARENAS * 4; i++) {
        pthread_t thread;
        char use[] = "pause";
        printf ("pause i=%d\n", i);
        my_pthread_create_joinable (&thread, 0, leaky_fn, (void *) use);
        while (!strcmp (use, "pause"))
            usleep (20000);
    }
    exit(0);
#endif

}
#endif
#endif

#if 1

#ifdef __cplusplus
    {
        char *p = new char[50];
        delete p;

        SomeClass *sc;
        sc = new SomeClass[5];
        delete[] sc;

#if 0
        try
        { 
            while (1)
                p = new char[1024 * 1024];
        } 
        catch (std::bad_alloc & ba) 
        { 
            std::cerr << "bad_alloc caught: " << ba.what() << '\n'; 
        } 
#endif

    }
#endif

#if defined(USE_TLS) && !defined(NO_INTERNAL_EXPOSE)
    check_user_interface ();
#endif

#if 0
printf_simple("a-%dfw\n", (double) 0xffffffffffffe000UL);
printf("b-%dfw\n", (double) 0xffffffffffffe000UL);
printf_simple("c-%dfw\n", 1e40);
printf("d-%dfw\n", 1e40);
printf_simple("e-%dfw\n", 1e-40);
printf("dfw-%dfw\n", 1e-40);

double dd;
unsigned long ld = 0xffffffffffffffffL;
dd = (double) ld;
printf("%dfw\n", dd);
ld = 0xfffffffffffff800L;
dd = (double) ld;
printf("%dfw\n", dd);

dd = 18446744073709549568.0;
ld = (unsigned long) dd;
printf("%lx\n", ld);

dd = 18446744073709548545.0;
ld = (unsigned long) dd;
printf("%lx\n", ld);

exit (0);
#endif

#if 0
    printf_simple("[%dfw]\n", 1234567890129.45678);
    printf_simple("[%dfw]\n", -0.00012345678);
    exit(0);
#endif

    printf("MALLOC_MAX=%llu\n", (unsigned long long) MALLOC_MAX);

    (void) argc;
    (void) argv;

p = (char *) malloc(20);
q = (char *) malloc(28);

free(p);
free(q);


#ifndef NULL_MODE
    dfwmalloc_truncate_heap ();
#endif

    speed_test (192);
    speed_test (4096);

    p = (char *) malloc (1);

    printf ("%p\n", p);

    (void) i;

#ifdef HAVE_PTHREAD
#if defined(FEATHERWEIGHT) || defined(FOR_VALGRIND)
    for (i = 0; i < 2; i++)
#else
    for (i = 0; i < 10; i++)
#endif
    {
        pthread_t thread;
        void *retval = NULL;
        printf ("creating thread %d\n", i);
        my_pthread_create (&thread, tf, NULL);
        pthread_join (thread, &retval);
        dfwmalloc_assert (retval == retstr);
    }

    my_pthread_create (&thread_bg, tf2, NULL);

    usleep(10000);

    {
        pthread_t thread;
        void *retval = NULL;
        my_pthread_create (&thread, tf3, NULL);

        while (!sem2) {
            usleep (10000);
        }

        free (shared_pointer);
        printf(" ");
        shared_pointer2 = (char *) realloc (shared_pointer2, 29);
        printf(" ");
        shared_pointer4 = (char *) realloc (shared_pointer4, 34);

        sem1 = 1;

        pthread_join (thread, &retval);
        dfwmalloc_assert (retval == retstr);
    }

    pthread_join (thread_bg, &retval_bg);
    dfwmalloc_assert (retval_bg == retstr);

    free (shared_pointer_bg);
    free (shared_pointer_bg2);

#endif

    printf ("\n");

#ifndef NULL_MODE
    dfwmalloc_truncate_heap ();
#endif

#ifdef HAVE_PTHREAD
    pthread_attr_destroy (&my_thread_attr);
#endif

#endif
    dump_stats (0);
    printf ("\n\n");
    malloc_stats ();
    printf ("Success --   ");
    dump_compile_options ();
    printf ("\n");
    return 0;
}

#endif
