// SPDX-FileCopyrightText: 2017-2020 maskray <i@maskray.me>
// SPDX-FileCopyrightText: 2017-2020 thestr4ng3r <info@florianmaerkl.de>
// SPDX-License-Identifier: LGPL-3.0-only

#include "rz_util/rz_assert.h"
#include "rz_vector.h"

// Optimize memory usage on glibc
#if __WORDSIZE == 32
// Chunk size 24, minus 4 (chunk header), minus 8 for capacity and len, 12 bytes remaining for 3 void *
#define INITIAL_VECTOR_LEN 3
#else
// For __WORDSIZE == 64
// Chunk size 48, minus 8 (chunk header), minus 8 for capacity and len, 32 bytes remaining for 4 void *
#define INITIAL_VECTOR_LEN 4
#endif

#define NEXT_VECTOR_CAPACITY (vec->capacity < INITIAL_VECTOR_LEN \
		? INITIAL_VECTOR_LEN \
		: vec->capacity <= 12 ? vec->capacity * 2 \
				      : vec->capacity + (vec->capacity >> 1))

#define RESIZE_OR_RETURN_VAL(next_capacity, retval) \
	do { \
		size_t new_capacity = next_capacity; \
		void **new_a = realloc(vec->a, vec->elem_size * new_capacity); \
		if (!new_a && new_capacity) { \
			return retval; \
		} \
		vec->a = new_a; \
		vec->capacity = new_capacity; \
	} while (0)

#define RESIZE_OR_RETURN_NULL(next_capacity)  RESIZE_OR_RETURN_VAL(next_capacity, NULL)
#define RESIZE_OR_RETURN_FALSE(next_capacity) RESIZE_OR_RETURN_VAL(next_capacity, false)

#define RZ_VECTOR_SWAP_TMP_SIZE 256

RZ_API void rz_vector_init(RzVector *vec, size_t elem_size, RzVectorFree free, void *free_user) {
	rz_return_if_fail(vec);
	vec->a = NULL;
	vec->reverse_sorted = false;
	vec->capacity = vec->len = 0;
	vec->elem_size = elem_size;
	vec->free = free;
	vec->free_user = free_user;
}

RZ_API RzVector *rz_vector_new(size_t elem_size, RzVectorFree free, void *free_user) {
	RzVector *vec = RZ_NEW(RzVector);
	if (!vec) {
		return NULL;
	}
	rz_vector_init(vec, elem_size, free, free_user);
	return vec;
}

static void vector_free_elems(RzVector *vec) {
	if (vec->free) {
		while (vec->len > 0) {
			vec->free(rz_vector_index_ptr(vec, --vec->len), vec->free_user);
		}
	} else {
		vec->len = 0;
	}
}

RZ_API void rz_vector_fini(RzVector *vec) {
	rz_return_if_fail(vec);
	rz_vector_clear(vec);
	vec->free = NULL;
	vec->free_user = NULL;
}

/**
 * \brief Removes all elements, frees the internal buffer, and
 * sets the vector's capacity to 0.
 *
 * Use rz_vector_purge() if the buffer's capacity should not change.
 */
RZ_API void rz_vector_clear(RZ_BORROW RzVector *vec) {
	rz_return_if_fail(vec);
	vector_free_elems(vec);
	RZ_FREE(vec->a);
	vec->capacity = 0;
}

RZ_API void rz_vector_free(RzVector *vec) {
	if (vec) {
		rz_vector_fini(vec);
		free(vec);
	}
}

static void rz_vector_assign(RzVector *vec, void *p, const void *elem) {
	rz_return_if_fail(vec && p && elem);
	memcpy(p, elem, vec->elem_size);
}

/**
 * \brief Set element at \p index.
 * This is a simple memcpy. Vector length is not updated.
 * Use rz_vector_assign_at() if this is needed.
 *
 * \param vec The vector to update.
 * \param index Index where to write the element to.
 * \param elem Pointer to the element to copy.
 */
RZ_API void rz_vector_set(RZ_BORROW RzVector *vec, size_t index, const RZ_NONNULL void *elem) {
	rz_return_if_fail(vec && index < rz_vector_capacity(vec) && elem);
	void *p = rz_vector_index_ptr(vec, index);
	rz_return_if_fail(p);
	rz_vector_assign(vec, p, elem);
}

