diff options
Diffstat (limited to 'libapol/src/vector.c')
-rw-r--r-- | libapol/src/vector.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/libapol/src/vector.c b/libapol/src/vector.c new file mode 100644 index 0000000..28fc897 --- /dev/null +++ b/libapol/src/vector.c @@ -0,0 +1,457 @@ +/** + * @file + * Contains the implementation of a generic vector. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <apol/vector.h> +#include "vector-internal.h" +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +/** The default initial capacity of a vector; must be a positive integer */ +#define APOL_VECTOR_DFLT_INIT_CAP 10 + +/** + * Generic vector structure. Stores elements as void*. + */ +struct apol_vector +{ + /** The array of element pointers, which will be resized as needed. */ + void **array; + /** The number of elements currently stored in array. */ + size_t size; + /** The actual amount of space in array. This amount will always + * be >= size and will grow exponentially as needed. */ + size_t capacity; + apol_vector_free_func *fr; +}; + +apol_vector_t *apol_vector_create(apol_vector_free_func * fr) +{ + return apol_vector_create_with_capacity(APOL_VECTOR_DFLT_INIT_CAP, fr); +} + +apol_vector_t *apol_vector_create_with_capacity(size_t cap, apol_vector_free_func * fr) +{ + apol_vector_t *v = NULL; + int error; + + if (cap < 1) { + cap = 1; + } + v = calloc(1, sizeof(apol_vector_t)); + if (!v) + return NULL; + v->array = calloc((v->capacity = cap), sizeof(void *)); + if (!(v->array)) { + error = errno; + free(v); + errno = error; + return NULL; + } + v->fr = fr; + return v; +} + +apol_vector_t *apol_vector_create_from_iter(qpol_iterator_t * iter, apol_vector_free_func * fr) +{ + size_t iter_size; + apol_vector_t *v; + void *item; + int error; + if (qpol_iterator_get_size(iter, &iter_size) < 0 || (v = apol_vector_create_with_capacity(iter_size, fr)) == NULL) { + return NULL; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, &item)) { + error = errno; + free(v); + errno = error; + return NULL; + } + apol_vector_append(v, item); + } + return v; +} + +apol_vector_t *apol_vector_create_from_vector(const apol_vector_t * v, apol_vector_dup_func * dup, void *data, + apol_vector_free_func * fr) +{ + apol_vector_t *new_v; + size_t i; + if (v == NULL) { + errno = EINVAL; + return NULL; + } + if ((new_v = apol_vector_create_with_capacity(v->capacity, fr)) == NULL) { + return NULL; + } + if (dup == NULL) { + memcpy(new_v->array, v->array, v->size * sizeof(void *)); + } else { + for (i = 0; i < v->size; i++) { + new_v->array[i] = dup(v->array[i], data); + } + } + new_v->size = v->size; + return new_v; +} + +apol_vector_t *apol_vector_create_from_intersection(const apol_vector_t * v1, + const apol_vector_t * v2, apol_vector_comp_func * cmp, void *data) +{ + apol_vector_t *new_v; + size_t i, j; + if (v1 == NULL || v2 == NULL) { + errno = EINVAL; + return NULL; + } + if ((new_v = apol_vector_create(NULL)) == NULL) { + return NULL; + } + for (i = 0; i < v1->size; i++) { + for (j = 0; j < v2->size; j++) { + if ((cmp != NULL && cmp(v1->array[i], v2->array[j], data) == 0) || + (cmp == NULL && v1->array[i] == v2->array[j])) { + if (apol_vector_append(new_v, v1->array[i]) < 0) { + apol_vector_destroy(&new_v); + return NULL; + } + break; + } + } + } + return new_v; +} + +void apol_vector_destroy(apol_vector_t ** v) +{ + size_t i = 0; + + if (!v || !(*v)) + return; + + if ((*v)->fr) { + for (i = 0; i < (*v)->size; i++) { + (*v)->fr((*v)->array[i]); + } + } + free((*v)->array); + (*v)->array = NULL; + free(*v); + *v = NULL; +} + +size_t apol_vector_get_size(const apol_vector_t * v) +{ + if (!v) { + errno = EINVAL; + return 0; + } else { + return v->size; + } +} + +size_t apol_vector_get_capacity(const apol_vector_t * v) +{ + if (!v) { + errno = EINVAL; + return 0; + } else { + return v->capacity; + } +} + +void *apol_vector_get_element(const apol_vector_t * v, size_t idx) +{ + if (!v || !(v->array)) { + errno = EINVAL; + return NULL; + } + + if (idx >= v->size) { + errno = ERANGE; + return NULL; + } + + return v->array[idx]; +} + +/** + * Grows a vector, by reallocating additional space for it. + * + * @param v Vector to which increase its size. + * + * @return 0 on success, -1 on error. + */ +static int apol_vector_grow(apol_vector_t * v) +{ + void **tmp; + size_t new_capacity = v->capacity; + if (new_capacity >= 128) { + new_capacity += 128; + } else { + new_capacity *= 2; + } + tmp = realloc(v->array, new_capacity * sizeof(void *)); + if (!tmp) { + return -1; + } + v->capacity = new_capacity; + v->array = tmp; + return 0; +} + +int apol_vector_get_index(const apol_vector_t * v, const void *elem, apol_vector_comp_func * cmp, void *data, size_t * i) +{ + if (!v || !i) { + errno = EINVAL; + return -1; + } + + for (*i = 0; *i < v->size; (*i)++) { + if ((cmp != NULL && cmp(v->array[*i], elem, data) == 0) || (cmp == NULL && elem == v->array[*i])) { + return 0; + } + } + return -1; +} + +int apol_vector_append(apol_vector_t * v, void *elem) +{ + if (!v) { + errno = EINVAL; + return -1; + } + + if (v->size >= v->capacity && apol_vector_grow(v)) { + return -1; + } + + v->array[v->size] = elem; + v->size++; + + return 0; +} + +int apol_vector_append_unique(apol_vector_t * v, void *elem, apol_vector_comp_func * cmp, void *data) +{ + size_t i; + if (apol_vector_get_index(v, elem, cmp, data, &i) < 0) { + return apol_vector_append(v, elem); + } + errno = EEXIST; + return 1; +} + +int apol_vector_compare(const apol_vector_t * a, const apol_vector_t * b, apol_vector_comp_func * cmp, void *data, size_t * i) +{ + int compval; + if (a == NULL || b == NULL || i == NULL) { + errno = EINVAL; + return 0; + } + size_t a_len = apol_vector_get_size(a); + size_t b_len = apol_vector_get_size(b); + for (*i = 0; *i < a_len && *i < b_len; (*i)++) { + if (cmp != NULL) { + compval = cmp(a->array[*i], b->array[*i], data); + } else { + compval = (int)((char *)a->array[*i] - (char *)b->array[*i]); + } + if (compval != 0) { + return compval; + } + } + if (a_len == b_len) { + return 0; + } else if (a_len < b_len) { + return -1; + } else { + return 1; + } +} + +static size_t vector_qsort_partition(void **data, size_t first, size_t last, apol_vector_comp_func * cmp, void *arg) +{ + void *pivot = data[last]; + size_t i = first, j = last; + while (i < j) { + if (cmp(data[i], pivot, arg) <= 0) { + i++; + } else { + data[j] = data[i]; + data[i] = data[j - 1]; + j--; + } + } + data[j] = pivot; + return j; +} + +static void vector_qsort(void **data, size_t first, size_t last, apol_vector_comp_func * cmp, void *arg) +{ + if (first < last) { + size_t i = vector_qsort_partition(data, first, last, cmp, arg); + /* need this explicit check here, because i is an + * unsigned integer, and subtracting 1 from 0 is + * bad */ + if (i > 0) { + vector_qsort(data, first, i - 1, cmp, arg); + } + vector_qsort(data, i + 1, last, cmp, arg); + } +} + +/** + * Generic comparison function, which treats elements of the vector as + * unsigned integers. + */ +static int vector_int_comp(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + char *i = (char *)a; + char *j = (char *)b; + if (i < j) { + return -1; + } else if (i > j) { + return 1; + } + return 0; +} + +/* implemented as an in-place quicksort */ +void apol_vector_sort(apol_vector_t * v, apol_vector_comp_func * cmp, void *data) +{ + if (!v) { + errno = EINVAL; + return; + } + if (cmp == NULL) { + cmp = vector_int_comp; + } + if (v->size > 1) { + vector_qsort(v->array, 0, v->size - 1, cmp, data); + } +} + +void apol_vector_sort_uniquify(apol_vector_t * v, apol_vector_comp_func * cmp, void *data) +{ + if (!v) { + errno = EINVAL; + return; + } + if (cmp == NULL) { + cmp = vector_int_comp; + } + if (v->size > 1) { + size_t i, j = 0; + void **new_array; + /* sweep through the array, do a quick compaction, + * then sort */ + for (i = 1; i < v->size; i++) { + if (cmp(v->array[i], v->array[j], data) != 0) { + /* found a unique element */ + j++; + v->array[j] = v->array[i]; + } else { + /* found a non-unique element */ + if (v->fr != NULL) { + v->fr(v->array[i]); + } + } + } + v->size = j + 1; + + apol_vector_sort(v, cmp, data); + j = 0; + for (i = 1; i < v->size; i++) { + if (cmp(v->array[i], v->array[j], data) != 0) { + /* found a unique element */ + j++; + v->array[j] = v->array[i]; + } else { + /* found a non-unique element */ + if (v->fr != NULL) { + v->fr(v->array[i]); + } + } + } + /* try to realloc vector to save space */ + v->size = j + 1; + if ((new_array = realloc(v->array, v->size * sizeof(void *))) != NULL) { + v->array = new_array; + v->capacity = v->size; + } + } +} + +int apol_vector_cat(apol_vector_t * dest, const apol_vector_t * src) +{ + size_t i, orig_size, cap; + void **a; + if (!src || !apol_vector_get_size(src)) { + return 0; /* nothing to append */ + } + + if (!dest) { + errno = EINVAL; + return -1; + } + orig_size = apol_vector_get_size(dest); + for (i = 0; i < apol_vector_get_size(src); i++) + if (apol_vector_append(dest, apol_vector_get_element(src, i))) { + /* revert if possible */ + if (orig_size == 0) { + cap = 1; + } else { + cap = orig_size; + } + a = realloc(dest->array, cap * sizeof(*a)); + if (a != NULL) { + dest->array = a; + } + dest->size = orig_size; + dest->capacity = cap; + return -1; + } + + return 0; +} + +int apol_vector_remove(apol_vector_t * v, const size_t idx) +{ + if (v == NULL || idx >= v->size) { + errno = EINVAL; + return -1; + } + memmove(v->array + idx, v->array + idx + 1, sizeof(v->array[0]) * (v->size - idx - 1)); + v->size--; + return 0; +} + +/******************** friend function below ********************/ + +void vector_set_free_func(apol_vector_t * v, apol_vector_free_func * fr) +{ + v->fr = fr; +} |