The objective of this proposal is to standardize the usage of common data structures within the context of the C language. The existence of a common standard interface for lists, hash tables, flexible arrays, and other containers has several advantages:

User code remains portable across different projects. In C, we all use the FILE abstraction, for instance. This abstraction allows software to be
compatible across a large spectrum of machines and operating systems. Imagine what would happen if each project had to develop a file stream
abstraction again and again. This is the case when using lists, for instance. Today, we have in all significant projects written in C a list
module, and probably other ones like hash tables, etc.

Avoid duplication of effort. Most of the list or hash tables modules can't be debugged completely and are the source of never ending problems.

Lack of standards makes the merging of two projects very difficult since in most cases the interfaces and data structures are slightly
different. This leads to a complete rewrite of one of the modules, or to ädapter" software that will translate from one list implementation
to the other, adding yet another layer of complexity to the merged project.

The language becomes more expressive since it becomes possible to reason within a high level environment. The lack of operations for
handling advanced data structures conditions programmers to use low level solutions like making an array with a fixed maximum size instead of a
list even if the programmer would agree that a list would be a more adequate solution to the problem. Confronted to the alternative of
developing yet another list module or opting for a low level solution many time constrained programmers will opt for the second solution.

The portable specifications provide a common framework for library writers and compiler/system designers to build compatible yet strongly specialized implementations.

The language becomes easier to analyze mathematically.
In their very interesting paper "Precise reasoning for programs using containers", Dillig, Dillig and Aiken
1 enumerate three main points that make program analysis easier using containers:

Understanding the contents of a container doesn't require understanding the container's implementation

Verifying container implementations requires different techniques and degrees of automation than verifying their clients. Hence, separating
these two tasks allows us to choose the verification techniques best suited for each purpose.

There are orders of magnitude more clients of a container than there are container implementations. This fact makes it possible to annotate
a handful of library interfaces in order to analyze many programs using these containers.

It is possible to abstract from the nature of any container (using the iterator construct) what allows a series of algorithms to
be written without having to bind them to a precise data structure. Containers present a uniform interface to the rest of the program.

The big innovation of C in the eighties was its standard library, that made input/output portable across machines and implementations. The container library would replicate again that idea, at a higher level.

The specifications presented here are completely scoped by the C99 specifications, and can be implemented even in compilers that do not implement C99 and remained within the C94 context. No language extensions are needed nor any are proposed.

The interfaces proposed try to present complete packages, i.e. interfaces with all the necessary functions to allow the widest usage: Serialization, searching, and many other functionalities are included in the proposed standard to allow for maximum code portability. It can be argued that this makes for "fat" containers, but if you read carefully you will notice that many things can be left out in systems that run in low memory or with feeble computing power.

This documentation is composed of several parts:

An introductory part where the general lines of the library are explained.

A specifications part where each function of the library is fully specified. This is the proposal for the next C standard.

An ëxamples" part that shows the uses of the library and allows you to have a better idea of how the usage of the library looks like.

An implementation part where the code of the sample implementation is discussed. This is designed as a guide for implementors to give them a basis to start with.

It has been a tradition in C to place raw performance as the most important quality of specifications. To follow this sacred cow, C specifications
ignored any error analysis arguing that any specification of failure modes would damage "performance". No matter that raw machine performance
increased by several orders of magnitude, the cost of a check for NULL was always "too expensive" to afford.

This kind of mental framework was described by one of the people in the discussion group "comp.lang.c++" as follows:2

In C++, the program is responsible for ensuring that all parameters to
the standard library functions are valid, not only the third parameter of
std::mismatch(). For example, also the first range for std:mismatch()
must be valid, one may not pass a start iterator from one container and
end iterator from another, for example. However, STL does not guarantee
any protection against such errors, this is just UB.

These specifications try to break away from that frame of thought. Each function specifies a minimal subset of failure modes as a consequence of its
error analysis. This allows user code to:

Detect and handle errors better. Error detection is simple: All functions return a negative
error code when they detect an error condition. Error detection can always test for the result
of any API with:

if (SomeCclApi(arg1,arg2) < 0) {
// error handling here
}

Ensure that errors will always have the same consequences. One of the worst consequences of undefined behavior is that the same error can
have completely different consequences depending on apparently random factors like previous contents of memory or previous allocation pattern.

At the same time, the mandatory error checking consists mainly of checks that can be implemented with a few integer comparisons. For instance a check
for zero is a single instruction in most processors. If implemented correctly the conditional jump after the comparison with zero is not taken in the
normal case and correctly predicted by the processor. This means that the pipeline is not disturbed and the cost for the whole operation is much less than a cycle.

Why is error analysis an essential part of any program specifications?

Because mistakes are a fact of life. Good programmers are good most of the time only. Even very good programmers do make mistakes3. Software
must be prepared to cope with this fact in an orderly fashion because if failure modes are not specified they have catastrophic consequences and lead
to brittle software that crashes randomly.

Note that error analysis is not error handling. Error handling is taking an action after an error, a task only the application can do.
What the library can do is to establish a framework where a user defined procedure receives enough information about the specific problem at hand.

Error analysis means that for each function and each API:

An analysis is performed of what are the consequences of any error in its inputs. Error codes are used to pass detailed error information
to the error procedure.

During its execution, an analysis is done of each step that can fail.

The outputs of the function are left in a consistent state, errors provoking the undo of the previous steps in most cases, leaving the inputs
as they were before the function was called. This feature allows library functions to be restartable after an error. For instance an out of memory
condition can be corrected by freeing memory and retrying.

The library provides hooks for the users that can control each step and provide functions that can do the error handling, for instance logging the
error and jumping to a pre-established recovery point.

Another design goal is to offer to the user a full feature set, complete with serializing, iterators, search, read-only containers and all the features
needed in most
situations. Other features are planned for later like multi-threading support. The objective here is to avoid incompatible and non portable extensions
because some essential feature is missing.

The library is designed with the possibility of implementing abstraction like serial and associative containers that allow software to treat several
containers in a way that abstract most of their features, improving code reuse by allowing to implement algorithms for a class of objects. This is
specially true in the iterators feature.

It can be argued that the C language lacks many of the abstractions constructs of other languages like templates, inheritance, and many others.
All that is true, but the objective of this proposal is to show that those constructs are just an aid to developing abstractions, an aid that
is paid in added complexity for the resulting language, and in a limitation of what is feasible within a given framework. Since C has no
framework, no preferred inheritance model, it is possible to create abstractions that are quite unconstrained: there is no framework precisely.

Even with all the tests, the performance of the library has been maintained at a high level compared to similar libraries
in other languages. The performance should improve if standardized because compiler writers could specialize their optimizations targeting this
code.

The specifications part of the proposal uses the same building blocks for each of the functions proposed.

Name

The name of the function. Note that when using this name, the container interface should be always before:
iList.Add, iDictionary.Add, etc.

The name is followed by the prototype defined as a function pointer. For the function Add of the container List we have

int (*Add)(List *list,const void *data);

This means that Add is a function pointer in the interface iList. It would be used as:
iList.Add(list,data).

Errors:

The minimal set of errors that can appear during the execution of the function is listed. Each implementation is free to add implementation specific errors to this list. Note that how the library behaves after an error is defined by the current error function in the container (if any), then by the behavior of the error function in the iError interface. This can be changed by the user by using the iError interface.

Returns:
The return value of the operation. Normally, negative values are error codes, positive values means success, and zero means non fatal errors, more in the sense of a warning.

In the context of this library, a container is a data structure used to organize data within a single logical object that allows for adding, searching
and removing data. In most containers the data is not further specified, but the library assumes that all elements of a container have the same type.
The data can be anything, images, numbers, text, whatever. The only thing that the container knows is the size of the data, if we store a series of
objects of the same size, or its address, if we store objects of different sizes. In the later case we store just a pointer in the
container4.

A special kind of containers, ValArrays, contain the basic types of the C language and the library treats them specially. There is one ValArray
for each elementary type. For character strings they are stored in ßtring collections", a term borrowed from the C# language.

Each container has a way of iterating through all its elements by using an ïterator" auxiliary object, that returns each stored object in sequence. In
sequential containers you can also iterate using an index variable, what can be cheaper than using iterators for arrays but very expensive in lists.

All objects stored by the library are copied into the library, and the library is responsible for the management of the associated storage. If you do not want this, just store a pointer to the data and manage the data yourself.

A container has a set of functions for accessing the elements it stores, called its interface. This object is a collection of function
pointers that provide the functionality of the container. The interfaces are stored in writable memory and the user can, at run time, change the
behavior of a class of containers by changing the function pointer. This operation is called ßubclassing" in this document.

Subclassing allows the user to modify the behavior of a container, maybe adding some functionality, without having to write all the container from
scratch. The user of the library can:

Use the stored pointer to the original function to call the original functionality, and add some functionality after that call.

Add some functionality and call the stored pointer afterwards.

Replace completely the functionality by its own without calling any of the former functions.

Subclassing adds enormous flexibility to this design, since it makes possible to add functionality in a transparent way.

Using the organization of the data as a classification criteria we have basically two different kinds of containers

1. Sequential containers

2. Associative containers

3. Statistical Containers

A sequential container is organized in a linear order. We have a sequence starting at index zero up to the number of elements stored. Data items can be
retrieved by index, and it makes sense to speak of a "next" and a "previous" element.

Sequential containers can be contiguous (arrays) or disjoint (lists). In the first case access is very fast since it implies multiplying the index by
the size of each element to get to any position in the data. In the second case access the nth element can be a lengthy operation since the chain of
"next" or "previous" pointers must be followed for each access to a given position.

An associative container stores an object divided in two parts: a key, that is used as a token for the data, and the data itself. It associates
key/value pairs. Speed of access is fast, but not linear, and can degrade as new items are stored in it.

Statistical containers are containers that return the probability of an item being found in them. See the bloom filter as an example.

In all cases, we have some basic properties of an abstract container that are common to all of them.

Functions to implement the life-cycle of the object: creation, maintenance and destruction.

Functions to add, replace and remove elements from the container.

A function that returns the number of objects stored in the container.

A function to report errors. This function (like all other function pointers) can be changed by the user of the library. In the sample implementation
it defaults to a simple function that prints the error in the standard error stream.

Each change in a container is recorded. This permits to validate pointers to a container: if the container has changed after the creation of the pointer, the pointer could be invalid.

All containers use a standard object to allocate and manage memory. The library provides a default allocator that contains the standard C functions malloc, free, realloc and calloc. Each container class can contain an allocator pointer, or each container can contain an allocator. The provided
sample implementation has a per container allocator, but in many applications a per class allocator could be enough, or even a single global allocator
that would be used by the whole library.

Managing a sequence involves trade offs what performance is concerned. If the usage will involve frequent insertion and deletion of objects you will
prefer a container that handles those operations in constant time: the time to add or delete an object doesn't increase with the number of elements in
the container. Such a container will be unlikely to provide also access to a given element in constant time. Access is likely to be much slower, and
what you gain in flexibility you loose in another dimension. It is the user of the library, the programmer, that decides what container fits best the
intended usage.

Since usage patterns change, however, the library tries to ensure that you can change the container you are using with minimal effort. If at the
beginning of an application a list looked like a good solution but later an array, that provides constant time access is better suited, you can change
the type of container without changing every line that uses it. The common vocabulary of the library makes this possible.

A generic approach using void *. This interface allows you to pass a pointer to the data and specify at creation time the size of the
object you want to store in the container. There is no checking at compile-time of the validity of the pointer and the associated data. There is no
checking at run time either since the software has no information to check something.

A templated generic approach where at compile time a templated file is specialized for a concrete type by writing a parameter file
where some macros are defined that are used by the templated file as arguments. The user of the library is required to write that file, compile
it, and adding the resulting object files to a user-specific library (or to the general containers library if he/she wishes) so that the linker finds
them when needed.

The functions to call, their names, etc, are the same in both approaches with one important exception: the templated approach needs an object instead
of a void * to the data as parameter, and returns an object instead of a void pointer to the data.

Each container is defined by its interface, i.e. the table of functions it supports. For each interface, its name is composed of a lower case ï" followed by the container name: iList, iVector, istrCollection, etc.

Each function of the interface receives always the container as its first argument. Obviously, the big exception is the creation function, that receives various arguments depending of which container or from what input, the container is to be created.

For each container interface a global object exists that allows direct access to the function table without the need of creating a container to access it.

This interface allows for simple access to each container using a very similar vocabulary:

iList.Add(list,object);
istrCollection.Add(strcol,object);

The objects stored in a container have always the same size. When storing objects of different sizes just store a pointer to the objects, since pointers have always the same size.

This specification describes the basic error handling that each function of the library must do. Other errors can appear in different implementations.

Error handling has three different phases:

Detection. All library functions detect blatantly wrong arguments, for instance a NULL pointer when an object is expected, or arguments out of their valid range, etc.

Reporting. When an error is detected the library calls the corresponding error function that receives a character string with the name of the
function where the error was detected, and an integer error code. Error codes are always negative constants.

Response. The library's default response to an error is to print it in the standard error stream. This can be modified by the user at any time by calling the SetErrorFunction API, replacing the default behavior with its own.

At each error, the library should call the container instance specific error handling when there is one, or call the general error handling function in the iError interface. When it is not possible to call the instance specific error function, for instance when the instance parameter is NULL , the library calls the general error handling function in the iError interface5.

The user of the library can either replace the default iError interface with a function that handles the error with a jump to a previously set recovery point, or treat the error locally using the return code.

All errors are negative constants, it suffices to test if the result is less than zero. In general we have:

In the positive return code some implementations may encode additional information. The sample implementation returns always 1
6. The zero return value means that nothing was done: it isn't an error but the container was empty for instance, or nothing was written to a
file, etc.

The error codes defined by this specification are:

CONTAINER_ERROR_BADARG
One of the parameters passed to a function is invalid. This is the same as the EDOM error code used by the function errno. If an implementation uses the errno mechanism it can set at each occurrence of this error also errno to EDOM.

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation7.

CONTAINER_ERROR_INDEX
The index is out of bounds. If an implementation uses the errno mechanism it can set errno to ERANGE.
The library passes extra parameters when this error is invoked: the container pointer, and a size_t containing the
the out of bounds index.

CONTAINER_ERROR_READONLY
The object is read-only and the operation would modify it 8.

CONTAINER_ERROR_INTERNAL
Unspecified error provoked by a problem in the implementation.

CONTAINER_ERROR_OBJECT_CHANGED
A change in the underlying object has invalidated an iterator. If an implementation uses errno it can set
errno to EILSEQ9.

CONTAINER_ERROR_CONTAINER_FULL
Implementations can limit the maximum number of elements a container can hold. This error indicates that the limit is reached.
11.

CONTAINER_ERROR_BADPOINTER
The debug implementation of free() has discovered an incorrect pointer attempting to be freed12.

CONTAINER_ERROR_BUFFEROVERFLOW
The debug implementation of free() discovered a buffer overflow.

CONTAINER_ERROR_WRONGFILE
You are trying to read a container from a stream that has no such container saved
13.

CONTAINER_ERROR_DIVISION_BY_ZERO
The library has detected an attempt to divide by zero14.

CONTAINER_ERROR_OVERFLOW
An overflow was detected in an arithmetic operation. Implementations are encouraged to detect overflow in all operations that
can generate one and report it through this error.

CONTAINER_ERROR_BADMASK
The mask given to a Select or SelectCopy operation is of a different length than the length of the associated
container. The library passes two pointers to the error function: The first to the container and the second to the mask.

CONTAINER_ERROR_NOENT
The library wants to open a file that doesn't exist or is not readable. A pointer to the name of the file is passed to the error function
15.

Other errors can be defined by each implementation.

The treatment of each error is done in the object defined by the iError interface.

All APIs of the library begin with the composite name formed by the letter i, followed by the element type name in the templated versions,
then the container name. This builds the interface name, i.e. the name of the function table object. This object is indexed by the name of the specific API being called.

For calling the Add API of the List container you would write:

iList.Add. This uses the generic implementation with void *.

For calling the Add API of the Vector container built from double objects you would write:

idoubleVector.Add. This uses the template implementation with a specific type, in this case the double type.

The interface object idoubleVector has one field for each API it supports. The same for the interface object iVector that is
a generic interface objects (uses and returns void * instead of specific types).

Other containers like the list container would have the equivalent API named as:

idoubleList.Add, or iList.Add.

The following drawing tries to make this clear:

The different containers built from a basic container (say List) are named by concatenating the name of the type and the name of the container:
intList, doubleList etc.

All data structures in this section are known and used for several decades. Lists are a common feature of any data processing task since the sixties for instance.
The library provides for abstract containers, and some examples of concrete ones for the elementary types.
We have:

Vectors. The general abstract vector container is implemented in the "Vector" container. This is a flexible array that allows for insertion/deletions,
with no cost for insertion at the end in most cases. Concrete implementations for the elementary types are provided for bits (bit-strings), strings
(null terminated), int/double/long double numeric data in the form of templates.

Lists. Single linked lists (List) and double linked lists (Dlist) are provided. Lists of strings and wide character strings are specified too.

Queue, Deque, Priority Queue

Trees (red/black trees, AVL trees)

Dictionary. This is a simple implementation of a hash table with character keys. It comes in two flavours16
:

This containers consist of a header and a list of elements containing each a pointer to the next element in the chain, and a pointer to the data item
stored. The end of the list is marked by a node that contains a NULL "next" pointer. Double linked lists contain an additional pointer to the previous
element.

This is a very flexible container, allowing you to add and delete elements easily just by rewriting some pointers. You can even split them in two
sublists just by zeroing somewhere the "next" pointer.

The price you pay for this flexibility is that sequential access is expensive, the cost of accessing the nth element increases linearly with n.

Storage overhead is one or two pointers per element stored in the list for single/double linked lists..

The data is stored directly after the pointer, there is no pointer to the data. This is a variable length structure with a fixed and a variable part.
To avoid using a standard C99 feature that could be absent in older compilers, we use a semi-generic pointer indexed either by one (for older compilers) or by nothing (standard C).

A specialization of the single linked list is provided for multibyte or wide character strings. The rationale for this specialization is that
zero terminated strings are variable length records what would make them impossible to store into a standard list that needs records of the
same size.

This container is an array with added operations that allow the user to insert and delete elements easily. It will resize itself if needed.

The access time is essentially the same as with a normal array. Insertion and deletion are possible but they are in general more expensive than with lists since the container must copy the elements to make place for a new element or to delete an element. An exception to this rule is the deletion of the last element that will be done in constant time since it implies only decrementing the number of elements in the container.

The storage overhead for each element is zero since this container doesn't require any pointers per object stored.

This container uses a reserve storage to avoid allocating new memory for each addition operation. This allows the Add operation to be done in constant time in most occasions.

Comparing vectors with plain arrays, there are following points to be made:

With plain arrays, a program cannot determine the array's capacity, which is to say, its dimension when it was allocated.
The program code must supply this information independently, and must maintain that information always current.

There isn't any simple way to increase the size of our array, once it’s been allocated. We often need to do that, rather than try to figure out
in advance how large it should be.

When accessing the array there is no automatic way to check if the index is within bounds. We have to program all array access specially
if we want to make sure there are no index errors.

This group is an specialization of flexible array. It features objects that contain numbers in different formats designed to facilitate operations
in numerical programming. There are ValArrays for the types short, int, long, float, double,
long double, size_t and long long. Each ValArray has the same basic operations (addition, subtraction, etc)
but some members have specialized operations: trigonometric operations are defined only in floating point ValArrays, boolean operations only in the unsigned versions of the int/short and the long long types.

ValArray functions come often in two flavors: The first uses two arrays where the left argument is both source and destination, and a second form where
a number is applied to the whole array. For instance we have AddTo(leftArray,rightArray) and AddToScalar(Array,number).

This container is designed to handle a collection of C strings. It is essentially an application of the flexible array container with some extra functionality to handle strings. It comes in two flavors, as strings in C: multi-byte and wide character strings.

This container is designed to handle arbitrary sequences of bits. Some algorithms that are easy to program with strings are much more complicated for bit-strings, like to one that mimics ßtrstr" ("bit-strstr").

The bits are packed with 8 bits per character unit. The overhead per bit is the size of the bit-string header only. No pointers are associated with each bit.

This is an associative container based on a hash table. It associates a text key with some arbitrary data. This container is not ordered. Access time to each element depends on how much elements are stored in it and on the efficacy of the hash function to maintain elements in different slots. Storage overhead per element is one pointer each, plus the size of the slot table. This is for a hash table with linked lists in each slot for managing collisions. Other implementations exist of course.

This is a probabilistic data structure used to quickly check if an element is not in a larger set of elements. It returns false positives with a given probability set when the container is built. Elements can be added to it but they can't be removed from the container. It stores no data, just a key.

Queues are designed to operate in a FIFO context (first-in first-out), where elements are inserted into one end of the container and extracted from the other. This container can be implemented as an adaptor using a single linked list as its base container. The sample implementation uses this strategy to show how adapters can look like. Other implementations can implement this container directly presenting the same interface.

Buffers are containers used to hold data temporarily, either to be transmitted or stored into some medium, or to be filtered and used later
by other parts of the application. The library provides two types of buffers:

Stream buffers. They are a linear sequence of bytes, like a file. They resize automatically if they need to, and they have a cursor
that points to the position where the next item will be stored.

Circular buffers. They store the last n items of a stream. They can contain any item as in the vector container, or they can contain
character strings, as in the string collection.

This container stores data associated with an integer "priority". The meaning of this integer is not further specified and defined by the application.
The Pop operation retrieves the data associated with the lowest priority.

This structure will be passed to the comparison functions. The ExtraArgs pointer will receive the pointer that was passed to the calling function. If both elements being compared are members of a single container, the ContainerRight member will be NULL .

This type defines the function used to compare two elements.
The result should be less than zero if elem1 is less than elem2, zero if they are equal, and bigger than zero if elem1 is bigger than element 2.

The default comparison function is memcmp. This function will compare all the object's area, including eventually padding bytes added by
compilers for alignment reasons. To minimize this problem always zero the objects before assigning the values. For instance:

This function should save the given element into the given stream. The ExtraArg argument receives any argument passed to the Save function.
The result should be bigger than zero if the operation completed successfully, zero or less than zero otherwise.

What the save function does is unknown to the rest of the library, basically the only requirement is that its output should be understood by the
read function, when called to read each element. The size of the written data can be bigger (or smaller) than the size of each stored element,
according to the specific requirements of the application data. This allows to write save functions tha would write a complete data set, including
embedded objects into the stream. For instance a save function can save the contents pointed by a character pointer instead of the pointer value
that would be meaningless when read again.

The default save function provided by the implementation just writes the contents of each element into the stream. Embedded pointers aren't followed.

This function should read into the given element from the given stream. The ExtraArg argument is passed to the container read function and allows to pass an argument to the user defined save function. The amount of data read is unknown to the rest of the software and the only requirement
is that it should reverse the work of the save function.

The default read function provided by the implementation should read the contents of one element from the stream and add it to the result container.

The result is bigger than zero if the operation completed successfully, zero or less than zero otherwise.

This function type is used to handle errors in each container. The first argument is the name of the function where the error occurred, the second is a
negative error code. No checks are performed on the function name argument, and other information or messages could be included in the message.

Note that this function is roughly compatible with the prototype of snprintf, and could be used with a format string, a buffer size, and a series of
arguments corresponding to the arguments the format requires. The only problem is the conversion between int and size_t.

The result value of the default error function is always NULL . This result will be in most cases ignored, except in the vector container where
it can be used to return special values in the case of the INDEX error.

When iVector interface detects an index error, it will pass in the extra arguments the array pointer and a size_t containing
the out of bounds index.

This function is called when an object is being destroyed. An object is destroyed when:

An Erase call is done.

A Replace call is done.

The Clear call is done.

The Select call is done with some element of the mask to zero.

The Resize function is called with an argument less than the size of the container.

This function should free any memory used by pointers within the object without freeing the object memory itself. In most cases the memory
used by the library is not allocated with malloc. Its result type is less than zero when an error occurred or greater than zero when
it finished successfully. It is implementation defined what happens if a destructor encounters an error.

The library uses always the same words to represent similar actions in all containers. In addition, each container can use specific words to name actions that are specific to it. In this section only the common actions will be documented, to give an overview of the common vocabulary available.

At the end of this documentation you will find a complete table that lists each action supported by the library and marks which container supports it.

Containers are created with a call to their "Create" function. The first argument is the size of the objects that will be stored in the container. The second is optional and is a hint to the number of elements that will be stored in the container.
Note that if you want to store objects of different sizes you just store a pointer to those objects instead of the objects themselves.
The creation functions can have several arguments, the first being always the size of the elements that the container will hold. The prototype can be:

Container * iContainer.Create(size_t elementsize,...);

The creation function needs to allocate memory to hold the container. This memory will be allocated using the current memory manager that is always an implicit argument to all creation functions. The rationale behind this design decision is that you don't change your memory allocation strategy at each call to a container creation function. This simplifies the interface at the expense of making the change of allocation strategy more expensive.

There is an abstract class of objects called "Generic container" that has all functions that are common to all containers. This is an abstraction,
and as such, it can't have any concrete examples: there is no creation function for a generic container. You can only create a concrete container, a list, a vector, etc.

Once created, and if the created container supports the generic interface, you can make a cast and treat the concrete container as an abstract member
of a mythical "generic" container. This can save you a lot of redundant code since your code is independent of the type of container and will run
with any object (even future objects) that support the generic container interface.

As everything, there is no free lunch. Precisely because of its generality the generic interface is missing a lot of functionality that you will find
in the concrete containers interfaces.

An implicit argument to all the creation functions is the current allocator, that is used to retrieve space for the container being built. To avoid changing the current allocator, what in multi-threaded environment would need acquiring a lock to that global variable, some containers support a creation function that receives an extra argument: a custom allocator.

Sometimes it can be useful for some containers (specially lists) to create the header structure using an already existing space, for instance in the space for local variables. For this an 'Init' function can exist, that initializes a container within an existing space.
Since normally the detailed structure (and the size of course) of each container header is implementation dependent, you use the Sizeof function with an argument of NULL to get the size of the header. This can be used within a C99 compiler environment to allocate the space for that variable.
17
The declaration of the container header in C99 would be:

If C99 is not available, the best way is to just print the size of the container you are interested in, and then use that value that should stay fixed
for a given version. This can be automated and you can find in the Appendix 1, a small program that generates a series of #defines with the
values of the sizes of the containers described in this documentation
18.

Initializing with existing data
All containers support the InitializeWith function. It will create a container using a table of elements to store. Its arguments are the size of
the objects to be stored, the number of those objects, and a pointer to the table. The table should be a contiguous memory area.

These functions copy the contents of the last or the first element into the given buffer. If the buffer is NULL, nothing is copied, but the
container is modified: the first or the last element is removed.

Several elements can be removed at once from a container using the RemoveRange function. This function is implemented in sequential
containers only.

int iContainer.RemoveRange(Container *c,size_t start, size_t end);

This function removes the elements whose index is greater or equal than start and less than end. If start equals end
nothing is removed and the result is zero. If end is greater than the length of the container it will be adjusted to 1 element past the
end. If no elements are removed the result is zero, positive when one or more elements are removed. The result is a negative error code when an
error occurs.

These functions return a pointer to the requested element or NULL if the element can't be retrieved. The resulting pointer points directly to the data
stored in the container. This could be used to bypass all the flags that control the access to the container. For read-only containers, use the
CopyElement function that returns a copy of the requested data into a buffer.

The function GetRange retrieves a slice of a container returning a container of the same type with a copy of the elements storeds in the
given range:

The pointer returned can be invalidated by some operations done to the container. For instance if you reverse the order of the elements in a container,
a pointer to the element zero will point to something else than when you obtained it. If a container needs reallocating its data space because you
added an element, all the pointers that point to data elements of the container can be invalidated. In general it is a bad idea to keep pointers
to elements in a container that is being modified

A mask is a sequence of boolean elements that contains zeroes or some value different from zero. They are used to select elements from a sequential container: for each element of the mask different from zero the corresponding value is selected, for all elements that contain a zero, that value is eliminated.

The selection operation can be destructive, reusing a container by eliminating from it all elements not selected by the mask, or can be a copy operation where the copy contains only the selected elements.

In the case of a destructive operation, the destructor functions are called for each element destroyed.

The functions CompareEqual and CompareEqualScalar produce a mask using two containers as input. Those masks can be used with the selection functions. It is possible of course, to create masks from completely different sources, either directly or by copying. See the iMask interface for further details.

