blob: 7eeb490e6916d62925f0dcb2dfe2c9eff35082cb [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache\@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation, and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.ibm.com . For more information
* on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/**
* $Log$
* Revision 1.3 1999/12/03 00:11:22 andyh
* Added DOMString.clone() to node parameters in and out of the DOM,
* where they had been missed.
*
* DOMString::rawBuffer, removed incorrect assumptions about it
* being null terminated.
*
* Revision 1.2 1999/11/30 21:16:25 roddey
* Changes to add the transcode() method to DOMString, which returns a transcoded
* version (to local code page) of the DOM string contents. And I changed all of the
* exception 'throw by pointer' to 'throw by value' style.
*
* Revision 1.1.1.1 1999/11/09 01:08:47 twl
* Initial checkin
*
* Revision 1.3 1999/11/08 20:44:12 rahul
* Swat for adding in Product name and CVS comment log variable.
*
*/
#include <util/PlatformUtils.hpp>
#include <util/RuntimeException.hpp>
#include <util/StdOut.hpp>
#include <util/TransService.hpp>
#include "DOMString.hpp"
#include <assert.h>
#include <string.h>
//----------------------------------------------
//
// DOMStringData
//
//----------------------------------------------
class DOMStringData
{
public:
int fBufferLength;
int fRefCount;
XMLCh fData[1];
static DOMStringData *allocateBuffer(int length);
inline void addRef();
inline void removeRef();
};
void DOMStringData::removeRef()
{
int result = XMLPlatformUtils::atomicDecrement(fRefCount);
if (result==0)
{
fBufferLength = 0xcccc;
fRefCount = 0xcccc;
delete this;
XMLPlatformUtils::atomicDecrement(DOMString::gLiveStringDataCount);
};
};
void DOMStringData::addRef()
{
XMLPlatformUtils::atomicIncrement(fRefCount);
};
DOMStringData *DOMStringData::allocateBuffer(int length)
{
int sizeToAllocate = sizeof(DOMStringData) // buffer will contain an
+ length*sizeof(XMLCh); // extra elem because of stub
// array in DOMStringData struct.
DOMStringData *buf = (DOMStringData *) new char[sizeToAllocate];
XMLPlatformUtils::atomicIncrement(DOMString::gLiveStringDataCount);
XMLPlatformUtils::atomicIncrement(DOMString::gTotalStringDataCount);
buf->fBufferLength = length;
buf->fRefCount = 1;
buf->fData[0] = 0;
return buf;
}
//----------------------------------------------------
//
// DOMStringHandle
//
//-----------------------------------------------------
class DOMStringHandle
{
public:
int fLength;
int fRefCount;
DOMStringData *fDSData;
void *operator new( size_t sizeToAlloc);
void operator delete( void *pvMem );
private:
static void *freeListPtr;
public:
static DOMStringHandle *createNewStringHandle(int bufLength);
DOMStringHandle *cloneStringHandle();
inline void addRef();
inline void removeRef();
~DOMStringHandle() {};
private:
inline DOMStringHandle() {};
};
//
// Specialized new and delete operators for DOMStringHandles.
// These are used, rather than the standard system operator new,
// for improved performance.
//
// We allocate largish blocks of memory using the standard system
// new function, and sub-allocate string handles from that block.
// Un-allocated string handles within the allocated blocks are kept
// in a singly linked list, making allocation and deallocation
// very quick in the common case.
//
// String handle allocation is thread safe. A multi-threaded
// application may have threads concurrently accessing multiple
// DOM documents; since all string handles come from the same pool,
// this allocator must be safe. The compare and exchange function,
// which is available as a single instruction in most processor
// architectures, and typically surfaced as an OS function,
// is used to safely update the string handle free list.
//
void *DOMStringHandle::freeListPtr = 0; // Point to the head of the
// free list of un-allocated
// string handles, or 0 if there
// are no free string handles.
static const int allocGroupSize = 1024; // Number of string handles to allocate
// as a chunk from the system's
// memory allocator.
void *DOMStringHandle::operator new(size_t sizeToAlloc)
{
assert(sizeToAlloc == sizeof(DOMStringHandle));
void *retPtr;
void *oldFreeListPtr;
do
{
retPtr = freeListPtr; // In the common case, freeListPtr points
// to an available string handle, and
// this will be the block we return.
if (retPtr == 0)
{
// Uncommon case. The free list of string handles is empty
// and we must allocate a new batch of them, using
// the system's operator new.
//
// Link all but one of these new StringHandles into our free list by
// deleting them with DOMStringHandle::operator delete.
// Unconditionally return the one that we didn't put in the free list.
//
// There is a remote chance that two threads could see the free list
// as empty at about the same time and both fall into this code.
// No great harm will result - the free list will have twice the
// usual number of new free handles added to it. It's not worth
// any added code complexity to prevent it from happening.
DOMStringHandle *dsg =
::new DOMStringHandle[allocGroupSize];
int i;
for (i=1; i<allocGroupSize-1; i++)
delete &dsg[i];
return &dsg[0];
}
// Thread-safe (atomic) update of freeListPtr.
oldFreeListPtr = XMLPlatformUtils::compareAndSwap(&freeListPtr, *(void **)retPtr, retPtr);
}
// This loop will normally exit the first time through. It will only repeat if,
// in a multi-threaded environment, some second thread updated the free list ptr
// in the interval between when we looked at it (retPtr = freeListPtr;) and when
// we attempted to update it ourselves with the compareAndSwap().
while (oldFreeListPtr != retPtr);
return retPtr;
};
void DOMStringHandle::operator delete(void *pMem)
{
void *initialFreeListPtr;
void *oldFreeListPtr;
do
{
*(void **)pMem = initialFreeListPtr = freeListPtr;
oldFreeListPtr = XMLPlatformUtils::compareAndSwap(&freeListPtr, pMem, initialFreeListPtr);
}
while (oldFreeListPtr != initialFreeListPtr);
};
void DOMStringHandle::addRef()
{
XMLPlatformUtils::atomicIncrement(fRefCount);
};
void DOMStringHandle::removeRef()
{
int result = XMLPlatformUtils::atomicDecrement(fRefCount);
if (result==0)
{
fDSData->removeRef();
delete this;
XMLPlatformUtils::atomicDecrement(DOMString::gLiveStringHandleCount);
};
};
DOMStringHandle *DOMStringHandle::createNewStringHandle(int bufLength)
{
DOMStringHandle *h = new DOMStringHandle;
XMLPlatformUtils::atomicIncrement(DOMString::gLiveStringHandleCount);
XMLPlatformUtils::atomicIncrement(DOMString::gTotalStringHandleCount);
h -> fLength = 0;
h -> fRefCount = 1;
h -> fDSData = DOMStringData::allocateBuffer(bufLength);
return h;
};
DOMStringHandle *DOMStringHandle::cloneStringHandle()
{
DOMStringHandle *h = new DOMStringHandle;
XMLPlatformUtils::atomicIncrement(DOMString::gLiveStringHandleCount);
h->fLength = fLength;
h->fRefCount = 1;
h->fDSData = fDSData;
h->fDSData->addRef();
return h;
}
//------------------------------------------------------------
//
// DOMString
//
//------------------------------------------------------------
int DOMString::gLiveStringDataCount = 0;
int DOMString::gTotalStringDataCount = 0;
int DOMString::gLiveStringHandleCount = 0;
int DOMString::gTotalStringHandleCount = 0;
DOMString::DOMString()
{
fHandle = 0;
};
DOMString::DOMString(const DOMString &other)
{
fHandle = other.fHandle;
if (fHandle)
fHandle->addRef();
};
DOMString::DOMString(const XMLCh *data)
{
fHandle = 0;
if (data != 0)
{
int dataLength = 0;
while (data[dataLength] != 0)
++dataLength;
if (dataLength != 0)
{
fHandle = DOMStringHandle::createNewStringHandle(dataLength+1);
fHandle->fLength = dataLength;
XMLCh *strData = fHandle->fDSData->fData;
int i;
for (i=0; i<dataLength ; ++i)
strData[i] = data[i];
strData[dataLength] = 0;
}
}
}
DOMString::DOMString(const XMLCh *data, int dataLength)
{
fHandle = 0;
if (data != 0)
{
if (dataLength > 0)
{
fHandle = DOMStringHandle::createNewStringHandle(dataLength+1);
fHandle->fLength = dataLength;
XMLCh *strData = fHandle->fDSData->fData;
int i;
for (i=0; i<dataLength ; ++i)
strData[i] = data[i];
strData[dataLength] = 0;
}
}
}
// getDOMConverter - get the converter from the system default
// codepage to Unicode that is to be used when
// a DOMString is constructed from a char *.
//
XMLTranscoder* getDomConverter()
{
static XMLTranscoder* gDomConverter = 0;
if (!gDomConverter)
{
XMLTranscoder* transcoder =
XMLPlatformUtils::fgTransService->makeNewDefTranscoder();
if (!transcoder)
XMLPlatformUtils::panic(XMLPlatformUtils::Panic_NoDefTranscoder
);
if (XMLPlatformUtils::compareAndSwap((void **)&gDomConverter,
transcoder, 0) != 0)
delete gDomConverter;
}
return gDomConverter;
};
//
// Create a DOMString from a char * string in the default code page
// of the system on which we are executing.
//
//
DOMString::DOMString(const char *srcString)
{
fHandle = 0;
if (srcString != 0)
{
XMLTranscoder* uniConverter = getDomConverter();
unsigned int len = 0;
unsigned int srcLen = strlen(srcString);
if (srcLen == 0)
return;
const unsigned int charsNeeded =
uniConverter->calcRequiredSize(srcString);
fHandle = DOMStringHandle::createNewStringHandle(charsNeeded + 1);
fHandle->fLength = charsNeeded;
XMLCh *strData = fHandle->fDSData->fData;
if (!uniConverter->transcode(srcString, strData, charsNeeded))
{
// <TBD> We should throw something here?
}
}
};
DOMString::DOMString(int initialBufferSize)
{
fHandle = 0;
if (initialBufferSize > 0)
fHandle = DOMStringHandle::createNewStringHandle(initialBufferSize);
};
DOMString::~DOMString()
{
if (fHandle)
fHandle->removeRef();
fHandle = 0;
};
DOMString & DOMString::operator =(const DOMString &other)
{
if (this == &other)
return *this;
if (fHandle)
fHandle->removeRef();
fHandle = other.fHandle;
if (fHandle)
fHandle->addRef();
return *this;
};
DOMString DOMString::operator + (const DOMString &other)
{
DOMString retString = this->clone();
retString.appendData(other);
return retString;
};
bool DOMString::operator ==(const DOMString &other) const
{
return this->fHandle == other.fHandle;
};
bool DOMString::operator !=(const DOMString &other) const
{
return this->fHandle != other.fHandle;
};
bool DOMString::operator == (const DOM_NullPtr *p) const
{
return (fHandle == 0);
};
bool DOMString::operator != (const DOM_NullPtr *p) const
{
return (fHandle != 0);
};
void DOMString::appendData(const DOMString &other)
{
int i;
if (other.fHandle == 0 || other.fHandle->fLength == 0)
return;
if (fHandle == 0 || fHandle->fLength == 0)
{
if (fHandle) fHandle->removeRef();
this->fHandle = other.fHandle->cloneStringHandle();
return;
}
int newLength = fHandle->fLength + other.fHandle->fLength;
if (newLength >= fHandle->fDSData->fBufferLength ||
fHandle->fDSData->fRefCount > 1)
{
// We can't stick the data to be added onto the end of the
// existing string, either because there is not space in
// the buffer, or because the buffer is being shared with
// some other string. So, make a new buffer.
DOMStringData *newBuf = DOMStringData::allocateBuffer(newLength);
XMLCh *newP = newBuf->fData;
XMLCh *oldP = fHandle->fDSData->fData;
for (i=0; i<fHandle->fLength; ++i)
newP[i] = oldP[i];
fHandle->fDSData->removeRef();
fHandle->fDSData = newBuf;
}
XMLCh *srcP = other.fHandle->fDSData->fData;
XMLCh *destP = &fHandle->fDSData->fData[fHandle->fLength];
for (i=0; i<=other.fHandle->fLength; i++)
destP[i] = srcP[i];
fHandle->fLength += other.fHandle->fLength;
}
XMLCh DOMString::charAt(int index) const
{
XMLCh retCh = 0;
if (fHandle != 0 && index >= 0 && index < fHandle->fLength)
retCh = fHandle->fDSData->fData[index];
return retCh;
};
DOMString DOMString::clone() const
{
DOMString retString;
if (fHandle != 0)
retString.fHandle = this->fHandle->cloneStringHandle();
return retString;
};
void DOMString::deleteData(int offset, int delLength)
{
assert(delLength >= 0);
int stringLen = this->length();
assert(offset < stringLen);
if (delLength == 0)
return;
if (offset + delLength >= stringLen)
delLength = stringLen - offset;
int newStringLength = stringLen - delLength;
if (fHandle->fDSData->fRefCount > 1 && offset+delLength < stringLen)
{
// The deletion is of a range in the middle of the string
// and there's another string handle using the buffer so
// we need to make a new buffer before moving characters
// around.
DOMStringData *newBuf = DOMStringData::allocateBuffer(newStringLength);
XMLCh *newP = newBuf->fData;
XMLCh *oldP = fHandle->fDSData->fData;
int i;
for (i=0; i<offset; i++)
newP[i] = oldP[i];
for (i=offset; i<newStringLength; i++)
newP[i] = oldP[i+delLength];
fHandle->fLength = newStringLength;
fHandle->fDSData->removeRef();
fHandle->fDSData = newBuf;
}
else if (offset+delLength < stringLen)
{
// The deletion is of a range in the middle of the string,
// but no other string is sharing the buffer, so we can
// just delete in place.
int i;
XMLCh *bufP = fHandle->fDSData->fData;
for (i=offset; i<newStringLength; i++)
bufP[i] = bufP[i+delLength];
fHandle->fLength = newStringLength;
}
else
{
// The deletion continues to the end of the string.
// Simply reset the length. We don't need to worry
// about other strings sharing the buffer because
// no characters are moved.
fHandle->fLength = newStringLength;
}
};
bool DOMString::equals(const DOMString &other) const
{
bool retVal = true;
if (this->fHandle != 0 && other.fHandle != 0)
{
if (this->fHandle->fLength != other.fHandle->fLength)
{
retVal = false;
}
else
{
XMLCh *thisP = this->fHandle->fDSData->fData;
XMLCh *otherP = other.fHandle->fDSData->fData;
int i;
for (i=0; i<this->fHandle->fLength; i++)
{
if (thisP[i] != otherP[i])
{
retVal = false;
break;
}
}
}
}
else
{
// At this point, one or more of the fHandle
// pointers is known to be zero.
if (fHandle && fHandle->fLength != 0 ||
other.fHandle && other.fHandle->fLength != 0)
retVal = false;
}
return retVal;
};
bool DOMString::equals(const XMLCh *other) const
{
if (this->fHandle != 0 && other != 0)
{
// Both strings have non-null data pointers, so
// we can go ahead and actually compare them.
XMLCh *thisP = this->fHandle->fDSData->fData;
int len = this->fHandle->fLength;
int i;
for (i=0; i<len; i++)
{
if (other[i] == 0) // "other" is null terminated.
return false; // (If there were no chance of a DOM
// string having a 0 char in the middle of
// it, this test could be omitted.)
if (thisP[i] != other[i])
return false;
}
if (other[len] != 0) // This test for the end of the other
return false; // string can't be done without first
// checking that we haven't walked off the
// end. (It has actually happened - off end
// of string, page, and valid memory.)
return true;
}
// At this point, we know that at least one of the strings had a null
// data pointer.
if (fHandle && fHandle->fLength != 0)
return false;
if (other && *other != 0)
return false;
return true; // Both strings are empty. DOMString treats zero-length
// and a null data pointer as equivalent.
};
void DOMString::insertData(int offset, const DOMString &src)
{
assert(offset>=0);
int origStrLength = this->length();
assert(offset<=origStrLength);
if (fHandle == 0)
{
*this = src.clone();
return;
}
if (src.fHandle == 0 ||src.fHandle->fLength == 0)
return;
XMLCh *srcP = src.fHandle->fDSData->fData;
int srcLength = src.fHandle->fLength;
int newLength = fHandle->fLength + srcLength;
if (newLength >= fHandle->fDSData->fBufferLength ||
fHandle->fDSData->fRefCount > 1)
{
// We can't stick the data to be added into the
// existing string, either because there is not space in
// the buffer, or because the buffer is being shared with
// some other string. So, make a new buffer.
DOMStringData *newBuf = DOMStringData::allocateBuffer(newLength);
XMLCh *newP = newBuf->fData;
XMLCh *oldP = fHandle->fDSData->fData;
int i;
for (i=0; i<offset; ++i)
newP[i] = oldP[i];
for (i=0; i<srcLength; i++)
newP[i+offset] = srcP[i];
for (i=offset; i<origStrLength; i++)
newP[i+srcLength] = oldP[i];
fHandle->fDSData->removeRef();
fHandle->fDSData = newBuf;
}
else
{
// There is room in the already-existing buffer to hold
// the data to be inserted. Insert it.
//
XMLCh *destP = fHandle->fDSData->fData;
int i;
for (i=origStrLength-1; i>=offset; i--)
destP[i+srcLength] = destP[i];
for (i=0; i<srcLength; i++)
destP[i+offset] = srcP[i];
};
fHandle->fLength += srcLength;
}
int DOMString::length() const
{
int len = 0;
if (fHandle != 0)
len = fHandle->fLength;
return len;
};
void DOMString::print() const
{
int len = this->length();
if (len > 0)
{
XMLCh *p = fHandle->fDSData->fData;
XMLStdOut out;
XMLCh* buffer = new XMLCh[len+1];
int i;
for (i=0; i<len; i++)
buffer[i] = p[i];
buffer[len] = 0;
out << buffer;
delete [] buffer;
};
};
void DOMString::println() const
{
XMLStdOut out;
print();
out << EndLn;
};
XMLCh *DOMString::rawBuffer() const
{
XMLCh *retP = 0;
if (fHandle)
{
retP = fHandle->fDSData->fData;
}
return retP;
};
char *DOMString::transcode() const
{
if (!fHandle || fHandle->fLength == 0)
{
char* retP = new char[1];
*retP = 0;
return retP;
}
// We've got some data
// DOMStrings are not always null terminated, so we may need to
// copy to another buffer first in order to null terminate it for
// use as input to the transcoding routines..
//
XMLCh* DOMStrData = fHandle->fDSData->fData;
const int localBufLen = 1000;
XMLCh localBuf[localBufLen];
XMLCh *allocatedBuf = 0;
XMLCh *srcP;
if (DOMStrData[fHandle->fLength] == 0)
{
// The data in the DOMString itself happens to be null terminated.
// Just use it in place.
srcP = DOMStrData;
}
else if (fHandle->fLength < localBufLen-1)
{
// The data is not null terminated, but does fit in the
// local buffer (fast allocation). Copy it over, and add
// the null termination,
memcpy(localBuf, DOMStrData, fHandle->fLength * sizeof(XMLCh));
srcP = localBuf;
srcP[fHandle->fLength] = 0;
}
else
{
// The data is too big for the local buffer. Heap allocate one.
allocatedBuf = srcP = new XMLCh[fHandle->fLength + 1];
memcpy(allocatedBuf, DOMStrData, fHandle->fLength * sizeof(XMLCh));
srcP[fHandle->fLength] = 0;
}
//
// Find out how many output chars we need and allocate a buffer big enough
// for that plus a null.
//
const unsigned int charsNeeded = getDomConverter()->calcRequiredSize(srcP);
char* retP = new char[charsNeeded + 1];
if (!getDomConverter()->transcode(srcP, retP, charsNeeded))
{
// <TBD> We should throw something here?
}
delete [] allocatedBuf; // which will be null if we didn't allocate one.
// Cap it off and return it
retP[charsNeeded] = 0;
return retP;
}
int DOMString::compareString(const DOMString &other) const
{
// Note: this strcmp does not match the semantics
// of the standard C strcmp. All it needs to do is
// define some less than - equals - greater than ordering
// of strings. How doesn't matter.
//
int thisLen = length();
int otherLen = other.length();
if (thisLen < otherLen)
return -1;
if (thisLen > otherLen)
return 1;
if (thisLen == 0)
return 0;
XMLCh *thisP = this->fHandle->fDSData->fData;
XMLCh *otherP = other.fHandle->fDSData->fData;
int i;
for (i=0; i<thisLen; i++)
{
if (thisP[i] < otherP[i])
return -1;
else if (thisP[i] > otherP[i])
return 1;
};
return 0;
};
DOMString DOMString::substringData(int offset, int count) const
{
DOMString retString;
if (count > 0)
{
int thisLen = length();
assert(offset>=0 && count>=0);
if (offset+count > thisLen)
{
assert (offset < thisLen);
count = thisLen - offset;
}
XMLCh *data = fHandle->fDSData->fData;
retString = DOMString(data+offset, count);
}
return retString;
};