D. J. Bernstein

The array library interface

An allocated array variable keeps track of There are two other possibilities for the state of an array variable: unallocated and failed. In both cases, there is no dynamically allocated region of memory.

A new array variable is normally created as a static variable:

     #include "array.h"

     static array x;
At this point it is unallocated. The array library provides various allocation and inspection functions.

A new array variable can also be created dynamically. It must be initialized to all-0, meaning unallocated, before it is given to any of the array functions. It must be returned to the unallocated (or failed) state, for example with array_reset, before it is destroyed. These rules prevent all memory leaks.


Expansion and inspection

     #include "array.h"

     static array x;
     t *p;
     int64 pos;

     p = array_allocate(&x,sizeof(t),pos);
array_allocate makes sure that enough bytes are allocated in x for at least pos+1 objects of type t. (The size of t must be positive; otherwise the effects are undefined.) If not enough bytes are allocated (or x is unallocated), array_allocate allocates more bytes, moving the dynamically allocated region if necessary. array_allocate often allocates somewhat more bytes than necessary, to save time later.

array_allocate then makes sure that the number of bytes initialized covers at least those pos+1 objects. If not enough bytes are initialized, array_allocate initializes more bytes (setting them to 0), up to exactly the end of the pos+1st object.

array_allocate then returns a pointer to the pos+1st object; i.e., object number pos, with objects numbered starting at 0. This pointer can be used to change or inspect the object. The pointer can continue to be used through subsequent calls to array_get, array_start, array_length, and array_bytes, but it must not be used after any other operations on this array.

If something goes wrong, array_allocate returns 0, setting errno appropriately, without touching x. In particular, array_allocate returns 0 if

array_allocate does not change x to have failed; if you want to do that, use array_fail.
     #include "array.h"

     static array x;
     t *p;
     int64 pos;

     p = array_get(&x,sizeof(t),pos);
     p = array_start(&x);
array_get is similar to array_allocate, but it does not allocate any extra bytes, and it does not initialize any extra bytes. It returns 0 if x is unallocated, for example, or if fewer than (pos+1)*sizeof(t) bytes are initialized.

array_start is the same as array_get with pos equal to 0.


     #include "array.h"

     static array x;
     int64 len;

     len = array_length(&x,sizeof(t));
     len = array_bytes(&x);
array_length returns the number of initialized bytes in x, divided by the size of t. In other words, array_get will succeed for positions 0 through array_length-1; it will fail for position array_length.

array_bytes returns the number of initialized bytes in x, without regard to t.

If x is unallocated, array_length and array_bytes return 0.

If x has failed, array_length and array_bytes return -1.


Truncation and deallocation

     #include "array.h"

     static array x;
     int64 len;

     array_truncate(&x,sizeof(t),len);
     array_trunc(&x);
array_truncate reduces the number of initialized bytes in x to exactly len*sizeof(t). If the number of initialized bytes was already this small (or smaller), array_truncate has no effect. If len is negative, array_truncate has no effect. If x is unallocated, array_truncate has no effect. If x has failed, array_truncate has no effect.

array_truncate does not change the allocation in x. If you want to free the memory used by x, use array_reset.

array_trunc is the same as array_truncate with len equal to 0.


     #include "array.h"

     static array x;

     array_reset(&x);
If x is allocated, array_reset frees the region that x points to, and switches x to being unallocated.

If x has failed, array_reset simply switches x to being unallocated.

If x is unallocated, array_reset has no effect.


     #include "array.h"

     static array x;

     array_fail(&x);
If x is allocated, array_fail frees the region that x points to, and switches x to have failed.

If x is unallocated, array_fail simply switches x to have failed.

If x has already failed, array_fail has no effect.


Comparison

     #include "array.h"

     static array x;
     static array y;

     array_equal(&x,&y);
array_equal returns nonzero if x and y have the same contents: i.e., Otherwise it returns 0.

Concatenation

     #include "array.h"

     static array x;
     static array y;

     array_cat(&x,&y);
array_cat appends y to x; i.e., it changes x, allocating more space if necessary, so that the initialized bytes in x are the previously initialized bytes in x followed by a copy of the initialized bytes in y.

If x has failed, array_cat has no effect.

If y has failed, array_cat switches x to have failed.

If not enough memory is available, array_cat switches x to have failed.


     #include "array.h"

     static array x;
     const char *y;
     int64 len;

     array_catb(&x,y,len);
array_catb appends the bytes y[0], y[1], ..., y[len-1] to the array x. It handles failure in the same way as array_cat.
     #include "array.h"

     static array x;
     const char *y;

     array_cats(&x,y);
     array_cats0(&x,y);
     array_cat0(&x);
array_cats appends the contents of the 0-terminated string y, not including the terminating 0 byte, to the array x.

array_cats0 appends the contents of y, including the terminating 0 byte, to x.

array_cat0 appends a 0 byte to x.

These functions handle failure in the same way as array_cat.


     #include "array.h"

     static array x;
     static array y;
     int64 pos;
     int64 stop;

     array_cate(&x,&y,pos,stop);
array_cate is like array_cat, but uses only byte positions pos through stop-1 in y. It fails if pos is negative, or if stop is smaller than pos, or if the number of initialized bytes in y is smaller than stop.