************************************************
**Storage Pools**: controlling memory management
************************************************

Ada gives full control to the user for memory management. That allows for
a number of optimization in your application. For instance, if you need to
allocate a lot of small chunks of memory, it is generally more efficient
to allocate a single large chunk, which is later divided into smaller
chunks. That results in a single system call, which speeds up your
application.

This can of course be done in most languages. However, that generally
means you have to remember not to use the standard memory allocations
like `malloc` or `new`, and instead call one of your
subprograms. If you ever decide to change the allocation strategy, or
want to experiment with several strategies, that means updating your
code in several places.

In Ada, when you declare the type of your data, you also specify through
a `'Storage_Pool` attribute how the memory for instances of that
type should be allocated. And that's it. You then use the usual
`new` keyword to allocate memory.

GNATColl provides a number of examples for such storage pools,
with various goals. There is also one advanced such pool in the GNAT
run-time itself, called `GNAT.Debug_Pools`, which allows you to
control memory leaks and whether all accesses do reference valid memory
location (and not memory that has already been deallocated).

In GNATColl, you will find the following storage pools:

*`GNATCOLL.Storage_Pools.Alignment`*
  This pool gives you full control over the alignment of your data. In
  general, Ada will only allow you to specify alignments up to a limited
  number of bytes, because the compiler must only accept alignments
  that can be satisfied in all contexts, in particular on the stack.

  This package overcomes that limitation, by allocating larger chunks
  of memory than needed, and returning an address within that chunk which
  is properly aligned.

*`GNATCOLL.Storage_Pools.Headers`*
  This pool allows you to allocate memory for the element and reserve extra
  space before it for a header. This header can be used to store per-element
  information, like for instance a reference counter, or next and previous
  links to other elements in the same collection.

  In many cases, this can be used to reduce the number of allocations, and
  thus speed up the overall application.
