[libxml2] Adding a new buf module for buffers



commit bca22f40c3b2b34169d8d2919dce4894d8eac1a4
Author: Daniel Veillard <veillard redhat com>
Date:   Wed Jul 11 16:48:47 2012 +0800

    Adding a new buf module for buffers
    
    This also add converter functions between xmlBuf and xmlBuffer
    * buf.c buf.h: the old xmlBuffer routines but modified for size_t
      and using xmlBuf instead of xmlBuffer
    * Makefile.am: add the 2 new files
    * include/libxml/xmlerror.h: add an entry for the new module
    * include/libxml/tree.h: expose the xmlBufPtr type but not the
      structure which stay private

 Makefile.am               |    6 +-
 buf.c                     | 1138 +++++++++++++++++++++++++++++++++++++++++++++
 buf.h                     |   64 +++
 include/libxml/tree.h     |   21 +-
 include/libxml/xmlerror.h |    6 +-
 5 files changed, 1229 insertions(+), 6 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index f82cefa..487a37e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -36,7 +36,7 @@ libxml2_la_SOURCES = SAX.c entities.c encoding.c error.c parserInternals.c  \
 		parser.c tree.c hash.c list.c xmlIO.c xmlmemory.c uri.c  \
 		valid.c xlink.c HTMLparser.c HTMLtree.c debugXML.c xpath.c  \
 		xpointer.c xinclude.c nanohttp.c nanoftp.c DOCBparser.c \
-		catalog.c globals.c threads.c c14n.c xmlstring.c \
+		catalog.c globals.c threads.c c14n.c xmlstring.c buf.c \
 		xmlregexp.c xmlschemas.c xmlschemastypes.c xmlunicode.c \
 		triostr.c trio.c xmlreader.c relaxng.c dict.c SAX2.c \
 		xmlwriter.c legacy.c chvalid.c pattern.c xmlsave.c \
@@ -46,7 +46,7 @@ libxml2_la_SOURCES = SAX.c entities.c encoding.c error.c parserInternals.c  \
 		parser.c tree.c hash.c list.c xmlIO.c xmlmemory.c uri.c  \
 		valid.c xlink.c HTMLparser.c HTMLtree.c debugXML.c xpath.c  \
 		xpointer.c xinclude.c nanohttp.c nanoftp.c DOCBparser.c \
-		catalog.c globals.c threads.c c14n.c xmlstring.c \
+		catalog.c globals.c threads.c c14n.c xmlstring.c buf.c \
 		xmlregexp.c xmlschemas.c xmlschemastypes.c xmlunicode.c \
 		xmlreader.c relaxng.c dict.c SAX2.c \
 		xmlwriter.c legacy.c chvalid.c pattern.c xmlsave.c \
@@ -1195,7 +1195,7 @@ EXTRA_DIST = xml2-config.in xml2Conf.sh.in libxml.spec.in libxml2.spec \
              example/Makefile.am example/gjobread.c example/gjobs.xml \
 	     $(man_MANS) libxml-2.0.pc.in libxml-2.0-uninstalled.pc.in \
 	     trionan.c trionan.h triostr.c triostr.h trio.c trio.h \
-	     triop.h triodef.h libxml.h elfgcchack.h xzlib.h \
+	     triop.h triodef.h libxml.h elfgcchack.h xzlib.h buf.h \
 	     testThreadsWin32.c genUnicode.py TODO_SCHEMAS \
 	     dbgen.pl dbgenattr.pl regressions.py regressions.xml \
 	     README.tests Makefile.tests libxml2.syms \
