diff options
Diffstat (limited to 'common/refarray/ref_array.c')
-rw-r--r-- | common/refarray/ref_array.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/common/refarray/ref_array.c b/common/refarray/ref_array.c new file mode 100644 index 000000000..d33a16b4f --- /dev/null +++ b/common/refarray/ref_array.c @@ -0,0 +1,294 @@ +/* + REF ARRAY + + Implementation of the dynamic array with reference count. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + This program 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 General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE +#include <errno.h> /* for errors */ +#include <stdint.h> +#include <malloc.h> +#include <string.h> +#include <stdio.h> + +#include "ref_array.h" +#include "config.h" +#include "trace.h" + +/* The structure used in referenced array */ +struct ref_array { + void *storage; /* The storage buffer */ + size_t elsize; /* Size of one element in the buffer */ + uint32_t size; /* Size of the storage in items */ + uint32_t grow_by; /* What increment use to reallocate memory */ + uint32_t len; /* Number of the elements in the array */ + uint32_t refcount; /* Reference count */ + ref_array_fn cb; /* Cleanup callback */ + void *cb_data; /* Caller's callback data */ +}; + +/****************************************************/ +/* INTERNAL FUNCTIONS */ +/****************************************************/ +static int ref_array_grow(struct ref_array *ra) +{ + int error = EOK; + void *newbuf = NULL; + + TRACE_FLOW_STRING("ref_array_grow", "Entry"); + + TRACE_INFO_NUMBER("Current length: ", ra->len); + TRACE_INFO_NUMBER("Current size: ", ra->size); + + /* Grow buffer if needed */ + newbuf = realloc(ra->storage, (ra->size + ra->grow_by) * ra->elsize); + if (newbuf == NULL) { + TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); + return ENOMEM; + } + + ra->storage = newbuf; + ra->size += ra->grow_by; + + TRACE_INFO_NUMBER("Final size: ", ra->size); + TRACE_FLOW_NUMBER("elapi_grow_data. Exit. Returning", error); + return error; + +} + + +/****************************************************/ +/* PUBLIC FUNCTIONS */ +/****************************************************/ + +/* Create referenced array */ +int ref_array_create(struct ref_array **ra, + size_t elemsz, + uint32_t grow_by, + ref_array_fn cb, + void *data) +{ + struct ref_array *new_ra = NULL; + + TRACE_FLOW_STRING("ref_array_create", "Entry"); + + if (!ra) { + TRACE_ERROR_NUMBER("Uninitialized argument.", EINVAL); + return EINVAL; + } + + if ((!elemsz) || (!grow_by)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + return EINVAL; + } + + new_ra = (struct ref_array *)malloc(sizeof(struct ref_array)); + + if (!new_ra) { + TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); + return ENOMEM; + } + + new_ra->storage = NULL; + new_ra->elsize = elemsz; + new_ra->size = 0; + new_ra->grow_by = grow_by; + new_ra->len = 0; + new_ra->refcount = 1; + new_ra->cb = cb; + new_ra->cb_data = data; + + *ra = new_ra; + + TRACE_FLOW_STRING("ref_array_create", "Exit"); + return EOK; +} + +/* Get new reference to an array */ +struct ref_array *ref_array_getref(struct ref_array *ra) +{ + TRACE_FLOW_STRING("ref_array_getref", "Entry"); + + /* Check if array is not NULL */ + if (ra) { + TRACE_INFO_NUMBER("Increasing reference count. Current: ", ra->refcount); + /* Increase reference count */ + ra->refcount++; + TRACE_INFO_NUMBER("Increased reference count. New: ", ra->refcount); + + } + else { + TRACE_ERROR_STRING("Uninitialized array.", "Returning NULL"); + } + + TRACE_FLOW_STRING("ref_array_getref", "Exit"); + return ra; +} + +/* Delete the array */ +void ref_array_destroy(struct ref_array *ra) +{ + int idx; + + TRACE_FLOW_STRING("ref_array_destroy", "Entry"); + + /* Check if array is not NULL */ + if (!ra) { + TRACE_ERROR_STRING("Uninitialized array.", "Coding error???"); + return; + } + + TRACE_INFO_NUMBER("Current reference count: ", ra->refcount); + if (ra->refcount) { + /* Decrease reference count */ + ra->refcount--; + if (ra->refcount == 0) { + TRACE_INFO_NUMBER("It is time to delete array. Count:", ra->refcount); + if (ra->cb) { + for (idx = 0; idx < ra->len; idx++) { + ra->cb((unsigned char *)(ra->storage) + idx * ra->elsize, + REF_ARRAY_DESTROY, ra->cb_data); + } + } + free(ra->storage); + free(ra); + } + } + else { + /* Should never be here... + * This can happen if the caller by mistake would try to + * destroy the object from within the callback. Brrr.... + */ + TRACE_ERROR_STRING("Reference count is 0.", "Coding error???"); + } + + TRACE_FLOW_STRING("ref_array_destroy", "Exit"); +} + +/* Add new element to the array */ +int ref_array_append(struct ref_array *ra, void *element) +{ + int error = EOK; + + TRACE_FLOW_STRING("ref_array_append", "Entry"); + if ((!ra) || (!element)) { + TRACE_ERROR_NUMBER("Uninitialized argument.", EINVAL); + return EINVAL; + } + + /* Do we have enough room for a new element? */ + if (ra->size == ra->len) { + error = ref_array_grow(ra); + if (error) { + TRACE_ERROR_NUMBER("Failed to grow array.", error); + return EINVAL; + } + } + + /* Copy element */ + memcpy((unsigned char *)(ra->storage) + ra->len * ra->elsize, + element, + ra->elsize); + + ra->len++; + + TRACE_FLOW_STRING("ref_array_append", "Exit"); + return error; +} + +/* Get element */ +void *ref_array_get(struct ref_array *ra, uint32_t idx, void *acptr) +{ + TRACE_FLOW_STRING("ref_array_get", "Entry"); + + if (!ra) { + TRACE_ERROR_STRING("Uninitialized argument.", ""); + return NULL; + } + + if (idx >= ra->len) { + TRACE_ERROR_NUMBER("Invalid idx.", idx); + return NULL; + } + + TRACE_INFO_NUMBER("Index: ", idx); + + if (acptr) { + + TRACE_INFO_STRING("Copying data.", ""); + memcpy(acptr, + (unsigned char *)(ra->storage) + idx * ra->elsize, + ra->elsize); + + } + + TRACE_FLOW_STRING("ref_array_get returning internal storage", "Exit"); + return (unsigned char *)(ra->storage) + idx * ra->elsize; +} + + +/* Get length */ +int ref_array_getlen(struct ref_array *ra, uint32_t *len) +{ + TRACE_FLOW_STRING("ref_array_getlen", "Entry"); + + if ((!ra) || (!len)) { + TRACE_ERROR_STRING("Uninitialized argument.", ""); + return EINVAL; + } + + *len = ra->len; + + TRACE_FLOW_STRING("ref_array_getlen", "Exit"); + return EOK; +} + +/* Alternative function to get length */ +uint32_t ref_array_len(struct ref_array *ra) +{ + TRACE_FLOW_STRING("ref_array_len", "Entry"); + + if (!ra) { + TRACE_ERROR_STRING("Uninitialized argument.", ""); + errno = EINVAL; + return 0; + } + + TRACE_FLOW_STRING("ref_array_len", "Exit"); + return ra->len; +} + + +/* Debug function */ +void ref_array_debug(struct ref_array *ra) +{ + int i,j; + + printf("\nARRAY DUMP START\n"); + printf("Length = %u\n", ra->len); + printf("Size = %u\n", ra->size); + printf("Element = %u\n", (unsigned int)(ra->elsize)); + printf("Grow by = %u\n", ra->grow_by); + printf("Count = %u\n", ra->refcount); + printf("ARRAY:\n"); + for (i = 0; i < ra->len; i++) { + for (j = 0; j < ra->elsize; j++) { + printf("%x", *((unsigned char *)(ra->storage) + i * ra->elsize + j)); + } + printf("\n%s\n", *((char **)((unsigned char *)(ra->storage) + i * ra->elsize))); + } + printf("\nARRAY DUMP END\n\n"); +} |