static size_t
sec_allocated (Block *block, void *memory)
{
	Cell *cell;
	word_t *word;

	ASSERT (block);
	ASSERT (memory);

	word = memory;
	--word;

#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t));
#endif

	/* Lookup the meta for this memory block (using guard pointer) */
	ASSERT (sec_is_valid_word (block, word));
	ASSERT (pool_valid (*word));
	cell = *word;

	sec_check_guards (cell);
	ASSERT (cell->next == NULL);
	ASSERT (cell->prev == NULL);
	ASSERT (cell->allocated > 0);

#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_NOACCESS (word, sizeof (word_t));
#endif

	return cell->allocated;
}
static Cell*
sec_neighbor_after (Block *block, Cell *cell)
{
	word_t *word;

	ASSERT (cell);
	ASSERT (block);

	word = cell->words + cell->n_words;
	if (!sec_is_valid_word (block, word))
		return NULL;

#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t));
#endif

	cell = *word;
	sec_check_guards (cell);

#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_NOACCESS (word, sizeof (word_t));
#endif

	return cell;
}
static void
sec_validate (Block *block)
{
	Cell *cell;
	word_t *word, *last;

#ifdef WITH_VALGRIND
	if (RUNNING_ON_VALGRIND)
		return;
#endif

	word = block->words;
	last = word + block->n_words;

	for (;;) {
		ASSERT (word < last);

		ASSERT (sec_is_valid_word (block, word));
		ASSERT (pool_valid (*word));
		cell = *word;
	
		/* Validate that it's actually for real */
		sec_check_guards (cell);
	
		/* Is it an allocated block? */
		if (cell->requested > 0) {
			ASSERT (cell->tag != NULL);
			ASSERT (cell->next != NULL);
			ASSERT (cell->prev != NULL);
			ASSERT (cell->next->prev == cell);
			ASSERT (cell->prev->next == cell);
			ASSERT (cell->requested <= (cell->n_words - 2) * sizeof (word_t));
		
		/* An unused block */
		} else {
			ASSERT (cell->tag == NULL);
			ASSERT (cell->next != NULL);
			ASSERT (cell->prev != NULL);
			ASSERT (cell->next->prev == cell);
			ASSERT (cell->prev->next == cell);
		}
		
		word += cell->n_words;
		if (word == last)
			break;
	}
}
int
egg_secure_check (const void *memory)
{
	Block *block = NULL;

	DO_LOCK ();

		/* Find out where it belongs to */
		for (block = all_blocks; block; block = block->next) {
			if (sec_is_valid_word (block, (word_t*)memory))
				break;
		}

	DO_UNLOCK ();

	return block == NULL ? 0 : 1;
}
void
egg_secure_free_full (void *memory, int flags)
{
	Block *block = NULL;

	if (memory == NULL)
		return;

	DO_LOCK ();

		/* Find out where it belongs to */
		for (block = all_blocks; block; block = block->next) {
			if (sec_is_valid_word (block, memory))
				break;
		}

#ifdef WITH_VALGRIND
		/* We like valgrind's warnings, so give it a first whack at checking for errors */
		if (block != NULL || !(flags & GKR_SECURE_USE_FALLBACK))
			VALGRIND_FREELIKE_BLOCK (memory, sizeof (word_t));
#endif

		if (block != NULL) {
			sec_free (block, memory);
			if (block->used == 0)
				sec_block_destroy (block);
		}

	DO_UNLOCK ();

	if (!block) {
		if ((flags & GKR_SECURE_USE_FALLBACK)) {
			egg_memory_fallback (memory, 0);
		} else {
			if (egg_secure_warnings)
				fprintf (stderr, "memory does not belong to mate-keyring: 0x%08lx\n",
				         (unsigned long)memory);
			ASSERT (0 && "memory does does not belong to mate-keyring");
		}
	}
}
static void
sec_validate (Block *block)
{
	Cell *cell;
	word_t *word, *last;

	word = block->words;
	last = word + block->n_words;

	for (;;) {
		ASSERT (word < last);

		ASSERT (sec_is_valid_word (block, word));
		ASSERT (pool_valid (*word));
		cell = *word;

		/* Validate that it's actually for real */
		sec_check_guards (cell);

		/* Is it an allocated block? */
		if (cell->allocated > 0) {
			ASSERT (cell->next == NULL);
			ASSERT (cell->prev == NULL);
			ASSERT (cell->allocated <= (cell->n_words - 2) * sizeof (word_t));

			/* An unused block */
		} else {
			ASSERT (cell->next);
			ASSERT (cell->prev);
			ASSERT (cell->next->prev == cell);
			ASSERT (cell->prev->next == cell);
		}

		word += cell->n_words;
		if (word == last)
			break;
	}
}
static void*
sec_realloc (Block *block, void *memory, size_t length)
{
	Cell *cell, *other;
	word_t *word;
	size_t n_words;
	size_t valid;
	void *alloc;

	/* Standard realloc behavior, should have been handled elsewhere */
	ASSERT (memory != NULL);
	ASSERT (length > 0);

	/* Dig out where the meta should be */
	word = memory;
	--word;

#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t));
#endif

	ASSERT (sec_is_valid_word (block, word));
	ASSERT (pool_valid (*word));
	cell = *word;

	/* Validate that it's actually for real */
	sec_check_guards (cell);
	ASSERT (cell->allocated > 0);
	ASSERT (cell->next == NULL);
	ASSERT (cell->prev == NULL);

	/* The amount of valid data */
	valid = cell->allocated;

	/* How many words we actually want */
	n_words = sec_size_to_words (length) + 2;

	/* Less memory is required than is in the cell */
	if (n_words <= cell->n_words) {

		/* TODO: No shrinking behavior yet */
		cell->allocated = length;
		alloc = sec_cell_to_memory (cell);

#ifdef WITH_VALGRIND
		VALGRIND_MAKE_MEM_DEFINED (alloc, length);
#endif

		/*
		 * Even though we may be reusing the same cell, that doesn't
		 * mean that the allocation is shrinking. It could have shrunk
		 * and is now expanding back some.
		 */
		if (length < valid)
			return sec_clear_memory (alloc, length, valid);
		else
			return alloc;
	}

	/* Need braaaaaiiiiiinsss... */
	while (cell->n_words < n_words) {

		/* See if we have a neighbor who can give us some memory */
		other = sec_neighbor_after (block, cell);
		if (!other || other->allocated != 0)
			break;

		/* Eat the whole neighbor if not too big */
		if (n_words - cell->n_words + WASTE >= other->n_words) {
			cell->n_words += other->n_words;
			sec_write_guards (cell);
			sec_remove_cell_ring (&block->unused, other);
			pool_free (other);

		/* Steal from the neighbor */
		} else {
			other->words += n_words - cell->n_words;
			other->n_words -= n_words - cell->n_words;
			sec_write_guards (other);
			cell->n_words = n_words;
			sec_write_guards (cell);
		}
	}

	if (cell->n_words >= n_words) {
		cell->allocated = length;
		alloc = sec_cell_to_memory (cell);

#ifdef WITH_VALGRIND
		VALGRIND_MAKE_MEM_DEFINED (alloc, length);
#endif

		return sec_clear_memory (alloc, valid, length);
	}

	/* That didn't work, try alloc/free */
	alloc = sec_alloc (block, length);
	if (alloc) {
		memcpy (alloc, memory, valid);
		sec_free (block, memory);
	}

	return alloc;
}
static void*
sec_free (Block *block, void *memory)
{
	Cell *cell, *other;
	word_t *word;

	ASSERT (block);
	ASSERT (memory);

	word = memory;
	--word;

#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t));
#endif

	/* Lookup the meta for this memory block (using guard pointer) */
	ASSERT (sec_is_valid_word (block, word));
	ASSERT (pool_valid (*word));
	cell = *word;

#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_DEFINED (cell->words, cell->n_words * sizeof (word_t));
#endif

	sec_check_guards (cell);
	sec_clear_memory (memory, 0, cell->allocated);

	sec_check_guards (cell);
	ASSERT (cell->next == NULL);
	ASSERT (cell->prev == NULL);
	ASSERT (cell->allocated > 0);

        /* Find previous unallocated neighbor, and merge if possible */
        other = sec_neighbor_before (block, cell);
        if (other && other->allocated == 0) {
        	ASSERT (other->next && other->prev);
        	other->n_words += cell->n_words;
        	sec_write_guards (other);
        	pool_free (cell);
        	cell = other;
        }

        /* Find next unallocated neighbor, and merge if possible */
        other = sec_neighbor_after (block, cell);
        if (other && other->allocated == 0) {
        	ASSERT (other->next && other->prev);
        	other->n_words += cell->n_words;
        	other->words = cell->words;
        	if (cell->next)
        		sec_remove_cell_ring (&block->unused, cell);
        	sec_write_guards (other);
        	pool_free (cell);
        	cell = other;
        }

        /* Add to the unused list if not already there */
        if (!cell->next)
        	sec_insert_cell_ring (&block->unused, cell);

        cell->allocated = 0;
        --block->used;
        return NULL;
}
void*
egg_secure_realloc_full (void *memory, size_t length, int flags)
{
	Block *block = NULL;
	size_t previous = 0;
	int donew = 0;
	void *alloc = NULL;

	if (length > 0xFFFFFFFF / 2) {
		if (egg_secure_warnings)
			fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n",
			         (unsigned long)length);
		return NULL;
	}

	if (memory == NULL)
		return egg_secure_alloc_full (length, flags);
	if (!length) {
		egg_secure_free_full (memory, flags);
		return NULL;
	}

	DO_LOCK ();

		/* Find out where it belongs to */
		for (block = all_blocks; block; block = block->next) {
			if (sec_is_valid_word (block, memory)) {
				previous = sec_allocated (block, memory);

#ifdef WITH_VALGRIND
				/* Let valgrind think we are unallocating so that it'll validate */
				VALGRIND_FREELIKE_BLOCK (memory, sizeof (word_t));
#endif

				alloc = sec_realloc (block, memory, length);

#ifdef WITH_VALGRIND
				/* Now tell valgrind about either the new block or old one */
				VALGRIND_MALLOCLIKE_BLOCK (alloc ? alloc : memory,
				                           alloc ? length : previous,
				                           sizeof (word_t), 1);
#endif
				break;
			}
		}

		/* If it didn't work we may need to allocate a new block */
		if (block && !alloc)
			donew = 1;

		if (block && block->used == 0)
			sec_block_destroy (block);

	DO_UNLOCK ();

	if (!block) {
		if ((flags & GKR_SECURE_USE_FALLBACK)) {
			/*
			 * In this case we can't zero the returned memory,
			 * because we don't know what the block size was.
			 */
			return egg_memory_fallback (memory, length);
		} else {
			if (egg_secure_warnings)
				fprintf (stderr, "memory does not belong to mate-keyring: 0x%08lx\n",
				         (unsigned long)memory);
			ASSERT (0 && "memory does does not belong to mate-keyring");
			return NULL;
		}
	}

	if (donew) {
		alloc = egg_secure_alloc_full (length, flags);
		if (alloc) {
			memcpy (alloc, memory, previous);
			egg_secure_free_full (memory, flags);
		}
	}

	if (!alloc)
		errno = ENOMEM;

	return alloc;
}