
#include "cb_page_info.h"
#include "cb_int_t.h"

#include <unistd.h>
#include <my_assert.h>


#define MAX_BLOCKS               22000

long __dfwmalloc_cb_max_head (void);
long __dfwmalloc_cb_refs (struct dfwmalloc *dfw);
long __dfwmalloc_cb_heap_index_to_chunk_size (int i);
long __dfwmalloc_cb_heap_index_from_pointer (void *u);
struct cb_page_info __dfwmalloc_cb_page_info (int c);
long __dfwmalloc_cb_head_index (int c);
void dfwmalloc_dfwtruncate_heap (struct dfwmalloc *dfw);
void __dfwmalloc_dfwforce_total_heap (struct dfwmalloc *dfw, long n);


static long get_heap_small (struct dfwmalloc *dfw)
{
    struct dfwmalloc_stats s;
    dfwmalloc_dfwstats (dfw, &s);
    return s.heap_small;
}

static long long get_max_cb_heap (void)
{
    struct dfwmalloc_stats s;
    dfwmalloc_stats (&s);
    return s.max_small_area;
}

static long get_max_cb_size (void)
{
    struct dfwmalloc_stats s;
    dfwmalloc_stats (&s);
    return s.max_cb_size;
}

static long get_cb_blocks (struct dfwmalloc *dfw)
{
    struct dfwmalloc_stats s;
    dfwmalloc_dfwstats (dfw, &s);
    return s.blocks_small;
}

static long get_cb_bytes (struct dfwmalloc *dfw)
{
    struct dfwmalloc_stats s;
    dfwmalloc_dfwstats (dfw, &s);
    return s.alloced_small;
}



union xl;

union xl {
    union xl *next;
    char s[1];
};

static void check_cb_heaps5 (void)
{
#ifndef NO_HEAP_LIMIT_TEST
#ifndef FOR_VALGRIND
    struct dfwmalloc *dfw;
    int i, n, c, leaks;
    union xl *first = NULL;
    union xl *p, *q;
    struct cb_page_info info;
    int cb_size, head_index;

    if ((get_max_cb_heap () >> 20) > 4000) {
        printf ("%ld MBytes is too much for this test\n", (long) get_max_cb_heap () >> 20);
        return;
    }

    dfw = dfwmalloc_new ();
    assert (dfw);
    c = get_max_cb_size ();
    printf ("allocated %ld bytes (%ld MBytes) to test heap limits\n", (long) get_max_cb_heap (), (long) get_max_cb_heap () >> 20);
    head_index = __dfwmalloc_cb_head_index (c);
    info = __dfwmalloc_cb_page_info (head_index);
    cb_size = __dfwmalloc_cb_heap_index_to_chunk_size (info.heap_index);
    n = get_max_cb_heap () / cb_size;

    for (i = 0; i < n; i++) {
        int j;
        for (j = 0; j < info.n_blocks; j++) {
            p = (union xl *) dfwmalloc_malloc (dfw, c);
            if (!p) {
/* do not core dump here: this will create a *huge* core: */
                printf ("errno = %d\n", (int) errno);
                exit (1);
            }
            p->next = first;
            first = p;
        }
    }

/* the heap is now full, so test malloc failure due to no memory */
    p = (union xl *) dfwmalloc_malloc (dfw, c);
    assert (!p);
    assert (errno == ENOMEM);

/* now free one block and confirm the error goes away */
    p = first;
    first = first->next;
    free (p);   /* <== test free from a different arena */

/* now malloc return to non-error return */
    q = (union xl *) dfwmalloc_malloc (dfw, c);
    assert (q);

/* now check error once more */
    p = (union xl *) dfwmalloc_malloc (dfw, c);
    assert (!p);
    assert (errno == ENOMEM);

/* cleanup */

    while (first) {
        p = first->next;
        dfwmalloc_free (dfw, first);
        first = p;
    }
    free (q);

    leaks = dfwmalloc_destroy (dfw);
    assert (leaks == 0);
    dfw = NULL;
    printf ("cb heap limit test success\n");
#endif
#endif
}

static int memset_verify (char *p, int c, int n)
{
    int i;
    for (i = 0; i < n; i++)
        if ((unsigned char) p[i] != (unsigned char) c)
            return 1;
    return 0;
}