The CompareEqual function compares two sequential containers of the same type and length, producing a mask containing a value different from zero at each element position where the pair of elements from each container is equal, zero otherwise.

The CompareEqualScalar function compares each element of the given sequential container with a single elemnt, producing a mask with a value
different from zero20 for each element that is equal to the given element.

The "Copy" function will make a fresh copy of a container. Some fields of the header are copied: the error and compare functions, the flags, and others. Memory will be allocated withe the source container allocator.

The functions "Save" and "Load" will save / load the contents, state, and characteristics of a container into / from disk. They need an open file stream, open in binary mode, and in the correct direction: saving needs a stream open in the write direction, loading needs a stream open in the read direction.

The InsertIn function will insert into the "destination" container the contents of the source container at the given position. The source is not modified in any way, and a copy of its data will be used. Both containers must be of the same type and store elements of the same type. The library only tests the element size of each one.

The Append function is similar to InsertIn: the elements of the second container are appended at the end of the first one. The big difference
is that the second container is destroyed. It is absorbed into the first: its elements are not copied but inserted. This means that a requirement is
that the allocator be the same in both containers.

This function will insert into the destination container the source container using the source container keys. Otherwise the same conditions apply as to the sequential containers: the containers must be of the same type and store elements of the same type.

One the most familiar design patterns is the ITERATOR pattern, which ‘provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Traditionally, this is achieved by identifying an ITERATOR interface that presents operations to initialize an iteration, to access the current element, to advance to the next element, and to test for completion; collection objects are expected to implement this interface, usually indirectly via an auxiliary object.

This is exactly the case in the iterator proposal here. Essential to the pattern is the idea that elements are accessed sequentially, but independently of their position in the collection; for example, labeling each element of a tree with its index in left-to-right order fits the pattern, but labeling each element with its depth does not. This traditional version of the pattern is sometimes called an EXTERNAL ITERATOR.

An alternative INTERNAL ITERATOR approach assigns responsibility for managing the traversal to the collection instead of the client: the client needs only to provide an operation, which the collection applies to each of its elements. The latter approach is simpler to use, but less flexible; for example, it is not possible for the iteration to affect the order in which elements are accessed, nor to terminate the iteration early. This is the algorithm followed by the Apply function.

You can iterate any sequential container with a simple loop. You use the "Size" function to limit the loop. At each loop step you get the corresponding
element with the "GetElement" function, present in this form in all containers.

For associative containers you retrieve first a strCollection containing all keys using the GetKeys function, present in all associative
containers. Then, you retrieve each element by looping through the string collection that you have obtained in a similar manner to the sequential
containers.

A function pointer that should point to a function that receives two arguments: the element of the container, and an extra argument where it can
receive (and write to) global information about the search. This extra argument is

The third one passed to the Apply function. Apply will pass this argument to the given function together with a pointer to the
element retrieved from the container.

Iterators provide a container-independent way of iterating that will work with any container, both sequential or associative. In associative containers
the specific sequence is implementation defined, and in sequential containers is the natural sequence.

The main objective for iterators is to break a dependence between an algorithm and the type of container it is working with. Since all containers
support iterators, you can write your code independently (to a great extent) from which specific iterator you are using.

Invariants:
The input container could be modified in some implementations. A list of existing iterators is possible, to allow invalidating them in case of
modifications to the container for example.

This interface allows users to write fully general algorithms that will work with any container, independently of its internal structure. Obviously the
performance can differ from container to container depending on usage.

All iterators will become invalid if the underlying container changes in any way, except through the iterator itself.23
Each container can conceptually be seen as a sequence of generations, or states. Beginning with the fresh constructed state, the container
evolves until it reaches the destroyed state after the execution of the Finalize function. This sequence of states interacts with an
iterator as follows: An iterator applies only to a single container state. Any modification of the container state, directly or
indirectly moves the state and invalidates the iterator.

An implementation may catch some of the movements of the container in the state space and report an error when an iterator is used that belongs to
a different container state. But not all access can be catched. If the user has pointers to an iterator's data and modifies this data without
using the container API an implementation may not catch this error.

Each container has a set of flags that can be read and written to change the container's behavior. The only flag that is defined by all containers
is the read-only flag. Implementations can extend this to offer different services like copy-on-write, or other applications.

The read-only flag means that no direct pointers to an element or to the whole data are returned, no functions that modify the
container are allowed to proceed and that the Clear()
and Finalize() APIs will not work. You must unset this flag to allow for destruction of the object.
24

Using the state space concept introduced above, this flag freezes the state of a container disallowing any further evolution. The only API that
can modify the state is the SetFlags API that can reset the state to a read/write state again.

Computes the size of the iterator for the given container. The objective here is to allow the declaration of the iterator as a local variable
to avoid having to free the iterator at the exit of the function.

All containers have a pointer to their allocator object. An allocator object is a simple interface that provides 4 functions:

malloc: A function that receives a size_t and returns a void * pointing to a memory block of the requested size, or NULL if no more memory is available. Note that this function receives the number of bytes to allocate, not the number of items.25

realloc: A function that will resize a previously allocated block.

free: A function that will release the memory allocated previously with malloc/realloc.

calloc: a function that will allocate n objects of m size and clear the memory block to zero before returning it.

At the start of the library runtime a default allocator object exists that uses the four functions of the standard C library. Other allocator objects can be used, and the user can change the global allocator at any time. Each container retrieves the default allocator object when created, and stores it in the container descriptor. Any further change to the default allocator will not affect existing containers that have already an allocator. When changing the allocator you should do that before creating the container.

Some containers are created without any heap management by default. You can introduce heap management by calling the UseHeap function, that will install a new heap in the container. Other containers are always created with a heap, and you should pass them an allocator object for object creation.

The problem with the traditional C memory management is that it requires that the programmer cares about each piece of RAM that is allocated by the program and follows the lifetime of each piece to ensure that it gets returned to the system for reuse. In today's software world, this is just impractical.

A better strategy is to use a pool of memory where related memory allocations can b e done from a common pool. When the module finishes, all the allocated pool is freed just by destroying the whole pool. This is much easier to manage, and in many cases more efficient.
The proposed interface has the following functionalities:

Creation. The creation function receives a memory allocator to use for this pool.

Alloc. This function receives a pool and a size and returns a memory block, or NULL if there is no more memory.

Clear. This erases all objects allocated in the pool without returning the memory to the system.

Finalize. This releases all memory and destroys all objects.

Note that there is no realloc, and that the "Clear" function is optional. Not all pools support it. The rationale for these decisions being that realloc would need to store the size of each block, what in a pool maintained by a single stack like pointer would be very expensive.

Many containers are used to store sets of objects of the same size. The library provides a specialized heap management software for this application. It stores vectors of objects of the same size. The interface provided is as follows:

Create. This function receives as an argument a memory manager object that will be used to allocate memory.

Automatic garbage collection is offered by some compiler systems as an alternative to traditional memory management. This solution is
not compatible with real time requirements, and is not practical in machines with very low memory configurations.

In other cases however, it can be a real simplification since the programmer is relieved from the huge task of taking care of each
piece of memory and to cater its disposal. A simple memory model is proposed: you program as if the amount of memory was infinite
and never worry about freeing the memory you use. Periodically the collector starts collecting unused memory chunks and adds them
to the pool of available memory or releases it to the underlying operating system.

This model is not the solution to all memory management problems. It can be a solution to some situations, specially when developing
in workstation environments where memory is freely available. The bugs that can appear are also very difficult to solve. One of the
most difficult is when you keep by mistake some reference to a large piece of memory making the recycling of the memory impossible.
In that case you have to search in all the code of the application for the reference that keeps the memory block marked as used, and that can
be very difficult in large applications.

In environments where multi-threading or other parallel programming constructs are possible, the implementation must provide for sequential
semantics, i.e. each operation should perform as described in this documentation with the additional caveat that any operation that modifies
a container must be atomic, i.e. it can't be interrupted leaving the container in an unstable or incoherent state. It is up to the
implementation to ensure that if an atomic operation is interrupted, the inconsistent container state will be invisible to other processes
or threads accessing the container.

A mask is a sequence that contains boolean data used for selection of items in a sequential container.
It is not specified if a mask is a bit string (i.e. a
strictly boolean array) or an array of chars or other integers used to hold the binary data. In all cases a value of the mask at a given position
means select if it is different than zero, or do not select if it is zero.

The interface offered by the mask object is very small. Masks can't be resized but they have an allocator to be able to reclaim the
memory they use when created. This allocator will be initialized to the current allocator when the mask is created.

Description:
Stores into src1 the result of a logical AND operation between each element of src1 with the corresponding element of src2.

Errors:

CONTAINER_ERROR_BADARG
Any mask pointer is NULL .

CONTAINER_ERROR_INCOMPATIBLE
The masks are of different length.

Returns:
A positive number if the operation was performed, a negative error code if an error occurs.

Clear

int (*Clear)(Mask *m);

Description:
Sets all elements of the mask to zero.

Errors:

CONTAINER_ERROR_BADARG
The mask pointer is NULL .

Returns:
A positive number if the mask was cleared, a negative error code if an error occurs.

Copy

Mask *(*Copy)(Mask *src);

Description:
Allocates a new mask and copies the contents of the given one into it.

Errors:

CONTAINER_ERROR_BADARG
The mask pointer is NULL .

Returns:
A pointer to the new mask or NULL if an error occurs.

CreateFromMask

Mask *(*CreateFromMask)(size_t length,char *data);

Description:
Creates a new mask with the specified length and copies the given data into the mask. Each character in the input data is transformed into the
mask internal representation. The storage is obtained using the CurrentAllocator pointer.

Errors:

CONTAINER_ERROR_BADARG
The data pointer is NULL

CONTAINER_ERROR_NOMEMORY
No memory is available to perform the allocation.

Returns:A pointer to a new mask or NULL if an error occurs.

Create

Mask *(*Create)(size_t length);

Description:
Creates a new mask with the specified length. The storage is obtained using the CurrentAllocator pointer. The data is initialized to zero.

Errors:

CONTAINER_ERROR_NOMEMORY
No memory is available to perform the allocation.

Returns:A pointer to a new mask or NULL if an error occurs.

Finalize

int (*Finalize)(Mask *m);

Description:
The memory used by the mask is reclaimed.

Errors:

CONTAINER_ERROR_BADARG
The mask pointer is NULL .

Returns:A positive number if the memory was reclaimed, or a negative error code.

Not

int (*Not)(Mask *src);

Description:
Stores into src the result of a logical NOT operation: each bit is inverted.

Errors:

CONTAINER_ERROR_BADARG
The mask pointer is NULL .

Returns:
A positive number if the operation was performed, a negative error code if an error occurs.

Or

int (*Or)(Mask *src1,Mask *src2);

Description:
Stores into src1 the result of a logical OR operation between each element of src1 with the corresponding element of src2.

Errors:

CONTAINER_ERROR_BADARG
Any mask pointer is NULL .

CONTAINER_ERROR_INCOMPATIBLE
The masks are of different length.

Returns:
A positive number if the operation was performed, a negative error code if an error occurs.

PopulationCount

size_t (*PopulationCount)(const Mask *m);

Description:
Counts the number of entries different from zero in the given mask, returning the sum.

Errors:

CONTAINER_ERROR_BADARG
The mask pointer is NULL .

Returns:
A positive number or zero.

Set

int (*Set)(Mask *m,size_t idx,int val);

Description:
Sets the given position to the given value if the value fits in the internal representation of the mask. If not, an implementation defined
conversion occurs.

Errors:

CONTAINER_ERROR_BADARG
The mask pointer is NULL .

CONTAINER_ERROR_INDEX
The index given is out of bounds.

Returns:A positive number if the value was set or a negative error code.

Size

size_t (*Size)(Mask *);

Description:
The number of elements in the mask is returned.

Errors:

CONTAINER_ERROR_BADARG
The mask pointer is NULL .

Returns:The number of elements. If the mask pointer is NULL , the result is zero.

Sizeof

size_t (*Sizeof)(Mask *);

Description:
The number of bytes used by the given mask. If the argument is NULL the number of bytes of the header structure is returned.

Several interfaces implement different memory allocation strategies. This should give flexibility to the implementations, allowing it to use several memory allocation strategies within the same container.

The library starts with the default memory manager, that contains pointers to the default C memory management functions: malloc, free, realloc and calloc. Another memory manager is the debug memory manager that should implement more checking and
maybe offer hooks to the debugger. The sample
implementation shows how to implement several simple checks, but other implementations can extend this simple interface providing
much more sophisticated controls26.

At startup, the CurrentAllocator points to an object constructed with the functions of the C standard library. This is a required interface.
The user can change at any time the current allocator by making the CurrentAllocator point to a different object. Note that this change
does not change the allocators of the containers already created but the allocators of the new containers allocated after the
change is made.

This is the established procedure to build custom memory allocators to provide for special alignment requirements, improve speed, allocate objects
from the stack instead of the heap, and many other usages.

The library can also include a debug version on top of the standard C functions, offering the same interface. Changing the CurrentAllocator to point to that object allows to switch to the debug version. The debug version of the sample implementation offers:

Some containers can benefit from a cacheing memory manager that manages a stock of objects of the same size. This is not required and not all implementations may provide it. If they do, the interface is:

In the sample implementation, many complex data structures are implemented using a heap. This allows automatically to have an iterator, since for
looping all elements of the container it suffices to iterate the underlying heap.
The standard interface for the heap is:

Description:
Creates a new heap object that will use the given memory manager to allocate memory. All elements will have the given size. If the memory manager object pointer is NULL , the object pointed by CurrentAllocator will be used.

Returns:a pointer to the new heap object or NULL , if an error occurred.

Errors:

CONTAINER_ERROR_BADARG
The element size is bigger than what the heap implementation can support..

CONTAINER_ERROR_NOMEMORY
Not enough memory is available to complete the operation.

Description:
Initializes the given buffer to a heap header object designed to hold objects of ElementSize bytes. The heap will use the given memory
manager. If the memory manager parameter is NULL the default memory manager is used.

This function supposes that the heap parameter points to a contiguous memory space at least enough to hold a ContainerHeap object.
The size of this object can be obtainer by using the iHeap.Size API with a NULL parameter.

Returns:
A pointer to the new ContainerHeap object or NULL if there is an error. Note that the pointer returned can be different from the passed in
pointer due to alignment requirements.

newObject

void *iHeap.newObject(ContainerHeap *heap);

Description:
The heap returns a pointer to a new object or NULL if no more memory is left.

Errors:

CONTAINER_ERROR_NOMEMORY
Not enough memory is available to complete the operation.

Returns:A pointer to an object or NULL if there is not enough memory to complete the operation.

FreeObject

size_t iHeap.FreeObject(ContainerHeap *heap,void *element);

Description:
Adds the given object to the list of free objects, allowing for recycling of memory without new allocations. The element pointer can be NULL .

Errors:

CONTAINER_ERROR_BADARG
The heap pointer is NULL .

Returns:The number of objects in the free list.

Clear

void iHeap.Clear(ContainerHeap *heap);

Description:
Releases all memory used by the free list and resets the heap object to its state as it was when created.

Errors:

CONTAINER_ERROR_BADARG
The heap pointer is NULL .

Finalize

void iHeap.Finalize(ContainerHeap *heap);

Description:
Destroys all memory used by the indicated heap and frees the heap object itself.

Errors:

CONTAINER_ERROR_BADARG
The heap pointer is NULL .

Sizeof

size_t iHeap.Sizeof(ContainerHeap *heap);

