/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"


void *
array_idx_modifiable_i(const struct array *array, unsigned int idx)
{
	i_assert(idx < array->buffer->used / array->element_size);
	return PTR_OFFSET(array->buffer->data,
			  MALLOC_MULTIPLY(idx, array->element_size));
}

void *array_idx_get_space_i(struct array *array, unsigned int idx)
{
	return buffer_get_space_unsafe(array->buffer,
				       MALLOC_MULTIPLY(idx, array->element_size),
				       array->element_size);
}

void array_idx_set_i(struct array *array, unsigned int idx, const void *data)
{
	buffer_write(array->buffer, MALLOC_MULTIPLY(idx, array->element_size),
		     data, array->element_size);
}

void array_idx_clear_i(struct array *array, unsigned int idx)
{
	buffer_write_zero(array->buffer,
			  MALLOC_MULTIPLY(idx, array->element_size),
			  array->element_size);
}

void *array_insert_space_i(struct array *array, unsigned int idx)
{
	void *data;
	size_t pos;

	pos = MALLOC_MULTIPLY(idx, array->element_size);
	buffer_copy(array->buffer, MALLOC_ADD(pos, array->element_size),
		    array->buffer, pos, SIZE_MAX);

	data = buffer_get_space_unsafe(array->buffer, pos, array->element_size);
	memset(data, 0, array->element_size);
	return data;
}

bool array_cmp_i(const struct array *array1, const struct array *array2)
{
	if (!array_is_created_i(array1) || array1->buffer->used == 0)
		return !array_is_created_i(array2) || array2->buffer->used == 0;

	if (!array_is_created_i(array2))
		return FALSE;

	return buffer_cmp(array1->buffer, array2->buffer);
}

bool array_equal_fn_i(const struct array *array1, const struct array *array2,
		      int (*cmp)(const void *, const void*))
{
	unsigned int count1, count2, i;
	size_t size;

	if (!array_is_created_i(array1) || array1->buffer->used == 0)
		return !array_is_created_i(array2) || array2->buffer->used == 0;

	if (!array_is_created_i(array2))
		return FALSE;

	count1 = array_count_i(array1); count2 = array_count_i(array2);
	if (count1 != count2)
		return FALSE;

	size = array1->element_size;
	i_assert(size == array2->element_size);

	for (i = 0; i < count1; i++) {
		if (cmp(CONST_PTR_OFFSET(array1->buffer->data, i * size),
			CONST_PTR_OFFSET(array2->buffer->data, i * size)) != 0)
			return FALSE;
	}
	return TRUE;
}

bool array_equal_fn_ctx_i(const struct array *array1, const struct array *array2,
			  int (*cmp)(const void *, const void *, const void *),
			  const void *context)
{
	unsigned int count1, count2, i;
	size_t size;

	if (!array_is_created_i(array1) || array1->buffer->used == 0)
		return !array_is_created_i(array2) || array2->buffer->used == 0;

	if (!array_is_created_i(array2))
		return FALSE;

	count1 = array_count_i(array1); count2 = array_count_i(array2);
	if (count1 != count2)
		return FALSE;

	size = array1->element_size;
	i_assert(size == array2->element_size);

	for (i = 0; i < count1; i++) {
		if (cmp(CONST_PTR_OFFSET(array1->buffer->data, i * size),
			CONST_PTR_OFFSET(array2->buffer->data, i * size), context) != 0)
			return FALSE;
	}
	return TRUE;
}

void array_reverse_i(struct array *array)
{
	const size_t element_size = array->element_size;
	unsigned int i, count = array_count_i(array);
	size_t size;
	void *data, *tmp;

	data = buffer_get_modifiable_data(array->buffer, &size);
	tmp = t_buffer_get(array->element_size);
	for (i = 0; i+1 < count; i++, count--) {
		memcpy(tmp, PTR_OFFSET(data, i * element_size), element_size);
		memcpy(PTR_OFFSET(data, i * element_size),
		       PTR_OFFSET(data, (count-1) * element_size),
		       element_size);
		memcpy(PTR_OFFSET(data, (count-1) * element_size), tmp,
		       element_size);
	}
}

void array_sort_i(struct array *array, int (*cmp)(const void *, const void *))
{
	unsigned int count;

	count = array_count_i(array);
	if (count == 0)
		return;
	qsort(buffer_get_modifiable_data(array->buffer, NULL),
	      count, array->element_size, cmp);
}

const void *array_bsearch_i(const struct array *array, const void *key,
		            int (*cmp)(const void *, const void *))
{
	unsigned int count;

	count = array_count_i(array);
	return bsearch(key, array->buffer->data,
		       count, array->element_size, cmp);
}

const void *array_lsearch_i(const struct array *array, const void *key,
			    int (*cmp)(const void *, const void *))
{
	const void * const data = array->buffer->data;
	const size_t s = array->element_size;
	unsigned int idx;

	for (idx = 0; idx < array_count_i(array); idx++) {
		if (cmp(key, CONST_PTR_OFFSET(data, idx * s)) == 0) {
			return PTR_OFFSET(data, idx * s);
		}
	}

	return NULL;
}

const void *array_lsearch_ptr_i(const struct array *array, const void *key)
{
	i_assert(array->element_size == sizeof(key));
	const void *const *data = array->buffer->data;
	unsigned int i, count = array_count_i(array);

	for (i = 0; i < count; i++) {
		if (data[i] == key)
			return data[i];
	}
	return NULL;
}

bool array_lsearch_ptr_idx_i(const struct array *array, const void *key,
			     unsigned int *idx_r)
{
	i_assert(array->element_size == sizeof(key));
	const void *const *data = array->buffer->data;
	unsigned int i, count = array_count_i(array);

	for (i = 0; i < count; i++) {
		if (data[i] == key) {
			*idx_r = i;
			return TRUE;
		}
	}
	return FALSE;
}
