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

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


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_max_cb_size (void)
{
    struct dfwmalloc_stats s;
    dfwmalloc_stats (&s);
    return s.max_cb_size;
}


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;
}



/* this must happen before backtrace is enabled */
void check_truncate_heap (void)
{
    int cb_n_sizes;
    int j, leaks;
    struct dfwmalloc *dfw;

    cb_n_sizes = get_max_cb_size ();

    dfwmalloc_truncate_heap ();

    dfw = dfwmalloc_new ();

    for (j = 0; j < 2; j++) {
#define N_UNLINKS       (10000000 / (cb_n_sizes + 1))
        char *r[N_UNLINKS], *p, *q = NULL, *l;
        int i, n;
        long total = 0;
        long rss1 = -1, rss2 = -1, rss3 = -1;

#if defined(NO_CB_HEAPS) || defined(NULL_MODE)
        n = 99;
#else
        n = cb_n_sizes + 1;
#endif

        l = (char *) dfwmalloc_malloc (dfw, n);
        memset (l, 0x55, n);

        usleep (100000);

        rss1 = get_os_heap_size ();

        printf ("check truncate heap %d\n", j);

        for (i = 0; i < N_UNLINKS; i++) {
            r[i] = (char *) dfwmalloc_malloc (dfw, n);
            memset (r[i], 0x55, n);     /* <== ensure block get put into RSS */
            total += n;
            if (i == 0)
                q = r[i];
        }

        usleep (100000);

        rss2 = get_os_heap_size ();

        printf ("%d %ld check truncate heap rss: %ld, %ld, %ld\n", n, total, rss1, rss2, rss3);

#ifndef NO_HEAP_TRUNCATION
        assert (approx_equal (rss1 * 1024 + 10000000, rss2 * 1024, 20));
#endif

        for (i = 0; i < N_UNLINKS; i++) {
            dfwmalloc_free (dfw, r[i]);
        }

#if !defined(FEATHERWEIGHT) && !defined(FOR_VALGRIND)
        p = (char *) dfwmalloc_malloc (dfw, n * 2);
#else
        p = (char *) dfwmalloc_malloc (dfw, 250);
#endif
#ifndef NO_HEAP_TRUNCATION
        assert (p == q);
#else
        (void) q;
        (void) p;
#endif
        dfwmalloc_free (dfw, p);

        usleep (100000);

        dfwmalloc_dfwtruncate_heap (dfw);

        rss3 = get_os_heap_size ();

        printf ("check truncate heap rss: %ld, %ld, %ld\n", rss1, rss2, rss3);

#ifndef NO_HEAP_TRUNCATION
        assert (approx_equal (rss3 * 1024 + 10000000, rss2 * 1024, 20));
#endif

        dfwmalloc_free (dfw, l);
    }

    leaks = dfwmalloc_destroy (dfw);
    assert (!leaks);
    printf ("truncate successful\n");
}

void entry (void)
{
    check_truncate_heap ();
}