Description:
Returns the number of bytes used by the given heap, including the size of the free list. If the argument "heap" is NULL , the result is the size of the heap header structure (i.e. sizeof(ContainerHeap).

This example uses the variable length arrays that have been introduced in the C language by the latest standard (C99). The Sizeof function
returns the size of the header object that is used to specify the size of the buffer. The buffer is passed to the InitHeap function using
a number of objects of 200 and the default memory allocator.

Many containers could benefit from a memory pool. A memory pool groups all allocations done in a specific context and can be released in a single call. This allows the programmer to avoid having to manage each single piece of memory like the basic interface.

Note that there is no realloc function. Pooled memory is often implemented without storing the size of the block to cut overhead. Since a realloc function could be expensive, implementations are not required to provide it.

Create

Pool *iPool.Create(ContainerAllocator *m);

Description:
Creates a new pool object that will use the given memory manager. If m is null, the object pointed by the CurrentAllocator will be used.

Errors:

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A pointer to the new object or NULL if the operation couldn't be completed.

Alloc

void *iPool.Alloc(Pool *pool,size_t size);

Description: Allocates size bytes from the pool pool. If there isn't enough memory to resize the pool the result is NULL .

Errors:

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A pointer to the allocated memory or NULL if error.

Calloc

void *iPool.Calloc(Pool *pool,size_t n,size_t size);

Description:
Allocates n objects of size ßize" in a single block. All memory is initialized to zero. If there is no memory left it returns NULL ;

Errors:

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A pointer to the allocated memory or NULL if error.

Clear

void iPool.Clear(Pool *);

Description:
Reclaims all memory used by the pool and leaves the object as it was when created.

Errors:

CONTAINER_ERROR_BADARG
The pool pointer is NULL .

Finalize

void iPool.Finalize(Pool *);

Description:
Reclaims all memory used by the pool and destroys the pool object itself.

The ïError" interface provides a default strategy for handling errors. The "RaiseError" function will be used as the default error function within the creation function for all containers that support a per container instance error function.

Description:
The parameter "fname" should be the name of the function where the error occurs. The ërrcode" parameter is a negative error code. The actual value of the code is defined for the cases mentioned in the section 2.4.1Return code. Other negative values can be defined by the implementation.

Other parameters can be passed depending on the error. The sample implementation never passes anything else but the name of the function where the
error occurs and the error code.

The behavior of the default error function is implementation specific. In the sample code this function will just print the error message in the standard error stream. Other implementations could end the program, log the error into a error stream, or do nothing.

Returns:No return value

EmptyErrorFunction

void iError.EmptyErrorFunction(const char *fname,int errcode,...);

Description:
This function can be used to ignore all errors within the library. It does nothing.

StrError

const char *iError.StrError(int errorCode);

Description:
Converts the given error code in a character string. If the error code doesn't correspond to any error defined by the implementation a character string
with an implementation defined value is returned.

SetErrorFunction

ErrorFunction iError.SetErrorFunction(ErrorFunction);

Description:
Changes the value of the default error function. If its argument is NULL , nothing is done, and the call is interpreted as a query of the current value.

The iterator object exposes at least the functions "GetFirst", for initializing the loop, and "GetNext", for getting the next element in the sequence.
The functions "NewIterator" and "deleteIterator" are specific to each container interface even if they all have the same syntax.

Returns:
A pointer to the current element or NULL , if the container is empty or an error occurs. If the container is read-only, a pointer to a copy of the element is returned. This pointer is valid only until the next iterator function is called.

GetFirst

void *(*GetFirst)(Iterator *);

Description:
This function initializes the given iterator to the first element in the container. For sequential operators this is the element with index zero. In
associative operators which element is the first is implementation defined and can change if elements are added or removed from the container.

If the container is empty the result is NULL .

Errors:

CONTAINER_ERROR_BADARG
The iterator pointer is NULL .

Returns:
A pointer to the first element or NULL , if the container is empty or an error occurs. If the container is read-only, a pointer to a copy of the element
is returned. This pointer is valid only until the next iterator function is called.

Description:
Positions de cursor at the next element and returns a pointer to its contents. If the iterator is at the end of the container the result is NULL
and the iterator remains at the last position, a subsequent call to GetCurrent returns the last element.

If the container is read-only, a pointer to a copy of the object is returned. This pointer is valid only until the next iterator function is called.

Errors:

CONTAINER_ERROR_BADARG
The iterator pointer is NULL .

CONTAINER_ERROR_OBJECT_CHANGED
The container has been modified and the iterator is invalid. Further calls always return NULL .

Returns:
A pointer to the next element or NULL , if the cursor reaches the last element. If the container is read-only, a pointer to a copy of the element is
returned, valid until the next element is retrieved

GetPrevious

void *(*GetPrevious)(Iterator *);

Description:
Positions de cursor at the previous element and returns a pointer to its contents. If the pointer is at the beginning of the container the
result is NULL and the iterator remains at the beginning, a subsequent call to GetCurrent will return the first element of the container.

This function is meaningful only in sequential containers. Its existence in associative containers is implementation defined. Even in sequential
containers, it can be very expensive to find a previous element, for instance in single linked lists. In those cases it can always return NULL .

Errors:

CONTAINER_ERROR_BADARG
The iterator pointer is NULL .

CONTAINER_ERROR_OBJECT_CHANGED
The container has been modified and the iterator is invalid. Further calls always return NULL .

Returns:
A pointer to the previous element or NULL , if the cursor reached the first element already. If the container is read-only, a pointer to a copy of the
element is returned.

Description:
Positions the cursor at the last element and returns a pointer to it. Returns NULL if the container is empty. If the container is read-only, a pointer
to a copy of the element is returned.

This function is meaningful only in sequential containers. Its existence in associative containers is implementation defined. Even in sequential
containers, it can be very expensive to find the last element, for instance in single linked lists. In those cases it can always return NULL .

Errors:

CONTAINER_ERROR_BADARG
The iterator pointer is NULL .

CONTAINER_ERROR_OBJECT_CHANGED
The container has been modified and the iterator is invalid. Further calls always return NULL .

Seek

void *(*Seek)(Iterator *it,size_t pos);

Description:
Positions the given iterator at the indicated position and then returns a pointer to the element's data at that position.
If the position is bigger than the last element of the container, the last element position will be used.

This function is supported in sequential containers only.

Errors:

CONTAINER_ERROR_BADARG
The iterator pointer is NULL .

CONTAINER_ERROR_OBJECT_CHANGED
The container has been modified and the iterator is invalid. Further calls always return NULL .

Returns:
A pointer to the data of the given element or NULL if an error occurs.

Replace

int (*Replace)(Iterator *it,void *data, int direction);

Description:
Replaces the current object pointed by the given iterator with the new data. If the
data
argument is NULL the element is erased from the
container. If the
direction
parameter is different from zero, in sequential containers the iterator will point to the next element,
otherwise it will point to the previous element. In associative containers this parameter is ignored and the iterator is always set to the next
element, if any.

Errors:

CONTAINER_ERROR_BADARG
The iterator pointer is NULL .

CONTAINER_ERROR_OBJECT_CHANGED
The container has been modified and the iterator is invalid. Further calls always return NULL .

CONTAINER_ERROR_READONLY
The container is read only.

Returns:A positive value if the element was changed or erased, zero if the container was empty, or a negative error code if an error occurred.

In its general form, the observer design pattern can be defined as a one-to-many dependency between objects so that when one object
changes state, all its dependents are notified and updated automatically.

When a container changes its state, specifically when elements are added or removed, it is sometimes necessary to update relationships that
can be very complex.
The observer interface is designed to simplify this operation by allowing the container to emit notifications to other objects that have
previously manifested interest in receiving them by subscribing to them. In general notifications are sent only when one of the defined
operations for a container occur, mostly operations that change the number of elements.

This interface then, establishes a relationship between two software entities:

The container, that is responsible for sending the notifications when appropriate

The receiver, that is an unspecified object represented by its callback function that is called when a change occurs that matches the
notifications specified in the subscription.

Since this relationship needs both objects, it will be finished when either object goes out of scope or breaks the relationship for whatever
reason. Both objects can unsubscribe (terminate) their relationship.

It is in general a bad idea to modify the object being observed during a notification since this could trigger other notification
messages. Implementations are not required to avoid this situation that is the responsability of the programmer. Contrary to the iterator interface
no error is issued when a possible infinite loop is started. Implementations may catch the error by limiting the number of recursive
invocations of this interface but they are not required to do so.

Since all messages sent by the containers have different type of information in the same two arguments that each message is associated with,
there is no possible compile time control of the usage of the received pointers or numbers. The observer function must correctly
discriminate between the different messages it can receive27.

Description:
This function will be called by the interface when a notification is received for an observed object. The call happens after all arguments have been processed, the actual work of the function is finished (when adding an object) or not yet done (when destroying an object).
The container is in a consistent state. For the callbacks that are called when an object is deleted from a
container the call happens before any call to free() and before any call to a destructor (if any) is done. For the calls that add an object
the callback is called after the container has been modified.

Arguments:

ObservedObject: Specifies the object that sends the notification, i.e. the container
that has the subscription. It is assumed that this container conforms to the iGeneric interface.

Operation: The operation that provoked the notification. Since it is possible to subscribe to several operations with only one callback function,
this argument allows the callback to discriminate between the operation notifications.

ExtraInfo: This argument is specific to each operation and conveys further information28 for each operation.

Description:
This function establishes the relationship between the observed object (argument 1) and the observer, represented by its callback (argument 2).
The third argument establishes which operations are to be observed.
This operation performs an allocation to register the relationship in the observer interface tables, therefore it can fail with an out of memory condition.

Errors:

CONTAINER_ERROR_BADARG
The observed object pointer is NULL , the callback function pointer is NULL , or the operations argument is zero.

CONTAINER_ERROR_NOMEMORY
There is not enough memory to proceed.

Returns:An integer greater than zero if the relationship was established, a negative error code otherwise.

Description:
This function will be used by the container to send a message to the receiver callback. The arguments correspond roughly to the arguments the callback
function will receive. "Notify" will call all the objects that are observing ObservedObject and that have subscribed to one of the
operations
specified in the Operation argument. This implies a search through the observer interface table, and possibly several calls, making
this function quite expensive. The time needed is roughly proportional to the number of registered callbacks and the complexity of the callbacks
themselves.

Errors:

CONTAINER_ERROR_BADARG
The ObservedObject pointer is NULL or the Operation argument is zero.

Returns:A positive number with the number of objects that received the notifications, zero if there was no match for the combination of observed object and operations specified, or a negative error code.

Description:
This function breaks the relationship between the observed object and the observer. There are several combinations of both arguments:

The ObservedObject argument is NULL . This means that the callback object wants to break its relationship to all objects it is
observing. The observer interface will remove all relationships that contain this callback from its tables.

The callback argument is NULL . This means that the given ObservedObject is going out of scope and wants to break all
relationships to all its observers. The interface removes from its tables all relationships that have this object as the observed object.
This happens normally immediately after the notification FINALIZE is sent.

If both callback and ObservedObject are non NULL , only the matching relationships will be removed from the tables.

We setup our observer function calling the Subscribe API. We request to be notified when there is an addition and when the object
finalizes. Our callback function does nothing but print some of its arguments. We see that we get called when the requested operations are performed.

The space overhead of single linked lists is smaller at the expense of more difficult access to the elements. It is up to the application programmer to decide which container fits best in his/her application
29.

It is often more efficient to get the next element from a list starting with the previous element instead of searching the whole list starting from the beginning. For this, the list and the Dlist containers provide:

FirstElement Start of the list

LastElement End of the list

NextElement Returns a pointer to the next element

PreviousElement Only in double linked lists. Returns a pointer to the previous element.

ElementData Extracts a pointer to the element data

SetElementData Modifies one element of the list.

Advance Returns the data of an element and advances the given pointer in one operation.

MoveBack Returns the data of an element and moves back the pointer one element. This operation is available only in double linked lists.

These operations can't be done in a read-only list.

The exact layout of the ListElement structure is undefined and private to each implementation. This is the reason for providing the ElementData function: it hides the exact position and layout of the data from user code, that remains independent from implementation details.

The interfaces of both containers are very similar. Double linked lists support all functions in single linked ones, and add a few more. To avoid unnecessary repetition we document here all the single linked list interface, then only the functions that the Dlist interface adds to it.

Lists are containers that store each element in a sequence, unidirectionally (single linked lists) or bidirectionally (double linked lists).
The advantage of linked lists is their flexibility. You can easily and with a very low cost remove or add elements by manipulating the links between the elements. Single linked lists have less overhead than their double linked counterparts (one pointer less in each node), but they tend to use a lot of computer power when inserting elements near the end of the list: you have to follow all links from the beginning until you find the right one.

The list nodes themselves do not move around, only their links are changed. This can be important if you maintain pointers to those elements. Obviously, if you delete a node, its contents (that do not move) could be recycled to contain something else than what you expect.

The iList interface consists (as all other interfaces) of a table of function pointers. The interface describes the behavior of the List container.

The stack operations push and pop are provided with PushFront and PopFront because they have a very low cost, insertion at the start of a single linked list is very fast. PushBack is the equivalent of the Add operation, but PopBack would have a very high cost since it would need going through all the list.

The list container features in some implementations a per list error function. This is the function that will be called for any errors, except in
cases where no list object exists: the creation function, or the error of getting a NULL pointer instead of a list pointer. In those cases the general
iError interface is used, and iError.RaiseError is called. The default value of the list error function is the function iError.RaiseError at the moment
the list is created.

Other implementations of this interface may specialize list for a certain category of uses: lists of a few elements would try to reduce overhead by
eliminating a per list error function and replace it with the standard error function in iError, for instance, eliminating their fields in the header.
If the read-only flag support is dropped, the whole "Flags" field can be eliminated. In such an implementation, the SetFlags primitive would always
return an error code.

The sample implementation of the list container supports the following state flags:

#define CONTAINER_READONLY 1

If this flag is set, no modifications to the container are allowed, and the Clear and Finalize functions will not work. Only copies of the data are
handed out, no direct pointers to the data are available.

#define CONTAINER_SORTED_FRONT 2
#define CONTAINER_SORTED_BACK 4

If this flag is set, the container is maintained always in sorted order, with the biggest element at the index zero for CONTAINER_SORTED_FRONT
or with the biggest element at the end if CONTAINER_SORTED_BACK is set. It is an error if both flags are set, and the results in that case
are implementation defined.

All ßpecialized" containers share the same interface with the following exceptions:

The functions where a void * to the element data is passed or where a void * is the result of the operation are replaced with the
actual data type of the specialization. For instance the GetElement API instead of returning a void pointer returns a pointer to the specific
data type: an
integer for intList, a double for doubleList etc.

The creation and initialization functions that construct a new container receive one argument less than its generic counterparts since the
size of each element is fixed.

To make things clear and to save work from the library user some specializations are delivered with the sample implementation to show how a
file templated container looks like.

In the right side of the drawing we see the generic list container using generic pointers (void *) and the stringlist container. Strings are
special because in C their length is the result of a function call instead of being fixed like other data types.

In the left side, we see three specialized containers for some numeric data types. Those containers are generated using two types of source files:

Parameter files: They define the data type and some other parameters like the comparison expression.

Templated files: They implement the specialized container. The pre-processor does the editing work on the templated file to yield several
different type definitions. Using this interface has the advantage of ensuring compile time checking of the arguments to the API, what is not
possible using generic pointers.

Description:
Adds the given element to the container. In its generic form it is assumed that "data" points to a contiguous memory area of at least ElementSize
bytes. Inits specialized form the data is passed by value. Returns a value greater than zero if the addition of the element to the list completed
successfully, a negative error code otherwise.

Errors:

CONTAINER_ERROR_BADARG
The list or the data pointers are NULL .

CONTAINER_ERROR_READONLY
The list is read-only. No modifications allowed.

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Invariants:
The input data is not modified.

Returns:
A positive number if the element was added or a negative error code otherwise.

Description:
Given the address of a pointer to an element, it returns a pointer to the data stored into that element and writes the address of the next element
into its argument ppElement. If ppElement is NULL it returns NULL . If *ppElement is NULL it also returns NULL , and obviously there
is no advancing done.

Returns:A pointer to the data stored in the given element or NULL if the data can't be retrieved.

Description:
Adds the n given elements to the end of the container. It is the same operations as the PushBack operation. It is assumed that "data" points to a
contiguous memory area of at least n*ElementSize bytes. If n is zero no error is issued even if the array pointer or the data pointer are
NULL .

Errors:

CONTAINER_ERROR_BADARG
The list or the data pointers are NULL , and n is not zero.

CONTAINER_ERROR_READONLY
The list is read-only. No modifications allowed.

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A positive number if the operation completed, negative error code otherwise.

Append

int (*Append)(List *list1,List *list2);

Description:
Appends the contents of list2 to list1 and destroys list2.

Errors:

CONTAINER_ERROR_BADARG
Either list1 or list2 are NULL .

CONTAINER_ERROR_READONLY
One or both lists are read only.

CONTAINER_ERROR_INCOMPATIBLE
The size of the elements in the lists differ, or the lists allocators are different 30.

Returns:
A positive value if the operation succeeded, or a negative error code otherwise.

Description:
Will call the given function for each element of the list. The first argument of the callback function receives an element of the list. The second argument of the callback is the arg argument that the Apply function receives and passes to the callback. This way some context can be passed to the callback, and from one element to the next.
Note that the result of the callback is not used. This allows all kinds of result types to be accepted after a suitable cast.
If the list is read-only, a copy of the element will be passed to the callback function.

Errors:

CONTAINER_ERROR_BADARG
Either list or Applyfn are NULL .

CONTAINER_ERROR_NOMEMORY
: The list is read-only and there is no more memory to allocate the buffer to copy each element.

Notes:

The list container of C++ has no direct equivalent, but in the algorithm part of the STL there is a "for_each" construct, that does essentially the same. Java and C# offer a similar "ForEach" functionality.

The above example shows a function callback as used by Äpply". It receives two pointers, one to the current element and another to an extra argument that in this case contains a pointer to the sum. For each call to the callback, the function adds the contents of the element to the sum.

The main function creates a list, adds two elements with the values 2 and 3, and then calls Äpply" to get their sum using the callback.

Description:
Returns the last element of the given list or NULL if the list is empty.

Errors:

CONTAINER_ERROR_BADARG
The list pointer is NULL .

CONTAINER_ERROR_READONLY
The list is read only.

Invariants:
The input list is not modified.

Returns:
The last element or NULL if the list is empty or an error occurs.

Clear

int (*Clear)(List *l);

Description:
Erases all stored data and releases the memory associated with it. The list header will not be destroyed, and its contents will be the same as when the list was initially created. It is an error to use this function when there are still active iterators for the container.31

Errors:

CONTAINER_ERROR_BADARG
The list pointer is NULL .

CONTAINER_ERROR_READONLY
The list is read only.

Returns:
The result is greater than zero if successful, or an error code if an error occurs.

Description:
Returns one if the given data is stored in the list, zero otherwise. The "data" argument is supposed to point to an element at least ElementSize bytes. The list's comparison function is used for determining if two elements are equal. This comparison function defaults to memcmp.

Errors:

CONTAINER_ERROR_BADARG
Either list or data are NULL .

Notes:

C++ has std::find that does essentially the same . Java and C# have a "Contains" method.

Example:

List *list;
int r = iList.Contains(list,&data);

Copy

List *(*Copy)(const List *L);

Description:
A shallow copy of the given list is performed. Only ElementSize bytes will be copied for each element. If the element contains pointers, only the pointers are copied, not the objects they point to. The new memory will be allocated using the given list's allocator.

Errors:

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

CONTAINER_ERROR_BADARG
The given list pointer is NULL .

Invariants:
The input list is not modified.

Notes:

C++ has no direct equivalent but the assignment operator should work, Java and C# support a copy method.

Description:
Copies the element data at the given position into the given buffer, assuming that at least ElementSize bytes of storage are available at the position pointed by the output buffer. The main usage of this function is to access data in a read only container for later modification.

Errors:

CONTAINER_ERROR_BADARG
The given list pointer or the output buffer are NULL .

CONTAINER_ERROR_INDEX
The given position is out of bounds.

Invariants:
The input list is not modified.

Returns:
A positive value if the operation succeeded, or a negative error code if it failed.

Notes:

Neither C# nor Java provide this functionality because the treatment of pointers in those languages makes the need for such a construct unnecessary.

Description:
The creation function returns an empty List container, initialized with all the default values.
The current memory manager is used to allocate the space needed for the List header. The list is supposed to contain elements of the same size. If the elements you want to store are of different size, use a pointer to them, and create the list with sizeof(void *) as the size parameter.

Returns:
A pointer to a newly created List or NULL if an error occurs.

Errors:

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

CONTAINER_ERROR_BADARG
The given element size is zero or greater than what the implementation allows for maximum object size.

Errors provoke the call of the current default error function of the library since this is the creation function and there isn't a container specific error function yet.

Description:
The creation function returns an empty List container, initialized with all the default values.
The given memory manager is used to allocate the space needed for the List header. The list is supposed to contain elements of the same size. If the elements you want to store are of different size, use a pointer to them, and create the list with sizeof(void *) as the size parameter.

Returns:
A pointer to a newly created List or NULL if an error occurs.

Errors:

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

CONTAINER_ERROR_BADARG
The given element size is zero or greater than what the implementation allows for maximum object size, or the given allocator pointer is NULL .

Errors provoke the call of the current default error function of the library since this is the creation function and there isn't a container specific error function yet.

Description:
Compares the given lists using the list comparison function of either list1 or list2 that must compare equal. If the list differ in their length, flags, or any other characteristic they compare unequal. If any of their elements differ, they compare unequal.
If both list1 and list2 are NULL they compare equal. If both list1 and list2 are empty they compare equal.

Errors:

None

Invariants:
The two lists are not modified.

Returns:
The result is one if the lists are equal, zero otherwise.

Erase

int (*Erase)(List *list,void *const data);

Description:
Removes from the list the element that matches the given data, that is assumed to be a pointer to an element.

Returns:
A negative error code if an error occurred, or a positive value that indicates that a match was found and the element was removed. If the element is not in the list the result is
CONTAINER_ERROR_NOTFOUND
.

Description:
Removes from the list all elements that match the given data, that is assumed to be a pointer to an element.

Returns:
A negative error code if an error occurred, or a positive value that indicates that a match was found and the element was removed. If the element is not in the list the result is
CONTAINER_ERROR_NOTFOUND
.

Errors:

CONTAINER_ERROR_BADARG
One or both arguments are NULL .

EraseAt

int (*EraseAt)(List *list,size_t idx);

Description:
Removes from the list the element at the given position.

Returns:
A negative error code if an error occurred or a positive value that indicates that the element was removed.

Description:
Removes from the list the given range, starting with the start index, until the element before the end index. If end
is greater than the length of the list, it will be 'rounded' to the length of the list.

Errors:

CONTAINER_ERROR_BADARG
The given list pointer is NULL .

Returns:
A positive number indicates success, zero means nothing was erased, and a negative number an error.

Description:
Finds the first element of the list and returns a pointer to it. This is a pointer to the element, not to the data stored at that element.
It is an error to attempt to use this function with a read-only list.

Errors:

CONTAINER_ERROR_BADARG
The list pointer is NULL .

CONTAINER_ERROR_READONLY
The list is read only.

Returns:A pointer to the element or NULL if the list is empty or an error occurs.

Description:
Selects a series of consecutive elements starting at position start and ending at position end. Both the elements at start and end are included in the result.
If start is greater than end start and end are interchanged. If end is bigger than the number of elements in list, only elements up to the number of elements will be used. If both start and end are out of range an error is issued and NULL is returned.
The selected elements are copied into a new list.

Invariants:
The original list remains unchanged.

Errors:

CONTAINER_ERROR_BADARG
The given list pointer is NULL

CONTAINER_ERROR_INDEX
Both start and end are out of range.

Returns:
A pointer to a new list containing the selected elements or NULL if an error occurs.

Description:
Initializes the memory pointed by the aList argument. The new list will use the allocator pointed by the current memory allocator.
It is assumed that the memory pointed by aList contains at least the size of the header object. This size can be obtained by calling the Sizeof function with a NULL argument.

Description:
Initializes the memory pointed by the aList argument. The new list will use the given allocator.
It is assumed that the memory pointed by aList contains at least the size of the header object. This size can be obtained by calling the Sizeof function with a NULL argument.

Description:
Inserts the new element. The new element will have the given index, that can go from zero to the list count inclusive, i.e. one more than the number of elements in the list. In single linked lists the cost for this operation is proportional to idx.

Errors:

CONTAINER_ERROR_BADARG
The given list pointer or the element given are NULL .

CONTAINER_ERROR_READONLY
The list is read only.

CONTAINER_ERROR_INDEX
The given position is out of bounds.

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Invariants:
The data is not modified.

Returns:
A positive value if the operation succeeded, or a negative error code if the operation failed.

We create a list of integers at line 12. We add zero to it, then again a zero at the first position.
Our list now is just 0 0. We insert at the
position 1 the value 2 in line 17. Then we add some data at the end. To print the list we use Apply with a function that receives the
file where the data should be printed in the ExtraArgs parameter.

InsertIn

int (*InsertIn)(List *Destination, size_t position, List *source);

Description:
Inserts the list given in its third argument at the given position in the list pointed to by its first argument. The data is copied, and the source argument is not modified in any way. Both lists must have elements of the same type. The library only tests the size of each one.

Errors:

CONTAINER_ERROR_BADARG
The source or the destination lists are NULL .

CONTAINER_ERROR_READONLY
The destination list is read only.

CONTAINER_ERROR_INDEX
The given position is out of bounds.

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

CONTAINER_ERROR_INCOMPATIBLE
The lists store elements of different size.

Returns:
A positive value if the operation succeeded, or a negative error code if the operation failed.

Description:
Returns a pointer to the last element stored in the given list or NULL if the list is empty or an error occurs. It is an error to call this
function in a read-only list.

Errors:

CONTAINER_ERROR_BADARG
The list pointer is NULL .

CONTAINER_ERROR_READONLY
The list is read only.

Returns:The last element or NULL

Load

List *(*Load)(FILE *stream,ReadFunction readFn,void *arg);

Description:
Reads a list previously saved with the Save function from the stream pointed to by stream. If readFn is not NULL , it will be used to read each element. The arg argument will be passed to the read function. If the read function is NULL , this argument is ignored and a default read function is used.

Errors:

CONTAINER_ERROR_BADARG
The given stream pointer is NULL .

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:
A new list or NULL if the operation could not be completed. Note that the function pointers in the list are NOT saved, nor any special allocator that was in the original list. Those values will be the values by default. To rebuild the original state the user should replace the pointers again with the new list.

NewIterator

Iterator *(*NewIterator)(List *list);

Description:
Allocates and initializes a new iterator object to iterate this list.

Errors:

CONTAINER_ERROR_NOMEMORY
No more memory is available.

Returns:A pointer to a new iterator or NULL if there is no more memory left.

Description:
Returns a pointer to the next element in the list. If the input list is NULL it returns NULL .

Errors:

None.

Returns:The next element or NULL .

PopFront

int (*PopFront)(List *L,void *result);

Description:
Pops the element at position zero copying it to the result pointer. If the "result" pointer is NULL , the first element is removed without any copying. The library supposes that result points to at least ElementSize bytes of contiguous storage.

Errors:

CONTAINER_ERROR_BADARG
The list or the result pointer are NULL .

CONTAINER_ERROR_READONLY
The list is read only.

Returns:
A positive value if an element was popped, zero if the list was empty, or a negative error code if an error occurred.

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:
A positive value if the operation completed, or a negative error code otherwise.

Example:

double d = 2.3;
if (iList.PushFront(list,&d) < 0)
printf("Error\n");

RemoveRange

int (*RemoveRange)(List *l,size_t start,size_t end);

Description:
Removes all elements having an index equal or greater than
start
and less than
end
. If
end
is greater than the number of elements
in the collection it will be adjusted to one more than the number of elements. If
start
is bigger than
end
the range is still valid and
starts with the value of
end
and ends with the value of
start
.

Errors:

CONTAINER_ERROR_BADARG
The list pointer is NULL .

Returns:
Zero if no elements were removed. Otherwise returns a positive number for success, a negative error code in case of an error.

ReplaceAt

int (*ReplaceAt)(List *list,size_t idx,const void *newData);

Description:
Replaces the list element at position idx with the new data starting at the position pointed to by "newData" and extending ElementSize bytes.

Errors:

CONTAINER_ERROR_BADARG
The list or the new element pointer are NULL .

CONTAINER_ERROR_READONLY
The list is read only.

CONTAINER_ERROR_INDEX
The given position is out of bounds.

Invariants:
The input data is not modified.

Returns:
A negative error code if an error occurs, or a positive value if the operation succeeded.

Description:
The contents of the given list are saved into the given stream. If the save function pointer is not NULL , it will be used to save the contents of each element and will receive the arg argument passed to Save. Otherwise a default save function will be used and arg will be ignored.

Returns:
A positive value if the operation completed, a negative value or EOF otherwise.

Select

int (*Select)(List *l,Mask *m);

Description:
Uses the given mask to select elements from the given list. The list is modified: all elements that have a corresponding value of zero in the mask are
erased from the list. The length of the mask should be equal to the length of the list. If there is a destructor set up for the list, it is called for
all the elements that are eliminated.

Invariants:
The mask is not modified.

Errors:

CONTAINER_ERROR_BADARG
The list or the mask are NULL .

CONTAINER_ERROR_READONLY
The list is read only.

CONTAINER_ERROR_INCOMPATIBLE
The mask and the list have different lengths.

Description:
Uses the given mask to select elements from the given list. The list is not modified: all elements that have a corresponding value different of zero
in the mask are copied to the new list. The length of the mask should be equal to the length of the list.

Invariants:
Neither the input list nor the mask are modified.

Errors:

CONTAINER_ERROR_BADARG
The list or the mask are NULL .

CONTAINER_ERROR_INCOMPATIBLE
The mask and the list have different lengths.

CONTAINER_ERROR_NOMEMORY
Insufficient ressources to create the result list.

Description: Sets the destructor function to its given argument. If the function argument is NULL nothing is changed and the call is interpreted as a query since the return value is the current value of the destructor function. If the list argument is NULL , the result is NULL .

Returns:The old value of the destructor.

SetElementData

int (*SetElementData)(List *l, ListElement *le,void *data);

Description:
Copies ElementSize bytes from its parameter data into the given list element. The list is modified even if all pointers into it could
remain valid. Any iterators into the list will stop working.

Errors:

CONTAINER_ERROR_BADARG
Any argument is NULL .

Returns:The old value of the error function, or NULL if there is an error.

SetErrorFunction

ErrorFunction (*SetErrorFunction)(List *L,ErrorFunction);

Description:
Replaces the current error function for the given list with the new error function if the
ErrorFunction
parameter is different from NULL . If
the
List
parameter is NULL the function returns the value of the current default error function.

Errors:

CONTAINER_ERROR_BADARG
The list pointer is NULL .

CONTAINER_ERROR_READONLY
The list is read only and the function argument is not NULL .

Returns:
The old value of the error function, or NULL if there is an error.

Size

size_t (*Size)(const List *l);

Description:
Returns the number of elements stored in the list.

Errors:

If the given list pointer is NULL , it returns SIZE_MAX.

Example:

List *li;
size_t bytes = iList.Size(li);

Sizeof

size_t (*Sizeof)(const List *list);

Description:
Returns the total size in bytes of the list, including the header, and all data stored in the list. If list is NULL , the result is the size of the List structure.

Returns:
The number of bytes used by the list or the size of the empty List container if the argument is NULL .

208−88 is 120. There are 10 elements, so each element is 12 bytes: 8 for a pointer and 4 for an integer32.

SizeofIterator

size_t (*SizeofIterator)(const List *list);

Description:
Returns the total size in bytes of the list iterator.

Returns:
The number of bytes used by the list iterator.

Example:

List *list;
size_t siz = iList.Sizeof(list);

Sort

int (*Sort)(List *list);

Description:
Sorts the given list using the list comparison function. The order of the original list is destroyed. You should copy it if you want to preserve it.

Returns:
A positive number if sorting succeeded, a negative error code if not.

Example:

List *list;
if (iList.Sort(list) < 0) { /* Error handling */ }

SplitAfter

List *(*SplitAfter)(List *list, ListElement *point);

Description:
Splits the source lost at the given element. This element must be an element of the source list. Checking that this is actually
the case is very expensive and probably will be skipped in most implementations. An error here will have very serious consequences for
the integrity of the system.

SplitAfter builds a new list with all the elements after the given element that becomes the last element of the source list.

Errors:

CONTAINER_ERROR_BADARG
The list pointer or the element are NULL .

CONTAINER_ERROR_READONLY
The source list is read only.

CONTAINER_ERROR_NOMEMORY
There aren't enough ressources to create a new list.

Returns:
A pointer to the newly created list or NULL if an error occurs or the given list element is the last element of the list.

Description:
Adds a heap manager to the given list, that should be empty. The heap manager will manage the free list and the allocation of new objects. Use this
function when the list will hold a great number of elements. This function is optional and may not be present in all implementations.
If m is NULL , the current memory manager object will be used for allocating and reclaiming memory. Otherwise m should be a memory manager object.

Errors:

CONTAINER_ERROR_BADARG
The list pointer is NULL .

CONTAINER_ERROR_NOT_EMPTY
The list is not empty or has already a heap.

Portability:
This function is optional and may not be present in all implementations.

The sample implementation proposes a stringlist container that can hold a list of strings. Its interface functions and all its vocabulary
are identical to the linked list one. This is a recommended extension but may not be present in all implementations of the library.

It comes in two flavors:

Single byte character strings. The name of the interface is iStringList.

Wide character strings. The name of the interface is iWStringList.

In this specialization the function GetElementSize returns always zero since it has no meaning in a list composed of strings of different
length.33

The function Sizeof that is very fast in normal lists (the element size is known), it is very expensive in string lists since all
the list needs to be scanned to add up the contents of each string.34

Another difference is that the function Save and the function Load do not use the user function argument since they are specialized
to load and save character strings. The function argument can have any value. It has been maintained for compatibility with the rest of the software.

Double linked lists have a pair of pointers pointing to the next and to the previous element in the list. It is easy then, to move in either direction through the list. The price to pay is a higher overhead for each element.
This container shares most of its interface with the single linked list container. Here we document the functions that aren't already described for the list container.

Description:
Given the address of a pointer to an element, it returns a pointer to the data stored into that element and writes the address of the previous element
into its argument ppElement. If ppElement is NULL it returns NULL . If *ppElement is NULL it also returns NULL .

Returns:A pointer to the data stored in the given element or NULL if the data can't be retrieved.

PopBack

int (*PopBack)(List *L,void *result);

Description:
Pops the element at the last position copying it to the result pointer. If the "result" pointer is NULL , the last element is removed without any
copying. Otherwise, the library supposes that result points to at least ElementSize bytes of contiguous storage.

Errors:

CONTAINER_ERROR_BADARG
The list or the result pointer are NULL .

CONTAINER_ERROR_READONLY
The list is read only.

Returns:
A positive value if an element was popped, zero if the list was empty, or a negative error code if an error occurred.

Description:
Inserts a list (parameter "toInsert") into another one (parameter "list") at the given position
that should be an element of "list". The direction argument means to insert before the position if zero, after the position if not zero.

Errors:

CONTAINER_ERROR_BADARG
The list, the list to be inserted or the element pointer are NULL .

The "vector" container is an array that resizes to accommodate new elements. Access is always checked against the array bounds.
Insertion and deletion of items are more expensive than in lists, and the cost increases linearly with the array size. Access is very cheap, since a
multiplication suffices to get to any array position.

Most functions of the interface are shared with the list, Dlist and the other sequential containers.

Some functions of the iVector interface will examine the return value of the error function after an index error is detected. If the error function
returns a pointer value different than NULL , it will be assumed that that is the value of the API that should be returned to the user. This allows
for the implementation of infinite arrays or arrays where an access into some element beyond the end of the array provokes an automatic
resize operation of the array up to the required length.

In case of an index error, this container passes always a pointer to the container and the out of range index to the error function. The functions
where the return value of the error function is used are:

CopyElement. If the return value is not NULL the returned pointer is used to copy into the result buffer.

GetElement. If the return pointer is not NULL the pointer is the result of the function.

InsertAt. If the return pointer is not NULL , the vector is resized up to the index that is required.

Description:
Adds the given element to the end of the container. It is the same operations as the PushBack operation. It is assumed that "data" points to a contiguous memory area of at least ElementSize bytes. Returns a value greater than zero if the addition completed successfully, a negative error code otherwise.

Errors:

CONTAINER_ERROR_BADARG
The vector or the data pointers are NULL .

CONTAINER_ERROR_READONLY
The vector is read-only. No modifications allowed.

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A positive number if the operation completed, negative error code otherwise.

Description:
Adds the n given elements to the end of the container. It is the same operations as the PushBack operation. It is assumed that "data" points to a contiguous memory area of at least n*ElementSize bytes. Returns a value greater than zero if the addition completed successfully, a negative error code otherwise. If n is zero no error is issued even if the array pointer or the data pointer are NULL .

Errors:

CONTAINER_ERROR_BADARG
The vector or the data pointers are NULL , and n is not zero.

CONTAINER_ERROR_READONLY
The vector is read-only. No modifications allowed.

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A positive number if the operation completed, negative error code otherwise.

Description:
Will call the given function for each element of the array. The first argument of the callback function receives an element of the array. The second argument of the callback is the arg argument that the Apply function receives and passes to the callback. This way some context can be passed to the callback, and from one element to the next.
Note that the result of the callback is not used. This allows all kinds of result types to be accepted after a suitable cast.
If the array is read-only, a copy of the element will be passed to the callback function.

Errors:

CONTAINER_ERROR_BADARG
Either list or Applyfn are NULL .

CONTAINER_ERROR_NOMEMORY
The list is read-only and there is no more memory to allocate the buffer to copy each element.

Description:
Returns the last element of the given vector or NULL if the vector is empty.

Errors:

CONTAINER_ERROR_BADARG
The vector pointer is NULL .

CONTAINER_ERROR_READONLY
The vector is read only.

Returns:
The last element or NULL if the vector is empty or an error occurs.

Clear

int (*Clear)(Vector *l);

Description:
Erases all stored data and releases the memory associated with it. The vector header will not be destroyed, and its contents will be the same as when the array was initially created. It is an error to use this function when there are still active iterators for the container.

Returns:
The result is greater than zero if successful, or an error code if an error occurs.

Errors:

CONTAINER_ERROR_BADARG
The vector pointer is NULL .

CONTAINER_ERROR_READONLY
The vector is read only.

Example:

Vector *Al;
int m = iVector.Clear(Al);

CompareEqual

Mask *(*CompareEqual)(ValArray *left,ValArray *right,
Mask bitarray);

Description:
Assigns to each element of the mask the result of comparing the corresponding elements of the left and right arrays. Conceptually this operation is:

Mask[i] = (left[i] == right[i])

If the mask argument is NULL it will be allocated and returned.
The allocator used is the global memory manager.
If it is not NULL it should contain at least enough positions to hold the data. If it doesn't it will be finalized using iMask.Finalize and
allocated with the necessary length.

Errors:

CONTAINER_ERROR_NOMEMORY
. The given byte array argument was NULL but there is no memory to allocate the result.

Description:
Assigns to each element of the mask the result of comparing the elements of the left array with the right argument.
Conceptually this operation is:

bit[i] = (left[i] == right)

If the bitarray argument is NULL it will be allocated and returned. The allocator used is the one from the left argument. If it is not NULL it will be allocated if its length is less than the needed length.

Errors:

CONTAINER_ERROR_NOMEMORY
. The given byte array argument was NULL but there is no memory to allocate the result.

Description:
Searches the given data in the array. The "data" argument is supposed to point to an element at least ElementSize bytes. The array's comparison function is used for determining if two elements are equal. This comparison function defaults to memcmp.

Errors:

CONTAINER_ERROR_BADARG
Either array or data are NULL .

Returns:
One if the given data is stored in the array, zero otherwise. If either the data pointer or the array pointer are NULL it returns a negative error code.

Description:
A shallow copy of the given array is performed. Only ElementSize bytes will be copied for each element. If the element contains pointers, only the pointers are copied, not the objects they point to. The new memory will be allocated using the given array's allocator.

Errors:

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

CONTAINER_ERROR_BADARG
The given vector pointer is NULL .

Example:

Vector *newVector,*OldVector;
newVector = iVector.Copy(OldVector);

CopyElement

Vector *(*Copy)(const Vector *A,size_t idx,void *result);

Description:
The element at the given index is copied into the output buffer that should be at least big enough to hold one element. If the index is bigger
than the number of elements the error function is called. If it returns a valid pointer (not NULL ) it is assumed that this is a pointer to a valid
element that should be copied into the output buffer.

Errors:

If the idx argument is out of range the CopyElement function calls the vector error function. If the error function returns a result
different than NULL , the function assumes that it is a pointer to some value that should be copied into the result buffer.

Description:
The creation function returns an empty array, initialized with all the default values.
The current memory manager is used to allocate the space needed for the header. The array is supposed to contain elements of the same size. If the elements you want to store are of different size, use a pointer to them, and create the array with sizeof(void *) as the size parameter.

Returns:
A pointer to a newly created array or NULL if an error occurs.

Errors:

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

CONTAINER_ERROR_BADARG
The given element size is zero.

Any errors provoke the call the current default error function of the library since this is the creation function.

Description:
This function is identical to Create with the difference that it accepts a pointer to an allocator object. Actually, Create can
be written as:

return CreateWithAllocator(elementsize,startsize,CurrentAllocator);

Equal

int (*Equal)(Vector *first,Vector *second);

Description:
Compares the given arrays. If they differ in their length, flags, or element size they compare unequal. If any of their elements differ, they compare unequal.
If both first and second are NULL they compare equal.

Description:
Removes from the vector the element that matches the given data, that is assumed to be a pointer to an element.

Returns:
A negative error code if an error occurred, or a positive value that indicates that at least one match was found and the elements were removed.
If the element is not in the vector the result value is
CONTAINER_ERROR_NOTFOUND
.

Description:
Removes from the list all elements that match the given data, that is assumed to be a pointer to an element.

Returns:
A negative error code if an error occurred, or a positive value that indicates that at least a match was found and the element was removed. If the
element is not in the list the result is
CONTAINER_ERROR_NOTFOUND
.

Errors:

CONTAINER_ERROR_BADARG
One or both arguments are NULL .

EraseAt

int (*EraseAt)(Vector *AL,size_t idx);

Description:
Removes from the array the element at the given position.

Returns:
A negative error code if an error occurred or a positive value that indicates that the element was removed.

Description:
Returns the first element of the given vector or NULL if the vector is empty.

Errors:

CONTAINER_ERROR_BADARG
The vector pointer is NULL .

CONTAINER_ERROR_READONLY
The vector is read only.

Returns:
The first element or NULL if the vector is empty or an error occurs.

GetCapacity

size_t (*GetCapacity)(const Vector *AL);

Description:
Returns the number of elements the array can hold before it needs to reallocate its data35.

Errors:

CONTAINER_ERROR_BADARG
The given array is NULL .

Returns:
The array capacity or zero if there was an error.

GetElementSize

size_t (*GetElementSize)(const Vector *AL);

Description:
Retrieves the size of the elements stored in the given vector. Note that this value can be different than the value given to the creation function because of alignment requirements. In template containers this function returns
sizeof(TYPE).

Errors:

CONTAINER_ERROR_BADARG
The given vector pointer is NULL .

Returns:
The element size.

Example:

Vector *AL;
size_t siz = iVector.GetElementSize(AL);

GetData

void **(*GetData)(const Vector *AL);

Description:
Returns a pointer to the data area of the container, or NULL if an error occurs.

Description:
Returns a read only pointer to the element at the given index, or NULL if the operation failed. This function will return NULL if the vector is read only. If the index is greater than the number of elements, the error function will be called. If the error function returns a valid pointer
(not NULL ) the result will be that pointer. This allows the construction of infinite arrays, or sparse arrays, etc. By default, the error function
returns always NULL .

Use the CopyElement function to get a read/write copy of an element of the vector.

Description:
GetFlags returns the state of the container flags, SetFlags sets the flags to a new value and returns the old value.

The Vector container supports the following flags:

CONTAINER_READONLY
If this flag is set, no modifications to the container are allowed, and the Clear and Finalize functions will not work. The GetElement function will always return NULL . You should use the CopyElement function to access the data

GetRange

Vector *(*GetRange)(Vector *AL,size_t start,size_t end);

Description:
Selects a series of consecutive elements starting at position start and ending at position end. Both the elements at start and end are included in the result.
If start > end or start > Size(AL), NULL is returned. If end is bigger than the number of elements in the vector AL, only elements up to the number of elements will be used.
The selected elements are copied into a new array. The original array remains unchanged.

Errors:

CONTAINER_ERROR_BADARG
The given array pointer or the element given are NULL

Returns:A pointer to a new vector containing the selected elements or NULL if an error occurs.

Description:
Returns an array built from indexing the first argument ("SC") with the array of indexes ÄL" that should be an array of size_t elements. The number of elements of the resulting array is equal to the number of elements of the indexes array.

Errors:

CONTAINER_ERROR_BADARG
The given array pointer or the indexes array are NULL .

CONTAINER_ERROR_INDEX
Any given position is out of bounds.

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:
A new array or NULL if an error occurs. No partial results are returned. If any index is out of bounds the whole operation fails.

Description:
Searches for an element in the array. If found its zero based index is returned in the pointer "result". Otherwise the result of the search is
CONTAINER_ERROR_NOTFOUND
. The ëxtraArgs" argument will be passed to the comparison function, that is used to compare elements.

Errors:

CONTAINER_ERROR_BADARG
The given array pointer or the element given are NULL .

Returns:
A positive number if the element is found, or a negative number containing an error code or the negative constant
CONTAINER_ERROR_NOTFOUND
.

Description:
Inserts the new element. The new element will have the given index, that can go from zero to the vector count inclusive, i.e. one more than the number of elements in the vector. If the index is out of bounds, the vector error function is called. If the function returns a valid pointer (a pointer
different than NULL ) it will be assumed that the vector should be increased to the index given. The vector is resized and the data is inserted
at the requested position. This allows the implementation of infinite vectors, sparse vectors, and other data structures.

Errors:

When this API detects an index error, it calls the error function. If the error function returns a pointer different from NULL , the API will extend
the requested vector to make possible the insertion. If the API is unable to extend the vector and error is returned and the vector remains unchanged.

CONTAINER_ERROR_BADARG
The given vector pointer or the element given are NULL .

CONTAINER_ERROR_READONLY
The vector is read only.

CONTAINER_ERROR_INDEX
The given position is out of bounds.

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:
A positive value if the operation succeeded, or a negative error code if the operation failed.

Description:
Inserts the array given in its third argument at the given position in the array pointed to by its first argument. The data is copied, and the source argument is not modified in any way. Both arrays must have elements of the same type. The library only tests the size of each one.

Errors:

CONTAINER_ERROR_BADARG
The source or the destination vectors are NULL .

CONTAINER_ERROR_READONLY
The destination vector is read only.

CONTAINER_ERROR_INDEX
The given position is out of bounds.

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

CONTAINER_ERROR_INCOMPATIBLE
The vectors store elements of different size.

Returns:
A positive value if the operation succeeded, or a negative error code if the operation failed.

Description:
Reads an array previously saved with the Save function from the stream pointed to by stream. If readFn is not NULL , it will be used to read each element. The arg argument will be passed to the read function. If the read function is NULL , this argument is ignored and a default read function is used.

Errors:

CONTAINER_ERROR_BADARG
The given stream pointer is NULL .

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:
A new array or NULL if the operation could not be completed. Note that the function pointers in the array are NOT saved, nor any special allocator that was in the original vector. Those values will be the values by default. To rebuild the original state the user should replace the pointers again in the new array.

NewIterator

Iterator *(*NewIterator)(Vector *AL);

Description:
Allocates and initializes a new iterator object to iterate this array.

Errors:

If no more memory is available it returns NULL .

Returns:
A pointer to a new iterator or NULL if there is no more memory left.

Description:
Returns the index of the first element that is different when comparing both arrays in the passed pointer mismatch. If one array is shorter than the other the comparison stops
when the last element from the shorter array is compared. The comparison stops when the first difference is spotted.

Errors:

CONTAINER_ERROR_BADARG
Any of the arguments is NULL .

CONTAINER_ERROR_INCOMPATIBLE
The containers have different comparison functions or store elements of different size.

Returns:
If a mismatch is found the result is greater than zero and the mismatch argument will contain the index of the first element that compared
unequal. This will be always the case for arrays of different length.

If both arrays are the same length and no differences are found the result is zero and the value pointed to by the
mismatch argument is one more than the length of the arrays.

If an error occurs, a negative error code is returned. The mismatch argument contains zero.

Description:
Copies the last element into the given result buffer and deletes the element from the container. If the result buffer is NULL , no copy is performed.

Errors:

CONTAINER_ERROR_BADARG
The array is NULL .

CONTAINER_ERROR_READONLY
The array is read only.

Returns:
A negative value if an error occurs, zero if the array is empty or greater than zero if the operation succeeded.

RemoveRange

int (*RemoveRange)(Vector *SC,size_t start,size_t end);

Description:
Removes all elements having an index equal or greater than
start
and less than
end
. If
end
is greater than the number of elements
in the collection it will be adjusted to one more than the number of elements. If
start
is bigger than
end
the range is still valid and
starts with the value of
end
and ends with the value of
start
.

Errors:

CONTAINER_ERROR_BADARG
The vector pointer is NULL .

Returns:
Zero if the vector is empty. Otherwise returns a positive number for success, a negative error code in case of an error.

Description:
The capacity of the vector is increased at least by the given amount in preparation for a planned increase in elements. The size is in element
units.

Errors:

CONTAINER_ERROR_BADARG
The array pointer is NULL .

CONTAINER_ERROR_READONLY
The array is read only.

CONTAINER_ERROR_NOMEMORY
Not enough memory is available

Returns:A positive number if the space could be reserved, a negative error code if not.

Resize

int (*Resize)(Vector *AL, size_t newSize);

Description:
Resizes the given vector to the new capacity, expressed in element units. If the new capacity is smaller than the elements in
the vector some elements will be erased.
For each erased element its destructor (if any) is called. If the requested newSize argument is equal to the number of elements in the
container, all storage beyond what is needed to store exactly newSize elements, is released.
This allows to signal the library that the extra storage is no longer needed36.

iVector.Resize(vec,iVector.Size(vec));

Obviously under some implementations it is maybe impossible to allocate exactly the space needed, for instance because space can be only
allocated in chunks bigger than the element size of the vector. In those cases only a "best effort" will be done and the used storage will be reduced
to a minimum.
If an error occurs, and the container can't be resized, no changes occur and the container is left unmodified.

Errors:

CONTAINER_ERROR_BADARG
The array pointer is NULL .

CONTAINER_ERROR_READONLY
The array is read only.

CONTAINER_ERROR_NOMEMORY
Not enough memory is available

Returns:
A negative error code if an error occurs, or a positive value if the operation succeeded. If the requested capacity is equal to the current
capacity the result is zero.

Reverse

int (*Reverse)(Vector *AL);

Description:
Reverses the order of the elements of the given Vector.

Errors:

CONTAINER_ERROR_BADARG
The array pointer is NULL .

CONTAINER_ERROR_READONLY
The array is read only.

CONTAINER_ERROR_NOMEMORY
Not enough memory for intermediate storage available

Returns:
A negative error code if an error occurs, or a positive value if the operation succeeded.

RotateRight

int (*RotateRight)(Vector *src,size_t n);

Description:
Rotates right the array by the indicated amount. The last n elements will be written to the start of
the array, and the rest will be shifted right.

Errors:

CONTAINER_ERROR_BADARG
The array pointer is NULL .

CONTAINER_ERROR_READONLY
The array is read only.

CONTAINER_ERROR_NOMEMORY
Not enough memory is available

Returns:A positive number if something was moved, zero otherwise (the input was zero or a
modulo of the array size).

RotateLeft

int (*RotateLeft)(Vector *src,size_t n);

Description:
Rotates left the array by the indicated amount. The first n elements will be written to the end of
the array, and the rest will be shifted left to fill the empty n places.

Errors:

CONTAINER_ERROR_BADARG
The array pointer is NULL .

CONTAINER_ERROR_READONLY
The array is read only.

CONTAINER_ERROR_NOMEMORY
Not enough memory for intermediate storage available

Returns:A positive number if something was moved, zero otherwise (the input was zero or a
modulo of the array size), or an error code less than zero if an error occurs.

Save

int (*Save)(const Vector *AL, FILE *out, SaveFunction Fn, void *arg);

Description:
The contents of the given vector are saved into the given stream. If the save function pointer is not NULL , it will be used to save the contents of each element and will receive the arg argument passed to Save, together with the output stream. Otherwise a default save function will be used and arg will be ignored.
The output stream must be opened for writing and must be in binary mode.

Description:
This function searches the vector for a match in a region of the stored objects, ignoring the rest of the data. It will start at the object with startIndex and compare (using memcmp) the specified region of each object. The region of interest within the stored object is specified
by a byte offset and a size.
If a match is found the search stops and the zero based index of the object will
be written into the result pointer. If the index of the start of the iteration is bigger than the number of elements in the array nothing
is searched and the result is zero.

If the sum of startByte and sizeKey is bigger than the size of the elements stored, the comparison will start at the given offset but will stop
at the end of the stored element.

Errors:

CONTAINER_ERROR_BADARG
The array or the item element pointers are NULL .

Returns:
A negative error code if an error occurs, zero if no match was found, or a positive number indicating that a match was found. The given index pointer is
modified only if a match is found.

Select

int (*Select)(Vector *v,Mask *m);

Description:
Using the given mask, the elements where the corresponding mask element is zero are eliminated, those with a mask
value different of zero are retained. The mask must have the same length as the array.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The mask and the array are of different length.

Description:
Using the given mask, the elements where the corresponding mask element is different from zero are copied into a new array, those with a mask
value different of zero are ignored. The mask must have the same length as the array.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The mask and the array are of different length.

Description:
Resizes the given Vector to a new value. The new capacity means there will be that number of elements allocated, avoiding costly resizing operations when new elements are added to the Vector. If the number given is less than the number of elements present in the array, elements are discarded from the end of the array.

Description: Sets the destructor function to its given argument. If the function argument is NULL nothing is changed and the call is interpreted as a query since the return value is the current value of the destructor function. If the vector argument is NULL , the result is NULL .

Returns:The old value of the destructor.

SetErrorFunction

ErrorFunction (*SetErrorFunction)(Vector *V,ErrorFunction);

Description:
Replaces the current error function for the given vector with the new error function if the
ErrorFunction
parameter is different from NULL . If
the
V
parameter is NULL the function returns the value of the current default error function.

Errors:

CONTAINER_ERROR_BADARG
The vector pointer is NULL .

CONTAINER_ERROR_READONLY
The vector is read only and the function argument is not NULL .

Returns:
The old value of the error function or NULL if there is an error.

Size

size_t (*Size)(const Vector *AL);

Description:
Returns the number of elements stored in the array.

Example:

Vector *AL;
size_t elem = iVector.Size(AL);

Sizeof

size_t (*Sizeof)(Vector *AL);

Description:
Returns the total size in bytes of the vector, including the header, and all data stored in it. If the argument is NULL , the size of the header only is returned.

Returns:
The number of bytes used by the vector or the size of the Vector header if the argument is NULL .

Example:

Vector *AL;
size_t size = iVector.Sizeof(AL);

Sort

int Sort(Vector *AL);

Description:
Sorts the given array using the its comparison function. The order of the original array is destroyed. You should copy it if you want to preserve it.

Returns:
A positive number if sorting succeeded, a negative error code if not.

Contrary to the other containers presented above like iList or iVector, bitstring receives and returns not pointers but values of bits. This is an important difference and makes for significant changes in the interface of many functions.

Other functions like Apply do not make much sense for bits and are provided just to be coherent in the overall design of the library. Obviously a function that needs a function call per bit is not very fast.
The function GetElementSize is provided for compatibility purposes only and returns always 1. Actually it should return 0.125 assuming
8 bits bytes.

Add

int (*Add)(BitString *BitStr,int);

Description:
Adds a bit at the end of the given bitstring.

Errors:

CONTAINER_ERROR_BADARG
The given pointer is NULL .

CONTAINER_ERROR_NOMEMORY
There is no memory to carry out the operation.

Returns:A positive number if the bit is added or a negative error code otherwise.

Description:
Makes a logical AND between the left and right arguments. The result is returned in a new bit string, both arguments are not modified. The length of the resulting bit string is the smallest length of both strings.

Returns:
A pointer to the newly allocated result or NULL in case of error.

Errors:

CONTAINER_ERROR_BADARG
One of both bitstring pointers are NULL .

CONTAINER_ERROR_NOMEMORY
Not enough memory is available to complete the operation.

Description:
Makes a logical AND of its two arguments and assigns the result into the left bit string. If the bit strings have a different length, the operation uses the bits of the right argument until either the end of the right argument or the end of the destination string is reached.

Description:
Returns all the bits between the start (inclusive) and the end (inclusive) indices. If end is smaller than start, start and end are exchanged.
If end is greater than the size of the bit string, all elements up to the last one are returned. If both start and end are out of range, an error is issued and the result is NULL .

Description:
Makes a logical OR between the left and right arguments. The result is returned in a new bit string, both arguments are not modified. The length of the resulting bit string is the smallest length of both strings.

Errors:

CONTAINER_ERROR_BADARG
One of both bitstring pointers are NULL .

CONTAINER_ERROR_NOMEMORY
Not enough memory is available to complete the operation.

OrAssign

int (*OrAssign)(BitString *left,BitString *right);

Description:
Makes a logical OR of its two arguments and assigns the result into the left bit string. If the bit strings have a different length, the operation uses the bits of the right argument until either the end of the right argument or the end of the destination string is reached.

Errors:

CONTAINER_ERROR_BADARG
One or both arguments are NULL .

Returns:A positive number or a negative error code in case of error.

PopulationCount

uintmax_t (*PopulationCount)(BitString *b);

Description:
Computes the number of 1 bits in the bit string.

Returns:
The number of set bits in the string.

Errors:

CONTAINER_ERROR_BADARG
The given argument is NULL .

Print

size_t (*Print)(BitString *b,size_t bufsiz,unsigned char *out);

Description:
Prints into the given buffer the contents of the bitstring b without exceeding the length of the given buffer bufsiz. The bits will
be grouped into 4 bits separated by a space. Each group of 8 bits will be separated from the rest by two spaces.

Errors:

CONTAINER_ERROR_BADARG
. The bit string pointer is NULL .

Returns:The number of characters written to the output string, including the terminating zero. If the output string pointer is NULL , it returns
the number of characters that would be needed to print the contents of the bitstring.

Description:
Sets the range of bits delimiteded by its start and end arguments to the value given by its newvalue argument. If the new
value is different than zero a '1' bit is written, otherwise the bit is set to zero. If the stop argument is bigger than the length of the
bitstring, the end of the string will be used.

CONTAINER_ERROR_BADARG
The bit string pointer is NULL.

CONTAINER_ERROR_INDEX
The start argument is bigger or equal to the length of the bitstring.

StringToBitString

BitString *(*StringToBitString)(unsigned char *);

Reads a bitstring from a character string. The character string should contain only the characters '1', '0', space and tab.

Errors:

CONTAINER_ERROR_BADARG
The character string pointer is NULL .

Returns:A pointer to the new bitstring or NULL if there was an error or the given character string did not contain any '1' or '0'.

Xor

BitString *(*Xor)(BitString *left,BitString *right);

Description:
Makes a logical XOR between the left and right arguments. The result is returned in a new bit string, both arguments are not modified. The length of the resulting bit string is the smallest length of both strings.

Returns:
A pointer to its result or NULL in case of error.

Errors:

CONTAINER_ERROR_BADARG
One of both bitstring pointers are NULL .

CONTAINER_ERROR_NOMEMORY
Not enough memory is available to complete the operation.

XorAssign

int (*XorAssign)(BitString *left,BitString *right);

Description:
Makes a logical XOR of its two arguments and assigns the result into the left bit string. If the bit strings have a different length, the operation uses the bits of the right argument until either the end of the right argument or the end of the destination string is reached.

To avoid unnecessary repetitions in this document here is documented a generic interface.
The word ElementType is either char for multi-byte strings, or
wchar_t for wide character strings. The word strCollection in this context means either

Description:
Adds each string of the array of string pointers at the end of the container. It is assumed that "data" points to a contiguous array of string pointers whose size is given by the "n" parameter. Returns a value greater than zero if the addition completed successfully, a negative error code otherwise. If n is zero nothing is done and no errors are issued, even if the array pointer or the data pointer are NULL .

Errors:

CONTAINER_ERROR_BADARG
The strCollection pointer or the data pointers are NULL .

CONTAINER_ERROR_READONLY
The collection is read-only. No modifications allowed.

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A positive number if the operation completed, negative error code otherwise.

Description:
Searches all occurrences of the given text in the given string collection.

Errors:

CONTAINER_ERROR_BADARG
The strCollection or the text pointer are NULL .

CONTAINER_ERROR_NOMEMORY
Not enough storage for holding the resulting array.

Invariants:
Neither the input collection, nor the given text are modified.

Returns:
An array list containing a pair of integers for each occurrence containing the zero based position of the line where the text was found and a second number indicating the character index within the line where the searched text occurs.
The result is NULL if there wasn't any occurrences of the searched text in the string collection or an error was detected.

Front

const CHARTYPE *(*Front)(const strCollection *l);

Description:
Returns the first element of the given list or NULL if the collection is empty.

Errors:

CONTAINER_ERROR_BADARG
The collection pointer is NULL .

CONTAINER_ERROR_READONLY
The collection is read only.

Invariants:
The input collection is not modified. The resulting pointer should not be modified in any way.

Returns:
The first element or NULL if the collection is empty or an error occurs.

Init

strCollection *(*Init)(strCollection *result, size_t startsize);

Description:
Initializes the given string collection to contain at least the number of strings given. Uses the current memory manager.

Errors:

CONTAINER_ERROR_NOMEMORY
There is no more memory left to complete the operation.

CONTAINER_ERROR_BADARG
The string collection pointer is NULL

Returns:A pointer to the initialized string collection or NULL if an error occurs.

Description:
Inserts the given strCollection into the destination strCollection at the given position. If the position is greater than the actual length of the string collection the new data will be inserted at the end.

The example creates two string collections, fills them with the string representation of the numbers from 0 to 9 and from 100 to 109, then inserts the second collection into the first one at position 5.

Description:
Returns the index of the first element that is different when comparing both collections in the passed pointer mismatch. If one is shorter than the other the comparison stops
when the last element from the shorter array is compared. The comparison also stops when the first difference is spotted.

Errors:

CONTAINER_ERROR_BADARG
Any of the arguments is NULL .

Invariants:
The input collections are not modified in any way. Both collections could be the same.

Returns:
If a mismatch is found the result is greater than zero and the mismatch argument will contain the index of the first element that compared
unequal. This will be always the case for arrays of different length.

If both arrays are the same length and no differences are found the result is zero and the value pointed to by the
mismatch argument is one more than the length of the arrays.

If an error occurs, a negative error code is returned. The mismatch argument contains zero.

Description:
If the string collection is not empty, it will copy at most buflen characters into the given buffer. If the buffer pointer is NULL or the length of the buffer is zero it will return the length of the element that would be popped.

Errors:

CONTAINER_ERROR_BADARG
The strCollection pointer is NULL .

Returns:
Zero if the collection was empty, a negative error code if an error occurs, or a positive value if the range was erased.

RemoveRange

int (*RemoveRange)(strCollection *SC,size_t start,size_t end);

Description:
Removes all strings having an index equal or greater than
start
and less than
end
. If
end
is greater than the number of elements
in the collection it will be adjusted to one more than the number of elements. If
start
is bigger than
end
the range is still valid and
starts with the value of
end
and ends with the value of
start
.

Errors:

CONTAINER_ERROR_BADARG
The strCollection pointer is NULL .

Returns:
Zero if the string collection is empty. Otherwise returns a positive number for success, a negative error code in case of an error.

The reason for this change is that a string container holds pointers to characters, hence a double indirection is needed by functions like
sort.

Errors:

CONTAINER_ERROR_BADARG
The strCollection pointer is NULL .

Returns:The old value of the comparison function.

WriteToFile

int (*WriteToFile)(const strCollection *SC,const char *fileName);

Description:
Writes the contents of the given string collection into a file with the given name. If the collection is empty an empty file is created. The resulting file contains a line for each string in the collection.

Errors:

CONTAINER_ERROR_BADARG
The strCollection pointer or the fileName are NULL .

Invariants:
The input collection is not modified.

Returns:
A positive number if the operation completes, or a negative error code otherwise. If the collection is empty the result is zero.

A dictionary is an associative container that associates a text key with a piece of data. It can be implemented by means of a hash table that uses a hash function to map the key into a restricted integer range, used to index a table. A common usage is to associate some data with a character key,
but it can also be used to just store character keys without any data associated with them. The container is then used just to see if a given key
is stored there or not. In this case the container should be created with object size of zero.

The interest of hash tables is that the access to objects using the key is very fast.

This interface (like the string collection container) comes in two flavors: One with keys of 8 bit characters, another with keys in the wide character
set. Both interfaces are identical, except for the keys parameter.
This is the interface for the multi-byte character set.

Description:
Adds the given element to the container using the given "key" string. It is assumed that "data" points to a contiguous memory area of at least ElementSize bytes. Both the key and the data are copied into the container. If the size of dictionary data elements is zero the
data

argument is ignored and can be NULL .

If an element exists with the given key, its contents are replaced with the new data. For a different behavior use Insert or Replace.

Errors:

CONTAINER_ERROR_BADARG
The dictionary, or the key pointers are NULL .

CONTAINER_ERROR_READONLY
The dictionary is read-only. No modifications allowed.

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A positive number if the operation added a new element, zero if the data was written into an existing element, or a negative error code if an error occurred.

Description:
Will call the given function for each element of the array. The first argument of the callback function receives they key ,the second is a pointer to the element of the Dictionary. The third argument of the callback is the ëxtraArg" argument that the Apply function receives and passes to the callback. This way some context can be passed to the callback, and from one element to the next.
Note that the result of the callback is not used. This allows all kinds of result types to be accepted after a suitable function type cast.
If the dictionary is read-only, a copy of the element will be passed to the callback function.

Errors:

CONTAINER_ERROR_BADARG
Either the dictionary pointer or Applyfn are NULL .

CONTAINER_ERROR_NOMEMORY
The dictionary is read-only and there is no more memory to allocate the buffer to copy each element.

Description:
Returns a vector containing all the elements in the dictionary (without any keys). If the element size of the dictionary is zero the result is NULL .

CONTAINER_ERROR_BADARG
The dictionary pointer is NULL .

CONTAINER_ERROR_NOMEMORY
The creation of the resulting vector failed or the dictionary is read-only and there is no more memory to allocate
the buffer to copy each element.

Returns:The new vector or NULL .

Clear

int (*Clear)(Dictionary *dict);

Description:
Erases all stored data and releases the memory associated with it. The dictionary header is not destroyed, and its contents will be the same as when it was initially created. It is an error to use this function when there are still active iterators for the container.

Returns:
The result is greater than zero if successful, or an error code if an error occurs.

Errors:

CONTAINER_ERROR_BADARG
The vector pointer is NULL .

CONTAINER_ERROR_READONLY
The vector is read only.

Example:

Dictionary *Dict;
int m = iDictionary.Clear(Dict);

Contains

int (*Contains)(Dictionary *Dict,const char *Key);

Description:
Returns one if the given key is stored in the dictionary, zero otherwise. If an error occurs it returns a negative error code.

Errors:

CONTAINER_ERROR_BADARG
Either Dict or Key are NULL .

Example:

Dictionary *dict;
int r = iDictionary.Contains(dict,"Item 1");

Copy

Dictionary *(*Copy)(Dictionary *Dict);

Description:
A shallow copy of the given dictionary is performed. Only ElementSize bytes will be copied for each element. If the element contains pointers, only the pointers are copied, not the objects they point to. The new memory will be allocated using the allocator in the source dictionary.

Errors:

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

CONTAINER_ERROR_BADARG
The given vector pointer is NULL .

Returns:A pointer to a copy of the given dictionary or NULL .

Example:

Dictionary *newDict,*Old;
newDict = iDictionary.Copy(Old);

CopyElement

int (*CopyElement)(Dictionary *Dict,cont char *Key, void *outbuf);

Description:
A shallow copy of the given dictionary element is performed.
Only element size bytes will be copied. If the element contains pointers, only the pointers are copied, not the objects they point to. The new memory will be allocated using the allocator in the source dictionary. If the element size is zero nothing is copied and the result is zero.

Errors:

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

CONTAINER_ERROR_BADARG
The given vector pointer is NULL .

Returns:A positive value for success, zero if the element size of the dictionary is zero, or a negative error code.

Description:
Creates a new dictionary with the given element size and with a table big enough to store hint entries. The Create function
uses the current memory manager as the allocator for the new dictionary. CreateWithAllocator uses the given allocator object.

Errors:

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:A pointer to the new dictionary or NULL if there is not enough memory to create it.

deleteIterator

int deleteIterator(Iterator *it);

Description:
Reclaims the memory used by the given iterator object

Returns:
Integer smaller than zero with error code or a positive number when the operation completes.

Errors:

CONTAINER_ERROR_BADARG
The iterator pointer is NULL .

Equal

int (*Equal)(Dictionary *d1,Dictionary *d2);

Description:
Compares the given dictionaries using their comparison function. If the dictionaries differ in their size, flags, or hash functions they compare unequal. If any of their elements differ, they compare unequal.
If both d1 and d2 are NULL they compare equal. If Both d1 and d2 are empty they compare equal.

Errors:

None

Returns:
The result is one if the dictionaries are equal, zero otherwise.

Erase

int (*Erase)(Dictionary *Dict,const char *key);

Description:
Removes from the dictionary the element that matches the given key.

Returns:
A positive value that indicates that a match was found and the element was removed. If no element matched the result is
CONTAINER_ERROR_NOTFOUND
. If an error occurs, a negative error code is returned.

Description:
Returns the number of elements divided by the size of the table.

Errors:

CONTAINER_ERROR_BADARG
The given dictionary pointer is NULL .

InsertIn

int (*InsertIn)(Dictionary *dst,Dictionary *src);

Description:
Inserts all keys of the
src
dictionary into the
dst
dictionary. If the container changes during the insertion process
the operation aborts.

Errors:

CONTAINER_ERROR_BADARG
The given dictionary pointer is NULL .

CONTAINER_ERROR_NOMEMORY
. There is not enough memory to complete the operation.

Returns:A positive number if successful, zero if the container changed during the operation, or a negative error code.

Init

Dictionary *(*Init)(Dictionary *Dict,size_t elementsize,size_t hint);

Description:
Initializes the indicated storage for use asa dictionary object. This procedure is completely equivalent to Create with the difference
that there is no allocation done for the dictionary header. Uses the current memory manager for the allocations of the slot table.

Returns:
A pointer to its first argument if successfull or NULL if there is no memory to complete the operation.

Description:
Construct a dictionary from the given keys and values. The Values argument should be either NULL or a valid pointer to n elements
of size elementSize. The keys argument should be a table of string pointers with each string associated with each element of the
Values table.

Errors:

CONTAINER_ERROR_BADARG
The keys argument is NULL .

CONTAINER_ERROR_NOMEMORY
. There is not enough memory to complete the operation.

Description:
Initializes the indicated storage for use as a dictionary object. This procedure is completely equivalent to CreateWithAllocator with the difference
that there is no allocation done for the dictionary header. Uses the given memory manager for the allocations of the slot table.

Returns:
A pointer to its first argument if successfull or NULL if there is no memory to complete the operation.

Insert

int (*Insert)(Dictionary *Dict, const char *key,void *Data);

Description:
Inserts the new key and its corresponding data into the given dictionary. If the key is already present, nothing is changed. This contrasts with the
behavior of Add that will replace an existing key.

Errors:

CONTAINER_ERROR_BADARG
Any of the given pointers is NULL .

CONTAINER_ERROR_READONLY
The array is read only.

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:A positive value if the key was inserted, zero if the key was already present, or a negative error code.

Load

Dictionary *(*Load)(FILE *stream,ReadFunction readFn,void *arg);

Description:
Reads a dictionary previously saved with the Save function from the stream pointed to by stream. If readFn is not NULL , it will be used to read each
element. The ärg" argument will be passed to the read function. If the read function is NULL , this argument is ignored and a default read function is
used.

Errors:

CONTAINER_ERROR_BADARG
The given stream pointer is NULL .

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:
A new dictionary or NULL if the operation could not be completed. Note that the function pointers in the array are NOT saved, nor any special allocator
that was in the original dictionary. Those values will be the values by default. To rebuild the original state the user should replace the pointers
again in the new array.

NewIterator

Iterator *(*NewIterator)(Dictionary *Dict);

Description:
Allocates and initializes a new iterator object to iterate this dictionary. The exact sequence is implementation defined but it will be the same for
the same dictionary with the same number of elements.

Errors:

If no more memory is available it returns NULL .

Returns:
A pointer to a new iterator or NULL if there is no more memory left.

Description: Sets the destructor function to its given argument. If the function argument is NULL nothing is changed and the call is interpreted as
a query since the return value is the current value of the destructor function. If the dictionary argument is NULL , the result is NULL .

Returns:The old value of the destructor.

SetHashFunction

size_t (*SetHashFunction)(Dicttionary *dict,HashFunction newFn);

Description: This function is both a query function and a function to change the hash function used by the given dictionary.

If the dictionary pointer is NULL returns the value of the default hash function used by the library at startup.

If the newFn parameter is NULL it returns the hash function used by the given dictionary without modifying it.

Otherwise it sets the hash function in the given dictionary to the new one, returning the value of the old one.

Size

size_t (*Size)(const Dictionary *Dict);

Description:
Returns the number of elements stored in the dictionary or SIZE_MAX if the dictionary pointer is NULL .

Description:
The contents of the given dictionary are saved into the given stream. If the save function pointer is not NULL , it will be used to save the contents of each element and will receive the arg argument passed to Save, together with the output stream. Otherwise a default save function will be used and arg will be ignored.
The output stream must be opened for writing and must be in binary mode.

Description:
Returns the total size in bytes of the dictionary, including the header, and all data stored in the dictionary, including the size of the dictionary header.
If Dict is NULL , the result is the size of the Dictionary structure.

Returns:
The number of bytes used by the dictionary or the size of the Dictionary structure if the argument is NULL .

Description:
Replaces the current error function for the given dictionary with the new error function if the
ErrorFunction
parameter is
different from NULL . If the
dict
parameter is NULL the function returns the value of the current default error function.

Errors:

CONTAINER_ERROR_BADARG
The dictionary pointer is NULL .

CONTAINER_ERROR_READONLY
The dictionary is read only and the function argument is not NULL .

Returns:
The old value of the error function or NULL if there is an error.

Size

size_t (*Size)(const Dictionary *d);

Description:
Returns the number of elements stored in the dictionary. If the argument is NULL the result is zero.

The tree map container uses a tree to associate keys to values. Trees are extremely efficient data structures that allow access to millions
of items with a few comparisons. Disadvantages include a greater overhead than other containers, and a complex machinery to maintain
them.

This associative container is special in that it contains no separate key, the elements themselves are the key. Obviously they need imperatively
a comparison function, and that comparison function could use some parts of the stored object as a key, but that is transparent to the interface.

An essential point in this container is the comparison function. Since all insertions searches and deletions from/to the tree are done using that
function, it is essential that is defined correctly. Like all other comparison functions it can receive an extra argument that conveys some kind
of context to it. This implies that functions like 'Add' have an extra argument to be able to pass this context to the comparison function.

It is important to stress that for this container it is essential that the comparison function returns always the same result for
two given elements. The context passed through this auxiliary arguments must not be used to change the result of the element comparison according
to some external factor. Any inconsistency in the comparison function will destroy completely the whole container and the user will be unable
to retrieve the data stored or (worst) retrieve the wrong data.

Hash table is a similar container as dictionary, but allows for more features at the expense of a slightly more complicated interface.
Keys aren't restricted to zero terminated strings but can be any kind of data.
The table resizes itself as it grows.
Merging two hash tables

Description:
Adds the given element to the container using the given "key" string. It is assumed that "data" points to a contiguous memory area of at least ht->ElementSize bytes. Both the key and the data are copied into the container.

If an element exists with the given key, its contents are replaced with the new data.

Errors:

CONTAINER_ERROR_BADARG
The hash table, the key or the data pointers are NULL .

CONTAINER_ERROR_READONLY
: The hash table is read-only. No modifications allowed.

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A positive number if the operation added a new element, zero if the data was written into an existing element, or a negative error code if an error occurred.

Description:
Apply will call the given function for each element of the array. The first argument of the callback function receives they key ,the second is the length of the key. The third is a pointer to one element of the table. The fourth argument of the callback is the ëxtraArg" argument that the Apply function receives and passes to the callback. This way some context can be passed to the callback, and from one element to the next.

Note that the result of the callback is not used. This allows all kinds of result types to be accepted after a suitable function type cast.

If the dictionary is read-only, a copy of the element will be passed to the callback function.

Errors:

CONTAINER_ERROR_BADARG
Either the hash table pointer or Applyfn are NULL .

CONTAINER_ERROR_NOMEMORY
The hash table is read-only and there is no more memory to allocate the buffer to copy each element.

Description:
Erases all stored data and releases the memory associated with it. The hash table header is not destroyed, and its contents will be the same as it was when initially created. It is an error to use this function when there are still active iterators for the container.

Returns:
The result is greater than zero if successful, or an error code if an error occurs.

Errors:

CONTAINER_ERROR_BADARG
The hash table pointer is NULL .

CONTAINER_ERROR_READONLY
The hash table is read only.

Example:

HashTable *ht;
int m = iHashTable.Clear(ht);

Copy

HashTable *(*Copy)(const HashTable *Orig,Pool *pool);

Description:
Copies the given hash table using the given pool. If "pool" is NULL ,the pool of the given hash table will be used.

Errors:

CONTAINER_ERROR_BADARG
The hash table pointer is NULL .

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Create

HashTable *(*Create)(size_t ElementSize);

Description:
Creates a new hash table and initializes all fields. The table will use the current memory manager for its pool.

Errors:

CONTAINER_ERROR_BADARG
The parameter is zero or bigger than the maximum size the implementation supports.

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

deleteIterator

int (*deleteIterator)(Iterator *);

Description:
Releases the memory used by the given iterator.

Errors:

CONTAINER_ERROR_BADARG
The parameter is NULL .

Returns:A positive value if successful or a negative error code.

Erase

int (*Erase)(HashTable *HT,void *key,size_t keyLength);

Description:
Removes from the hash table the element with the given key.

Errors:

CONTAINER_ERROR_BADARG
The hash table parameter or the key pointer are NULL , or the keyLength is zero.

Returns:
A positive number if the operation completed, a negative error code otherwise.

CONTAINER_ERROR_BADARG
The hash table parameter or the key pointer are NULL , or the keyLen parameter is zero.

Returns:
A pointer to the element or NULL if no element with the specified key exists.

GetFlags

unsigned (*GetFlags)(const HashTable *HT);

Description:
Returns an unsigned integer with the state of the table.

Load

HashTable *(*Load)(FILE *stream,ReadFunction readFn,void *arg);

Description:
Reads a table previously saved with the Save function from the stream pointed to by stream. If readFn is not NULL , it will be used to read each element. The ärg" argument will be passed to the read function. If the read function is NULL , this argument is ignored and a default read function is used.

Errors:

CONTAINER_ERROR_BADARG
The given stream pointer is NULL .

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:
A new table or NULL if the operation could not be completed. Note that the function pointers in the array are NOT saved in most implementations, nor any special allocator that was in the original table. In most implementations those values will be the values by default. To rebuild the original state the user should replace the pointers again in the new table.

Description:
Merge two hash tables into one new hash table. If the same key is present in both tables, call the supplied merge function to produce a merged value for the key in the new table. Both hash tables must use the same hash function.
The arguments should be:

The pool to use when allocating memory. If NULL , the pool of the "base" hash table will be used.

The first table to be used in the merge.

The second table

An argument to pass to the merger function.

NewIterator

Iterator *(*NewIterator)(HashTable *HT);

Description:
Allocates and initializes a new iterator object to iterate this table. The exact sequence of each object returned is implementation defined but it will be the same for the same dictionary with the same number of elements.

Errors:

CONTAINER_ERROR_BADARG
The parameter is NULL .

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A pointer to a new iterator or NULL if the operation couldn't be completed.

Description:
The contents of the given table are saved into the given stream. If the save function pointer is not NULL , it will be used to save the contents of each element and will receive the arg argument passed to Save, together with the output stream. Otherwise a default save function will be used and arg will be ignored.
The output stream must be opened for writing and must be in binary mode.

Errors:

CONTAINER_ERROR_BADARG
The array pointer or the stream pointer are NULL .

EOF A disk input/output error occurred.

Returns:
A positive value if the operation completed, a negative value or EOF otherwise.

Description:
Replaces the current error function for the given table with the new error function if the
ErrorFunction
parameter is different from NULL . If
the
HT
parameter is NULL the function returns the value of the current default error function.

Errors:

CONTAINER_ERROR_BADARG
The table pointer is NULL .

CONTAINER_ERROR_READONLY
The table is read only and the function argument is not NULL .

Returns:
The old value of the error function or NULL if there is an error.

Size

size_t (*Size)(const HashTable *HT);

Description:
Returns the number of elements stored in the given table.

Errors:

CONTAINER_ERROR_BADARG
The table pointer is NULL .

Returns:
The number of elements stored in the table

Sizeof

size_t (*Sizeof)(const HashTable *HT);

Description:
Returns the number of bytes of storage used in the given table including the size of the elements stored in it.
If HT is NULL the result is the size of the HashTable header.

Returns:
The number of elements stored in the table or the size of the HashTable header if the HT pointer is NULL .

Queues are a type of container adaptors, specifically designed to operate in a FIFO context (first-in first-out), where elements are inserted into one end of the container and extracted from the other.

The sample implementation shows how to implement this container as an ädaptor" container, i.e. based on another container. The implementation uses a linked list to implement a queue
38.

All methods are exactly like the ones in other containers except for Enqueue, that is equivalent to Ädd" since adds one element at the end of the container, and Dequeue, that is the same as PopFront, i.e. pops the first element of the container.

Front

int (*Front)(Queue *Q,void *result);

Description:
Returns the contents of the first element in the given memory area that should be at least the size of the element size of the queue. Note that nothing is changed, and the first element is not erased from the container.

Returns:
A positive number for success, zero if the queue is empty or a negative error code.

Errors:

CONTAINER_ERROR_BADARG
The Queue pointer is NULL .

Back

int (*Back)(Queue *Q,void *result);

Description:
Returns the contents of the last element in the given memory area that should be at least the size of the element size of the queue. Note that nothing is changed, and the last element is not erased from the container.

Returns:
A positive number for success, zero if the queue is empty or a negative error code.

Errors:

CONTAINER_ERROR_BADARG
The Queue pointer is NULL .

GetData

List *(*GetData)(Queue *q);

Description:
Queues are based on the list container. It is not necessary to duplicate all the list functions in the queue interface: this function allows you to access the underlying list and use all the list specific APIs with it.

Returns:
A pointer to the list container or NULL if the queue pointer passed is NULL .

Deque (usually pronounced like "deck") is an irregular acronym of double-ended queue. Double-ended queues are a kind of sequence containers. As such, their elements are ordered following a strict linear sequence.
Deques may be implemented by specific libraries in different ways, but in all cases they allow for adding and retrieving elements at both ends, with storage always handled automatically (expanding and contracting as needed).

Operations to insert and retrieve elements in the middle are not provided because if users need a plain sequential container they can use one. Individual implementation can offer those if they think it is useful. This differs from the C++ implementation.

Here is a little table with a Rosetta stone for deque:

C

Ada

C++

Java

Perl

PHP

Python

PushBack

Append

push_back

offerLast

push

array_push

append

PushFront

Prepend

push_front

offerFirst

unshift

array_unshift

appendleft

PopBack

Delete_Last

pop_back

pollLast

pop

array_pop

pop

PopFront

Delete_First

pop_front

pollFirst

shift

array_shift

popleft

Back

Last_Element

back

peekLast

$array[-1]

end

< obj > [-1]

Some functions that the C++ interface provides like is_empty() can be obtained in this implementation simply by invoking:

The deque container can be implemented as an adaptor container, for instance based on a double linked list or in an vector. In any case the underlying container interface is not visible.

Apply

void (*Apply)(Deque *d,int (Applyfn)(void *,void *),void *arg);

Description:
Will call the given function for each element. The first argument of the callback function receives an element of the array. The second argument of the callback is the arg argument that the Apply function receives and passes to the callback. This way some context can be passed to the callback, and from one element to the next.
Note that the result of the callback is not used. This allows all kinds of result types to be accepted after a suitable cast.
If the array is read-only, a copy of the element will be passed to the callback function.

Errors:

CONTAINER_ERROR_BADARG
Either the deque or Applyfn are NULL .

CONTAINER_ERROR_NOMEMORY
The list is read-only and there is no more memory to allocate the buffer to copy each element.

Back

int (*Back)(Deque *d,void *outbuf);

Description:
Copies into the given buffer the last element stored in the Deque d.

Errors:

CONTAINER_ERROR_BADARG
Either d or outbuf are NULL .

Returns:
A positive value of the operation completed, zero if the container is empty, or a negative error code otherwise.

Clear

int (*Clear)(Deque *Q);

Description:
Erases all elements stored in the queue and reclaims the memory used. The Deque object itself is not destroyed.

Errors:

CONTAINER_ERROR_BADARG
The deque pointer is NULL .

CONTAINER_ERROR_READONLY
The deque is read-only. No modifications allowed.

Contains

size_t (*Contains)(Deque * d, void* item);

Description:
Searches the deque for the given data, returning its (index one based) position or zero if not found.
Errors

CONTAINER_ERROR_BADARG
The deque pointer is NULL .

Returns:
The index of element or zero if not found.

Copy

Deque *(*Copy)(Deque *d);

Description:
Makes a copy of the given deque.

Errors:

CONTAINER_ERROR_BADARG
The deque pointer is NULL .

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A pointer to the new container or NULL if the operation did not complete.

Create

Deque *(*Create)(size_t elementSize);

Description:
Creates a new Deque container using ëlementSize" as the size that each element will have.

Errors:

CONTAINER_ERROR_BADARG
The elementSize parameter is zero or bigger than what the implementation supports.

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A pointer to the new container or NULL if the operation did not complete.

Description:
Compares the given deques using their comparison function. If they differ in their size, flags, or compare functions they compare unequal. If any of their elements differ, they compare unequal.
If both d1 and d2 are NULL they compare equal. If both are empty, they compare equal.

Errors:

None

Returns:
The result is one if the deques are equal, zero otherwise.

Front

int (*Front)(Deque *d,void *outbuf);

Description:
Copies into the given buffer the first element stored in the Deque d.

Errors:

CONTAINER_ERROR_BADARG
Either d or outbuf are NULL .

Returns:
A positive value of the operation completed, zero if the container is empty, or a negative error code otherwise.

Erase

int (*Erase)(Deque * d, void* item);

Description:
Erases the first occurrence of the given element from the container if found, starting from the front.

Errors:

CONTAINER_ERROR_BADARG
The deque pointer or the item pointer are NULL .

CONTAINER_ERROR_READONLY
The deque is read-only. No modifications allowed.

Returns:
A positive number if the item was found and erased, zero if the item wasn't found, or a negative error code if the operation did not complete.

Finalize

int (*Finalize)(Deque *d);

Description:
Reclaims all memory used by the container erasing all elements, if any. Then it destroys the container object itself.

Errors:

CONTAINER_ERROR_BADARG
The deque pointer is NULL .

CONTAINER_ERROR_READONLY
The deque is read-only. No modifications allowed.

Returns:
A positive number if the operation completed, a negative error code otherwise.

GetFlags

unsigned (*GetFlags)(Deque *d);

Description:
Retrieves the state of the flags. If the implementation doesn't support this field this function always returns zero.

Errors:

CONTAINER_ERROR_BADARG
The deque pointer is NULL .

Returns:
The state of the flags field.

Load

Deque *(*Load)(FILE *stream,ReadFunction readFn,void *arg);

Description:
Reads a deque previously saved with the Save function from the stream pointed to by stream. If readFn is not NULL , it will be used to read each element. The ärg" argument will be passed to the read function. If the read function is NULL , this argument is ignored and a default read function is used.

Errors:

CONTAINER_ERROR_BADARG
The given stream pointer is NULL .

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:
A new deque or NULL if the operation could not be completed. Note that the function pointers in the deque are NOT saved in most implementations, nor any special allocator that was in the original table. In most implementations those values will be the values by default. To rebuild the original state the user should replace the pointers again in the new table.

PopBack

int (*PopBack)(Deque *d,void *outbuf);

Description:
Copies into the given buffer the last element stored in the Deque d, then erases the element from the deque.

Errors:

CONTAINER_ERROR_BADARG
Either d or outbuf are NULL .

Returns:
A positive value of the operation completed, zero if the container is empty, or a negative error code otherwise.

PopFront

int (*PopFront)(Deque *d,void *outbuf);

Description:
Copies into the given buffer the first element stored in the Deque d, thnen erases the element from the deque.

Errors:

CONTAINER_ERROR_BADARG
Either d or outbuf are NULL .

Returns:
A positive value of the operation completed, zero if the container is empty, or a negative error code otherwise.

PushBack

int (*PushBack)(Deque *d,void *element);

Description:
Adds the given element to the end of the deque. It is assumed that ëlement" points to a contiguous memory area of at least ElementSize bytes.

Errors:

CONTAINER_ERROR_BADARG
The deque or the element pointers are NULL .

CONTAINER_ERROR_READONLY
The deque is read-only. No modifications allowed.

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A positive number if the operation added a new element, or a negative error code if an error occurred.

Description:
The contents of the given deque are saved into the given stream. If the save function pointer is not NULL , it will be used to save the contents of each element and will receive the arg argument passed to Save, together with the output stream. Otherwise a default save function will be used and arg will be ignored.
The output stream must be opened for writing and must be in binary mode.

Priority queues are queues where each element has a priority associated with it. In this implementation the elements with the lowest priority
associated with the data are served first.

The value of the priority key must be within the bounds set up by the two manifest constants:

CCL_PRIORITY_MIN
CCL_PRIORITY_MAX

They are defined by the implementation and they define a subset of an integer or long integer range. This allows the implementation to save some
values for special "markers" if needed. If the implementation doesn't need this feature it can define the bounds as to cover the full
possible range for the key type.

The key type is defined as the C99 type intptr_t: the integer type that can hold a pointer.

The example uses two loops: one for filling the priority queue, the other for printing all its elements.

We create the priority queue
with an element size of 20. This is more than enough to hold a character string containing the textual representation of the priority.

In the first loop (lines 9 to 15) we associate a priority that we obtain from the random number generator, to a character string containing
the text representation of it. We use the Push primitive to add to the priority queue (line 13). In case of any error we stop.

We print some information about the queue in lines 16-18. In line 19 we store the length of the queue in a temporary variable.
This is necessary since we will use the Pop primitive that erases the lowest priority element, so the length of the queue will
change. We can't use MAX_ITERATIONS either because it could be that the queue doesn't have its full length because an error
in line 13 forced us to break the first loop in line 14 withoutb reaching MAX_ITERATIONS39.

The output is obviously insorted order, since the queue releases the data from the lowest priority to the highest.

Bloom filters allow you to determine cheaply and quickly if an element is member of a set without actually looking into the large set. This container
doesn't store any data, just a series of bits indicating whether the element is there.
It can return false answers, specifically a false positive meaning
it can answer ÿes, the element is there" when in fact it is not. When it tells you however that the element is not there you can be sure
it is not in the set. The probability that a false answer occurs can be calculated in function of the size reserved for the bit table: the bigger
the table, the smaller the probability of a false answer for a fixed number of elements. 40

Description:
Returns the space in bytes that would occupy a bloom filter to hold the given number of elements with the given probability. The probability parameter
should be greater than zero and smaller than 1.0. For values very close to the values zero and one,
a huge number of bits can be necessary and the filter
creation function will return NULL because of lack memory problems.

Errors:

CONTAINER_ERROR_BADARG
The probability is smaller or equal than zero, or bigger or equal than one.

Returns:
The number of bytes needed or zero in case of error.

Create

BloomFilter *(*Create)(size_t maxElements,double probability);

Description:
Creates and initializes a filter with space enough to hold MaxElements with the given probability for a false answer. The probability parameter
should be greater than zero and smaller than 1.0. For values very close to the values zero and one,
a huge number of bits can be necessary and the filter
creation function will return NULL because of lack memory problems.

Errors:

CONTAINER_ERROR_BADARG
The probability is smaller or equal than zero, or bigger or equal than one.

CONTAINER_ERROR_NOMEM
There is no memory for the allocation of the necessary data structures.

Returns:
A pointer to a newly allocated bloom filter or NULL in case of error.

Add

size_t (*Add)(BloomFilter *b,const void *key,size_t keylen);

Description:
Adds the given key to the filter. The keylen argument should be the length of the key, that should never be zero.

Errors:

CONTAINER_ERROR_BADARG
The filter pointer or the key pointer are NULL , or the keylen is zero.

CONTAINER_ERROR_CONTAINER_FULL
. The maximum number of elements has been reached.

Returns:
The number of elements in the filter or zero if there is an error.

Find

int (*Find)(BloomFilter *b,const void *key,size_t keylen);

Description:
Searches the given key in the filter.

Errors:

CONTAINER_ERROR_BADARG
The filter pointer or the key pointer are NULL , or the keylen is zero.

Returns:
One if the element is found, zero if it is not, or a negative error code if an error occurs.

Clear

int (*Clear)(BloomFilter *b);

Description:
Removes all elements from the filter. No memory is released.

Errors:

CONTAINER_ERROR_BADARG
The given pointer is NULL .

Returns:
One if all elements were cleared, a negative error code otherwise.

Finalize

int (*Finalize)(BloomFilter *b);

Description:
Releases all memory held by the filter.

Errors:

CONTAINER_ERROR_BADARG
The given pointer is NULL .

Returns:
One if all elements were cleared, a negative error code otherwise.

Value arrays are a group of containers that store the basic types of the language: short, int, long, long long, float, double, long double
and have some specialized operations that should be done in hardware when the underlying CPU allows it. The objective here is to simplify the
vector interface replacing the void * with the concrete type that these arrays hold.

We have the following ValArrays:

Name

Interface name

Element type

ValArrayShort

iValArrayShort

short

ValArrayInt

iValArrayInt

int

ValArrayUInt

iValArrayUInt

unsigned

ValArrayLong

iValArrayLong

long

ValArrayDouble

iValArrayDouble

double

ValArrayFloat

iValArrayFloat

float

ValArrayLongDouble

iValArrayLongDouble

long double

ValArrayLLong

iValArrayLLong

long long

ValArrayULLong

iValArrayULLong

unsigned long long

ValArraySize_t

iValArraySize_t

size_t

Some types can be just aliases for other types. For instance when int and long have the same size there is no point in providing a separate
implementation. This will be always the case with the type size_t that will be an alias for one of the unsigned types. This type is needed to
represent arrays of indices that can be used to select elements into another array.

The operations supported are the same as the vector data type with several differences:

Simplified interfaces. For instance in the vector container the result of GetElement is always a pointer to the data. ValArray simplifies this by
using directly the underlying type as return value. The functions that change their signature are:

Contains. Second parameter is not a pointer but the underlying type.

Erase. Second parameter is not a pointer but the underlying type.

Apply. The apply function receives the underlying type and not a pointer.

Add. The second argument is the underlying type.

GetElement. Returns the underlying type.

PushBack.Second argument changes.

PopFront. Returns the underlying type.

InsertAt. Second argument.

ReplaceAt. Third argument

IndexOf. Second argument.

Insert. Second argument

AddRange. Second argument is not a void pointer but a pointer to the underlying type.

CopyElement. Second argument is not a void pointer but a pointer to the underlying type.

CopyTo. The return type is not a void ** but a pointer to an array of the underlying type.

No destructors. There is no point in using destructors with the basic types.

No extra arguments used in the comparison function. The comparison is done inline whenever possible. The function SetComparisonFunction
is accepted but does nothing.

Creation functions do not need the element size parameter.

The GetElementSize returns the size in bytes of the underlying type but doesn't use its argument that can be NULL .

The Save and Load functions do not need a user defined save/load function since it is obvious how the basic
types are to be written to the disk: they contain surely no pointers that need to be followed. Their signature is changed.

Each operation described below needs two compatible arrays, i.e. arrays that have the same number of elements. If that is not the case an error
occurs. A single number can be used in place of an array, extending it to the shape of the array. The naming convention is to add the "Scalar" token
to the operation name, so we have for instance MultiplyWith and MultiplyWithScalar.

In general all operations use the data of the left argument and write their results into the left argument. The right argument remains unmodified.
This allows to construct efficient RPN evaluators to avoid allocating intermediate results.

A slice is a description of a certain portion of the array. It has three fields:

Start, The zero based index of the element that starts the slice.

Length. The number of elements that are selected by the slice.

Increment. The number of elements that are skipped between elements when passing from one element of the slice to the next.

When a ValArray is created, the slice used is the default one: 0, Size(ValArray), 1. The slice starts at element zero,
has the same number of elements that the number of elements in the array, and its increment is 1. Using the API SetSlice and
ResetSlice you can modify the elements that will be selected for all operations. When a slice is active, all elements that aren't in the
selected slice are ignored.

Slices are maintained by the library automatically. If you erase elements from the array until the slice is empty, the library automatically
resets the slice. If you add elements, the length of the slice will increase if necessary.

A Mask is a boolean vector of ones or zeroes that selects elements from the array. It can be implemented as a bitstring or as a sequence
of bytes, this is implementation defined. When used in combination with some ValArray operation, it selects the elements that will be affected
by the operation.

This is a generic interface description. The ElementType token is replaced in each ValArray by the underlying type:
int, double, etc. In the same
style, ValArray and the ValArrayInterface tokens are replaced with the corresponding value array name and interface name.

Description:
If any of the values in the source array is smaller than zero it will be negated. This function is only defined for signed or floating point types.
It has no meaning with unsigned types.

Returns:Zero if the array was empty, a positive number if successful.

Accumulate

ElementType (*Accumulate)(ValArray *src);

Description:
Calculates the sum of all the elements of the given vector. If a slice definition is active only the slice elements are considered.

Errors:

None are mandatory but implementations should check for overflow when possible.

Returns:The sum of the elements.

Add

int (*Add)(ValArray *AL,ElementType newval);

Description:
Adds an element at the end of the array. If a slice is active, the increment field (stride) will be used: the new element will be separated by the
increment field of the slice and empty fields will be filed with zeroes. The length of the slice will be incremented by one.

Errors:

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A positive number if the element was added or a negative error code otherwise.

AddRange

int (*AddRange)(ValArray *AL,size_t n,ElementType *newvalues);

Description:
Adds a range of elements at the end of the array. If a slice is active, the increment field (stride) will be used: each new element will be separated by
the increment field of the slice and empty fields will be filed with zeroes. The length of the slice will be incremented by n. If n is zero no error
will be issued and the result is a positive number.

Errors:

CONTAINER_ERROR_NOMEMORY
Not enough memory to complete the operation.

Returns:
A positive number if the elements were added or a negative error code otherwise.

Description:
Calls the given function for each element of the array. If a slice is active only the elements in the slice will be used.

Errors:

None

Returns:A positive number

And

int (*And)(ValArray *left,ValArray *right);

Description:
Performs a bitwise AND operation between each element of the right argument with the corresponding element of the left argument. Conceptually this operation is: left &= right. This operation is allowed only between unsigned integer types. For floating point data this operation has no
meaning. If a slice is active only the slice elements are affected. If both arrays have slices they must be compatible, i.e. they must have the
same length.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The arrays or slices have a different number of elements.

Returns:A positive number for success, or a negative error code.

BitLeftShift

int (*BitLeftShift)(ValArray *dst,int shift);

Description:
Shifts left each element of the given ValArray by shift bits. If shift is negative it performs a right shift instead.

Errors:

No errors.

Returns:A positive number or a negative error code if an implementation detects an invalid pointer. This error is not required to be detected.

BitRightShift

int (*BitRightShift)(ValArray *dst,int shift);

Description:
Shifts right each element of the given ValArray by shift bits. If shift is negative it performs a left shift instead.

Errors:

No errors.

Returns:A positive number or a negative error code if an implementation detects an invalid pointer. This error is not required to be detected.

Clear

int (*Clear)(ValArray *array);

Description:
Sets the number of elements to zero but doesn't release any memory. Any slice definitions are cleared.

Errors:

None.

Returns:A positive integer.

Compare

char *(*Compare)(ValArray *left,ValArray *right,
char *bitarray);

Description:
Assigns to each byte of the bitarray the result of comparing the corresponding elements of the left and right arrays. Conceptually this operation is:

byte[i] = (left[i] < right[i]) ? -1 : (left[i] == right[i]) ? 0 : 1

If the bytearray argument is NULL it will be allocated and returned. The allocator used is the one from the left argument.
If it is not NULL it will be assumed that it contains at least
GetSize(left)
positions available.

Errors:

CONTAINER_ERROR_NOMEMORY
. The given byte array argument was NULL but there is no memory to allocate the result.

Description:
Assigns to each byte of the bitarray the result of comparing the elements of the left array with the right argument.
Conceptually this operation is:

byte[i] = (left[i] < right) ? -1 : (left[i] == right) ? 0 : 1

If the bitarray argument is NULL it will be allocated and returned. The allocator used is the one from the left argument. If it is not NULL it will be assumed that it contains at least
GetSize(left)
positions available.

Errors:

CONTAINER_ERROR_NOMEMORY
. The given byte array argument was NULL but there is no memory to allocate the result.

Returns:A pointer to the bitarray or NULL if an error occurs.

CompareEqual

Mask *(*CompareEqual)(ValArray *left,ValArray *right,
Mask bitarray);

Description:
Assigns to each element of the mask the result of comparing the corresponding elements of the left and right arrays. Conceptually this operation is:

bit[i] = (left[i] == right[i])

If the bitarray argument is NULL it will be allocated and returned. The allocator used is the global memory manager.
If it is not NULL it should contain at least enough positions to hold the data.

Errors:

CONTAINER_ERROR_NOMEMORY
. The given byte array argument was NULL but there is no memory to allocate the result.

Description:
Assigns to each bit of the bitarray the result of comparing the elements of the left array with the right argument.
Conceptually this operation is:

bit[i] = (left[i] == right)

If the bitarray argument is NULL it will be allocated and returned. The allocator used is the one from the left argument. If it is not NULL it will be assumed that it contains at least

1+GetSize(left)/CHAR_BIT

positions available.

Errors:

CONTAINER_ERROR_NOMEMORY
. The given byte array argument was NULL but there is no memory to allocate the result.

Returns:A pointer to the bitarray or NULL if an error occurs.

Contains

int (*Contains)(ValArray *a,ElementType data);

Description:
Searches the given data in the array. If any slice specifications are active, only the slice is searched.

Errors:

None

Returns:
One if the given data is stored in the array, zero otherwise.

Copy

ValArray *(*Copy)(const ValArray *A);

Description:
A copy of the given array is performed. The new memory will be allocated using the given array's allocator. If any slice specifications are current,
only the elements of the slice will be copied into the resulting vector, that will have the size of the slice. Slice specifications are not copied.

Errors:

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:The new array.

CopyTo

ElementType *(*CopyTo)(ValArray *AL);

Description:
Copies the whole contents of the given array into a table of newly allocated elements. If a slice specification is active only the slice will be
returned.

Errors:

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Description:
Creates an array with size elements and fills it with elements of the ValArray data type starting with
the startValue argument, and increasing it by the value of increment at each step. The increment value can be negative or zero. If it is zero the array is filled with the same value. This is equivalent to the Fill API43.

Errors:

CONTAINER_ERROR_NOMEMORY
Not enough storage to complete this operation.

Returns:A pointer to the sequence or NULL if an error occurs.

DivideBy

int (*DivideBy)(ValArray *left,ValArray *right);

Description:
Divides each element of the left argument by the corresponding element of the right argument. Conceptually this operation is: left /= right.
If any of the elements of the right argument is zero, an error occurs and the computation stops, leaving the left argument with some elements divided
and others not44.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The arrays have a different number of elements.

CONTAINER_ERROR_DIVIDE_BY_ZERO
The second argument has an element that is zero.

Returns:A positive number for success, or a negative error code.

DivideByScalar

int (*DivideByScalar)(ValArray *left, ElementType right);

Description:
Divides each element of the left argument by the right argument. Conceptually this operation is: left /= right. If the right argument is zero
an error occurs and the left argument remains unchanged.

Errors:

CONTAINER_ERROR_DIVIDE_BY_ZERO
The second argument is zero.

Returns:A positive number for success, or a negative error code.

DivideScalarBy

int (*DivideScalarBy)(ElementType left, ValArray *right);

Description:
Divides each element of the left argument by the right argument. Conceptually this operation is: right = left / right. If the left argument is zero
an error occurs and the right argument remains unchanged.

Errors:

CONTAINER_ERROR_DIVIDE_BY_ZERO
The first argument is zero.

Returns:A positive number for success, or a negative error code.

Equal

int (*Equal)(const ValArray *src1,const ValArray *src2);

Description:
Returns 1 if both arrays are equal, zero otherwise. It is legal to compare an array with NULL . If both arrays are NULL they compare equal. Any slice
definitions in the arrays must be equal. If equal, they restrict the number of elements compared.

Errors:

None.

Returns:True or false depending if the arrays are equal or not.

Erase

int (*Erase)(ValArray *AL,ElementType data);

Description:
Removes from the vector the element that matches the given data.

Errors:

CONTAINER_ERROR_NOTFOUND
No match was found.

Returns:
A negative error code if an error occurred, or a positive value that indicates that a match was found and the element was removed. If the element is not
in the ValArray the result value is
CONTAINER_ERROR_NOTFOUND
.

EraseAll

int (*EraseAll)(ValArray *v,const void *data);

Description:
Removes from the array all elements that match the given data, that is assumed to be a pointer to an element.

Returns:
A negative error code if an error occurred, or a positive value that indicates that at least a match was found and the element was removed. If the
element is not in the list the result is
CONTAINER_ERROR_NOTFOUND
.

EraseAt

int (*EraseAt)(ValArray *AL,size_t idx);

Description:
Removes from the array the element at the given position. If a slice specification is defined for the array, the index is understood as an
index within the slice and not as an index in the array.

Errors:

CONTAINER_ERROR_BADARG
The given vector pointer is NULL .

CONTAINER_ERROR_INDEX
The given position is out of bounds.

Returns:
A negative error code if an error occurred or a positive value that indicates that the element was removed.

Description:
This function is exactly like the Compare function but designed for comparing floating point numbers. Direct comparison of floating point numbers are known to be problematic. This comparison will be realized within the tolerance defined by the fourth parameter

The sample implementation uses the ideas of Donald Knuth
45
as implemeted by Theodore C. Belding
46
In the documentation of its software, Mr Belding writes:

What is needed is a comparison operator that takes into account a
certain amount of uncertainty:

In the above code, a neighborhood is defined that extends a distance
epsilon to either side of y on the real number line. If x falls
within epsilon of y, x is declared to be equal to y (the first case,
above). If x is greater than y by an amount that is greater than
epsilon, x is declared to be greater than y (the second case, above).
If x is less than y by an amount that is greater than epsilon, x is
declared to be less than y (the third case, above).

The problem then becomes to determine an appropriate value of epsilon.
A fixed value of epsilon would not work for all x and y; epsilon
should be scaled larger or smaller depending on the magnitudes of the
numbers to be compared.

A floating point number is represented by two numbers, the significand
(also called the fraction or mantissa) and the exponent, and a sign,
where

0 <= significand < 1

and

number = sign * significand * pow(2, exponent).

Knuth's suggestion is to scale epsilon by the exponent of the larger of the
two floating point numbers to be compared:

∆ = epsilon ×maxExponent

where maxExponent is the exponent of max(x, y). Delta can then be
substituted for epsilon in the code snippets above.

Determining epsilon

Now that we have found a way to scale epsilon to work with a wide
range of x and y, we still need to choose an appropriate epsilon,
before scaling.

If the number of binary digits of error, e, is known, then epsilon
can be calculated as follows:

FLT_EPSILON and DBL_EPSILON are equivalent to 1 ulp for single- and
double-precision numbers, respectively; they are defined in the
standard C header file <float.h>. (An ulp is one unit in the last
place of the significand, or fraction part, of a floating point
number; see Knuth for more details.)

Errors:

CONTAINER_ERROR_NOMEMORY
. The given byte array argument was NULL but there is no memory to allocate the result.

Description:
Assigns to all members of the array a sequence that starts at start, and is incremented by the given amount at each array position.
The start and increment arguments can hold any value without restrictions, unless they go beyond the maximum value allowed for the given data
type

Description:
Prints in the indicated stream each element of the given array using the indicated format string. If the array is empty nothing is printed and
the result is zero. There is a newline character appended to the output if the array wasn't empty and no error occurred.

Errors:

CONTAINER_ERROR_EOF
An output error occurred: impossible to write to the stream.

Returns:
The number of characters written to the stream, zero if the array was empty, or a negative error code.

GetCapacity

size_t (*GetCapacity)(const ValArray *AL);

Description:
Returns the number of elements the array can hold before it needs to reallocate its data.

Errors:

None

Returns:
The array capacity.

GetData

ElementType *(*GetData)(const ValArray *AL);

Description:
Returns a pointer to the data area of the container, or NULL if an error occurs.

Errors:

CONTAINER_ERROR_READONLY
The container is read-only.

CONTAINER_ERROR_BADARG
The given pointer is NULL

Returns:The pointer to the array's data or NULL .

GetElement

ElementType (*GetElement)(const ValArray *AL,size_t idx);

Description:
Returns the value stored at the element with the given index.

Errors:

CONTAINER_ERROR_INDEX
The given position is out of bounds.

Returns:The element's value or the minimum value that can be stored in ElementType if the index is out of bounds.

GetElementSize

size_t (*GetElementSize)(const ValArray *AL);

Description:
Returns the size of the elements stored in the ValArray. The argument is not used and can be NULL .

Errors:

None.

GetRange

ValArray *(*GetRange)(const ValArray *AL,size_t start,size_t end);

Description:
Selects a series of consecutive elements starting at position start and ending at position one less than end.
If start > end or start > Size(ValArray), NULL is returned. If end is bigger than the number of elements in the array, only elements up to the number of elements will be used.

If a slice is active in the array, the arguments will be understood as indices in the slice and not in the original array.

The selected elements are copied into a new array. The original array remains unchanged.

Errors:

None

Returns::
A pointer to a new ValArray containing the selected elements or NULL if an error occurs.

Description:
Copies into the given pointers the contents of the current slice specifications for the given array. If any of the given pointers is NULL nothing is
copied into it and no error is issued. If the array has no slice specification all fields will be set to zero if not NULL , and the result is zero.

Errors:

No errors

Returns:Zero if there isn't any slice specification, a positive number otherwise.

Example:

/* This expression allows you to determine if there
is a slice defined for a given array */
if (iValArray.GetSlice(myValArray,NULL,NULL,NULL)) {
/* The array has a slice defined into it */
}

IndexIn

ValArray *(*IndexIn)(const ValArray *source,ValArraySize_t *indices);

Description:
Returns an array built from indexing the first argument (ßource") with the array of indexes ïndices" that should be an array of
size_t elements. The number of elements of the resulting array is equal to the number of elements of the indexes array.

Errors:

CONTAINER_ERROR_INDEX
Any given position is out of bounds.

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:
A new array or NULL if an error occurs. No partial results are returned. If any index is out of bounds the whole operation fails.

IndexOf

int (*IndexOf)(const ValArray *l,ElementType data,size_t *result);

Description:
Searches for an element in the array. If found its zero based index is returned in the pointer "result". Otherwise the result of the search is
CONTAINER_ERROR_NOTFOUND
. The ëxtraArgs" argument will be passed to the comparison function, that is used to compare elements.

Errors:

CONTAINER_ERROR_BADARG
The given array pointer or the element given are NULL .

Returns:
A positive number if the element is found, or a negative number containing an error code or the negative constant
CONTAINER_ERROR_NOTFOUND
.

InitializeWith

ValArray *(*InitializeWith)(size_t n, ElementType *data);

Description:
Creates and initializes a new ValArray with the given data table. The first argument is the number of items in the table and the second
is a pointer to a storage area that should contain at least the given number of items.
The data is copied into the new array.

Errors:

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:A pointer to the new array or NULL if an error occurs.

InsertAt

int (*InsertAt)(ValArray *src,size_t idx,ElementType newData);

Description:
Inserts the new element. The new element will have the given index, that can go from zero to the vector count inclusive, i.e. one more than the number of elements in the vector.

Errors:

CONTAINER_ERROR_INDEX
The given position is out of bounds.

CONTAINER_ERROR_NOMEMORY
There is not enough memory to complete the operation.

Returns:
A positive value if the operation succeeded, or a negative error code if the operation failed.

Inverse

int (*Inverse)(ValArray *src);

Description:
Computes for each element the inverse (1/element) and stores it in-place.
If a slice definition is active only their elements will be used. If any of the elements is
zero the computation stops and the result is an error code.

Returns:A positive number if successful, a negative error code otherwise.

Max

ElementType (*Max)(const ValArray *src);

Description:
Returns the biggest element in the container. If the container is empty it returns the smallest element that can be stored into the array's data
type49.

Memset

int (*Memset)(ValArray *dst,ElementType data,size_t length);

Description:
Assigns to each element of the argument the given data. Conceptually this operation is: dst = data. If a slice is active in the destination
array only the elements described by the slice are modified. If the given length is bigger than the number of elements in the array, the array
will be expanded to accomodate the new elements.

Errors:

CONTAINER_ERROR_NOMEMORY
There is not enough ressources to expand the array to the desired length.

Returns:A positive number for success, a negative error code otherwise.

Min

ElementType (*Min)(const ValArray *src);

Description:
Returns the smallest element in the container. If the container is empty it returns the biggest element that can be stored into the array's data
type50.

Description:
Returns the index of the first element that is different when comparing both arrays in the passed pointer mismatch. If one array is shorter than the other the comparison stops
when the last element from the shorter array is compared. The comparison stops when the first difference is spotted.

If there are slice definitions in one or both arrays, they will be used.

Errors:

None

Returns:
If a mismatch is found the result is greater than zero and the mismatch argument will contain the index of the first element that compared
unequal. This will be always the case for arrays of different length.

If both arrays are the same length and no differences are found the result is zero and the value pointed to by the
mismatch argument is one more than the length of the arrays.

If an error occurs, a negative error code is returned. The mismatch argument contains zero.

MultiplyWith

int (*MultiplyWith)(ValArray *left,ValArray *right);

Description:
Multiplies each element of the right argument with the corresponding element of the left argument. Conceptually this operation is: left *= right.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The arrays have a different number of elements.

Returns:A positive number for success, or a negative error code.

MultiplyWithScalar

int (*MultiplyWithScalar)(ValArray *left, ElementType right);

Description:
Multiplies each element of the left argument with the right argument. Conceptually this operation is: left *= right.

Errors:

None. Some implementations could detect a bad pointer.

Returns:A positive number for success, or a negative error code.

Not

int (*Not)(ValArray *v);

Description:
Performs a bitwise Not operation of each element of the argument. Conceptually this operation is: v = ~v. This operation is allowed only between unsigned integer types. For floating point data this operation has no
meaning. If a slice is active, only its elements will be affected.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The arrays have a different number of elements.

Returns:A positive number for success, or a negative error code.

Or

int (*Or)(ValArray *left,const ValArray *right);

Description:
Performs a bitwise or operation between each element of the right argument with the corresponding element of the left argument. Conceptually this
operation is: left |= right. This operation is allowed only between unsigned integer types. For floating point data this operation has no
meaning. This operation will only affect the elements in the active slice, if a slice is active.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The arrays have a different number of elements.

Returns:A positive number for success, or a negative error code.

OrScalar

int (*Or)(ValArray *left,ElementType right);

Description:
Performs a bitwise or operation between each element of the left argument with the right argument. Conceptually this operation is: left |= right. This operation is allowed only between unsigned integer types. For floating point data this operation has no
meaning.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The arrays have a different number of elements.

Returns:A positive number for success, or a negative error code.

PopBack

int (*PopBack)(ValArray *AL,ElementType *result);

Description:
Copies the last element into the given result buffer and deletes the element from the container. If the result buffer is NULL , no copy is performed.
If a slice specification is active, the element deleted will be the last element of the slice, and the length of the slice will be reduced by one.
If the slice becomes empty, the slice specifications are reset.

Errors:

None

Returns:
A negative value if an error occurs, zero if the array is empty or greater than zero if the operation succeeded.

Product

ElementType (*Product)(ValArray *src);

Description:
Calculates the product of all the elements of the given vector. If a slice definition is active only the slice elements are considered.

Errors:

None are mandatory but implementations should check for overflow when possible.

Returns:The product of the elements.

Reverse

int (*Reverse)(ValArray *AL);

Description:
Reverses the order of the elements of the given array.

Errors:

CONTAINER_ERROR_NOMEMORY
Not enough memory for intermediate storage available

Returns:
A negative error code if an error occurs, or a positive value if the operation succeeded.

ResetSlice

int (*ResetSlice)(ValArray *array);

Description: Eliminates any slices specifications from the given array.

Errors:

None.

Returns:If a slice specification was removed returns 1, if no slice was defined in the given array returns zero.

RotateLeft

int (*RotateLeft)(ValArray *src,size_t n);

Description:
Rotates left the array by the indicated amount. The first n elements will be written to the end of
the array, and the rest will be shifted left to fill the empty n places.

Errors:

None

Returns:A positive number if something was moved, zero otherwise (the input was zero or a
modulo of the array size).

Description:
Rotates right the array by the indicated amount. The last n elements will be written to the start of
the array, and the rest will be shifted right.

Errors:

None

Returns:A positive number if something was moved, zero otherwise (the input was zero or a
modulo of the array size).

Save

int (*Save)(const ValArray *AL, FILE *out);

Description:
The contents of the given ValArray are saved into the given stream. This function is a simplified version of the Save function in the Vector container
since it doesn't feature a save function. Since ValArrays hold primitive types they are saved in a single write into the output stream.
The output stream must be opened for writing and must be in binary mode.

Errors:

EOF A disk input/output error occurred.

Returns:
A positive value if the operation completed, a negative value or EOF otherwise.

Description:
This function does nothing and returns always NULL . It is retained for compatibility purposes with other containers.

Errors:

None

Returns:Always NULL .

Select

int (*Select)(ValArray *va,Mask *m);

Description:
Using the given mask, the elements where the corresponding mask element is zero are eliminated, those with a mask
value different of zero are retained. The mask must have the same length as the array.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The mask and the array are of different length.

Description:
Using the given mask, the elements where the corresponding mask element is different from zero are copied into a new array, those with a mask
value different of zero are ignored. The mask must have the same length as the array.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The mask and the array are of different length.

Description:
Defines a slice for the given array. The slice starts at the given index, and has length elements. Between each element and the next
there are increment positions. All operations done with the array will be done to the elements defined by the slice.

Constraints:

The start argument can't be greater or equal than the number of elements in the array.

The increment argument must be greater than zero

The length argument must be greater than zero. If it is greater than the number of elements in the array or greater than the number of
elements that would fit with the given increment and start it will be adjusted accordingly.

If any slice specification was defined for the given array it will be replaced by the new one.

Errors:

CONTAINER_ERROR_BADARG
One of the arguments doesn't meet the above constraints.

CONTAINER_ERROR_NOMEMORY
. There is no memory to allocate the slice specifications.

Description:
Returns the total number of elements stored in the array. If there is a slice definition it will not be used. To know the size of a slice use theGetSlice API.

Sizeof

size_t (*Sizeof)(ValArray *AL);

Description:
Returns the total size in bytes of the ValArray, including the header, and all data stored in it. If the argument is NULL , the size of the header only is returned.

Returns:
The number of bytes used by the vector or the size of the ValArray header if the argument is NULL .

Sort

int (*Sort)(ValArray *AL);

Description:
Sorts the given array. The order of the original array is destroyed. You should copy it if you want to preserve it. If a slice specification is active
only the elements in the slice will be sorted.

Errors:

CONTAINER_ERROR_NOMEMORY
Temporary storage for the operation is absent.

Returns:
A positive number if sorting succeeded, a negative error code if not.

SubtractFrom

int (*SubtractFrom)(ValArray *left,const ValArray *right);

Description:
Subtracts each element of the right argument from the corresponding element of the left argument. Conceptually this operation is: left -= right.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The arrays have a different number of elements.

Returns:A positive number for success, or a negative error code.

SubtractFromScalar

int (*SubtractFromScalar)(ElementType left,ValArray *right);

Description:
Subtracts from the left argument the right argument. Conceptually this operation is: right = left - right.

Errors:

None.

Returns:A positive number for success, or a negative error code.

SubtractScalarFrom

int (*SubtractScalarFrom)(ValArray *left,ElementType right);

Description:
Subtracts from the left argument the right argument. Conceptually this operation is: left -= right.

Errors:

None.

Returns:A positive number for success, or a negative error code.

SumTo

int (*SumTo)(ValArray *left,ValArray *right);

Description:
Adds each element of the right argument to the corresponding element of the left argument. Conceptually this operation is: left += right.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The arrays have a different number of elements.

Returns:A positive number for success, or a negative error code.

SumToScalar

int (*SumTo)(ValArray *left,ElementType right);

Description:
Adds each element of the left argument the right argument. Conceptually this operation is: left += right.

Errors:

No errors.

Returns:A positive number for success, or a negative error code.

Xor

int (*Xor)(ValArray *left,ValArray *right);

Description:
Performs a bitwise Xor operation between each element of the right argument with the corresponding element of the left argument. Conceptually this operation is: left ^= right. This operation is allowed only between unsigned integer types. For floating point data this operation has no
meaning.

Errors:

CONTAINER_ERROR_INCOMPATIBLE
The arrays have a different number of elements.

Returns:A positive number for success, or a negative error code.

XorScalar

int (*XorScalar)(ValArray *left,ElementType right);

Description:
Performs a bitwise Xor operation between each element of the left argument with the right argument. Conceptually this operation is: left ^= right. This operation is allowed only between unsigned integer types. For floating point data this operation has no
meaning.

The buffers interface is greatly simplified compared to the interface of a general container.
The usage of a buffer as an intermediate storage means there is no sense in including all the functionality of a container. The library provides two
types:

The Java language provides a typed buffer functionality. This buffers are not resizable, have a cursor and other more sophisticated operations
than the buffers proposed here like slicing and compacting.Their place in the Java class hierarchy is: Object → Native I/O →
ByteBuffer. There are methods for viewing the buffer as float, doubles, etc.

The C# language provides also a typed buffer class of the primitive types (char, float, int, etc). It is called System.Buffer and provides a few
methods for determining its length and read/write a single byte. The language itself doesn't provide any circular buffers class but several
implementations are available in the net. The same situation applies for Java.

In the usual simplicity of that language, the class has around 60 entry points, including specialized templates for some functions. Around 14 auxiliary
types are involved and the documentation for this component goes for around 50 pages. That is the exact opposite of this proposal.

The design objective in this library is to keep buffers small and, while providing functionality, reduce the interface to a minimum. Compacting is not
feasible in C due to the wide use of pointers. If there is a pointer to the data in the buffer, moving it would invalidate the pointer making for hard
to debug crashes 51.

This objects are designed to store sequentially arbitrary data, resizing themselves as necessary. There is a cursor, a pointer that indicates where
the next data item will be written. You can move the cursor, overwriting old data, or leaving holes in the buffer structure
52
.

Noteworthy are the two functions that work with with buffers and files: ReadFromFile and WriteToFile. The allow you to work with a buffer when the file can't be read in one buffer, allowing
piecewise processing of portions of the file.

The function CreateFromFile allows you to read all the file contents into a buffer.

Clear

int (*Clear)(StreamBuffer *b);

Description:
Sets the cursor at position zero and zeroes the whole buffer.

Description:
Creates a new buffer using the given allocator and start size. If the start size is zero a default value is used.

Errors:

CONTAINER_ERROR_NOMEMORY
There is no more memory to complete the operation.

Returns:A pointer to the new buffer or NULL if there is no memory left.

Finalize

int (*Finalize)(StreamBuffer *b);

Description:
Releases all memory used by the buffer.

Errors:

CONTAINER_ERROR_BADARG
The given buffer pointer is NULL .

Returns:A positive value if successful or anegative error code.

GetData

const char *(*GetData)(const StreamBuffer *b);

Description:
Returns a pointer to the data stored in the buffer. The data is read-only (const).

Errors:

CONTAINER_ERROR_BADARG
The given buffer pointer is NULL

Invariants:
The given buffer is not modified. The returned pointer should not be modified.

Returns:A pointer to the buffer's data or NULL, if an error occurs.

GetPosition

size_t (*GetPosition)(const StreamBuffer *b);

Description:
Returns the current cursor position.

Errors:

CONTAINER_ERROR_BADARG
The stream buffer pointer is NULL

Invariants:
The stream buffer is not modified.

Returns:The cursor position or zero if there is an error. Note that zero is also a valid cursor position.53

Read

size_t (*Read)(StreamBuffer *b, void *data, size_t siz);

Description:
Reads siz bytes from the given buffer, starting from the position of the cursor. If the buffer finishes before siz characters are
read, reading stops, and less characters than requested are returned. It is assumed that the data buffer contains at least siz
characters.

Errors:

CONTAINER_ERROR_BADARG
Either the stream buffer, the data buffer are NULL .

Invariants:
None. The given buffer is modified since the cursor is updated to the new position. The given buffer is modified since the data is copied
into it.

Returns:The number of characters copied or zero if there is an error. Note that if the number of requested characters is zero, this function
will also return zero.

ReadFromFile

int (*ReadFromFile)(StreamBuffer *b,FILE *f);

Description:
Fills the given buffer with data from the given file. The cursor is reset to position zero.

Errors:

CONTAINER_ERROR_BADARG
The stream buffer pointer is NULL

Returns:The number of bytes read or a negative error code.

Resize

int (*Resize)(StreamBuffer *b,size_t newSize);

Description:
Resizes the buffer to the requested size. The new size can be bigger or smaller than the current size. All pointers to the data in the buffer
are invalid after this operation.

Errors:

CONTAINER_ERROR_BADARG
The stream buffer pointer is NULL .

CONTAINER_ERROR_NOMEMORY
There is not enough memory to satisfy the request.

Returns:A negative error code if an error occurs, zero if the requested size is equal to the current size, or a positive number
if the request was satisfied.

SetPosition

int (*SetPosition)(StreamBuffer *b, size_t pos);

Description:
Sets the cursor at the given position. If the position is bigger than the size of the buffer the cursor is moved to the end of the buffer.

Description: Returns the allocated size of the buffer. If the buffer pointer is NULL returns the size of the buffer header.

Errors:

None

Invariants:
The given buffer is not modified.

Returns:The size of the buffer.

Write

size_t (*Write)(StreamBuffer *b,void *data, size_t siz);

Description:
Writes into the buffer siz characters from the passed pointer data. The characters are written starting at the cursor
position. If the buffer is too small to hold the data, it will be enlarged using its allocator.

Errors:

CONTAINER_ERROR_NOMEMORY
. There is no more memory to enlarge the buffer.

CONTAINER_ERROR_BADARG
The stream buffer pointer or the data pointer is NULL .

Creates a stream buffer. It assumes success and does not test the return value of the creation function. The buffer is dimensioned too small
for the data it will contain so it has to resize several times.

Prepares a string buffer with sprintf and writes the resulting string including its terminating zero in the stream buffer. Note that
zeroes have no special significance in buffers. It loops ten times doing this operation.

It ends the buffer with a terminating double zero.

It prints the buffer size and the number of characters it has written. Note that they are not the same. The buffer has been resized
several times, and at each time the new capacity is determined by an internal algorithm. Since we did not move the cursor the position of the
cursor give us the number of characters written.

It obtains a pointer to the data in the buffer

It prints all the strings in the buffer to standard output. Each character string from 1 to 9 is 7 bytes long, including its terminating zero.
The tenth string is 9 bytes, also including the terminating zero. We have then: (7*9)+9 = 72.

It destroys the buffer.

WriteToFile

int (*WriteToFile)(StreamBuffer *b,FILE *outfile);

Description:
Writes all the contents of the given buffer to the given file. The cursor is reset to the begin of the buffer.

Errors:

CONTAINER_ERROR_BADARG
The stream buffer pointer or the file pointer is NULL
returns The number of bytes written.

Description:
Adds the given data element to the circular buffer. If the buffer is full, the oldest element's place will be overwritten with the new data
and the container remains full with the same number of elements.

Errors:

CONTAINER_ERROR_BADARG
One or both arguments are NULL .

Invariants:
The given data element is not modified but copied into the container.

Returns:A negative error code if an error occurs. If the container is full zero is returned. If a new element was added a positive number is returned.

Clear

int (*Clear)(CircularBuffer *cb);

Description:
Resets the number of elements inside the container to empty without freeeing the memory used by the buffer.

Errors:

CONTAINER_ERROR_BADARG
The buffer pointer b is NULL .

Returns:A negative error code if an error occurs, or a positive number when the container is reset.

Description:
Creates an empty circular buffer that can hold at most sizeBuffer elements, each element being of size ElementSize. Uses the given allocator
to allocate memory.

Errors:

CONTAINER_ERROR_BADARG
One or both sizes are zero, or the allocator pointer is NULL .

CONTAINER_ERROR_NOMEM
There is no memory left.

Returns:A pointer to a new circular buffer or NULL if an error occurs.

Create

CircularBuffer *(*Create)(size_t ElementSize, size_t sizeBuffer);

Description:
Creates an empty circular buffer that can hold at most sizeBuffer elements, each element being of size ElementSize. Uses the CurrentAllocator
to allocate memory.

Errors:

CONTAINER_ERROR_BADARG
One or both arguments are zero.

CONTAINER_ERROR_NOMEM
There is no memory left.

Returns:A pointer to a new circular buffer or NULL if an error occurs.

Finalize

int (*Finalize)(CircularBuffer *cb);

Description:
Reclaims all memory used by the given buffer.

Errors:

CONTAINER_ERROR_BADARG
The buffer pointer is NULL .

Returns:A positive value if the container is destroyed, a negative error code otherwise.

PeekFront

int (*PeekFront)(CircularBuffer *b,void *result);

Description:
Copies one item from the front of the circular buffer into the given buffer without removing the item from the container.

Errors:

CONTAINER_ERROR_BADARG
The buffer pointer or the result buffer are NULL .

Returns:A negative error code if an error occurs, zero if the buffer was empty, or a positive number if an item was copied.

PopFront

int (*PopFront)(CircularBuffer *b,void *result);

Description:
Copies one item from the front of the circular buffer into the given buffer and removes the item from the container. If the result pointer is NULL
the item is removed but nothing is copied.

Errors:

CONTAINER_ERROR_BADARG
The buffer pointer is NULL .

Returns:A negative error code if an error occurs, zero if the buffer was empty, or a positive number if an item was removed.

This interface allows the user to use containers in a generic way, ignoring its specific type.
Note that there is no "GenericContainer" object; you can't create any generic container. Once a specific container is created, it can be used as a generic container at any time since all containers comply with the generic interface. This interface just dispatches internally to the actual container and therefore incurs in a slight performance cost.
55

Conceptually, the generic interfaces represent a base class (GenericContainer) and two derived classes: Sequential and Associative containers.
It would be possible to derive more classes, for instance a numeric container class that could be implemented in the future, This is left open for future releases of this specification.
56

This functions return the obvious results already described in the documentation of their container-specific counterparts and not repeated here. We only note the absence of a creation function, or any means to add an object.
57

Based on the generic interface, we have generic sequential and associative interfaces. They contain generic functions for adding and removing objects.

No design can ever cover all special cases that can arise during development. The advantage of the interface design is that you can enhance the library by subclassing functions that add functionality you need when absent.
Subclassing means in this context that you replace a function of the library with a new function written by you that either replaces completely the functionality of the library or that either before or after the library function adds some code that implements an enhancement.

There are several ways to enhance the library in this way:

Replace the function in the container interface object. This affects all containers of this type, including those that are already created. This involves simply assigning to the function you want to replace a new function pointer that points to a compatible function. You can save the old value and add some functionality, call the old function pointer to do what the library does, then you can add code that runs after the old library function has finished.

Replace the function in a copy of the functions table of a single object. This way is less intrusive than the former, since only one container is affected: the one where you modify the function table. The downside is that instead of using the simple syntax:

iList.Add

you have to use the container's table:

Container->VTable->Add(...)

This represents quite a different syntax, but this can be less of a problem if you hide it under some convenient macros 58.

On the up side, another advantage of this syntax is that you do not need to change your source code if you change the type of the container. If you write:

myContainer->Vtable->Add(myContainer,object);

this will stay the same for lists, arrays, string collections or whatever sequential container you are using. You can then change completely the type of the container just by changing the declaration.

Converting one type of container into another, or creating a new container with some or all the data of an existing one are routine operations not
specified in the core library.

The reason is that there is a downside to the interface definitions as presented here: all functions within an interface module are declared
static to avoid polluting the user name space with those names. This has the consequence that interfaces are a monolithic piece of
code that can't be splitted.

Converting an object from type Ä" to another of type "B" implies then that we have both interfaces present. If for every container
we would define a conversion into all others, the function table of each container would need all other interfaces and if a user uses just
a single container it would need to link with the whole library. To avoid this problem, no conversions are specified even when surely
converting a ValArrayInt into a ValArrayDouble is an operation that will be needed sooner or later.

This function stops at an error returning a partial result. Other error handling strategies could be to finalize the
incomplete container and return NULL , or call the iError interface and then do a long jump to a recovery point, etc.

Using the Apply API.
There are surely more complex requirements for conversions. For instance we could need to extract only certain parts of the input
container. In that case
writing a special function to be called by Apply is justified. For instance if we have an array of structures representing customers
we could want to make a string collection with the names of all of them.

We can conceptually define an array as a function that maps an input value index into some output that is the value of the
array at that position.

In this context, an infinite array is a function that maps any member from the set of positive natural numbers (a size_t) into some resulting value. This function must have a value for all possible input values of its size_t argument. For instance the function
value=(index+5)/(index−5) is not usable since it would provoke a division by zero at index=5.

Infinite arrays exist in many computer languages.

In APL they were proposed by McDonnel and Shallit in their paper Ëxtending APL to Infinity" 59.

Common lisp has the "Series" construct that is similar to infinite arrays.

The Translucid computer language features each variable as an infinite array of all its values
60

Since in the C language arrays must contain elements of the same type, obvious restrictions apply: all C types have specific bounds (defined in the
appropiate headers) so that a conceptually correct function like the Fibonacci function for instance, is not usable beyond a certain value of the
input index because of output overflow: the Fibonacci numbers grow without limit.

To implement an infinite array using the library is relatively easy. The iVector interface has the necessary hooks for doing this.
When an index error occurs, the library calls the error function of the given vector passing it the name of the function, the integer constant
CONTAINER_ERROR_INDEX and a pointerto the array and the requested index. If the error function returns any other value than NULL ,
the Library will assume that it is a valid pointer to some result where the real value of the array at that position is stored.

Using this information we can write this first simple implementation of an infinite array. The array function will be the identity function
i.e. the array will contain the value of the index at each position.

The central piece of the implementation is the Fn function (lines 6 to 20)
that will be our replacement of the default vector error function. This function
will only return something if the error is an error index (line 12). Otherwise it calls the default function stored in a static pointer.

If the error is the expected index error, we fetch the arguments (lines 15 and 16) and we set the value.
The address of the static area is returned.

We have to write a special creation function (lines 22 to 29) that creates a vector and replaces its error function
with our own, saving the old
value in a global variable. This value will be used if the error is not an index error.

We can now write our test program that returns 10 integers from our array. Its output is

20 21 22 23 24 25 26 27 28 29

Note that our ïnfinite" array is still a perfectly valid vector object and if you use it in a "normal" way it will store the data you give it and
return that data when you index it as any array. What it is shown in the example above is just how we can change the return value of the functions when
an index error is detected by the library. All other uses are untouched and the vector will still behave as a normal vector.

Note also that if the error is something different than the error the software wants to modify the old procedure is called. This means that
this type of changes can be built in a cascade, each one handling only a definite type of error.

We have used here the generic interface using void pointers. Obviously we could use an integer vector instead of the generic one. To do that it would
be necessary to change all lines that contain Vector into intVector and then call the iintVector.Create function
instead of the plain iVector.Create.

Within the error procedure it is possible to call any function of the library. For instance, we can detect that the index error is the
result of the ÏnsertAt" API, and decide to enlarge the array automatically. To do this we should return a non NULL value from within our
error procedure.

As you can see, that is already the case! That is why the next section is called:

Lines 9-15 are concerned with opening the input file, with some error
checking.

In line 16 we create a dictionary, requesting a size of zero for the
data associated with the key since we aren't storing any data, just the
key, and we suppose that the table will contain more or less 500
entries. If the file contains much more lines performance could
suffer but the algorithm would still work.

Lines 19-25 are the main loop of the program. We read each line into
the buffer and add it to then dictionary. If the Ädd" API returns
a positive number the line wasn't there, if it returns zero the
line was already in the dictionary. If the result is negative it
is an error code and we stop the loop aborting the operation. Failure
can be provoked only by lack of memory.

If the result is positive we print the line.

Cleanup is performed in lines 26 and 27: we dispose of the dictionary
and close the file.

We create two string collections containing the text (lines 13-14). Two iterators are used to get each line of both files( lines 16 17). The rest
is just cleanup: we delete the iterators (lines 26-27) and finalize the string collections (lines 29 30).

This solution using the library is shorter and easier to write than a solution reading each line with fgets() but needs enough memory
to hold both files in memory at the same time.

We keep some generality by using a general prototype and definition for the function we are using. We could have defined the callback as:

int (*fn)(int *);

That prototype would have been unusable for lists that use doubles, for instance. With the current definition we can use this "MapcarArgs" structure with any other list.

The actual function we are calling encapsulates all knowledge about the data stored in the list and the operation we perform with that data. The other parts of the software do not need to know anything about it. It returns a static pointer to the result of the operation it performs using the given element as data that will be overwritten at each call. The intended usage is to save that result before making the next call.
It can be defined as follows:

Still, our version of mapcar is still specific to lists. A more general version would use a sequential container to make a mapcar function that would be able to work with any type of sequential container.

The basic idea is to provide an empty container of the desired result type as an extra argument to mapcar. We use an iterator instead of Äpply", obtaining a single compact function that will take any sequential container as input an add the result of the function to any type of sequential container.

Other similar functions can be built from this model. For instance "mapcon", a function that needs two containers to build a resulting container. The result is made out of the results of a binary function that will receive one element from each container.

Note that not all errors are detected, and we stop at the smallest container, producing a result compatible with the smallest of both arguments.
Note too that we make a very superficial compatibility test to see if the arguments contain the same type of object, using their size as an indication. This test would ignore elements of the same size but incompatible, for instance floats and 32 bit integers, or 64 bit integers and double precision elements, etc.

The standard answer to the above problems is to point out that C has a tradition of keeping things simple and expecting programmers that take care of low level details. If you want more error support, you will find out with minimal research a lot of languages ready to make all kinds of hand holding for you.

The objective of the sample implementation is to serve as a guide for the implementers of this proposal. It is not the fastest implementation and it is not the most efficient or compact one. As any other software, it contains bugs, that I hope to iron out with time.

Please note that all the decisions done for the sample implementation are not part of the specifications of the containers library. Other
implementations could do completely different things.

Source files that implement a specific container: list.c vector.c, etc. This containers use a void pointer to receive thir arguments and return
a void pointer when retrieving their data.

Source files that implement a templated container, i.e. the file needs a parameter file and a templated implementation
file. In this type of files we have a small file that defines the templated file parameters (in the form of pre-processor macros) and then just
includes the templated implementation file. This containers receive data of a concrete type passed by value and return the same data passed by value.

File

Description

bitstrings.c

The bitstring container.

bloom.c

The bloom filter container

buffer.c

Growable buffers and circular buffers

ccl_internal.h

Definitions of all the types defined internally by the library.

containers.h

Main header file of the library. It defines all the user visible interfaces.

An interface is a table of functions. This tables are a monolithic construct: if you use only one of the functions of the
interface you will have to link with all of the interface functions, whether you use them or not.

It is important then, that each interface doesn't "pull in" other interfaces since then you would end up linking with the whole library even if
you use a small fraction of it.

Some of this is inevitable though. All interfaces use the observer interface, so if you use any interface the observer interface will be
pulled in. It is important then, that the dependencies of the observer interface be kept to a minimum
61. Problem is, it is necessary for the
observer interface to set the flags of the container being registered as an observed object. This means that the generic interface is needed.

Go avoid pulling in the whole generic interface, the observer functions use the vTable. In the sample implementation all containers
have a table of functions as first interface field. Since the description of the generic container object is published in "containers.h" it can
use the generic notation.

This works because the sample implementation has carefully designed all interface to be binary compatible, allowing an easy implementation
of the generic interface. All vTables have the GetFlags function at the same place, so we always call the correct function.

Another interface that is used by all other interfaces is the error interface. It has been kept as small as possible to avoid pulling in too much
data into the fixed overhead.

Vtable. All containers in the sample implementation contain a pointer to the table of functions of their interface.

Size. The number of elements this container stores.

Flags. Stores the state of the container. The only flag the sample implementation uses is the READ_ONLY_FLAG but many others
are possible, for instance a 'locked' flag for multi-threading access, or a 'copy on write' flag for lazy copy, etc.

ElementSize. All containers in the sample implementation can store objects of the same size. This is not really a limitation since you
can store objects of ANY size by storing a pointer in the container.
An alternative design would store objects of any size but it would need to store the size of each object in addition to the data used by the object.
The specialized containers like bitstrings, string collections or integer/double arrays do not need this field obviously, and its presence is optional.

The list header uses this structure to store the elements62. As you can see, there is no space wasted in a pointer to the element stored. The element stored is placed just behind the Next pointer. The
downside of this decision is that we can't recycle this object to store other different objects of different size.

The figure shows the files associated with the two implementations of the list container. The generic pointers implementation is list.c and the
templated implementation is in listgen.c. The files depending on listgen.c are parameter files for listgen.c.

Some machines require that data be stored at particular addresses, always a multiple of two. For instance SPARC machines require that doubles be
aligned at a multiple of 8. The structure for our list element above would provoke a crash when used to store doubles
63.

In the public containers.h header file we refer always to an abstract structure _List. We define it here. This schema allows other
implementation to use the same header with maybe radically different implementations of their data structure.

Vtable, count, Flags, ElementSize. This fields were described in the generic container section.

timestamp. This field is incremented at each modification of the list, and allows the iterators to detect if the container changes
during an iteration: they store the value of this field at the start of the iteration, and before each iteration they compare it with its current
value. If there are any changes, they return NULL .

Last. Stores a pointer to the last element of the list. This allows the addition of an element at the end of the list to be fast,
avoiding a complete rescan of the list. This field is an optimization, all algorithms of a single linked list would work without this field.

First. The start of the linked list.

Compare. A comparison function for the type of elements stored in the list.

RaiseError. A function that will be called when an error occurs. This field is necessary only if you want to keep the flexibility of
having a different error function for each list that the client software builds. An alternative implementation would store a pointer to an error
function in the interface.

Allocator. A set of functions that allocates memory for this list. In an implementation that needs less flexibility and is more
interested in saving space it could be replaced by the default allocator.

The sample implementation has certainly a quite voluminous header because of a design decision to keep things very flexible. Other implementations
could trim most of the fields, and an absolute minimal implementation would trim Last, Compare, RaiseError, Heap,
and Allocator. If the implementation assumes that only one iterator per container is allowed, the timestamp field could be replace by
a single bit ('changed') in the Flags field.64

(1): In the templated versions of the container this field is defined as: TYPE Data; where TYPE is a type
definition passed to the file as a parameter.

We have now two pointers followed by the stored data. All other fields are exactly identical to the ones in the single linked list. The single
difference is the existence of a free list. This could have been done in the single linked list implementation too.

Arrays are the containers that use the smallest overhead per element: zero. The only overhead is the header structure, whose cost is amortized since it is fixed for all elements that the array can hold.

This is a 'flexible' array however, what means that there is some spare space allocated for allowing further growth, and that different allocation strategies can be followed when allocating a new chunk of array space when the existing array is full.

This container consists of an array of single linked lists. It could have been done with an Vector of List containers but a dedicated implementation is justified because of a greater efficiency. The advantages of the Vector container (secured access, flexible expansion) are not needed since the array has a fixed length that never changes.

String collections are just flexible arrays of pointers to C character strings. They share all the fields of the Vector container, the only specific
field is a context that is passed to the string comparison function. This context can contain flags or other information to use with special text
encodings (wide characters for instance) or other data like regular expressions, etc.

A character array. Each position can hold more values, for instance -1, 0, and 1, to hold the results of comparisons. This solution was retained
for the sample implementation. Item access is simplified and faster, at the expense of course of more memory.

Bit strings do not need the ElementSize field obviously. The BIT_TYPE macro is defined as unsigned char. In general it should be an
unsigned integer type that could be different from char, maybe a 32 bit type or even larger.

This part contains only the functions that the interface offers. It is defined in the public header containers.h.

A private, container specific part that comes right behind the public part and stores additional information that is needed for each container.
For instance the list container will add following fields:

struct ListIterator {
Iterator it; // Includes the public part
List *L; // List this iterator is going through
size_t index; // The position where we are in the list
list_element *Current;// The current element
size_t timestamp; // The timestamp of the list when this
// iterator was created
char ElementBuffer[1];// The current element if needed
};

User code should only see and use the public part, as if the iterator was only the public part. Internally all iterator functions are completely
different functions, specific for the container they should iterate. It looks like from user code, as you were always calling the same function because
the syntax and name is the same. This allows for a certain abstraction in the source code that uses this functions, allowing to express a whole range
of algorithms in terms of general concepts.

Each of the functions that implement GetNextGetFirst, etc starts with a cast of the input argument that is declared as an
Iterator structure to a concrete container iterator like our ListIterator above.

In all those structures there is a common ground. They have:

A pointer to the container the iterator is using.

Some fields for storing the current position within the container, i.e. a cursor.

A timestamp field to detect if the container has changed during the iteration.

A buffer that allows the iterator to store an element of the container. This area contains a copy of the current element. Normally the
iterators return a pointer to the data of each element, but in the case of read only containers a pointer to this area will be returned.
This allows to maintain the read only semantics.

This field will be incremented by each modification of the list. When an iterator is created it will copy the current value of the time stamp, and for
each subsequent operation it will compare the value of the container with its saved copy. They should be always equal, otherwise the iterator will
return always NULL . The only exception to this rule is the iterator Replace function that will modify the container without invalidating
the iterator that calls it. Other iterators to the same container will be invalidated.

The timestamp field should be incremented in all operations that somehow modify the container, not only in the operations that modify the number of
elements.

Notes:

Implementation issues

There is currently no way to know when you delete a container if there are iterators that are still
pointing to it. This could be detected by simply having a counter of the number of iterators a container has, but that would mean more overhead for the
already fat header objects...

The current interface requires that the user calls the deleteIterator function when you are done using the iterator. This is
a source of memory leaks if you forget
to do it. An easier way to do this would be to maintain a list of current iterators, to be freed automatically when the container is
destroyed. Obviously this supposes that you do not create thousands of iterators but that you reuse the iterators for different loops.

Only one container will be shown here in full: the List container. For the others, only some functions will be explained to save space. You are
invited to read the distributed code of course that is part of this work.

This function adds one element at the end. The Add entry point performs the error checking and calls Add_nd an internal
function that does the actual work. This is needed because other functions call internally Add after they have already performed
the error checking.

The Add_nd function requests a new list element (5). If that suceeds the new element must be inserted in the list.
If the list is empty it just establishes the start of the list (9), if not, it adds it after the last element (12). The new
list element is the last one (14). Errors leave the list unchanged. Exclusive access to the list is needed between the line 8 and the line 16 in the code.
This operation is a modification of the list, and it needs to update the timestamp value to notify possible iterators
that they are invalid.

If the Add_nd function was successfull and this container has a registered observer we notify the observer of this
event.

This function calls repeatedly Add_nd for each element of the given array. Any error provokes an abort and the original
list is left unchanged.

Error checking is done in lines 6 to 15, testing for NULL for the list and the data. If the number of elements is zero the
function does nothing and returns zero. The code accepts data as NULL if the number of elements is zero. If n is
zero this code still checks that the list is not NULL , and that the list is not read only, considering both to be errors.
Nothing is specified for those cases and you can't rely on this behavior for other implemetations.

Note that at compile time we do not know the
size of each element and we can't index into this array. We just setup a generic pointer to the start of the data area (16), and
increment it by the size of each element at each iteration (line 35).
This implementation supposes that the size of the elements as assumed by the list is the same as the size of then element as assumed by the calling program.

If an error occurs when adding elements the new elements are discarded, the list is reset to its previous state and an
error code is returned. (lines 20-33). The eventually added elements are discarded (lines 24-30).

Notes:

It would be far more efficient to test at the start of the loop if there is enough space for the
n
list elements than
doing it within the loop. That would eliminate the code for reclaiming the already allocated items. This isn't done because
the list allocator could be the default malloc function that doesn't allow queries of this type.

This function adds the second argument list to the first one. The second list is destroyed because all its elements are inserted into the first one. The result is obtained by pointer manipulation: no data is moved at all, and any pointers to the objects in the second list remain valid.

Error checking is done in lines 4 to 19. Then, the observer interface is considered. Since the second list will be destroyed
a notification is sent to any observers that listen to events in that list. A notification is sent to the first list also,
informing the observers of this event.

The actual work can then begin (lines 26 to 36):
l2
is appended to
l1
and the list header of
l2
is freed.

Notes:

The test for compatibility between both lists is done with the size of an element,
assuming elements of the same size are of the same type. This could
very well be false but there is no portable way of test this at run time. Anyway, since a container doesn't care what is
inside the objects it manages you can store elements of different types but the same size in a single container.

This function calls the given function for each element. If the container is read only, a copy of each element is passed to the called function. This
copy is allocated with "malloc" because it is used for internal purposes, and the standard allocator for the list could be a heap based, i.e. one that
doesn't really free any memory. That could be a problem if repeated calls to Apply are done.

This function does not pass any pointer to the called function to mark the list as changed if the data passed to it is rewritten. This means that
there is no way to let the called function inform the rest of the software of any modifications. This can be justified by the fact that only the data,
not the container itself can be modified, but this can be tricky in multi-threaded environments. Other implementations could pass some pointer or away
to inform the rest of the software that a modification has been done.

This function should clear all stored elements and reset some fields of the header structure so that the resulting list header is almost the same as when it was created. The only difference is that any functions like the comparison function or the error function are not cleared. If they were changed by the user they still remain changed.

Like in other functions we have a no-debug function (named Clear_nd) that assumes all its parameters are correct, and the
official entry point that checks its arguments. If we are compiling with a garbage collector in mind we can save us all the
work of releasing each element since the collector will do that automatically.

After the error checking, this function positions at the given element and copies its contents into the given buffer.
Other designs are obviously possible.

This function could return a newly allocated buffer. This poses other problems like the type of allocator to use. If we use the list allocator we could run into problems if it is a specialized allocator that is designed for allocating list elements from a pool where no 'free' operation exists. Another, more important problem with that solution is that it forces an allocation when none is necessary if the buffer you use is stack based.

The function could require the buffer length to be sure there are no buffer overflows. This solution was discarded because it actually increases the chances of errors: you have to pass the size of the buffer, and if you pass the wrong one more problems arise. Is it an error if you pass more space than is actually needed? It could be an error if the passed size differs from the size of the elements stored or it could be just a consequence that you used the sizeof(buffer) expression with a bigger buffer than necessary.

This function just writes the given element to the disk. Together with the default load function they allow for a very effective serialization package for containers. Obviously here we have a shallow copy, and all this will never work for recursive saves, i.e. for elements that contain pointers.

This routine retrieves the list header object from the hidden part of the iterator and uses its allocator object to free the memory used by the iterator.

The functions NewIterator and deleteIterator should occur in pairs like many others in C: malloc and free, fopen and fclose, etc. It would be very easy to have in the header object a counter of iterators that should be zero when the list is destroyed or cleared.

If two null pointers are passed to the Equal function it returns true. This is a design decision: Equal doesn't have any error result. Either the two objects are equal or not.

A redundant test is done at the end of the function: if the lists have the same count and all elements are equal, link1 and link2 should be NULL . If they aren't that means there is a memory overwrite problem somewhere...

This function should free the memory used by the header object. It is fundamental that this will never be done with an object not allocated with that iterator in the first place, i.e. when the user has called Init instead of Create. This can't be tested in a portable manner since there is no function to verify that a given memory space belongs or not to a given allocator.66

Returns the current object pointed by the given iterator. This function should be called only after GetFirst is called. It verifies this by testing if a correct value is stored in the index field. This value is stored by the NewIterator function. This simple algorithm avoids the usage of an uninitialized iterator at the cost of one integer comparison per call.

This function should set the iteration at the first element of the container, ready to get the iteration started. After the error checking phase it returns a pointer to the data in the first element, or a pointer to a copy of that data if the container is read only.

Advances the cursor to the next element and returns either a pointer to it or a pointer to a copy if the list is read only. The test for the cursor being NULL avoids using GetNext with an uninitialized iterator.

There were heated discussions about this function. In single linked lists it is necessary to go through the whole list at each call to this function. This is extremely inefficient and its usage should be avoided, it is much better to use double linked lists if you are interested in bi-directional cursor positioning. In the other hand this should be a required iterator feature, and rather than filling this function pointer with a function that just returns an error, the user is better served with a function that actually returns the previous item. Besides for short lists the performance lost is quite small, and would justify using lists with smaller overhead per item.67.

A new list is constructed from the given range of elements. The elements are copied. Any error during the construction of the new list provokes a NULL result: the copied elements are destroyed. Only correctly constructed ranges are returned. A recurring problem arises because it is impossible to report any details about the error that stops the copy. The result is actually boolean, either everything worked and there is a non NULL result, or something didn't.

An alternative design would have an integer return code, and a pointer to a result. This option was discarded because it is cumbersome and the most likely reason for Add to fail is lack of memory.

The design of this function went through several iterations. The big problem was the result type: a size_t, that in most cases is an unsigned quantity. A negative error result then was out of the question. But then, how would you indicate an error? 68

A first solution was to return a 1 based index and reserve zero for the 'not found' value. That could work, but was the source of many bugs in the rest of the software when the value was used without decrementing it first.

A second solution was to reserve a value within the size_t range to represent the 'not found' result. That works, and it is doable, but produced other, more subtle, problems in the rest of the sofwtare since in all checks of a size_t, it could be that this size_t has a value that is actually the sentinel value of IndexOf: the tests tended to multiply and the handling of those tests started to become a problem.

Here you see the third iteration: the function receives a pointer to a size_t that will be set if the function returns with a result greater than zero.

Another, completely different issue is the fact that in lists, this function is inefficient since it forces the function that uses the result to restart a list traversal to access the nth element. Much more efficient would be to do something immediately with the result, or to return a list element that allows the calling software to use it without going again through the list.

Problems with those solutions is that they are not portable, and that they would expose the inner workings of the list container to the users. The list_element structure is not even mentioned in the public containers.h.

This function initializes a piece of storage to a list container. This allows the user to use stack storage for the list container, saving an allocation from the heap, and the corresponding need to free that storage.

The load function is long and complex. As always, the process starts with error checking. All streams written to by its counterpart Save are
marked with a container specific globally unique identifier (GUID). This ensures that a load function from the list container will not crash if passed a file that belongs to an array or a dictionary, or a totally unrelated file. The guids can be changed to mark the versions of the software and
allow more advanced versions to read older versions.

Then, the header object is read, what gives the data to continue the process, since we now know the number of elements and the size of each element.

A new list is created with the given element size, and we start reading count elements from the stream. Any error provokes the destruction of the elements read so far and a result of NULL.

Contrary to most versions of this function, PopFront does not return the data of the element but stores it in a pointer that it receives. If the pointer is NULL , the data is just discarded.

The problem with returning a pointer to the first element, is that the user code should remember to discard it when no longer needed, and it should discard it using the same allocator that the list used to allocate it. That would be a very error prone interface.

The operation when RemoveAt is called with the index of the last element is equivalent to the PopBack function, that is absent in the single linked list interface. After much discussions, we decided that the generic interface would have only Push and Pop, and that each container would fill those functions with the most efficient implementation available for it. For lists, the most efficient implementation is PopFront and PushFront. For arrays, the most efficient is PushBack and PopBack. For double linked lists is either.

After error checking (not shown), position the cursor at the right item, then copy from the given data pointer the element size bytes needed.

An open issue is whether the "timestamp" field should be changed. Nothing in the list structure has been changed, only the data stored in the container. Any iterators will go on working as advertised even if this function is called to replace many items in the list. In the other hand, if user programs were making assumptions about the data (for instance a search function doesn't always look again at past items to see if they have been changed) this could bad consequences. As a rule, any change will provoke the incrementing of the "timestamp" counter.

The RotateLeft and the RotateRightfunctions can be implemented without any movement of the stored objects themselves. It suffices to make the list start at another place: n places after the start for left rotates, or n places before the end for right rotates.

The RotateRight/RotateLeft functions check their arguments to the contrary of their ValArray counterparts that do not. This
implementation shows also a checking of values that should be non-null but could be NULL if there is a memory overwrite or another
similar problem.

This function positions the given iterator at the desired position. Several alternatives are possible, for instance position the iterator at a
given item. This can be obtained now only by calling first IndexOf, then Seek, what forces to go through the list twice.

This function returns the old value of the comparison function and sets it to the new one, if the new one is not NULL . This allows to query the comparison function without changing it, avoiding yet another trivial function like GetComparisonFunction. This is just what in other languages like Objective C or others is called a property of the iList object. Objective C makes all this automatic with its synthetise directive.

In C there isn't any such hand holding and you have to write that code yourself. There are several other functions in the same style like SetErrorFunction, Size (that returns the count field) and SetFlags. They aren't listed here but you can look at the code by browsing through the list.c file distributed
with this software.

Returns the number of bytes used by the given list, including the data, and all overhead. For lists, tghis is the size of the header object, and for
each element the overhead of a pointer to the next element and the size of each stored object. With a NULL list pointer returns the size of the list
header object, what allows you to allocate buffers containing a header object and use the Init function.

This function basically builds an array and calls quicksort, nothing really fancy. Note that it calls a modified version of the library
function quicksort, since it needs to pass a context to it for the comparison function.
The default comparison function is listed below:

After the normal error checking of arguments, this function gets the next element after the given one. If there is none, it is impossible
to split the list after after the given element since it is the last. We return NULL (lines 15-16).

If there is an element, it will be the head of the new list. We create a new list (line 17) using the source list allocator
and we set the given element (argument pt) as the first one of the new
list. We then count the elements (lines 20 to 24) in the new list
since we need to fill the "count" field in the new list. This makes this operation much more expensive than it would be
if we didn't maintain a "count" field70.
In lines 27-31 we set the correct fields in the new list, decrease the "count" field in the source list by the number of elements in the new list,
and we note the fact that the input list has been modified in line 31.

Rotating a list is very simple: Cut the list at the desired place, and append the list elements from the start up to the cut point at the
end of the list.

After the error checking is done (lines 3-12) we start
a first loop where we find the place to cut: lines 15-19.
Then, we cut (set the previous element's Next pointer to NULL in line 21) and append the
elements we cutted to the end of the list, updating the list's header.

This function installs a heap to be used by the list. This is very important for huge lists, since performance goes quickly down if you call malloc
for each element you add to the list. Basically, the heap is just a way to allocate memory in blocks so that malloc calls are reduced.

Queues are, to use the C++ terminology, adaptor containers, i.e. containers based on other containers, in this case a list. We describe here
an implementation with the objective to show how those adaptors can be implemented, and how you can restrain the interface of the underlying container
with a small cost.

If passed a NULL queue, we return the size of the Queue header object. Note that we do not return the size of the underlying list even if it has been allocated and uses up space. An alternative design would have required to take into account the list header as it would have been part of the overhead of the Queue object. But in that case we could never know the size of the Queue itself...

Dictionary is an instance of a hash table where the key is supposed to contain character strings (names) that are associated with some data. Hash
tables are normal tables that are indexed by a hash function, i.e. a function that maps character strings into some integer that is used to index the
table. At each slot of the table we find a linked list of elements that were classified by the hash function into the same slot. If we have a good hash function, i.e. one that spreads evenly the elements across the table, we can have a speed up for searching an element of the order of the table size, in the best case.

One of the important aspects of a dictionary implementation is to use a good hash function, i.e. one that distributes evenly the keys. I have picked
up for this work one of the most used functions of this type. Here is the documentation I found for this function in the Apache runtime:

This is the popular `times 33' hash algorithm which is used by perl and that also appears in Berkeley DB. This is one of the best
known hash functions for strings because it is both computed very fast and distributes very well.

The originator may be Dan Bernstein but the code in Berkeley DB cites Chris Torek as the source. The best citation I have found
is "Chris Torek, Hash function for text in C, Usenet message < 27038@mimsy.umd.edu > in comp.lang.c , October, 1990." in Rich
Salz's USENIX 1992 paper about INN which can be found at http://citeseer.nj.nec.com/salz92internetnews.html.

The magic of number 33, i.e. why it works better than many other constants, prime or not, has never been adequately explained by
anyone. So I try an explanation: if one experimentally tests all multipliers between 1 and 256 (as I did while writing a low-level
data structure library some time ago) one detects that even numbers are not useable at all. The remaining 128 odd numbers
(except for the number 1) work more or less all equally well. They all distribute in an acceptable way and this way fill a hash
table with an average percent of approx. 86%.

If one compares the chi2
values of the variants (see Bob Jenkins "Hashing FAQ" at
http://burtleburtle.net/bob/hash/hashfaq.html for a description of chi2), the number 33 not even has the best value.

But the
number 33 and a few other equally good numbers like 17, 31, 63, 127 and 129 have nevertheless a great advantage to the remaining
numbers in the large set of possible multipliers: their multiply operation can be replaced by a faster operation based on just one
shift plus either a single addition or subtraction operation. And because a hash function has to both distribute good and has to
be very fast to compute, those few numbers should be preferred.

Dan Bernstein created this algorithm and posted it in a newsgroup. It is known by many as the Chris Torek hash because Chris went a long way toward popularizing it. Since then it has been used successfully by many, but despite that the algorithm itself is not very sound when it comes to avalanche and permutation of the internal state. It has proven very good for small character keys, where it can outperform algorithms that result in a more random distribution.

Bernstein's hash should be used with caution. It performs very well in practice, for no apparently known reasons (much like how the constant 33 does better than more logical constants for no apparent reason), but in theory it is not up to snuff. Always test this function with sample data for every application to ensure that it does not encounter a degenerate case and cause excessive collisions.

Note that I have slightly modified the algorithm by using a scatter table of 256 positions filled with random numbers. The objective is to avoid
that letters that appear frequently in the text would tend to cluster the keys in the same position.

This default function may not be the best for the data in the user's application. The library has reserved a field in the dictionary header object for a pointer to a hash function that can be changed by the user.

Another important aspect of the dictionary implementation is the decision of how many slots the table should have. I have followed the recommendations of Dave Hanson in his Book "C interfaces and Implementations"72, and I use a small table of primes to decide what size the table should have:

The primes in the table are the nearest primes to the regular powers of two. Table sizes can range from 509 to more than 130000, what gives a really
wide range of table sizes. Obviously, bigger tables could be necessary, and other specialized implementations could use the hint parameter
to extend this algorithm or to use a completely different algorithm altogether.

Call the hash function and use its result modulo the size of the slot table to fetch the list at the indicated slot.

See if the key was absent. If that is the case, we need to add a new key. We copy the key and allocate memory for a new list element
that is initialized afterwards with the copied value of the key and inserted into the list.

Copy in the value. If it was a new key, its value is initialized, if the key was already present we overwrite the old contents.

This function uses strcmp for comparing keys. This has the advantage of simplicity and speed, but in many other contexts a key comparison function
would be necessary, to allow for keys in Unicode for instance, or for binary keys, for instance a GUID or similar binary data.

An important design decision was to replace the data associated with a key if the key is already there. This is a decision that has consequences for
all associative containers, since it must be coherent in all of them. Since the Ïnsert" function allows for non-destructive insertions, Add was
allowed to replace contents since this is a very common operation for instance in some symbol tables, where Ïnsert if absent or replace if present"
is used to ensure that a symbol is associated with a certain value. 73. At the same time we need a Replace function since we want to get an error if the element we want to replace was not found.
A small table makes this clearer

Iterators in sequential containers are conceptually easy: just start at the first and stop at the last. In associative containers however things are
more complicated since there is no obvious way to order them. The solution retained in the sample implementation involves going through all elements
starting at the first element of the slots table, and for each slot go through the linked list of items if any. This guarantees to visit all elements
in a fixed order. As an example of this here is the Apply function that should go through all elements calling the given function for each
one of them.

As we outlined above, we start at slot zero, going upwards. If we find a non-empty slot, we go through the linked list of items.

Iterators are implemented using the same algorithm, and need conceptually two indexes to remember their position: a first index for the slots table,
and another for the position in the list of items at that slot.

The index field remembers the position in the slot table, and the dl field is just a small structure that contains a link to the
next item in the linked list and a pointer to the key. Storing the list element itself spare us the work of going through all the list to position ourselves at each advance of the cursor in the list.

This container is a completely different beast as all other ones we have in the library. It is a probabilistic data structure. It was conceived by
Mr Burton Howard Bloom in 1970 according to D. E Knuth in his Art of Computer Programming.

Bloom filters are designed to cheaply test if a given element is in a large set. It is possible that the filter says that an element is there
when in fact, it is not. But if the filter says it is not there you can be ceratin that the element is not in the set.

You can add elements to the set but not remove them. The more elements you add to the filter, the larger the posibility of getting false positives, i.e.
getting an answer of ÿes, the element is there" when in fact it is not.

The library provides a sample of how a malloc used for debugging allocation problems could look like. It is designed to be enhanced and even if it
has several important features like detection of double free and buffer overflows, it is not a competitor for the professional versions you can find
in the market like valgrind or similar.

The given size will be aligned to a multiple of size_t. It is assumed that this size is the size of a register, and will be
good for any type of allocation. In some machines this may be completely wrong, for instance for some quantities the Intel processors need an
alignment of 16 bytes, and there is no implementation of size_t with that size.

We reserve three words more than the requested size to store:

The "magic number". This is just an integer that will enable us to ensure that we are dealing with a valid block. Blocks that have this number two
words below the address passed to our Free function will be assumed to be real blocks. There
is of course a chance that the memory
could contain that number for other reasons, but choosing a value that can't be a pointer and that is high above 100 millions give us a fighting chance
that the
probablity of hitting a bad positive is fairly low.

The length of the block. This will allow us to verify that nothing was written beyond the required length of the block.

A guard at the end of the block. We will ensure that we can read this quantity when freeing the block.

We obtain memory using malloc. If not available we just return NULL .

We keep a counter of all memory allocated so far. This counter should be zero at program exit. It helps to detect the leaks between two
operations: it suffices to note the value of the counter before some part of the software and then see if the counter returns to the
same value after the module has finished.

We write the two different integers at the start and at the end of the block, together with its size.

We set to zero all memory even if the program didn't ask us. This ensures that any error that accesses uninitialized memory will
always have the same consequences.

The other functions that complete this memory manager (free, realloc calloc) are not shown here (they are available in the source code
of the library). They just undo what Malloc has built, calling the error functions if they detect a problem.

This simple system has several drawbacks.

If a buffer ünderflow" happens, i.e. something is written to memory before the start of the block, our field "length" could be
wrong. Depending on the resulting contents of the length field after the overwrite we could have a bogus length and access some invalid memory.

Memory overwrites after the magic number that guards the end of the block are not detected. This is obviously impossible to detect
unless we would just inspect each memory write, but a few words more after the end of the block could give us some extra security.

For completeness here is the code of the free function for the debugging malloc sample:

Line 8:
We seek to the start of the real block and we point to it with a pointer to int (line 9).

If we find the signature we erase the signature immediately (line 11). This avoids that
we ever process this block again. We get in line 12 the size of the block and we point
to the end of it. If we do not find our magic number it has been erased because our
block was somehow overwritten. We report that and stop any further processing.

If we find our magic number all is OK and we free the block. We set it to zero before
to avoid that its data is used again (line 21).

If we do not find the signature after we seek for it we do nothing but report an error:
the block has been overwritten or we have been handed a bogus pointer to our free
function. Since our data is written before the start of the block, the software assumes
that it is a bad pointer since in most cases buffer overflows go beyond the end
of the block. It could be that it is actually a buffer overflow error however.

This interface allows arbitrary functions to be called when some interesting event happens.
It supposes several actors that play together:

An object that wants to be notified when some event occurs. This object will be represented by
its callback function.

An object that emits events and necessary calls the interface to announce them.

An associative interface that associates objects with their corresponding
observer functions.

The observer interface has three entry points:

Subscribe. This operation is started by an object that wants to be notified of events
happening in a specific container. It calls the associative interface to be notified
when those events occur.

Notify. The container sends events descriptions to the interface. The interface searches
the observer list and if an interested object exists, its associated function is called.

Unsubscribe Either the container is going out of scope or the object that receives
the notifications is going out of scope and wants to stop the process. The associative
interface is called to break the event stream. It can be that an either object is no
longer interested in receiving notifications for a specific container without any
change in scope: One of the objects desires to break the relationship.

The association is between an observed object (the container) and another unspecified object represented by its
callback here. The flags contain in each bit an event code74. If an observer wants to subscribe to several events
it sets different bits in this field.

Note that we do not characterize further the observed object: it is just a void *. This is not
a great idea since the InitObserver function assumes it is a generic container.

We need a table of this objects because several containers could have several observers defined.

We initialize an observer object, and if that succeeds we add it to the association tables.
We use temporary storage for the initialization because the ÄddObserver" function copies
the contents into the table75.

This function assumes that it receives a container that follows the requirements of generic containers,
i.e. it has a Get/Set flags field. It sets a bit in the flags field that is tested at each function
that modifies the number of elements within the container logic. This means in most machines a bit
test, a very fast operation that should not really affect the speed of the library code in a
significant way.

A far more important consideration is that the interface is called with a notification for many
functions that the user hasn't subscribed at all. This could be speeded up simply by storing
the flags somewhere in the container, but the sample implementation doesn't go that far. The
reason is that it is assumed that observers are seldom used, and the objects that have an
observer defined are surely heavyweight objects where the slow down caused by the observer
interface is not that significant.

Of course this assumptions could be very wrong: other, better implementations could decide
otherwise.

The AddObject function is responsible for inserting a new association in the existing table.
First (in 1: above) it searches for a free slot. If a free slot is available
it copies the new association into it and returns.

If there isn't any free slot it attempts to enlarge the table (2:). If an error occurs, the
original table is still valid but no more elements can't be added. It reports the error
and returns with the error code.

This is a simple linear search function. We search for an association that has the same
observed object and in the flags field has a bit set that indicates that is interested in this operation.
If both conditions are true we call the registered function.

The observer object wishes to stop observing. This case is represented by a NULL ObservedObject argument meaning that all
observed objects for this callback should be affected. This is handled in the code marked 1: above.

The observed object (the container) wishes to stop being observed. This case is represented by a NULL callback argument, meaning that
all callbacks are affected. This is handled in the code marked 2: above.

Only a single relationship should be stopped between a single object and a single callback. This is handled in the code marked 3: above.

To erase an item we just set it to zero, supposing that the next time an object subscribes the empty slot will be found and used.
Obviously this method could waste some space in case we ever do only a single relationship in the whole program. The number of slots
that is reserved in the sample implementation is small, to avoid wasting memory in case there are few observers. More sophisticated
implementations can add features here.

All ValArrays are implemented using a template file that receives its parameters from a small c file. The same is done for the header files, that
are also controlled by a header file. Since header files do not contain any definitions, only declarations, the different headers are grouped into
a single header file that includes the templated file several times. Here is an excertp of valarray.h:

Lines 4-9 remove a possible previous definition of the parameters we are going to use for valarraygen.h. Then we start defining the parameters:

_ValArray is ValArraySize_t (line 10)

The ElementType parameter is the actual type of the elements to be stored into each ValArray.

Lines 13 and 14 define symbols used to test for certain attributes within valarraygen.h. Some functions are defined in types that
correspond to those attributes and omitted in the types where they are not. The attributes defined are:

Unsigned. This encloses all unsigned types. In these types bitwise operations are legal.

Integer. This encloses all integer types. The mod operation is defined for these types.

Not Integer implies float. Operations like fcmp are defined only for floats.

All parameters defined, we can include the valarraygen.h file. This file uses the defines above to define the interface data structure.

The really end user visible name is at line 19: the name of the interface.

The valarraygen.h (gen for generic) defines the interfaces for all the parameter types.
Here is a small part of it so that you get the idea:

In a very similar way, the generic ValArray containers for all basic types are organized in a small parameter file "valarrayint.c", "valarraydouble.c"
and others that make the necessary defines so that the underlying valarraygen.c defines a function for each required basic type.
Programming in valarraygen.c is fairly simple. Here is a function that is parametrized by the ElementType macro:

Slice management. All operations in a ValArray are constrained by the current slice, that starts with a slice that encloses the whole array
(the start is zero, the increment is one, and the length of the slice is the length of the array). When a slice is defined for an array, it will be
used, if not, an implicit slice is used that includes the whole array.

Error analysis is simplified for ValArrays, and no NULL checking is done. However hard errors like an index error (trying to index an aray beyond
its bounds) are always reported.

Contrary to the GetElement function in the vector container we do not return a pointer to the element but the element
itself. For the basic types this can always be done and is very efficient. For more complex types use vector instead of ValArray.

The valarraygen.c file can be used to provide for an array of actually any data structure that is small enough to be returned by value.
It suffices to change the ElementType to the concerned structure whose definition must be visible to the compiler. In the next chapter we see how this
could be done.

If you take the source code of a container like ärraylist", for instance, you will notice that all those "void *äre actually a single type, i.e. the type of the objects being stored in the container. All generic containers use "void *" as the type under which the objects are stored so that the same code works with many different types.

Obviously another way is possible. You could actually replace the object type within that code and build a family of functions and types that can be specialized by its type parameter. For instance:

We use the name of the parameter to build a family of names, and we use the name of the type parameter to declare an array of elements of that specific type as the contents of the array. This double usage allows us to build different name spaces for each different array type, so that we can declare arrays of different types without problems.

Using the same pattern, we can build a family of functions for this container that is specialized to a concrete type of element. For instance we can write:

Now we can build a simple program in C that will do the substitution work for us. To make things easier, that program should build two files:

The header file, that will contain the type definitions for our array.

The C source file, containing all the parametrized function definitions.

We separate the commands to change the name of the file from the rest of the text by introducing in the first positions of a line a sequence of three or more @ signs. Normally we will have two of those "commands": one for the header file, another for the c file.

Besides that, our program is just a plain text substitution. No parsing, nor anything else is required. If we write "$(TYPE)" within a comment or a character string, it will be changed too.

The heart of this program is the ßtrrepl" function that replaces a given character string in a piece of text. If you call it with a NULL output parameter, it will return the number of characters that the replacement would need if any. For completeness, here is the code for strrepl:

For instance to substitute by "double" in the template file ärraylist.tpl" we would use:

expand arraylist.tpl double

We would obtain doublearray.h and doublearray.c

BUG: Obviously, this supposes that the type name does NOT contain any spaces or other characters like '*' or "[ ]". If you want to use types with
those characters you should substitute them with a "_" for instance, and make a typedef:

Instead of using a separate program we can try to use the pre-processor to make the grunt of the editing work. This is the path taken by the
sample implementation. We will describe here the listgen.c and listgen.h files that implement a type-generic list container.

Each type needs two files:

A header file where the data structures are declared. The name of this file is composed from the name of the type and the name of the
container. For instance for a double linked list of integers we would have: intdlist.h.

An implementation file where the code for the data type resides. The name is the same as the header file but with a .c extension
76.

In the sample implementation the different files (intlist.h, doublelist.h, etc) are very small files that mainly define a single macro,
including afterwards a generic file with the bulk of the code. For instance here is the intlist.h file:

Replicate all code of the container. This has the advantage of sparing an extra call instruction at run time and making possible use of
specialized code tailored to the specific data type being compiled.

Make a thin translation layer to reuse the code of the container. This has the advantage of minimizing the source code, making bug fixing
easier since the bugs will be fixed in only one place and not in many different specializations.

This first edition of the sample implementation has chosen the second strategy since the code is new, and probably a lot of changes will be necessary
before it stabilizes. In general we have three types of functions:

Functions where the signature has changed: instead of working with a void * they receive the concrete data type of the container specialization. This allows for compile time checking of arguments, what is a very good improvement over the generic void * functions.
An example of those functions looks like this:

Functions that have the same signature and are replaced by the equivalent functions of the generic container at the creation of the new
container.

Functions that for performance reasons are rewritten in a generic way, i.e. their body is present in the specialized container. An example
is the quick sort function, that receives an expression parameter in the form of a compile time macro that is expanded in the body of the
function. This enables a big performance boost: Instead of the sorting function calling a function that does a memcmp of the data, the
comparison expression is used, avoiding two function calls of overhead at each comparison.

Here is a table of all functions of the library. It indicates which functions are implemented in each container. Some containers are fused
together since they implement exactly the same functions: all the ValArray containers are displayed in a single column, the containers
StringList and wStringList share the same column also.

2We were discussing
the specifications of the mismatach function of the C++ STL and why any error analysis is absent. The C++ STL prescribes a bounded
region for the first container, but just a starting point for the second one. If the second is shorter than the specified range of the first
undefined behavior ensues and anything can happen. In many cases this änything" is different each time the same error occurs. In our
specific case mismatch would read from memory that doesn't belong to the container it started with. Depending on the contents of
that memory a crash could happen, or worst, a wrong result returned to the calling software, etc.

3Donald Knuth, the author of the TeX typesetting program can be without doubt be qualified as a good programmer (and an excellent computer scientist). But he, like anybody else, is not without flaws. See:
www.tug.org/texmf-dist/doc/generic/knuth/errata/errorlog.pdf. There are hundreds of entries in that log.

4Matthew Wilson uses a more restrictive definition of a container in his book Ëxtended STL (Vol 1, page 16)" :

A container is a
collection that owns its objects and provides operations by which those objects may be accessed and modified and, optionally, added removed and
rearranged.

By this definition, containers that have just pointers to their elements wouldn't be containers at all.

5There is no automatic cleanup of objects left by active functions in the stack. This can be a problem or not, depending if your use a garbage collection or not. If you use a garbage collector, this problem doesn't even appear: the unused objects will be automatically collected. If you don't, you should test for the return code of each function.

6In general most return values could be a +size_t+,but it would be very difficult to differentiate a huge unsigned number from a
negative error code. Under some versions of UNIX there exists the +rsize_t+for a signed version of +size_t+but it is not in the C
standard.

8The most similar error code using the POSIX standard would be EPERM here.

9In the C99 standard this error is reserved for a wrong sequence of wide character bytes. Here it would
be used for a wrong sequence of operations what somehow changes the meaning of the error code. It is used since the standard has only three error
codes.

C# features a hashtable class that "Represents a collection of key/value pairs that are organized based on the hash code of the key."
according to the documentation from Microsoft.

Java has relatively recently added a HashTable class that "... maps keys to values. Any non-null object can be used as a key or as a value."

Fortran doesn't include them in the language itself but there are librares that implement hash tables in Fortran. For instance
Herbert J. Bernstein implemented a hash table library in Fortran 2003.

In Common Lisp hash tables are standard:
make-hash-table
and other functions implement all the needed functionality.

They were absent from the C++ STL for unknown reasons. They have been now incoporated into the latest C++ standard.

17This incredibly useful feature has been made now optional by the C99 committee, even if it was mandatory when the C99 standard was published.

18
Other frameworks use a similar method. For instance Apple Foundation classes has several classes that take an ällocator" argument, for instance the
CFBundleCreate and other functions that create objects.

19In previous versions these functions returned some useful information in case of success, for instance the number of elements left. The problem is that an int can't span all the possible values of a size_t data type but it is needed to return negative
error codes. If the result type would be a size_t the negative error codes would be transformed in other values, etc. After some iterations
the present solution was used: a positive number is equal to success, without specifying what the positive number is, or what information (if any)
should be coded in it. The sample implementation always returns 1.

21In general it is a bad idea to save elements containing pointers without a custom save/load function. The
pointers restored are with almost certainty wrong when restored in another environment

22Note that the container is not declared as const and could possibly be modified either directly or
indirectly by the function being applied to it. Some modifications like modifying the number of elements could lead to undefined behavior since it
is not required that the Apply loop tests at each iteration if the container has been modified

23This is completely
different to the C++ language. In C++ you may have an invalid iterator or not if you change the underlying container, depending on the
operation and the specific container involved. This interface was discarded for the following reasons:

There are many rules to remember without underlying principles.
You have to know the specifics of each container to know
if the iterators are invalidated or not. This breaks the independence
of the algorithm code from the underlying container.

Any error leads directly to catastrophic consequences instead
of being caught and signaled in an orderly fashion. Worst, errors
do not produce always the same consequences, depending on what
were the contents of the invalid memory you are using, on the
memory allocation pattern, etc. In short, any error leads to
very difficult maintenance problems.

Any modifications of the container type lead to a review of
all code that uses that container since the rules change from
container to container. Iterators that worked could be invalid
now. This another source of errors.

24Contrary to C++ const directive this is done at run time and an explicit check of this flag is needed. This has disadvantages
(one instruction and a conditional jump are needed) but it has also advantages: you can set it when you pass some container to another module, and unset it when you need to update the container. This solution is more flexible than the static solution at the cost of a very small runtime cost.

26An open issue is whether the interface of the memory allocator should be extended with functions like
GetSize for instance, that would return the size of a given memory block, or other query functions like isMallocBlock that would
allow to verify if a memory block belongs to the pool. Some proposals were discussed in the discussion group comp.std.c but nothing official
has emerged from the committee meetings

27An alternative design would have been to specify not one type of
observer function but to define a different function type for each possible message the containers could send. We would have then a SubscribeAdd
SubscribeErase SubscribeReplace functions, combined with NotifyAdd, NotifyErase, NotifyReplace functions. That design would have been easier to
control at compile time. It was rejected because of the increased complexity of the interface and the necessity for the user to define a