/**
 * \brief Set \p n elements, starting at element \p i to \p c.
 */
static void rz_vector_zeroize(RzVector *vec, size_t i, size_t n) {
	rz_return_if_fail(vec);
	memset((ut8 *)vec->a + (vec->elem_size * i), 0, vec->elem_size * n);
}

/**
 * \brief Clone the contents of \p src into \p dst.
 * \param dst The vector to clone into.
 * \param src The vector to clone from.
 * \param item_cpy The function to copy every element of \p src into \p dst
 * \return true on success, false on failure.
 */
RZ_API bool rz_vector_clone_intof(
	RZ_NONNULL RZ_BORROW RZ_OUT RzVector *dst,
	RZ_NONNULL RZ_BORROW RZ_IN const RzVector *src,
	RZ_NULLABLE const RzVectorItemCpyFunc item_cpy) {
	rz_return_val_if_fail(dst && src, false);
	dst->capacity = src->capacity;
	dst->len = src->len;
	dst->elem_size = src->elem_size;
	if (item_cpy) {
		dst->free = src->free;
		dst->free_user = src->free_user;
	} else {
		dst->free = NULL;
		dst->free_user = NULL;
	}
	if (!dst->len) {
		dst->a = NULL;
	} else {
		dst->a = malloc(src->elem_size * src->capacity);
		if (!dst->a) {
			return false;
		}
		const ut64 len = rz_vector_len(src);
		if (item_cpy) {
			for (ut64 i = 0; i < len; ++i) {
				item_cpy((ut8 *)(dst->a) + i * src->elem_size,
					(ut8 *)(src->a) + i * src->elem_size);
			}
		} else {
			memcpy(dst->a, src->a, src->elem_size * len);
		}
	}
	return true;
}

/**
 * Construct a new vector with the same contents and capacity as \p vec.
 * \param vec The source vector
 * \return The new vector
 */
RZ_API RZ_OWN RzVector *rz_vector_clonef(
	RZ_NONNULL RZ_BORROW RZ_IN const RzVector *vec,
	RZ_NULLABLE const RzVectorItemCpyFunc item_cpy) {
	rz_return_val_if_fail(vec, NULL);
	RzVector *dst = RZ_NEW(RzVector);
	if (!dst) {
		return NULL;
	}
	if (!rz_vector_clone_intof(dst, vec, item_cpy)) {
		free(dst);
		return NULL;
	}
	return dst;
}

/**
 * \brief Clone the contents of \p src into \p dst.
 * \param dst The vector to clone into.
 * \param src The vector to clone from.
 * \return true on success, false on failure.
 */
RZ_API bool rz_vector_clone_into(
	RZ_NONNULL RZ_BORROW RZ_OUT RzVector *dst,
	RZ_NONNULL RZ_BORROW RZ_IN const RzVector *src) {
	const bool ret = rz_vector_clone_intof(dst, src, NULL);
	dst->free = NULL;
	dst->free_user = NULL;
	return ret;
}

/**
 * \brief Construct a new vector with the same contents and capacity as \p vec.
 * The free function of the resulting vector will be NULL, so if elements are considered
 * to be owned and freed by \p vec, this will still be the case and the returned vector
 * only borrows them.
 *
 * \param vec The source vector
 * \return The new vector
 */
RZ_API RZ_OWN RzVector *rz_vector_clone(
	RZ_NONNULL RZ_BORROW RZ_IN const RzVector *vec) {
	RzVector *dst = rz_vector_clonef(vec, NULL);
	if (!dst) {
		return NULL;
	}
	dst->free = NULL;
	dst->free_user = NULL;
	return dst;
}

/**
 * \brief Assign the element \p elem at \p index in the vector.
 *
 * NOTE: This function can update the length of the vector. If the index
 * points after the last element, but not beyond the vector's capacity, it
 * sets the vector length to \p index + 1. Elements at [len, index) are set to zero.
 * Use rz_vector_set() if you need sideeffect-less manipulation of the vector slots.
 *
 * \param vec The vector to assign to.
 * \param index The index to assign the element to.
 * \param elem Pointer to the element to assign. If NULL, only the vector length is updated under the above condition.
 *
 * \return Pointer to the element at \p index. Or NULL in case of failure.
 */
