agrow

https://bitbucket.org/iwcrhsimmcotf/agrow

This is a macros based library in pure C that implements resizable arrays. It’s similar to C++ std::vector in that it uses contiguous, automatically growing memory to store the elements.

Just like std::vector it allocates more memory than actually needed to handle future growth.

To use it in your project, copy agrow.h to the include folder.

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#include "agrow.h"

AGROW_DECL(myarray, int)  /* expands to prototypes */

AGROW_DEF(myarray, int)  /* expands to definitions */

int
main(void)
{
    printf(" * simple: ");

    /* reserve space for at least 3 elements */
    myarray *a = myarray_new(3);

    /* append 5 elements */
    int *p = myarray_append(a, NULL, NULL);
    *p = 6;
    p = myarray_append(a, NULL, NULL);
    *p = 7;
    p = myarray_append(a, NULL, NULL);
    *p = 8;
    p = myarray_append(a, NULL, NULL);
    *p = 9;
    p = myarray_append(a, NULL, NULL);
    *p = 10;
    assert(myarray_size(a) == 5);

    /* iteration */
    p = myarray_data(a);
    int sum = 0;
    for (size_t i = 0, s = myarray_size(a); i < s; i++) {
        sum += p[i];
    }
    assert(sum == 6 + 7 + 8 + 9 + 10);
    /* (there is also myarray_each() that accepts a callback) */

    myarray_destroy(&a, NULL, NULL);  /* free memory */

    printf("OK\n");
    return 0;
}

Macros

AGROW_DECL()

Creates forward declarations for struct and methods that comprise a new dynamic array.

AGROW_DECL(Arr, T)

Parameters

  • Arr: New array type and also a prefix for methods
  • T: Type of the elements

To generate complete definitions of struct and methods, you should also call AGROW_DEF().

AGROW_DEF()

Expands to complete definitions of struct and methods previously declared with AGROW_DECL().

AGROW_DEF(Arr, T)

Parameters

Same as for AGROW_DECL()

Callbacks

Some api methods require a callback function as one of their parameters. The api method then may invoke that function on individual array elements. All such callbacks share the same signature.

int your_callback(size_t pos, T *ptr, void *data) {/*...*/}

Parameters

  • pos: Index of the element
  • ptr: Pointer to the element
  • data: Extra argument

Return value

The meaning of the return value varies for different methods.

Api

{?}_new()

Creates an empty array allocated on the heap.

myarray *myarray_new(size_t atleast)

Parameters

  • atleast: How many elements should be reserved

The actual amount of reserved elements (capacity) may be equal or greater than the value of atleast.

Return value

Pointer to created array or NULL on failure.

Example

myarray *a = myarray_new(AT_LEAST_ELEMS);
if (a == NULL) {
    printf("couldn't create an array\n");
    return -1;
}

/* ... */

myarray_destroy(&a, onfree, NULL);

Notes

Instances created with this method should be destroyed with destroy() method.

{?}_destroy()

Destroys an instance of an array created with new().

void myarray_destroy(myarray **selfptr,
                    myarray_callback onfree,
                    void *data)

Params:

  • selfptr: Pointer to pointer to array
  • onfree: Callback function that is called on every element
  • data: Extra argument passed to every invokation of onfree()

If selfptr points to NULL, this methods does nothing.

The purpose of onfree() is to finalize array elements. If no such finalization is needed, you may pass NULL instead of onfree.

The value returned from onfree() is ignored.

{?}_get_ptr()

Returns a pointer to the element with index pos or NULL if position is out of range.

elem *myarray_get_ptr(myarray *self, size_t pos)

Parameters

  • self: Pointer to array
  • pos: Element’s index

Return value

Element’s pointer on success or NULL on failure.

Notes

To get the beginning of elements space, you can also use data().

{?}_data()

Returns a pointer to the beginning of allocated space.

elem *myarray_data(myarray *self)

It’s equivalent to myarray_get_ptr(a, 0) except it succeeds even if size == 0.

Parameters

  • self: Pointer to array

{?}_each()

Iterates over the elements.

int myarray_each(myarray *self,
                myarray_callback oneach,
                void *data)

Parameters

  • self: Pointer to array
  • oneach: Callback function that is called on every element
  • data: Extra argument passed to every invokation of oneach()

The return value of oneach() callback

  • 0: success. Method each() should proceed iterating
  • a negative value: failure. Method each() should stop iterating and return failure
  • a positive value: success. Method each() should stop iterating and return success (early exit).

Return value

0 on success and -1 on failure.

{?}_size()

Returns the current number of elements.

size_t myarray_size(myarray *self)

Parameters

  • self: Pointer to array

Example

myarray *a = myarray_new(1000);
printf("%zu\n", myarray_size(a));           /* 0 */
myarray_append(a, /*...*/);
myarray_append(a, /*...*/);
myarray_append(a, /*...*/);
printf("%zu\n", myarray_size(a));           /* 3 */