static void check_cb_heaps (long force)
{
    struct dfwmalloc *dfw;
    int leaks;
    struct cb_page_info info;

/* too much for the stack frame, so make static */
    static char *p[MAX_BLOCKS];
    int i, j, n = -1, m;

    printf("cb reference checks\n");

    for (j = 1; n != get_max_cb_size (); j++) {
        printf("%d.", j);
        fflush (stdout);

        info = __dfwmalloc_cb_page_info (j);
        n = info.round_up;
        m = info.n_blocks;

#define MALLOCV(i) \
    do { \
        int __i; \
        __i = (i); \
        p[__i] = (char *) dfwmalloc_malloc (dfw, n); \
        memset (p[__i], __i, n - 1); \
        p[__i][n - 1] = (char) (255 - __i); \
    } while (0)

#define FREEV(i) \
    do { \
        int __i; \
        __i = (i); \
        assert (!memset_verify (p[__i], __i, n - 1)); \
        assert (p[__i][n - 1] == (char) (255 - __i)); \
        memset (p[__i], 0x55, n - 1); \
        p[__i][n - 1] = (char) 0xaa; \
        dfwmalloc_free (dfw, p[__i]); \
    } while (0)

/* First check if we can fill up a control block and re-use everything on the free list.
 * This fails if the free list is corrupted in any way. One time this happens is if
 * pointers don't fit in an unsigned short -- which will happen if MIN_ALIGN is too
 * small: */
    dfw = dfwmalloc_new ();
    __dfwmalloc_dfwforce_total_heap (dfw, force);

    for (i = 0; i < m; i++) {
        int k;

        MALLOCV (i);

        assert (__dfwmalloc_cb_refs (dfw) == 1);
        for (k = 0; k <= i; k++) {
            assert (!memset_verify (p[k], k, n - 1));
            assert (p[k][n - 1] == (char) (255 - k));
        }
    }

    for (i = 1; i < m; i++) {
        int k;
        assert (!memset_verify (p[0], 0, n - 1));
        assert (p[0][n - 1] == (char) (255 - 0));
        for (k = i; k < m; k++) {
            assert (!memset_verify (p[k], k, n - 1));
            assert (p[k][n - 1] == (char) (255 - k));
        }
        FREEV (i);
        assert (__dfwmalloc_cb_refs (dfw) == 1);
    }

    for (i = 1; i < m; i++) {
        int k;
        MALLOCV (i);
        assert (__dfwmalloc_cb_refs (dfw) == 1);
        for (k = 0; k <= i; k++) {
            assert (!memset_verify (p[k], k, n - 1));
            assert (p[k][n - 1] == (char) (255 - k));
        }
    }

    for (i = 0; i < m; i++) {
        int k;
        for (k = i; k < m; k++) {
            assert (!memset_verify (p[k], k, n - 1));
            assert (p[k][n - 1] == (char) (255 - k));
        }
        assert (__dfwmalloc_cb_refs (dfw) == 1);
        FREEV (i);
        assert (__dfwmalloc_cb_refs (dfw) == (i < m - 1));
    }
    assert (__dfwmalloc_cb_refs (dfw) == 0);

    leaks = dfwmalloc_destroy (dfw);
    assert (leaks == 0);

    dfw = dfwmalloc_new ();
    __dfwmalloc_dfwforce_total_heap (dfw, force);

if (info.n_blocks == 1) {

    for (i = 0; i < 5; i++) {
        MALLOCV (i);
        assert (__dfwmalloc_cb_refs (dfw) == i + 1);
    }
    for (i = 0; i < 5; i++) {
        FREEV (i);
        assert (__dfwmalloc_cb_refs (dfw) == 4 - i);
    }

} else {

    for (i = 0; i < m; i++) {
        MALLOCV (i);
        assert (__dfwmalloc_cb_refs (dfw) == 1);
    }
    MALLOCV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 2);
    MALLOCV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 2 + 1 / info.n_blocks);
    MALLOCV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 2 + 2 / info.n_blocks);

    for (i = 0; i < m - 2; i++) {
        FREEV (i);
        assert (__dfwmalloc_cb_refs (dfw) == 2);
    }

    FREEV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 2 + 2 / info.n_blocks);
    FREEV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 1 + 2 / info.n_blocks);
    FREEV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 1 + 2 / info.n_blocks);
    FREEV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 1);
    FREEV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 0);

    for (i = 0; i < m - 2; i++) {
        MALLOCV (i);
        assert (__dfwmalloc_cb_refs (dfw) == 1);
    }
    MALLOCV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 1);
    MALLOCV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 1);
    MALLOCV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 2);
    MALLOCV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 2);
    MALLOCV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 2 + 2 / info.n_blocks);
    for (i = 0; i < m - 2; i++) {
        FREEV (i);
        assert (__dfwmalloc_cb_refs (dfw) == 2);
    }
    FREEV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 2 + 2 / info.n_blocks);
    FREEV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 1 + 2 / info.n_blocks);
    FREEV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 1 + 2 / info.n_blocks);
    FREEV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 1);
    FREEV (i++);
    assert (__dfwmalloc_cb_refs (dfw) == 0);

}