diff --git a/buf.c b/buf.c
new file mode 100644
index 0000000..520a024
--- /dev/null
+++ b/buf.c
@@ -0,0 +1,1138 @@
+/*
+ * buf.c: memory buffers for libxml2
+ *
+ * new buffer structures and entry points to simplify the maintainance
+ * of libxml2 and ensure we keep good control over memory allocations
+ * and stay 64 bits clean.
+ * The new entry point use the xmlBufPtr opaque structure and
+ * xmlBuf...() counterparts to the old xmlBuf...() functions
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel veillard com
+ */
+
+#define IN_LIBXML
+#include "libxml.h"
+
+#include <string.h> /* for memset() only ! */
+#include <limits.h>
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <libxml/tree.h>
+#include <libxml/globals.h>
+#include <libxml/tree.h>
+#include "buf.h"
+
+/**
+ * xmlBuf:
+ *
+ * A buffer structure
+ */
+
+struct _xmlBuf {
+    xmlChar *content;		/* The buffer content UTF8 */
+    size_t use;		        /* The buffer size used */
+    size_t size;		/* The buffer size */
+    xmlBufferAllocationScheme alloc; /* The realloc method */
+    xmlChar *contentIO;		/* in IO mode we may have a different base */
+    xmlBufferPtr buffer;        /* wrapper for an old buffer */
+    int error;                  /* an error code if a failure occured */
+};
+
+/**
+ * xmlBufMemoryError:
+ * @extra:  extra informations
+ *
+ * Handle an out of memory condition
+ * To be improved...
+ */
+static void
+xmlBufMemoryError(xmlBufPtr buf, const char *extra)
+{
+    __xmlSimpleError(XML_FROM_BUFFER, XML_ERR_NO_MEMORY, NULL, NULL, extra);
+    if ((buf) && (buf->error == 0))
+        buf->error = XML_ERR_NO_MEMORY;
+}
+
+/**
+ * xmlBufOverflowError:
+ * @extra:  extra informations
+ *
+ * Handle a buffer overflow error
+ * To be improved...
+ */
+static void
+xmlBufOverflowError(xmlBufPtr buf, const char *extra)
+{
+    __xmlSimpleError(XML_FROM_BUFFER, XML_BUF_OVERFLOW, NULL, NULL, extra);
+    if ((buf) && (buf->error == 0))
+        buf->error = XML_BUF_OVERFLOW;
+}
+
+
+/**
+ * xmlBufCreate:
+ *
+ * routine to create an XML buffer.
+ * returns the new structure.
+ */
+xmlBufPtr
+xmlBufCreate(void) {
+    xmlBufPtr ret;
+
+    ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
+    if (ret == NULL) {
+	xmlBufMemoryError(NULL, "creating buffer");
+        return(NULL);
+    }
+    ret->use = 0;
+    ret->error = 0;
+    ret->buffer = NULL;
+    ret->size = xmlDefaultBufferSize;
+    ret->alloc = xmlBufferAllocScheme;
+    ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar));
+    if (ret->content == NULL) {
+	xmlBufMemoryError(ret, "creating buffer");
+	xmlFree(ret);
+        return(NULL);
+    }
+    ret->content[0] = 0;
+    ret->contentIO = NULL;
+    return(ret);
+}
+
+/**
+ * xmlBufCreateSize:
+ * @size: initial size of buffer
+ *
+ * routine to create an XML buffer.
+ * returns the new structure.
+ */
+xmlBufPtr
+xmlBufCreateSize(size_t size) {
+    xmlBufPtr ret;
+
+    ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
+    if (ret == NULL) {
+	xmlBufMemoryError(NULL, "creating buffer");
+        return(NULL);
+    }
+    ret->use = 0;
+    ret->error = 0;
+    ret->buffer = NULL;
+    ret->alloc = xmlBufferAllocScheme;
+    ret->size = (size ? size+2 : 0);         /* +1 for ending null */
+    if (ret->size){
+        ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar));
+        if (ret->content == NULL) {
+	    xmlBufMemoryError(ret, "creating buffer");
+            xmlFree(ret);
+            return(NULL);
+        }
+        ret->content[0] = 0;
+    } else
+	ret->content = NULL;
+    ret->contentIO = NULL;
+    return(ret);
+}
+
+/**
+ * xmlBufDetach:
+ * @buf:  the buffer
+ *
+ * Remove the string contained in a buffer and gie it back to the
+ * caller. The buffer is reset to an empty content.
+ * This doesn't work with immutable buffers as they can't be reset.
+ *
+ * Returns the previous string contained by the buffer.
+ */
+xmlChar *
+xmlBufDetach(xmlBufPtr buf) {
+    xmlChar *ret;
+
+    if (buf == NULL)
+        return(NULL);
+    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE)
+        return(NULL);
+    if (buf->buffer != NULL)
+        return(NULL);
+    if (buf->error)
+        return(NULL);
+
+    ret = buf->content;
+    buf->content = NULL;
+    buf->size = 0;
+    buf->use = 0;
+
+    return ret;
+}
+
+
+/**
+ * xmlBufCreateStatic:
+ * @mem: the memory area
+ * @size:  the size in byte
+ *
+ * routine to create an XML buffer from an immutable memory area.
+ * The area won't be modified nor copied, and is expected to be
+ * present until the end of the buffer lifetime.
+ *
+ * returns the new structure.
+ */
+xmlBufPtr
+xmlBufCreateStatic(void *mem, size_t size) {
+    xmlBufPtr ret;
+
+    if ((mem == NULL) || (size == 0))
+        return(NULL);
+
+    ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
+    if (ret == NULL) {
+	xmlBufMemoryError(NULL, "creating buffer");
+        return(NULL);
+    }
+    ret->use = size;
+    ret->size = size;
+    ret->alloc = XML_BUFFER_ALLOC_IMMUTABLE;
+    ret->content = (xmlChar *) mem;
+    ret->error = 0;
+    ret->buffer = NULL;
+    return(ret);
+}
+
+/**
+ *
+ *
+ */
+int
+xmlBufGetAllocationScheme(xmlBufPtr buf) {
+    if (buf == NULL) {
+#ifdef DEBUG_BUFFER
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlBufGetAllocationScheme: buf == NULL\n");
+#endif
+        return(-1);
+    }
+    return(buf->alloc);
+}
+
+/**
+ * xmlBufSetAllocationScheme:
+ * @buf:  the buffer to tune
+ * @scheme:  allocation scheme to use
+ *
+ * Sets the allocation scheme for this buffer
+ *
+ * returns 0 in case of success and -1 in case of failure
+ */
+int
+xmlBufSetAllocationScheme(xmlBufPtr buf,
+                          xmlBufferAllocationScheme scheme) {
+    if (buf == NULL) {
+#ifdef DEBUG_BUFFER
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlBufSetAllocationScheme: buf == NULL\n");
+#endif
+        return(-1);
+    }
+    if ((buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) ||
+        (buf->alloc == XML_BUFFER_ALLOC_IO))
+        return(-1);
+    if ((scheme == XML_BUFFER_ALLOC_DOUBLEIT) ||
+        (scheme == XML_BUFFER_ALLOC_EXACT) ||
+        (scheme == XML_BUFFER_ALLOC_HYBRID) ||
+        (scheme == XML_BUFFER_ALLOC_IMMUTABLE)) {
+	buf->alloc = scheme;
+        if (buf->buffer)
+            buf->buffer->alloc = scheme;
+        return(0);
+    }
+    /*
+     * Switching a buffer ALLOC_IO has the side effect of initializing
+     * the contentIO field with the current content
+     */
+    if (scheme == XML_BUFFER_ALLOC_IO) {
+        buf->alloc = XML_BUFFER_ALLOC_IO;
+        buf->contentIO = buf->content;
+    }
+    return(-1);
+}
+
+/**
+ * xmlBufFree:
+ * @buf:  the buffer to free
+ *
+ * Frees an XML buffer. It frees both the content and the structure which
+ * encapsulate it.
+ */
+void
+xmlBufFree(xmlBufPtr buf) {
+    if (buf == NULL) {
+#ifdef DEBUG_BUFFER
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlBufFree: buf == NULL\n");
+#endif
+	return;
+    }
+
+    if ((buf->alloc == XML_BUFFER_ALLOC_IO) &&
+        (buf->contentIO != NULL)) {
+        xmlFree(buf->contentIO);
+    } else if ((buf->content != NULL) &&
+        (buf->alloc != XML_BUFFER_ALLOC_IMMUTABLE)) {
+        xmlFree(buf->content);
+    }
+    xmlFree(buf);
+}
+
+/**
+ * xmlBufEmpty:
+ * @buf:  the buffer
+ *
+ * empty a buffer.
+ */
+void
+xmlBufEmpty(xmlBufPtr buf) {
+    if (buf == NULL) return;
+    if (buf->content == NULL) return;
+    buf->use = 0;
+    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) {
+        buf->content = BAD_CAST "";
+    } else if ((buf->alloc == XML_BUFFER_ALLOC_IO) &&
+               (buf->contentIO != NULL)) {
+        size_t start_buf = buf->content - buf->contentIO;
+
+	buf->size += start_buf;
+        buf->content = buf->contentIO;
+        buf->content[0] = 0;
+    } else {
+        buf->content[0] = 0;
+    }
+}
+
+/**
+ * xmlBufShrink:
+ * @buf:  the buffer to dump
+ * @len:  the number of xmlChar to remove
+ *
+ * Remove the beginning of an XML buffer.
+ * NOTE that this routine behaviour differs from xmlBufferShrink()
+ * as it will return 0 on error instead of -1 due to size_t being
+ * used as the return type.
+ *
+ * Returns the number of byte removed or 0 in case of failure
+ */
+size_t
+xmlBufShrink(xmlBufPtr buf, size_t len) {
+    if (buf == NULL) return(0);
+    if (len == 0) return(0);
+    if (len > buf->use) return(0);
+
+    buf->use -= len;
+    if ((buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) ||
+        ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL))) {
+	/*
+	 * we just move the content pointer, but also make sure
+	 * the perceived buffer size has shrinked accordingly
+	 */
+        buf->content += len;
+	buf->size -= len;
+
+        /*
+	 * sometimes though it maybe be better to really shrink
+	 * on IO buffers
+	 */
+	if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
+	    size_t start_buf = buf->content - buf->contentIO;
+	    if (start_buf >= buf->size) {
+		memmove(buf->contentIO, &buf->content[0], buf->use);
+		buf->content = buf->contentIO;
+		buf->content[buf->use] = 0;
+		buf->size += start_buf;
+	    }
+	}
+    } else {
+	memmove(buf->content, &buf->content[len], buf->use);
+	buf->content[buf->use] = 0;
+    }
+    return(len);
+}
+
+/**
+ * xmlBufGrowInternal:
+ * @buf:  the buffer
+ * @len:  the minimum free size to allocate
+ *
+ * Grow the available space of an XML buffer, @len is the target value
+ * Error checking should be done on buf->error since using the return
+ * value doesn't work that well
+ *
+ * Returns 0 in case of error or the length made available otherwise
+ */
+static size_t
+xmlBufGrowInternal(xmlBufPtr buf, size_t len) {
+    size_t size;
+    xmlChar *newbuf;
+
+    if (buf == NULL) return(0);
+
+    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0);
+    if (buf->use + len < buf->size)
+        return(buf->size - buf->use);
+
+    /*
+     * Windows has a BIG problem on realloc timing, so we try to double
+     * the buffer size (if that's enough) (bug 146697)
+     * Apparently BSD too, and it's probably best for linux too
+     * On an embedded system this may be something to change
+     */
+#if 1
+    if (buf->size > (size_t) len)
+        size = buf->size * 2;
+    else
+        size = buf->use + len + 100;
+#else
+    size = buf->use + len + 100;
+#endif
+
+    if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
+        size_t start_buf = buf->content - buf->contentIO;
+
+	newbuf = (xmlChar *) xmlRealloc(buf->contentIO, start_buf + size);
+	if (newbuf == NULL) {
+	    xmlBufMemoryError(buf, "growing buffer");
+	    return(0);
+	}
+	buf->contentIO = newbuf;
+	buf->content = newbuf + start_buf;
+    } else {
+	newbuf = (xmlChar *) xmlRealloc(buf->content, size);
+	if (newbuf == NULL) {
+	    xmlBufMemoryError(buf, "growing buffer");
+	    return(0);
+	}
+	buf->content = newbuf;
+    }
+    buf->size = size;
+    return(buf->size - buf->use);
+}
+
+/**
+ * xmlBufGrow:
+ * @buf:  the buffer
+ * @len:  the minimum free size to allocate
+ *
+ * Grow the available space of an XML buffer, @len is the target value
+ * This is been kept compatible with xmlBufferGrow() as much as possible
+ *
+ * Returns -1 in case of error or the length made available otherwise
+ */
+int
+xmlBufGrow(xmlBufPtr buf, int len) {
+    size_t ret;
+
+    if ((buf == NULL) || (len < 0)) return(-1);
+    if (len == 0)
+        return(0);
+    ret = xmlBufGrowInternal(buf, len);
+    if (buf->error != 0)
+        return(-1);
+    return((int) ret);
+}
+
+/**
+ * xmlBufInflate:
+ * @buf:  the buffer
+ * @len:  the minimum extra free size to allocate
+ *
+ * Grow the available space of an XML buffer, adding at least @len bytes
+ *
+ * Returns 0 if successful or -1 in case of error
+ */
+int
+xmlBufInflate(xmlBufPtr buf, size_t len) {
+    if (buf == NULL) return(-1);
+    xmlBufGrowInternal(buf, len + buf->size);
+    if (buf->error)
+        return(-1);
+    return(0);
+}
+
+/**
+ * xmlBufDump:
+ * @file:  the file output
+ * @buf:  the buffer to dump
+ *
+ * Dumps an XML buffer to  a FILE *.
+ * Returns the number of #xmlChar written
+ */
+size_t
+xmlBufDump(FILE *file, xmlBufPtr buf) {
+    size_t ret;
+
+    if (buf == NULL) {
+#ifdef DEBUG_BUFFER
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlBufDump: buf == NULL\n");
+#endif
+	return(0);
+    }
+    if (buf->content == NULL) {
+#ifdef DEBUG_BUFFER
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlBufDump: buf->content == NULL\n");
+#endif
+	return(0);
+    }
+    if (file == NULL)
+	file = stdout;
+    ret = fwrite(buf->content, sizeof(xmlChar), buf->use, file);
+    return(ret);
+}
+
+/**
+ * xmlBufContent:
+ * @buf:  the buffer
+ *
+ * Function to extract the content of a buffer
+ *
+ * Returns the internal content
+ */
+
+xmlChar *
+xmlBufContent(const xmlBufPtr buf)
+{
+    if(!buf)
+        return NULL;
+
+    return(buf->content);
+}
+
+/**
+ * xmlBufEnd:
+ * @buf:  the buffer
+ *
+ * Function to extract the end of the content of a buffer
+ *
+ * Returns the end of the internal content or NULL in case of error
+ */
+
+xmlChar *
+xmlBufEnd(const xmlBufPtr buf)
+{
+    if(!buf)
+        return NULL;
+
+    return(&buf->content[buf->use]);
+}
+
+/**
+ * xmlBufAddLen:
+ * @buf:  the buffer
+ * @len:  the size which were added at the end
+ *
+ * Sometime data may be added at the end of the buffer without
+ * using the xmlBuf APIs that is used to expand the used space
+ * and set the zero terminating at the end of the buffer
+ *
+ * Returns -1 in case of error and 0 otherwise
+ */
+int
+xmlBufAddLen(xmlBufPtr buf, size_t len) {
+    if ((buf == NULL) || (len > (buf->size - buf->use)))
+        return(-1);
+    buf->use += len;
+    if (buf->size > buf->use)
+        buf->content[buf->use] = 0;
+    else
+        return(-1);
+    return(0);
+}
+
+/**
+ * xmlBufErase:
+ * @buf:  the buffer
+ * @len:  the size to erase at the end
+ *
+ * Sometime data need to be erased at the end of the buffer
+ *
+ * Returns -1 in case of error and 0 otherwise
+ */
+int
+xmlBufErase(xmlBufPtr buf, size_t len) {
+    if ((buf == NULL) || (len > buf->use))
+        return(-1);
+    buf->use -= len;
+    buf->content[buf->use] = 0;
+    return(0);
+}
+
+/**
+ * xmlBufLength:
+ * @buf:  the buffer
+ *
+ * Function to get the length of a buffer
+ *
+ * Returns the length of data in the internal content
+ */
+
+size_t
+xmlBufLength(const xmlBufPtr buf)
+{
+    if (!buf)
+        return 0;
+
+    return(buf->use);
+}
+
+/**
+ * xmlBufUse:
+ * @buf:  the buffer
+ *
+ * Function to get the length of a buffer
+ *
+ * Returns the length of data in the internal content
+ */
+
+size_t
+xmlBufUse(const xmlBufPtr buf)
+{
+    if (!buf)
+        return 0;
+
+    return(buf->use);
+}
+
+/**
+ * xmlBufAvail:
+ * @buf:  the buffer
+ *
+ * Function to find how much free space is allocated but not
+ * used in the buffer. It does not account for the terminating zero
+ * usually needed
+ *
+ * Returns the amount or 0 if none or an error occured
+ */
+
+size_t
+xmlBufAvail(const xmlBufPtr buf)
+{
+    if (!buf)
+        return 0;
+
+    return(buf->size - buf->use);
+}
+
+/**
+ * xmlBufIsEmpty:
+ * @buf:  the buffer
+ *
+ * Tell if a buffer is empty
+ *
+ * Returns 0 if no, 1 if yes and -1 in case of error
+ */
+int
+xmlBufIsEmpty(const xmlBufPtr buf)
+{
+    if(!buf)
+        return(-1);
+
+    return(buf->use == 0);
+}
+
+/**
+ * xmlBufResize:
+ * @buf:  the buffer to resize
+ * @size:  the desired size
+ *
+ * Resize a buffer to accommodate minimum size of @size.
+ *
+ * Returns  0 in case of problems, 1 otherwise
+ */
+int
+xmlBufResize(xmlBufPtr buf, size_t size)
+{
+    unsigned int newSize;
+    xmlChar* rebuf = NULL;
+    size_t start_buf;
+
+    if (buf == NULL)
+        return(0);
+
+    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0);
+
+    /* Don't resize if we don't have to */
+    if (size < buf->size)
+        return 1;
+
+    /* figure out new size */
+    switch (buf->alloc){
+	case XML_BUFFER_ALLOC_IO:
+	case XML_BUFFER_ALLOC_DOUBLEIT:
+	    /*take care of empty case*/
+	    newSize = (buf->size ? buf->size*2 : size + 10);
+	    while (size > newSize) {
+	        if (newSize > UINT_MAX / 2) {
+	            xmlBufMemoryError(buf, "growing buffer");
+	            return 0;
+	        }
+	        newSize *= 2;
+	    }
+	    break;
+	case XML_BUFFER_ALLOC_EXACT:
+	    newSize = size+10;
+	    break;
+        case XML_BUFFER_ALLOC_HYBRID:
+            if (buf->use < BASE_BUFFER_SIZE)
+                newSize = size;
+            else {
+                newSize = buf->size * 2;
+                while (size > newSize) {
+                    if (newSize > UINT_MAX / 2) {
+                        xmlBufMemoryError(buf, "growing buffer");
+                        return 0;
+                    }
+                    newSize *= 2;
+                }
+            }
+            break;
+
+	default:
+	    newSize = size+10;
+	    break;
+    }
+
+    if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
+        start_buf = buf->content - buf->contentIO;
+
+        if (start_buf > newSize) {
+	    /* move data back to start */
+	    memmove(buf->contentIO, buf->content, buf->use);
+	    buf->content = buf->contentIO;
+	    buf->content[buf->use] = 0;
+	    buf->size += start_buf;
+	} else {
+	    rebuf = (xmlChar *) xmlRealloc(buf->contentIO, start_buf + newSize);
+	    if (rebuf == NULL) {
+		xmlBufMemoryError(buf, "growing buffer");
+		return 0;
+	    }
+	    buf->contentIO = rebuf;
+	    buf->content = rebuf + start_buf;
+	}
+    } else {
+	if (buf->content == NULL) {
+	    rebuf = (xmlChar *) xmlMallocAtomic(newSize);
+	} else if (buf->size - buf->use < 100) {
+	    rebuf = (xmlChar *) xmlRealloc(buf->content, newSize);
+        } else {
+	    /*
+	     * if we are reallocating a buffer far from being full, it's
+	     * better to make a new allocation and copy only the used range
+	     * and free the old one.
+	     */
+	    rebuf = (xmlChar *) xmlMallocAtomic(newSize);
+	    if (rebuf != NULL) {
+		memcpy(rebuf, buf->content, buf->use);
+		xmlFree(buf->content);
+		rebuf[buf->use] = 0;
+	    }
+	}
+	if (rebuf == NULL) {
+	    xmlBufMemoryError(buf, "growing buffer");
+	    return 0;
+	}
+	buf->content = rebuf;
+    }
+    buf->size = newSize;
+
+    return 1;
+}
+
+/**
+ * xmlBufAdd:
+ * @buf:  the buffer to dump
+ * @str:  the #xmlChar string
+ * @len:  the number of #xmlChar to add
+ *
+ * Add a string range to an XML buffer. if len == -1, the length of
+ * str is recomputed.
+ *
+ * Returns 0 successful, a positive error code number otherwise
+ *         and -1 in case of internal or API error.
+ */
+int
+xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) {
+    unsigned int needSize;
+
+    if ((str == NULL) || (buf == NULL)) {
+	return -1;
+    }
+    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
+    if (len < -1) {
+#ifdef DEBUG_BUFFER
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlBufAdd: len < 0\n");
+#endif
+	return -1;
+    }
+    if (len == 0) return 0;
+
+    if (len < 0)
+        len = xmlStrlen(str);
+
+    if (len < 0) return -1;
+    if (len == 0) return 0;
+
+    needSize = buf->use + len + 2;
+    if (needSize > buf->size){
+        if (!xmlBufResize(buf, needSize)){
+	    xmlBufMemoryError(buf, "growing buffer");
+            return XML_ERR_NO_MEMORY;
+        }
+    }
+
+    memmove(&buf->content[buf->use], str, len*sizeof(xmlChar));
+    buf->use += len;
+    buf->content[buf->use] = 0;
+    return 0;
+}
+
+/**
+ * xmlBufAddHead:
+ * @buf:  the buffer
+ * @str:  the #xmlChar string
+ * @len:  the number of #xmlChar to add
+ *
+ * Add a string range to the beginning of an XML buffer.
+ * if len == -1, the length of @str is recomputed.
+ *
+ * Returns 0 successful, a positive error code number otherwise
+ *         and -1 in case of internal or API error.
+ */
+int
+xmlBufAddHead(xmlBufPtr buf, const xmlChar *str, int len) {
+    unsigned int needSize;
+
+    if (buf == NULL)
+        return(-1);
+    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
+    if (str == NULL) {
+#ifdef DEBUG_BUFFER
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlBufAddHead: str == NULL\n");
+#endif
+	return -1;
+    }
+    if (len < -1) {
+#ifdef DEBUG_BUFFER
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlBufAddHead: len < 0\n");
+#endif
+	return -1;
+    }
+    if (len == 0) return 0;
+
+    if (len < 0)
+        len = xmlStrlen(str);
+
+    if (len <= 0) return -1;
+
+    if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
+        size_t start_buf = buf->content - buf->contentIO;
+
+	if (start_buf > (unsigned int) len) {
+	    /*
+	     * We can add it in the space previously shrinked
+	     */
+	    buf->content -= len;
+            memmove(&buf->content[0], str, len);
+	    buf->use += len;
+	    buf->size += len;
+	    return(0);
+	}
+    }
+    needSize = buf->use + len + 2;
+    if (needSize > buf->size){
+        if (!xmlBufResize(buf, needSize)){
+	    xmlBufMemoryError(buf, "growing buffer");
+            return XML_ERR_NO_MEMORY;
+        }
+    }
+
+    memmove(&buf->content[len], &buf->content[0], buf->use);
+    memmove(&buf->content[0], str, len);
+    buf->use += len;
+    buf->content[buf->use] = 0;
+    return 0;
+}
+
+/**
+ * xmlBufCat:
+ * @buf:  the buffer to add to
+ * @str:  the #xmlChar string
+ *
+ * Append a zero terminated string to an XML buffer.
+ *
+ * Returns 0 successful, a positive error code number otherwise
+ *         and -1 in case of internal or API error.
+ */
+int
+xmlBufCat(xmlBufPtr buf, const xmlChar *str) {
+    if (buf == NULL)
+        return(-1);
+    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
+    if (str == NULL) return -1;
+    return xmlBufAdd(buf, str, -1);
+}
+
+/**
+ * xmlBufCCat:
+ * @buf:  the buffer to dump
+ * @str:  the C char string
+ *
+ * Append a zero terminated C string to an XML buffer.
+ *
+ * Returns 0 successful, a positive error code number otherwise
+ *         and -1 in case of internal or API error.
+ */
+int
+xmlBufCCat(xmlBufPtr buf, const char *str) {
+    const char *cur;
+
+    if (buf == NULL)
+        return(-1);
+    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
+    if (str == NULL) {
+#ifdef DEBUG_BUFFER
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlBufCCat: str == NULL\n");
+#endif
+	return -1;
+    }
+    for (cur = str;*cur != 0;cur++) {
+        if (buf->use  + 10 >= buf->size) {
+            if (!xmlBufResize(buf, buf->use+10)){
+		xmlBufMemoryError(buf, "growing buffer");
+                return XML_ERR_NO_MEMORY;
+            }
+        }
+        buf->content[buf->use++] = *cur;
+    }
+    buf->content[buf->use] = 0;
+    return 0;
+}
+
+/**
+ * xmlBufWriteCHAR:
+ * @buf:  the XML buffer
+ * @string:  the string to add
+ *
+ * routine which manages and grows an output buffer. This one adds
+ * xmlChars at the end of the buffer.
+ *
+ * Returns 0 if successful, a positive error code number otherwise
+ *         and -1 in case of internal or API error.
+ */
+int
+xmlBufWriteCHAR(xmlBufPtr buf, const xmlChar *string) {
+    if (buf == NULL)
+        return(-1);
+    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE)
+        return(-1);
+    return(xmlBufCat(buf, string));
+}
+
+/**
+ * xmlBufWriteChar:
+ * @buf:  the XML buffer output
+ * @string:  the string to add
+ *
+ * routine which manage and grows an output buffer. This one add
+ * C chars at the end of the array.
+ *
+ * Returns 0 if successful, a positive error code number otherwise
+ *         and -1 in case of internal or API error.
+ */
+int
+xmlBufWriteChar(xmlBufPtr buf, const char *string) {
+    if (buf == NULL)
+        return(-1);
+    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE)
+        return(-1);
+    return(xmlBufCCat(buf, string));
+}
+
+
+/**
+ * xmlBufWriteQuotedString:
+ * @buf:  the XML buffer output
+ * @string:  the string to add
+ *
+ * routine which manage and grows an output buffer. This one writes
+ * a quoted or double quoted #xmlChar string, checking first if it holds
+ * quote or double-quotes internally
+ *
+ * Returns 0 if successful, a positive error code number otherwise
+ *         and -1 in case of internal or API error.
+ */
+int
+xmlBufWriteQuotedString(xmlBufPtr buf, const xmlChar *string) {
+    const xmlChar *cur, *base;
+    if (buf == NULL)
+        return(-1);
+    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE)
+        return(-1);
+    if (xmlStrchr(string, '\"')) {
+        if (xmlStrchr(string, '\'')) {
+#ifdef DEBUG_BUFFER
+	    xmlGenericError(xmlGenericErrorContext,
+ "xmlBufWriteQuotedString: string contains quote and double-quotes !\n");
+#endif
+	    xmlBufCCat(buf, "\"");
+            base = cur = string;
+            while(*cur != 0){
+                if(*cur == '"'){
+                    if (base != cur)
+                        xmlBufAdd(buf, base, cur - base);
+                    xmlBufAdd(buf, BAD_CAST "&quot;", 6);
+                    cur++;
+                    base = cur;
+                }
+                else {
+                    cur++;
+                }
+            }
+            if (base != cur)
+                xmlBufAdd(buf, base, cur - base);
+	    xmlBufCCat(buf, "\"");
+	}
+        else{
+	    xmlBufCCat(buf, "\'");
+            xmlBufCat(buf, string);
+	    xmlBufCCat(buf, "\'");
+        }
+    } else {
+        xmlBufCCat(buf, "\"");
+        xmlBufCat(buf, string);
+        xmlBufCCat(buf, "\"");
+    }
+    return(0);
+}
+
+/**
+ * xmlBufFromBuffer:
+ * @buffer: incoming old buffer to convert to a new one
+ *
+ * Helper routine to switch from the old buffer structures in use
+ * in various APIs. It creates a wrapper xmlBufPtr which will be
+ * used for internal processing until the xmlBufBackToBuffer() is
+ * issued.
+ *
+ * Returns a new xmlBufPtr unless the call failed and NULL is returned
+ */
+xmlBufPtr
+xmlBufFromBuffer(xmlBufferPtr buffer) {
+    xmlBufPtr ret;
+
+    if (buffer == NULL)
+        return(NULL);
+
+    ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
+    if (ret == NULL) {
+	xmlBufMemoryError(NULL, "creating buffer");
+        return(NULL);
+    }
+    ret->use = buffer->use;
+    ret->size = buffer->size;
+    ret->error = 0;
+    ret->buffer = buffer;
+    ret->alloc = buffer->alloc;
+    ret->content = buffer->content;
+    ret->contentIO = buffer->contentIO;
+
+    return(ret);
+}
+
+/**
+ * xmlBufBackToBuffer:
+ * @buf: new buffer wrapping the old one
+ *
+ * Function to be called once internal processing had been done to
+ * update back the buffer provided by the user. This can lead to
+ * a failure in case the size accumulated in the xmlBuf is larger
+ * than what an xmlBuffer can support on 64 bits (INT_MAX)
+ * The xmlBufPtr @buf wrapper is deallocated by this call in any case.
+ *
+ * Returns the old xmlBufferPtr unless the call failed and NULL is returned
+ */
+xmlBufferPtr
+xmlBufBackToBuffer(xmlBufPtr buf) {
+    xmlBufferPtr ret;
+
+    if (buf == NULL)
+        return(NULL);
+    if (buf->buffer == NULL) {
+        xmlBufFree(buf);
+        return(NULL);
+    }
+
+    ret = buf->buffer;
+    /*
+     * What to do in case of error in the buffer ???
+     */
+    if (buf->use > INT_MAX) {
+        /*
+         * Worse case, we really allocated and used more than the
+         * maximum allowed memory for an xmlBuffer on this architecture.
+         * Keep the buffer but provide a truncated size value.
+         */
+        xmlBufOverflowError(buf, "Used size too big for xmlBuffer");
+        ret->use = INT_MAX;
+        ret->size = INT_MAX;
+    } else if (buf->size > INT_MAX) {
+        /*
+         * milder case, we allocated more than the maximum allowed memory
+         * for an xmlBuffer on this architecture, but used less than the
+         * limit.
+         * Keep the buffer but provide a truncated size value.
+         */
+        xmlBufOverflowError(buf, "Allocated size too big for xmlBuffer");
+        ret->size = INT_MAX;
+    }
+    ret->use = (int) buf->use;
+    ret->size = (int) buf->size;
+    ret->alloc = buf->alloc;
+    ret->content = buf->content;
+    ret->contentIO = buf->contentIO;
+    xmlFree(buf);
+    return(ret);
+}
+
+/**
+ * xmlBufMergeBuffer:
+ * @buf: an xmlBufPtr
+ * @buffer: the buffer to consume into @buf
+ *
+ * The content of @buffer is appended to @buf and @buffer is freed
+ *
+ * Returns -1 in case of error, 0 otherwise, in any case @buffer is freed
+ */
+int
+xmlBufMergeBuffer(xmlBufPtr buf, xmlBufferPtr buffer) {
+    int ret;
+
+    if (buffer == NULL)
+        return(0);
+    if ((buf != NULL) && (buffer->content != NULL) && (buffer->use > 0)) {
+        ret = xmlBufAdd(buf, buffer->content, buffer->use);
+    }
+    xmlBufferFree(buffer);
+    return(ret);
+}
diff --git a/buf.h b/buf.h
new file mode 100644
index 0000000..4d3afd0
--- /dev/null
+++ b/buf.h
@@ -0,0 +1,64 @@
+/*
+ * buf.h: Internal Interfaces for memory buffers in libxml2
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel veillard com
+ */
+
+#ifndef __XML_BUF_H__
+#define __XML_BUF_H__
+
+#include <libxml/tree.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+xmlBufPtr xmlBufCreate(void);
+xmlBufPtr xmlBufCreateSize(size_t size);
+xmlBufPtr xmlBufCreateStatic(void *mem, size_t size);
+
+int xmlBufSetAllocationScheme(xmlBufPtr buf,
+                              xmlBufferAllocationScheme scheme);
+int xmlBufGetAllocationScheme(xmlBufPtr buf);
+
+void xmlBufFree(xmlBufPtr buf);
+void xmlBufEmpty(xmlBufPtr buf);
+
+size_t xmlBufShrink(xmlBufPtr buf, size_t len);
+int xmlBufGrow(xmlBufPtr buf, int len);
+int xmlBufInflate(xmlBufPtr buf, size_t len);
+int xmlBufResize(xmlBufPtr buf, size_t len);
+
+int xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len);
+int xmlBufAddHead(xmlBufPtr buf, const xmlChar *str, int len);
+int xmlBufCat(xmlBufPtr buf, const xmlChar *str);
+int xmlBufCCat(xmlBufPtr buf, const char *str);
+int xmlBufWriteCHAR(xmlBufPtr buf, const xmlChar *string);
+int xmlBufWriteChar(xmlBufPtr buf, const char *string);
+int xmlBufWriteQuotedString(xmlBufPtr buf, const xmlChar *string);
+
+size_t xmlBufAvail(xmlBufPtr buf);
+size_t xmlBufLength(xmlBufPtr buf);
+size_t xmlBufUse(xmlBufPtr buf);
+int xmlBufIsEmpty(xmlBufPtr buf);
+int xmlBufAddLen(xmlBufPtr buf, size_t len);
+int xmlBufErase(xmlBufPtr buf, size_t len);
+
+xmlChar * xmlBufContent(const xmlBufPtr buf);
+xmlChar * xmlBufEnd(const xmlBufPtr buf);
+
+xmlChar * xmlBufDetach(xmlBufPtr buf);
+
+size_t xmlBufDump(FILE *file, xmlBufPtr buf);
+
+xmlBufPtr xmlBufFromBuffer(xmlBufferPtr buffer);
+xmlBufferPtr xmlBufBackToBuffer(xmlBufPtr buf);
+int xmlBufMergeBuffer(xmlBufPtr buf, xmlBufferPtr buffer);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_BUF_H__ */
+
diff --git a/include/libxml/tree.h b/include/libxml/tree.h
index 2196f8d..7e821bb 100644
--- a/include/libxml/tree.h
+++ b/include/libxml/tree.h
@@ -13,6 +13,7 @@
 #define __XML_TREE_H__
 
 #include <stdio.h>
