summaryrefslogtreecommitdiffstats
path: root/common/refarray/ref_array.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/refarray/ref_array.c')
-rw-r--r--common/refarray/ref_array.c294
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");
+}