#define check_heap_limits       entry
#include <my_assert.h>


static long long get_max_heap (void)
{
    struct dfwmalloc_stats s;
    dfwmalloc_stats (&s);
    return s.max_heap;
}

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


static int approx_equal (long a, long b, int percent)
{
    if (b < a * (100 + percent) / 100)
        if (a < b * (100 + percent) / 100)
            return 1;
    return 0;
}


struct xl2;

struct xl2 {
    struct xl2 *next;
    unsigned char s[1];
};

void check_heap_limits (void)
{
#ifndef NO_HEAP_LIMIT_TEST
#ifndef FOR_VALGRIND
    struct dfwmalloc *dfw;
    int i, n, leaks;
    struct xl2 *first = NULL;
    struct xl2 *p, *q;
    long long max_heap;
    long max_cb_size;

    free(malloc(1));

#define N (sizeof (struct xl2) + max_cb_size)

    max_heap = get_max_heap ();
    max_cb_size = get_max_cb_size ();

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

    dfw = dfwmalloc_new ();
    assert (dfw);
    printf ("allocated %ld bytes (%ld MBytes) to test heap limits\n", (long) max_heap, (long) max_heap >> 20);
    n = max_heap / N;

    for (i = 0;; i++) {
        p = (struct xl2 *) dfwmalloc_malloc (dfw, N);
        if (!p) {
            assert (errno == ENOMEM);
            assert (approx_equal (i, n, 10));
            break;
        }
        memset (p->s, (i & 0xFF), max_cb_size);
        p->next = first;
        first = p;
    }

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

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

/* now malloc return to non-error return */
    q = (struct xl2 *) dfwmalloc_malloc (dfw, N);
    assert (q);

/* now check error once more */
    p = (struct xl2 *) dfwmalloc_malloc (dfw, N);
    assert (!p);
    assert (errno == ENOMEM);

/* cleanup */

    while (first) {
        int j;
        p = first->next;
        i--;
        for (j = 0; j < (int) max_cb_size; j++) {
            unsigned char ch;
            ch = (i & 0xFF);
            assert (first->s[j] == ch);
        }
        dfwmalloc_free (dfw, first);
        first = p;
    }

    dfwmalloc_free (dfw, q);

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