RZ_API void *rz_vector_assign_at(RZ_BORROW RzVector *vec, size_t index, RZ_NULLABLE const void *elem) {
	rz_return_val_if_fail(vec && index < vec->capacity, NULL);
	void *p = rz_vector_index_ptr(vec, index);
	if (elem) {
		rz_vector_assign(vec, p, elem);
	}
	if (index >= rz_vector_len(vec)) {
		size_t len = rz_vector_len(vec);
		// Also zero the slot at index, if no element is assigned to it.
		size_t n = index - len + (!elem ? 1 : 0);
		rz_vector_zeroize(vec, len, n);
		vec->len = index + 1;
	}
	return p;
}

/**
 * \brief Removes the element at the given index.
 * This function will not keep the order of the elements.
 * Due to this, it won't use memmove and has much better
 * performance than rz_vector_remove_at().
 *
 * \param vec The vector to remove the element from.
 * \param index The index of the element to remove.
 * \param into Optional pointer to copy the removed element into.
 */
RZ_API void rz_vector_remove_at_unsorted(RZ_BORROW RzVector *vec, size_t index, RZ_OUT RZ_NULLABLE void *into) {
	rz_return_if_fail(vec);
	if (rz_vector_empty(vec)) {
		return;
	}
	size_t l = rz_vector_len(vec) - 1;
	if (index < l) {
		rz_vector_swap(vec, index, l);
	}
	rz_vector_pop(vec, into);
}

RZ_API void rz_vector_remove_at(RzVector *vec, size_t index, void *into) {
	if (rz_vector_empty(vec)) {
		return;
	}
	void *p = rz_vector_index_ptr(vec, index);
	if (into) {
		rz_vector_assign(vec, into, p);
	}
	vec->len--;
	if (index < vec->len) {
		memmove(p, (char *)p + vec->elem_size, vec->elem_size * (vec->len - index));
	}
}

RZ_API void rz_vector_remove_range(RzVector *vec, size_t index, size_t count, void *into) {
	rz_return_if_fail(vec && index + count <= vec->len);
	void *p = rz_vector_index_ptr(vec, index);
	if (into) {
		memcpy(into, p, count * vec->elem_size);
	}
	vec->len -= count;
	if (index < vec->len) {
		memmove(p, (char *)p + vec->elem_size * count, vec->elem_size * (vec->len - index));
	}
}

/**
 * \brief Deletes all elements in the vector. The internal buffer is not freed
 * so the vector's capacity stays the same.
 *
 * Use rz_vector_clear() if the buffer should be freed.
 */
RZ_API void rz_vector_purge(RZ_BORROW RzVector *vec) {
	vector_free_elems(vec);
}

RZ_API void *rz_vector_insert(RzVector *vec, size_t index, void *x) {
	rz_return_val_if_fail(vec && index <= vec->len, NULL);
	if (vec->len >= vec->capacity) {
		RESIZE_OR_RETURN_NULL(NEXT_VECTOR_CAPACITY);
	}
	void *p = rz_vector_index_ptr(vec, index);
	if (index < vec->len) {
		memmove((char *)p + vec->elem_size, p, vec->elem_size * (vec->len - index));
	}
	vec->len++;
	if (x) {
		rz_vector_assign(vec, p, x);
	}
	return p;
}

/**
 * \brief Inserts \p count elements from \p first in vector \p vec at index \p index, shifting elements if necessary.
 *
 * \param vec The vector to insert in.
 * \param index The index to insert the new elements. It can be equal to vector length which means insert-at-the-end.
 * \param first The array containing the new elements. If NULL, \p count empty elements will be inserted.
 * \param count The number of elements from \p first to be inserted, or number of empty elements if \p first is NULL.
 * \return A pointer to the inserted elements.
 */
