@SubAppendix
    @Title { Arenas and arrays }
    @Tag { ha }
@Begin
@LP
@I { Note -- the Ha library has been flown in from another project
of the author's, called Howard.  Its header file is @C { howard_a.h }. }
@PP
This section describes Howard's Ha library, which provides arenas and
extensible arrays.
ha. @Index { Ha }
@BeginSubSubAppendices

@SubSubAppendix
    @Title { Arenas }
    @Tag { ha.arenas }
@Begin
@LP
An arena is an object (a pointer to a private struct) of type
@C { HA_ARENA }.  It represents an unlimited amount of @I { arena memory }:
heap memory held in an arena so that it can be freed all at once later.
Starting from Version 2.1, all memory allocated by KHE is arena memory.
# , for reasons explained in Section {@NumberOf intro.memory}.
@PP
For creating and deleting arenas, the operations are
@ID @C {
HA_ARENA HaArenaMake(bool large);
}
@C { HaArenaMake } creates an arena.  Parameter @C { large } does
nothing except get itself stored in the arena, where it can be
retrieved by calling
@ID @C {
bool HaArenaLarge(HA_ARENA a);
}
It is a hint that the arena may become (very) large.  Function
@ID @C {
void HaArenaDelete(HA_ARENA a);
}
deletes @C { a }, freeing all its arena memory.  Also,
@ID @C {
void HaArenaClear(HA_ARENA a);
}
frees most of @C { a }'s memory, returning it to its state immediately
after @C { HaArenaMake }.
@PP
The author has had some unpleasant experiences with the @C { malloc }
memory allocator supplied with his Linux system on large runs with
multiple threads, and he has concluded that recycling memory via
@C { malloc } and @C { free } is best avoided.  Accordingly, function
@ID @C {
HaArenaRecycle(HA_ARENA a);
}
is provided, which is similar to @C { HaArenaClear } in that it
tells @C { a } that all of its objects except @C { a } itself are
no longer wanted, but different in that it does not free @C { a }'s
memory.  Instead, it retains it and uses it for subsequent allocations.
@PP
@C { HaArenaRecycle } is usually used in conjunction with a free list
of arenas.  When an arena is needed, it is taken from this list if
possible, otherwise it is created by @C { HaArenaMake }.  When an
arena @C { a } is no longer needed, @C { HaArenaRecycle(a) } is
called and @C { a } is added to the free list.  In this way, memory
is efficiently recycled without calling @C { free }.  Functions
@C { KheArenaSetArenaBegin } and @C { KheArenaSetArenaEnd }
(Appendix {@NumberOf ha.arena_sets}) do this.
@PP
Operations
@ID @C {
void *HaAlloc(HA_ARENA a, size_t size);
void HaMake(X res, HA_ARENA a);
}
allocate memory.  @C { HaAlloc } returns a pointer to at least
@C { size_t } bytes of arena memory from @C { a }, aligned suitably
for any data and initialized to zero.  Macro @C { HaMake } sets
@C { res } (which may have any pointer type @C { X }) to point to at
least @C { sizeof(*res) } bytes of memory obtained from @C { HaAlloc }.
These objects may not be resized.  For resizable objects, see
Section {@NumberOf ha.allocator}.
@PP
Arenas obtain their memory from @C { malloc } (actually @C { calloc }).
As long as @C { calloc } continues to supply memory, an arena will
continue to supply memory to the user.  If a request for memory from
@C { calloc } fails, then @C { HaArenaMake } and @C { HaAlloc }
return @C { NULL }, and @C { HaMake } sets @C { res } to @C { NULL }.
@PP
The memory pointed to by a variable @C { a } of type @C { HA_ARENA }
is arena memory from arena @C { a }.  This memory is freed along with
the rest of the arena memory when the arena is deleted.
@PP
@C { HaAlloc } initializes the memory to zero for two reasons.  First,
an uninitialized object field can cause a program to behave differently
each time it runs.  If all object memory is initialized to zero, an
uninitialized field is still a bug, but at least the program behaves
the same each time it runs.  Second, @C { HaArrayContains }
(Section {@NumberOf ha.arrays}) compares generic objects using
@C { memcmp }.  When these objects are structs with gaps in them,
this will only be correct if the gaps are equal.
@PP
The cost of calling @C { HaArenaMake } and @C { HaArenaDelete } is
small enough to allow many small arenas to come and go.  A call to
@C { HaArenaMake } generates one call to @C { calloc } requesting
14 words of memory, a few internal function calls which will
certainly be inlined, and about 15 initializing assignments.  A
call to @C { HaArenaDelete } generates one call to @C { free } and
three assignments when the arena is empty, growing logarithmically
(i.e. negligibly slowly) as the amount of memory allocated in the
arena increases.  Section {@NumberOf ha.allocator} has more detail.
@End @SubSubAppendix

@SubSubAppendix
    @Title { Arena sets }
    @Tag { ha.arena_sets }
@Begin
@LP
An @I { arena set } is a set of arenas.  It is a convenient place
to store recycled arenas while they are waiting for new uses.
To create a new, empty arena set, call
@ID @C {
HA_ARENA_SET HaArenaSetMake(HA_ARENA a);
}
The arena set (but not its arenas) lies in arena @C { a } and will
be freed when @C { a } is freed.
@PP
In practice, what is mainly wanted is the ability to add an arena
and extract one:
@ID @C {
void HaArenaSetAddArena(HA_ARENA_SET as, HA_ARENA arena);
HA_ARENA HaArenaSetLastAndDelete(HA_ARENA_SET as);
}
@C { HaArenaSetAddArena } adds @C { arena } to @C { as }, and
@C { HaArenaSetLastAndDelete } deletes and returns the last
arena from @C { as }.  Also useful are
@ID @C {
int HaArenaSetArenaCount(HA_ARENA_SET as);
HA_ARENA HaArenaSetArena(HA_ARENA_SET as, int i);
}
which return the number of arenas in @C { as }, and the @C { i }th
arena, counting from 0 as usual.
@PP
Typically, there is one arena set per thread.  When a thread
terminates, the arenas of its arena set need to be passed on
to the arena set of the parent thread.  For this there is
@ID @C {
void HaArenaSetMerge(HA_ARENA_SET dest_as, HA_ARENA_SET src_as);
}
It moves the arenas of @C { src_as } to @C { dest_as }, leaving
@C { src_as } empty.
@PP
Since arena sets are mainly for recycling arenas, two
convenience functions are offered:
@ID @C {
HA_ARENA HaArenaSetArenaBegin(HA_ARENA_SET as, bool large);
void HaArenaSetArenaEnd(HA_ARENA_SET as, HA_ARENA a);
}
If @C { as } is non-@C { NULL }, @C { HaArenaSetArenaBegin } returns
a fresh arena, extracted from @C { as } if there is one with the given
@C { large } attribute, or else newly created, and @C { HaArenaSetArenaEnd }
calls @C { HaArenaRecycle(a) } then adds @C { a } to @C { as }.  If
@C { as } is @C { NULL }, the two functions just call @C { HaArenaMake }
and @C { HaArenaDelete }.
@PP
For completeness, a few other operations are provided:
@ID @C {
void HaArenaSetClear(HA_ARENA_SET as);
void HaArenaSetDropFromEnd(HA_ARENA_SET as, int n);
void HaArenaSetDeleteArena(HA_ARENA_SET as, HA_ARENA arena);
bool HaArenaSetContainsArena(HA_ARENA_SET as, HA_ARENA arena, int *pos);
void HaArenaSetDebug(HA_ARENA_SET as, int verbosity, int indent,
  FILE *fp);
}
These clear @C { as } back to the empty set, remove the last
@C { n } arenas from @C { as }, delete @C { arena } (which must
be present) from @C { as }, return @C { true } when @C { arena }
lies in @C { as }, and produce a debug print of @C { as } onto
@C { fp } with the given verbosity and indent.
@End @SubSubAppendix

@SubSubAppendix
    @Title { Arrays }
    @Tag { ha.arrays }
@Begin
@LP
Like C's native arrays, Ha's arrays are @I { generic }:  they may
have elements of any one type, of any width, and the C compiler
will report an error if there is a type mismatch.  But, unlike
C's arrays, Ha's arrays are @I { extensible }:  they may grow to
any length during use.
@PP
The type of an extensible generic array must be declared using a
@C { typedef } invoking macro @C { HA_ARRAY }.  For example, the
following declarations already appear within @C { howard_a.h }:
@ID @C {
typedef HA_ARRAY(bool)        HA_ARRAY_BOOL;
typedef HA_ARRAY(char)        HA_ARRAY_NCHAR;
typedef HA_ARRAY(wchar_t)     HA_ARRAY_CHAR;
typedef HA_ARRAY(short)       HA_ARRAY_SHORT;
typedef HA_ARRAY(int)         HA_ARRAY_INT;
typedef HA_ARRAY(int64_t)     HA_ARRAY_INT64;
typedef HA_ARRAY(void *)      HA_ARRAY_VOIDP;
typedef HA_ARRAY(char *)      HA_ARRAY_NSTRING;
typedef HA_ARRAY(wchar_t *)   HA_ARRAY_STRING;
typedef HA_ARRAY(float)       HA_ARRAY_FLOAT;
typedef HA_ARRAY(double)      HA_ARRAY_DOUBLE;
}
ha_array @Index @C { HA_ARRAY }
Create your own array type by placing any type at all between the
parentheses.
@PP
To gain access to @C { wchar_t } and @C { int64_t }, @C { howard_a.h }
includes header files @C { <wchar.h> } and @C { <stdint.h> }.  Use of
@C { long } just leads to trouble, in the author's experience, since
its width varies across platforms, so @C { int64_t }, a standard
64-bit signed integral type, is used instead.
@PP
A variable of any of these types is a struct (not a pointer to a struct)
with three fields:  a typed pointer to arena memory holding the elements,
the number of elements that that memory @I can hold, and the number of
elements that it currently @I does hold.  Structs are used rather than
pointers to structs because extensible arrays are mainly used as aids to
the implementation of other abstractions, and are thus usually private
to one class or function, not shared.  So there is no problem in having
their structs lie directly in class objects or on the call stack, rather
than in arena memory at the end of a pointer; and it is more efficient
this way.
@PP
An array may be a field of an object that lies in one arena, while the
array's arena memory lies in a different arena.  But that would be
unusual, since the array would normally have the same lifetime as
the object, and thus would naturally belong in the same arena.
@PP
When an array is initialized, it contains no elements and no arena
memory is allocated for it.  Its pointer to arena memory points to
a shared empty array in its arena.  As the array grows, arena memory
for it is allocated and reallocated, but always from the same arena.
Each reallocation approximately doubles the number of elements that
the array can hold, ensuring that another reallocation will not be
needed soon, while wasting at most as much space as is used.  Memory
freed by a reallocation becomes available to hold other resizable
objects in the same arena.
@PP
If one array is assigned to another using the C @C { = } operator
or parameter passing, the arrays will have separate copies of their
three fields, yet share their elements.  This is only safe when the
original array is not used afterwards, or the array's length remains
constant thereafter.
@PP
Ha's array operations are macros, necessarily so since they are
generic.  They take structs as parameters, not pointers to structs.
This encourages the user to think of arrays as opaque objects, like
file pointers and so on.  A disadvantage of macros is that their
parameters may be evaluated more than once during a call.  Unless
explicitly stated otherwise, the user should assume that all
parameters of all array operations are evaluated more than once.
In many cases they are.
@PP
The first operation on any array must be to initialize it by a call to
@ID @C {
void HaArrayInit(ARRAY_X a, HA_ARENA arena);
}
This sets @C { a } to empty and specifies the arena which will
supply its memory when elements are added later.  Here and
throughout this section, array operations are presented as though
they are functions, even though they are really macros, and
@C { ARRAY_X } stands for the type created by
@ID @C {
typedef HA_ARRAY(X) ARRAY_X;
}
for any type @C { X }.  To find the arena that an initialized array
@C { a } lies in, call
@ID @C {
HA_ARENA HaArrayArena(ARRAY_X a);
}
In general, memory allocated by Howard's functions can only be
reclaimed by deleting the arena.  However, resizable objects
such as arrays are an exception, and function
@ID @C {
void HaArrayFree(ARRAY_X a);
}
frees the arena memory used by @C { a }, if any.  This does not free
@C { a } itself; @C { a } is not a pointer.  It frees the memory
holding the elements of @C { a }, making it available to other
resizable objects in @C { a }'s arena.
@PP
To find the number of elements currently stored in an array, call
@ID @C {
int HaArrayCount(ARRAY_X a);
}
The elements have indexes from @C { 0 } to @C { HaArrayCount(a) - 1 }
inclusive, as usual in C.  For efficiency, array bounds are not checked
by any Ha operation.  To access the element with index @C { i }, or the
first element, or the last element, call
@ID @C {
X HaArray(ARRAY_X a, int i);
X HaArrayFirst(ARRAY_X a);
X HaArrayLast(ARRAY_X a);
}
@C { HaArray } and @C { HaArrayFirst } evaluate their parameters only
once, and all three operations can be used as variables as well as
values.  So one can write, for example,
@ID @C {
HaArray(frequencies, i)++;
}
to increment the element of @C { frequencies } whose index is @C { i },
or
@ID @C {
do_something(&HaArrayFirst(a))
}
to pass a pointer to an element.
@PP
To add one element to an array, the operations are
@ID @C {
X HaArrayAdd(ARRAY_X a, int i, X x);
X HaArrayAddFirst(ARRAY_X a, X x);
X HaArrayAddLast(ARRAY_X a, X x);
}
@C { HaArrayAdd } adds @C { x } to @C { a } at index @C { i }, which
may range from @C { 0 } to @C { HaArrayCount(a) } inclusive.  It
makes room for @C { x } by shifting elements up one place, including
reallocating arena memory if necessary.  It returns @C { x }.
@C { HaArrayAddFirst(a, x) } is equivalent to @C { HaArrayAdd(a, 0, x) },
and @C { HaArrayAddLast(a, x) } is a faster version of
@C { HaArrayAdd(a, HaArrayCount(a), x) }.
@ID @C {
void HaArrayFill(ARRAY_X a, int len, X x);
}
adds @C { x } 0 or more times to the end of @C { a }, stopping
when @C { HaArrayCount(a) } is at least @C { len }.
@ID @C {
X HaArrayPut(ARRAY_X a, int i, X x);
}
replaces the value at index @C { i } with @C { x } and returns @C { x }.
It evaluates its parameters only once.  And
@ID @C {
void HaArrayMove(ARRAY_X a, int dest_i, int src_i, int len);
}
uses the C @C { memmove } function to move (that is, copy with
overlapping allowed) the @C { len } elements starting at index
@C { src_i } to index @C { dest_i }.  It assumes without checking
that @C { len >= 0 } and that @C { src_i } and @C { dest_i }
are at least @C { 0 } and at most @C { HaArrayCount(a) - len }.  It
is used by @C { HaArrayAdd } above and @C { HaArrayDeleteAndShift }
below to do their shifting.
@PP
For searching an array there is
@ID @C {
bool HaArrayContains(ARRAY_X a, X x, int *pos);
}
It returns @C { true } if @C { a } contains @C { x }, setting @C { *pos }
to the index of its first occurrence; otherwise it returns @C { false },
leaving @C { *pos } unchanged.  The individual comparisons are made by
@C { memcmp }.
@PP
Two operations delete the @C { i }th element, offering two ways to
fill the gap it leaves behind:
@ID @C {
void HaArrayDeleteAndShift(ARRAY_X a, int i);
void HaArrayDeleteAndPlug(ARRAY_X a, int i);
}
@C { HaArrayDeleteAndShift } shifts the elements after @C { i }
down one place; @C { HaArrayDeleteAndPlug } assigns the last element
to position @C { i }, then deletes the last element.  Operations
@ID @C {
bool HaArrayFindDeleteAndShift(ARRAY_X a, X x, int *pos);
bool HaArrayFindDeleteAndPlug(ARRAY_X a, X x, int *pos);
}
call @C { HaArrayContains }, returning what it returns but also
using @C { HaArrayDeleteAndShift } or @C { HaArrayDeleteAndPlug }
to delete the element it found, if any.  There are also
@ID @C {
void HaArrayDeleteLast(ARRAY_X a);
void HaArrayDeleteLastSlice(ARRAY_X a, int n);
void HaArrayClear(ARRAY_X a);
}
for deleting the last element, deleting the last @C { n } elements
(which can be done very efficiently), and deleting the last
@C { HaArrayCount(a) } elements, leaving the array empty.  And
@ID @C {
X HaArrayLastAndDelete(ARRAY_X a);
}
returns the last element of @C { a } and also deletes it from @C { a }.
Deleting elements does not free any memory.  The vacated memory
remains available to the array, should it decide to grow again.
@PP
Here are some more complex operations that change the
contents of arrays.
@ID @C {
void HaArraySwap(ARRAY_X a, int i, int j, X tmp);
}
Swap the elements of @C { a } at positions @C { i } and @C { j }.
Parameter @C { tmp } is a variable used to hold an element temporarily
while swapping.
@ID @C {
void HaArrayWholeSwap(ARRAY_X a, ARRAY_X b, ARRAY_X tmp);
}
Swap two whole arrays, that is, swap the contents of their structs,
using @C { tmp } as a temporary.
@ID @C {
void HaArrayAppend(ARRAY_X dest, ARRAY_X source, int i);
}
Append the elements of @C { source } to the end of @C { dest },
leaving @C { source } unchanged.  Parameter @C { i } is a
variable used as an external cursor when scanning @C { source }.
@ID { 0.98 1.0 } @Scale @C {
void HaArraySort(ARRAY_X a, int(*compar)(const void *, const void *));
}
Sort @C { a } by means of a call to @C { qsort }, using @C { compar }
as the comparison function.
@ID @C {
void HaArraySortUnique(ARRAY_X a,
  int(*compar)(const void *, const void *));
}
Like @C { HaArraySort }, except that after sorting, elements are
deleted until no two adjacent elements return 0 when compared using
@C { compar }.  If this is done purely for uniqueifying, it is common
to implement @C { compar } as a mere subtraction of two pointers.
However, on a 64-bit architecture this yields a 64-bit integer, and
merely returning this cast to @C { int }, the return type of @C { compar },
does not work.  Use a conditional expression returning @C { -1 }, @C { 0 },
or @C { 1 } instead.
@PP
Finally, Ha offers iterator macros for traversing arrays:
@ID @C {
HaArrayForEach(ARRAY_X a, X x, int i)
HaArrayForEachReverse(ARRAY_X a, X x, int i)
}
These iterate over the elements of @C { a }, in forward or reverse
order.  Within each iteration, @C { x } is one element of @C { a }
and @C { i } is the index of @C { x } in @C { a }.  For example,
@ID @C {
HaArrayForEach(strings, str, i)
  fprintf(stdout, "string %d: %s\n", i, str);
}
prints the elements of array @C { strings }.  Like all Howard's
iterators, both macros expand to
@ID @C { for( ... ; ... ; ... ) }
and may be used syntactically in any way that this construct may be.
@End @SubSubAppendix

@SubSubAppendix
    @Title { Version string }
@Begin
@LP
Macro @C { HA_HOWARD_VERSION } is a wide character string defining
ha_howard_version @Index @C { HA_HOWARD_VERSION }
the current version of Howard.  For example, its value was
@ID @C { L"Howard Version 1.0 (June 2011)" }
at the time of writing.
@End @SubSubAppendix

#@SubSubAppendix
#    @Title { Version string and assertions }
#@Begin
#@LP
#Ha is used by every Howard library, so it has accumulated a few
#miscellaneous, generally useful features.  These are documented
#in this section.
#@PP
#Macro @C { HA_HOWARD_VERSION } is a wide character string defining
#ha_howard_version @Index @C { HA_HOWARD_VERSION }
#the current version of Howard.  For example, its value was
#@ID @C { L"Howard Version 1.0 (June 2011)" }
#at the time of writing.  Ha also offers two functions for checking
#assertions:
#@ID @C {
#void HaAbort(wchar_t *fmt, ...);
#void HaAssert(bool cond, wchar_t *fmt, ...);
#}
#ha.abort @Index @C { HaAbort }
#ha.assert @Index @C { HaAssert }
#@C { HaAbort }'s parameters are the same as @C { wprintf }'s, but it
#prints onto @C { stderr } and then calls @C { abort }.  @C { HaAssert }
#does nothing if @C { cond } is @C { true }, and it does what @C { HAbort }
#does if @C { cond } is @C { false }.  It is a function, not a macro, so
#its parameters must be well-defined whether @C { cond } is true or not.
#@End @SubSubAppendix

@SubSubAppendix
    @Title { Howard's memory allocator }
    @Tag { ha.allocator }
@Begin
@LP
This section contains more information about Howard's memory allocator
than the user is likely to need.  It explains how memory is aligned,
presents the operations for allocating resizable arena memory, and
describes how the allocator works.
@PP
Howard's memory allocator promises to return memory aligned correctly for
any kind of data.  However, there seems to be no standard way to find out
what that alignment is.  So file @C { howard_a.h } includes a typedef of
a type @C { HA_ALIGN_TYPE }, and the allocator assumes that memory aligned
with this type aligns with all types.  By default this typedef is
@ID @C {
typedef void *HA_ALIGN_TYPE;
}
but it may be changed to any type whose width is at least the width of
a pointer.  @C { HaArenaMake } checks this condition and aborts if it
does not hold, since the implementation depends on it.
@PP
@I { Resizable arena memory } is arena memory returned by functions
@C { HaResizableAlloc } and @C { HaResizableReAlloc }, defined
below.  Given a pointer to resizable arena memory, the allocator
can deduce what arena it is from and what its size is (in bytes):
@ID @C {
HA_ARENA HaResizableArena(void *resizable);
size_t HaResizableSize(void *resizable);
}
The value of @C { HaResizableSize } may be larger than the size
requested when @C { resizable } was allocated.  The functions
for allocating and freeing resizable arena memory are
@ID @C {
void *HaResizableAlloc(HA_ARENA a);
void *HaResizableReAlloc(void *resizable, size_t size);
void HaResizableFree(void *resizable);
}
@C { HaResizableAlloc } returns a pointer to @C { 0 } bytes of
resizable arena memory from arena @C { a }.  This may seem useless,
but experience shows that it produces the most convenient initial
value.  All pointers to 0 bytes from @C { a } are shared, so there is
no memory cost.  @C { HaResizableReAlloc } assumes that @C { resizable }
points to resizable arena memory, and begins by finding its arena and
size.  If @C { size } is no larger than this old size, @C { resizable }
is returned.  If @C { size } is larger, a pointer to @C { size } or
more bytes of resizable arena memory from the same arena is returned.
Its first old size bytes are copied from @C { resizable } using
@C { memcpy }, and @C { resizable } is reclaimed for re-use by other
calls for resizable memory from the same arena (unless its size is 0).
@C { HaResizableFree } reclaims @C { resizable } just as
@C { HaResizableReAlloc } does, but without allocating new memory.
@PP
Like ordinary arena memory, resizable arena memory is aligned suitably
for any kind of data.  Resizable arena memory is not initialized to
zero, however.
@PP
The remainder of this section describes the implementation of the arena
memory allocator, including how it tries to avoid various problems that
memory allocators are prone to.
@PP
An arena obtains its memory from @C { calloc }.  A piece of memory given
to an arena by @C { calloc } will be called a @I { chunk }; a piece of
memory given to the user by an arena will be called a @I { block }.
@PP
Let @M { A } be @C { sizeof(HA_ALIGN_TYPE) }.  Since the memory
returned has to align, every block might as well contain (and does
contain) a number of bytes which is a multiple of @M { A }.  If the
number requested is not a multiple of @M { A }, it is increased to
the next multiple of @M { A }.  The resulting wasted memory is
called the @I { alignment overhead }.  It will be negligible in
practice, and often zero.
@PP
An arena cannot satisfy all the block requests it receives out of one
chunk.  So it calls @C { calloc } more than once, and maintains a
linked list of all the chunks it receives.  The arena object contains
a pointer to the most recently obtained chunk; this chunk's first
@C { A } bytes hold a pointer to the next most recently obtained
chunk, and so on.  The second @C { A } bytes in each chunk hold
the total number of words in the chunk, and the total number of
words not yet allocated.  Thus the memory overhead per chunk is
@C { calloc }'s overhead, plus @M { 2A } bytes to hold the 
the singly linked list of chunks and the two integers.
@PP
The linked list serves two purposes.  First, when the arena is
deleted, its memory is freed by traversing the list and freeing
each chunk.  The arena object itself, and the block list header
objects described below, lie within chunks like user blocks do,
and so are freed when the chunks are freed.  Second, when a block
is required, the first step is to try to obtain it from the first
chunk on the list.  Later chunks may not be entirely used up, but
they are never tried.
@PP
When one chunk holds many blocks, arena allocation is much better
than general allocation.  Blocks are allocated contiguously within
chunks, with no memory overhead other than the alignment overhead.
Unless a new chunk is needed, allocating a block is very fast:
just round up the requested size, test whether memory is available
in the first chunk, and make two assignments.
@PP
All chunks cannot be the same size, for two reasons.  First, if
they were, for memory efficiency one would want that size to be
large; but a large chunk would be wasteful if the arena remains
small, as it may do if there is one arena per function.  Second,
a request for a block whose size is larger than the chunk size
could not be satisfied.
@PP
Accordingly, the chunks obtained from @C { calloc } vary in size,
as follows.  The arena contains a @I { normal size } field.  Whenever
a new chunk is needed, the normal size is first increased.  Then, if
the request is for less memory than the normal size, a chunk of the
normal size is obtained from @C { calloc } and placed first in the
chunk list, and the request is satisfied from within that chunk.  If
the request is for as much as or more memory than the normal size, a
chunk of the requested size is obtained from @C { calloc } and placed
second in the chunk list, and the request is satisfied from this chunk,
which it fills completely.
@PP
The normal sizes are @M { ( 2 sup n - 2 )A }, for @M { n = 4 },
@M { n = 5 }, and so on.  This ensures that memory is not wasted on
unnecessarily large chunks.  When @C { calloc }'s overhead is added,
chunk sizes will probably be powers of 2, and as @M { n } increases,
a thread-aware @C { calloc } will allocate chunks on cache boundaries.
Even if @C { calloc } is not thread-aware, each arena should be accessed
by only one thread, which alone should give reasonable cache behaviour.
@PP
When an arena is recycled, its chunks are moved to a different singly
linked list, the @I { recycled chunk list }, and their memory is reset
to 0.  The chunk holding the arena object itself is treated somewhat
differently:  it stays on the main chunk list, and only the part of
its memory that does not hold the arena object is reset to 0.  When
allocating memory, if the current chunk is too full, then before making
a new chunk, the recycled chunk list is examined.  If it is non-empty,
its first chunk becomes the current chunk and the whole process is
restarted.  Otherwise a new chunk is obtained from @C { calloc } as usual.
@PP
It remains to describe how resizable blocks are handled.  The size
of each resizable block is @M { R sub n ` A } for some @M { n >= 0 },
where @M { R sub 0 = 0 } and @M { R sub n = 3 cdot 2 sup n - 1 } for
@M { n >= 1 }.  These numbers (0, 5, 11, 23, 47, ...) make good hash
table sizes.  From 5 onwards, each is obtained from its predecessor
by doubling and adding one.
@PP
Growing out of each arena object is a linked list of
@I { block list header } objects.  The first block list header
contains @M { R sub 0 } and a pointer to a singly linked list of all
free blocks of size @M { R sub 0 A } (this particular pointer is
always @C { NULL }); the second contains @M { R sub 1 } and a pointer
to a singly linked list of all free blocks of size @M { R sub 1 A };
and so on.  Each block list header also contains a pointer to its
arena and a pointer to the block list header for the next larger
size.  Initially, only the first block list header is present.
@PP
In addition to the @M { R sub n A } bytes passed to the user, a
resizable block has @M { A } bytes, just in front of the pointer
returned to the user, holding a pointer to the block list header
holding @M { R sub n }.  If the block is free, its second @M { A }
bytes holds a pointer to the next free resizable block of that size.
@PP
Given a user's pointer to a resizable block, one can find its
block list header by going back @M { A } bytes and following
the pointer.  The block list header gives access to the block's
arena and size, and to the free block list of blocks of that size.
@PP
A resizable block of at least a given size can be obtained by searching
the block list header list for the first block list header whose block
size is sufficiently large.  New block list headers are added if
required as the search proceeds.  Once the appropriate block list
header is reached, its first free block is returned to the user; or if
it has no free blocks, a fresh block is obtained from @C { HaAlloc }, a
pointer to the block list header is placed in its first @M { A } bytes,
and a pointer to its @M { (A + 1) }th byte is returned to the user.
@C { HaResizableReAlloc } begins its search for a block list header
from @C { resizable }'s block list header.  Most calls to
@C { HaResizableReAlloc } request blocks about double the old size,
so most traversals of the list of block list headers visit only one
block list header, ensuring that the time taken to find a new
resizable block is usually a small constant.
@PP
The memory overhead is @M { A } bytes per allocated block (holding the
pointer to the block list header), plus the space occupied by the block
list headers (negligible once the blocks grow to even moderate size),
plus the free blocks, plus any unused space within allocated blocks.
@PP
The worst case is elicited by an arena containing a single extensible
array that grows one element at a time.  (This case can be duplicated
by growing two arrays in parallel.)  Now, resizable blocks are needed
just because the application cannot predict how much memory will be
needed.  Thus, the application might as well ask for sizes of the
form @M { R sub n ` A }, and the extensible array module does this.
As the array grows, it leaves a trail of freed blocks behind it of
sizes @M { (5 + 1)A }, @M { (11 + 1)A }, @M { (23 + 1)A }, and so on.
Their total size is less than half the current block size.  The current
block may itself be only half full, so at worst, three times as much
memory is allocated as is used.  But none of this memory is completely
lost:  half of it is available for further growth of the array, the
other half is available for other arrays, and all of it is freed
when the arena is deleted.
@End @SubSubAppendix

@EndSubSubAppendices
@End @SubAppendix
