File:  [ELWIX - Embedded LightWeight unIX -] / gpl / axl / src / axl_stream.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Fri Feb 17 12:50:03 2012 UTC (12 years, 3 months ago) by misho
Branches: axl, MAIN
CVS tags: HEAD, AXL0_6_7
version 0.6.7

/*
 *  LibAxl:  Another XML library
 *  Copyright (C) 2006 Advanced Software Production Line, S.L.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2.1 of
 *  the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to the Free
 *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307 USA
 *  
 *  You may find a copy of the license under this software is released
 *  at COPYING file. This is LGPL software: you are welcome to
 *  develop proprietary applications using this library without any
 *  royalty or fee but returning back any change, improvement or
 *  addition in the form of source code, project image, documentation
 *  patches, etc. 
 *
 *  For commercial support on build XML enabled solutions contact us:
 *          
 *      Postal address:
 *         Advanced Software Production Line, S.L.
 *         Edificio Alius A, Oficina 102,
 *         C/ Antonio Suarez Nº 10,
 *         Alcalá de Henares 28802 Madrid
 *         Spain
 *
 *      Email address:
 *         info@aspl.es - http://www.aspl.es/xml
 */

#include <axl.h>
#include <math.h>

#define LOG_DOMAIN "axl-stream"

/** 
 * @internal
 *
 * @brief Basic checking which allows to test if the provided size
 * could be satisfied by the current status of the provided stream.
 * 
 * @param stream The stream where the operation will be performed.
 *
 * @param size The size to check.
 * 
 * @return 1 if the size falls out side the stream limit or 0 it not.
 */
#define fall_out_side_checking(stream, size) ((size + stream->stream_index) > stream->stream_size)

/** 
 * @internal
 * 
 * This is the size of the buffer allocated while using the axlStream
 * as a representation of a streaming media, like a file
 * descriptor. This value should contain a value using the (4k * n + 1).
 * 
 * @param stream 
 */
#define STREAM_BUFFER_SIZE 8192

/** 
 * @internal
 *
 * Internal definition used to represent the maximum value used for
 * calls done to match a set of chunks. 
 */
#define MAX_INSPECTED_CHUNKS 30

typedef  enum {
	STREAM_FD,
	STREAM_MEM
}axlStreamType;

struct _axlStream {

	/* current stream content */
	char * stream;

	/* where the current stream is positioned. */
	int    stream_index;

	/* global index for the device being streamed */
	int    global_index;

	/* current stream size */
	int    stream_size;

	/* stream buffer size (maximum amount of bytes to hold, used
	 * also to measure the temporal buffer) */
	int    buffer_size;
	
	/* previous inspected stream size */
	int    previous_inspect;

	/* last chunk get from the stream */
	char   * last_chunk;
	
	/* last near to reference. */
	char   * last_near_to;

	/* last get following reference */
	char   * last_get_following;

	/* support variable for chunk matching */
	char  ** chunks;

	/* support variable for chunk matching */
	int   * lengths;

	/* a reference to the associated element to this stream */
	axlList * elements_linked;
	
	/*The function to execute when the element must be destroyed. */ 
	axlDestroyFunc element_destroy;

	/* Stream type configuration. Information source is coming
	 * from a file descriptor of a memory address. */
	axlStreamType type;

	/* File descriptor associated with the given stream. In the
	 * case the stream if a STREAM_FD, this is the file descriptor
	 * associated. */
	int           fd;

	/* Temporal buffer used by the stream to handle prebuffering
	 * operations.
	 */
	char      * temp;
	char      * decode_temp;
	int         decode_temp_size;
	int         decode_temp_index;
	int         decode_temp_last;
	int         decode_temp_remain;
	char      * source_encoding;

	/* here is how these variables are used to decode content:
	 * 
	 * [   <---  decode_temp buffer ---> ] <- decode_temp_size: total buffer size (bytes)
	 *   ^                ^
	 *   |                |     
	 *   |                +-- decode_temp_last: last valid content in the buffer (bytes)
	 *   +-- decode_temp_index: next content to be consumed (bytes)
	 *
	 * [decode_temp_remain]: is the amount of content pending to
	 * be decoded. That is, starting from decode_temp_remain until
	 * last is the valid content still to be decoded.
	 *
	 * [source_encoding]: is defined to hold the value of the
	 * format for the source.
	 */

	/* more variables used to perform work: at get until */
	char      * valid_chars;
	int         chunk_matched;
	axl_bool    accept_terminator;
	int         result_size;
	int         chunk_num;

	/** 
	 * @internal Internal variable used to notify get_until
	 * function that the last 0 character for stream memory
	 * operation done in a STREAM_MEM must be considered as a
	 * terminator character found.
	 */
	axl_bool        zero;

	/** 
	 * @internal Alloc function to be used to require memory for
	 * chunks to be returned. This is used by Axl Stream to allow
	 * external modules to handle how memory is allocated while
	 * calling to axl_stream_get_until* API. 
	 *
	 * Currently, it is used by the axl_doc module to allocate axl
	 * stream using a string factory.
	 */
	axlStreamAlloc alloc;
	
	/** 
	 * @internal User defined data to be passed to the alloc
	 * function.
	 */
	axlPointer     alloc_data;

	/** 
	 * @internal Function used by the stream to decode content
	 * into utf-8. This is not used in all cases.
	 */
	axlStreamDecode decode_f;
	
	/** 
	 * @internal Reference to user defined pointer provided to the
	 * decode_f function.
	 */
	axlPointer      decode_f_data;

	/** 
	 * @internal Value that allows to signal that the buffer needs
	 * to be expanded (no matter what shows current indexes).
	 */
	axl_bool        needs_expansion;

	/** 
	 * @internal Reference to the content check function
	 * installed.
	 */
	axlStreamContentCheck check_f;

	/** 
	 * @internal Reference to user defined pointer to be passed to
	 * the content check function (check_f).
	 */
	axlPointer            check_f_data;
};


/**
 * \defgroup axl_stream_module Axl Stream Document: Abstract stream where a xml document is expected (also provided string functions)
 */

/** 
 * \addtogroup axl_stream_module
 * @{
 */

/** 
 * @internal
 *
 * @brief Read the next available information on the file descriptor
 * placing that information into the stream.
 * 
 * @param stream The stream where the pre-buffering operation will be
 * performed.
 *
 * @param appended_content New content to be included at the begining
 * of the stream while doing the prebuffer operation. This value could
 * be null.
 *
 * @param appended_size The size for the appended content to be added.
 * 
 * @return \ref axl_true if the requested padding and buffer size were
 * filled or \ref axl_false if end of file was reached. In that case the
 * stream size is not updated.
 */
axl_bool axl_stream_prebuffer (axlStream * stream)
{
	int  bytes_read;
	int  op_result;

	/* check some environment conditions */
	axl_return_val_if_fail (stream, axl_false);

	/* no prebuffering is the stream type is not a file descriptor
	 * source and if the socket is closed */
	if (stream->type != STREAM_FD || stream->fd == -1) {
		return axl_false;
	} /* end if */

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "prebuffering the stream..");

	/* displace memory only if there were data already consumed */
	if (stream->stream_index > 0 && ((stream->stream_size - stream->stream_index) > 0)) {

		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "   moving previous content at the begining of the buffer, current index: %d, size: %d, current status: %s",
			   stream->stream_index, stream->stream_size - stream->stream_index,
			   stream->stream + stream->stream_index);

		/* displace memory already read to be at the begining
		 * of the stream */
		memcpy (stream->temp, stream->stream + stream->stream_index,
			stream->stream_size - stream->stream_index);

		/* now copy displaced content back to the stream */
		memcpy (stream->stream, stream->temp, 
			stream->stream_size - stream->stream_index);

		/* update the index to the positioned at the next byte
		 * available on the buffer */
		stream->stream_size  = (stream->stream_size - stream->stream_index);
	} else {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "   nothing to prebuffer on the head (stream->size=%d, stream-index=%d, buffer-size=%d)",
			   stream->stream_size, stream->stream_index, stream->buffer_size);

		/* check here if the buffer is full of content and a call to
		 * prebuffer was done */
		if (((stream->stream_size == stream->buffer_size) && 
		    (stream->stream_index == 0)) || stream->needs_expansion) {
			/* looks like the caller is prebuffering
			 * having all buffers full of content .. */
			

			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, 
				   "   requested to prebuffer with buffers full of content (stream-index=%d, stream-size=%d, stream-buffer-size=%d, stream-needs-expansion=%d)",
				   stream->stream_index, stream->stream_size, stream->buffer_size, stream->needs_expansion);

			/* unflag expansion requested */
			if (stream->needs_expansion)
				stream->needs_expansion = axl_false;

			/* duplicate the buffer size */
			stream->buffer_size += (stream->buffer_size);
			stream->stream       = realloc (stream->stream, stream->buffer_size + 1);
			stream->temp         = realloc (stream->temp,   stream->buffer_size + 1);

			if (stream->stream == NULL) {
				__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, 
				   "   failed to update buffer sizes (realloc operation failed)",
				   stream->stream_index, stream->stream_size, stream->buffer_size);
				close (stream->fd);
				stream->fd = -1;
				return axl_false;
			} /* end if */
			
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, 
				   "   buffer updated (stream-index=%d, stream-size=%d, stream-buffer-size=%d)",
				   stream->stream_index, stream->stream_size, stream->buffer_size);
		} else {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
				   "   clearing stream-size=%d, having stream-index=%d and stream-buffer-size=%d",
				   stream->stream_size, stream->stream_index, stream->buffer_size);

			/* reset if the stream size is just the content consumed */
			if (stream->stream_size == stream->stream_index)
				stream->stream_size  = 0; 
		}
	}

	/* reset current index */
	stream->stream_index = 0;

	/* check if we have decode functions defined */
	if (stream->decode_f) {
		while (1) {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, 
				   "check temporal decode buffer for pending content: decode_temp_index=%d, decode_temp_last=%d, decode_temp_size=%d",
				   stream->decode_temp_index, 
				   stream->decode_temp_last,
				   stream->decode_temp_size);

			if (stream->decode_temp_last > 0) {
				/* call to decode */
				if (! axl_stream_decode (stream, 
							 /* output */
							 stream->stream + stream->stream_size,
							 /* max output size */
							 stream->buffer_size - stream->stream_size,
							 /* output decoded */
							 &bytes_read, &op_result, NULL)) {
					__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "failed to decode content at the temporal decode buffer");
					return axl_false;
				} /* end if */

				/* check if the decode operation
				 * signaled that not enough espace was
				 * available to decode and no output
				 * was decoed, int his case flag the
				 * stream to do a stream expansion on
				 * the next call */
				if (op_result == 2 && bytes_read == 0)
					stream->needs_expansion = axl_true;

				/* add bytes read to the size */
				stream->stream_size += bytes_read;

			} /* end if */

			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, 
				   "after checking to decode, op_result=%d, bytes_read=%d, stream->buffer_size=%d, stream->stream_size=%d",
				   op_result, bytes_read, stream->buffer_size, stream->stream_size);

			/* stop if no enought space if left and no
			 * more bytes were translated */
			if (op_result == 2) 
				break;

			/* check if there are still space to decode at the stream */
			if ((stream->buffer_size - stream->stream_size) > 0) {
				/* read content inside the decde temp buffer */
				bytes_read = read (stream->fd, stream->decode_temp + stream->decode_temp_last,
						   stream->decode_temp_size - stream->decode_temp_last);
				
				/* update the amount of data available tat the decode temp */
				if (bytes_read > 0)
					stream->decode_temp_last += bytes_read;
				else if (bytes_read == 0 && stream->fd > 0) {
					__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "end of file reached");
					close (stream->fd);
					stream->fd = -1;
				} /* end if */
			} else {
				/* no more space is available at the reading buffer */
				break;
			} /* end if */

			/* check to terminate */
			if (stream->decode_temp_index == 0 && 
			    stream->decode_temp_last == 0 && 
			    stream->fd == -1) 
				return axl_true;
			
		} /* end while */

	} else {

		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "   reading on buffer index: %d, size: %d (starting from: %d, length: %d)",
			   stream->stream_index, stream->stream_size, stream->stream_size, stream->buffer_size - stream->stream_size);

		/* read current content */
		bytes_read = read (stream->fd, stream->stream + stream->stream_size,
				   stream->buffer_size - stream->stream_size);


		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "   bytes read from the file, size %d", bytes_read);

		/* call to check */
		if (stream->check_f) {
			/* call to check */
			if (! axl_stream_content_check (stream, stream->stream + stream->stream_size, bytes_read, NULL)) {
				__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "failed to prebuffer, content check function have failed");
				return axl_false;
			}
		} /* end if */
		
		/* check for end of file reached */
		if (bytes_read == 0) {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "end of file reached");
			close (stream->fd);
			stream->fd = -1;
			return axl_false;
		}
		
		/* set the new size, that is the padding, the content already
		 * read, and the bytes already read */
		stream->stream_size += bytes_read;
	} /* end if */

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "   (before read) current buffer size: %d",
		   stream->stream_size);
	
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "   prebuffered data: stream size: %d, new content: %s",
		   bytes_read, stream->stream + stream->stream_index);

	return axl_true;
}

