| /* |
| * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| * EXPRESS 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 APPLE COMPUTER, INC. OR |
| * 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. |
| */ |
| |
| #include "config.h" |
| #include "DeprecatedString.h" |
| |
| #include "CString.h" |
| #include "FloatConversion.h" |
| #include "Logging.h" |
| #include "PlatformString.h" |
| #include "RegularExpression.h" |
| #include "TextEncoding.h" |
| #include <kjs/dtoa.h> |
| #include <kjs/identifier.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <wtf/Platform.h> |
| #include <wtf/StringExtras.h> |
| |
| #if PLATFORM(WIN_OS) |
| #include <windows.h> |
| #endif |
| |
| #if PLATFORM(QT) |
| #include <QString> |
| #endif |
| |
| using namespace std; |
| using namespace KJS; |
| |
| namespace WebCore { |
| |
| COMPILE_ASSERT(sizeof(DeprecatedChar) == 2, deprecated_char_is_2_bytes) |
| |
| #define CHECK_FOR_HANDLE_LEAKS 0 |
| |
| #if PLATFORM(SYMBIAN) |
| #undef CHECK_FOR_HANDLE_LEAKS |
| // symbian:fixme need page aligned allocations as Symbian platform does not have support for valloc |
| #define CHECK_FOR_HANDLE_LEAKS 1 |
| #endif |
| |
| #define ALLOC_QCHAR_GOOD_SIZE(X) (X) |
| #define ALLOC_CHAR_GOOD_SIZE(X) (X) |
| |
| #define ALLOC_CHAR(N) (char*)fastMalloc(N) |
| #define REALLOC_CHAR(P, N) (char *)fastRealloc(P, N) |
| #define DELETE_CHAR(P) fastFree(P) |
| |
| #define WEBCORE_ALLOCATE_CHARACTERS(N) (DeprecatedChar*)fastMalloc(sizeof(DeprecatedChar)*(N)) |
| #define WEBCORE_REALLOCATE_CHARACTERS(P, N) (DeprecatedChar *)fastRealloc(P, sizeof(DeprecatedChar)*(N)) |
| #define DELETE_QCHAR(P) fastFree(P) |
| |
| #ifndef CHECK_FOR_HANDLE_LEAKS |
| struct HandleNode; |
| struct HandlePageNode; |
| |
| static HandleNode *allocateNode(HandlePageNode *pageNode); |
| static HandlePageNode *allocatePageNode(); |
| |
| static HandlePageNode *usedNodeAllocationPages = 0; |
| static HandlePageNode *freeNodeAllocationPages = 0; |
| |
| static inline void initializeHandleNodes() |
| { |
| if (freeNodeAllocationPages == 0) |
| freeNodeAllocationPages = allocatePageNode(); |
| } |
| #endif |
| |
| static inline DeprecatedStringData **allocateHandle() |
| { |
| #ifdef CHECK_FOR_HANDLE_LEAKS |
| return static_cast<DeprecatedStringData **>(fastMalloc(sizeof(DeprecatedStringData *))); |
| #else |
| |
| initializeHandleNodes(); |
| |
| return reinterpret_cast<DeprecatedStringData **>(allocateNode(freeNodeAllocationPages)); |
| #endif |
| } |
| |
| static void freeHandle(DeprecatedStringData **); |
| |
| #define IS_ASCII_QCHAR(c) ((c).unicode() > 0 && (c).unicode() <= 0xff) |
| |
| static const int caseDelta = ('a' - 'A'); |
| |
| const char * const DeprecatedString::null = 0; |
| |
| DeprecatedStringData *DeprecatedString::shared_null = 0; |
| DeprecatedStringData **DeprecatedString::shared_null_handle = 0; |
| |
| // ------------------------------------------------------------------------- |
| // Utility functions |
| // ------------------------------------------------------------------------- |
| |
| static inline int ucstrcmp( const DeprecatedString &as, const DeprecatedString &bs ) |
| { |
| const DeprecatedChar *a = as.unicode(); |
| const DeprecatedChar *b = bs.unicode(); |
| if ( a == b ) |
| return 0; |
| if ( a == 0 ) |
| return 1; |
| if ( b == 0 ) |
| return -1; |
| int l = min(as.length(), bs.length()); |
| while ( l-- && *a == *b ) |
| a++,b++; |
| if ( l == -1 ) |
| return ( as.length() - bs.length() ); |
| return a->unicode() - b->unicode(); |
| } |
| |
| |
| static bool equal(const DeprecatedChar *a, const char *b, int l) |
| { |
| ASSERT(l >= 0); |
| while (l--) { |
| if (*a != *b) |
| return false; |
| a++; b++; |
| } |
| return true; |
| } |
| |
| // Not a "true" case insensitive compare; only insensitive for plain ASCII. |
| |
| static bool equalCaseInsensitive(const char *a, const char *b, int l) |
| { |
| ASSERT(l >= 0); |
| while (l--) { |
| if (tolower(*a) != tolower(*b)) |
| return false; |
| a++; b++; |
| } |
| return true; |
| } |
| |
| static bool equalCaseInsensitive(const DeprecatedChar *a, const char *b, int l) |
| { |
| ASSERT(l >= 0); |
| while (l--) { |
| if (tolower(a->unicode()) != tolower(*b)) |
| return false; |
| a++; b++; |
| } |
| return true; |
| } |
| |
| static bool equalCaseInsensitive(const DeprecatedChar *a, const DeprecatedChar *b, int l) |
| { |
| ASSERT(l >= 0); |
| while (l--) { |
| if (tolower(a->unicode()) != tolower(b->unicode())) |
| return false; |
| a++; b++; |
| } |
| return true; |
| } |
| |
| static inline bool equalCaseInsensitive(char c1, char c2) |
| { |
| return tolower(c1) == tolower(c2); |
| } |
| |
| static inline bool equalCaseInsensitive(DeprecatedChar c1, char c2) |
| { |
| return tolower(c1.unicode()) == tolower(static_cast<unsigned char>(c2)); |
| } |
| |
| static bool isCharacterAllowedInBase(DeprecatedChar c, int base) |
| { |
| int uc = c.unicode(); |
| if (isdigit(uc)) |
| return uc - '0' < base; |
| if (isalpha(uc)) { |
| if (base > 36) |
| base = 36; |
| return (uc >= 'a' && uc < 'a' + base - 10) |
| || (uc >= 'A' && uc < 'A' + base - 10); |
| } |
| return false; |
| } |
| |
| // ------------------------------------------------------------------------- |
| // DeprecatedStringData |
| // ------------------------------------------------------------------------- |
| |
| // FIXME, make constructor explicity take a 'copy' flag. |
| // This can be used to hand off ownership of allocated data when detaching and |
| // deleting QStrings. |
| |
| DeprecatedStringData::DeprecatedStringData() : |
| refCount(1), _length(0), _unicode(0), _ascii(0), _maxUnicode(WEBCORE_DS_INTERNAL_BUFFER_UCHARS), _isUnicodeValid(0), _isHeapAllocated(0), _maxAscii(WEBCORE_DS_INTERNAL_BUFFER_CHARS), _isAsciiValid(1) |
| { |
| _ascii = _internalBuffer; |
| _internalBuffer[0] = 0; |
| } |
| |
| void DeprecatedStringData::initialize() |
| { |
| refCount = 1; |
| _length = 0; |
| _unicode = 0; |
| _ascii = _internalBuffer; |
| _maxUnicode = WEBCORE_DS_INTERNAL_BUFFER_UCHARS; |
| _isUnicodeValid = 0; |
| _maxAscii = WEBCORE_DS_INTERNAL_BUFFER_CHARS; |
| _isAsciiValid = 1; |
| _internalBuffer[0] = 0; |
| _isHeapAllocated = 0; |
| } |
| |
| // Don't copy data. |
| DeprecatedStringData::DeprecatedStringData(DeprecatedChar *u, unsigned l, unsigned m) : |
| refCount(1), _length(l), _unicode(u), _ascii(0), _maxUnicode(m), _isUnicodeValid(1), _isHeapAllocated(0), _maxAscii(WEBCORE_DS_INTERNAL_BUFFER_CHARS), _isAsciiValid(0) |
| { |
| ASSERT(m >= l); |
| } |
| |
| // Don't copy data. |
| void DeprecatedStringData::initialize(DeprecatedChar *u, unsigned l, unsigned m) |
| { |
| ASSERT(m >= l); |
| refCount = 1; |
| _length = l; |
| _unicode = u; |
| _ascii = 0; |
| _maxUnicode = m; |
| _isUnicodeValid = 1; |
| _maxAscii = 0; |
| _isAsciiValid = 0; |
| _isHeapAllocated = 0; |
| } |
| |
| // Copy data |
| DeprecatedStringData::DeprecatedStringData(const DeprecatedChar *u, unsigned l) |
| { |
| initialize (u, l); |
| } |
| |
| // Copy data |
| void DeprecatedStringData::initialize(const DeprecatedChar *u, unsigned l) |
| { |
| refCount = 1; |
| _length = l; |
| _ascii = 0; |
| _isUnicodeValid = 1; |
| _maxAscii = 0; |
| _isAsciiValid = 0; |
| _isHeapAllocated = 0; |
| |
| if (l > WEBCORE_DS_INTERNAL_BUFFER_UCHARS) { |
| _maxUnicode = ALLOC_QCHAR_GOOD_SIZE(l); |
| _unicode = WEBCORE_ALLOCATE_CHARACTERS(_maxUnicode); |
| memcpy(_unicode, u, l*sizeof(DeprecatedChar)); |
| } else { |
| _maxUnicode = WEBCORE_DS_INTERNAL_BUFFER_UCHARS; |
| _unicode = (DeprecatedChar *)_internalBuffer; |
| if (l) |
| memcpy(_internalBuffer, u, l*sizeof(DeprecatedChar)); |
| } |
| } |
| |
| |
| // Copy data |
| DeprecatedStringData::DeprecatedStringData(const char *a, unsigned l) |
| { |
| initialize(a, l); |
| } |
| |
| |
| // Copy data |
| void DeprecatedStringData::initialize(const char *a, unsigned l) |
| { |
| refCount = 1; |
| _length = l; |
| _unicode = 0; |
| _isUnicodeValid = 0; |
| _maxUnicode = 0; |
| _isAsciiValid = 1; |
| _isHeapAllocated = 0; |
| |
| if (l > WEBCORE_DS_INTERNAL_BUFFER_CHARS) { |
| _maxAscii = ALLOC_CHAR_GOOD_SIZE(l+1); |
| _ascii = ALLOC_CHAR(_maxAscii); |
| if (a) |
| memcpy(_ascii, a, l); |
| _ascii[l] = 0; |
| } else { |
| _maxAscii = WEBCORE_DS_INTERNAL_BUFFER_CHARS; |
| _ascii = _internalBuffer; |
| if (a) |
| memcpy(_internalBuffer, a, l); |
| _internalBuffer[l] = 0; |
| } |
| } |
| |
| DeprecatedStringData* DeprecatedStringData::createAndAdopt(DeprecatedStringData &o) |
| { |
| DeprecatedStringData* data = new DeprecatedStringData(); |
| data->adopt(o); |
| return data; |
| } |
| |
| void DeprecatedStringData::adopt(DeprecatedStringData& o) |
| { |
| ASSERT(refCount == 1); |
| _length = o._length; |
| _unicode = o._unicode; |
| _ascii = o._ascii; |
| _maxUnicode = o._maxUnicode; |
| _isUnicodeValid = o._isUnicodeValid; |
| _isHeapAllocated = 0; |
| _maxAscii = o._maxAscii; |
| _isAsciiValid = o._isAsciiValid; |
| |
| // Handle the case where either the Unicode or 8-bit pointer was |
| // pointing to the internal buffer. We need to point at the |
| // internal buffer in the new object, and copy the characters. |
| if (_unicode == reinterpret_cast<DeprecatedChar *>(o._internalBuffer)) { |
| if (_isUnicodeValid) { |
| ASSERT(!_isAsciiValid || _ascii != o._internalBuffer); |
| ASSERT(_length <= WEBCORE_DS_INTERNAL_BUFFER_UCHARS); |
| memcpy(_internalBuffer, o._internalBuffer, _length * sizeof(DeprecatedChar)); |
| _unicode = reinterpret_cast<DeprecatedChar *>(_internalBuffer); |
| } else { |
| _unicode = 0; |
| } |
| } |
| if (_ascii == o._internalBuffer) { |
| if (_isAsciiValid) { |
| ASSERT(_length <= WEBCORE_DS_INTERNAL_BUFFER_CHARS); |
| memcpy(_internalBuffer, o._internalBuffer, _length); |
| _internalBuffer[_length] = 0; |
| _ascii = _internalBuffer; |
| } else { |
| _ascii = 0; |
| } |
| } |
| |
| // Clean up the other DeprecatedStringData just enough so that it can be destroyed |
| // cleanly. It's not in a good enough state to use, but that's OK. It just |
| // needs to be in a state where ~DeprecatedStringData won't do anything harmful, |
| // and setting these to 0 will do that (preventing any double-free problems). |
| o._unicode = 0; |
| o._ascii = 0; |
| } |
| |
| DeprecatedStringData *DeprecatedString::makeSharedNull() |
| { |
| if (!shared_null) { |
| shared_null = new DeprecatedStringData; |
| shared_null->ref(); |
| shared_null->_maxAscii = 0; |
| shared_null->_maxUnicode = 0; |
| shared_null->_unicode = (DeprecatedChar *)&shared_null->_internalBuffer[0]; |
| shared_null->_isUnicodeValid = 1; |
| } |
| return shared_null; |
| } |
| |
| DeprecatedStringData **DeprecatedString::makeSharedNullHandle() |
| { |
| if (!shared_null_handle) { |
| shared_null_handle = allocateHandle(); |
| *shared_null_handle = makeSharedNull(); |
| } |
| return shared_null_handle; |
| } |
| |
| DeprecatedStringData::~DeprecatedStringData() |
| { |
| ASSERT(refCount == 0); |
| if (_unicode && !isUnicodeInternal()) |
| DELETE_QCHAR(_unicode); |
| if (_ascii && !isAsciiInternal()) |
| DELETE_CHAR(_ascii); |
| } |
| |
| void DeprecatedStringData::increaseAsciiSize(unsigned size) |
| { |
| ASSERT(this != DeprecatedString::shared_null); |
| |
| unsigned newSize = (unsigned)ALLOC_CHAR_GOOD_SIZE((size * 3 + 1) / 2); |
| |
| if (!_isAsciiValid) |
| makeAscii(); |
| ASSERT(_isAsciiValid); |
| |
| if (isAsciiInternal()) { |
| char *newAscii = ALLOC_CHAR(newSize); |
| if (_length) |
| memcpy(newAscii, _ascii, _length); |
| _ascii = newAscii; |
| } else { |
| _ascii = REALLOC_CHAR(_ascii, newSize); |
| } |
| |
| _maxAscii = newSize; |
| _isAsciiValid = 1; |
| _isUnicodeValid = 0; |
| } |
| |
| |
| void DeprecatedStringData::increaseUnicodeSize(unsigned size) |
| { |
| ASSERT(size > _length); |
| ASSERT(this != DeprecatedString::shared_null); |
| |
| unsigned newSize = (unsigned)ALLOC_QCHAR_GOOD_SIZE((size * 3 + 1) / 2); |
| |
| if (!_isUnicodeValid) |
| makeUnicode(); |
| ASSERT(_isUnicodeValid); |
| |
| if (isUnicodeInternal()) { |
| DeprecatedChar *newUni = WEBCORE_ALLOCATE_CHARACTERS(newSize); |
| if (_length) |
| memcpy(newUni, _unicode, _length*sizeof(DeprecatedChar)); |
| _unicode = newUni; |
| } else { |
| _unicode = WEBCORE_REALLOCATE_CHARACTERS(_unicode, newSize); |
| } |
| |
| _maxUnicode = newSize; |
| _isUnicodeValid = 1; |
| _isAsciiValid = 0; |
| } |
| |
| |
| char *DeprecatedStringData::makeAscii() |
| { |
| ASSERT(this != DeprecatedString::shared_null); |
| |
| if (_isUnicodeValid){ |
| DeprecatedChar copyBuf[WEBCORE_DS_INTERNAL_BUFFER_CHARS]; |
| DeprecatedChar *str; |
| |
| if (_ascii && !isAsciiInternal()) |
| DELETE_QCHAR(_ascii); |
| |
| if (_length < WEBCORE_DS_INTERNAL_BUFFER_CHARS){ |
| if (isUnicodeInternal()) { |
| unsigned i = _length; |
| DeprecatedChar *tp = ©Buf[0], *fp = _unicode; |
| while (i--) |
| *tp++ = *fp++; |
| str = ©Buf[0]; |
| _isUnicodeValid = 0; |
| } |
| else |
| str = _unicode; |
| _ascii = _internalBuffer; |
| _maxAscii = WEBCORE_DS_INTERNAL_BUFFER_CHARS; |
| } |
| else { |
| unsigned newSize = ALLOC_CHAR_GOOD_SIZE(_length+1); |
| _ascii = ALLOC_CHAR(newSize); |
| _maxAscii = newSize; |
| str = _unicode; |
| } |
| |
| unsigned i = _length; |
| char* cp = _ascii; |
| while (i--) |
| // FIXME: this converts non-Latin1 characters to '\0', which may be not what we want in some cases. |
| // In particular, toDouble() may fail to report errors, believing that the string ends earlier |
| // than it actually does. |
| *cp++ = (*str++).latin1(); |
| *cp = 0; |
| |
| _isAsciiValid = 1; |
| } |
| else if (!_isAsciiValid) |
| FATAL("ASCII character cache not valid"); |
| |
| return _ascii; |
| } |
| |
| |
| DeprecatedChar *DeprecatedStringData::makeUnicode() |
| { |
| ASSERT(this != DeprecatedString::shared_null); |
| |
| if (_isAsciiValid){ |
| char copyBuf[WEBCORE_DS_INTERNAL_BUFFER_CHARS]; |
| char *str; |
| |
| if (_unicode && !isUnicodeInternal()) |
| DELETE_QCHAR(_unicode); |
| |
| if (_length <= WEBCORE_DS_INTERNAL_BUFFER_UCHARS){ |
| if (isAsciiInternal()) { |
| unsigned i = _length; |
| char *tp = ©Buf[0], *fp = _ascii; |
| while (i--) |
| *tp++ = *fp++; |
| str = ©Buf[0]; |
| _isAsciiValid = 0; |
| } |
| else |
| str = _ascii; |
| _unicode = (DeprecatedChar *)_internalBuffer; |
| _maxUnicode = WEBCORE_DS_INTERNAL_BUFFER_UCHARS; |
| } |
| else { |
| unsigned newSize = ALLOC_QCHAR_GOOD_SIZE(_length); |
| _unicode = WEBCORE_ALLOCATE_CHARACTERS(newSize); |
| _maxUnicode = newSize; |
| str = _ascii; |
| } |
| unsigned i = _length; |
| DeprecatedChar *cp = _unicode; |
| while ( i-- ) |
| *cp++ = *str++; |
| |
| _isUnicodeValid = 1; |
| } |
| else if (!_isUnicodeValid) |
| FATAL("invalid character cache"); |
| |
| return _unicode; |
| } |
| |
| |
| // ------------------------------------------------------------------------- |
| // DeprecatedString |
| // ------------------------------------------------------------------------- |
| |
| |
| DeprecatedString DeprecatedString::number(int n) |
| { |
| DeprecatedString qs; |
| qs.setNum(n); |
| return qs; |
| } |
| |
| DeprecatedString DeprecatedString::number(unsigned n) |
| { |
| DeprecatedString qs; |
| qs.setNum(n); |
| return qs; |
| } |
| |
| DeprecatedString DeprecatedString::number(long n) |
| { |
| DeprecatedString qs; |
| qs.setNum(n); |
| return qs; |
| } |
| |
| DeprecatedString DeprecatedString::number(unsigned long n) |
| { |
| DeprecatedString qs; |
| qs.setNum(n); |
| return qs; |
| } |
| |
| DeprecatedString DeprecatedString::number(double n) |
| { |
| DeprecatedString qs; |
| qs.setNum(n); |
| return qs; |
| } |
| |
| inline void DeprecatedString::detachIfInternal() |
| { |
| DeprecatedStringData *oldData = *dataHandle; |
| if (oldData->refCount > 1 && oldData == &internalData) { |
| DeprecatedStringData *newData = DeprecatedStringData::createAndAdopt(*oldData); |
| newData->_isHeapAllocated = 1; |
| newData->refCount = oldData->refCount; |
| oldData->refCount = 1; |
| oldData->deref(); |
| *dataHandle = newData; |
| } |
| } |
| |
| const DeprecatedChar *DeprecatedString::stableUnicode() |
| { |
| // if we're using the internal data of another string, detach now |
| if (!dataHandle[0]->_isHeapAllocated && *dataHandle != &internalData) { |
| detach(); |
| } |
| return unicode(); |
| } |
| |
| |
| DeprecatedString::~DeprecatedString() |
| { |
| ASSERT(dataHandle); |
| ASSERT(dataHandle[0]->refCount != 0); |
| |
| // Only free the handle if no other string has a reference to the |
| // data. The handle will be freed by the string that has the |
| // last reference to data. |
| bool needToFreeHandle = dataHandle[0]->refCount == 1 && *dataHandle != shared_null; |
| |
| // Copy our internal data if necessary, other strings still need it. |
| detachIfInternal(); |
| |
| // Remove our reference. This should always be the last reference |
| // if *dataHandle points to our internal DeprecatedStringData. If we just detached, |
| // this will remove the extra ref from the new handle. |
| dataHandle[0]->deref(); |
| |
| ASSERT(*dataHandle != &internalData || dataHandle[0]->refCount == 0); |
| |
| if (needToFreeHandle) |
| freeHandle(dataHandle); |
| |
| #ifndef NDEBUG |
| dataHandle = 0; |
| #endif |
| } |
| |
| |
| DeprecatedString::DeprecatedString() |
| { |
| internalData.deref(); |
| dataHandle = makeSharedNullHandle(); |
| dataHandle[0]->ref(); |
| } |
| |
| |
| // Careful, just used by DeprecatedConstString |
| DeprecatedString::DeprecatedString(DeprecatedStringData *constData, bool /*dummy*/) |
| { |
| internalData.deref(); |
| dataHandle = allocateHandle(); |
| *dataHandle = constData; |
| |
| // The DeprecatedConstString constructor allocated the DeprecatedStringData. |
| constData->_isHeapAllocated = 1; |
| } |
| |
| |
| DeprecatedString::DeprecatedString(DeprecatedChar qc) |
| { |
| dataHandle = allocateHandle(); |
| |
| // Copy the DeprecatedChar. |
| if (IS_ASCII_QCHAR(qc)) { |
| char c = qc.unicode(); |
| *dataHandle = &internalData; |
| internalData.initialize( &c, 1 ); |
| } |
| else { |
| *dataHandle = &internalData; |
| internalData.initialize( &qc, 1 ); |
| } |
| } |
| |
| DeprecatedString::DeprecatedString(const DeprecatedChar *unicode, unsigned length) |
| { |
| if (!unicode || !length) { |
| internalData.deref(); |
| dataHandle = makeSharedNullHandle(); |
| dataHandle[0]->ref(); |
| } else { |
| dataHandle = allocateHandle(); |
| |
| // Copy the DeprecatedChar * |
| *dataHandle = &internalData; |
| internalData.initialize(unicode, length); |
| } |
| } |
| |
| DeprecatedString::DeprecatedString(const char *chs) |
| { |
| if (chs) { |
| internalData.initialize(chs,strlen(chs)); |
| dataHandle = allocateHandle(); |
| *dataHandle = &internalData; |
| } else { |
| internalData.deref(); |
| dataHandle = makeSharedNullHandle(); |
| dataHandle[0]->ref(); |
| } |
| } |
| |
| DeprecatedString::DeprecatedString(const char *chs, int len) |
| { |
| dataHandle = allocateHandle(); |
| *dataHandle = &internalData; |
| internalData.initialize(chs,len); |
| } |
| |
| DeprecatedString::DeprecatedString(const DeprecatedString &qs) : dataHandle(qs.dataHandle) |
| { |
| internalData.deref(); |
| dataHandle[0]->ref(); |
| } |
| |
| DeprecatedString &DeprecatedString::operator=(const DeprecatedString &qs) |
| { |
| if (this == &qs) |
| return *this; |
| |
| // Free our handle if it isn't the shared null handle, and if no-one else is using it. |
| bool needToFreeHandle = dataHandle != shared_null_handle && dataHandle[0]->refCount == 1; |
| |
| qs.dataHandle[0]->ref(); |
| deref(); |
| |
| if (needToFreeHandle) |
| freeHandle(dataHandle); |
| |
| dataHandle = qs.dataHandle; |
| |
| return *this; |
| } |
| |
| DeprecatedString &DeprecatedString::operator=(const DeprecatedCString &qcs) |
| { |
| return setLatin1(qcs); |
| } |
| |
| DeprecatedString &DeprecatedString::operator=(const char *chs) |
| { |
| return setLatin1(chs); |
| } |
| |
| DeprecatedString &DeprecatedString::operator=(DeprecatedChar qc) |
| { |
| return *this = DeprecatedString(qc); |
| } |
| |
| DeprecatedString &DeprecatedString::operator=(char ch) |
| { |
| return *this = DeprecatedString(DeprecatedChar(ch)); |
| } |
| |
| DeprecatedChar DeprecatedString::at(unsigned i) const |
| { |
| DeprecatedStringData *thisData = *dataHandle; |
| |
| if (i >= thisData->_length) |
| return 0; |
| |
| if (thisData->_isAsciiValid) { |
| return thisData->_ascii[i]; |
| } |
| |
| ASSERT(thisData->_isUnicodeValid); |
| return thisData->_unicode[i]; |
| } |
| |
| int DeprecatedString::compare(const DeprecatedString& s) const |
| { |
| if (dataHandle[0]->_isAsciiValid && s.dataHandle[0]->_isAsciiValid) |
| return strcmp(ascii(), s.ascii()); |
| return ucstrcmp(*this,s); |
| } |
| |
| int DeprecatedString::compare(const char *chs) const |
| { |
| if (!chs) |
| return isEmpty() ? 0 : 1; |
| DeprecatedStringData *d = dataHandle[0]; |
| if (d->_isAsciiValid) |
| return strcmp(ascii(), chs); |
| const DeprecatedChar *s = unicode(); |
| unsigned len = d->_length; |
| for (unsigned i = 0; i != len; ++i) { |
| char c2 = chs[i]; |
| if (!c2) |
| return 1; |
| DeprecatedChar c1 = s[i]; |
| if (c1.unicode() < c2) |
| return -1; |
| if (c1.unicode() > c2) |
| return 1; |
| } |
| return chs[len] ? -1 : 0; |
| } |
| |
| bool DeprecatedString::startsWith( const DeprecatedString& s ) const |
| { |
| if (dataHandle[0]->_isAsciiValid){ |
| const char *asc = ascii(); |
| |
| for ( int i =0; i < (int) s.dataHandle[0]->_length; i++ ) { |
| if ( i >= (int) dataHandle[0]->_length || asc[i] != s[i] ) |
| return false; |
| } |
| } |
| else if (dataHandle[0]->_isUnicodeValid){ |
| const DeprecatedChar *uni = unicode(); |
| |
| for ( int i =0; i < (int) s.dataHandle[0]->_length; i++ ) { |
| if ( i >= (int) dataHandle[0]->_length || uni[i] != s[i] ) |
| return false; |
| } |
| } |
| else |
| FATAL("invalid character cache"); |
| |
| return true; |
| } |
| |
| bool DeprecatedString::startsWith(const char *prefix) const |
| { |
| DeprecatedStringData *data = *dataHandle; |
| |
| unsigned prefixLength = strlen(prefix); |
| if (data->_isAsciiValid) { |
| return strncmp(prefix, data->_ascii, prefixLength) == 0; |
| } else { |
| ASSERT(data->_isUnicodeValid); |
| if (prefixLength > data->_length) { |
| return false; |
| } |
| const DeprecatedChar *uni = data->_unicode; |
| for (unsigned i = 0; i < prefixLength; ++i) { |
| if (uni[i] != prefix[i]) { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| |
| bool DeprecatedString::startsWith(const char *prefix, bool caseSensitive) const |
| { |
| if (caseSensitive) { |
| return startsWith(prefix); |
| } |
| |
| DeprecatedStringData *data = *dataHandle; |
| |
| unsigned prefixLength = strlen(prefix); |
| if (data->_isAsciiValid) { |
| return strncasecmp(prefix, data->_ascii, prefixLength) == 0; |
| } else { |
| ASSERT(data->_isUnicodeValid); |
| if (prefixLength > data->_length) { |
| return false; |
| } |
| const DeprecatedChar *uni = data->_unicode; |
| for (unsigned i = 0; i < prefixLength; ++i) { |
| if (!equalCaseInsensitive(uni[i], prefix[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| |
| bool DeprecatedString::endsWith(const DeprecatedString& s) const |
| { |
| const DeprecatedChar *uni = unicode(); |
| |
| int length = dataHandle[0]->_length; |
| int slength = s.dataHandle[0]->_length; |
| if (length < slength) |
| return false; |
| |
| for (int i = length - slength, j = 0; i < length; i++, j++) { |
| if (uni[i] != s[j]) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool DeprecatedString::isNull() const |
| { |
| return dataHandle == shared_null_handle; |
| } |
| |
| int DeprecatedString::find(DeprecatedChar qc, int index) const |
| { |
| if (dataHandle[0]->_isAsciiValid) { |
| if (!IS_ASCII_QCHAR(qc)) |
| return -1; |
| return find(qc.unicode(), index); |
| } |
| return find(DeprecatedString(qc), index, true); |
| } |
| |
| int DeprecatedString::find(char ch, int index) const |
| { |
| if (dataHandle[0]->_isAsciiValid){ |
| const char *cp = ascii(); |
| |
| if ( index < 0 ) |
| index += dataHandle[0]->_length; |
| |
| if (index >= (int)dataHandle[0]->_length) |
| return -1; |
| |
| for (int i = index; i < (int)dataHandle[0]->_length; i++) |
| if (cp[i] == ch) |
| return i; |
| } |
| else if (dataHandle[0]->_isUnicodeValid) |
| return find(DeprecatedChar(ch), index, true); |
| else |
| FATAL("invalid character cache"); |
| |
| return -1; |
| } |
| |
| int DeprecatedString::find(const DeprecatedString &str, int index, bool caseSensitive) const |
| { |
| // FIXME, use the first character algorithm |
| /* |
| We use some weird hashing for efficiency's sake. Instead of |
| comparing strings, we compare the sum of str with that of |
| a part of this DeprecatedString. Only if that matches, we call memcmp |
| or ucstrnicmp. |
| |
| The hash value of a string is the sum of the cells of its |
| QChars. |
| */ |
| if ( index < 0 ) |
| index += dataHandle[0]->_length; |
| int lstr = str.dataHandle[0]->_length; |
| int lthis = dataHandle[0]->_length - index; |
| if ( (unsigned)lthis > dataHandle[0]->_length ) |
| return -1; |
| int delta = lthis - lstr; |
| if ( delta < 0 ) |
| return -1; |
| |
| const DeprecatedChar *uthis = unicode() + index; |
| const DeprecatedChar *ustr = str.unicode(); |
| unsigned hthis = 0; |
| unsigned hstr = 0; |
| int i; |
| if ( caseSensitive ) { |
| for ( i = 0; i < lstr; i++ ) { |
| hthis += uthis[i].unicode(); |
| hstr += ustr[i].unicode(); |
| } |
| i = 0; |
| while ( true ) { |
| if ( hthis == hstr && memcmp(uthis + i, ustr, lstr * sizeof(DeprecatedChar)) == 0 ) |
| return index + i; |
| if ( i == delta ) |
| return -1; |
| hthis += uthis[i + lstr].unicode(); |
| hthis -= uthis[i].unicode(); |
| i++; |
| } |
| } else { |
| for ( i = 0; i < lstr; i++ ) { |
| hthis += tolower(uthis[i].unicode()); |
| hstr += tolower(ustr[i].unicode()); |
| } |
| i = 0; |
| while ( true ) { |
| if ( hthis == hstr && equalCaseInsensitive(uthis + i, ustr, lstr) ) |
| return index + i; |
| if ( i == delta ) |
| return -1; |
| hthis += tolower(uthis[i + lstr].unicode()); |
| hthis -= tolower(uthis[i].unicode()); |
| i++; |
| } |
| } |
| } |
| |
| // This function should be as fast as possible, every little bit helps. |
| // Our usage patterns are typically small strings. In time trials |
| // this simplistic algorithm is much faster than Boyer-Moore or hash |
| // based algorithms. |
| int DeprecatedString::find(const char *chs, int index, bool caseSensitive) const |
| { |
| if (!chs || index < 0) |
| return -1; |
| |
| DeprecatedStringData *data = *dataHandle; |
| |
| int chsLength = strlen(chs); |
| int n = data->_length - index; |
| if (n < 0) |
| return -1; |
| n -= chsLength - 1; |
| if (n <= 0) |
| return -1; |
| |
| const char *chsPlusOne = chs + 1; |
| int chsLengthMinusOne = chsLength - 1; |
| |
| if (data->_isAsciiValid) { |
| char *ptr = data->_ascii + index - 1; |
| if (caseSensitive) { |
| char c = *chs; |
| do { |
| if (*++ptr == c && memcmp(ptr + 1, chsPlusOne, chsLengthMinusOne) == 0) { |
| return data->_length - chsLength - n + 1; |
| } |
| } while (--n); |
| } else { |
| int lc = tolower(*chs); |
| do { |
| if (tolower(*++ptr) == lc && equalCaseInsensitive(ptr + 1, chsPlusOne, chsLengthMinusOne)) { |
| return data->_length - chsLength - n + 1; |
| } |
| } while (--n); |
| } |
| } else { |
| ASSERT(data->_isUnicodeValid); |
| |
| const DeprecatedChar *ptr = data->_unicode + index - 1; |
| if (caseSensitive) { |
| DeprecatedChar c = *chs; |
| do { |
| if (*++ptr == c && equal(ptr + 1, chsPlusOne, chsLengthMinusOne)) { |
| return data->_length - chsLength - n + 1; |
| } |
| } while (--n); |
| } else { |
| int lc = tolower((unsigned char)*chs); |
| do { |
| if (tolower((++ptr)->unicode()) == lc && equalCaseInsensitive(ptr + 1, chsPlusOne, chsLengthMinusOne)) { |
| return data->_length - chsLength - n + 1; |
| } |
| } while (--n); |
| } |
| } |
| |
| return -1; |
| } |
| |
| int DeprecatedString::find(const RegularExpression &qre, int index) const |
| { |
| if ( index < 0 ) |
| index += dataHandle[0]->_length; |
| return qre.match( *this, index ); |
| } |
| |
| int DeprecatedString::findRev(char ch, int index) const |
| { |
| if (dataHandle[0]->_isAsciiValid){ |
| const char *cp = ascii(); |
| |
| if (index < 0) |
| index += dataHandle[0]->_length; |
| if (index > (int)dataHandle[0]->_length) |
| return -1; |
| |
| for (int i = index; i >= 0; i--) { |
| if (cp[i] == ch) |
| return i; |
| } |
| } |
| else if (dataHandle[0]->_isUnicodeValid) |
| return findRev(DeprecatedString(DeprecatedChar(ch)), index); |
| else |
| FATAL("invalid character cache"); |
| |
| return -1; |
| } |
| |
| int DeprecatedString::findRev(const char *chs, int index) const |
| { |
| return findRev(DeprecatedString(chs), index); |
| } |
| |
| int DeprecatedString::findRev( const DeprecatedString& str, int index, bool cs ) const |
| { |
| // FIXME, use the first character algorithm |
| /* |
| See DeprecatedString::find() for explanations. |
| */ |
| int lthis = dataHandle[0]->_length; |
| if ( index < 0 ) |
| index += lthis; |
| |
| int lstr = str.dataHandle[0]->_length; |
| int delta = lthis - lstr; |
| if ( index < 0 || index > lthis || delta < 0 ) |
| return -1; |
| if ( index > delta ) |
| index = delta; |
| |
| const DeprecatedChar *uthis = unicode(); |
| const DeprecatedChar *ustr = str.unicode(); |
| unsigned hthis = 0; |
| unsigned hstr = 0; |
| int i; |
| if ( cs ) { |
| for ( i = 0; i < lstr; i++ ) { |
| hthis += uthis[index + i].unicode(); |
| hstr += ustr[i].unicode(); |
| } |
| i = index; |
| while ( true ) { |
| if ( hthis == hstr && memcmp(uthis + i, ustr, lstr * sizeof(DeprecatedChar)) == 0 ) |
| return i; |
| if ( i == 0 ) |
| return -1; |
| i--; |
| hthis -= uthis[i + lstr].unicode(); |
| hthis += uthis[i].unicode(); |
| } |
| } else { |
| for ( i = 0; i < lstr; i++ ) { |
| hthis += uthis[index + i].lower().unicode(); |
| hstr += ustr[i].lower().unicode(); |
| } |
| i = index; |
| while ( true ) { |
| if ( hthis == hstr && equalCaseInsensitive(uthis + i, ustr, lstr) ) |
| return i; |
| if ( i == 0 ) |
| return -1; |
| i--; |
| hthis -= uthis[i + lstr].lower().unicode(); |
| hthis += uthis[i].lower().unicode(); |
| } |
| } |
| |
| // Should never get here. |
| return -1; |
| } |
| |
| |
| int DeprecatedString::contains(DeprecatedChar c, bool cs) const |
| { |
| int count = 0; |
| |
| DeprecatedStringData *data = *dataHandle; |
| |
| if (data->_isAsciiValid) { |
| if (!IS_ASCII_QCHAR(c)) |
| return 0; |
| const char *cPtr = data->_ascii; |
| int n = data->_length; |
| char ac = c.unicode(); |
| if (cs) { // case sensitive |
| while (n--) |
| count += *cPtr++ == ac; |
| } else { // case insensitive |
| int lc = tolower(ac); |
| while (n--) { |
| count += tolower(*cPtr++) == lc; |
| } |
| } |
| } else { |
| ASSERT(data->_isUnicodeValid); |
| const DeprecatedChar *uc = data->_unicode; |
| int n = data->_length; |
| if (cs) { // case sensitive |
| while ( n-- ) |
| count += *uc++ == c; |
| } else { // case insensitive |
| int lc = tolower(c.unicode()); |
| while (n--) { |
| count += tolower(uc->unicode()) == lc; |
| uc++; |
| } |
| } |
| } |
| |
| return count; |
| } |
| |
| int DeprecatedString::contains(char ch) const |
| { |
| return contains(DeprecatedChar(ch), true); |
| } |
| |
| int DeprecatedString::contains(const char *str, bool caseSensitive) const |
| { |
| if (!str) |
| return 0; |
| |
| int len = strlen(str); |
| char c = *str; |
| |
| DeprecatedStringData *data = *dataHandle; |
| int n = data->_length; |
| |
| n -= len - 1; |
| if (n <= 0) |
| return 0; |
| |
| int count = 0; |
| |
| if (data->_isAsciiValid) { |
| const char *p = data->_ascii; |
| if (caseSensitive) { |
| do { |
| count += *p == c && memcmp(p + 1, str + 1, len - 1) == 0; |
| p++; |
| } while (--n); |
| } else { |
| int lc = tolower(c); |
| do { |
| count += tolower(*p) == lc && equalCaseInsensitive(p + 1, str + 1, len - 1); |
| p++; |
| } while (--n); |
| } |
| } else { |
| ASSERT(data->_isUnicodeValid); |
| const DeprecatedChar *p = data->_unicode; |
| if (caseSensitive) { |
| do { |
| count += *p == c && equal(p + 1, str + 1, len - 1); |
| p++; |
| } while (--n); |
| } else { |
| int lc = tolower(c); |
| do { |
| count += tolower(p->unicode()) == lc && equalCaseInsensitive(p + 1, str + 1, len - 1); |
| p++; |
| } while (--n); |
| } |
| } |
| |
| return count; |
| } |
| |
| int DeprecatedString::contains(const DeprecatedString &str, bool caseSensitive) const |
| { |
| if (str.isEmpty()) |
| return 0; |
| |
| const DeprecatedChar *strP = str.unicode(); |
| int len = str.dataHandle[0]->_length; |
| DeprecatedChar c = *strP; |
| |
| const DeprecatedChar *p = unicode(); |
| int n = dataHandle[0]->_length; |
| |
| n -= len - 1; |
| if (n <= 0) |
| return 0; |
| |
| int count = 0; |
| |
| if (caseSensitive) { |
| int byteCount = len * sizeof(DeprecatedChar); |
| do { |
| count += *p == c && memcmp(p, strP, byteCount) == 0; |
| ++p; |
| } while (--n); |
| } else { |
| do { |
| count += p->lower() == c && equalCaseInsensitive(p, strP, len) == 0; |
| ++p; |
| } while (--n); |
| } |
| |
| return count; |
| } |
| |
| bool DeprecatedString::isAllASCII() const |
| { |
| DeprecatedStringData *data = *dataHandle; |
| |
| int n = data->_length; |
| if (data->_isAsciiValid) { |
| const char *p = data->_ascii; |
| while (n--) { |
| unsigned char c = *p++; |
| if (c > 0x7F) { |
| return false; |
| } |
| } |
| } else { |
| ASSERT(data->_isUnicodeValid); |
| const DeprecatedChar *p = data->_unicode; |
| while (n--) { |
| if ((*p++).unicode() > 0x7F) { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| bool DeprecatedString::isAllLatin1() const |
| { |
| DeprecatedStringData *data = *dataHandle; |
| |
| if (data->_isAsciiValid) { |
| return true; |
| } |
| |
| ASSERT(data->_isUnicodeValid); |
| int n = data->_length; |
| const DeprecatedChar *p = data->_unicode; |
| while (n--) { |
| if ((*p++).unicode() > 0xFF) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool DeprecatedString::hasFastLatin1() const |
| { |
| DeprecatedStringData *data = *dataHandle; |
| return data->_isAsciiValid; |
| } |
| |
| void DeprecatedString::copyLatin1(char *buffer, unsigned position, unsigned maxLength) const |
| { |
| DeprecatedStringData *data = *dataHandle; |
| |
| int length = data->_length; |
| if (position > static_cast<unsigned>(length)) |
| length = 0; |
| else |
| length -= position; |
| if (static_cast<unsigned>(length) > maxLength) |
| length = static_cast<int>(maxLength); |
| |
| buffer[length] = 0; |
| |
| if (data->_isAsciiValid) { |
| memcpy(buffer, data->_ascii + position, length); |
| return; |
| } |
| |
| ASSERT(data->_isUnicodeValid); |
| const DeprecatedChar* uc = data->_unicode + position; |
| while (length--) |
| *buffer++ = (*uc++).latin1(); |
| } |
| |
| short DeprecatedString::toShort(bool *ok, int base) const |
| { |
| int v = toInt(ok, base); |
| short sv = v; |
| if (sv != v) { |
| if (ok) |
| *ok = false; |
| return 0; |
| } |
| return sv; |
| } |
| |
| unsigned short DeprecatedString::toUShort(bool *ok, int base) const |
| { |
| unsigned v = toUInt(ok, base); |
| unsigned short sv = v; |
| if (sv != v) { |
| if (ok) |
| *ok = false; |
| return 0; |
| } |
| return sv; |
| } |
| |
| template <typename IntegralType> static inline |
| IntegralType toIntegralType(const DeprecatedString& string, bool *ok, int base) |
| { |
| static const IntegralType integralMax = std::numeric_limits<IntegralType>::max(); |
| static const bool isSigned = std::numeric_limits<IntegralType>::is_signed; |
| const DeprecatedChar* p = string.unicode(); |
| const IntegralType maxMultiplier = integralMax / base; |
| |
| int length = string.length(); |
| IntegralType value = 0; |
| bool isOk = false; |
| bool isNegative = false; |
| |
| if (!p) |
| goto bye; |
| |
| // skip leading whitespace |
| while (length && p->isSpace()) { |
| length--; |
| p++; |
| } |
| |
| if (isSigned && length && *p == '-') { |
| length--; |
| p++; |
| isNegative = true; |
| } else if (length && *p == '+') { |
| length--; |
| p++; |
| } |
| |
| if (!length || !isCharacterAllowedInBase(*p, base)) |
| goto bye; |
| |
| while (length && isCharacterAllowedInBase(*p, base)) { |
| length--; |
| IntegralType digitValue; |
| int c = p->unicode(); |
| if (isdigit(c)) |
| digitValue = c - '0'; |
| else if (c >= 'a') |
| digitValue = c - 'a' + 10; |
| else |
| digitValue = c - 'A' + 10; |
| |
| if (value > maxMultiplier || (value == maxMultiplier && digitValue > (integralMax % base) + isNegative)) |
| goto bye; |
| |
| value = base * value + digitValue; |
| p++; |
| } |
| |
| if (isNegative) |
| value = -value; |
| |
| // skip trailing space |
| while (length && p->isSpace()) { |
| length--; |
| p++; |
| } |
| |
| if (!length) |
| isOk = true; |
| bye: |
| if (ok) |
| *ok = isOk; |
| return isOk ? value : 0; |
| } |
| |
| int DeprecatedString::toInt(bool *ok, int base) const |
| { |
| return toIntegralType<int>(*this, ok, base); |
| } |
| |
| int64_t DeprecatedString::toInt64(bool *ok, int base) const |
| { |
| return toIntegralType<int64_t>(*this, ok, base); |
| } |
| |
| unsigned DeprecatedString::toUInt(bool *ok, int base) const |
| { |
| return toIntegralType<unsigned>(*this, ok, base); |
| } |
| |
| uint64_t DeprecatedString::toUInt64(bool *ok, int base) const |
| { |
| return toIntegralType<uint64_t>(*this, ok, base); |
| } |
| |
| double DeprecatedString::toDouble(bool *ok) const |
| { |
| if (isEmpty()) { |
| if (ok) |
| *ok = false; |
| return 0; |
| } |
| const char *s = latin1(); |
| char *end; |
| double val = kjs_strtod(s, &end); |
| if (ok) |
| *ok = end == 0 || *end == '\0'; |
| return val; |
| } |
| |
| float DeprecatedString::toFloat(bool* ok) const |
| { |
| // FIXME: this will return ok even when the string does not fit into a float |
| return narrowPrecisionToFloat(toDouble(ok)); |
| } |
| |
| DeprecatedString DeprecatedString::left(unsigned len) const |
| { |
| return mid(0, len); |
| } |
| |
| DeprecatedString DeprecatedString::right(unsigned len) const |
| { |
| return mid(length() - len, len); |
| } |
| |
| DeprecatedString DeprecatedString::mid(unsigned start, unsigned len) const |
| { |
| if (dataHandle && *dataHandle) { |
| DeprecatedStringData &data = **dataHandle; |
| |
| // clip length |
| if (start >= data._length) |
| return DeprecatedString(); |
| |
| if (len > data._length - start) |
| len = data._length - start; |
| |
| if (len == 0) |
| return DeprecatedString(); |
| |
| if (start == 0 && len == data._length) |
| return *this; |
| |
| ASSERT(start + len >= start && // unsigned overflow |
| start + len <= data._length); // past the end |
| |
| // ascii case |
| if (data._isAsciiValid && data._ascii) |
| return DeprecatedString(&data._ascii[start] , len); |
| |
| // unicode case |
| if (data._isUnicodeValid && data._unicode) |
| return DeprecatedString(&data._unicode[start], len); |
| } |
| |
| // degenerate case |
| return DeprecatedString(); |
| } |
| |
| DeprecatedString DeprecatedString::copy() const |
| { |
| // does not need to be a deep copy |
| return DeprecatedString(*this); |
| } |
| |
| DeprecatedString DeprecatedString::lower() const |
| { |
| DeprecatedString s(*this); |
| DeprecatedStringData *d = *s.dataHandle; |
| int l = d->_length; |
| if (l) { |
| bool detached = false; |
| if (d->_isAsciiValid) { |
| char *p = d->_ascii; |
| while (l--) { |
| char c = *p; |
| // FIXME: Doesn't work for 0x80-0xFF. |
| if (c >= 'A' && c <= 'Z') { |
| if (!detached) { |
| s.detach(); |
| d = *s.dataHandle; |
| p = d->_ascii + d->_length - l - 1; |
| detached = true; |
| } |
| *p = c + ('a' - 'A'); |
| } |
| p++; |
| } |
| } |
| else { |
| ASSERT(d->_isUnicodeValid); |
| DeprecatedChar *p = d->_unicode; |
| while (l--) { |
| DeprecatedChar c = *p; |
| // FIXME: Doesn't work for 0x80-0xFF. |
| if (IS_ASCII_QCHAR(c)) { |
| if (c.unicode() >= 'A' && c.unicode() <= 'Z') { |
| if (!detached) { |
| s.detach(); |
| d = *s.dataHandle; |
| p = d->_unicode + d->_length - l - 1; |
| detached = true; |
| } |
| *p = c.unicode() + ('a' - 'A'); |
| } |
| } else { |
| DeprecatedChar clower = c.lower(); |
| if (clower != c) { |
| if (!detached) { |
| s.detach(); |
| d = *s.dataHandle; |
| p = d->_unicode + d->_length - l - 1; |
| detached = true; |
| } |
| *p = clower; |
| } |
| } |
| p++; |
| } |
| } |
| } |
| return s; |
| } |
| |
| DeprecatedString DeprecatedString::stripWhiteSpace() const |
| { |
| if ( isEmpty() ) // nothing to do |
| return *this; |
| if ( !at(0).isSpace() && !at(dataHandle[0]->_length-1).isSpace() ) |
| return *this; |
| |
| int start = 0; |
| int end = dataHandle[0]->_length - 1; |
| |
| DeprecatedString result = fromLatin1(""); |
| while ( start<=end && at(start).isSpace() ) // skip white space from start |
| start++; |
| if ( start > end ) { // only white space |
| return result; |
| } |
| while ( end && at(end).isSpace() ) // skip white space from end |
| end--; |
| int l = end - start + 1; |
| |
| if (dataHandle[0]->_isAsciiValid){ |
| result.setLength( l ); |
| if ( l ) |
| memcpy(const_cast<char*>(result.dataHandle[0]->ascii()), &ascii()[start], l ); |
| } |
| else if (dataHandle[0]->_isUnicodeValid){ |
| result.setLength( l ); |
| if ( l ) |
| memcpy(result.forceUnicode(), &unicode()[start], sizeof(DeprecatedChar)*l ); |
| } |
| else |
| FATAL("invalid character cache"); |
| return result; |
| } |
| |
| DeprecatedString DeprecatedString::simplifyWhiteSpace() const |
| { |
| if ( isEmpty() ) // nothing to do |
| return *this; |
| |
| DeprecatedString result; |
| |
| if (dataHandle[0]->_isAsciiValid){ |
| result.setLength( dataHandle[0]->_length ); |
| const char *from = ascii(); |
| const char *fromend = from + dataHandle[0]->_length; |
| int outc=0; |
| |
| char *to = const_cast<char*>(result.ascii()); |
| while ( true ) { |
| while ( from!=fromend && DeprecatedChar(*from).isSpace() ) |
| from++; |
| while ( from!=fromend && !DeprecatedChar(*from).isSpace() ) |
| to[outc++] = *from++; |
| if ( from!=fromend ) |
| to[outc++] = ' '; |
| else |
| break; |
| } |
| if ( outc > 0 && to[outc-1] == ' ' ) |
| outc--; |
| result.truncate( outc ); |
| } |
| else if (dataHandle[0]->_isUnicodeValid){ |
| result.setLength( dataHandle[0]->_length ); |
| const DeprecatedChar *from = unicode(); |
| const DeprecatedChar *fromend = from + dataHandle[0]->_length; |
| int outc=0; |
| |
| DeprecatedChar *to = result.forceUnicode(); |
| while ( true ) { |
| while ( from!=fromend && from->isSpace() ) |
| from++; |
| while ( from!=fromend && !from->isSpace() ) |
| to[outc++] = *from++; |
| if ( from!=fromend ) |
| to[outc++] = ' '; |
| else |
| break; |
| } |
| if ( outc > 0 && to[outc-1] == ' ' ) |
| outc--; |
| result.truncate( outc ); |
| } |
| else |
| FATAL("invalid character cache"); |
| |
| return result; |
| } |
| |
| void DeprecatedString::deref() |
| { |
| dataHandle[0]->deref(); |
| } |
| |
| |
| DeprecatedString &DeprecatedString::setUnicode(const DeprecatedChar *uni, unsigned len) |
| { |
| detachAndDiscardCharacters(); |
| |
| // Free our handle if it isn't the shared null handle, and if no-one else is using it. |
| bool needToFreeHandle = dataHandle != shared_null_handle && dataHandle[0]->refCount == 1; |
| |
| if (len == 0) { |
| deref(); |
| if (needToFreeHandle) |
| freeHandle(dataHandle); |
| dataHandle = makeSharedNullHandle(); |
| dataHandle[0]->ref(); |
| } else if (len > dataHandle[0]->_maxUnicode || dataHandle[0]->refCount != 1 || !dataHandle[0]->_isUnicodeValid) { |
| deref(); |
| if (needToFreeHandle) |
| freeHandle(dataHandle); |
| dataHandle = allocateHandle(); |
| *dataHandle = new DeprecatedStringData(uni, len); |
| dataHandle[0]->_isHeapAllocated = 1; |
| } else { |
| if ( uni ) |
| memcpy( (void *)unicode(), uni, sizeof(DeprecatedChar)*len ); |
| dataHandle[0]->_length = len; |
| dataHandle[0]->_isAsciiValid = 0; |
| } |
| |
| return *this; |
| } |
| |
| |
| DeprecatedString &DeprecatedString::setLatin1(const char *str, int len) |
| { |
| if ( str == 0 ) |
| return setUnicode(0,0); |
| if ( len < 0 ) |
| len = strlen(str); |
| |
| detachAndDiscardCharacters(); |
| |
| // Free our handle if it isn't the shared null handle, and if no-one else is using it. |
| bool needToFreeHandle = dataHandle != shared_null_handle && dataHandle[0]->refCount == 1; |
| |
| if (len+1 > (int)dataHandle[0]->_maxAscii || dataHandle[0]->refCount != 1 || !dataHandle[0]->_isAsciiValid) { |
| deref(); |
| if (needToFreeHandle) |
| freeHandle(dataHandle); |
| dataHandle = allocateHandle(); |
| *dataHandle = new DeprecatedStringData(str,len); |
| dataHandle[0]->_isHeapAllocated = 1; |
| } else { |
| strcpy(const_cast<char*>(ascii()), str ); |
| dataHandle[0]->_length = len; |
| dataHandle[0]->_isUnicodeValid = 0; |
| } |
| return *this; |
| } |
| |
| DeprecatedString &DeprecatedString::setNum(short n) |
| { |
| return format("%d", n); |
| } |
| |
| DeprecatedString &DeprecatedString::setNum(unsigned short n) |
| { |
| return format("%u", n); |
| } |
| |
| DeprecatedString &DeprecatedString::setNum(int n) |
| { |
| return format("%d", n); |
| } |
| |
| DeprecatedString &DeprecatedString::setNum(unsigned n) |
| { |
| return format("%u", n); |
| } |
| |
| DeprecatedString &DeprecatedString::setNum(long n) |
| { |
| return format("%ld", n); |
| } |
| |
| DeprecatedString &DeprecatedString::setNum(unsigned long n) |
| { |
| return format("%lu", n); |
| } |
| |
| DeprecatedString &DeprecatedString::setNum(double n) |
| { |
| return format("%.6lg", n); |
| } |
| |
| DeprecatedString &DeprecatedString::format(const char *format, ...) |
| { |
| // FIXME: this needs the same windows compat fixes as String::format |
| |
| va_list args; |
| va_start(args, format); |
| |
| // Do the format once to get the length. |
| #if PLATFORM(WIN_OS) |
| int result = _vscprintf(format, args); |
| #else |
| char ch; |
| int result = vsnprintf(&ch, 1, format, args); |
| #endif |
| |
| // Handle the empty string case to simplify the code below. |
| if (result <= 0) { // POSIX returns 0 in error; Windows returns a negative number. |
| setUnicode(0, 0); |
| return *this; |
| } |
| unsigned len = result; |
| |
| // Arrange for storage for the resulting string. |
| detachAndDiscardCharacters(); |
| if (len >= dataHandle[0]->_maxAscii || dataHandle[0]->refCount != 1 || !dataHandle[0]->_isAsciiValid) { |
| // Free our handle if it isn't the shared null handle, and if no-one else is using it. |
| bool needToFreeHandle = dataHandle != shared_null_handle && dataHandle[0]->refCount == 1; |
| deref(); |
| if (needToFreeHandle) |
| freeHandle(dataHandle); |
| dataHandle = allocateHandle(); |
| *dataHandle = new DeprecatedStringData((char *)0, len); |
| dataHandle[0]->_isHeapAllocated = 1; |
| } else { |
| dataHandle[0]->_length = len; |
| dataHandle[0]->_isUnicodeValid = 0; |
| } |
| |
| // Now do the formatting again, guaranteed to fit. |
| vsprintf(const_cast<char*>(ascii()), format, args); |
| |
| va_end(args); |
| return *this; |
| } |
| |
| DeprecatedString &DeprecatedString::prepend(const DeprecatedString &qs) |
| { |
| return insert(0, qs); |
| } |
| |
| DeprecatedString &DeprecatedString::prepend(const DeprecatedChar *characters, unsigned length) |
| { |
| return insert(0, characters, length); |
| } |
| |
| DeprecatedString &DeprecatedString::append(const DeprecatedString &qs) |
| { |
| return insert(dataHandle[0]->_length, qs); |
| } |
| |
| DeprecatedString &DeprecatedString::append(const char *characters, unsigned length) |
| { |
| return insert(dataHandle[0]->_length, characters, length); |
| } |
| |
| DeprecatedString &DeprecatedString::append(const DeprecatedChar *characters, unsigned length) |
| { |
| return insert(dataHandle[0]->_length, characters, length); |
| } |
| |
| DeprecatedString &DeprecatedString::insert(unsigned index, const char *insertChars, unsigned insertLength) |
| { |
| if (insertLength == 0) |
| return *this; |
| |
| detach(); |
| |
| if (dataHandle[0]->_isAsciiValid){ |
| unsigned originalLength = dataHandle[0]->_length; |
| char *targetChars; |
| |
| // Ensure that we have enough space. |
| setLength (originalLength + insertLength); |
| targetChars = const_cast<char*>(ascii()); |
| |
| // Move tail to make space for inserted characters. |
| memmove (targetChars+index+insertLength, targetChars+index, originalLength-index); |
| |
| // Insert characters. |
| memcpy (targetChars+index, insertChars, insertLength); |
| |
| dataHandle[0]->_isUnicodeValid = 0; |
| } |
| else if (dataHandle[0]->_isUnicodeValid){ |
| unsigned originalLength = dataHandle[0]->_length; |
| DeprecatedChar *targetChars; |
| |
| // Ensure that we have enough space. |
| setLength (originalLength + insertLength); |
| targetChars = (DeprecatedChar *)unicode(); |
| |
| // Move tail to make space for inserted characters. |
| memmove (targetChars+(index+insertLength), targetChars+index, (originalLength-index)*sizeof(DeprecatedChar)); |
| |
| // Insert characters. |
| unsigned i = insertLength; |
| DeprecatedChar *target = targetChars+index; |
| |
| while (i--) |
| *target++ = *insertChars++; |
| } |
| else |
| FATAL("invalid character cache"); |
| |
| return *this; |
| } |
| |
| |
| DeprecatedString &DeprecatedString::insert(unsigned index, const DeprecatedString &qs) |
| { |
| if (qs.dataHandle[0]->_length == 0) |
| return *this; |
| |
| if (dataHandle[0]->_isAsciiValid && qs.isAllLatin1()) { |
| insert(index, qs.latin1(), qs.length()); |
| } |
| else { |
| unsigned insertLength = qs.dataHandle[0]->_length; |
| unsigned originalLength = dataHandle[0]->_length; |
| |
| forceUnicode(); |
| |
| // Ensure that we have enough space. |
| setLength (originalLength + insertLength); |
| DeprecatedChar *targetChars = const_cast<DeprecatedChar *>(unicode()); |
| |
| // Move tail to make space for inserted characters. |
| memmove (targetChars+(index+insertLength), targetChars+index, (originalLength-index)*sizeof(DeprecatedChar)); |
| |
| // Insert characters. |
| if (qs.dataHandle[0]->_isAsciiValid){ |
| unsigned i = insertLength; |
| DeprecatedChar *target = targetChars+index; |
| char *a = const_cast<char*>(qs.ascii()); |
| |
| while (i--) |
| *target++ = *a++; |
| } |
| else { |
| DeprecatedChar *insertChars = (DeprecatedChar *)qs.unicode(); |
| memcpy (targetChars+index, insertChars, insertLength*sizeof(DeprecatedChar)); |
| } |
| |
| dataHandle[0]->_isAsciiValid = 0; |
| } |
| |
| return *this; |
| } |
| |
| |
| DeprecatedString &DeprecatedString::insert(unsigned index, const DeprecatedChar *insertChars, unsigned insertLength) |
| { |
| if (insertLength == 0) |
| return *this; |
| |
| forceUnicode(); |
| |
| unsigned originalLength = dataHandle[0]->_length; |
| setLength(originalLength + insertLength); |
| |
| DeprecatedChar *targetChars = const_cast<DeprecatedChar *>(unicode()); |
| if (originalLength > index) { |
| memmove(targetChars + index + insertLength, targetChars + index, (originalLength - index) * sizeof(DeprecatedChar)); |
| } |
| memcpy(targetChars + index, insertChars, insertLength * sizeof(DeprecatedChar)); |
| |
| return *this; |
| } |
| |
| |
| DeprecatedString &DeprecatedString::insert(unsigned index, DeprecatedChar qc) |
| { |
| detach(); |
| |
| if (dataHandle[0]->_isAsciiValid && IS_ASCII_QCHAR(qc)){ |
| unsigned originalLength = dataHandle[0]->_length; |
| char insertChar = qc.unicode(); |
| char *targetChars; |
| |
| // Ensure that we have enough space. |
| setLength (originalLength + 1); |
| targetChars = const_cast<char*>(ascii()); |
| |
| // Move tail to make space for inserted character. |
| memmove (targetChars+index+1, targetChars+index, originalLength-index); |
| |
| // Insert character. |
| targetChars[index] = insertChar; |
| targetChars[dataHandle[0]->_length] = 0; |
| |
| dataHandle[0]->_isUnicodeValid = 0; |
| } |
| else { |
| unsigned originalLength = dataHandle[0]->_length; |
| |
| forceUnicode(); |
| |
| // Ensure that we have enough space. |
| setLength (originalLength + 1); |
| DeprecatedChar *targetChars = const_cast<DeprecatedChar *>(unicode()); |
| |
| // Move tail to make space for inserted character. |
| memmove (targetChars+(index+1), targetChars+index, (originalLength-index)*sizeof(DeprecatedChar)); |
| |
| targetChars[index] = qc; |
| } |
| |
| return *this; |
| } |
| |
| |
| DeprecatedString &DeprecatedString::insert(unsigned index, char ch) |
| { |
| detach(); |
| |
| if (dataHandle[0]->_isAsciiValid) { |
| unsigned originalLength = dataHandle[0]->_length; |
| char *targetChars; |
| |
| // Ensure that we have enough space. |
| setLength (originalLength + 1); |
| targetChars = const_cast<char*>(ascii()); |
| |
| // Move tail to make space for inserted character. |
| memmove (targetChars+index+1, targetChars+index, originalLength-index); |
| |
| // Insert character. |
| targetChars[index] = ch; |
| targetChars[dataHandle[0]->_length] = 0; |
| |
| dataHandle[0]->_isUnicodeValid = 0; |
| } |
| else if (dataHandle[0]->_isUnicodeValid){ |
| unsigned originalLength = dataHandle[0]->_length; |
| DeprecatedChar *targetChars; |
| |
| // Ensure that we have enough space. |
| setLength (originalLength + 1); |
| targetChars = (DeprecatedChar *)unicode(); |
| |
| // Move tail to make space for inserted character. |
| memmove (targetChars+(index+1), targetChars+index, (originalLength-index)*sizeof(DeprecatedChar)); |
| |
| targetChars[index] = (DeprecatedChar)ch; |
| } |
| else |
| FATAL("invalid character cache"); |
| |
| return *this; |
| } |
| |
| // Copy DeprecatedStringData if necessary. Must be called before the string data is mutated. |
| void DeprecatedString::detach() |
| { |
| DeprecatedStringData *oldData = *dataHandle; |
| |
| if (oldData->refCount == 1 && oldData != shared_null) |
| return; |
| |
| // Copy data for this string so we can safely mutate it. |
| DeprecatedStringData *newData; |
| if (oldData->_isAsciiValid) |
| newData = new DeprecatedStringData(oldData->ascii(), oldData->_length); |
| else |
| newData = new DeprecatedStringData(oldData->unicode(), oldData->_length); |
| newData->_isHeapAllocated = 1; |
| |
| // There is now one less client for the old data. |
| oldData->deref(); |
| |
| // If the old data is our internal data, then we'll keep that. |
| // This decreases the chance we'll have to do a detachInternal later |
| // when this object is destroyed. |
| if (oldData == &internalData) { |
| newData->refCount = oldData->refCount; |
| oldData->refCount = 1; |
| *dataHandle = newData; |
| newData = oldData; |
| } |
| |
| // Create a new handle. |
| dataHandle = allocateHandle(); |
| *dataHandle = newData; |
| } |
| |
| void DeprecatedString::detachAndDiscardCharacters() |
| { |
| // Missing optimization: Don't bother copying the old data if we detach. |
| detach(); |
| } |
| |
| DeprecatedString &DeprecatedString::remove(unsigned index, unsigned len) |
| { |
| unsigned olen = dataHandle[0]->_length; |
| if ( index >= olen ) { |
| // range problems |
| } else if ( index + len >= olen ) { // index ok |
| setLength( index ); |
| } else if ( len != 0 ) { |
| // Missing optimization: Could avoid copying characters we are going to remove |
| // by making a special version of detach(). |
| |
| detach(); |
| |
| if (dataHandle[0]->_isAsciiValid){ |
| memmove( dataHandle[0]->ascii()+index, dataHandle[0]->ascii()+index+len, |
| sizeof(char)*(olen-index-len) ); |
| setLength( olen-len ); |
| dataHandle[0]->_isUnicodeValid = 0; |
| } |
| else if (dataHandle[0]->_isUnicodeValid){ |
| memmove( dataHandle[0]->unicode()+index, dataHandle[0]->unicode()+index+len, |
| sizeof(DeprecatedChar)*(olen-index-len) ); |
| setLength( olen-len ); |
| } |
| else |
| FATAL("invalid character cache"); |
| } |
| return *this; |
| } |
| |
| DeprecatedString &DeprecatedString::replace(unsigned index, unsigned len, const DeprecatedString& str) |
| { |
| return remove(index, len).insert(index, str); |
| } |
| |
| DeprecatedString &DeprecatedString::replace(char pattern, const DeprecatedString &str) |
| { |
| int slen = str.dataHandle[0]->_length; |
| int index = 0; |
| while ((index = find(pattern, index)) >= 0) { |
| replace(index, 1, str); |
| index += slen; |
| } |
| return *this; |
| } |
| |
| DeprecatedString &DeprecatedString::replace(DeprecatedChar pattern, const DeprecatedString &str) |
| { |
| int slen = str.dataHandle[0]->_length; |
| int index = 0; |
| while ((index = find(pattern, index)) >= 0) { |
| replace(index, 1, str); |
| index += slen; |
| } |
| return *this; |
| } |
| |
| DeprecatedString &DeprecatedString::replace(const DeprecatedString &pattern, const DeprecatedString &str) |
| { |
| if (pattern.isEmpty()) |
| return *this; |
| int plen = pattern.dataHandle[0]->_length; |
| int slen = str.dataHandle[0]->_length; |
| int index = 0; |
| while ((index = find(pattern, index)) >= 0) { |
| replace(index, plen, str); |
| index += slen; |
| } |
| return *this; |
| } |
| |
| |
| DeprecatedString &DeprecatedString::replace(const RegularExpression &qre, const DeprecatedString &str) |
| { |
| if ( isEmpty() ) |
| return *this; |
| int index = 0; |
| int slen = str.dataHandle[0]->_length; |
| int len; |
| while ( index < (int)dataHandle[0]->_length ) { |
| index = qre.match( *this, index, &len); |
| if ( index >= 0 ) { |
| replace( index, len, str ); |
| index += slen; |
| if ( !len ) |
| break; // Avoid infinite loop on 0-length matches, e.g. [a-z]* |
| } |
| else |
| break; |
| } |
| return *this; |
| } |
| |
| |
| DeprecatedString &DeprecatedString::replace(DeprecatedChar oldChar, DeprecatedChar newChar) |
| { |
| if (oldChar != newChar && find(oldChar) != -1) { |
| unsigned length = dataHandle[0]->_length; |
| |
| detach(); |
| if (dataHandle[0]->_isAsciiValid && IS_ASCII_QCHAR(newChar)) { |
| char *p = const_cast<char *>(ascii()); |
| dataHandle[0]->_isUnicodeValid = 0; |
| char oldC = oldChar.unicode(); |
| char newC = newChar.unicode(); |
| for (unsigned i = 0; i != length; ++i) { |
| if (p[i] == oldC) { |
| p[i] = newC; |
| } |
| } |
| } else { |
| DeprecatedChar *p = const_cast<DeprecatedChar *>(unicode()); |
| dataHandle[0]->_isAsciiValid = 0; |
| for (unsigned i = 0; i != length; ++i) { |
| if (p[i] == oldChar) { |
| p[i] = newChar; |
| } |
| } |
| } |
| } |
| |
| return *this; |
| } |
| |
| |
| DeprecatedChar *DeprecatedString::forceUnicode() |
| { |
| detach(); |
| DeprecatedChar *result = const_cast<DeprecatedChar *>(unicode()); |
| dataHandle[0]->_isAsciiValid = 0; |
| return result; |
| } |
| |
| |
| // Increase buffer size if necessary. Newly allocated |
| // bytes will contain garbage. |
| void DeprecatedString::setLength(unsigned newLen) |
| { |
| if (newLen == 0) { |
| setUnicode(0, 0); |
| return; |
| } |
| |
| // Missing optimization: Could avoid copying characters we are going to remove |
| // by making a special version of detach(). |
| detach(); |
| |
| ASSERT(dataHandle != shared_null_handle); |
| |
| if (dataHandle[0]->_isAsciiValid){ |
| if (newLen+1 > dataHandle[0]->_maxAscii) { |
| dataHandle[0]->increaseAsciiSize(newLen+1); |
| } |
| // Ensure null termination, although newly allocated |
| // bytes contain garbage. |
| dataHandle[0]->_ascii[newLen] = 0; |
| } |
| |
| if (dataHandle[0]->_isUnicodeValid){ |
| if (newLen > dataHandle[0]->_maxUnicode) { |
| dataHandle[0]->increaseUnicodeSize(newLen); |
| } |
| } |
| |
| dataHandle[0]->_length = newLen; |
| } |
| |
| |
| void DeprecatedString::truncate(unsigned newLen) |
| { |
| if ( newLen < dataHandle[0]->_length ) |
| setLength( newLen ); |
| } |
| |
| void DeprecatedString::fill(DeprecatedChar qc, int len) |
| { |
| detachAndDiscardCharacters(); |
| |
| // len == -1 means fill to string length. |
| if (len < 0) { |
| len = dataHandle[0]->_length; |
| } |
| |
| if (len == 0) { |
| if (dataHandle != shared_null_handle) { |
| ASSERT(dataHandle[0]->refCount == 1); |
| deref(); |
| freeHandle(dataHandle); |
| dataHandle = makeSharedNullHandle(); |
| shared_null->ref(); |
| } |
| } else { |
| if (dataHandle[0]->_isAsciiValid && IS_ASCII_QCHAR(qc)) { |
| setLength(len); |
| char *nd = const_cast<char*>(ascii()); |
| while (len--) |
| *nd++ = qc.unicode(); |
| dataHandle[0]->_isUnicodeValid = 0; |
| } else { |
| setLength(len); |
| DeprecatedChar *nd = forceUnicode(); |
| while (len--) |
| *nd++ = qc; |
| } |
| } |
| } |
| |
| DeprecatedString &DeprecatedString::append(DeprecatedChar qc) |
| { |
| detach(); |
| |
| DeprecatedStringData *thisData = *dataHandle; |
| if (thisData->_isUnicodeValid && thisData->_length + 1 < thisData->_maxUnicode){ |
| thisData->_unicode[thisData->_length] = qc; |
| thisData->_length++; |
| thisData->_isAsciiValid = 0; |
| return *this; |
| } |
| else if (thisData->_isAsciiValid && IS_ASCII_QCHAR(qc) && thisData->_length + 2 < thisData->_maxAscii){ |
| thisData->_ascii[thisData->_length] = qc.unicode(); |
| thisData->_length++; |
| thisData->_ascii[thisData->_length] = 0; |
| thisData->_isUnicodeValid = 0; |
| return *this; |
| } |
| return insert(thisData->_length, qc); |
| } |
| |
| DeprecatedString &DeprecatedString::append(char ch) |
| { |
| detach(); |
| |
| DeprecatedStringData *thisData = *dataHandle; |
| if (thisData->_isUnicodeValid && thisData->_length + 1 < thisData->_maxUnicode){ |
| thisData->_unicode[thisData->_length] = (DeprecatedChar)ch; |
| thisData->_length++; |
| thisData->_isAsciiValid = 0; |
| return *this; |
| } |
| else if (thisData->_isAsciiValid && thisData->_length + 2 < thisData->_maxAscii){ |
| thisData->_ascii[thisData->_length] = ch; |
| thisData->_length++; |
| thisData->_ascii[thisData->_length] = 0; |
| thisData->_isUnicodeValid = 0; |
| return *this; |
| } |
| return insert(thisData->_length, ch); |
| } |
| |
| void DeprecatedString::reserve(unsigned length) |
| { |
| if (length > dataHandle[0]->_maxUnicode) { |
| detach(); |
| dataHandle[0]->increaseUnicodeSize(length); |
| } |
| } |
| |
| bool operator==(const DeprecatedString &s1, const DeprecatedString &s2) |
| { |
| if (s1.dataHandle[0]->_isAsciiValid && s2.dataHandle[0]->_isAsciiValid) { |
| return strcmp(s1.ascii(), s2.ascii()) == 0; |
| } |
| return s1.dataHandle[0]->_length == s2.dataHandle[0]->_length |
| && memcmp(s1.unicode(), s2.unicode(), s1.dataHandle[0]->_length * sizeof(DeprecatedChar)) == 0; |
| } |
| |
| bool operator==(const DeprecatedString &s1, const char *chs) |
| { |
| if (!chs) |
| return s1.isNull(); |
| DeprecatedStringData *d = s1.dataHandle[0]; |
| unsigned len = d->_length; |
| if (d->_isAsciiValid) { |
| const char *s = s1.ascii(); |
| for (unsigned i = 0; i != len; ++i) { |
| char c = chs[i]; |
| if (!c || s[i] != c) |
| return false; |
| } |
| } else { |
| const DeprecatedChar *s = s1.unicode(); |
| for (unsigned i = 0; i != len; ++i) { |
| char c = chs[i]; |
| if (!c || s[i] != c) |
| return false; |
| } |
| } |
| return chs[len] == '\0'; |
| } |
| |
| DeprecatedString operator+(const DeprecatedString &qs1, const DeprecatedString &qs2) |
| { |
| return DeprecatedString(qs1) += qs2; |
| } |
| |
| DeprecatedString operator+(const DeprecatedString &qs, const char *chs) |
| { |
| return DeprecatedString(qs) += chs; |
| } |
| |
| DeprecatedString operator+(const DeprecatedString &qs, DeprecatedChar qc) |
| { |
| return DeprecatedString(qs) += qc; |
| } |
| |
| DeprecatedString operator+(const DeprecatedString &qs, char ch) |
| { |
| return DeprecatedString(qs) += ch; |
| } |
| |
| DeprecatedString operator+(const char *chs, const DeprecatedString &qs) |
| { |
| return DeprecatedString(chs) += qs; |
| } |
| |
| DeprecatedString operator+(DeprecatedChar qc, const DeprecatedString &qs) |
| { |
| return DeprecatedString(qc) += qs; |
| } |
| |
| DeprecatedString operator+(char ch, const DeprecatedString &qs) |
| { |
| return DeprecatedString(DeprecatedChar(ch)) += qs; |
| } |
| |
| DeprecatedConstString::DeprecatedConstString(const DeprecatedChar* unicode, unsigned length) : |
| DeprecatedString(new DeprecatedStringData((DeprecatedChar *)unicode, length, length), true) |
| { |
| } |
| |
| DeprecatedConstString::~DeprecatedConstString() |
| { |
| DeprecatedStringData *data = *dataHandle; |
| if (data->refCount > 1) { |
| DeprecatedChar *tp; |
| if (data->_length <= WEBCORE_DS_INTERNAL_BUFFER_UCHARS) { |
| data->_maxUnicode = WEBCORE_DS_INTERNAL_BUFFER_UCHARS; |
| tp = (DeprecatedChar *)&data->_internalBuffer[0]; |
| } else { |
| data->_maxUnicode = ALLOC_QCHAR_GOOD_SIZE(data->_length); |
| tp = WEBCORE_ALLOCATE_CHARACTERS(data->_maxUnicode); |
| } |
| memcpy(tp, data->_unicode, data->_length * sizeof(DeprecatedChar)); |
| data->_unicode = tp; |
| data->_isUnicodeValid = 1; |
| data->_isAsciiValid = 0; |
| } else { |
| data->_unicode = 0; |
| } |
| } |
| |
| struct HandlePageNode |
| { |
| HandlePageNode *next; |
| HandlePageNode *previous; |
| void *nodes; |
| }; |
| |
| struct HandleNode { |
| union { |
| struct { |
| unsigned short next; |
| unsigned short previous; |
| } internalNode; |
| |
| HandleNode *freeNodes; // Always at block[0] in page. |
| |
| HandlePageNode *pageNode; // Always at block[1] in page |
| |
| void *handle; |
| } type; |
| }; |
| |
| #ifndef CHECK_FOR_HANDLE_LEAKS |
| |
| static const size_t pageSize = 4096; |
| static const uintptr_t pageMask = ~(pageSize - 1); |
| static const size_t nodeBlockSize = pageSize / sizeof(HandleNode); |
| |
| static HandleNode *initializeHandleNodeBlock(HandlePageNode *pageNode) |
| { |
| unsigned i; |
| HandleNode* block; |
| HandleNode* aNode; |
| |
| #if PLATFORM(WIN_OS) |
| block = (HandleNode*)VirtualAlloc(0, pageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); |
| #elif PLATFORM(SYMBIAN) |
| // symbian::fixme needs to do page aligned allocation as valloc is not supported. |
| block = NULL; |
| #else |
| block = (HandleNode*)valloc(pageSize); |
| #endif |
| |
| for (i = 2; i < nodeBlockSize; i++) { |
| aNode = &block[i]; |
| if (i > 2) |
| aNode->type.internalNode.previous = i-1; |
| else |
| aNode->type.internalNode.previous = 0; |
| if (i != nodeBlockSize - 1) |
| aNode->type.internalNode.next = i+1; |
| else |
| aNode->type.internalNode.next = 0; |
| } |
| block[0].type.freeNodes = &block[nodeBlockSize - 1]; |
| block[1].type.pageNode = pageNode; |
| |
| return block; |
| } |
| |
| static HandlePageNode *allocatePageNode() |
| { |
| HandlePageNode *node = (HandlePageNode *)fastMalloc(sizeof(HandlePageNode)); |
| node->next = node->previous = 0; |
| node->nodes = initializeHandleNodeBlock(node); |
| return node; |
| } |
| |
| static HandleNode *allocateNode(HandlePageNode *pageNode) |
| { |
| HandleNode *block = (HandleNode *)pageNode->nodes; |
| HandleNode *freeNodes = block[0].type.freeNodes; |
| HandleNode *allocated; |
| |
| // Check to see if we're out of nodes. |
| if (freeNodes == 0) { |
| FATAL("out of nodes"); |
| return 0; |
| } |
| |
| // Remove node from end of free list |
| allocated = freeNodes; |
| if (allocated->type.internalNode.previous >= 2) { |
| block[0].type.freeNodes = block + allocated->type.internalNode.previous; |
| block[0].type.freeNodes->type.internalNode.next = 0; |
| } |
| else { |
| // Used last node on this page. |
| block[0].type.freeNodes = 0; |
| |
| freeNodeAllocationPages = freeNodeAllocationPages->previous; |
| if (freeNodeAllocationPages) |
| freeNodeAllocationPages->next = 0; |
| |
| pageNode->previous = usedNodeAllocationPages; |
| pageNode->next = 0; |
| if (usedNodeAllocationPages) |
| usedNodeAllocationPages->next = pageNode; |
| usedNodeAllocationPages = pageNode; |
| } |
| |
| return allocated; |
| } |
| |
| #endif |
| |
| void freeHandle(DeprecatedStringData **_free) |
| { |
| #ifdef CHECK_FOR_HANDLE_LEAKS |
| fastFree(_free); |
| return; |
| #else |
| |
| HandleNode *free = (HandleNode *)_free; |
| HandleNode *base = (HandleNode *)((uintptr_t)free & pageMask); |
| HandleNode *freeNodes = base[0].type.freeNodes; |
| HandlePageNode *pageNode = base[1].type.pageNode; |
| |
| if (freeNodes == 0){ |
| free->type.internalNode.previous = 0; |
| } |
| else { |
| // Insert at head of free list. |
| free->type.internalNode.previous = freeNodes - base; |
| freeNodes->type.internalNode.next = free - base; |
| } |
| free->type.internalNode.next = 0; |
| base[0].type.freeNodes = free; |
| |
| // Remove page from used/free list and place on free list |
| if (freeNodeAllocationPages != pageNode) { |
| if (pageNode->previous) |
| pageNode->previous->next = pageNode->next; |
| if (pageNode->next) |
| pageNode->next->previous = pageNode->previous; |
| if (usedNodeAllocationPages == pageNode) |
| usedNodeAllocationPages = pageNode->previous; |
| |
| pageNode->previous = freeNodeAllocationPages; |
| pageNode->next = 0; |
| if (freeNodeAllocationPages) |
| freeNodeAllocationPages->next = pageNode; |
| freeNodeAllocationPages = pageNode; |
| } |
| #endif |
| } |
| |
| DeprecatedString DeprecatedString::fromUtf8(const char *chs) |
| { |
| return UTF8Encoding().decode(chs, strlen(chs)).deprecatedString(); |
| } |
| |
| DeprecatedString DeprecatedString::fromUtf8(const char *chs, int len) |
| { |
| return UTF8Encoding().decode(chs, len).deprecatedString(); |
| } |
| |
| DeprecatedCString DeprecatedString::utf8(int& length) const |
| { |
| DeprecatedCString result = UTF8Encoding().encode((::UChar*)unicode(), this->length()).deprecatedCString(); |
| length = result.length(); |
| return result; |
| } |
| |
| DeprecatedString::DeprecatedString(const Identifier& str) |
| { |
| if (str.isNull()) { |
| internalData.deref(); |
| dataHandle = makeSharedNullHandle(); |
| dataHandle[0]->ref(); |
| } else { |
| dataHandle = allocateHandle(); |
| *dataHandle = &internalData; |
| internalData.initialize(reinterpret_cast<const DeprecatedChar*>(str.data()), str.size()); |
| } |
| } |
| |
| DeprecatedString::DeprecatedString(const UString& str) |
| { |
| if (str.isNull()) { |
| internalData.deref(); |
| dataHandle = makeSharedNullHandle(); |
| dataHandle[0]->ref(); |
| } else { |
| dataHandle = allocateHandle(); |
| *dataHandle = &internalData; |
| internalData.initialize(reinterpret_cast<const DeprecatedChar*>(str.data()), str.size()); |
| } |
| } |
| |
| #if PLATFORM(QT) |
| DeprecatedString::DeprecatedString(const QString& str) |
| { |
| if (str.isNull()) { |
| internalData.deref(); |
| dataHandle = makeSharedNullHandle(); |
| dataHandle[0]->ref(); |
| } else { |
| dataHandle = allocateHandle(); |
| *dataHandle = &internalData; |
| internalData.initialize(reinterpret_cast<const DeprecatedChar*>(str.data()), str.length()); |
| } |
| } |
| #endif |
| |
| DeprecatedString::operator Identifier() const |
| { |
| if (isNull()) |
| return Identifier(); |
| return Identifier(reinterpret_cast<const KJS::UChar*>(unicode()), length()); |
| } |
| |
| DeprecatedString::operator UString() const |
| { |
| if (isNull()) |
| return UString(); |
| return UString(reinterpret_cast<const KJS::UChar*>(unicode()), length()); |
| } |
| |
| } |