Noeud:obstack, Noeud « Next »:, Noeud « Previous »:alloca, Noeud « Up »:Memory Management



obstack

Another problem with the traditional malloc API is that it is very difficult to cope with chunks of memory that may expand during runtime, or are of unknown length when the call to malloc is made. If you need to keep the memory over a function call boundary, alloca is of no use.

Typically, this is a problem when reading strings into an application - either you have to scan the string once to calculate the length and then again to copy it into a correctly sized block of memory; or you have to call realloc to fetch a bigger block of memory each time you detect that you are about to go out of bounds. example 2.5 uses the second of these methods.

     char *
     read_string (FILE *input)
     {
       size_t i       = 0;
       size_t size    = 10;          /* Take a guess.  */
       char * string  = (char *) malloc (1+ size);
       int    c;
     
       while ((c = fgetc (input)) != EOF)
         {
           if (isspace (c))
             break;
     
           if (i == size)
             {
               size  *= 2;
               string = realloc (string, 1+ size);
             }
     
           string[i++] = (char) c;
         }
       string[i] = '\0';
     
       return string;
     }
     
     Example 2.5: Reading a string into memory -- malloc version
     

In effect obstacks manage the resizing of allocated memory for you, albeit in a far more efficient manner than the manual reallocation from example 2.5. All of the functions and macros described in this section can be accessed by including the obstack.h header in your file.

struct obstack data type
An opaque handle for an obstack. All of the functions for managing obstacks take a pointer to one of these structures. You can have as many obstacks in your program as you like, and each is capable of holding many strings. However there can be only one growing string in each obstack at any given time - when that string is complete, you can finalise it and start another string in the same obstack. As soon as you have done that, the finalised string cannot be changed. You are not limited to strings in fact, you can store any kind of growing memory object in an obstack, provided that only one object in each obstack is active. Hence the term stack: the active object is the top "plate" on the stack and can be accessed and changed, but you can't get to the plates underneath.

Before you can call any of the obstack functions, you need to decide how those functions will allocate and release dynamic memory, and must define the following macros in your source file:

     #define obstack_chunk_alloc  malloc
     #define obstack_chunk_free   free
     

You can define these macros to use any memory allocation scheme, though you must define them before any other calls to obstack functions. Despite our use of malloc and free to manage memory, obstacks request and release memory in large chunks which is more time efficient.

int obstack_init (struct obstack*obstack_handle) glibc function
This function initialises the obstack such that it is ready to have objects stored in it. This function must always be called to initialise an obstack before it can be used with any of the other functions detailed in the rest of this section.

     #include <obstack.h>
     
     #define obstack_chunk_alloc  malloc
     #define obstack_chunk_free   free
     
     static struct obstack *string_obs = NULL;
     ...
     
     char *
     read_string (FILE *input)
     {
       if (string_obs == NULL)
         {
           string_obs = (struct obstack *) malloc (sizeof (struct obstack));
           obstack_init (string_obs);
         }
     
       while ((c = fgetc (input)) != EOF)
         {
           if (isspace (c))
             break;
     
           obstack_1grow (string_obs, (char) c);
         }
     
       return (char *) obstack_finish (string_obs);
     }
     
     Example 2.6: Reading a string into memory -- obstack version
     

In example 2.6 we keep adding characters to the growing object as soon as they are read in from the input stream. The obstack takes care of ensuring that there is enough memory to add the characters, internally fetching more memory from the system using obstack_chunk_alloc as necessary. Consequently the object might move occasionally as it is growing, so it is important not to rely on the address of the object before it has finished.

Remember that only one object in the obstack can be growing at any given time. The object is started implicitly as soon as any bytes are added to it with the following function calls, but must be finished explicitly using obstack_finish.

int obstack_1grow (struct obstack*obstack_handle, char ch) glibc function
Simply grow the current object in obstack_handle by a single byte, ch.

void *obstack_finish (struct obstack*obstack_handle) glibc function
Declare that the current object has finished growing, and return the address of the start of that object. The next time one of the grow functions is called for obstack_handle, a new object will be started.

There are also other functions for growing the current object in an obstack by a different size than a single byte:

int obstack_ptr_grow (struct obstack*obstack_handle, void *data) glibc function
Grow the current object in obstack_handle by sufficient size to hold a pointer, and fill that space with a copy of data.

int obstack_int_grow (struct obstack*obstack_handle, int data) glibc function
Grow the current object in obstack_handle by sufficient size to hold an int, and fill that space with a copy of data.

int obstack_grow (struct obstack*obstack_handle, void *data, int size) glibc function
Grow the current object in obstack_handle by size bytes, and fill that space by copying size bytes from data.

int obstack_grow0 (struct obstack*obstack_handle, void *data, int size) glibc function
Grow the current object in obstack_handle by size bytes, and fill that space by copying size bytes from data, followed by a null character.

int obstack_blank (struct obstack*obstack_handle, int size) glibc function
Grow the current object in obstack_handle by size bytes, but leave them uninitialized. You can also shrink the current object by passing a negative value in size, if you are careful not to reduce the size of the object below zero.

int obstack_object_size (struct obstack*obstack_handle) glibc function
Return the size of the growing object. This function is useful for ensuring that you don't decrease the size of an object below zero with obstack_blank. If the current object has not yet been grown (for example immediately after a call to obstack_finish), this function will return zero.