/** 
 * @brief Creates a new byte stream using as data the string pointer
 * and the size.
 * 
 * The \ref axlStream object is an abstraction that allows to
 * interface with a memory chunk, a file descriptor (or a socket) or a
 * direct path, with a contenience API that allows inspecting and
 * accepting the streamed content.
 *
 *
 * Here is an example on how to create an stream from a memory chunk:
 * \code
 * axlStream * stream;
 * axlError  * error;
 *
 * // create the stream and check result 
 * stream = axl_stream_new (source_content, size_content, -1, NULL, &error);
 * if (stream == NULL) {
 *     printf ("An error was found: %s\n", axl_error_get (error));
 *     axl_error_free (error);
 *     return;
 * }
 * 
 * // stream created
 * \endcode
 *
 * In the case an stream is required to parse a file descriptor
 * (including a socket):
 * 
 * \code
 * stream = axl_stream_new (NULL, -1, NULL, fd, &error);
 * \endcode
 *
 * You can also provide a file path to let the axl stream module to
 * open the file and buffer it as it requires to consume characters:
 *
 * \code
 * stream = axl_stream_new (NULL, -1, "c:/myFiles/document.xml", -1, &error);
 * \endcode
 * 
 *
 * Once initialized the \ref axlStream object, you can use the
 * following function to check and consume characters:
 * 
 * - \ref axl_stream_inspect
 * - \ref axl_stream_peek
 * - \ref axl_stream_accept
 * 
 * There alternatives APIs that allows to get the content until some
 * patter is found (or a set of patterns):
 * 
 * - \ref axl_stream_get_until
 *
 * 
 * @param stream_source A pointer to the memory where the data to be
 * streamed is located.
 *
 * @param stream_size How many bytes are available to perform
 * streaming. You can pass a -1 value to allow the function to
 * calculate the stream source size (stream_source param).
 *
 * @param file_path Optional parameter to allow reading the stream
 * from a file using the open API.
 *
 * @param fd_handler Optional parameter to allow reading the stream
 * from a file descriptor, that could be a file, a socket, etc..
 *
 * @param error Optional \ref axlError reference where errors will be reported.
 * 
 * @return A newly allocated stream instance that should be
 * deallocated by using \ref axl_stream_free. The function could
 * return a NULL value is received a NULL stream or a non positive
 * stream size.
 */
axlStream * axl_stream_new (const char * stream_source, int stream_size,
			    const char * file_path,     int fd_handler,
			    axlError ** error)
{
	axlStream * stream;
	int         fd;

	/* perform some environmental checkings */
	if (file_path != NULL || (fd_handler > 0)) {
		if (fd_handler < 0) {
			/* a file handle */
			if ((fd = open (file_path, O_RDONLY)) < 0) {
				axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "unable to open file a the location provided: %s, check location and permissions.", file_path);

				axl_error_new (-1, "unable to read file provided", NULL, error);
				return NULL;
			}
		}else
			fd = fd_handler;

		/* create the stream holder */
		stream               = axl_new (axlStream, 1);
		stream->buffer_size  = STREAM_BUFFER_SIZE;
		stream->type         = STREAM_FD;
		stream->fd           = fd;

		/* allocate 4k to perform streaming */
		stream->stream       = axl_new (char, stream->buffer_size + 1);
		stream->temp         = axl_new (char, stream->buffer_size + 1);

		/* prebuffer */
		axl_stream_prebuffer (stream);
	}else {
		/* check chunk received */
		if (stream_source == NULL) {
			axl_error_new (-1, "Requested to open a stream without providing an memory chunk, file descriptor or a file path", NULL, error);
			return NULL;
		}

		/* check memory chunk size */
		if (stream_size == -1)
			stream_size = strlen (stream_source);

		/* create the stream holder */
		stream               = axl_new (axlStream, 1);
		stream->buffer_size  = stream_size;
		stream->type         = STREAM_MEM;

		/* copy source */
		stream->stream       = axl_new (char, stream_size + 1);
		memcpy (stream->stream, stream_source, stream_size);
		/* nullify the last entry to avoid problems with
		   printing functions and other APIs relaying on
		   finding \0 at the end of the string */
		stream->stream[stream_size] = 0;
		
		/* set initial indicators */
		stream->stream_size  = stream_size;
	}

	/* initilize common stream part */
	stream->chunks  = axl_new (char *, MAX_INSPECTED_CHUNKS + 1);
	stream->lengths = axl_new (int, MAX_INSPECTED_CHUNKS + 1);

	/* return newly created stream */
	return stream;
}

/** 
 * @internal
 *
 * Internal implementation to support axl_stream_inspect and
 * axl_stream_peek.
 * 
 * @param stream The stream where the inspect operation will be
 * performed.
 *
 * @param chunk The chunk to be used while performing the inspect
 * operation.
 *
 * @param inspected_size The size for the chunk to be checked or -1 if
 * a chunk size calculation is required.
 *
 * @param alsoAccept axl_true to make the function to accept stream
 * that is properly inspected, axl_false if not.
 * 
 * @return See \ref axl_stream_inspect.
 */
#define axl_stream_common_inspect(i,stream,chunk,inspected_size,alsoAccept)\
if (inspected_size == -1)\
	inspected_size = strlen (chunk);\
if (axl_stream_fall_outside (stream, inspected_size))\
	return -1;\
i = 0;\
while (chunk [i] != 0 && (stream->stream + stream->stream_index) [i] != 0) {\
     if (chunk [i] != (stream->stream + stream->stream_index) [i])\
        return 0;\
     i++;\
     if (i == inspected_size) {\
	stream->previous_inspect = inspected_size;\
	if (alsoAccept) {\
		axl_stream_accept (stream);\
	}\
	return 1;\
     }\
}\
	return 0


/** 
 * @brief Allows to perform an inspection of the given chunk on the
 * given stream.
 *
 * The <i>chunk</i> will be checked to apper at the begining of the
 * stream. This means that, having the current state of the stream,
 * the chunk is checked to be found at the very begining of the
 * stream.
 *
 * If the function succeed, an implict call to \ref axl_stream_accept
 * is done. In the case you are only checking but no stream acceptance
 * is required, use instead: \ref axl_stream_peek.
 * 
 * @param stream The \ref axlStream where the operation will be
 * performed.
 * 
 * @param chunk The chunk that is expected to be found at the begin of the stream.
 * 
 * @param inspected_size The size of the chunk provided to be inspected.
 * 
 * @return The function returns the following values according to the result: 
 *
 * - <b>0</b> if the chunk wasn't found inside the stream but no error was
 * found.
 *
 * - <b>1</b> if the chunk is found inside the given stream.
 *
 * - <b>-1</b> means that no more stream is left to satify the operation.
 *
 * - <b>-2</b> means that the parameters received are wrong either
 * because stream is a NULL reference or because chunk is the same.
 */
int         axl_stream_inspect (axlStream * stream, const char * chunk, int inspected_size)
{
	int iterator;

	/* call to common implementation */
	axl_stream_common_inspect (iterator, stream, chunk, inspected_size, axl_true);
}

/** 
 * @brief Allows to check the provide char code at the given \ref
 * axlStream, using optionally an offset to perform the check.
 * 
 * @param stream The stream where the check operation will be
 * implemented.
 *
 * @param value The value to check.
 *
 * @param offset The stream offset to apply to the check. Use 0 to not
 * apply offset.
 * 
 * @return \ref axl_true if the provided value is found at the current
 * stream index (taking into consideration offset).
 */
axl_bool         axl_stream_inspect_code    (axlStream * stream, char value, int offset)
{
	axl_return_val_if_fail (stream, axl_false);
	
	/* check the value */
	return stream->stream [stream->stream_index + offset] == value;
}

/** 
 * @brief Allows to perform a stream inspection without automatically
 * accept content properly inspected.
 * 
 * @param stream The stream where the peek operation will be
 * performed.
 *
 * @param chunk The chunk to lookup.
 *
 * @param inspected_size The size of the chunk provided to be
 * inspected.
 * 
 * @return See \ref axl_stream_inspect.
 */
int         axl_stream_peek            (axlStream * stream, const char * chunk, int inspected_size)
{
	int iterator;

	/* call to common implementation */
	axl_stream_common_inspect (iterator, stream, chunk, inspected_size, axl_false);
}

/** 
 * @brief Allows to perform several, not excluyen inspect operations,
 * over the given stream.
 *
 * Here is an example:
 * \code
 * if (axl_stream_inspect_several (stream,         // the stream 
 *                                 2,              // two chunks to recognize 
 *                                 "or", 2,        // first chunk and its length
 *                                 "||", 2) > 0) { // second chunk and its length
 *      // chunk matched!!
 * }
 * \endcode
 * @param stream The stream where the operation will be performed.
 *
 * @param chunk_num The chunk number to inspect.
 * 
 * @return The function returns the following values: 
 * 
 * - <b>0</b>: if no chunk is found inside the given stream, according to the
 * provided chunks.
 *
 * - <b>N</b>: is returned to denote that the Nth chunk was found.
 *
 * - <b>-1</b>: is returned if no more stream is left to satisfy the operation.
 * 
 * - <b>-2</b>: means that the parameters received are wrong either because
 * stream is NULL or any other parameter.
 */
int         axl_stream_inspect_several (axlStream * stream, int chunk_num, ...)
{
	va_list   args;
	int       iterator   = 0;
	char    * chunk      = NULL;
	int       length     = 0;
	int       last_value = 0;

	axl_return_val_if_fail (stream,        -1);
	axl_return_val_if_fail (chunk_num > 0, -1);

	va_start (args, chunk_num);

	/* check each chunk */
	while (iterator < chunk_num) {

		/* get the next chunk */
		chunk  = va_arg (args, char *);
		length = va_arg (args, int);

		if (length == -1)
			length = strlen (chunk);
		
		/* check the chunk read */
		switch (axl_stream_inspect (stream, chunk, length)) {
		case -2:
			/* wrong parameter received */
			last_value = -2;
			break;
		case -1:
			/* there is no more stream left */
			last_value = -1;
			break;
		case 0:
			/* the chunk wasn't found, break and
			 * continue. */
			break;
		default:
			/* the chunk was found */
			va_end (args);
			return (iterator + 1);
		}
		
		/* update the iterator */
		iterator ++;
	}

	/* close va arg */
	va_end (args);

	/* return that no chunk was found */
	return last_value;
}

/** 
 * @brief Accept previous inspected chunk to be consumed and moves
 * current stream current the size equal to the chunk inspected.
 * 
 * @param stream The stream where the byte inspected size will be
 * accepted.
 */
void axl_stream_accept (axlStream * stream)
{
	axl_return_if_fail (stream);

	/* simple memory chunk parsing */
	stream->stream_index     += stream->previous_inspect;
	stream->global_index     += stream->previous_inspect;

	stream->previous_inspect  = 0;
	if (stream->last_chunk != NULL)
		axl_free (stream->last_chunk);
	stream->last_chunk = NULL;

	return;
}

/** 
 * @brief Push new content at the begin of the stream.
 * 
 * @param stream The stream that will be updated with new content.
 *
 * @param content The content to be added.
 *
 * @param size The size of the content to be added.
 */
void axl_stream_push (axlStream * stream, const char * content, int size)
{
	axl_return_if_fail (stream && content);
	
	/* place the content at the begin of the stream */
	axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "calling to push the stream..");

	/* check if the current stream buffer could hold the pushed
	 * content plus the content that is already placed */
	if (stream->stream_size < (stream->stream_size - stream->stream_index + size)) {
		/* seems we can't hold the content at this moment, so, update the stream size */
		stream->buffer_size = stream->stream_size - stream->stream_index + size;

		/* alloc a new temporal buffer */
		axl_free (stream->temp);
		stream->temp = axl_new (char, stream->buffer_size + 1);
		memcpy (stream->temp, content, size);
		
		/* displace memory already read to be at the begining
		 * of the stream */
		memcpy (stream->temp + size, stream->stream + stream->stream_index,
			stream->stream_size - stream->stream_index);

		/* now realloc the buffer */
		axl_free (stream->stream);
		stream->stream = axl_new (char, stream->buffer_size + 1);

		/* now copy displaced content back to the stream */
		memcpy (stream->stream, stream->temp, 
			(stream->stream_size - stream->stream_index) + size);
	} else {

		/* check for the temporal buffer to be created */
		if (stream->temp == NULL)
			stream->temp = axl_new (char, stream->buffer_size + 1);

		/* copy the content */
		memcpy (stream->temp, content, size);

		/* displace memory already read to be at the begining
		 * of the stream */
		memcpy (stream->temp + size, stream->stream + stream->stream_index,
			stream->stream_size - stream->stream_index);

		/* now copy displaced content back to the stream */
		memcpy (stream->stream, stream->temp, 
			(stream->stream_size - stream->stream_index) + size);

	} /* end if */

	/* update the index to the positioned at the next byte
	 * available on the buffer */
	stream->stream_size  = (stream->stream_size - stream->stream_index) + size;

	/* reset the index */
	stream->stream_index = 0;

	/* clean previous state */
	axl_stream_accept (stream);

	return;
}

