#include "private.h"

/* flag to indicate whether to check memory or not */
static bool_t checking = BOOL_FALSE;

typedef struct {
	bool_t result;
	unsigned free_blocks;
	lub_heap_t *heap;
} check_arg_t;
/*--------------------------------------------------------- */
static void process_segment(void *s, unsigned index, size_t size, void *a)
{
	lub_heap_segment_t *segment = s;
	lub_heap_block_t *block = lub_heap_block_getfirst(segment);
	check_arg_t *arg = a;
	/*
	 * don't bother checking if this segment has zero blocks
	 */
	if (arg->heap->stats.free_blocks || arg->heap->stats.alloc_blocks) {
		arg->result = lub_heap_block_check(block);
		if (BOOL_TRUE == arg->result) {
			/* check that the first block has the segment set */
			arg->result =
			    (block->free.tag.segment ==
			     1) ? BOOL_TRUE : BOOL_FALSE;
			if (block->free.tag.free) {
				++arg->free_blocks;
			}
		}

		/* now iterate along the block chain checking each one */
		while ((BOOL_TRUE == arg->result)
		       && (block = lub_heap_block_getnext(block))) {
			/* check this block */
			arg->result = lub_heap_block_check(block);
			if (BOOL_TRUE == arg->result) {
				/* ensure that all non-first blocks have the segment clear */
				arg->result =
				    (block->free.tag.segment ==
				     0) ? BOOL_TRUE : BOOL_FALSE;
				if (block->free.tag.free) {
					++arg->free_blocks;
				}
			}
		}
	}
}

/*--------------------------------------------------------- */
bool_t lub_heap_check(bool_t enable)
{
	bool_t result = checking;

	checking = enable;

	return result;
}

/*--------------------------------------------------------- */
bool_t lub_heap_is_checking(void)
{
	return checking;
}

/*--------------------------------------------------------- */
bool_t lub_heap_check_memory(lub_heap_t * this)
{
	check_arg_t arg;

	arg.result = BOOL_TRUE;

	if (BOOL_TRUE == checking) {
		arg.free_blocks = 0;
		arg.heap = this;
		/* iterate all the segments in the system */
		lub_heap_foreach_segment(this, process_segment, &arg);

		/* now check that the stats map to the available free blocks 
		 * checking the alloc blocks is more difficult because of the hidden
		 * overheads when performing leak detection.
		 */
		if ((this->stats.free_blocks != arg.free_blocks)) {
			arg.result = BOOL_FALSE;
		}
	}
	return arg.result;
}

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