You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

370 lines
9.7 KiB

/* -*- coding: utf-8 -*-
* ----------------------------------------------------------------------
* Copyright © 2011-2013, RedJack, LLC.
* All rights reserved.
*
* Please see the COPYING file in this distribution for license details.
* ----------------------------------------------------------------------
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#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 <stdio.h>
#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);
}