/** 
 * @brief Allows to configure current index to be accepted by the
 * stream. 
 * 
 * @param stream The stream where the operation will be performed.
 *
 * @param index Count to move internal stream index.
 *
 * NOTE: the function reset current internal state (by doing an
 * implicit call to \ref axl_stream_accept).
 */
void        axl_stream_move            (axlStream * stream, int index)
{
	axl_return_if_fail (stream);

	axl_stream_accept (stream);
	stream->stream_index     = index;
	
	return;
}

/** 
 * @brief Makes the current index to be moved the amount of bytes
 * signaled by the parameter bytes.
 * 
 * @param stream The stream to update.
 * @param bytes The number of bytes to move the index. 
 */
void        axl_stream_step            (axlStream * stream, int bytes)
{
	axl_return_if_fail (stream);
	axl_return_if_fail (bytes >= 0);

	axl_stream_accept (stream);
	stream->stream_index += bytes;

	return;
}

/** 
 * @brief Returns the next chunk available on the stream.
 *
 * This function allows to get next available chunk, validating it
 * with provided valid_chars variable, until the chunk provided are
 * found.
 *
 * Currently, valid_chars is not used, so, the chunk returned is not
 * validated against the value provided.
 * 
 * As an example if it is required to get the encoding content, you
 * could do the next call:
 * 
 * \code
 * // reference to the allocated result
 * char * result;
 * 
 * // chunk matched variable
 * int chunk_matched;
 * 
 * // get the next chunk until a " or ' is found
 * result = axl_stream_get_until (stream, NULL, &chunk_matched, AXL_TRUE, 2, "\"", "'");
 * \endcode
 *
 * Value returned from this function mustn't be deallocated. However,
 * because the value returned is dinamically allocated by the
 * function, you can avoid doing a double allocation operation by
 * nullifying the internal reference to the result returned, making
 * the caller the only owner of the reference returned. To do this
 * use: \ref axl_stream_nullify with \ref LAST_CHUNK.
 * 
 * 
 * @param stream The stream were the chunk will be extracted.
 *
 * @param valid_chars The valid set of characters, to validate content
 * to be returned. Currently this is not implemented, so, you can
 * provide a NULL value.
 *
 * @param chunk_matched An optional pointer to an integer to notify
 * the chunk matched by the function. Chunk matching notification
 * starts from 0 up to number of chunks to match - 1. If the end of
 * the stream is reached, -2 is returned.
 * 
 * @param accept_terminator While calling to this function, the
 * terminator detected to stop the operation could also be accepted by
 * the stream, making it not necessary to accept the terminator once
 * the function have ended. However, this could be a problem while
 * performing peeking code. You can provide a AXL_FALSE value to make the
 * function to not accept the terminator found as to be consumed.
 *
 * @param chunk_num The number of chunks to be checked as a valid terminators.
 * 
 * @return The chunk recognizied, not including the terminator that
 * have made this operation to stop. 
 */
char      * axl_stream_get_until       (axlStream * stream, 
					char      * valid_chars, 
					int       * chunk_matched,
					axl_bool    accept_terminator,
					int         chunk_num, ...)
{
	char * result;
	va_list args;
	
	/* open the standard argument */
	va_start (args, chunk_num);

	/* call to get next chunk separated by the provided values */
	result = axl_stream_get_untilv (stream, valid_chars, chunk_matched, accept_terminator, NULL, chunk_num, args);

	/* close the standard argument */
	va_end (args);

	/* return value read */
	return result;
}

/** 
 * @brief Works the same way like axl_strteam_get_until but wihtout
 * allocating the memory returned, and filling the size for the chunk
 * returned in result_size reference.
 * 
 * @param stream The stream where the operation will be performed.
 *
 * @param valid_chars The valid chars reference to match (currently
 * not implemented).
 *
 * @param chunk_matched The chunk matched reference 
 *
 * @param accept_terminator Configure if the terminator should be
 * accepted or not.
 *
 * @param result_size The variable where the result size will be
 * returned. This variable is not optional. It must be configured to
 * hold the size of the content returned. If you provided a NULL
 * reference to this value then the function will fail. 
 *
 * @param chunk_num The number of chunks to match.
 * 
 * @return A reference to the internal stream copy. The reference
 * returned must not be deallocated.
 *
 * NOTE: This function have a particular function that could produce
 * not desired results. Because the stream returns a reference to the
 * current allocated stream, if nullifies the last position (\\0) to
 * avoid memory problems with printf APIs and any other code that
 * relay on the fact that C strings are NULL terminated. If the
 * content immediately following to the string returned is meaningful,
 * then you can't use this function. Example:
 * 
 * \code
 *   stream: CONTENTCONTENT2
 *           ^
 *           |
 *           +--- stream index
 *   
 *   calling to axl_stream_get_until_ref (stream, NULL, NULL, axl_false,
 *                                        &size, 1, "CONTENT");
 * 
 *   while cause stream: CONTENT\0ONTENT2
 *                               ^
 *                               |
 *                               +--- stream index
 * 
 *   and the function returning "CONTENT". See the fact that the
 *   next "C" from the word CONTENT2 is nullified.
 *
 * \endcode
 *
 * An indication that this function is not what you want is that you
 * are not accepting the terminator (accept_terminator=axl_false).
 */
char      * axl_stream_get_until_ref   (axlStream * stream, 
					char      * valid_chars, 
					int       * chunk_matched,
					axl_bool    accept_terminator,
					int       * result_size,
					int         chunk_num, ...)
{
	char * result;
	va_list args;

	axl_return_val_if_fail (result_size, NULL);
	
	/* open the standard argument */
	va_start (args, chunk_num);

	/* call to get next chunk separated by the provided values */
	result = axl_stream_get_untilv (stream, valid_chars, chunk_matched, accept_terminator, result_size, chunk_num, args);

	/* close the standard argument */
	va_end (args);

	/* return value read */
	return result;
}

/** 
 * @brief Allows to get the next string until the separators provided
 * are found or the end of the stream memory is reached.
 *
 * \ref axlStream type was designed to support parsing xml
 * documents. This documents have elements that allows to now where
 * the input has finished. Howerver, \ref axlStream abstraction has
 * showed to be powerful enough to be usable to parse other kinds of
 * elements that don't have lexical terminators to let the user to
 * provide that chunk to be matched.
 *
 * In those cases, this function allows to perform the same function
 * as \ref axl_stream_get_until but also checking, and using, as
 * terminator the end of the stream.
 * 
 * This allows to parse expressions like:
 * \code
 * int         chunk_matched;
 * axlStream * stream;
 * char      * string;
 * 
 * // create the stream 
 * stream = axl_stream_new ("array.value", -1, NULL, -1, &error);
 *
 * // parse first array identifier 
 * string = axl_stream_get_until_zero (stream, NULL, &chunk_matched, 
 *                                     axl_false, 2, "[", ".", NULL);
 *
 * // received "array"
 *
 * // parse again 
 * string = axl_stream_get_until_zero (stream, NULL, &chunk_matched, 
 *                                     axl_false, 2, "[", ".", NULL);
 *
 * // received "value" and chunk_matched == (-2)
 * \endcode
 *  
 * 
 * @param stream The stream were the chunk will be extracted.
 *
 * @param valid_chars The valid set of characters, to validate content
 * to be returned. Currently this is not implemented, so, you can
 * provide a NULL value.
 *
 * @param chunk_matched An optional pointer to an integer to notify
 * the chunk matched by the function. Chunk matching notification
 * starts from 0 up to number of chunks to match - 1. If the end of
 * the stream is reached while searching for the content to match,
 * chunk_matched is configured to -2.
 * 
 * @param accept_terminator While calling to this function, the
 * terminator detected to stop the operation could also be accepted by
 * the stream, making it not necessary to accept the terminator once
 * the function have ended. However, this could be a problem while
 * performing peeking code. You can provide a AXL_FALSE value to make the
 * function to not accept the terminator found as to be consumed.
 *
 * @param chunk_num The number of chunks to be checked as a valid terminators.
 * 
 * @return The chunk recognizied, not including the terminator that
 * have made this operation to stop. Rembember to check the
 * chunk_matched variable to be equal to -2. This will mean that the
 * string returned doesn't match any terminator provided because end
 * of the stream was reached while looking for them.
 */
char      * axl_stream_get_until_zero  (axlStream * stream, 
					char      * valid_chars, 
					int       * chunk_matched,
					axl_bool    accept_terminator,
					int         chunk_num, ...)
{
	char * result;
	va_list args;
	
	/* open the standard argument */
	va_start (args, chunk_num);

	/* call to get next chunk separated by the provided values */
	stream->zero = axl_true;
	result       = axl_stream_get_untilv (stream, valid_chars, chunk_matched, accept_terminator, NULL, chunk_num, args);
	stream->zero = axl_false;

	/* close the standard argument */
	va_end (args);

	/* return value read */
	return result;
}

char      * axl_stream_get_until_ref_zero  (axlStream * stream, 
					    char      * valid_chars, 
					    int       * chunk_matched,
					    axl_bool    accept_terminator,
					    int       * result_size,
					    int         chunk_num, ...)
{
	char * result;
	va_list args;
	
	/* open the standard argument */
	va_start (args, chunk_num);

	/* call to get next chunk separated by the provided values */
	stream->zero = axl_true;
	result       = axl_stream_get_untilv (stream, valid_chars, chunk_matched, accept_terminator, result_size, chunk_num, args);
	stream->zero = axl_false;

	/* close the standard argument */
	va_end (args);

	/* return value read */
	return result;
}

/** 
 * @brief Allows to configure the handler to be executed to alloc
 * memory for the axl_stream_get_until* API.
 * 
 * @param stream The stream to configure with the alloc function.
 *
 * @param handler The handler to be called when the streams requires
 * to alloc memory.
 *
 * @param data User defined pointer to be called to the allocator
 * function defined by the <b>handler</b> parameter.
 */
void       axl_stream_set_buffer_alloc   (axlStream      * stream,
					  axlStreamAlloc   handler,
					  axlPointer       data)
{
	axl_return_if_fail (stream);

	/* just configure the alloc handler */
	stream->alloc      = handler;
	stream->alloc_data = data;

	return;
}

/** 
 * @brief Allows to nullify the internal reference of the stream,
 * making that reference to be not deallocated once the stream is
 * moving.
 *
 * This is mainly used to reduce the malloc/free round trip while
 * using the stream abstraction, making the stream received from the
 * memory chunk to be allocated only once, avoiding the double
 * allocate-free cycle.
 * 
 * @param stream The \ref axlStream where the operation will be
 * performed.
 *
 * @param item The item to nullify. 
 */
void        axl_stream_nullify         (axlStream * stream,
					NullifyItem item)
{
	/* do not operate if a null stream reference is received */
	axl_return_if_fail (stream);

	switch (item) {
	case LAST_CHUNK:
		stream->last_chunk         = NULL;
		break;
	case LAST_NEAR_TO:
		stream->last_near_to       = NULL;
		break;
	case LAST_GET_FOLLOWING:
		stream->last_get_following = NULL;
		break;
	}
	
	/* nothing more to do here */
	return;
}

/** 
 * @internal 
 * 
 * Wide implementation for axl stream get until (which checks for
 * every index chunks provided instead of checking the first one until
 * a prebuffer operation is required).
 */