#ifdef WITH_ACCOUNTS
    assert (get_cb_blocks (dfw) == 0);
    assert (get_cb_bytes (dfw) == 0);
#endif

    leaks = dfwmalloc_destroy (dfw);
    assert (leaks == 0);

    }

    printf("\n");
    printf("cb reference checks -- pass\n");
}

struct one_cb {
    unsigned char *p[MAX_BLOCKS];
    unsigned char magic;
    int blocks;
    int num_bytes;
    int is_malloced;
};

static void cb_free (struct one_cb *b, struct dfwmalloc *dfw)
{
    int i;
    unsigned char c;
    c = b->magic;
    for (i = 0; i < b->blocks; i++) {
        assert (i < (int) (sizeof (b->p) / sizeof (b->p[0])));
        assert (b->p[i] != NULL);
        assert (b->p[i][0] == (i & 0xff));
        assert (b->p[i][1] == c);
        assert (b->p[i][b->num_bytes - 1] == (i & 0xff));
        assert (b->p[i][b->num_bytes - 2] == c);
    }
    for (i = 0; i < b->blocks; i++) {
        assert (i < (int) (sizeof (b->p) / sizeof (b->p[0])));
        assert (b->p[i] != NULL);
        assert (b->p[i][0] == (i & 0xff));
        assert (b->p[i][1] == c);
        assert (b->p[i][b->num_bytes - 1] == (i & 0xff));
        assert (b->p[i][b->num_bytes - 2] == c);
        dfwmalloc_free (dfw, b->p[i]);
    }
    memset (b, '\0', sizeof (*b));
}

static void cb_fill (struct one_cb *b, struct dfwmalloc *dfw, int num_bytes, int blocks)
{
    int i;
    unsigned char c;
    c = b->magic = random() & 0xff;
    b->blocks = blocks;
    b->num_bytes = num_bytes;
    b->is_malloced = 1;

    for (i = 0; i < blocks; i++) {
        assert (i < (int) (sizeof (b->p) / sizeof (b->p[0])));
        assert (b->num_bytes == num_bytes);
        b->p[i] = (unsigned char *) dfwmalloc_malloc (dfw, num_bytes);
        assert (b->p[i] != NULL);
        b->p[i][0] = i;
        b->p[i][1] = c;
        b->p[i][num_bytes - 1] = i;
        b->p[i][num_bytes - 2] = c;
    }
}

