/*
 * Soft:        Keepalived is a failover program for the LVS project
 *              <www.linuxvirtualserver.org>. It monitor & manipulate
 *              a loadbalanced server pool using multi-layer checks.
 *
 * Part:        Vector structure manipulation.
 *
 * Author:      Alexandre Cassen, <acassen@linux-vs.org>
 *
 *              This program is distributed in the hope that it will be useful,
 *              but WITHOUT ANY WARRANTY; without even the implied warranty of
 *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *              See the GNU General Public License for more details.
 *
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
 *              as published by the Free Software Foundation; either version
 *              2 of the License, or (at your option) any later version.
 *
 * Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
 */

#include "config.h"

#include <string.h>

#include "vector.h"
#include "memory.h"

/* Function to call if attempt to read beyond end of strvec */
static null_strvec_handler_t null_strvec_handler;

null_strvec_handler_t register_null_strvec_handler(null_strvec_handler_t null_strvec_func)
{
	null_strvec_handler_t old_handler = null_strvec_handler;

	null_strvec_handler = null_strvec_func;

	return old_handler;
}

null_strvec_handler_t unregister_null_strvec_handler(void)
{
	null_strvec_handler_t old_handler = null_strvec_handler;

	null_strvec_handler = NULL;

	return old_handler;
}

const char *
strvec_slot(const vector_t *strvec, size_t index)
{
	if (strvec &&
	    index < vector_size(strvec) &&
	    strvec->slot[index])
		return strvec->slot[index];

	if (null_strvec_handler)
		(*null_strvec_handler)(strvec, index);

	return "";
}

/*
 * Initialize vector struct.
 * allocalted 'size' slot elements then return vector.
 */
vector_t *
vector_alloc_r(void)
{
	vector_t *v;

	PMALLOC(v);

	return v;
}

#ifdef _INCLUDE_UNUSED_CODE_
vector_t *
vector_init(unsigned int size)
{
	vector_t *v = vector_alloc();

	/* allocate at least one slot */
	if (size == 0)
		size = 1;

	v->allocated = size;
	v->active = 0;
	v->slot = MALLOC(sizeof(void *) * size);
	return v;
}
#endif

/* allocated one slot */
void
vector_alloc_slot_r(vector_t *v)
{
	v->allocated += VECTOR_DEFAULT_SIZE;
	if (v->slot)
		v->slot = REALLOC(v->slot, sizeof(void *) * v->allocated);
	else
		v->slot = MALLOC(sizeof(void *) * v->allocated);
}

/* Copy / dup a vector */
vector_t *
vector_copy_r(const vector_t *v)
{
	unsigned int size;
	vector_t *new = vector_alloc();

	new->active = v->active;
	new->allocated = v->allocated;

	size = sizeof(void *) * (v->allocated);
	new->slot = MALLOC(size);
	memcpy(new->slot, v->slot, size);

	return new;
}

#ifdef _INCLUDE_UNUSED_CODE_
/* Insert a value into a specific slot */
static void
vector_insert_slot(vector_t *v, unsigned int index, void *value)
{
	unsigned int i;

	vector_alloc_slot(v);
	for (i = (v->allocated / VECTOR_DEFAULT_SIZE) - 2; i >= index; i--)
		v->slot[i + 1] = v->slot[i];
	v->slot[index] = value;
	if (v->active >= index + 1)
		v->active++;
	else
		v->active = index + 1;
}

/* Check assigned index, and if it runs short double index pointer */
static void
vector_ensure(const vector_t *v, unsigned int num)
{
	if (v->allocated > num)
		return;

	v->slot = REALLOC(v->slot, sizeof(void *) * (v->allocated * 2));
	memset(&v->slot[v->allocated], 0, sizeof(void *) * v->allocated);
	v->allocated *= 2;

	if (v->allocated <= num)
		vector_ensure(v, num);
}

/* This function only returns next empty slot index.  It dose not mean
 * the slot's index memory is assigned, please call vector_ensure()
 * after calling this function.
 */
static int
vector_empty_slot(const vector_t *v)
{
	unsigned int i;

	if (v->active == 0)
		return 0;

	for (i = 0; i < v->active; i++) {
		if (v->slot[i] == 0) {
			return i;
		}
	}

	return i;
}

/* Set value to the smallest empty slot. */
static int
vector_set(const vector_t *v, void *val)
{
	unsigned int i;

	i = vector_empty_slot(v);
	vector_ensure(v, i);

	v->slot[i] = val;

	if (v->active <= i)
		v->active = i + 1;

	return i;
}
#endif