char * __axl_stream_get_untilv_wide (axlStream * stream, va_list args)
{

	int          iterator    = 0;
	int          index       = 0;
	int          _index      = 0;
	int          length      = 0;
	int          max_length  = 0;
	axl_bool     matched;
	char       * string      = NULL;
	axl_bool     match_empty = axl_false;
	int          empty_index = 0;

	/* get how many bytes remains to be read */
	int          remains;
	
	/* set current matched value */
	stream->chunk_matched = -1;
	
	/* iterate over the chunk list */
	while (iterator < stream->chunk_num) {

		/* get the chunk */
		stream->chunks [iterator]  = va_arg (args, char *);

		/* check if we have to match the emtpy string, and
		 * don't install the value to be matched */
		if (axl_cmp (stream->chunks[iterator], " ")) {
			match_empty = axl_true;

			/* reset the length size to detect an emtpy
			 * string */
			stream->lengths [iterator] = 0;
			empty_index                = iterator;
		}else {
			/* get the size */
			stream->lengths [iterator] = strlen (stream->chunks [iterator]);
		}

		/* get current length */
		if (stream->lengths [iterator] > max_length)
			max_length = stream->lengths [iterator];

		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found matching chunk: '%s' length: %d", 
			   stream->chunks[iterator], stream->lengths [iterator]);

		/* update index */
		iterator ++;
	}

	/* calculate how many bytes ara available */
	remains = stream->stream_size - stream->stream_index;

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "remaining data to be inspected: %d bytes (stream->stream-size=%d, stream->stream-index=%d)",
		   remains, stream->stream_size, stream->stream_index);

	/* now we have chunks to lookup, stream until get the stream
	 * limited by the chunks received. */
	do {

		/* decrease remain bytes to be read until perform a
		 * prebuffer operation */
		remains--;

		/* only prebuffer for this type of stream */
		if (stream->type == STREAM_FD) {
			
			/* check if the index is falling out side the buffer boundaries */
			if (remains < 0) {

				if (! axl_stream_prebuffer (stream)) {
					/* check if a call to zero was found */
					if (stream->zero) {
						/* flag that chunk matched
						 * will be -2 */
						stream->chunk_matched = -2;
						goto matched_return_result;
					} /* end if */
					__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "failed while prebuffer (stream->type = %d)", stream->type);

					return NULL;
				}
				
				/* update remains value but this time removing
				 * one unit (the unit to be consumed at the
				 * next sentence) */
				remains = stream->stream_size - index - 1;

				/* work around: because the index is updated
				 * at the end of the loop, it is required to
				 * decrease the index in one unit in the case
				 * a prebuffer operation happens */
				if (index > 0) {
					index--;
				}
				
			} /* end if */
		}

		/* check we don't get out side the memory */
		if (stream->type == STREAM_MEM) {
			if (remains < 0) {

				/* check for zero stream ended
				 * support */
				if (stream->zero) {
					/* flag that chunk matched
					 * will be -2 */
					stream->chunk_matched = -2;
					goto matched_return_result;
				}

				/* seems there is no more room to
				 * read */
				return NULL;
			} /* end if */
		} /* end if */

		
		/* compare chunks received for each index increased
		 * one step */
	init_get_until:
		_index   = stream->stream_index + index;
		matched  = axl_false;
		iterator = 0;

		/* before iterating, check if we have to match the
		 * empty string */
		if (match_empty) {
			/* check for a white space item */
			if ((stream->stream[_index] == ' ')  ||
			    (stream->stream[_index] == '\n') ||
			    (stream->stream[_index] == '\t') ||
			    (stream->stream[_index] == '\r')) {

				__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "matched by white space value matched_chunk=%d",
					   iterator);

				/* string matched */
				length         = 1;
				matched        = axl_true;
				iterator       = empty_index;
			}
		} /* end if */

		/* the empty string wasn't matched, now check for the
		 * rest of chunks */
		while ((! matched) && (iterator < stream->chunk_num)) {
			
			/* get current length for the chunk to check */
			length  = stream->lengths [iterator];

			/* check the length returned */
			matched = axl_false;
			if (length > 0 && ((_index + length) <= stream->stream_size)) {
				
				/* try to figure out if the next
				 * string match */
				if ((stream->chunks [iterator][0] == 
				     stream->stream [_index])) {
					
					if ((length == 1) ||
					    (axl_memcmp (stream->chunks [iterator] + 1, stream->stream + _index + 1, length -1))) {
						__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "matched as normal situation.. at index=%d (stream size=%d)",
							   stream->stream_index + _index + 1, stream->stream_size);
						/* flag as matched */
						matched = axl_true;
					} /* end if */
				} /* end if */
			} /* end if */
			
			/* check if we have found the chunk we were looking */
			if (! matched) {
				/* update iterator */
				iterator ++;
			}
		} /* end while */

		/* check that the function have found a chunk */
		if (matched) {
			/* check for matching a more specific
			 * terminal than a general */
			if ((length < max_length) && 
			    ((_index + length) == stream->stream_size)) {
				__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "detected possible false positive");
				
				/* do a prebuffer operation,
				 * and if success, try to
				 * check again all chunks at
				 * the current index */
				if (axl_stream_prebuffer (stream))
					goto init_get_until;
			}

			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "matched_chunk=%d", iterator);
			
			/* report which is the chunk being
			 * matched by the expresion */
			stream->chunk_matched = iterator;

		matched_return_result:
			
			/* result is found from last stream
			 * index read up to index */
			if (stream->last_chunk != NULL) {
				axl_free (stream->last_chunk);
				stream->last_chunk = NULL;
			}
			
			/* get a copy to the chunk to be returned */
			if (! stream->result_size) {
				if (stream->alloc != NULL)
					stream->last_chunk = stream->alloc (index + 1, stream->alloc_data);
				else 
					stream->last_chunk = axl_new (char, index + 1);
				memcpy (stream->last_chunk, stream->stream + stream->stream_index, index);
			}else {
				/* *result_size = index;*/
				stream->result_size = index;
				string              = stream->stream + stream->stream_index;
				
				/* set a termination mark */
				string [ index  ] = 0;
				__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
					   "Nullify internally index=%d to avoid printf problems", index);
			}
			
			/* in the case a memory chunk is being read */
			if (stream->accept_terminator) {
				stream->stream_index     += length;
				stream->global_index     += length;
			}
			stream->stream_index             += index;
			stream->global_index             += index;
			stream->previous_inspect          = 0;
			
			
			/* return allocated result */
			if (! stream->result_size)
				return stream->last_chunk;

			/* return reference result */
			return string;
		} /* end if */
		
		/* it seems that the chunk wasn't found */
		index++;

	}while (axl_true);

	/* return a NULL chunk. */
	return NULL;	
}

/** 
 * @brief Allows to perform the same operation like \ref
 * axl_stream_get_untilv but providing an already initialized and
 * opened std arg.
 *
 * This function is in fact, used by \ref axl_stream_get_untilv.
 * 
 * @param stream The stream where the operation will be peformed.
 * 
 * @param valid_chars The valid chars set to be used while reading
 * data.
 *
 * @param chunk_matched An optional value where the matched chunk will
 * be reported.
 *
 * @param accept_terminator Configure if terminator read should be
 * accepted or only the chunk read.
 *
 * @param result_size Allows to notify the caller with the chunk size
 * that is being returned by the function.
 *
 * @param chunk_num How many terminators are configured.
 *
 * @param args The list of terminators.
 * 
 * @return The chunk read or NULL if fails.
 */
char      * axl_stream_get_untilv      (axlStream * stream, 
					char      * valid_chars, 
					int       * chunk_matched,
					axl_bool    accept_terminator,
					int       * result_size,
					int         chunk_num, 
					va_list args)
{
	char * result;


	/* check max inspected chunks */
	if (chunk_num > MAX_INSPECTED_CHUNKS) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "unable to parse stream for the number of chunks to recognize. Max number supported is %d, but received %d",
			   MAX_INSPECTED_CHUNKS, stream->chunk_num);
		return NULL;
	}

	/* configure variables for the operation */
	stream->valid_chars       = valid_chars;
	stream->accept_terminator = accept_terminator;
	stream->result_size       = (result_size != NULL);
	stream->chunk_num         = chunk_num;

	/* call to current implementation */
	result = __axl_stream_get_untilv_wide (stream, args);

	/* check for returning references */
	if (result_size != NULL)
		*result_size   = stream->result_size;
	if (chunk_matched != NULL)
		*chunk_matched = stream->chunk_matched;

	/* return string matched */
	return result;
}


/** 
 * @brief Returns current index status for the given stream.
 *
 * This function could be used to get current index being read for the
 * stream received.
 * 
 * @param stream The stream where the index will be reported.
 * 
 * @return The index or -1 if fails.
 */
int         axl_stream_get_index       (axlStream * stream)
{
	axl_return_val_if_fail (stream, -1);

	return stream->stream_index;
}

/** 
 * @brief Returns current global index for the device being streamed.
 * 
 * @param stream The stream where the data is being requested.
 * 
 * @return The index or -1 if fails.
 */
int         axl_stream_get_global_index (axlStream * stream)
{
	axl_return_val_if_fail (stream, -1);

	return stream->global_index;
}

/** 
 * @brief Allows to get current stream size.
 * 
 * @param stream The stream where the stream size will be returned.
 * 
 * @return The stream size or -1 if fails. 
 */
int         axl_stream_get_size        (axlStream * stream)
{
	axl_return_val_if_fail (stream, -1);

	return stream->stream_size;
}

/** 
 * @brief Allows to get current status of the given stream, taking the
 * current index, getting an amount of <b>count</b> bytes before and
 * after the given index.
 *
 * This function is mainly used to get a piece of the stream at the
 * given position while reporting errors. This allows to show the
 * piece of xml that is failing.
 *
 * The string return must not be deallocated. Value returned is
 * actually managed by the stream object associated.
 * 
 * @param stream The stream where the near to operation will be performed.
 * @param count The amount of bytes to take.
 * 
 * @return A string that is taken counting bytes with provided
 * <b>count</b> value starting from the index. Stream provided must be
 * not NULL and count value must be greater than 0. 
 */
const char  * axl_stream_get_near_to     (axlStream * stream, int count)
{
	int first_index;
	int last_index;

	axl_return_val_if_fail (stream, NULL);
	axl_return_val_if_fail (count > 0, NULL);
	
	/* get first index */
	if ((stream->stream_index - count) <= 0)
		first_index = 0;
	else
		first_index = stream->stream_index - count;

	/* get last index */
	if ((stream->stream_index + count) >= (stream->stream_size - 1) )
		last_index  = (stream->stream_size) - first_index;
	else
		last_index  = (stream->stream_index + count) - first_index;

	/* release previous near to chunk */
	if (stream->last_near_to != NULL)
		axl_free (stream->last_near_to);

	stream->last_near_to = axl_new (char, last_index + 1);
	memcpy (stream->last_near_to, stream->stream + first_index, last_index);

	/* return current near to operation */
	return stream->last_near_to;
	
}

/** 
 * @brief Allows to get the following <b>count</b> bytes read from the
 * stream.
 * 
 * @param stream The stream where the operation will be performed.
 * @param count How many bytes to get from the stream.
 * 
 * @return A string referece, containing the first <b>count</b> bytes
 * or NULL if fails. Reference returned shouldn't be deallocated.
 */
const char  * axl_stream_get_following   (axlStream * stream, int count)
{
	axl_return_val_if_fail (stream, NULL);

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting next characters from stream: index=%d size=%d",
		   stream->stream_index, stream->stream_size);

	/* check index */
	if (stream->stream_index >= stream->stream_size)
		return NULL;

	if ((count + stream->stream_index) > stream->stream_size) {
		count = stream->stream_size - stream->stream_index;
	}

	/* free previously allocated memory */
	if (stream->last_get_following != NULL)
		axl_free (stream->last_get_following);

	/* copy stream content */
	stream->last_get_following = axl_new (char, count + 1);
	memcpy (stream->last_get_following, stream->stream + stream->stream_index, count);

	/* return reference created */
	return stream->last_get_following;
}

typedef struct _AxlStreamAssociatedData {
	axlPointer       data;
	axlDestroyFunc   destroy_func;
	axl_bool         free_on_finish;
}AxlStreamAssociatedData;


/** 
 * @internal
 * 
 * Internal deallocation function to reclaim memory used by the \ref
 * AxlStreamAssociatedData.
 * 
 * @param data The data to be deallocated.
 */
void __stream_associated_data_free (AxlStreamAssociatedData * data)
{
	/* do nothing if NULL is received */
	if (data == NULL)
		return;

	/* destroy associated data used provided destroy function */
	if (data->destroy_func != NULL && data->data != NULL)
		data->destroy_func (data->data);

	/* now free the node it self */
	axl_free (data);

	return;
}