static void check_cb_heaps4 (void)
{
#define N_CB    50
/* too much for the stack frame, so make static */
    static struct one_cb b[N_CB];
    struct dfwmalloc *dfw;
    int n;
    int n2;
    int head_index;
    int head_index2;
    int i;
    int leaks;
    int cb_size;
    struct cb_page_info info;
    struct cb_page_info info2;
    unsigned char *q, *qc;

    dfw = dfwmalloc_new ();

    n = __dfwmalloc_cb_page_info (__dfwmalloc_cb_max_head () / 4).round_up + 1;

    for (;;) {
        head_index = __dfwmalloc_cb_head_index (n);
        info = __dfwmalloc_cb_page_info (head_index);

    /* try to find a different head index on the same heap index for this experiment */
        for (n2 = n;; n2++) {
            head_index2 = __dfwmalloc_cb_head_index (n2);
            info2 = __dfwmalloc_cb_page_info (head_index2);
            if (head_index2 != head_index) {
                n2 = info2.round_up;
                break;
            }
        }

        if (info2.heap_index != info.heap_index) {
            assert (n < 65536);
            n += 16;
            continue;
        }
        break;
    }

    assert (info2.heap_index == info.heap_index);
    printf("next head at %d\n", n2);

    for (i = 0; i < N_CB; i++)
        b[i].is_malloced = 0;

    cb_size = __dfwmalloc_cb_heap_index_to_chunk_size (info.heap_index);

    assert (get_heap_small (dfw) == 0);
    cb_fill (&b[0], dfw, n, info.n_blocks);
    assert (get_heap_small (dfw) == cb_size);
    cb_fill (&b[1], dfw, n, info.n_blocks);
    assert (get_heap_small (dfw) == cb_size * 2);
    cb_free (&b[0], dfw);
    assert (get_heap_small (dfw) == cb_size * 2);
    cb_free (&b[1], dfw);
    assert (get_heap_small (dfw) == cb_size * 2);

#ifdef WITH_ACCOUNTS
    assert (get_cb_blocks (dfw) == 0);
    assert (get_cb_bytes (dfw) == 0);
#endif

    leaks = dfwmalloc_destroy (dfw);
    assert (leaks == 0);






    dfw = dfwmalloc_new ();

    head_index = __dfwmalloc_cb_head_index (n);
    info = __dfwmalloc_cb_page_info (head_index);

    for (i = 0; i < N_CB; i++)
        b[i].is_malloced = 0;

    cb_size = __dfwmalloc_cb_heap_index_to_chunk_size (info.heap_index);

    assert (get_heap_small (dfw) == 0);
    cb_fill (&b[0], dfw, n, info.n_blocks);
    assert (get_heap_small (dfw) == cb_size);
    cb_fill (&b[1], dfw, n, info.n_blocks);
    assert (get_heap_small (dfw) == cb_size * 2);
    cb_free (&b[1], dfw);
    assert (get_heap_small (dfw) == cb_size * 2);
    cb_free (&b[0], dfw);
    assert (get_heap_small (dfw) == cb_size * 2);


#ifdef WITH_ACCOUNTS
    assert (get_cb_blocks (dfw) == 0);
    assert (get_cb_bytes (dfw) == 0);
#endif

    leaks = dfwmalloc_destroy (dfw);
    assert (leaks == 0);





/* test to reuse an block at the end of the heap: */
    dfw = dfwmalloc_new ();

    head_index = __dfwmalloc_cb_head_index (n);
    info = __dfwmalloc_cb_page_info (head_index);

    for (i = 0; i < N_CB; i++)
        b[i].is_malloced = 0;

    cb_size = __dfwmalloc_cb_heap_index_to_chunk_size (info.heap_index);

    assert (get_heap_small (dfw) == 0);
    cb_fill (&b[0], dfw, n, info.n_blocks);
    qc = b->p[0];
    assert (get_heap_small (dfw) == cb_size);
    cb_fill (&b[1], dfw, n, info.n_blocks);
    assert (get_heap_small (dfw) == cb_size * 2);

    usleep (40000);     /* ensure timeout at (*6*) */

    cb_free (&b[1], dfw);

    assert (get_heap_small (dfw) == cb_size);
    cb_free (&b[0], dfw);
    assert (get_heap_small (dfw) == 0);

    q = (unsigned char *) dfwmalloc_malloc(dfw, n2);
    head_index = __dfwmalloc_cb_head_index (n);
    head_index2 = __dfwmalloc_cb_head_index (n2);
    assert (__dfwmalloc_cb_page_info (head_index2).round_up != __dfwmalloc_cb_page_info (head_index).round_up);
    assert (__dfwmalloc_cb_page_info (head_index2).heap_index == __dfwmalloc_cb_page_info (head_index).heap_index);

#ifdef WITH_ACCOUNTS
    assert (get_cb_blocks (dfw) == 1);
    assert (get_cb_bytes (dfw) == n2);
#endif
    {
        int dd, dd_cmp;
        dd = (q - qc);
        if (dd < 0)
            dd = -dd;

        dd_cmp = __dfwmalloc_cb_page_info (head_index2).first_block - __dfwmalloc_cb_page_info (head_index).first_block;
        if (dd_cmp < 0)
            dd_cmp = -dd_cmp;

#ifndef NO_HEAP_TRUNCATION
        assert (dd == dd_cmp);
#endif
    }

    free (q);

    leaks = dfwmalloc_destroy (dfw);
    assert (leaks == 0);

















/* test to reuse an block closer to the start of the heap: */
    dfw = dfwmalloc_new ();

    head_index = __dfwmalloc_cb_head_index (n);
    info = __dfwmalloc_cb_page_info (head_index);

    for (i = 0; i < N_CB; i++)
        b[i].is_malloced = 0;

    cb_size = __dfwmalloc_cb_heap_index_to_chunk_size (info.heap_index);

    assert (get_heap_small (dfw) == 0);
    cb_fill (&b[0], dfw, n, info.n_blocks);
    qc = b->p[0];
    assert (get_heap_small (dfw) == cb_size);
    cb_fill (&b[1], dfw, n, info.n_blocks);
    assert (get_heap_small (dfw) == cb_size * 2);

    usleep (40000);     /* ensure timeout at (*6*) */
    cb_free (&b[0], dfw);
    assert (get_heap_small (dfw) == cb_size * 2);

    q = (unsigned char *) dfwmalloc_malloc(dfw, n2);

    head_index = __dfwmalloc_cb_head_index (n);
    head_index2 = __dfwmalloc_cb_head_index (n2);
    assert (__dfwmalloc_cb_page_info (head_index2).round_up != __dfwmalloc_cb_page_info (head_index).round_up);
    assert (__dfwmalloc_cb_page_info (head_index2).heap_index == __dfwmalloc_cb_page_info (head_index).heap_index);

    {
        int dd, dd_cmp;
        dd = (q - qc);
        if (dd < 0)
            dd = -dd;

        dd_cmp = __dfwmalloc_cb_page_info (head_index2).first_block - __dfwmalloc_cb_page_info (head_index).first_block;
        if (dd_cmp < 0)
            dd_cmp = -dd_cmp;

#if !defined(NO_HEAP_TRUNCATION) && !defined(NO_RANKHEAP)
        assert (dd == dd_cmp);
#endif
    }

    usleep (40000);     /* ensure timeout at (*6*) */

    free (q);
    

    cb_free (&b[1], dfw);
#ifndef NO_HEAP_TRUNCATION
    assert (get_heap_small (dfw) == 0);
#endif

    leaks = dfwmalloc_destroy (dfw);
    assert (leaks == 0);











    dfw = dfwmalloc_new ();

    head_index = __dfwmalloc_cb_head_index (n);
    info = __dfwmalloc_cb_page_info (head_index);

    for (i = 0; i < N_CB; i++)
        b[i].is_malloced = 0;

    cb_size = __dfwmalloc_cb_heap_index_to_chunk_size (info.heap_index);

    for (i = 0; i < 100000; i++) {
        struct one_cb *a;
        a = &b[random() % N_CB];
        if (a->is_malloced) {
            cb_free (a, dfw);
        } else {
            cb_fill (a, dfw, n, info.n_blocks);
        }
    }

    for (i = 0; i < N_CB; i++) {
        struct one_cb *a;
        a = &b[i];
        if (a->is_malloced)
            cb_free (a, dfw);
    }

#ifdef WITH_ACCOUNTS
    assert (get_cb_blocks (dfw) == 0);
    assert (get_cb_bytes (dfw) == 0);
#endif

    leaks = dfwmalloc_destroy (dfw);
    assert (leaks == 0);
    printf ("cb heaps 4\n");

}