+#include <limits.h>
 #include <libxml/xmlversion.h>
 #include <libxml/xmlstring.h>
 
@@ -81,7 +82,8 @@ typedef enum {
 /**
  * xmlBuffer:
  *
- * A buffer structure.
+ * A buffer structure, this old construct is limited to 2GB and
+ * is being deprecated, use API with xmlBuf instead
  */
 typedef struct _xmlBuffer xmlBuffer;
 typedef xmlBuffer *xmlBufferPtr;
@@ -94,6 +96,23 @@ struct _xmlBuffer {
 };
 
 /**
+ * xmlBuf:
+ *
+ * A buffer structure, new one, the actual structure internals are not public
+ */
+
+typedef struct _xmlBuf xmlBuf;
+
+/**
+ * xmlBufPtr:
+ *
+ * A pointer to a buffer structure, the actual structure internals are not
+ * public
+ */
+
+typedef xmlBuf *xmlBufPtr;
+
+/**
  * XML_XML_NAMESPACE:
  *
  * This is the namespace for the special xml: prefix predefined in the
diff --git a/include/libxml/xmlerror.h b/include/libxml/xmlerror.h
index e924211..85caf52 100644
--- a/include/libxml/xmlerror.h
+++ b/include/libxml/xmlerror.h
@@ -62,7 +62,8 @@ typedef enum {
     XML_FROM_WRITER,	/* The xmlwriter module */
     XML_FROM_MODULE,	/* The dynamically loaded module module*/
     XML_FROM_I18N,	/* The module handling character conversion */
-    XML_FROM_SCHEMATRONV	/* The Schematron validator module */
+    XML_FROM_SCHEMATRONV,/* The Schematron validator module */
+    XML_FROM_BUFFER     /* The buffers module */
 } xmlErrorDomain;
 
 /**
@@ -825,7 +826,8 @@ typedef enum {
     XML_I18N_NO_HANDLER, /* 6001 */
     XML_I18N_EXCESS_HANDLER, /* 6002 */
     XML_I18N_CONV_FAILED, /* 6003 */
-    XML_I18N_NO_OUTPUT /* 6004 */
+    XML_I18N_NO_OUTPUT, /* 6004 */
+    XML_BUF_OVERFLOW = 7000
 #if 0
     XML_CHECK_, /* 5033 */
     XML_CHECK_X /* 503 */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]