src/qt3support/tools/q3garray.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/qt3support/tools/q3garray.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,798 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+#if defined(Q_CC_BOR)
+    // needed for qsort() because of a std namespace problem on Borland
+#   include "qplatformdefs.h"
+#elif defined(Q_WS_WIN)
+    // needed for bsearch on some platforms
+#   include "qt_windows.h"
+#endif
+
+#define	 Q3GARRAY_CPP
+#include "q3garray.h"
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef QT_NO_THREAD
+#  include "private/qmutexpool_p.h"
+#endif
+
+#if defined(Q_OS_WINCE)
+#   include "qfunctions_wince.h"
+#endif
+QT_BEGIN_NAMESPACE
+
+/*
+  If USE_MALLOC isn't defined, we use new[] and delete[] to allocate
+  memory. The documentation for QMemArray<T>::assign() explicitly
+  mentions that the array is freed using free(), so don't mess around
+  with USE_MALLOC unless you know what you're doing.
+*/
+#define USE_MALLOC
+
+#undef NEW
+#undef DELETE
+
+#if defined(USE_MALLOC)
+#define NEW(type,size)	((type*)malloc(size*sizeof(type)))
+#define DELETE(array)	(free((char*)array))
+#else
+#define NEW(type,size)	(new type[size])
+#define DELETE(array)	(delete[] array)
+#define DONT_USE_REALLOC			// comment to use realloc()
+#endif
+
+/*!
+  \class Q3GArray
+  \reentrant
+  \brief The Q3GArray class is an internal class for implementing the QMemArray class.
+
+  \internal
+
+  Q3GArray is a strictly internal class that acts as base class for the
+  QMemArray template array.
+
+  It contains an array of bytes and has no notion of an array element.
+*/
+
+
+/*!
+  Constructs a null array.
+*/
+
+Q3GArray::Q3GArray()
+{
+    shd = newData();
+    Q_CHECK_PTR(shd);
+}
+
+/*!
+  Dummy constructor; does not allocate any data.
+
+  This constructor does not initialize any array data so subclasses
+  must do it. The intention is to make the code more efficient.
+*/
+
+Q3GArray::Q3GArray(int, int)
+    : shd(0)
+{
+}
+
+/*!
+  Constructs an array with room for \a size bytes.
+*/
+
+Q3GArray::Q3GArray(int size)
+{
+    if (size < 0) {
+#if defined(QT_CHECK_RANGE)
+	qWarning("Q3GArray: Cannot allocate array with negative length");
+#endif
+	size = 0;
+    }
+    shd = newData();
+    Q_CHECK_PTR(shd);
+    if (size == 0)				// zero length
+	return;
+    shd->data = NEW(char,size);
+    Q_CHECK_PTR(shd->data);
+    shd->len =
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+	shd->maxl =
+#endif
+	size;
+}
+
+/*!
+  Constructs a shallow copy of \a a.
+*/
+
+Q3GArray::Q3GArray(const Q3GArray &a)
+{
+    shd = a.shd;
+    shd->ref();
+}
+
+/*!
+  Dereferences the array data and deletes it if this was the last
+  reference.
+*/
+
+Q3GArray::~Q3GArray()
+{
+    if (shd && shd->deref()) {		// delete when last reference
+	if (shd->data)			// is lost
+	    DELETE(shd->data);
+	deleteData(shd);
+	shd = 0;
+    }
+}
+
+
+/*!
+  \fn Q3GArray &Q3GArray::operator=(const Q3GArray &a)
+
+  Assigns a shallow copy of \a a to this array and returns a reference to
+  this array.  Equivalent to assign().
+*/
+
+/*!
+  \fn void Q3GArray::detach()
+
+  Detaches this array from shared array data.
+*/
+
+/*!
+  \fn char *Q3GArray::data() const
+
+  Returns a pointer to the actual array data.
+*/
+
+/*!
+  \fn uint Q3GArray::nrefs() const
+
+  Returns the reference count.
+*/
+
+/*!
+  \fn uint Q3GArray::size() const
+
+  Returns the size of the array, in bytes.
+*/
+
+
+/*!
+  Returns true if this array is equal to \a a, otherwise false.
+  The comparison is bitwise, of course.
+*/
+
+bool Q3GArray::isEqual(const Q3GArray &a) const
+{
+    if (size() != a.size())			// different size
+	return false;
+    if (data() == a.data())			// has same data
+	return true;
+    return (size() ? memcmp(data(), a.data(), size()) : 0) == 0;
+}
+
+
+/*!
+  Resizes the array to \a newsize bytes. \a optim is either
+  MemOptim (the default) or SpeedOptim.
+*/
+bool Q3GArray::resize(uint newsize, Optimization optim)
+{
+#ifndef QT_Q3GARRAY_SPEED_OPTIM
+    Q_UNUSED(optim);
+#endif
+
+    if (newsize == shd->len
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+	 && newsize == shd->maxl
+#endif
+	) // nothing to do
+	return true;
+    if (newsize == 0) {			// remove array
+	if (shd->data)
+	    DELETE(shd->data);
+	shd->data = 0;
+	shd->len = 0;
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+	shd->maxl = 0;
+#endif
+	return true;
+    }
+
+    uint newmaxl = newsize;
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+    if (optim == SpeedOptim) {
+	if (newsize <= shd->maxl &&
+	     (newsize * 4 > shd->maxl || shd->maxl <= 4)) {
+	    shd->len = newsize;
+	    return true;
+	}
+	newmaxl = 4;
+	while (newmaxl < newsize)
+	    newmaxl *= 2;
+	// try to spare some memory
+	if (newmaxl >= 1024 * 1024 && newsize <= newmaxl - (newmaxl >> 2))
+	    newmaxl -= newmaxl >> 2;
+    }
+    shd->maxl = newmaxl;
+#endif
+
+    if (shd->data) {				// existing data
+#if defined(DONT_USE_REALLOC)
+	char *newdata = NEW(char,newsize);	// manual realloc
+	memcpy(newdata, shd->data, QMIN(shd->len,newmaxl));
+	DELETE(shd->data);
+	shd->data = newdata;
+#else
+	shd->data = (char *)realloc(shd->data, newmaxl);
+#endif
+    } else {
+	shd->data = NEW(char,newmaxl);
+    }
+    if (!shd->data)				// no memory
+	return false;
+    shd->len = newsize;
+    return true;
+}
+
+/*!\overload
+*/
+bool Q3GArray::resize(uint newsize)
+{
+    return resize(newsize, MemOptim);
+}
+
+
+/*!
+  Fills the array with the repeated occurrences of \a d, which is
+  \a sz bytes long.
+  If \a len is specified as different from -1, then the array will be
+  resized to \a len*sz before it is filled.
+
+  Returns true if successful, or false if the memory cannot be allocated
+  (only when \a len != -1).
+
+  \sa resize()
+*/
+
+bool Q3GArray::fill(const char *d, int len, uint sz)
+{
+    if (len < 0)
+	len = shd->len/sz;			// default: use array length
+    else if (!resize(len*sz))
+	return false;
+    if (sz == 1)				// 8 bit elements
+	memset(data(), *d, len);
+    else if (sz == 4) {			// 32 bit elements
+	register Q_INT32 *x = (Q_INT32*)data();
+	Q_INT32 v = *((Q_INT32*)d);
+	while (len--)
+	    *x++ = v;
+    } else if (sz == 2) {			// 16 bit elements
+	register Q_INT16 *x = (Q_INT16*)data();
+	Q_INT16 v = *((Q_INT16*)d);
+	while (len--)
+	    *x++ = v;
+    } else {					// any other size elements
+	register char *x = data();
+	while (len--) {			// more complicated
+	    memcpy(x, d, sz);
+	    x += sz;
+	}
+    }
+    return true;
+}
+
+/*!
+    \overload
+  Shallow copy. Dereference the current array and references the data
+  contained in \a a instead. Returns a reference to this array.
+  \sa operator=()
+*/
+
+Q3GArray &Q3GArray::assign(const Q3GArray &a)
+{
+    a.shd->ref();				// avoid 'a = a'
+    if (shd->deref()) {			// delete when last reference
+	if (shd->data)			// is lost
+	    DELETE(shd->data);
+	deleteData(shd);
+    }
+    shd = a.shd;
+    return *this;
+}
+
+/*!
+  Shallow copy. Dereference the current array and references the
+  array data \a d, which contains \a len bytes.
+  Returns a reference to this array.
+
+  Do not delete \a d later, because Q3GArray takes care of that.
+*/
+
+Q3GArray &Q3GArray::assign(const char *d, uint len)
+{
+    if (shd->count > 1) {			// disconnect this
+	shd->count--;
+	shd = newData();
+	Q_CHECK_PTR(shd);
+    } else {
+	if (shd->data)
+	    DELETE(shd->data);
+    }
+    shd->data = (char *)d;
+    shd->len =
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+	shd->maxl =
+#endif
+	len;
+    return *this;
+}
+
+/*!
+  Deep copy. Dereference the current array and obtains a copy of the data
+  contained in \a a instead. Returns a reference to this array.
+  \sa assign(), operator=()
+*/
+
+Q3GArray &Q3GArray::duplicate(const Q3GArray &a)
+{
+    if (a.shd == shd) {			// a.duplicate(a) !
+	if (shd->count > 1) {
+	    shd->count--;
+	    register array_data *n = newData();
+	    Q_CHECK_PTR(n);
+	    if ((n->len=shd->len)) {
+		n->data = NEW(char,n->len);
+		Q_CHECK_PTR(n->data);
+		if (n->data)
+		    memcpy(n->data, shd->data, n->len);
+	    } else {
+		n->data = 0;
+	    }
+	    shd = n;
+	}
+	return *this;
+    }
+    char *oldptr = 0;
+    if (shd->count > 1) {			// disconnect this
+	shd->count--;
+	shd = newData();
+	Q_CHECK_PTR(shd);
+    } else {					// delete after copy was made
+	oldptr = shd->data;
+    }
+    if (a.shd->len) {				// duplicate data
+	shd->data = NEW(char,a.shd->len);
+	Q_CHECK_PTR(shd->data);
+	if (shd->data)
+	    memcpy(shd->data, a.shd->data, a.shd->len);
+    } else {
+	shd->data = 0;
+    }
+    shd->len =
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+	shd->maxl =
+#endif
+	a.shd->len;
+    if (oldptr)
+	DELETE(oldptr);
+    return *this;
+}
+
+/*!
+    \overload
+  Deep copy. Dereferences the current array and obtains a copy of
+  \a len characters from array data \a d instead.  Returns a reference
+  to this array.
+  \sa assign(), operator=()
+*/
+
+Q3GArray &Q3GArray::duplicate(const char *d, uint len)
+{
+    char *data;
+    if (d == 0 || len == 0) {
+	data = 0;
+	len  = 0;
+    } else {
+	if (shd->count == 1 && shd->len == len) {
+	    if (shd->data != d)		// avoid self-assignment
+		memcpy(shd->data, d, len);	// use same buffer
+	    return *this;
+	}
+	data = NEW(char,len);
+	Q_CHECK_PTR(data);
+	memcpy(data, d, len);
+    }
+    if (shd->count > 1) {			// detach
+	shd->count--;
+	shd = newData();
+	Q_CHECK_PTR(shd);
+    } else {					// just a single reference
+	if (shd->data)
+	    DELETE(shd->data);
+    }
+    shd->data = data;
+    shd->len =
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+	shd->maxl =
+#endif
+	len;
+    return *this;
+}
+
+/*!
+  Resizes this array to \a len bytes and copies the \a len bytes at
+  address \a d into it.
+
+  \warning This function disregards the reference count mechanism.  If
+  other Q3GArrays reference the same data as this, all will be updated.
+*/
+
+void Q3GArray::store(const char *d, uint len)
+{						// store, but not deref
+    resize(len);
+    memcpy(shd->data, d, len);
+}
+
+
+/*!
+  \fn array_data *Q3GArray::sharedBlock() const
+
+  Returns a pointer to the shared array block.
+
+  \warning
+
+  Do not use this function.  Using it is begging for trouble.  We dare
+  not remove it, for fear of breaking code, but we \e strongly
+  discourage new use of it.
+*/
+
+/*!
+  \fn void Q3GArray::setSharedBlock(array_data *p)
+
+  Sets the shared array block to \a p.
+
+  \warning
+
+  Do not use this function.  Using it is begging for trouble.  We dare
+  not remove it, for fear of breaking code, but we \e strongly
+  discourage new use of it.
+*/
+
+
+/*!
+  Sets raw data and returns a reference to the array.
+
+  Dereferences the current array and sets the new array data to \a d and
+  the new array size to \a len.	 Do not attempt to resize or re-assign the
+  array data when raw data has been set.
+  Call resetRawData(d,len) to reset the array.
+
+  Setting raw data is useful because it sets QMemArray data without
+  allocating memory or copying data.
+
+  Example of intended use:
+  \snippet doc/src/snippets/code/src_qt3support_tools_q3garray.cpp 0
+
+  Example of misuse (do not do this):
+  \snippet doc/src/snippets/code/src_qt3support_tools_q3garray.cpp 1
+
+  \warning If you do not call resetRawData(), Q3GArray will attempt to
+  deallocate or reallocate the raw data, which might not be too good.
+  Be careful.
+*/
+
+Q3GArray &Q3GArray::setRawData(const char *d, uint len)
+{
+    duplicate(0, 0);				// set null data
+    shd->data = (char *)d;
+    shd->len  = len;
+    return *this;
+}
+
+/*!
+  Resets raw data.
+
+  The arguments must be the data, \a d, and length \a len, that were
+  passed to setRawData().  This is for consistency checking.
+*/
+
+void Q3GArray::resetRawData(const char *d, uint len)
+{
+    if (d != shd->data || len != shd->len) {
+#if defined(QT_CHECK_STATE)
+	qWarning("Q3GArray::resetRawData: Inconsistent arguments");
+#endif
+	return;
+    }
+    shd->data = 0;
+    shd->len  = 0;
+}
+
+
+/*!
+  Finds the first occurrence of \a d in the array from position \a index,
+  where \a sz is the size of the \a d element.
+
+  Note that \a index is given in units of \a sz, not bytes.
+
+  This function only compares whole cells, not bytes.
+*/
+
+int Q3GArray::find(const char *d, uint index, uint sz) const
+{
+    index *= sz;
+    if (index >= shd->len) {
+#if defined(QT_CHECK_RANGE)
+	qWarning("Q3GArray::find: Index %d out of range", index/sz);
+#endif
+	return -1;
+    }
+    register uint i;
+    uint ii;
+    switch (sz) {
+	case 1: {				// 8 bit elements
+	    register char *x = data() + index;
+	    char v = *d;
+	    for (i=index; i<shd->len; i++) {
+		if (*x++ == v)
+		    break;
+	    }
+	    ii = i;
+	    }
+	    break;
+	case 2: {				// 16 bit elements
+	    register Q_INT16 *x = (Q_INT16*)(data() + index);
+	    Q_INT16 v = *((Q_INT16*)d);
+	    for (i=index; i<shd->len; i+=2) {
+		if (*x++ == v)
+		    break;
+	    }
+	    ii = i/2;
+	    }
+	    break;
+	case 4: {				// 32 bit elements
+	    register Q_INT32 *x = (Q_INT32*)(data() + index);
+	    Q_INT32 v = *((Q_INT32*)d);
+	    for (i=index; i<shd->len; i+=4) {
+		if (*x++ == v)
+		    break;
+	    }
+	    ii = i/4;
+	    }
+	    break;
+	default: {				// any size elements
+	    for (i=index; i<shd->len; i+=sz) {
+		if (memcmp(d, &shd->data[i], sz) == 0)
+		    break;
+	    }
+	    ii = i/sz;
+	    }
+	    break;
+    }
+    return i<shd->len ? (int)ii : -1;
+}
+
+/*!
+  Returns the number of occurrences of \a d in the array, where \a sz is
+  the size of the \a d element.
+
+  This function only compares whole cells, not bytes.
+*/
+
+int Q3GArray::contains(const char *d, uint sz) const
+{
+    register uint i = shd->len;
+    int count = 0;
+    switch (sz) {
+	case 1: {				// 8 bit elements
+	    register char *x = data();
+	    char v = *d;
+	    while (i--) {
+		if (*x++ == v)
+		    count++;
+	    }
+	    }
+	    break;
+	case 2: {				// 16 bit elements
+	    register Q_INT16 *x = (Q_INT16*)data();
+	    Q_INT16 v = *((Q_INT16*)d);
+	    i /= 2;
+	    while (i--) {
+		if (*x++ == v)
+		    count++;
+	    }
+	    }
+	    break;
+	case 4: {				// 32 bit elements
+	    register Q_INT32 *x = (Q_INT32*)data();
+	    Q_INT32 v = *((Q_INT32*)d);
+	    i /= 4;
+	    while (i--) {
+		if (*x++ == v)
+		    count++;
+	    }
+	    }
+	    break;
+	default: {				// any size elements
+	    for (i=0; i<shd->len; i+=sz) {
+		if (memcmp(d, &shd->data[i], sz) == 0)
+		    count++;
+	    }
+	    }
+	    break;
+    }
+    return count;
+}
+
+static int cmp_item_size = 0;
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+#ifdef Q_OS_WINCE
+static int __cdecl cmp_arr(const void *n1, const void *n2)
+#else
+static int cmp_arr(const void *n1, const void *n2)
+#endif
+{
+    return (n1 && n2) ? memcmp(n1, n2, cmp_item_size)
+			: (n1 ? 1 : (n2 ? -1 : 0));
+    // ### Qt 3.0: Add a virtual compareItems() method and call that instead
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+/*!
+  Sorts the first \a sz items of the array.
+*/
+
+void Q3GArray::sort(uint sz)
+{
+    int numItems = size() / sz;
+    if (numItems < 2)
+	return;
+
+#ifndef QT_NO_THREAD
+    QMutexLocker locker(QMutexPool::globalInstanceGet(&cmp_item_size));
+#endif
+
+    cmp_item_size = sz;
+    qsort(shd->data, numItems, sz, cmp_arr);
+}
+
+/*!
+  Binary search; assumes that \a d is a sorted array of size \a sz.
+*/
+
+int Q3GArray::bsearch(const char *d, uint sz) const
+{
+    int numItems = size() / sz;
+    if (!numItems)
+	return -1;
+
+#ifndef QT_NO_THREAD
+    QMutexLocker locker(QMutexPool::globalInstanceGet(&cmp_item_size));
+#endif
+
+    cmp_item_size = sz;
+    char* r = (char*)::bsearch(d, shd->data, numItems, sz, cmp_arr);
+    if (!r)
+	return -1;
+    while((r >= shd->data + sz) && (cmp_arr(r - sz, d) == 0))
+	r -= sz;	// search to first of equal elements; bsearch is undef
+    return (int)((r - shd->data) / sz);
+}
+
+
+/*!
+  \fn char *Q3GArray::at(uint index) const
+
+  Returns a pointer to the byte at offset \a index in the array.
+*/
+
+/*!
+  Expand the array if necessary, and copies (the first part of) its
+  contents from the \a index * \a sz bytes at \a d.
+
+  Returns true if the operation succeeds, false if it runs out of
+  memory.
+
+  \warning This function disregards the reference count mechanism.  If
+  other Q3GArrays reference the same data as this, all will be changed.
+*/
+
+bool Q3GArray::setExpand(uint index, const char *d, uint sz)
+{
+    index *= sz;
+    if (index >= shd->len) {
+	if (!resize(index+sz))		// no memory
+	    return false;
+    }
+    memcpy(data() + index, d, sz);
+    return true;
+}
+
+
+/*!
+  Prints a warning message if at() or [] is given a bad index.
+*/
+
+void Q3GArray::msg_index(uint index)
+{
+#if defined(QT_CHECK_RANGE)
+    qWarning("Q3GArray::at: Absolute index %d out of range", index);
+#else
+    Q_UNUSED(index)
+#endif
+}
+
+
+/*!
+  Returns a new shared array block.
+*/
+
+Q3GArray::array_data * Q3GArray::newData()
+{
+    return new array_data;
+}
+
+
+/*!
+  Deletes the shared array block \a p.
+*/
+
+void Q3GArray::deleteData(array_data *p)
+{
+    delete p;
+}
+
+QT_END_NAMESPACE