static void check_cb_heaps3 (void)
{
    struct dfwmalloc *dfw;
    int i, n;
    long num_bytes;

    for (num_bytes = 2; num_bytes < get_max_cb_size (); num_bytes++) {
        char *p[MAX_BLOCKS];
        int leaks;

        assert (num_bytes >= 2);

        dfw = dfwmalloc_new ();

{
struct cb_page_info info;
int head_index;
head_index = __dfwmalloc_cb_head_index (num_bytes);
info = __dfwmalloc_cb_page_info (head_index);
n = info.n_blocks;

        for (i = 0; i < n; i++) {
            p[i] = (char *) dfwmalloc_malloc (dfw, num_bytes);
            p[i][0] = i;
            p[i][num_bytes - 1] = i;
        }

{
    char *cb;
    char *u;
    ULONG64 m;
    ULONG64 b;
    u = p[0];
    b = (ULONG64) __dfwmalloc_cb_heap_index_to_chunk_size (__dfwmalloc_cb_heap_index_from_pointer (u));
    m = b - 1;
    cb = (char *) (void *) ((ULONG64) u & ~m);

        assert ((ULONG64) p[0] > (ULONG64) cb);
        assert ((ULONG64) p[n - 1] + num_bytes <= (ULONG64) cb + b - sizeof(cb_int_t));

        p[i] = (char *) dfwmalloc_malloc (dfw, num_bytes);
        p[i][0] = i;
        p[i][num_bytes - 1] = i;

        assert ((ULONG64) p[i] > (ULONG64) cb + b);

        for (i = 0; i < n + 1; i++) {
            assert ((unsigned char) p[i][0] == (unsigned char) i);
            assert ((unsigned char) p[i][num_bytes - 1] == (unsigned char) i);
            memset (p[i], 0x55, num_bytes);
            assert (dfwmalloc_dfwcheck (dfw, p[i]) >= num_bytes);
            dfwmalloc_free (dfw, p[i]);
        }

        for (i = 0; i < n * 5 / 4; i++) {
            p[i] = (char *) dfwmalloc_malloc (dfw, num_bytes);
            p[i][0] = (i ^ 0x4a);
            assert ((unsigned char) p[i][0] == (unsigned char) (i ^ 0x4a));
            p[i][num_bytes - 1] = (i ^ 0x4a);
            assert ((unsigned char) p[i][num_bytes - 1] == (unsigned char) (i ^ 0x4a));
        }

        for (i = 0; i < n * 5 / 4; i++) {
            assert ((unsigned char) p[i][0] == (unsigned char) (i ^ 0x4a));
            assert ((unsigned char) p[i][num_bytes - 1] == (unsigned char) (i ^ 0x4a));
            memset (p[i], 0x55, num_bytes);
            assert (dfwmalloc_dfwcheck (dfw, p[i]) >= num_bytes);
            dfwmalloc_free (dfw, p[i]);
        }

#ifdef WITH_ACCOUNTS
        assert (get_cb_blocks (dfw) == 0);
        assert (get_cb_bytes (dfw) == 0);
#endif

        leaks = dfwmalloc_destroy (dfw);
        assert (leaks == 0);
}
}

    }
    printf ("cb heaps 3 success\n");
}