/* Set a vector slot value */
void
vector_set_slot(vector_t *v, void *value)
{
	unsigned int i = v->allocated - 1;

	v->slot[i] = value;
	v->active = v->allocated;
}

#ifdef _INCLUDE_UNUSED_CODE_
/* Set value to specified index slot. */
static int
vector_set_index(vector_t *v, unsigned int i, void *val)
{
	vector_ensure(v, i);

	v->slot[i] = val;

	if (v->active <= i)
		v->active = i + 1;

	return i;
}

/* Look up vector.  */
static void *
vector_lookup(const vector_t *v, unsigned int i)
{
	if (i >= v->active)
		return NULL;
	return v->slot[i];
}

/* Lookup vector, ensure it. */
static void *
vector_lookup_ensure(const vector_t *v, unsigned int i)
{
	vector_ensure(v, i);
	return v->slot[i];
}
#endif

/* Unset value at specified index slot. */
void
vector_unset(vector_t *v, unsigned int i)
{
	if (i >= v->allocated || !v->slot[i])
		return;

	v->slot[i] = NULL;

	if (i + 1 == v->active) {
		v->active--;
		while (i && v->slot[--i] == NULL && v->active--)
			;	/* Is this ugly ? */
	}
}

/* Count the number of not empty slot. */
unsigned int __attribute__ ((pure))
vector_count(const vector_t *v)
{
	unsigned int i;
	unsigned count = 0;

	for (i = 0; i < v->active; i++) {
		if (v->slot[i] != NULL) {
			count++;
		}
	}

	return count;
}

#ifdef _INCLUDE_UNUSED_CODE_
/* Free memory vector allocation */
void
vector_only_wrapper_free(vector_t *v)
{
	FREE(v);
}

void
vector_only_slot_free(void *slot)
{
	FREE(slot);
}

void
vector_only_index_free(void *slot)
{
	vector_only_slot_free(slot);
}
#endif

void
vector_free_r(const vector_t *v)
{
	if (v->slot)
		FREE_CONST_ONLY(v->slot);
	FREE_CONST_ONLY(v);
}

vector_t *
vector_compact_r(const vector_t *v)
{
	unsigned size, i, j;
	vector_t *new;

	for (size = 0, i = 0; i < v->active; i++) {
		if (v->slot[i])
			size++;
	}

	if (size) {
		new = vector_alloc();
		new->active = size;
		new->allocated = size;

		new->slot = MALLOC(sizeof(void *) * size);

		for (i = 0, j = 0; i < size; i++, j++) {
			while (!v->slot[j])
				j++;
			new->slot[i] = v->slot[j];
		}
	} else
		new = NULL;

	vector_free(v);

	return new;
}

/* String vector related */
char *
make_strvec_str(const vector_t *v, unsigned start)
{
	size_t len;
	char *str;
	unsigned i;

	for (i = start, len = 0; i < v->allocated; i++) {
		if (v->slot[i])
			len += strlen(v->slot[i]) + 1;
	}

	str = MALLOC(len);

	for (i = start, len = 0; i < v->allocated; i++) {
		if (v->slot[i]) {
			if (i > start)
				str[len++] = ' ';
			strcpy(str + len, v->slot[i]);
			len += strlen(v->slot[i]);
		}
	}

	return str;
}

void
free_strvec(const vector_t *strvec)
{
	unsigned int i;
	char *str;

	if (!strvec)
		return;

	for (i = 0; i < vector_size(strvec); i++) {
		if ((str = vector_slot(strvec, i)) != NULL) {
			FREE(str);
		}
	}

	vector_free(strvec);
}

vector_t *
strvec_remove_slot(vector_t *strvec, unsigned slot)
{
	if (slot > strvec->allocated || !strvec->slot[slot])
		return strvec;

	FREE(strvec->slot[slot]);
	vector_unset(strvec, slot);

	return vector_compact_r(strvec);
}

#ifdef _INCLUDE_UNUSED_CODE_
/* dump vector slots */
void
vector_dump(FILE *fp, const vector_t *v)
{
	unsigned int i;

	fprintf(fp, "Vector Size : %d, active %d\n", v->allocated, v->active);

	for (i = 0; i < v->allocated; i++) {
		if (v->slot[i] != NULL) {
			fprintf(fp, "  Slot [%d]: %p\n", i, vector_slot(v, i));
		}
	}
}

void
dump_strvec(const vector_t *strvec)
{
	unsigned int i;
	char *str;

	if (!strvec)
		return;

	printf("String Vector : ");

	for (i = 0; i < vector_size(strvec); i++) {
		str = vector_slot(strvec, i);
		printf("[%i]=%s ", i, str);
	}
	printf("\n");
}
#endif