{?}_capacity()

Returns the amount of elements that can fit into currently allocated memory.

size_t myarray_capacity(myarray *self)

Parameters

  • self: Pointer to array

Example

myarray *a = myarray_new(1000);
printf("%s\n", myarray_capacity(a) >= 1000? "true" : "false");  /* true */

{?}_append()

Appends a new element to the end of array.

elem *myarray_append(myarray *self,
                    myarray_callback onappend,
                    void *data)

If capacity is exceeded, it tries to allocate more memory.

Parameters

  • self: Pointer to array
  • onappend: Callback function that is called on the appended element (if append was successful)
  • data: Extra argument passed to the invokation of onappend()

The purpose of onappend is to initialize the new element. If no such initialization is needed, you may pass NULL instead of onappend. The return value of onappend()

  • 0: append() should succeed
  • a negative value: append() should fail

Return value

A valid pointer to the new element on success, NULL on failure.

Example

/* if elem is integer type */

int
onap(size_t idx, elem *p, void *data)
{
    *p = 77;
    return 0;
}

/* ... */

elem *p = myarray_append(a, onap, NULL);
printf("%d\n", *p);                 /* 77 */

{?}_shy_append()

Appends a new element to the end of array (fail if capacity is exceeded).

elem *myarray_shy_append(myarray *self,
                        myarray_callback onappend,
                        void *data,
                        int *isfull)

Unlike append() it never tries to allocate more memory.

Parameters

  • (self, onappend and data are the same as for append())
  • isfull: Pointer to a flag signalling that shy_append() failed due to exceeded capacity.

If isfull points to NULL, it is ignored.

If shy_append() fails due to capacity limit is reached, the flag pointed by isfull is set to a true value.

If shy_append() fails due to some other error, the flag pointed by isfull is set to a false value.

Return value

A valid pointer to the new element on success, NULL on failure.

{?}_resize()

Sets the new size of the array.

size_t myarray_resize(myarray *self,
                    size_t size,
                    myarray_callback onadd,
                    myarray_callback ondel,
                    void *data)

Just like append() it will try to allocate more memory if the new size is greater than capacity.

Parameters

  • self: Pointer to array
  • size: The new size
  • onadd: Callback function that is called on every appended element (if the new size is greater than the current one)
  • ondel: Callback function that is called on every discarded element (if the new size is less than the current one)
  • data: Extra argument passed to the invokation of whichever used callback

The purpose of onadd function is to initialize new elements. The purpose of ondel function is to finalize discarded elements. If no such initialization or finalization is needed, you may pass NULL instead of the corresponding callback. The return value of onadd() and ondel()

  • 0: the method should proceed resizing (adding or deleting elements)
  • a negative value: the method should stop and return failure

Return value

The new size of array. On errors it might be different from what was requested.

Notes

This method never frees any allocated memory even when discarding elements.

Example

myarray *a = myarray_new(10);
printf("%zu\n", myarray_size(a));           /* 0 */
myarray_resize(a, 1000, NULL, NULL, NULL);
printf("%zu\n", myarray_size(a));           /* 1000 */

{?}_vacuum()

Removes certain elements from array.

size_t myarray_vacuum(myarray *self,
                    myarray_callback isgarbage,
                    myarray_callback onmove,
                    void *data)

If removed elements create gaps, the kept elements are shifted down to close these gaps.

Parameters

  • self: Pointer to array
  • isgarbage: Callback function that is called on every element
  • onmove: Callback function that is called on every moved element
  • data: Extra argument passed to every invokation of isgarbage() and onmove()

The purpose of isgarbage() function is to tell whether the element should be kept or removed. The return value of isgarbage()

  • 0: the element should be kept
  • 1: the element should be removed and the vacuum() method should continue applying isgarbage() to other elements
  • 2: the element should be removed and the vacuum() method should stop applying isgarbage() to other elements keeping them in the array

Also isgarbage() callback should finalize the element if it decides to return 1 or 2.

The purpose of onmove() function is to perform additional logic when an element is shifted to close gaps. It is called after the element is copied to the new location. The return value of onmove() is ignored.

Return value

The new size.

Notes

This method never frees any allocated memory even when elements get removed.

{?}_reserve()

Set new minimal amount of preallocated elements.

size_t myarray_reserve(myarray *self, size_t atleast)

Parameters

  • self: Pointer to array
  • atleast: Minimal amount of elements to reserve

Return value

New capacity (which might be bigger than requested value atleast).

Notes

If atleast param is less than the current array size, the current size is used as the minimal amount of reserved elements and atleast is ignored.

If atleast is greater than current capacity, this method may allocate more memory. If atleast is less than current capacity, this method may free some memory.