/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011-2013, RedJack, LLC. * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include "libcork/core/types.h" #include "libcork/ds/array.h" #include "libcork/helpers/errors.h" #ifndef CORK_ARRAY_DEBUG #define CORK_ARRAY_DEBUG 0 #endif #if CORK_ARRAY_DEBUG #include #define DEBUG(...) \ do { \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n"); \ } while (0) #else #define DEBUG(...) /* nothing */ #endif /*----------------------------------------------------------------------- * Resizable arrays */ struct cork_array_priv { size_t allocated_count; size_t allocated_size; size_t element_size; size_t initialized_count; void *user_data; cork_free_f free_user_data; cork_init_f init; cork_done_f done; cork_init_f reuse; cork_done_f remove; }; void cork_raw_array_init(struct cork_raw_array *array, size_t element_size) { array->items = NULL; array->size = 0; array->priv = cork_new(struct cork_array_priv); array->priv->allocated_count = 0; array->priv->allocated_size = 0; array->priv->element_size = element_size; array->priv->initialized_count = 0; array->priv->user_data = NULL; array->priv->free_user_data = NULL; array->priv->init = NULL; array->priv->done = NULL; array->priv->reuse = NULL; array->priv->remove = NULL; } void cork_raw_array_done(struct cork_raw_array *array) { if (array->priv->done != NULL) { size_t i; char *element = array->items; for (i = 0; i < array->priv->initialized_count; i++) { array->priv->done(array->priv->user_data, element); element += array->priv->element_size; } } if (array->items != NULL) { free(array->items); } cork_free_user_data(array->priv); free(array->priv); } void cork_raw_array_set_callback_data(struct cork_raw_array *array, void *user_data, cork_free_f free_user_data) { array->priv->user_data = user_data; array->priv->free_user_data = free_user_data; } void cork_raw_array_set_init(struct cork_raw_array *array, cork_init_f init) { array->priv->init = init; } void cork_raw_array_set_done(struct cork_raw_array *array, cork_done_f done) { array->priv->done = done; } void cork_raw_array_set_reuse(struct cork_raw_array *array, cork_init_f reuse) { array->priv->reuse = reuse; } void cork_raw_array_set_remove(struct cork_raw_array *array, cork_done_f remove) { array->priv->remove = remove; } size_t cork_raw_array_element_size(const struct cork_raw_array *array) { return array->priv->element_size; } void cork_raw_array_clear(struct cork_raw_array *array) { if (array->priv->remove != NULL) { size_t i; char *element = array->items; for (i = 0; i < array->priv->initialized_count; i++) { array->priv->remove(array->priv->user_data, element); element += array->priv->element_size; } } array->size = 0; } void * cork_raw_array_elements(const struct cork_raw_array *array) { return array->items; } void * cork_raw_array_at(const struct cork_raw_array *array, size_t index) { return ((char *) array->items) + (array->priv->element_size * index); } size_t cork_raw_array_size(const struct cork_raw_array *array) { return array->size; } bool cork_raw_array_is_empty(const struct cork_raw_array *array) { return (array->size == 0); } void cork_raw_array_ensure_size(struct cork_raw_array *array, size_t desired_count) { size_t desired_size; DEBUG("--- Array %p: Ensure %zu %zu-byte elements", array, desired_count, array->priv->element_size); desired_size = desired_count * array->priv->element_size; if (desired_size > array->priv->allocated_size) { size_t new_count = array->priv->allocated_count * 2; size_t new_size = array->priv->allocated_size * 2; if (desired_size > new_size) { new_count = desired_count; new_size = desired_size; } DEBUG("--- Array %p: Reallocating %zu->%zu bytes", array, array->priv->allocated_size, new_size); array->items = cork_realloc(array->items, new_size); array->priv->allocated_count = new_count; array->priv->allocated_size = new_size; } } void * cork_raw_array_append(struct cork_raw_array *array) { size_t index; void *element; index = array->size++; cork_raw_array_ensure_size(array, array->size); element = cork_raw_array_at(array, index); /* Call the init or reset callback, depending on whether this entry has been * initialized before. */ /* Since we can currently only add elements by appending them one at a time, * then this entry is either already initialized, or is the first * uninitialized entry. */ assert(index <= array->priv->initialized_count); if (index == array->priv->initialized_count) { /* This element has not been initialized yet. */ array->priv->initialized_count++; if (array->priv->init != NULL) { array->priv->init(array->priv->user_data, element); } } else { /* This element has already been initialized. */ if (array->priv->reuse != NULL) { array->priv->reuse(array->priv->user_data, element); } } return element; } int cork_raw_array_copy(struct cork_raw_array *dest, const struct cork_raw_array *src, cork_copy_f copy, void *user_data) { size_t i; size_t reuse_count; char *dest_element; DEBUG("--- Copying %zu elements (%zu bytes) from %p to %p", src->size, src->size * dest->priv->element_size, src, dest); assert(dest->priv->element_size == src->priv->element_size); cork_array_clear(dest); cork_array_ensure_size(dest, src->size); /* Initialize enough elements to hold the contents of src */ reuse_count = dest->priv->initialized_count; if (src->size < reuse_count) { reuse_count = src->size; } dest_element = dest->items; if (dest->priv->reuse != NULL) { DEBUG(" Calling reuse on elements 0-%zu", reuse_count); for (i = 0; i < reuse_count; i++) { dest->priv->reuse(dest->priv->user_data, dest_element); dest_element += dest->priv->element_size; } } else { dest_element += reuse_count * dest->priv->element_size; } if (dest->priv->init != NULL) { DEBUG(" Calling init on elements %zu-%zu", reuse_count, src->size); for (i = reuse_count; i < src->size; i++) { dest->priv->init(dest->priv->user_data, dest_element); dest_element += dest->priv->element_size; } } if (src->size > dest->priv->initialized_count) { dest->priv->initialized_count = src->size; } /* If the caller provided a copy function, let it copy each element in turn. * Otherwise, bulk copy everything using memcpy. */ if (copy == NULL) { memcpy(dest->items, src->items, src->size * dest->priv->element_size); } else { const char *src_element = src->items; dest_element = dest->items; for (i = 0; i < src->size; i++) { rii_check(copy(user_data, dest_element, src_element)); dest_element += dest->priv->element_size; src_element += dest->priv->element_size; } } dest->size = src->size; return 0; } /*----------------------------------------------------------------------- * Pointer arrays */ struct cork_pointer_array { cork_free_f free; }; static void pointer__init(void *user_data, void *vvalue) { void **value = vvalue; *value = NULL; } static void pointer__done(void *user_data, void *vvalue) { struct cork_pointer_array *ptr_array = user_data; void **value = vvalue; if (*value != NULL) { ptr_array->free(*value); } } static void pointer__remove(void *user_data, void *vvalue) { struct cork_pointer_array *ptr_array = user_data; void **value = vvalue; if (*value != NULL) { ptr_array->free(*value); } *value = NULL; } void cork_raw_pointer_array_init(struct cork_raw_array *array, cork_free_f free_ptr) { struct cork_pointer_array *ptr_array = cork_new(struct cork_pointer_array); ptr_array->free = free_ptr; cork_raw_array_init(array, sizeof(void *)); cork_array_set_callback_data(array, ptr_array, free); cork_array_set_init(array, pointer__init); cork_array_set_done(array, pointer__done); cork_array_set_remove(array, pointer__remove); } /*----------------------------------------------------------------------- * String arrays */ void cork_string_array_init(struct cork_string_array *array) { cork_raw_pointer_array_init ((struct cork_raw_array *) array, (cork_free_f) cork_strfree); } void cork_string_array_append(struct cork_string_array *array, const char *str) { const char *copy = cork_strdup(str); cork_array_append(array, copy); } static int string__copy(void *user_data, void *vdest, const void *vsrc) { const char **dest = vdest; const char **src = (const char **) vsrc; *dest = cork_strdup(*src); return 0; } void cork_string_array_copy(struct cork_string_array *dest, const struct cork_string_array *src) { CORK_ATTR_UNUSED int rc; rc = cork_array_copy(dest, src, string__copy, NULL); /* cork_array_copy can only fail if the copy callback fails, and ours * doesn't! */ assert(rc == 0); }