/*
   +----------------------------------------------------------------------+
   | Xdebug                                                               |
   +----------------------------------------------------------------------+
   | Copyright (c) 2002-2020 Derick Rethans                               |
   +----------------------------------------------------------------------+
   | This source file is subject to version 1.01 of the Xdebug license,   |
   | that is bundled with this package in the file LICENSE, and is        |
   | available at through the world-wide-web at                           |
   | https://xdebug.org/license.php                                       |
   | If you did not receive a copy of the Xdebug license and are unable   |
   | to obtain it through the world-wide-web, please send a note to       |
   | derick@xdebug.org so we can mail you a copy immediately.             |
   +----------------------------------------------------------------------+
 */

#ifndef __XDEBUG_VECTOR_H__
#define __XDEBUG_VECTOR_H__

#include <stddef.h>
#include "mm.h"

typedef void (*xdebug_vector_dtor)(void *);

typedef struct _xdebug_vector {
	size_t capacity;
	size_t count;

	size_t              element_size;
	void               *data;
	xdebug_vector_dtor  dtor;
} xdebug_vector;

static void __xdebug_grow_vector_if_needed(xdebug_vector *v)
{
	if (v->count + 1 > v->capacity) {
		if (v->capacity == 0) {
			v->capacity = 32;
		} else {
			v->capacity = v->capacity * 3 / 2;
		}

		v->data = xdrealloc(v->data, v->capacity * v->element_size);
	}
}

static inline void *xdebug_vector_push(xdebug_vector *v)
{
	void *tmp_top = NULL;

	__xdebug_grow_vector_if_needed(v);

	v->count++;

	tmp_top = ((char*) v->data + ((v->count - 1) * v->element_size));
	memset(tmp_top, '\0', v->element_size);

	return tmp_top;
}

static inline void xdebug_vector_pop(xdebug_vector *v)
{
	v->dtor(((char*) v->data + ((v->count - 1) * v->element_size)));
	v->count--;
}

static inline void *xdebug_vector_element_get(xdebug_vector *v, size_t index)
{
	if (!v || index >= v->count) {
		return NULL;
	}
	return ((char*) v->data + (index * (v)->element_size));
}

static inline int xdebug_vector_element_is_valid(xdebug_vector *v, void *element)
{
	if ((char*) element < (char*) v->data) {
		return 0;
	}
	if ((char*) element > ((char*) v->data + ((v->count - 1) * v->element_size))) {
		return 0;
	}

	return 1;
}

#define XDEBUG_VECTOR_HEAD(v) xdebug_vector_element_get((v), 0)
#define XDEBUG_VECTOR_TAIL(v) xdebug_vector_element_get((v), (v)->count-1)

#define XDEBUG_VECTOR_COUNT(v) (v)->count

static inline xdebug_vector *xdebug_vector_alloc(size_t element_size, xdebug_vector_dtor dtor)
{
	xdebug_vector *tmp;

	tmp = xdmalloc(sizeof(xdebug_vector));
	tmp->capacity = 0;
	tmp->count = 0;
	tmp->data = NULL;
	tmp->dtor = dtor;
	tmp->element_size = element_size;

	return tmp;
}

static inline void xdebug_vector_destroy(xdebug_vector *v)
{
	while (XDEBUG_VECTOR_COUNT(v)) {
		xdebug_vector_pop(v);
	}
	xdfree(v->data);
	xdfree(v);
}

#endif /* __XDEBUG_VECTOR_H__ */
