##
##  OSSP al - Assembly Line
##  Copyright (c) 2002-2005 The OSSP Project <http://www.ossp.org/>
##  Copyright (c) 2002-2005 Cable & Wireless <http://www.cw.com/>
##  Copyright (c) 2002-2005 Ralf S. Engelschall <rse@engelschall.com>
##  Copyright (c) 2002-2005 Michael van Elst <mlelstv@serpens.de>
##
##  This file is part of OSSP al, an abstract datatype of a data buffer
##  that can assemble, move and truncate data but avoids actual copying
##  and which can be found at http://www.ossp.org/pkg/lib/al/.
##
##  Permission to use, copy, modify, and distribute this software for
##  any purpose with or without fee is hereby granted, provided that
##  the above copyright notice and this permission notice appear in all
##  copies.
##
##  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
##  SUCH DAMAGE.
##
##  al.pod: assembly line library manual page
##

=pod

=head1 NAME

B<OSSP al> - Assembly Line

=head1 VERSION

B<OSSP al AL_VERSION_STR>

=head1 SYNOPSIS

=over 4

=item B<Abstract Data Types>:

al_rc_t,
al_t,
al_tx_t,
al_td_t,
al_chunk_t.

=item B<Assembly Line Operations>:

al_create,
al_destroy,
al_append_bytes,
al_prepend_bytes,
al_attach_buffer,
al_splice,
al_setlabel,
al_bytes.

=item B<Traversal Operations>:

al_txalloc,
al_txfree,
al_traverse,
al_traverse_next,
al_traverse_end,
al_traverse_cb.

=item B<Convenience Operations>:

al_flatten,
al_copy,
al_firstlabel,
al_spanlabel.

=item B<Chunk Operations>:

al_chunk_len,
al_chunk_span,
al_chunk_label,
al_same_label,
al_chunk_ptr.

=item B<Error Handling>:

al_error.

=back

=head1 DESCRIPTION

B<OSSP al> defines an abstract data type of a data buffer that can
assemble, move and truncate chunks of data in a stream but avoids
actual copying. It was built to deal efficiently with communication
streams between software modules. It especially provides flexible
semantical data attribution through by-chunk labeling. It also
has convenient chunk traversal methods and optional OSSP ex based
exception handling.

=head1 DATA TYPES

B<OSSP al> uses six data types in its API:

=over 4

=item B<al_rc_t> (Return Code Type)

This is an exported enumerated integer type with the following possible
values:

 AL_OK       Everything Ok
 AL_ERR_ARG  Invalid Argument
 AL_ERR_MEM  Not Enough Memory
 AL_ERR_EOF  End Of Communication
 AL_ERR_INT  Internal Error

=item B<al_t> (Assembly Line Type)

This is an opaque data type representing a data buffer.
Only pointers to this abstract data type are used in the API.

=item B<al_label_t> (Label Type)

This is an opaque pointer type representing a specific data flavour.
You can restrict traversal operations to data that was marked with
the specific flavour. Usually you would cast a pointer to the
object that maintains the data to B<al_label_t>. You may use
NULL as a label but on traversal NULL matches any label.

=item B<al_tx_t> (Traversal Context Type)

This is an opaque data type representing the state of a buffer
traversal operation. Only pointers to this abstract data type are
used in the API.

=item B<al_td_t> (Traversal Direction Type)

This is an exported enumerated integer type with the following possible
values:

 AL_FORWARD        traverse assembly line from beginning to end
 AL_BACKWARD       traverse assembly line from end to beginning
 AL_FORWARD_SPAN   like AL_FORWARD, but stop when label does not match
 AL_BACKWARD_SPAN  like AL_BACKWARD, but stop when label does not match

=item B<al_chunk_t> (Chunk Type)

This is an opaque data type representing a chunk of a buffer during
a traversal operation. Only pointers to this abstract data type are
used in the API. The B<al_chunk_t> type is used to generate a pointer
and byte count to access the data in the buffer.

=back