RZ_API void *rz_vector_insert_range(RzVector *vec, size_t index, RZ_NULLABLE void *first, size_t count) {
	rz_return_val_if_fail(vec && index <= vec->len, NULL);
	if (count == 0) {
		return (char *)vec->a + vec->elem_size * index;
	}
	if (vec->len + count > vec->capacity) {
		RESIZE_OR_RETURN_NULL(RZ_MAX(NEXT_VECTOR_CAPACITY, vec->len + count));
	}
	size_t sz = count * vec->elem_size;
	void *p = rz_vector_index_ptr(vec, index);
	if (index < vec->len) {
		memmove((char *)p + sz, p, vec->elem_size * (vec->len - index));
	}
	vec->len += count;
	if (first) {
		memcpy(p, first, sz);
	}
	return p;
}

static bool bin_search_range(RZ_NONNULL RzVector *vec, RZ_NONNULL void *elem, RzVectorComparator cmp, void *user, RZ_OUT size_t *i) {
	size_t vlen = rz_vector_len(vec);
	if (vlen == 0) {
		*i = 0;
		return false;
	}

	size_t left = 0;
	size_t right = vlen;

	while (left < right) {
		size_t mid = left + (right - left) / 2;
		int cmp_res = cmp(elem, rz_vector_index_ptr(vec, mid), user);

		if (cmp_res == 0) {
			*i = mid;
			return true;
		}

		if (vec->reverse_sorted) {
			if (cmp_res > 0) {
				right = mid;
			} else {
				left = mid + 1;
			}
		} else {
			if (cmp_res > 0) {
				left = mid + 1;
			} else {
				right = mid;
			}
		}
	}

	*i = left;
	return false;
}

/**
 * \brief Inserts an element into a sorted vector keeping the order.
 * NOTE: This function assumes the vector is already sorted!
 * If it isn't the final position of the element is undefined.
 *
 * \param vec A sorted vector to insert the element into.
 * \param elem Pointer to the element to insert into the vector.
 * \param cmp The comparator for the elements.
 * \param user The user data passed to the comparator.
 *
 * \return Pointer to the position in the vector where the element was placed.
 * Or NULL in case of failure.
 */
RZ_API void *rz_vector_insert_sorted(RZ_NONNULL RzVector *vec, RZ_NONNULL void *elem, RzVectorComparator cmp, void *user) {
	rz_return_val_if_fail(vec && elem, NULL);

	size_t len = rz_vector_len(vec);
	if (len < 1) {
		return rz_vector_push(vec, elem);
	}

	size_t insert_index = 0;

	bin_search_range(vec, elem, cmp, user, &insert_index);

	return rz_vector_insert(vec, insert_index, elem);
}

/**
 * \brief Finds an element in the sorted vector via binary search.
 * NOTE: This function assumes the vector is already sorted!
 * If it isn't the result is undefined!
 *
 * \param vec A sorted vector to find the element in.
 * \param elem Pointer to the element to find in the vector.
 * \param cmp The comparator for the elements.
 * \param user The user data passed to the comparator.
 *
 * \return Index into the vector where the element is located.
 * Or SZT_MAX in case of failure or if no element was found.
 */
RZ_API size_t rz_vector_find_sorted(RZ_NONNULL RzVector *vec, RZ_NONNULL void *elem, RzVectorComparator cmp, void *user) {
	rz_return_val_if_fail(vec && elem, SZT_MAX);

	size_t i;
	if (!bin_search_range(vec, elem, cmp, user, &i)) {
		return SZT_MAX;
	}
	return i;
}

RZ_API void rz_vector_pop(RzVector *vec, void *into) {
	if (rz_vector_empty(vec)) {
		return;
	}
	if (into) {
		rz_vector_assign(vec, into, rz_vector_index_ptr(vec, vec->len - 1));
	}
	vec->len--;
}

RZ_API void rz_vector_pop_front(RzVector *vec, void *into) {
	if (rz_vector_empty(vec)) {
		return;
	}
	rz_vector_remove_at(vec, 0, into);
}

RZ_API void *rz_vector_push(RzVector *vec, void *x) {
	rz_return_val_if_fail(vec, NULL);
	if (vec->len >= vec->capacity) {
		RESIZE_OR_RETURN_NULL(NEXT_VECTOR_CAPACITY);
	}
	void *p = rz_vector_index_ptr(vec, vec->len++);
	if (x) {
		rz_vector_assign(vec, p, x);
	}
	return p;
}