/** 
 * @brief Associates the given \ref axlPointer with the given stream to be
 * life-time dependant.
 *
 * While performing the XML parsing, errors will be produced. This
 * function ensures that the axlDoc document will be released if the
 * stream is also released.
 *
 * This not only reduces the possibility to produce a memory leak also
 * allows to write less code.
 *
 * Once the stream is not useful and it is required to be released,
 * but not doing so with the \ref axlDoc instance, a call to \ref
 * axl_stream_unlink is also required.
 * 
 * @param stream The axlStream where the document will be linked to.
 *
 * @param element The element to link (may a axlDoc or a axlDtd).
 *
 * @param func The function to call once the stream is released.
 */
void        axl_stream_link            (axlStream  *   stream,
					axlPointer     element,
					axlDestroyFunc func)
{
	/* call to base implementation */
	axl_stream_link_full (stream, element, func, axl_false);

	return;
}

/** 
 * @brief Allows to associate data references with a destroy function,
 * like \ref axl_stream_link, but ensuring the object reference will
 * be released once finished the axl stream, no mather if the
 * application code calls to \ref axl_stream_unlink.
 * 
 * @param stream The axlStream where the document will be linked to.
 *
 * @param element The element to link (may a axlDoc or a axlDtd).
 *
 *
 * @param func The function to call once the stream is released.
 *
 * @param free_on_finish axl_true to make the reference to be released on
 * \ref axlStream deallocation. Providing \ref axl_false to this value is
 * equivalent to call to \ref axl_stream_link directly.
 */
void       axl_stream_link_full     (axlStream  *   stream,
				     axlPointer     element,
				     axlDestroyFunc func,
				     axl_bool       free_on_finish)
{
	AxlStreamAssociatedData * data;
	axl_return_if_fail (stream);
	axl_return_if_fail (element);
	axl_return_if_fail (func);
	
	/* that's all */
	if (stream->elements_linked == NULL)
		stream->elements_linked = axl_list_new (axl_list_always_return_1, 
							(axlDestroyFunc) __stream_associated_data_free);

	/* create the data to be stored */
	data                 = axl_new (AxlStreamAssociatedData, 1);
	data->data           = element;
	data->destroy_func   = func;
	data->free_on_finish = free_on_finish;

	/* add the item to be destroy once the stream is unrefered */
	axl_list_add (stream->elements_linked, data);

	return;
}

/** 
 * @brief Unlinks the associated \ref axlDoc instance.
 * 
 * @param stream The stream where the operation will be performed.
 */
void axl_stream_unlink (axlStream * stream)
{
	int                       iterator;
	AxlStreamAssociatedData * data;

	axl_return_if_fail (stream);
	
	/* clear document association */
	iterator = 0;
	while (iterator < axl_list_length (stream->elements_linked)) {
		/* get a referece to the node to destroy */
		data = axl_list_get_nth (stream->elements_linked, iterator);
		/* clear it */
		if (! data->free_on_finish) {
			data->data         = NULL;
			data->destroy_func = NULL;
		}
		iterator++;
	}
	return;
}

/** 
 * @brief Allows to deallocate memory used by the \ref axlStream
 * received.
 * 
 * @param stream The stream to be deallocated.
 */
void axl_stream_free (axlStream * stream)
{
	axl_return_if_fail (stream);

	/* release memory */
	axl_free (stream->stream);

	/* release associated document is defined. */
	if (stream->elements_linked) 
		axl_list_free (stream->elements_linked);

	/* releaset last chunk */
	if (stream->last_chunk != NULL)
		axl_free (stream->last_chunk);

	/* releaset last near to */
	if (stream->last_near_to != NULL)
		axl_free (stream->last_near_to);

	/* releaset last get following */
	if (stream->last_get_following != NULL)
		axl_free (stream->last_get_following);

	if (stream->fd > 0) {
		/* close file descriptor if defined */
		close (stream->fd);
	}

	/* free memory allocated for chunk matching */
	axl_free (stream->chunks);

	/* free lengths */
	axl_free (stream->lengths);

	/* free temporal buffer */
	axl_free (stream->temp);
	axl_free (stream->decode_temp);
	axl_free (stream->source_encoding);

	/* release memory allocated by the stream received. */
	axl_free (stream);

	return;
}


/** 
 * @brief Allows to check if the given chunk is a white space in the
 * same of the XML 1.0 Third edition.
 *
 * The XML standard understand the "white space", also reffered as S,
 * as the following characters: \\x20 (the white space itself), \\n, \\r
 * and \\t.
 *
 * This function allows to check if the given chunk contains a white
 * space, in the previous sense.
 * 
 * @param chunk The chunk to check
 * 
 * @return axl_true if the chunk contains a white space or axl_false
 * if not.
 */
axl_bool        axl_stream_is_white_space  (char * chunk)
{
	/* do not complain about receive a null refernce chunk */
	if (chunk == NULL)
		return axl_false;
	
	if (chunk[0] == ' ')
		return axl_true;
	if (chunk[0] == '\n')
		return axl_true;
	if (chunk[0] == '\t')
		return axl_true;
	if (chunk[0] == '\r')
		return axl_true;

	/* no white space was found */
	return axl_false;
}

/** 
 * @brief Support function which consumes white spaces in the W3C
 * sense.
 * 
 * @param stream The stream where the operation will be performed.
 * 
 * @return axl_true if more white spaces could be consumed, axl_false
 * if not.
 */
void axl_stream_consume_white_spaces (axlStream * stream)
{
	/* get how many bytes remains to be read */
	int remains = stream->stream_size - stream->stream_index;

	while (axl_true) {
		/* decrase the number of bytes remaining to be read
		 * and check if it is zero or less than zero to
		 * prebuffer. NOTE: remains could be 1 (remains one
		 * byte) making it possible to consume one byte
		 * more */
		remains--;
		if (remains < 0) {
			/* we fall outside the stream, so a prebuffer
			 * operation is required */
			if (! axl_stream_prebuffer (stream))
				return;
			
			/* update remains value but this time removing
			 * one unit (the unit to be consumed at the
			 * next sentence) */
			remains = stream->stream_size - stream->stream_index - 1;
                }
		
		/* check for a white space */
		if ((stream->stream[stream->stream_index] == ' ')  ||
		    (stream->stream[stream->stream_index] == '\n') ||
		    (stream->stream[stream->stream_index] == '\t') ||
		    (stream->stream[stream->stream_index] == '\r')) {

			/* update internal indexes */
			stream->stream_index++;
			stream->global_index++;
			stream->previous_inspect  = 0;
		}else {
			/* return */
			return;
		}
		
	} /* end while */

	return;
}



/** 
 * @internal
 * @brief Allows to compare two strings pointed by 
 * 
 * @param chunk1 The string to be compared.
 *
 * @param chunk2 The second string to be compared.
 *
 * @param size The amount of bytes to be compared for the two incoming
 * values.
 * 
 * @return axl_true if both string are equal, axl_false if not. If
 * some value provided is NULL or the size to compare is not greater
 * than 0 the function will return axl_false directly.
 */
axl_bool        axl_stream_cmp             (const char * chunk1, const char * chunk2, int size)
{
	/* perform some environmental condition checking */
	if (chunk1 == NULL)
		return axl_false;
	if (chunk2 == NULL)
		return axl_false;
	if (size < 0)
		return axl_false;
	
	/* report current comparation status */
	if ((chunk1[0] == chunk2[0])) {
		if ((size == 1) ||
		    (axl_memcmp (chunk1 + 1, chunk2 + 1, size -1))) {
			return axl_true;
		} /* end if */
	} /* end if */

	return axl_false;
}

/** 
 * @brief Provides the same function like axl_stream_cmp but
 * ignoring the case of the characters (case insensitive manner).
 * 
 * @param chunk1 The string to be compared.
 *
 * @param chunk2 The second string to be compared.
 *
 * @param size The amount of bytes to be compared for the two incoming
 * values.
 * 
 * @return axl_true if both string are equal, axl_false if not. If
 * some value provided is NULL or the size to compare is not greater
 * than 0 the function will return axl_false directly.
 */
axl_bool        axl_stream_casecmp           (const char * chunk1, const char * chunk2, int size)
{
	/* perform some environmental condition checking */
	if (chunk1 == NULL)
		return axl_false;
	if (chunk2 == NULL)
		return axl_false;
	if (size < 0)
		return axl_false;

	/* returh if both strings are equal. */
#if defined(AXL_OS_WIN32)
	return _strnicmp (chunk1, chunk2, size) == 0;
#else
	return strncasecmp (chunk1, chunk2, size) == 0;
#endif
}

/** 
 * @internal
 *
 * @brief Allows to check if the provided stream could support
 * checking a chunk of, at least, the provided inspected size.
 * 
 * @param stream The stream where the inspected operation is being
 * requested.
 *
 * @param inspected_size The size to inspected, that is being
 * requested to be checked on the given stream.
 * 
 * @return \ref axl_true if the provided chunk falls out side the
 * stream boundaries, or axl_false if requested inspected size could
 * be supported.
 */
axl_bool axl_stream_fall_outside (axlStream * stream, int inspected_size)
{
	/* if the content is inside memory, check it */
	if (fall_out_side_checking (stream, inspected_size)) {
		return (! axl_stream_prebuffer (stream));
	}
	
	/* otherwise, axl_false is returned */
	return axl_false;
}

/** 
 * @internal
 *
 * @brief Allows to check if the given stream have as a next element
 * the provided chunk.
 * 
 * @param stream The stream where the operation will be performed.
 * @param chunk The chunk to check
 * 
 * @return Returns axl_true if the given stream contains the value requested
 * or axl_false if not.
 */
axl_bool         axl_stream_check           (axlStream * stream, char * chunk, int inspected_size)
{
	int iterator;

	/* call to the internal implementation of axl_memcmp */
	_memcmp(iterator,chunk,(stream->stream + stream->stream_index), inspected_size);

}

/** 
 * @brief Allows to get current status of the stream. 
 *
 * If the is exhausted and have no more data to be read.
 * 
 * @param stream The stream that is being checked.
 * 
 * @return axl_true if the stream is exhausted or axl_false if not.
 */
axl_bool        axl_stream_remains         (axlStream * stream)
{
	axl_return_val_if_fail (stream, axl_false);

	
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking for stream status with stream index=%d and stream size=%d",
		   stream->stream_index, stream->stream_size);
		
	/* check if the stream is exhausted */
	if (stream->stream_index >= (stream->stream_size)) {

		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "prebufferring from remains");

		/* in the case the stream is exhausted, try to read
		 * more content from the streaming */
		return axl_stream_prebuffer (stream);
	}
	return axl_true;
}


/* @} */

/**
 * \defgroup axl_string_module Axl String: String functions provided by the Axl Stream module.
 */

/** 
 * \addtogroup axl_string_module
 * @{
 */

/** 
 *
 * @brief Allows to trim the provided chunk, removing all white spaces
 * (returns, white spaces, carry return and tabulars) that comes as
 * preffix and suffix for the string provided, referenced by chunk.
 *
 * The function retuns the reference to the new chunk already
 * translated. The function doesn't perform any memory allocation. It
 * uses the memory already used to hold the chunk provided, returning
 * a pointer for the first item that is not a white space and
 * nullifing the first item that is a white space behind the chunk.
 *
 * This function is particular useful while getting the content 
 * 
 * @param chunk The chunk to trim.
 *
 */
void      axl_stream_trim            (char * chunk)
{
	/* call to trim */
	axl_stream_trim_with_size (chunk, NULL);

	return;
}

/** 
 * @brief The function works like \ref axl_stream_trim, but providing
 * the count of bytes trimmed from the string.
 *
 * @param chunk The chunk to trim.
 *
 * @param trimmed An optional reference that returns the count of bytes
 * trimmed by the operation.
 */
void        axl_stream_trim_with_size  (char * chunk, int * trimmed)
{
	int    iterator;
	int    iterator2;
	int    end;
	int    total;

	/* perform some environment check */
	axl_return_if_fail (chunk);

	/* check empty string received */
	if (strlen (chunk) == 0) {
		if (trimmed)
			*trimmed = 0;
		return;
	}

	/* check the amount of white spaces to remove from the
	 * begin */
	iterator = 0;
	while (chunk[iterator] != 0) {
		
		/* check that the iterator is not pointing to a white
		 * space */
		if (! axl_stream_is_white_space (chunk + iterator))
			break;
		
		/* update the iterator */
		iterator++;
	}

	/* check for the really basic case where an empty string is found */
	if (iterator == strlen (chunk)) {
		/* an empty string, trim it all */
		chunk [0] = 0;
		if (trimmed)
			*trimmed = iterator;
		return;
	} /* end if */

	/* now get the position for the last valid character in the
	 * chunk */
	total   = strlen (chunk) -1;
	end     = total;
	while (chunk[end] != 0) {
		
		/* stop if a white space is found */
		if (! axl_stream_is_white_space (chunk + end)) {
			break;
		}

		/* update the iterator to eat the next white space */
		end--;
	}

	/* the number of items trimmed */
	total -= end;
	total += iterator;
	
	/* copy the exact amount of non white spaces items */
	iterator2 = 0;
	while (iterator2 < (end - iterator + 1)) {
		/* copy the content */
		chunk [iterator2] = chunk [iterator + iterator2];

		/* update the iterator */
		iterator2++;
	}
	chunk [ end - iterator + 1] = 0;

	if (trimmed != NULL)
		*trimmed = total;

	/* return the result reference */
	return;	
}

