home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  

Writing Apache Modules with Perl and C
By:   Lincoln Stein and Doug MacEachern
Published:   O'Reilly & Associates, Inc.  - March 1999

Copyright 1999 by O'Reilly & Associates, Inc.


   Show Contents   Previous Page   Next Page

Chapter 10 - C API Reference Guide, Part I
The Array API

In this section...

The array_header Type
Creating and Manipulating Arrays
Accessing Array Elements


   Show Contents   Go to Top   Previous Page   Next Page

The HTTP protocol is filled with lists: lists of language codes, HTTP header fields, MIME types, and so forth. In general, it's not possible to predict the size of the lists in advance, and some of them can be quite large. In order to deal with this, Apache provides an array API that allows you to create lists of arbitrary type and length, much like the dynamic arrays provided by Perl. More complex data structures, such as the table type (described later in this chapter), are built on top of Apache arrays.

The array_header Type

   Show Contents   Go to Top   Previous Page   Next Page

The core of the array API is the array_header structure, whose definition you can find in include/alloc.h:

typedef struct {
    ap_pool *pool;
   int elt_size;
   int nelts;
   int nalloc;
   char *elts;
} array_header;

The fields you'll use are the nelts field, which holds the current number of elements in the array, and the elts field, which contains a pointer to the data in the array. elts is declared as a char* for the convenience of internal routines. You will need to cast it to a pointer of the correct type before you can access elements safely. You should not need to worry about the other fields, but note that the array_header contains a pointer back to the resource pool from which it was allocated. This means that both the array header and its contents will be freed automatically when the pool reaches the end of its lifetime.

Creating and Manipulating Arrays

   Show Contents   Go to Top   Previous Page   Next Page

Here are the API calls for working with arrays. Although array elements can be of any arbitrary type, we use char* strings in our examples for the sake of simplicity.

array_header *ap_make_array (pool* p, int nelts, int elt_size)

This function allocates a new array from resource pool p with elements elt_size bytes in length and enough initial space to hold nelts elements. If successful, the call will return a pointer to a new array_header.

If the array needs to grow beyond its initial length, the array API will resize it automatically. This means that you can safely allocate an array with zero elements.

Here is an example of creating a new array with an initial length of 5 elements. Each element is large enough to hold a char* string pointer:

array_header *arr = ap_make_array(p, 5, sizeof(char*));

void *ap_push_array (array_header *arr)

The ap_push_array() function is used to add a new element to the end of the list. It works a little differently than you would expect. Instead of allocating the new element first and then pushing it onto the end of the list, you call ap_push_array() to allocate the element for you. It will create a new element of the correct size within the array's pool and return a pointer to the new element. You cast the returned void* to the data type of your choice and copy your data in.

Here's an example in which we add a new element containing the string text/html to the end of the array. The call to ap_push_array() allocates room for a new char* pointer, while the subsequent call to ap_pstrdup() allocates room for the string itself and copies its address into the element. This example assumes that the array was created with ap_ make_array() using an elt_size determined by sizeof(char *).

char **new;
new = (char **)ap_push_array(arr);
*new = ap_pstrdup(r->pool, "text/html");
/* or in a single line */
*(char **)ap_push_array(arr) = ap_pstrdup(r->pool, "text/html");

void ap_array_cat (array_header *dst, const array_header *src)

If you wish to add the elements of one array to another, the ap_array_cat() function will handle the job. It takes two array_header pointers as arguments and concatenates the second onto the end of the first. The first will grow as necessary. Note that the elt_size used to create each array should be the same; otherwise, unpredictable results may occur.

ap_array_cat(arr, other_arr);

array_header *ap_append_arrays (pool *p, const array_header *a1, const array_header *a2)

This function is similar to ap_array_cat() but creates a brand new array in the indicated pool. The resulting array will be the concatenation of a1 and a2.

array_header *new = ap_append_arrays(p, one_array, another_array);

char *ap_array_pstrcat (pool *p, const array_header *arr, const char sep)

This function builds a new string using the elements of the given array. If sep is non- NULL, it will be inserted as a delimiter between the substrings.

char *table_cells = ap_array_pstrcat(r->pool, cells, ' ');

array_header *ap_copy_array (pool *p, const array_header *src)

The ap_copy_array() function creates a new array in the indicated pool, then copies into it all the elements in src.

array_header *new = ap_copy_array(p, arr);

array_header *ap_copy_array_hdr (pool *p, const array_header *src)

This function is similar to ap_copy_array() but implements a deferred copy. Initially only a new header is created which points back to the data of the original array. Only if the new copy is extended with an ap_push_array() or ap_array_cat() is the old data copied. If the array is never extended, it avoids the overhead of a full copy.

array_header *new = ap_copy_array_hdr(p, arr);

Accessing Array Elements

   Show Contents   Go to Top   Previous Page   Next Page

There are no functions in the API to fetch specific elements or to iterate over the list. However, it is simple enough to pull the array out of the elts field and typecast it into a C array of the proper type. You can use the nelts field to keep track of the number of elements inside the array. In this example, we iterate over the entire array, printing out its contents (using the ap_rprintf() function, which we will discuss later):

char **list = (char**)arr->elts;
for(i = 0; i < arr->nelts; i++) {
   ap_rprintf(r, "item %d -> %s\n", i, list[i]);

Changing an item in the array is done in a similar way:

((char **)arr->elts)[2] = "transmission aborted"

If you wish to clear the array, simply set nelts to zero:

arr->nelts = 0;
   Show Contents   Go to Top   Previous Page   Next Page
Copyright 1999 by O'Reilly & Associates, Inc.