On 06/26/2013 03:13 PM, Tanu Kaskinen wrote: > A dynamic array is a nice simple container, but the old interface > wasn't quite what I wanted it to be. I like GLib's way of providing > the free callback at the container creation time, because that way > the free callback doesn't have to be given every time something is > removed from the array. > > The allocation pattern was changed too: instead of increasing the > array size always by 25 when the array gets full, the size gets > doubled now. Instead of starting with zero allocated size, the initial > allocated size is now 25. > > The array can't store NULL pointers anymore, and pa_dynarray_get() was > changed so that it's forbidden to try to access elements outside the > valid range. > > The set of supported operations may seem a bit arbitrary. The > operation set is by no means complete at this point. I have included > only those operations that are required by the current code and some > unpublished code of mine. > --- > src/pulsecore/cli-command.c | 4 +-- > src/pulsecore/dynarray.c | 85 +++++++++++++++++++-------------------------- > src/pulsecore/dynarray.h | 49 +++++++++++++++----------- > src/pulsecore/tokenizer.c | 8 +++-- > 4 files changed, 72 insertions(+), 74 deletions(-) > > diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c > index 866cd16..6644d64 100644 > --- a/src/pulsecore/cli-command.c > +++ b/src/pulsecore/cli-command.c > @@ -1983,7 +1983,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b > char **sorted_files; > struct dirent *de; > pa_bool_t failed = FALSE; > - pa_dynarray *files = pa_dynarray_new(); > + pa_dynarray *files = pa_dynarray_new(NULL); > > while ((de = readdir(d))) { > char *extn; > @@ -2003,7 +2003,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b > sorted_files = pa_xnew(char*, count); > for (i = 0; i < count; ++i) > sorted_files[i] = pa_dynarray_get(files, i); > - pa_dynarray_free(files, NULL); > + pa_dynarray_free(files); > > for (i = 0; i < count; ++i) { > for (unsigned j = 0; j < count; ++j) { > diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c > index 78b2eb9..008f8d3 100644 > --- a/src/pulsecore/dynarray.c > +++ b/src/pulsecore/dynarray.c > @@ -31,82 +31,69 @@ > > #include "dynarray.h" > > -/* If the array becomes to small, increase its size by 25 entries */ > -#define INCREASE_BY 25 > +#define MIN_N_ALLOCATED 25U > > struct pa_dynarray { > void **data; > unsigned n_allocated, n_entries; > + pa_free_cb_t free_cb; > }; > > -pa_dynarray* pa_dynarray_new(void) { > - pa_dynarray *a; > +pa_dynarray* pa_dynarray_new(pa_free_cb_t free_cb) { > + pa_dynarray *array; > > - a = pa_xnew(pa_dynarray, 1); > - a->data = NULL; > - a->n_entries = 0; > - a->n_allocated = 0; > + array = pa_xnew0(pa_dynarray, 1); > + array->data = pa_xnew(void *, MIN_N_ALLOCATED); > + array->n_allocated = MIN_N_ALLOCATED; With the MAX change below, we should probably default to an empty array like it was done previously - it could save some memory if we have many empty arrays. If you agree, feel free to push with that change. > + array->free_cb = free_cb; > > - return a; > + return array; > } > > -void pa_dynarray_free(pa_dynarray *a, pa_free_cb_t free_func) { > +void pa_dynarray_free(pa_dynarray *array) { > unsigned i; > - pa_assert(a); > + pa_assert(array); > > - if (free_func) > - for (i = 0; i < a->n_entries; i++) > - if (a->data[i]) > - free_func(a->data[i]); > + if (array->free_cb) > + for (i = 0; i < array->n_entries; i++) > + array->free_cb(array->data[i]); > > - pa_xfree(a->data); > - pa_xfree(a); > + pa_xfree(array->data); > + pa_xfree(array); > } > > -void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) { > - pa_assert(a); > +void pa_dynarray_append(pa_dynarray *array, void *p) { > + pa_assert(array); > + pa_assert(p); > > - if (i >= a->n_allocated) { > - unsigned n; > + if (array->n_entries == array->n_allocated) { > + unsigned n = PA_MAX(array->n_allocated * 2, MIN_N_ALLOCATED); > > - if (!p) > - return; > - > - n = i+INCREASE_BY; > - a->data = pa_xrealloc(a->data, sizeof(void*)*n); > - memset(a->data+a->n_allocated, 0, sizeof(void*)*(n-a->n_allocated)); > - a->n_allocated = n; > + array->data = pa_xrealloc(array->data, sizeof(void *) * n); > + array->n_allocated = n; > } > > - a->data[i] = p; > - > - if (i >= a->n_entries) > - a->n_entries = i+1; > + array->data[array->n_entries++] = p; > } > > -unsigned pa_dynarray_append(pa_dynarray*a, void *p) { > - unsigned i; > - > - pa_assert(a); > - > - i = a->n_entries; > - pa_dynarray_put(a, i, p); > +void *pa_dynarray_get(pa_dynarray *array, unsigned i) { > + pa_assert(array); > + pa_assert(i < array->n_entries); > > - return i; > + return array->data[i]; > } > > -void *pa_dynarray_get(pa_dynarray*a, unsigned i) { > - pa_assert(a); > +void *pa_dynarray_steal_last(pa_dynarray *array) { > + pa_assert(array); > > - if (i >= a->n_entries) > + if (array->n_entries > 0) > + return array->data[--array->n_entries]; > + else > return NULL; > - > - pa_assert(a->data); > - return a->data[i]; > } > > -unsigned pa_dynarray_size(pa_dynarray*a) { > - pa_assert(a); > +unsigned pa_dynarray_size(pa_dynarray *array) { > + pa_assert(array); > > - return a->n_entries; > + return array->n_entries; > } > diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h > index 70deebb..04dd2d2 100644 > --- a/src/pulsecore/dynarray.h > +++ b/src/pulsecore/dynarray.h > @@ -26,26 +26,33 @@ > > typedef struct pa_dynarray pa_dynarray; > > -/* Implementation of a simple dynamically sized array. The array > - * expands if required, but doesn't shrink if possible. Memory > - * management of the array's entries is the user's job. */ > - > -pa_dynarray* pa_dynarray_new(void); > - > -/* Free the array calling the specified function for every entry in > - * the array. The function may be NULL. */ > -void pa_dynarray_free(pa_dynarray *a, pa_free_cb_t free_func); > - > -/* Store p at position i in the array */ > -void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p); > - > -/* Store p a the first free position in the array. Returns the index > - * of that entry. If entries are removed from the array their position > - * are not filled any more by this function. */ > -unsigned pa_dynarray_append(pa_dynarray*a, void *p); > - > -void *pa_dynarray_get(pa_dynarray*a, unsigned i); > - > -unsigned pa_dynarray_size(pa_dynarray*a); > +/* Implementation of a simple dynamically sized array for storing pointers. > + * > + * When the array is created, a free callback can be provided, which will be > + * then used when removing items from the array and when freeing the array. If > + * the free callback is not provided, the memory management of the stored items > + * is the responsibility of the array user. If there is need to remove items > + * from the array without freeing them, while also having the free callback > + * set, the functions with "steal" in their name can be used. > + * > + * Removing items from the middle of the array causes the subsequent items to > + * be moved to fill the gap, so it's not efficient with large arrays. If the > + * order of the array is not important, however, functions with "fast" in their > + * name can be used, in which case the gap is filled by moving only the last > + * item(s). XXX: Currently there are no functions with "fast" in their name, > + * but such functions will be added if they are ever needed. > + * > + * The array doesn't support storing NULL pointers. */ > + > +pa_dynarray* pa_dynarray_new(pa_free_cb_t free_cb); > +void pa_dynarray_free(pa_dynarray *array); > + > +void pa_dynarray_append(pa_dynarray *array, void *p); > +void *pa_dynarray_get(pa_dynarray *array, unsigned i); > + > +/* Returns the removed item, or NULL if the array is empty. */ > +void *pa_dynarray_steal_last(pa_dynarray *array); > + > +unsigned pa_dynarray_size(pa_dynarray *array); > > #endif > diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c > index cb682d6..4c610e8 100644 > --- a/src/pulsecore/tokenizer.c > +++ b/src/pulsecore/tokenizer.c > @@ -63,7 +63,7 @@ static void parse(pa_dynarray*a, const char *s, unsigned args) { > pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) { > pa_dynarray *a; > > - a = pa_dynarray_new(); > + a = pa_dynarray_new(pa_xfree); > parse(a, s, args); > return (pa_tokenizer*) a; > } > @@ -72,12 +72,16 @@ void pa_tokenizer_free(pa_tokenizer *t) { > pa_dynarray *a = (pa_dynarray*) t; > > pa_assert(a); > - pa_dynarray_free(a, pa_xfree); > + pa_dynarray_free(a); > } > > const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i) { > pa_dynarray *a = (pa_dynarray*) t; > > pa_assert(a); > + > + if (i >= pa_dynarray_size(a)) > + return NULL; > + > return pa_dynarray_get(a, i); > } > -- David Henningsson, Canonical Ltd. https://launchpad.net/~diwic