blob: 104a9b2cc19118f09f7b4278cd7b2ea5e7734887 [file] [log] [blame]
/*
* *****************************************************************************
*
* Copyright 2018 Gavin D. Howard
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* *****************************************************************************
*
* Code to manipulate vectors (resizable arrays).
*
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <status.h>
#include <vector.h>
#include <bc.h>
BcStatus bc_vec_double(BcVec *vec) {
uint8_t *ptr = realloc(vec->array, vec->size * (vec->cap * 2));
if (!ptr) return BC_STATUS_MALLOC_FAIL;
vec->array = ptr;
vec->cap *= 2;
return BC_STATUS_SUCCESS;
}
BcStatus bc_vec_init(BcVec *vec, size_t esize, BcVecFreeFunc dtor) {
assert(vec && esize);
vec->size = esize;
vec->cap = BC_VEC_INITIAL_CAP;
vec->len = 0;
vec->dtor = dtor;
vec->array = malloc(esize * BC_VEC_INITIAL_CAP);
if (!vec->array) return BC_STATUS_MALLOC_FAIL;
return BC_STATUS_SUCCESS;
}
BcStatus bc_vec_expand(BcVec *vec, size_t request) {
uint8_t *ptr;
assert(vec);
if (vec->cap >= request) return BC_STATUS_SUCCESS;
ptr = realloc(vec->array, vec->size * request);
if (!ptr) return BC_STATUS_MALLOC_FAIL;
vec->array = ptr;
vec->cap = request;
return BC_STATUS_SUCCESS;
}
BcStatus bc_vec_push(BcVec *vec, void *data) {
BcStatus status;
size_t size;
assert(vec && data);
vec->cap += !vec->cap;
if (vec->len == vec->cap && (status = bc_vec_double(vec))) return status;
size = vec->size;
memmove(vec->array + (size * vec->len++), data, size);
return BC_STATUS_SUCCESS;
}
BcStatus bc_vec_pushByte(BcVec *vec, uint8_t data) {
BcStatus status;
assert(vec && vec->size == sizeof(uint8_t));
vec->cap += !vec->cap;
if (vec->len == vec->cap && (status = bc_vec_double(vec))) return status;
vec->array[vec->len++] = data;
return BC_STATUS_SUCCESS;
}
BcStatus bc_vec_pushAt(BcVec *vec, void *data, size_t idx) {
BcStatus status;
uint8_t *ptr;
size_t size;
assert(vec && data && idx <= vec->len);
vec->cap += !vec->cap;
if (idx == vec->len) return bc_vec_push(vec, data);
if (vec->len == vec->cap && (status = bc_vec_double(vec))) return status;
size = vec->size;
ptr = vec->array + size * idx;
memmove(ptr + size, ptr, size * (vec->len++ - idx));
memmove(ptr, data, size);
return BC_STATUS_SUCCESS;
}
void* bc_vec_top(const BcVec *vec) {
if (!vec || !vec->len) return NULL;
return vec->array + vec->size * (vec->len - 1);
}
void* bc_vec_item(const BcVec *vec, size_t idx) {
if (!vec || !vec->len || idx >= vec->len) return NULL;
return vec->array + vec->size * idx;
}
void* bc_vec_item_rev(const BcVec *vec, size_t idx) {
if (!vec || !vec->len || idx >= vec->len) return NULL;
return vec->array + vec->size * (vec->len - idx - 1);
}
void bc_vec_pop(BcVec *vec) {
assert(vec);
if (!vec->len) return;
vec->len -= 1;
if (vec->dtor) vec->dtor(vec->array + (vec->size * vec->len));
}
void bc_vec_npop(BcVec *vec, size_t n) {
assert(vec && n <= vec->len);
if (!vec->dtor) vec->len -= n;
else {
size_t len = vec->len - n;
while (vec->len > len) bc_vec_pop(vec);
}
}
void bc_vec_free(void *vec) {
BcVec *s;
size_t esize, len, i;
BcVecFreeFunc sfree;
uint8_t *array;
s = (BcVec*) vec;
if (!s) return;
sfree = s->dtor;
if (sfree) {
len = s->len;
array = s->array;
esize = s->size;
for (i = 0; i < len; ++i) sfree(array + (i * esize));
}
free(s->array);
memset(s, 0, sizeof(BcVec));
}
size_t bc_veco_find(const BcVecO* vec, void *data) {
BcVecCmpFunc cmp;
size_t low, high;
cmp = vec->cmp;
low = 0;
high = vec->vec.len;
while (low < high) {
size_t mid;
int result;
uint8_t *ptr;
mid = (low + high) / 2;
ptr = bc_vec_item(&vec->vec, mid);
result = cmp(data, ptr);
if (!result) return mid;
if (result < 0) high = mid;
else low = mid + 1;
}
return low;
}
BcStatus bc_veco_init(BcVecO* vec, size_t esize,
BcVecFreeFunc dtor, BcVecCmpFunc cmp)
{
assert(vec && esize && cmp);
vec->cmp = cmp;
return bc_vec_init(&vec->vec, esize, dtor);
}
BcStatus bc_veco_insert(BcVecO* vec, void *data, size_t *idx) {
BcStatus status;
assert(vec && data && idx);
*idx = bc_veco_find(vec, data);
if (*idx > vec->vec.len) return BC_STATUS_VEC_OUT_OF_BOUNDS;
if (*idx != vec->vec.len && !vec->cmp(data, bc_vec_item(&vec->vec, *idx)))
return BC_STATUS_VEC_ITEM_EXISTS;
if (*idx >= vec->vec.len) {
*idx = vec->vec.len;
status = bc_vec_push(&vec->vec, data);
}
else status = bc_vec_pushAt(&vec->vec, data, *idx);
return status;
}
size_t bc_veco_index(const BcVecO* v, void *data) {
size_t i;
assert(v && data);
i = bc_veco_find(v, data);
if (i >= v->vec.len || v->cmp(data, bc_vec_item(&v->vec, i))) return BC_INVALID_IDX;
return i;
}