RZ_API void *rz_vector_push_front(RzVector *vec, void *x) {
	rz_return_val_if_fail(vec, NULL);
	return rz_vector_insert(vec, 0, x);
}

/**
 * \brief Checks if the given element is in the vector.
 *
 * \param vec The vector to search in.
 * \param elem Pointer to the element to search.
 *
 * \return True if the vector contains the element, false otherwise.
 */
RZ_API bool rz_vector_contains(const RZ_NONNULL RzVector *vec, const RZ_NONNULL void *elem) {
	rz_return_val_if_fail(vec && elem, false);
	for (size_t i = 0; i < vec->len; i++) {
		// Casts to make Windows happy.
		char *elem_v = ((char *)vec->a) + (vec->elem_size * i);
		if (memcmp(elem_v, (char *)elem, vec->elem_size) == 0) {
			return true;
		}
	}
	return false;
}

/**
 * \brief Swaps two elements in the vector.
 *
 * \param vec The vector to swap elements in.
 * \param index_a The index of an element.
 * \param index_b The index of another element.
 *
 * \return True if elements were swapped. False in case of error.
 */
RZ_API bool rz_vector_swap(RzVector *vec, size_t index_a, size_t index_b) {
	rz_return_val_if_fail(vec && index_a < vec->len && index_b < vec->len, false);
	if (index_a == index_b) {
		return true;
	}
	void *elem_a = rz_vector_index_ptr(vec, index_a);
	void *elem_b = rz_vector_index_ptr(vec, index_b);

	ut8 stack_tmp[RZ_VECTOR_SWAP_TMP_SIZE];
	void *tmp = vec->elem_size <= sizeof(stack_tmp) ? stack_tmp : malloc(vec->elem_size);
	if (RZ_UNLIKELY(!tmp)) {
		rz_warn_if_reached();
		return false;
	}

	memcpy(tmp, elem_a, vec->elem_size);
	memcpy(elem_a, elem_b, vec->elem_size);
	memcpy(elem_b, tmp, vec->elem_size);

	if (tmp != stack_tmp) {
		free(tmp);
	}
	return true;
}

RZ_API void *rz_vector_reserve(RzVector *vec, size_t capacity) {
	rz_return_val_if_fail(vec, NULL);
	if (vec->capacity < capacity) {
		RESIZE_OR_RETURN_NULL(capacity);
	}
	return vec->a;
}

RZ_API void *rz_vector_shrink(RzVector *vec) {
	rz_return_val_if_fail(vec, NULL);
	if (vec->len < vec->capacity) {
		RESIZE_OR_RETURN_NULL(vec->len);
	}
	return vec->a;
}

/**
 * \brief Turn the vector into a fixed-size array.
 * This will clear the vector and return an array of its original contents whose
 * ownership is transferred to the caller.
 * This is useful when RzVector is used for its dynamically growing functionality as an
 * intermediate step to generate a fixed-size array in the end.
 */
RZ_API RZ_OWN void *rz_vector_take_array(RZ_BORROW RzVector *vec) {
	rz_return_val_if_fail(vec, NULL);
	rz_vector_shrink(vec);
	void *r = vec->a;
	vec->a = NULL;
	vec->capacity = vec->len = 0;
	return r;
}

// CLRS Quicksort. It is slow, but simple.
#define VEC_INDEX(a, i) (char *)a + elem_size *(i)

// Recursive quicksort. \p t and \p pivot are caller-provided scratch buffers of
// elem_size bytes each; they are reused across the whole recursion so the sort
// performs no per-call allocation.
static void vector_quick_sort_rec(void *a, size_t elem_size, size_t len, RzVectorComparator cmp, bool reverse, void *user, void *t, void *pivot) {
	if (len <= 1) {
		return;
	}
	size_t i = rand() % len, j = 0;

	memcpy(pivot, VEC_INDEX(a, i), elem_size);
	if (i != len - 1) {
		memcpy(VEC_INDEX(a, i), VEC_INDEX(a, len - 1), elem_size);
	}
	for (i = 0; i < len - 1; i++) {
		int c = cmp(VEC_INDEX(a, i), pivot, user);
		if ((c < 0 && !reverse) || (c > 0 && reverse)) {
			if (j != i) {
				memcpy(t, VEC_INDEX(a, i), elem_size);
				memcpy(VEC_INDEX(a, i), VEC_INDEX(a, j), elem_size);
				memcpy(VEC_INDEX(a, j), t, elem_size);
			}
			j++;
		}
	}
	if (j != len - 1) {
		memcpy(VEC_INDEX(a, len - 1), VEC_INDEX(a, j), elem_size);
	}
	memcpy(VEC_INDEX(a, j), pivot, elem_size);
	vector_quick_sort_rec(a, elem_size, j, cmp, reverse, user, t, pivot);
	vector_quick_sort_rec(VEC_INDEX(a, j + 1), elem_size, len - j - 1, cmp, reverse, user, t, pivot);
}

