/* 
 * Copyright 2008-2018 Aerospike, Inc.
 *
 * Portions may be licensed to Aerospike, Inc. under one or more contributor
 * license agreements.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
#include <aerospike/as_vector.h>
#include <citrusleaf/alloc.h>

#define FLAGS_HEAP 1
#define FLAGS_CREATED 2

void
as_vector_init(as_vector* vector, uint32_t item_size, uint32_t capacity)
{
	vector->list = cf_malloc(capacity * item_size);
	vector->capacity = capacity;
	vector->item_size = item_size;
	vector->size = 0;
	vector->flags = 1;
}

as_vector*
as_vector_create(uint32_t item_size, uint32_t capacity)
{
	as_vector* vector = cf_malloc(sizeof(as_vector));
	as_vector_init(vector, item_size, capacity);
	vector->flags = FLAGS_HEAP | FLAGS_CREATED;
	return vector;
}

void
as_vector_destroy(as_vector* vector)
{
	if (vector->flags & FLAGS_HEAP) {
		cf_free(vector->list);
		
		if (vector->flags & FLAGS_CREATED) {
			cf_free(vector);
		}
	}
}

void
as_vector_increase_capacity(as_vector* vector)
{
	if (vector->flags & FLAGS_HEAP) {
		vector->capacity *= 2;
		vector->list = cf_realloc(vector->list, vector->capacity * vector->item_size);
	}
	else {
		uint32_t capacity = vector->capacity * 2;
		void* tmp = cf_malloc(capacity * vector->item_size);
		memcpy(tmp, vector->list, vector->capacity * vector->item_size);
		vector->list = tmp;
		vector->capacity = capacity;
		vector->flags |= FLAGS_HEAP;
	}
}

bool
as_vector_append_unique(as_vector* vector, void* value)
{
	char* item = vector->list;
	for (uint32_t i = 0; i < vector->size; i++) {
		if (memcmp(item, value, vector->item_size) == 0) {
			return false;
		}
		item += vector->item_size;
	}
	as_vector_append(vector, value);
	return true;
}

void*
as_vector_to_array(as_vector* vector, uint32_t* size)
{
	size_t len = vector->size * vector->item_size;
	void* array = cf_malloc(len);
	memcpy(array, vector->list, len);
	*size = vector->size;
	return array;
}

bool
as_vector_remove(as_vector* vector, uint32_t index)
{
	if (index >= vector->size) {
		return false;
	}

	memmove((uint8_t*)vector->list + (index * vector->item_size),
		    (uint8_t*)vector->list + ((index + 1) * vector->item_size),
		    (vector->size - index - 1) * vector->item_size);

	vector->size--;
	return true;
}