static void check_cb_heaps2 (void)
{
    struct dfwmalloc *dfw;
    char **p;
    int i, j;
    int n, m;
    struct cb_page_info info;
    int the_head = 20;

    if (the_head >= __dfwmalloc_cb_max_head ())
        the_head = __dfwmalloc_cb_max_head () / 2;
    info = __dfwmalloc_cb_page_info (the_head);
    n = info.round_up;
    m = info.n_blocks;

    dfw = dfwmalloc_new ();
    for (i = 450; i < 475; i++) {
        char *q;
        q = (char *) dfwmalloc_malloc (dfw, i);
        assert (!((ULONG64) q % (1 << MIN_ALIGN)));
    }
    dfwmalloc_destroy (dfw);



    dfw = dfwmalloc_new ();

    p = (char **) malloc (50 * m * sizeof (char *));

    for (j = 1; j < 50; j++) {
        for (i = 0; i < m; i++) {
            char *q;
            q = (char *) dfwmalloc_malloc (dfw, n);
            assert (q != NULL);
            p[j * m + i] = q;
            assert (__dfwmalloc_cb_refs (dfw) == j);
        }
    }

    for (j = 49; j >= 1; j--) {
        for (i = 0; i < m; i++) {
            char *q;
            assert (__dfwmalloc_cb_refs (dfw) == j);
            q = p[j * m + i];
            assert (q != NULL);
            dfwmalloc_free (dfw, q);
        }
    }

    {
        int leaks;
        leaks = dfwmalloc_destroy (dfw);
        assert (leaks == 0);
    }




    dfw = dfwmalloc_new ();

    p = (char **) malloc (50 * m * sizeof (char *));

    for (j = 1; j < 50; j++) {
        for (i = 0; i < m; i++) {
            char *q;
            q = (char *) dfwmalloc_malloc (dfw, n);
            assert (q != NULL);
            p[j * m + i] = q;
            assert (__dfwmalloc_cb_refs (dfw) == j);
        }
    }

    for (j = 1; j < 50; j++) {
        for (i = 0; i < m; i++) {
            char *q;
            assert (__dfwmalloc_cb_refs (dfw) == 50 - j);
            q = p[j * m + i];
            assert (q != NULL);
            dfwmalloc_free (dfw, q);
        }
    }

    {
        int leaks;
        leaks = dfwmalloc_destroy (dfw);
        assert (leaks == 0);
    }
}




void entry (void)
{
    char *q;

    q = (char *) malloc (1);    /* ensure setup */
    (void) q;

    check_cb_heaps (128 * 1024 * 1024 /* force condition at (*7*) */);
    check_cb_heaps (0);
    check_cb_heaps5 ();
    check_cb_heaps4 ();
    check_cb_heaps3 ();
    check_cb_heaps2 ();
}