#define RZ_VECTOR_SORT_TMP_SIZE 256

static void vector_quick_sort(void *a, size_t elem_size, size_t len, RzVectorComparator cmp, bool reverse, void *user) {
	rz_return_if_fail(a);
	if (len <= 1) {
		return;
	}
	// Allocate the two scratch buffers once for the whole sort instead of on
	// every recursive call. Small elements (the common case) use the stack.
	ut8 t_buf[RZ_VECTOR_SORT_TMP_SIZE];
	ut8 pivot_buf[RZ_VECTOR_SORT_TMP_SIZE];
	void *t = elem_size <= RZ_VECTOR_SORT_TMP_SIZE ? (void *)t_buf : malloc(elem_size);
	void *pivot = elem_size <= RZ_VECTOR_SORT_TMP_SIZE ? (void *)pivot_buf : malloc(elem_size);
	if (!t || !pivot) {
		if (t != (void *)t_buf) {
			free(t);
		}
		if (pivot != (void *)pivot_buf) {
			free(pivot);
		}
		RZ_LOG_ERROR("Failed to allocate memory\n");
		return;
	}

	vector_quick_sort_rec(a, elem_size, len, cmp, reverse, user, t, pivot);

	if (t != (void *)t_buf) {
		free(t);
	}
	if (pivot != (void *)pivot_buf) {
		free(pivot);
	}
}
#undef VEC_INDEX

/**
 * \brief Sort function for RzVector
 *
 * \param vec pointer to RzVector
 * \param cmp function used for comparing elements while sorting
 * \param reverse sort order, ascending order when reverse = False
 * \param user user pointer to extra data.
 */
RZ_API void rz_vector_sort(RzVector *vec, RzVectorComparator cmp, bool reverse, void *user) {
	rz_return_if_fail(vec && cmp);
	vec->reverse_sorted = reverse;
	if (rz_vector_empty(vec)) {
		return;
	}
	vector_quick_sort(vec->a, vec->elem_size, vec->len, cmp, reverse, user);
}

// pvector

static void pvector_free_elem(void *e, void *user) {
	void *p = *((void **)e);
	RzPVectorFree elem_free = (RzPVectorFree)user;
	elem_free(p);
}

RZ_API void rz_pvector_init(RzPVector *vec, RzPVectorFree free) {
	rz_vector_init(&vec->v, sizeof(void *), free ? pvector_free_elem : NULL, free);
}

RZ_API RzPVector *rz_pvector_new(RzPVectorFree free) {
	RzPVector *v = RZ_NEW(RzPVector);
	if (!v) {
		return NULL;
	}
	rz_pvector_init(v, free);
	return v;
}

RZ_API RzPVector *rz_pvector_new_with_len(RzPVectorFree free, size_t length) {
	RzPVector *v = rz_pvector_new(free);
	if (!v) {
		return NULL;
	}
	void **p = rz_pvector_reserve(v, length);
	if (!p) {
		rz_pvector_free(v);
		return NULL;
	}
	rz_vector_zeroize(&v->v, 0, v->v.capacity);
	v->v.len = length;
	return v;
}

/**
 * \brief Removes all elements and frees the internal buffer.
 */
RZ_API void rz_pvector_clear(RZ_BORROW RzPVector *vec) {
	rz_return_if_fail(vec);
	rz_vector_clear(&vec->v);
}

RZ_API void rz_pvector_fini(RzPVector *vec) {
	rz_return_if_fail(vec);
	rz_vector_fini(&vec->v);
}