=head1 FUNCTIONS

B<OSSP al> provides a bunch of API functions, all modelled after the
same prototype: "C<al_rc_t> C<al_>I<name>C<(al_>[C<chunk>]C<_t *,>
...C<)>". This means every function returns C<al_rc_t> to indicate its
success (C<AL_OK>) or failure (C<AL_ERR_XXX>) by returning a return code
(the corresponding describing text can be determined by passing this
return code to C<al_error>). Each function name starts with the common
prefix C<al_> and receives a C<al_t> (or C<al_chunk_t>) object on which
it operates as its first argument.

=head2 Assembly Line Operations

=over 4

=item al_rc_t B<al_create>(al_t **I<alp>);

Create an assembly line abstraction object. 
The object is stored in I<alp> on success.

Example: C<al_t *al; al_create(&al);>

=item al_rc_t B<al_destroy>(al_t *I<al>);

Destroy an assembly line abstraction object.
The object I<al> is invalid after this call succeeded.

Example: C<al_destroy(al);>

=item al_rc_t B<al_append_bytes>(al_t *I<al>, const char *I<src>, size_t I<n>, al_label_t I<label>);

Append I<n> bytes from a storage array at I<src> to the assembly line. The
bytes are copied, memory is allocated as necessary. The data is tagged
with I<label>.

Example: C<al_append_bytes(al, "Goodbye cruel world\n", 20, NULL);>

=item al_rc_t B<al_prepend_bytes>(al_t *I<al>, const char *I<src>, size_t I<n>, al_label_t I<label>);

Prepend I<n> bytes from a storage array at I<src> to the assembly line. The
bytes are copied, memory is allocated as necessary.

Example: C<al_prepend_bytes(al, "Hello world\n", 12, NULL);>

=item al_rc_t B<al_attach_buffer>(al_t *I<al>, char *I<p>, size_t I<n>, al_label_t I<label>, void (*I<freemem>)(char *, size_t, void *), void *I<u>);

Attach the storage array starting at I<p> with size I<n> at the end of
the assembly line. Its content becomes part of the assembly line
and is subject to assembly line operations. The storage array must stay
in scope until it is no longer referenced by the assembly line. When
this happens the function I<freemem> is called with the original pointer
I<p>, size I<n> and an arbitrary pointer I<u>. Passing a NULL pointer
for I<freemem> is valid, then no callback takes place, which might be
appropriate for static buffers.

Example: C<char store[] = "foo\n"; al_attach_buffer(al, store, sizeof(store), NULL, NULL, NULL);>

=item al_rc_t B<al_splice>(al_t *I<al>, size_t I<off>, size_t I<n>, al_t *I<nal>, al_t *I<tal>);

This is the general data move operation modelled after the perl operator
B<splice>.

I<off> and I<n> are byte counts that define a span of bytes within the
source assembly line I<al>. These bytes are moved to the target assembly line
I<tal> while the content of the new assembly line I<nal> is moved to the source
to replace the selected span.

There are two deviations from the Perl operator to avoid copying:

The move to the target assembly line I<tal> appends the data to its end.
The move from the new assembly line I<nal> removes the data from its origin.

The target assembly line I<tal> may be B<NULL>, the data bytes that would
be moved to the target are then discarded. This avoids creation
and destruction of a dummy target.

The new assembly line I<nal> may be B<NULL>, then nothing is inserted into
the source. This avoids creation and destruction of an empty assembly line.

Examples:

 al_t *source;
 al_t *insertion;
 al_t *buffer;

 al_create(&source);
 al_create(&insertion);
 al_create(&buffer);

 al_append_bytes(source, "Hello world\n", 12, NULL);
 al_append_bytes(insertion, "Goodbye cruel", 13, NULL);

 al_splice(source, 0, 5, insertion, buffer);

The buffer now holds the string "Hello".
The source now holds the string "Goodbye cruel world\n".
The insertion is now empty.

 al_append_bytes(insertion, "brave", 5, NULL);
 al_splice(source, 8, 5, insertion, NULL);