/** 
 * @brief Allows to remote occurences of value from the provided
 * string (chunk).
 *
 * The function do not allocate new memory for the result. All
 * operations are applied to the string received (chunk).
 *
 * The idea behind the functions is to allow removing values from the
 * string, joining remaining content. For example, removing "-" from
 * the string "iso-8859-15" yields "iso885915".
 * 
 * @param chunk The string that holds values to be removed.  
 *
 * @param value The value to be removed.
 *
 * @param first If only the first ocurrence of value must be removed,
 * otherwise all ocurrences will be removed from the string.
 */
void        axl_stream_remove            (char * chunk, const char * value, axl_bool first)
{
	int iterator;
	int iterator2;
	int length;
	int v_length;

	axl_return_if_fail (chunk);
	axl_return_if_fail (value);

	/* get lengths */
	length   = strlen (chunk);
	v_length = strlen (value);

	/* check for basic cases */
	if (length == v_length) {
		/* check if both strings are equal, then nullify */
		if (axl_cmp (chunk, value))
			chunk [0] = 0;
		return;
	} else if (length < v_length) {
		/* nothing to remove because parent string is too
		 * small to store the content */
		return;
	} /* end if */
		

	/* locate the string */
	iterator = 0;
	while (iterator < length) {
		/* check if the string value was found */
		if (axl_memcmp (chunk + iterator, value, v_length)) {
			/* string found, move content remaining (if is found) */
			if ((length - iterator - v_length) > 0) {
				iterator2 = 0;
				while (iterator2 < (length - iterator - v_length)) {
					chunk [iterator + iterator2] = chunk [iterator + iterator2 + v_length];
					iterator2++;
				} /* end while */
			} /* end if */
			
			/* update length to the new value */
			length -= v_length;

			/* check to terminate for first ocurrence */
			if (first) {
				chunk [length] = 0;
				return;
			}
			continue;
		} /* end if */

		/* next position */
		iterator++;
	} /* end while */

	/* nullify and terminate */
	chunk [length] = 0;
	return;
}

/** 
 * @brief Allows to copy the given chunk, supposing that is a properly
 * format C string that ends with a '\\0' value.
 *
 * This function allows to perform a copy for the given string. If a
 * copy limited by a size is required, use \ref axl_stream_strdup_n.
 * 
 * @param chunk The chunk to copy
 * 
 * @return A newly allocated string or NULL if fails.
 */
char      * axl_stream_strdup          (const char * chunk)
{
	char * result;
	int    length;

	/* return NULL reference if a NULL reference is received */
	if (chunk == NULL)
		return NULL;

	length = strlen (chunk);
	result = axl_new (char, length + 1);
	
	memcpy (result, chunk, length);

	return result;
}

/** 
 * @brief Allows to perform a copy for the <b>n</b> first bytes from
 * the <b>chunk</b> received.
 * 
 * @param chunk The chunk to copy
 *
 * @param n How many bytes to copy from the given chunk.
 * 
 * @return A newly allocated chunk, copied from the given chunk with a
 * size of <b>n</b> bytes. The function will check that the
 * <b>chunk</b> and the <b>n</b> values are not null and non-zero.
 */
char      * axl_stream_strdup_n (const char * chunk, int n)
{
	char * result;

	axl_return_val_if_fail (chunk, NULL);
	axl_return_val_if_fail (n, NULL);
	
	result = axl_new (char, n + 1);
	memcpy (result, chunk, n);
	
	return result;
}

/** 
 * @internal Allows to calculate the amount of memory required to
 * store the string that will representing the construction provided
 * by the printf-like format received and its arguments.
 * 
 * @param format The printf-like format to be printed.
 *
 * @param args The set of arguments that the printf applies to.
 *
 * <i><b>NOTE:</b> not all printf specification is supported. Generally, the
 * following is supported: %s, %d, %f, %g, %ld, %lg and all
 * combinations that provides precision, number of items inside the
 * integer part, etc: %6.2f, %+2d, etc. An especial case not supported
 * is %lld, %llu and %llg.</i>
 *
 * @return Return the number of bytes that must be allocated to hold
 * the string (including the string terminator \0). If the format is
 * not correct or it is not properly formated according to the value
 * found at the argument set, the function will return -1.
 */
int axl_stream_vprintf_len (const char * format, va_list args)
{
	/** IMPLEMENTATION NOTE: in the case this code is update,
	 * update exarg_vprintf_len **/

#if defined (AXL_OS_WIN32) && ! defined (__GNUC__)
#   if HAVE_VSCPRINTF
	if (format == NULL)
		return 0;
	return _vscprintf (format, args) + 1;
#   else
	char buffer[8192];
	if (format == NULL)
		return 0;
	return _vsnprintf (buffer, 8191, format, args) + 1;
#   endif
#else
	/* gnu gcc case */
	if (format == NULL)
		return 0;
	return vsnprintf (NULL, 0, format, args) + 1;
#endif
}

/** 
 * @brief Allows to perform a printf operation on the provided buffer
 * (which must be allocated by the caller, and its size signaled by
 * buffer_size).
 *
 *
 * @param buffer The already allocated buffer to hold the result.
 *
 * @param buffer_size The size of the buffer provided. 
 *
 * @param real_size Optional reference where the real space required
 * to hold the content will be placed. In cases where the content is
 * enough small to hold in the buffer, this value will contain the
 * same value as returned by the function. In the case the buffer
 * provide can't hold all the content, the function will return at
 * maximum (buffer_size - 1) bytes written, that is, all content that
 * was possible to be included plus a trailing \\0 to terminate the
 * string, and, if defined <i>real_size</i> variable, it will contain
 * the space that will be required.
 *
 * @param format The printf like format to use to create the content
 * to be placed at the buffer provided.
 *
 * @return The amount of bytes written. The function will return at
 * maximum buffer_size - 1 bytes written. Use <i>real_size</i>
 * variable to check if the function was able to write all content (if
 * real_size == value returned). 
 */
int axl_stream_printf_buffer (char * buffer, 
			      int    buffer_size, 
			      int  * real_size,
			      const char * format, ...)
{
	va_list args;
	int     result;
#if defined(AXL_OS_WIN32)
	int     check_size;
#endif 

	/* check foramt and optn */
	if (format == NULL) {
		/* clean real size if it was defined */
		if (real_size)
			(*real_size) = 0;
		return 0;
	}
	/* open stdargs */
	va_start (args, format);

# if defined (AXL_OS_WIN32)
	/* because undre windows all its string family of functions
	 * return -1 in the case not enough espace is available, we
	 * have to check first before calling to _vsnprintf */
	check_size = axl_stream_vprintf_len (format, args);

	/* call to close to avoid problems with amd64 platforms */
	va_end (args);
	va_start (args, format);

	/* windows case */
	result = _vsnprintf (buffer, buffer_size, format, args);
	if (result == -1) {
		/* nullify last character and update "result" to point
		 * to the amount of data written (buffer - 1). */
		result = (buffer_size - 1);
		buffer[result] = 0; 
	} /* end if */
#else
	/* gnu gcc case */
	result = vsnprintf (buffer, buffer_size, format, args);
#endif
	/* close stdarg */
	va_end (args);

	/* report real size required */
	if (real_size) {
#if defined(AXL_OS_WIN32)
		(*real_size) = check_size - 1;
#else
		(*real_size) = result;
#endif
	} /* end if */

	/* limit result */
	if (result > (buffer_size - 1))
		result = (buffer_size - 1);

	return result;
}

/** 
 * @internal Function that allows to get how many bytes will be
 * required to hold the format and the arguments provided.
 * 
 * @param format The printf-like format followed by the rest of
 * arguments.
 * 
 * @return Return the number of bytes that must be allocated to hold
 * the string (including the string terminator \0). If the format is
 * not correct or it is not properly formated according to the value
 * found at the argument set, the function will return -1.
 */
int axl_stream_printf_len (const char * format, ...)
{
	int     result;
	va_list args;

	va_start (args, format);

	/* get the result */
	result = axl_stream_vprintf_len (format, args);
	
	va_end (args);

	return result;
}


/** 
 * @brief Allows to produce an newly allocated string produced by the
 * chunk received plus arguments, using the printf-like format.
 *
 * @param chunk The chunk to copy.
 * 
 * @return A newly allocated chunk.
 */
char      * axl_stream_strdup_printf   (const char * chunk, ...)
{
	char    * result   = NULL;
	va_list   args;
	
	axl_return_val_if_fail (chunk, NULL);

	/* open std args */
	va_start (args, chunk);

	/* get the string */
	result = axl_stream_strdup_printfv (chunk, args);
	
	/* close std args */
	va_end (args);
	
	return result;
}

/** 
 * @brief DEPRECATED: Allows to produce an string representing the
 * message hold by chunk with the parameters provided.
 * 
 * @param chunk The message chunk to print.
 * @param args The arguments for the chunk.
 * 
 * @return A newly allocated string.
 *
 * IMPLEMENTATION NOTE: This function may have a fundamental bug due
 * to the design of va_list arguments under amd64 platforms. In short,
 * a function receiving a va_list argument can't use it twice. In you
 * are running amd64, check your axl_config.h did find
 * AXL_HAVE_VASPRINTF.
 */
char  * axl_stream_strdup_printfv    (const char * chunk, va_list args)
{
	/** IMPLEMENTATION NOTE: place update exarg_strdup_printfv
	 * code in the case this code is updated **/

#ifndef AXL_HAVE_VASPRINTF
	int       size;
#endif
	char    * result   = NULL;

	axl_return_val_if_fail (chunk, NULL);

#ifdef AXL_HAVE_VASPRINTF
	/* do the operation using the GNU extension */
	if (vasprintf (&result, chunk, args) == -1)
		return NULL;
#else
	/* get the amount of memory to be allocated */
	size = axl_stream_vprintf_len (chunk, args);

	/* check result */
	if (size == -1) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "unable to calculate the amount of memory for the strdup_printf operation");
		return NULL;
	} /* end if */

	/* allocate memory */
	result   = axl_new (char, size + 2);
	
	/* copy current size */
#    if defined(AXL_OS_WIN32) && ! defined (__GNUC__)
	_vsnprintf_s (result, size + 1, size, chunk, args);
#    else
	vsnprintf (result, size + 1, chunk, args);
#    endif
#endif
	/* return the result */
	return result;
}

/** 
 * @brief Allows to create a newly allocated chunk, providing its
 * values as a printf call function, but also returning the chunk
 * size.
 *
 * This function works like \ref axl_stream_strdup_printf, but
 * providing an integer reference where the result chunk length will
 * be returned. 
 * 
 * @param chunk The printf chunk format to allocate.
 *
 * @param chunk_size A reference to fill the chunk lenght.
 * 
 * @return A newly allocated chunk.
 */
char    * axl_stream_strdup_printf_len (const char * chunk, int * chunk_size, ...)
{
#ifndef AXL_HAVE_VASPRINTF
	int       size;
#endif
	int       new_size;
	char    * result;
	va_list   args;
	
	axl_return_val_if_fail (chunk, NULL);

	/* open std args */
	va_start (args, chunk_size);

#ifdef AXL_HAVE_VASPRINTF
	/* do the operation using the GNU extension */
	new_size = vasprintf (&result, chunk, args);

	/* reopen to avoid amd64 bug */
	va_end (args);
	va_start (args, chunk_size);
#else
	/* get the amount of memory to be allocated */
	size = axl_stream_vprintf_len (chunk, args);

	/* reopen to avoid amd64 bug */
	va_end (args);
	va_start (args, chunk_size);
	
	/* check result */
	if (size == -1) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "unable to calculate the amount of memory for the strdup_printf operation");
		return NULL;
	} /* end if */

	/* allocate memory */
	result   = axl_new (char, size + 2);

	/* copy current size */
#if defined(AXL_OS_WIN32) && ! defined (__GNUC__)
	new_size = _vsnprintf_s (result, size + 1, size, chunk, args);
#else
	new_size = vsnprintf (result, size + 1, chunk, args);
#endif
#endif
	
	/* close std args */
	va_end (args);

	/* fill the chunk size result */
	if (chunk_size != NULL)
		*chunk_size = new_size;

	return result;
}

