/* * Copyright 1999-2002,2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Id: DOMString.cpp 180016 2005-06-04 19:49:30Z jberry $ */ #include #include #include #include #include #include #include "DOM_DOMException.hpp" #include "DOMString.hpp" #ifndef XML_DEBUG #include "DOMStringImpl.hpp" #endif #include #include XERCES_CPP_NAMESPACE_BEGIN //---------------------------------------------- // // Forward decls // //---------------------------------------------- static void reinitDomConverter(); static void reinitDomMutex(); XMLLCPTranscoder* getDomConverter(); // --------------------------------------------------------------------------- // Local static functions // --------------------------------------------------------------------------- // getDOMConverter - get the converter from the system default // codepage to Unicode that is to be used when // a DOMString is constructed from a char *. // static XMLLCPTranscoder* gDomConverter = 0; static XMLRegisterCleanup cleanupDomConverter; int DOMString::gLiveStringDataCount = 0; int DOMString::gTotalStringDataCount = 0; int DOMString::gLiveStringHandleCount = 0; int DOMString::gTotalStringHandleCount = 0; XMLLCPTranscoder* getDomConverter() { if (!gDomConverter) { XMLLCPTranscoder* transcoder = XMLPlatformUtils::fgTransService->makeNewLCPTranscoder(); if (!transcoder) XMLPlatformUtils::panic(PanicHandler::Panic_NoDefTranscoder ); if (XMLPlatformUtils::compareAndSwap((void **)&gDomConverter, transcoder, 0) != 0) delete transcoder; else cleanupDomConverter.registerCleanup(reinitDomConverter); } return gDomConverter; } // // There is one global mutex that is used to synchronize access to the // allocator free list for DOMStringHandles. This function gets that // mutex, and will create it on the first attempt to get it. // static XMLMutex* DOMStringHandleMutex = 0; // Mutex will be deleted by ~DOMStringHandle. static XMLRegisterCleanup cleanupDomMutex; XMLMutex& DOMStringHandle::getMutex() { if (!DOMStringHandleMutex) { XMLMutex* tmpMutex = new XMLMutex(XMLPlatformUtils::fgMemoryManager); if (XMLPlatformUtils::compareAndSwap((void**)&DOMStringHandleMutex, tmpMutex, 0)) { // Someone beat us to it, so let's clean up ours delete tmpMutex; } else cleanupDomMutex.registerCleanup(reinitDomMutex); } return *DOMStringHandleMutex; } //---------------------------------------------- // // DOMStringData // //---------------------------------------------- void DOMStringData::removeRef() { int result = XMLPlatformUtils::atomicDecrement(fRefCount); if (result==0) { fBufferLength = 0xcccc; fRefCount = 0xcccc; XMLPlatformUtils::fgMemoryManager->deallocate(this);//delete [] this; // was allocated with new char[size] ! XMLPlatformUtils::atomicDecrement(DOMString::gLiveStringDataCount); } } void DOMStringData::addRef() { XMLPlatformUtils::atomicIncrement(fRefCount); } DOMStringData *DOMStringData::allocateBuffer(unsigned int length) { unsigned int sizeToAllocate = sizeof(DOMStringData) // buffer will contain an + length*sizeof(XMLCh); // extra elem because of stub // array in DOMStringData struct. DOMStringData *buf = 0; buf = (DOMStringData *) XMLPlatformUtils::fgMemoryManager->allocate ( sizeToAllocate * sizeof(char) );//new char[sizeToAllocate]; XMLPlatformUtils::atomicIncrement(DOMString::gLiveStringDataCount); XMLPlatformUtils::atomicIncrement(DOMString::gTotalStringDataCount); buf->fBufferLength = length; buf->fRefCount = 1; buf->fData[0] = 0; return buf; } //---------------------------------------------------- // // 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. DOMStringHandle *DOMStringHandle::blockListPtr = 0; // Point to the head of the list // of larger blocks in which DOMStringHandles // are allocated. // // Operator new for DOMStringHandles. Called implicitly from the // DOMStringHandle constructor. // void *DOMStringHandle::operator new(size_t sizeToAlloc) { assert(sizeToAlloc == sizeof(DOMStringHandle)); void *retPtr; XMLMutexLock lock(&getMutex()); // Lock the DOMStringHandle mutex for // the duration of this function. if (freeListPtr == 0) { // Uncommon case. The free list of string handles is empty // Allocate a new batch of them, using the system's // operator new to get a chunk of memory. // DOMStringHandle *dsg = (DOMStringHandle*) XMLPlatformUtils::fgMemoryManager->allocate ( allocGroupSize * sizeof(DOMStringHandle) );//::new DOMStringHandle[allocGroupSize]; // Link the block itself into the list of blocks. The purpose of this is to // let us locate and delete the blocks when shutting down. // *(DOMStringHandle **)dsg = blockListPtr; blockListPtr = dsg; // Link all of the new storage for StringHandles into the StringHandle free list int i; // Start with index 1; index 0 is reserved for linking the // larger allocation blocks together. for (i=1; ideallocate(pThisBlock);//delete [] pThisBlock; } blockListPtr = 0; freeListPtr = 0; } } void DOMStringHandle::addRef() { XMLPlatformUtils::atomicIncrement(fRefCount); } void DOMStringHandle::removeRef() { int result = XMLPlatformUtils::atomicDecrement(fRefCount); if (result==0) { fDSData->removeRef(); // delete this; DOMStringHandle* ptr = this; delete ptr; } } DOMStringHandle *DOMStringHandle::createNewStringHandle(unsigned int bufLength) { DOMStringHandle *h = new DOMStringHandle; XMLPlatformUtils::atomicIncrement(DOMString::gTotalStringHandleCount); h -> fLength = 0; h -> fRefCount = 1; h -> fDSData = DOMStringData::allocateBuffer(bufLength); return h; } DOMStringHandle *DOMStringHandle::cloneStringHandle() { DOMStringHandle *h = new DOMStringHandle; h->fLength = fLength; h->fRefCount = 1; h->fDSData = fDSData; h->fDSData->addRef(); return h; } //------------------------------------------------------------ // // DOMString // //------------------------------------------------------------ DOMString::DOMString() { fHandle = 0; } DOMString::DOMString(const DOMString &other) : XMemory(other) { fHandle = other.fHandle; if (fHandle) fHandle->addRef(); } DOMString::DOMString(const XMLCh *data) { fHandle = 0; if (data != 0) { unsigned int dataLength = 0; while (data[dataLength] != 0) ++dataLength; if (dataLength != 0) { fHandle = DOMStringHandle::createNewStringHandle(dataLength+1); fHandle->fLength = dataLength; XMLCh *strData = fHandle->fDSData->fData; unsigned int i; for (i=0; i 0) { fHandle = DOMStringHandle::createNewStringHandle(dataLength+1); fHandle->fLength = dataLength; XMLCh *strData = fHandle->fDSData->fData; unsigned int i; for (i=0; ifDSData->fData; if (!uniConverter->transcode(srcString, strData, srcLen) || (XMLString::stringLen(strData) != srcLen)) { // conversion failed, so try again if (fHandle) fHandle->removeRef(); fHandle = 0; srcLen = uniConverter->calcRequiredSize(srcString); fHandle = DOMStringHandle::createNewStringHandle(srcLen + 1); XMLCh *strData2 = fHandle->fDSData->fData; if (!uniConverter->transcode(srcString, strData2, srcLen)) { // We should throw something here? } } fHandle->fLength = srcLen; } } DOMString::DOMString(int nullValue) { assert(nullValue == 0); fHandle = 0; } 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 = (DOM_NullPtr *arg) { assert(arg == 0); if (fHandle) fHandle->removeRef(); fHandle = 0; return *this; } 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::reserve(unsigned int size) { if (fHandle == 0) { if (size > 0) fHandle = DOMStringHandle::createNewStringHandle(size); } } void DOMString::appendData(const DOMString &other) { if (other.fHandle == 0 || other.fHandle->fLength == 0) return; // If this string is empty and this string does not have an // already allocated buffer sufficient to hold the string being // appended, return a clone of the other string. // if (fHandle == 0 || (fHandle->fLength == 0 && fHandle->fDSData->fBufferLength < other.fHandle->fLength)) { if (fHandle) fHandle->removeRef(); this->fHandle = other.fHandle->cloneStringHandle(); return; } unsigned 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+1); XMLCh *newP = newBuf->fData; XMLCh *oldP = fHandle->fDSData->fData; unsigned int i; for (i=0; ifLength; ++i) newP[i] = oldP[i]; fHandle->fDSData->removeRef(); fHandle->fDSData = newBuf; } // // This string now had enough buffer room to hold the data to // be appended. Go ahead and copy it in. XMLCh *srcP = other.fHandle->fDSData->fData; XMLCh *destP = &fHandle->fDSData->fData[fHandle->fLength]; unsigned int i; for (i=0; ifLength; i++) destP[i] = srcP[i]; fHandle->fLength += other.fHandle->fLength; } void DOMString::appendData(XMLCh ch) { unsigned int newLength = 0; if (fHandle == 0) { fHandle = DOMStringHandle::createNewStringHandle(2); newLength = 1; } else newLength = fHandle->fLength + 1; 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+1); XMLCh *newP = newBuf->fData; XMLCh *oldP = fHandle->fDSData->fData; unsigned int i; for (i=0; ifLength; ++i) newP[i] = oldP[i]; fHandle->fDSData->removeRef(); fHandle->fDSData = newBuf; } XMLCh *destP = &fHandle->fDSData->fData[fHandle->fLength]; destP[0] = ch; fHandle->fLength ++; } // TODO: A custom version could be written more efficiently, avoiding // the creation of the temporary DOMString void DOMString::appendData(const XMLCh* other) { appendData(DOMString(other)); } DOMString& DOMString::operator +=(const DOMString &other) { appendData(other); return *this; } DOMString& DOMString::operator +=(const XMLCh *str) { appendData(str); return *this; } DOMString& DOMString::operator +=(XMLCh ch) { appendData(ch); return *this; } XMLCh DOMString::charAt(unsigned int index) const { XMLCh retCh = 0; if ((fHandle != 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(unsigned int offset, unsigned int delLength) { unsigned int stringLen = this->length(); if (offset > stringLen) throw DOM_DOMException(DOM_DOMException::INDEX_SIZE_ERR, 0); // Cap the value of delLength to avoid trouble with overflows // in the following length computations. if (delLength > stringLen) delLength = stringLen; // If the length of data to be deleted would extend off the end // of the string, cut it back to stop at the end of string. if (offset + delLength >= stringLen) delLength = stringLen - offset; if (delLength == 0) return; unsigned 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+1); XMLCh *newP = newBuf->fData; XMLCh *oldP = fHandle->fDSData->fData; unsigned int i; for (i=0; ifLength = 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. unsigned int i; XMLCh *bufP = fHandle->fDSData->fData; for (i=offset; ifLength = 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; unsigned int i; for (i=0; ifHandle->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; unsigned int len = this->fHandle->fLength; unsigned int i; for (i=0; ifLength != 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(unsigned int offset, const DOMString &src) { unsigned int origStrLength = this->length(); if (offset > origStrLength) throw DOM_DOMException(DOM_DOMException::INDEX_SIZE_ERR, 0); if (fHandle == 0) { *this = src.clone(); return; } if (src.fHandle == 0 || src.fHandle->fLength == 0) return; XMLCh *srcP = src.fHandle->fDSData->fData; unsigned int srcLength = src.fHandle->fLength; unsigned int newLength = fHandle->fLength + srcLength; if (newLength >= fHandle->fDSData->fBufferLength || fHandle->fDSData->fRefCount > 1 || fHandle == src.fHandle ) { // 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+1); XMLCh *newP = newBuf->fData; XMLCh *oldP = fHandle->fDSData->fData; unsigned int i; for (i=0; ifDSData->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=(int)origStrLength-1; i>=(int)offset; i--) destP[i+srcLength] = destP[i]; unsigned int j; for (j=0; jfLength += srcLength; } unsigned int DOMString::length() const { unsigned int len = 0; if (fHandle != 0) len = fHandle->fLength; return len; } void DOMString::print() const { unsigned int len = this->length(); if (len > 0) { // Transcode from Unicode to char * in whatever the system local code page is. char *pc = transcode(XMLPlatformUtils::fgMemoryManager); fputs(pc, stdout); XMLPlatformUtils::fgMemoryManager->deallocate(pc);//delete [] pc; } } void DOMString::println() const { print(); putchar('\n'); } const XMLCh *DOMString::rawBuffer() const { XMLCh *retP = 0; if (fHandle) { retP = fHandle->fDSData->fData; retP[fHandle->fLength]=0; } 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 const XMLCh* srcP = rawBuffer(); // // Find out how many output chars we need and allocate a buffer big enough // for that plus a null. // // The charsNeeded normally is same as fHandle->fLength. To enhance performance, // we start with this estimate, and if overflow, then call calcRequiredSize for actual size unsigned int charsNeeded = fHandle->fLength; char* retP = new char[charsNeeded + 1]; if (!getDomConverter()->transcode(srcP, retP, charsNeeded) || (XMLString::stringLen(retP) != charsNeeded)) { delete [] retP; charsNeeded = getDomConverter()->calcRequiredSize(srcP); retP = new char[charsNeeded + 1]; if (!getDomConverter()->transcode(srcP, retP, charsNeeded)) { // We should throw something here? } } // Cap it off and return it retP[charsNeeded] = 0; return retP; } char *DOMString::transcode(MemoryManager* const manager) const { if (!fHandle || fHandle->fLength == 0) { char* retP = (char*) manager->allocate(sizeof(char));//new char[1]; *retP = 0; return retP; } // We've got some data const XMLCh* srcP = rawBuffer(); // // Find out how many output chars we need and allocate a buffer big enough // for that plus a null. // // The charsNeeded normally is same as fHandle->fLength. To enhance performance, // we start with this estimate, and if overflow, then call calcRequiredSize for actual size unsigned int charsNeeded = fHandle->fLength; char* retP = (char*) manager->allocate((charsNeeded + 1) * sizeof(char));//new char[charsNeeded + 1]; if (!getDomConverter()->transcode(srcP, retP, charsNeeded) || (XMLString::stringLen(retP) != charsNeeded)) { manager->deallocate(retP);//delete [] retP; charsNeeded = getDomConverter()->calcRequiredSize(srcP); retP = (char*) manager->allocate((charsNeeded + 1) * sizeof(char));//new char[charsNeeded + 1]; if (!getDomConverter()->transcode(srcP, retP, charsNeeded)) { // We should throw something here? } } // Cap it off and return it retP[charsNeeded] = 0; return retP; } DOMString DOMString::transcode(const char* str) { return DOMString(str); } bool DOMString::operator < (const DOMString &other) const { return (compareString(other) < 0); } 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. // unsigned int thisLen = length(); unsigned 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; unsigned int i; for (i=0; i otherP[i]) return 1; } return 0; } DOMString DOMString::substringData(unsigned int offset, unsigned int count) const { unsigned int thisLen = length(); if (offset > thisLen) throw DOM_DOMException(DOM_DOMException::INDEX_SIZE_ERR, 0); // Cap count to the string length to eliminate overflow // problems when we get passed bogus values, like -1. if (count > thisLen) count = thisLen; // If the count extends past the end of the string, cut it // back so that the returned string will stop at the end // of the source string. if (offset + count >= thisLen) count = thisLen - offset; if (count == 0) return DOMString(); // If the substring starts at the beginning of the original string // we do not need to copy the data, but can set up a new // string handle with the shorter length. if (offset == 0) { DOMString retString = this->clone(); retString.fHandle->fLength = count; return retString; } // The substring starts somewhere in the interior of the orignal string. // Create a completely new DOMString. No buffer sharing is possible. XMLCh *data = fHandle->fDSData->fData; return DOMString(data+offset, count); } DOMString operator + (const DOMString &lhs, const DOMString &rhs) { DOMString retString = lhs.clone(); retString.appendData(rhs); return retString; } DOMString operator + (const DOMString &lhs, const XMLCh* rhs) { DOMString retString = lhs.clone(); retString.appendData(rhs); return retString; } DOMString operator + (const XMLCh* lhs, const DOMString& rhs) { DOMString retString = DOMString(lhs); retString.appendData(rhs); return retString; } DOMString operator + (const DOMString &lhs, XMLCh rhs) { DOMString retString = lhs.clone(); retString.appendData(rhs); return retString; } DOMString operator + (XMLCh lhs, const DOMString& rhs) { DOMString retString; retString.appendData(lhs); retString.appendData(rhs); return retString; } // ----------------------------------------------------------------------- // Notification that lazy data has been deleted // ----------------------------------------------------------------------- static void reinitDomConverter() { delete gDomConverter; // Delete the local code page converter. gDomConverter = 0; } static void reinitDomMutex() { delete DOMStringHandleMutex; // Delete the synchronization mutex, DOMStringHandleMutex = 0; } XERCES_CPP_NAMESPACE_END