The source now holds the string "Goodbye brave world\n".
The insertion is now empty.

 al_append_bytes(insertion, "B", 1, NULL);
 al_splice(source, 0, 8, NULL, buffer);
 al_splice(source, 0, 1, insertion, NULL);
 al_append_bytes(insertion, "\n", 1, NULL);
 al_splice(buffer, al_bytes(buffer)-1, 1, insertion, NULL),

The source now holds the string "Brave world\n".
The buffer now holds the string "HelloGoodbye\n".
The insertion is empty.

=item al_rc_t B<al_setlabel>(al_t *I<al>, size_t I<off>, size_t I<n>, al_label_t oldlabel, al_label_t newlabel);

I<off> and I<n> are byte counts that define a span of bytes within the
source assembly line I<al>. The bytes within that span that match I<oldlabel>
are tagged with I<newlabel>, any existing labels for these bytes are
overwritten.

=item size_t B<al_bytes>(const al_t *I<al>);

Returns the number of bytes stored in the assembly line.

Example: C<al_t *al; size_t count; count = al_bytes(al);>

=back

=head2 Traversal Operations

=over 4

=item al_rc_t B<al_txalloc>(al_t *I<al>, al_tx_t **txp);

Allocate a traversal context.

Example: C<al_tx_t *tx; al_txalloc(&tx);>

=item al_rc_t B<al_txfree>(al_t *I<al>, al_tx_t *tx);

Free a traversal context.

Example: C<al_tx_t *tx; al_txfree(tx);>

=item al_rc_t B<al_traverse>(al_t *I<al>, size_t I<off>, size_t I<n>, al_td_t I<dir>, al_label_t I<label>, al_tx_t *I<tx>);

Start traversing the assembly line I<al> beginning at byte offset I<off>
for up to I<n> bytes in direction I<dir>. If I<label> is not NULL then
you will see only data that was tagged with I<label>. The state of the
traversal is stored in the supplied context I<tx>.

This function fails when the offset is outside the assembly line bounds.


=item al_rc_t B<al_traverse_next>(al_t *I<al>, al_tx_t *I<tx>, al_chunk_t **I<alcp>);

Complete a traversal step on the assembly line I<al> using the initialized
context I<tx>. In each step a chunk descriptor is filled and stored in
I<alcp>. All bytes of the chunk are guaranteed to be stored in a flat
array and can be accessed through the chunk operations described below.

The function returns AL_ERR_EOF when it passes the end (or beginning
in case of backward traversal) of the assembly line.

=item al_rc_t B<al_traverse_end>(al_t *I<al>, al_tx_t *I<tx>, int final);

Clean up internal state of traversal. If I<final> is zero, you may
continue the traversal later by calling B<al_traverse_next>. If
I<final> is non-zero you need to start a new traversal. It is
mandatory that every traversal that was started is finished by
a call to B<al_traverse_end> with I<final> set to a non-zero value.

=item al_rc_t B<al_traverse_cb>(al_t *I<al>, size_t I<off>, size_t I<n>, al_td_t I<dir>, al_label_t I<label>, al_rc_t (*I<cb>)(al_chunk_t *, void *), void *u);

B<al_traverse_cb> is a wrapper function that does a full assembly line traversal in
a single call. In every step a chunk descriptor is passed to the callback
function I<cb> together with a user supplied pointer I<u>. When the
callback function returns AL_OK the traversal continues, when it returns
AL_ERR_EOF the traversal is aborted and AL_OK is returned to the original
caller. Any other return code returned by the callback is passed to the
original caller verbatim.

=back

=head2 Convenience Operations

=over 4

=item al_rc_t B<al_flatten>(al_t *I<al>, size_t I<off>, size_t I<n>, al_td_t I<dir>, char *I<dst>, size_t *I<lenp>);