/** 
 * @brief Allows to split the provided chunk, into several pieces that
 * are separated by the separator (or separators) provided.
 *
 * The function will try to split the chunk provide using the
 * separator provided, and optionally, all separators provided.
 *
 * Here is an example:
 * \code
 * char ** result;
 *
 * // split the provided value using the ':', ';' and ',' as separators.
 * result = axl_stream_split (value, 3, ":", ";", ",");
 * \endcode
 *
 * The value returned must be deallocated using \ref axl_stream_freev.
 *
 * @param chunk The chunk to split.
 *
 * @param separator_num The number os separators to be used while
 * spliting the chunk.
 * 
 * @return A newly allocated string, that must be deallocated by using
 * \ref axl_stream_freev. The function will return a NULL if the chunk
 * or the separators provided are NULL.
 *
 * NOTE: See also \ref axl_split.
 */
char     ** axl_stream_split           (const char * chunk, int separator_num, ...)
{
	va_list      args;
	char      ** separators;
	char      ** result;
	int          iterator;
	int          index;
	int          previous_index;
	int          count      = 0;
	int          length     = 0;

	/* check received values */
	axl_return_val_if_fail (chunk, NULL);
	axl_return_val_if_fail (separator_num > 0, NULL);

	separators = axl_new (char *, separator_num + 1);
	iterator   = 0;
	va_start (args, separator_num);

	/* get all separators to be used */
	while (iterator < separator_num) {
		separators[iterator] = va_arg (args, char *);
		iterator++;
	} /* end if */
	
	va_end (args);

	/* now, count the number of strings that we will get by
	 * separating the string into several pieces */
	index    = 0;
	while (*(chunk + index) != 0) {

		/* reset the iterator */
		iterator = 0;
		while (iterator < separator_num) { 

			/* compare the current index with the current
			 * separator */
			length = strlen (separators[iterator]);
			if (axl_memcmp (chunk + index, separators[iterator], length)) {

				/* update items found */
				count++;

				/* update index to skip the item found */
				index += length - 1; /* make the last index to be captured the the -1 */

				/* break the loop */
				break;
			}
			iterator++;
		}

		/* update the index to the next item */
		index++;
	} /* end if */
	
	/* create the result that will hold items separated */
	result = axl_new (char *, count + 2);

	/* now copy items found */
	count          = 0;
	index          = 0;

	/* remember previous_index */
	previous_index = index;
	while (*(chunk + index) != 0) {

		/* reset the iterator */
		iterator = 0;
		while (iterator < separator_num) { 

			/* compare the current index with the current
			 * separator */
			length = strlen (separators[iterator]);
			if (axl_memcmp (chunk + index, separators[iterator], length)) {

				/* copy the chunk found */
				result[count] = axl_new (char, index - previous_index + 1);
				memcpy (result[count], chunk + previous_index, index - previous_index);

				/* axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item found (last) '%s' (index=%d != previous=%d", result[count],  index, previous_index); */

				/* update items found */
				count++;

				/* update index to skip the item found */
				if (*(chunk + index + length) == 0) {
					/* in the case no more elements to read will be found */
					/* put an empty space at the end */
					result [count]    = axl_new (char, 1);
					
					axl_free (separators);
					return result;
				}

				/* remember previous_index */
				index += length; 
				previous_index = index;
				index--; /* make the last index to be captured the the -1 */
				break;
			}
			iterator++;
		}

		/* update the index to the next item */
		index++;
	}

	/* check for a last chunk */
	if (index != previous_index) {
		/* copy the chunk found */
		result[count] = axl_new (char, index - previous_index + 1);
		memcpy (result[count], chunk + previous_index, index - previous_index);

		/* axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item found (last) '%s' (index=%d != previous=%d", result[count], index, previous_index); */
	}

	
	/* release memory */
	axl_free (separators);
	
	return result;
}

/** 
 * @brief Allows to clean an split created by \ref axl_stream_split by
 * removing all items found to be empty strings.
 * 
 * @param split The split to be updated by removing all empty string
 * items.
 *
 */
void        axl_stream_clean_split     (char ** split)
{
	int iterator;
	int iterator2;
	int iterator3;

	/* check input */
	axl_return_if_fail (split);

	/* remove empty strings */
	iterator = 0;
	while (split[iterator]) {
		if (strlen (split[iterator]) == 0) {

			/* clear position joint */
			axl_free (split[iterator]);
			split[iterator] = NULL;

			/* move strings */
			iterator3 = 0;
			iterator2 = iterator + 1;
			while (split[iterator2 + iterator3]) {
				/* move reference */
				split[iterator + iterator3]  = split[iterator2 + iterator3];
				
				/* nullify */
				split[iterator2 + iterator3] = NULL;
				
				/* next position */
				iterator3++;
			} /* end while */
			continue;
		} /* end if */

		/* next iterator */
		iterator++;
	} /* end while */

	return;
}

/** 
 * @brief Allows to implement the oposite operation of \ref
 * axl_stream_split, by joing all strings provided inside the array
 * (strings), using as separator the value provided.
 * 
 * @param strings The set of strings to be joined.
 *
 * @param separator The separator to be used to join all strings
 * provided.
 *
 * 
 * @return A newly allocated reference, that must be release using
 * \ref axl_free.
 *
 * NOTE: See also \ref axl_join.
 */
char      * axl_stream_join            (char      ** strings, 
					const char * separator)
{
	int         length;
	int         sep_length;
	int         iterator;
	char      * result;
	axl_bool    next_sep;

	axl_return_val_if_fail (strings && strings[0], NULL);
	axl_return_val_if_fail (separator, NULL);

	/* get the amount of data to be allocated */
	length   = 0;
	iterator = 0;

	/* for each value to be joined */
	while (strings [iterator]) {
		/* count the number of bytes for each string */
		length += strlen (strings[iterator]);

		/* next iterator */
		iterator++;
	} /* end while */

	/* check for the basic case */
	if (iterator == 1) {
		/* only one piece is contained in the set of strings
		 * provided, so nothing can be joined */
		return axl_strdup (strings[0]);
	}
	
	/* add to the length the number of separatos to be added
	 * (wihtout 1) and add a traling byte to terminate the
	 * string */
	sep_length = strlen (separator);
	length    += (sep_length * (iterator - 1)) + 1;
	result     = axl_new (char, length);

	iterator   = 0;
	next_sep   = axl_false;
	length     = 0;

	while (strings [iterator]) {

		/* copy the content */
		if (next_sep) {
			memcpy (result + length, separator, sep_length);

			/* update the length */
			length += sep_length;
		} else {
			memcpy (result + length, strings[iterator], strlen (strings[iterator]));

			/* update the length */
			length += strlen (strings[iterator]);
		} /* end if */

		/* check if next is separator */
		next_sep = ! next_sep;

		/* update the iterator only if next value to be
		 * handled is a separator */
		if (next_sep) 
			iterator++;
	} /* end while */

	/* return string created */
	return result;
}

/** 
 * @brief Allows to replace the provided string by the provided
 * replacement on the provided source string, doing the replacement in
 * an effective manner.
 *
 * @param source The source string where to look and replace. The
 * result will be reported as a new pointer on this parameter. The
 * function will dealloc previous string (passed in source).
 *
 * @param source_len The replace function can handle binary
 * strings. This parameter signals the function how many bytes are
 * found on the source pointer.
 *
 * @param string The string that will be looked for replacement. The
 * string can be binary data but its length must be configured.
 *
 * @param string_len String length or -1. The string parameter can be
 * binary data (may include \0) but length must be especified. If -1
 * is provided, the function will use strlen () to get current string
 * size.
 *
 * @param replacement The replace string. The replacement can be
 * binary data but its length must be configured.
 *
 * @param replacement_len Replacement string length or -1. The
 * replacement parameter can be binary data (may include \0) but
 * length must be especified. If -1 is provided, the function will use
 * strlen () to get current string size.
 *
 * @return The function returns the new size of the string. The
 * function returns the same source length when no replacement was
 * done. The function return source_len in the case some argument is NULL.
 */
int  axl_stream_replace         (char        ** source,      int source_len, 
				 const char   * string,      int string_len, 
				 const char   * replacement, int replacement_len)
{
	int    iterator;
	int    iterator2;
	int    count;
	char * result;
	int    old_source_len;

	/* check arguments */
	axl_return_val_if_fail (source && string && replacement, source_len);

	/* get sizes if not configured */
	if (source_len == -1)
		source_len = strlen (*source);
	if (string_len == -1)
		string_len = strlen (string);
	if (replacement_len == -1)
		replacement_len = strlen (replacement);

	/* find how many strings must be replaced */
	iterator = 0;
	count    = 0;
	while ((iterator + string_len - 1) < source_len) {
		/* check if the string is found */
		if (axl_memcmp ((*source) + iterator, string, string_len)) {
			/* string found ! */
			count++;
			
			/* skip these bytes */
			iterator += string_len;
			continue;
		}

		/* next position */
		iterator++;
	} /* end while */

	/* check if we have found some to replace */
	if (count == 0)
		return source_len;

	/* update source length */
	old_source_len = source_len;
	source_len     = source_len - (string_len * count) + (replacement_len * count);

	/* alloc memory for the replacement */
	result     = axl_new (char, source_len + 1);

	/* do replacement */
	iterator  = 0;
	iterator2 = 0;
	while (iterator < old_source_len) {
		/* check if the string is found */
		if (((iterator + string_len - 1) < old_source_len) && axl_memcmp ((*source) + iterator, string, string_len)) {
			/* string found!, replace */
			memcpy (result + iterator2, replacement, replacement_len);
			
			/* skip these bytes */
			iterator  += string_len;
			iterator2 += replacement_len;
			continue;
		}

		/* copy byte by byte */
		result[iterator2] = (*source)[iterator];

		/* next position */
		iterator++;
		iterator2++;
	} /* end while */

	/* release and report new string */
	axl_free (*source);
	*source = result;

	return source_len;
}

/** 
 * @brief Allows to concatenate the two given strings into a single
 * one.
 *
 * The function will concatenate both string into a newly allocated
 * string that is the result of taking chunk1 followed by chunk2. 
 * 
 * If the function receive a NULL value for one of the string
 * received, the result from this function will be the other
 * string. This is done to support a common basic case where the
 * string provided for one of the arguments is the one being used two
 * hold an iterative result. This variable usually is NULL.
 *
 * Once the string returned is no longer needed, \ref axl_free must be
 * used to deallocate the result. 
 *
 * The function will use the strlen function to calculate current
 * chunk sizes to provide the result.
 *
 * 
 * @param chunk1 The first chunk to be placed on the result.
 *
 * @param chunk2 The second chunk to be placed on the result.
 * 
 * @return A newly allocated string, containing both strings, or NULL
 * if fails. The only way for this function to fail is to provide two
 * NULL references as incoming strings.
 *
 * NOTE: See also \ref axl_concat.
 */
char      * axl_stream_concat          (const char * chunk1, const char * chunk2)
{
	char * result;
	int    len1;
	int    len2;

	axl_return_val_if_fail ((chunk2 != NULL) || (chunk1 != NULL), NULL);

	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called..");

	if (chunk1 == NULL) {

		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called.., returning %s", chunk2);

		/* return the result */
		return axl_strdup (chunk2);
	}

	if (chunk2 == NULL) {

		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called.., returning %s", chunk1);

		/* return the result */
		return axl_strdup (chunk1);
	}

	/* return the concatenation */
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "concat called.., returning %s%s", chunk1, chunk2);

	/* alloc enough memory to hold both strings */
	len1 = strlen (chunk1);
	len2 = strlen (chunk2);
	result = axl_new (char, len1 + len2 + 1);

	/* copy the content */
	memcpy (result, chunk1, len1);
	memcpy (result + len1, chunk2, len2);

	/* return the string created */
	return result;


}


/** 
 * @brief Returns current number of items inside the chunks reference
 * provided.
 * 
 * @param chunks The chunks reference, which contains a list of
 * chunks.
 * 
 * @return The number of chunks that the reference has or -1 if it
 * fails.
 */
int         axl_stream_strv_num        (char ** chunks)
{
	int iterator = 0;

	axl_return_val_if_fail (chunks, -1);
	/* release memory used by all elements inside the chunk */
	while (chunks[iterator] != 0) {
		iterator++;
	}

	/* return current number of chunks */
	return iterator;
	
}

/** 
 * @brief Allows to release memory used by elements returned by \ref
 * axl_stream_split and other function that return a pointer to a char **.
 * 
 * @param chunks The chunk to release.
 */