RZ_API void rz_pvector_free(RzPVector *vec) {
	if (!vec) {
		return;
	}
	rz_vector_fini(&vec->v);
	free(vec);
}

/**
 * \brief Checks if a the pointer \p x is in the vector.
 *
 * \param vec The vector to search in.
 * \param x The pointer to search.
 *
 * \return Returns the pointer to the \p x pointer in the vector if found. NULL otherwise.
 */
RZ_API void **rz_pvector_contains(RzPVector *vec, const void *x) {
	rz_return_val_if_fail(vec, NULL);
	size_t i;
	for (i = 0; i < vec->v.len; i++) {
		if (((void **)vec->v.a)[i] == x) {
			return &((void **)vec->v.a)[i];
		}
	}
	return NULL;
}

/**
 * \brief Find the \p element in the \p vec
 * \param vec the RzPVector to search in
 * \param value the value that elements in pvector compare against by \p cmp
 * \param cmp the comparator function
 * \return the iter of the element if found, NULL otherwise
 */
RZ_API RZ_BORROW void **rz_pvector_find(RZ_NONNULL const RzPVector *vec, RZ_NONNULL const void *value, RZ_NONNULL RzPVectorComparator cmp, void *user) {
	rz_return_val_if_fail(vec, NULL);

	void **iter;
	rz_pvector_foreach (vec, iter) {
		if (!cmp(value, *iter, user)) {
			return iter;
		}
	}
	return NULL;
}

/**
 * \brief Find the \p element in the \p vec.
 * \param vec the RzPVector to search in.
 * \param value the value that elements in pvector compare against by \p cmp.
 * \param cmp the comparator function.
 * \return Returns the index of the first matching element, SZT_MAX otherwise.
 */
RZ_API size_t rz_pvector_find_index(RZ_NONNULL const RzPVector *vec, RZ_NONNULL const void *value, RZ_NONNULL RzPVectorComparator cmp, void *user) {
	rz_return_val_if_fail(vec, SZT_MAX);

	void **iter = NULL;
	size_t i = 0;
	rz_pvector_enumerate (vec, iter, i) {
		if (!cmp(value, *iter, user)) {
			return i;
		}
	}
	return SZT_MAX;
}

/**
 * \brief Joins 2 pvector into one (pvec2 pointer needs to be freed by the user)
 *
 **/
RZ_API bool rz_pvector_join(RZ_NONNULL RzPVector *pvec1, RZ_NONNULL RzPVector *pvec2) {
	rz_return_val_if_fail(pvec1 && pvec2, 0);

	if (rz_pvector_empty(pvec2)) {
		return false;
	}

	if (pvec1->v.len + pvec2->v.len > pvec1->v.capacity) {
		RzVector *vec = &pvec1->v;
		RESIZE_OR_RETURN_NULL(RZ_MAX(NEXT_VECTOR_CAPACITY, pvec1->v.len + pvec2->v.len));
	}
	memmove((void **)pvec1->v.a + pvec1->v.len, pvec2->v.a, pvec2->v.elem_size * pvec2->v.len);
	pvec1->v.len += pvec2->v.len;

	// element in pvec2 is freed by pvec1
	pvec2->v.len = 0;

	return true;
}

/**
 * \brief Assign the pointer \p ptr at \p index into the pvector.
 *
 * NOTE: This function can update the length of the vector. If the index
 * points after the last element, but not beyond the vector's capacity, it
 * sets the vector length to \p index + 1. Elements at [len, index) are set to zero.
 * Use rz_pvector_set() if you need sideeffect-less manipulation of the vector slots.
 *
 * \param vec The pvector to assign to.
 * \param index The index to assign the pointer to.
 * \param ptr The pointer to assign.
 *
 * \return The pointer stored at \p index before. NULL if index >= vec->len or in case of failure.
 */
