/**
 * \file
 * A pointer queue that can be sorted.
 *
 * Copyright (C) 2014 Xamarin Inc
 *
 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
 */

#ifdef HAVE_SGEN_GC

#include <string.h>

#include "mono/sgen/sgen-gc.h"
#include "mono/sgen/sgen-pointer-queue.h"

void
sgen_pointer_queue_clear (SgenPointerQueue *queue)
{
	queue->next_slot = 0;
}

void
sgen_pointer_queue_init (SgenPointerQueue *queue, int mem_type)
{
	queue->next_slot = 0;
	queue->size = 0;
	queue->data = NULL;
	queue->mem_type = mem_type;
}

static void
realloc_queue (SgenPointerQueue *queue)
{
	size_t new_size = queue->size ? queue->size + queue->size/2 : 1024;
	void **new_data = (void **)sgen_alloc_internal_dynamic (sizeof (void*) * new_size, queue->mem_type, TRUE);

	memcpy (new_data, queue->data, sizeof (void*) * queue->next_slot);
	sgen_free_internal_dynamic (queue->data, sizeof (void*) * queue->size, queue->mem_type);
	queue->data = new_data;
	queue->size = new_size;
	SGEN_LOG (4, "Reallocated pointer queue to size: %lu", (unsigned long)new_size);
}

gboolean
sgen_pointer_queue_will_grow (SgenPointerQueue *queue)
{
	return queue->next_slot >= queue->size;
}

void
sgen_pointer_queue_add (SgenPointerQueue *queue, void *ptr)
{
	if (sgen_pointer_queue_will_grow (queue))
		realloc_queue (queue);

	queue->data [queue->next_slot++] = ptr;
}

void*
sgen_pointer_queue_pop (SgenPointerQueue *queue)
{
	g_assert (queue->next_slot);

	return queue->data [--queue->next_slot];
}

size_t
sgen_pointer_queue_search (SgenPointerQueue *queue, void *addr)
{
	size_t first = 0, last = queue->next_slot;
	while (first < last) {
		size_t middle = first + ((last - first) >> 1);
		if (addr <= queue->data [middle])
			last = middle;
		else
			first = middle + 1;
	}
	g_assert (first == last);
	return first;
}

/*
 * Removes all NULL pointers from the queue.
 */
void
sgen_pointer_queue_remove_nulls (SgenPointerQueue *queue)
{
	void **start, **cur, **end;
	start = cur = queue->data;
	end = queue->data + queue->next_slot;
	while (cur < end) {
		if (*cur)
			*start++ = *cur++;
		else
			++cur;
	}
	queue->next_slot = start - queue->data;
}

/*
 * Sorts the pointers in the queue, then removes duplicates.
 */
void
sgen_pointer_queue_sort_uniq (SgenPointerQueue *queue)
{
	void **start, **cur, **end;
	/* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
	/* it may be better to keep ranges of pinned memory instead of individually pinning objects */
	SGEN_LOG (5, "Sorting pointer queue, size: %lu", (unsigned long)queue->next_slot);
	if (queue->next_slot > 1)
		sgen_sort_addresses (queue->data, queue->next_slot);
	start = cur = queue->data;
	end = queue->data + queue->next_slot;
	while (cur < end) {
		*start = *cur++;
		while (cur < end && *start == *cur)
			cur++;
		start++;
	};
	queue->next_slot = start - queue->data;
	SGEN_LOG (5, "Pointer queue reduced to size: %lu", (unsigned long)queue->next_slot);
}

/*
 * Does a linear search through the pointer queue to find `ptr`.  Returns the index if
 * found, otherwise (size_t)-1.
 */
size_t
sgen_pointer_queue_find (SgenPointerQueue *queue, void *ptr)
{
	size_t i;
	for (i = 0; i < queue->next_slot; ++i)
		if (queue->data [i] == ptr)
			return i;
	return (size_t)-1;
}

gboolean
sgen_pointer_queue_is_empty (SgenPointerQueue *queue)
{
	return !queue->next_slot;
}

void
sgen_pointer_queue_free (SgenPointerQueue *queue)
{
	sgen_free_internal_dynamic (queue->data, sizeof (void*) * queue->size, queue->mem_type);
}

#endif