void        axl_stream_freev           (char ** chunks)
{
	int iterator = 0;

	axl_return_if_fail (chunks);
	 
	/* release memory used by all elements inside the chunk */
	while (chunks[iterator] != 0) {
		axl_free (chunks[iterator]);
		iterator++;
	}
	
	/* now release the chunk inside */
	axl_free (chunks);
	
	/* nothing more to do */
	return;
}

/** 
 * @internal 
 *
 * @brief Internal function to support \ref axl_stream_to_upper and
 * \ref axl_stream_to_lower
 * 
 * @param chunk The chunk to modify
 * @param desp Bits to increase.
 */
void __axl_stream_common_to (char * chunk, axl_bool to_upper)
{
	int iterator = 0;

	axl_return_if_fail (chunk);

	while (chunk[iterator] != 0) {
		/* change first ascii level */
		if (to_upper)
			chunk[iterator] = toupper (chunk[iterator]);
		else
			chunk[iterator] = tolower (chunk[iterator]);
		
		/* update iterators */
		iterator++;
	} 	

	return;
}

/** 
 * @brief Makes the provided string to be converted to upper case
 * letters.
 *
 * The function will modify the address provided. If you want to get a
 * upper case copy for a particular string, copy it first, by using
 * \ref axl_strdup, and then use this function.
 *
 * The function, for convenience, will also return the string
 * reference received, already modified. This could be used while
 * using this function to convert parameters that are expected by
 * other functions.
 * 
 * @param chunk The chunk to upper case.
 */
char *    axl_stream_to_upper        (char  * chunk)
{
	__axl_stream_common_to (chunk, axl_true);
	return chunk;
}

/** 
 * @brief Allows to convert the provided string into lower cases
 * letter.
 *
 * The function, for convenience, will also return the string
 * reference received, already modified. This could be used while
 * using this function to convert parameters that are expected by
 * other functions.
 * 
 * @param chunk The chunk to lower case.
 */
char *     axl_stream_to_lower        (char  * chunk)
{
	__axl_stream_common_to (chunk, axl_false);
	return chunk;
}

/** 
 * @brief Allows to perform a to upper operation, like \ref
 * axl_stream_to_upper, but returning an new allocated reference.
 * 
 * @param chunk The string reference to upper.
 * 
 * @return A new reference allocated containing the result or NULL if
 * it fails.
 */
char      * axl_stream_to_upper_copy   (const char  * chunk)
{
	char * result;

	/* perform some environmental checks */
	axl_return_val_if_fail (chunk, NULL);

	/* make a copy */
	result = axl_strdup (chunk);

	/* upper it */
	__axl_stream_common_to (result, axl_true);

	/* return the result */
	return result;
}


/** 
 * @brief Allows to perform a to lower operation, like \ref
 * axl_stream_to_upper, but returning an new allocated reference.
 * 
 * @param chunk The string reference to lower.
 * 
 * @return A new reference allocated containing the result or NULL if
 * it fails.
 */
char      * axl_stream_to_lower_copy   (const char  * chunk)
{
	char * result;

	/* perform some environmental checks */
	axl_return_val_if_fail (chunk, NULL);

	/* make a copy */
	result = axl_strdup (chunk);

	/* lower it */
	__axl_stream_common_to (result, axl_false);

	/* return the result */
	return result;
}

/** 
 * @brief Allows to compare two strings provided, s1 and s1 to be
 * equal.
 *
 * In the case both are equal, \ref axl_true is returned. Otherwise \ref
 * axl_false. The function compares that both are equal not only by making
 * the first to be contained inside the second string. The check also
 * ensures that "test" isn't equal to "test1".
 *
 * @param string First string to check.
 *
 * @param string2 Second string to check.
 * 
 * @return \ref axl_true if both string are equal, otherwise \ref axl_false is
 * returned.
 */
axl_bool axl_cmp (const char * string, const char * string2)
{
	int iterator = 0;

	if (string == NULL)
		return axl_false;
	if (string2 == NULL)
		return axl_false;
	
	/* for each item inside the iterator */
	while (string [iterator] != 0 && string2 [iterator] != 0) {
		
		/* check the content */
		if (string [iterator] != string2 [iterator])
			return axl_false;

		/* update the iterator */
		iterator++;
		
	} /* end while */
	
	/* check that both string ends at the same point */
	if (string [iterator] != 0 ||
	    string2 [iterator] != 0)
		return axl_false;
	
	return axl_true;
}

axl_bool        axl_casecmp (const char * string, const char * string2)
{
	int length;

	if (string == NULL)
		return axl_false;
	if (string2 == NULL)
		return axl_false;

	/* get length associated to first string */
	length = strlen (string);
	if (length != strlen (string2))
		return axl_false;
	
	/* now check both lengths */
	return axl_stream_casecmp (string, string2, length);
	
}


/** 
 * @brief Allows to check if both strings provided are equal on its
 * initial size bytes.
 *
 * This function is more efficient than common memcmp because it
 * doesn't perform the additional work to figure out which are the
 * bytes that differ both strings.
 * 
 * @param string The string to check.
 *
 * @param string2 The second string to check.
 *
 * @param size The size to check for both strings to be equal.
 * 
 * @return \ref axl_true if the both strings are equal for its initial
 * size bytes or \ref axl_false if not.
 */
axl_bool axl_memcmp (const char * string, const char * string2, int size)
{
	int iterator = 0;

	_memcmp(iterator,string,string2,size);
}

/** 
 *
 * @brief Perform a memory copy from the string provided.
 * 
 * @param string The string to copy.
 * 
 * @return A newly allocated value or NULL if it fails. The value
 * returned, must be deallocated using \ref axl_free.
 */
char * axl_strdup (const char * string)
{
	return (string != NULL) ? (char *) axl_stream_strdup ((char *) string) : NULL;
}

/** 
 * @internal Function that handles decoding operations when decode
 * functions are defined.
 * 
 * @param stream The stream where the decode operation will be
 * performed. 
 *
 * @param error Optional \ref axlError reference where errors will be
 * reported.
 * 
 * @return axl_true if the operation was completed.
 */
axl_bool axl_stream_decode (axlStream  * stream, 
			char       * output, 
			int          output_max_size, 
			int        * output_decoded, 
			int        * op_result, 
			axlError  ** error)
{

	int result;
	int size;

	/* clear op_result if defined */
	if (op_result)
		*op_result = 0;

	/* decode content from the stream directly */
	result = stream->decode_f (
		/* source */
		stream->decode_temp,
		/* source size */
		stream->decode_temp_last,
		/* source encoding: encode used by the source
		 * content provided */
		stream->source_encoding,
		/* output of content decoded and its size */
		output,
		/* output max size */
		output_max_size,
		/* output decoded */
		output_decoded,
		/* source remaining */
		&(stream->decode_temp_remain),
		/* user defined data */
		stream->decode_f_data);

	/* set op result if defined */
	if (op_result)
		*op_result = result;
	
	/* check result */
	if (result == 0) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "after decode operation result=%d, output_decoded (new buffer size)=%d (from %d original bytes)",
			   result, output_decoded ? *output_decoded : 0, stream->decode_temp_last);
		axl_error_new (-1, "found internal failure at decode operation, unable to complete entity parsing",
			       stream, error);
		return axl_false;
	} /* end if */
	
	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "after decode operation result=%d, output_decoded (new buffer size)=%d",
		   result, output_decoded ? *output_decoded : 0);
	
	/* update axl stream internal state */
	if (result == 1) {
		/* then the conversión was complete a no data
		 * is pending the the temporal decode
		 * buffer */
		stream->decode_temp_index = 0;
		stream->decode_temp_last  = 0;
	} else if (result == 2) {
		/* not enough space was found at the destination
		 * buffer, pack content at the decode buffer at the
		 * next operation */
		size = stream->decode_temp_last - stream->decode_temp_remain;
		if (size <= 0) {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, 
				   "found decode function return 2 (signaling pending data to be decoded) but last - remain yields to %d",
				   size);
			axl_error_new (-1, "found decode function return 2 (signaling pending data to be decoded) but last - remain yields to 0 or less", NULL, error);
			return axl_false;
		} /* end if */
		
		/* moving data */
		while (stream->decode_temp_index < size) { 
			stream->decode_temp[stream->decode_temp_index] = stream->decode_temp[stream->decode_temp_remain +  stream->decode_temp_index];
			stream->decode_temp_index++;
		} /* end while */
		
		/* now reset */
		stream->decode_temp_index = 0;
		stream->decode_temp_last  = size;
		
		/* reset to 1 since we have moved content to
		 * the begin of the buffer */
		result = 1;
	} /* end if */

	return (result == 1);
}

/** 
 * @internal Function used by the axl stream module to call to check
 * function defined.
 * 
 * @return axl_true if the check was fine, otherwise axl_false is returned.
 */
axl_bool axl_stream_content_check (axlStream * stream, const char * content, int content_length, axlError ** error)
{
	if (stream == NULL || content == NULL) {
		__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "content check function failed because null reference was received.");
		axl_error_new (-1, "content check function failed because null reference was received.", stream, error);
		return axl_false;
	} /* end if */
	
	/* return appropiate value */
	if (stream->check_f (content, content_length, stream->source_encoding, stream->check_f_data, error) == 1) 
		return axl_true;

	__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "content check function have failed");

	/* check if error reference was defined */
	return axl_false;
}


/** 
 * @internal Allows to configure a decode functions to be used to
 * translate content read into utf-8. 
 * 
 * @param stream The stream to be configured.
 *
 * @param source_encoding Original source encode.
 *
 * @param decode_f The function to be called to translate/check
 * content read into utf-8. If a null value is provided the decode
 * function is removed.
 *
 * @param error The reference where errors will be reported.
 *
 * @return axl_true if the function setup decode handler properly
 * otherwise axl_false is returned.
 */
axl_bool        axl_stream_setup_decode        (axlStream         * stream,
						const char        * source_encoding,
						axlStreamDecode     decode_f,
						axlPointer          user_data,
						axlError         ** error)
{
	axl_return_val_if_fail (stream, axl_false);

	/* do not check if the decode_f function is NULL (it's a valid
	 * case) */
	stream->decode_f      = decode_f;
	stream->decode_f_data = user_data;

	/* store source encoding */
	if (source_encoding != NULL)
		stream->source_encoding = axl_strdup (source_encoding);

	/* call to check and decode if required bufferede content */
	if (stream->decode_f) {
		/* init decode buffer */
		stream->decode_temp_size = (stream->buffer_size * 2) + 1;
		stream->decode_temp      = axl_new (char, stream->decode_temp_size);

		/* move content into decode temporal buffer */
		memcpy (stream->decode_temp, 
			stream->stream + stream->stream_index, 
			stream->stream_size - stream->stream_index);
		stream->decode_temp_index = 0;
		stream->decode_temp_last  = stream->stream_size - stream->stream_index;
		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "procesing %d bytes from decode buffer (total size: %d, current index: 0)", 
			   stream->decode_temp_last, stream->decode_temp_size);

		/* call to decode content */
		if (! axl_stream_decode (stream, 
					 /* output */
					 (stream->stream + stream->stream_index),
					 /* output max size */
					 stream->buffer_size - stream->stream_index,
					 /* output decoded */
					 &(stream->stream_size),
					 /* do not define op result */
					 NULL,
					 error))
			return axl_false;
		
		/* add to the stream size the current index */
		stream->stream_size += stream->stream_index;
		
	} /* end if */

	/* return result */
	return axl_true;
}

/** 
 * @brief Function that allows to configure a handler that is executed
 * to check content read into the axl stream buffer. See \ref
 * axlStreamContentCheck for more information.
 * 
 * @param stream The stream that is going to be configured.
 *
 * @param source_encoding The source encoding detected.
 *
 * @param check The function that implements the check.
 *
 * @param user_data User defined data to be passed to the check
 * function.
 *
 * @param error Optional \ref axlError reference where errors will be
 * reported.
 * 
 * @return The function returns axl_true if the cheker was installed and
 * first execution was completed.
 */
axl_bool    axl_stream_setup_check         (axlStream                * stream,
					    const char               * source_encoding,
					    axlStreamContentCheck      check,
					    axlPointer                 user_data,
					    axlError                ** error)
{
	axl_return_val_if_fail (stream, axl_false);

	/* do not check if the decode_f function is NULL (it's a valid
	 * case) */
	stream->check_f      = check;
	stream->check_f_data = user_data;

	/* store source encoding */
	if (source_encoding != NULL) 
		stream->source_encoding = axl_strdup (source_encoding);

	if (stream->check_f) {
		/* call to check */
		if (! axl_stream_content_check (stream, stream->stream + stream->stream_index, stream->stream_size - stream->stream_index, error)) {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "content check function have failed, looks like there is a problem with content");
			return axl_false;
		} /* end if */
	} /* end if */

	return axl_true;
}

/* @} */


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>