I<off> and I<n> are byte counts that define a span of bytes with the
assembly line I<al>. These bytes are copied to the storage array I<dst>
which must be sized appropriately.
I<off> must be a valid offset, I<n> must be positive but may exceed
the size of the assembly line.
The actual number of bytes that is copied to the destination is stored
in I<lenp>.
If I<dst> is NULL then no data is copied but the number of bytes is
still counted in I<lenp>. This can be used to precalculate the size
of the needed storage array by passing an arbitrary high maximum size
as I<n>.
If I<dir> denotes a backwards traversal the storage array is filled
from its end.

Example:

 al_t *al;
 char buffer[42];
 size_t actual;

 al_flatten(al, 500, 42, AL_FORWARD, buffer, &actual);

=item al_rc_t B<al_copy>(al_t *I<al>, size_t I<off>, size_t I<n>, al_td_t I<dir>, al_t *I<tal>);

I<off> and I<n> are byte counts that define a span of bytes within the
assembly line I<al>. These bytes are appended to the target assembly line I<tal>,
memory is allocated as necessary.
I<off> must be a valid offset, I<n> must be positive but may exceed
the size of the assembly line.

Example:

 al_t *al;
 al_t *tal;

 al_create(&tal);
 al_flatten(al, 500, 42, tal);

=item al_rc_t B<al_firstlabel>(al_t *I<al>, size_t I<off>, size_t I<n>, al_td_t I<dir>, al_label_t *I<labelp>);

I<off> and I<n> are byte counts that define a span of bytes within the
assembly line I<al>. The label that was attached to the first byte
within the defined span is stored in I<labelp>, otherwise B<al_firstlabel>
returns an error.

=item al_rc_t B<al_spanlabel>(al_t *I<al>, size_t I<off>, size_t I<n>, al_label_t *I<label>, size_t *<offp>, size_t *<spanp>);

I<off> and I<n> are byte counts that define a span of bytes within the
assembly line I<al>. This span is searched for data tagged with the I<label>.
The absolute byte offset of the first byte matching the label and the
length of the span of the same label is returned in I<offp> and I<spanp>
respectively.

=back

=head2 Chunk Operations

=over 4

=item size_t B<al_chunk_len>(al_chunk_t *I<alc>);

Returns the number of bytes in a chunk.

=item size_t B<al_chunk_span>(al_chunk_t *I<alc>, size_t I<off>, size_t I<n>);

I<off> and I<n> are byte counts that define a span of bytes.
B<al_chunk_span> returns the number of bytes that are stored in the chunk.
I<off> must be a valid offset, I<n> must be positive but may exceed
the size of the chunk.

=item al_label_t B<al_chunk_label>(al_chunk_t *I<alc>);

Return the label that was used to tag the data in I<alc>. This can
be NULL.

=item int B<al_same_label>(al_chunk_t *I<alc>, al_label_t *I<label>);

Return true if I<label> matches the label that was used to tag the data
in I<alc>. A NULL I<label> matches everything.

=item char *B<al_chunk_ptr>(al_chunk_t *I<alc>, size_t I<off>);

Returns the pointer to the byte at offset I<off> within the chunk I<alc>.
I<off> must be positive and must not exceed the size of the chunk.
Since all bytes of the chunk are guaranteed to be stored in a flat
array the pointer can be used to reference every byte within the chunk.

Example:

 al_chunk_t *alc;
 char *start, *end;

 start = al_chunk_ptr(alc, 0);
 end   = start + al_chunk_len(alc) - 1;

=back

=head2 Error Handling

=over 4

=item const char *B<al_error>(al_rc_t I<rv>);

Retrieve a string that describes the return code I<rv> in english.

=back

=head1 SEE ALSO

=head1 HISTORY

B<OSSP al> was invented in October 2002 by Michael van Elst
E<lt>mlelstv@serpens.deE<gt> under contract with Cable & Wireless
Germany E<lt>http://www.cw.com/deE<gt> for use inside the OSSP project
E<lt>http://www.ossp.org/E<gt>.

=head1 AUTHORS

 Michael van Elst
 mlelstv@serpens.de

=cut