RZ_API void *rz_pvector_assign_at(RZ_BORROW RZ_NONNULL RzPVector *vec, size_t index, RZ_OWN RZ_NULLABLE void *ptr) {
	rz_return_val_if_fail(vec, NULL);
	if (index >= rz_pvector_capacity(vec)) {
		if (vec->v.free_user && ptr) {
			RzPVectorFree free_fn = (RzPVectorFree)vec->v.free_user;
			free_fn(ptr);
		}
		return NULL;
	}
	bool increased_len = index >= rz_pvector_len(vec);
	void **p = rz_vector_index_ptr(&vec->v, index);
	void *prev = !p || increased_len ? NULL : *p;
	rz_vector_assign_at(&vec->v, index, &ptr);
	return prev;
}

/**
 * \brief Removes the element at the given index.
 * This function will not keep the order of the elements.
 * Due to this, it won't use memmove and has much better
 * performance than rz_pvector_remove_at().
 *
 * \param vec The vector to remove the element from.
 * \param index The index of the element to remove.
 *
 * \return The removed pointer. Or NULL in case of failure.
 */
RZ_API void *rz_pvector_remove_at_unsorted(RZ_BORROW RzPVector *vec, size_t index) {
	rz_return_val_if_fail(vec, NULL);
	void *r = rz_pvector_at(vec, index);
	rz_vector_remove_at_unsorted(&vec->v, index, NULL);
	return r;
}

RZ_API void *rz_pvector_remove_at(RzPVector *vec, size_t index) {
	rz_return_val_if_fail(vec, NULL);
	void *r = rz_pvector_at(vec, index);
	rz_vector_remove_at(&vec->v, index, NULL);
	return r;
}

RZ_API void rz_pvector_remove_data(RzPVector *vec, void *x) {
	void **el = rz_pvector_contains(vec, x);
	if (!el) {
		return;
	}

	size_t index = el - (void **)vec->v.a;
	rz_vector_remove_at(&vec->v, index, NULL);
}

RZ_API void *rz_pvector_pop(RzPVector *vec) {
	rz_return_val_if_fail(vec, NULL);
	void *r = rz_pvector_at(vec, vec->v.len - 1);
	rz_vector_pop(&vec->v, NULL);
	return r;
}

RZ_API void *rz_pvector_pop_front(RzPVector *vec) {
	rz_return_val_if_fail(vec, NULL);
	void *r = rz_pvector_at(vec, 0);
	rz_vector_pop_front(&vec->v, NULL);
	return r;
}

// CLRS Quicksort. It is slow, but simple.
static void quick_sort(void **a, size_t n, RzPVectorComparator cmp, void *user) {
	if (n <= 1) {
		return;
	}
	size_t i = rand() % n, j = 0;
	void *t, *pivot = a[i];
	a[i] = a[n - 1];
	for (i = 0; i < n - 1; i++) {
		if (cmp(a[i], pivot, user) < 0) {
			t = a[i];
			a[i] = a[j];
			a[j] = t;
			j++;
		}
	}
	a[n - 1] = a[j];
	a[j] = pivot;
	quick_sort(a, j, cmp, user);
	quick_sort(a + j + 1, n - j - 1, cmp, user);
}

RZ_API void rz_pvector_sort(RzPVector *vec, RzPVectorComparator cmp, void *user) {
	rz_return_if_fail(vec && cmp);
	if (rz_pvector_empty(vec)) {
		return;
	}
	quick_sort(vec->v.a, vec->v.len, cmp, user);
}

/**
 * \brief Find the unique values in the \p vec and push it in a new RzPVector.
 * \param vec the RzPVector to search in.
 * \param cmp the comparator function.
 * \param user the user data for \p cmp function.
 * \return Returns a new RzPVector which contains only unique values.
 */
RZ_API RZ_OWN RzPVector *rz_pvector_uniq(RZ_NONNULL const RzPVector *vec, RZ_NONNULL RzPVectorComparator cmp, void *user) {
	rz_return_val_if_fail(vec && cmp, NULL);

	RzPVector *npv = rz_pvector_new(NULL);
	if (!npv) {
		return NULL;
	}
	void **it;
	rz_pvector_foreach (vec, it) {
		bool found = false;
		void **it2;
		void *item = *it;
		rz_pvector_foreach (npv, it2) {
			void *item2 = *it2;
			if (cmp(item, item2, user) == 0) {
				found = true;
				break;
			}
		}
		if (!found) {
			rz_pvector_push(npv, item);
		}
	}
	return npv;
}
