michael@0: /* michael@0: ****************************************************************************** michael@0: * Copyright (C) 1999-2013, International Business Machines Corporation and michael@0: * others. All Rights Reserved. michael@0: ****************************************************************************** michael@0: * michael@0: * File unistr.cpp michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 09/25/98 stephen Creation. michael@0: * 04/20/99 stephen Overhauled per 4/16 code review. michael@0: * 07/09/99 stephen Renamed {hi,lo},{byte,word} to icu_X for HP/UX michael@0: * 11/18/99 aliu Added handleReplaceBetween() to make inherit from michael@0: * Replaceable. michael@0: * 06/25/01 grhoten Removed the dependency on iostream michael@0: ****************************************************************************** michael@0: */ michael@0: michael@0: #include "unicode/utypes.h" michael@0: #include "unicode/appendable.h" michael@0: #include "unicode/putil.h" michael@0: #include "cstring.h" michael@0: #include "cmemory.h" michael@0: #include "unicode/ustring.h" michael@0: #include "unicode/unistr.h" michael@0: #include "unicode/utf.h" michael@0: #include "unicode/utf16.h" michael@0: #include "uelement.h" michael@0: #include "ustr_imp.h" michael@0: #include "umutex.h" michael@0: #include "uassert.h" michael@0: michael@0: #if 0 michael@0: michael@0: #include michael@0: using namespace std; michael@0: michael@0: //DEBUGGING michael@0: void michael@0: print(const UnicodeString& s, michael@0: const char *name) michael@0: { michael@0: UChar c; michael@0: cout << name << ":|"; michael@0: for(int i = 0; i < s.length(); ++i) { michael@0: c = s[i]; michael@0: if(c>= 0x007E || c < 0x0020) michael@0: cout << "[0x" << hex << s[i] << "]"; michael@0: else michael@0: cout << (char) s[i]; michael@0: } michael@0: cout << '|' << endl; michael@0: } michael@0: michael@0: void michael@0: print(const UChar *s, michael@0: int32_t len, michael@0: const char *name) michael@0: { michael@0: UChar c; michael@0: cout << name << ":|"; michael@0: for(int i = 0; i < len; ++i) { michael@0: c = s[i]; michael@0: if(c>= 0x007E || c < 0x0020) michael@0: cout << "[0x" << hex << s[i] << "]"; michael@0: else michael@0: cout << (char) s[i]; michael@0: } michael@0: cout << '|' << endl; michael@0: } michael@0: // END DEBUGGING michael@0: #endif michael@0: michael@0: // Local function definitions for now michael@0: michael@0: // need to copy areas that may overlap michael@0: static michael@0: inline void michael@0: us_arrayCopy(const UChar *src, int32_t srcStart, michael@0: UChar *dst, int32_t dstStart, int32_t count) michael@0: { michael@0: if(count>0) { michael@0: uprv_memmove(dst+dstStart, src+srcStart, (size_t)(count*sizeof(*src))); michael@0: } michael@0: } michael@0: michael@0: // u_unescapeAt() callback to get a UChar from a UnicodeString michael@0: U_CDECL_BEGIN michael@0: static UChar U_CALLCONV michael@0: UnicodeString_charAt(int32_t offset, void *context) { michael@0: return ((icu::UnicodeString*) context)->charAt(offset); michael@0: } michael@0: U_CDECL_END michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: /* The Replaceable virtual destructor can't be defined in the header michael@0: due to how AIX works with multiple definitions of virtual functions. michael@0: */ michael@0: Replaceable::~Replaceable() {} michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UnicodeString) michael@0: michael@0: UnicodeString U_EXPORT2 michael@0: operator+ (const UnicodeString &s1, const UnicodeString &s2) { michael@0: return michael@0: UnicodeString(s1.length()+s2.length()+1, (UChar32)0, 0). michael@0: append(s1). michael@0: append(s2); michael@0: } michael@0: michael@0: //======================================== michael@0: // Reference Counting functions, put at top of file so that optimizing compilers michael@0: // have a chance to automatically inline. michael@0: //======================================== michael@0: michael@0: void michael@0: UnicodeString::addRef() { michael@0: umtx_atomic_inc((u_atomic_int32_t *)fUnion.fFields.fArray - 1); michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::removeRef() { michael@0: return umtx_atomic_dec((u_atomic_int32_t *)fUnion.fFields.fArray - 1); michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::refCount() const { michael@0: return umtx_loadAcquire(*((u_atomic_int32_t *)fUnion.fFields.fArray - 1)); michael@0: } michael@0: michael@0: void michael@0: UnicodeString::releaseArray() { michael@0: if((fFlags & kRefCounted) && removeRef() == 0) { michael@0: uprv_free((int32_t *)fUnion.fFields.fArray - 1); michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: //======================================== michael@0: // Constructors michael@0: //======================================== michael@0: michael@0: // The default constructor is inline in unistr.h. michael@0: michael@0: UnicodeString::UnicodeString(int32_t capacity, UChar32 c, int32_t count) michael@0: : fShortLength(0), michael@0: fFlags(0) michael@0: { michael@0: if(count <= 0 || (uint32_t)c > 0x10ffff) { michael@0: // just allocate and do not do anything else michael@0: allocate(capacity); michael@0: } else { michael@0: // count > 0, allocate and fill the new string with count c's michael@0: int32_t unitCount = U16_LENGTH(c), length = count * unitCount; michael@0: if(capacity < length) { michael@0: capacity = length; michael@0: } michael@0: if(allocate(capacity)) { michael@0: UChar *array = getArrayStart(); michael@0: int32_t i = 0; michael@0: michael@0: // fill the new string with c michael@0: if(unitCount == 1) { michael@0: // fill with length UChars michael@0: while(i < length) { michael@0: array[i++] = (UChar)c; michael@0: } michael@0: } else { michael@0: // get the code units for c michael@0: UChar units[U16_MAX_LENGTH]; michael@0: U16_APPEND_UNSAFE(units, i, c); michael@0: michael@0: // now it must be i==unitCount michael@0: i = 0; michael@0: michael@0: // for Unicode, unitCount can only be 1, 2, 3, or 4 michael@0: // 1 is handled above michael@0: while(i < length) { michael@0: int32_t unitIdx = 0; michael@0: while(unitIdx < unitCount) { michael@0: array[i++]=units[unitIdx++]; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: setLength(length); michael@0: } michael@0: } michael@0: michael@0: UnicodeString::UnicodeString(UChar ch) michael@0: : fShortLength(1), michael@0: fFlags(kShortString) michael@0: { michael@0: fUnion.fStackBuffer[0] = ch; michael@0: } michael@0: michael@0: UnicodeString::UnicodeString(UChar32 ch) michael@0: : fShortLength(0), michael@0: fFlags(kShortString) michael@0: { michael@0: int32_t i = 0; michael@0: UBool isError = FALSE; michael@0: U16_APPEND(fUnion.fStackBuffer, i, US_STACKBUF_SIZE, ch, isError); michael@0: // We test isError so that the compiler does not complain that we don't. michael@0: // If isError then i==0 which is what we want anyway. michael@0: if(!isError) { michael@0: fShortLength = (int8_t)i; michael@0: } michael@0: } michael@0: michael@0: UnicodeString::UnicodeString(const UChar *text) michael@0: : fShortLength(0), michael@0: fFlags(kShortString) michael@0: { michael@0: doReplace(0, 0, text, 0, -1); michael@0: } michael@0: michael@0: UnicodeString::UnicodeString(const UChar *text, michael@0: int32_t textLength) michael@0: : fShortLength(0), michael@0: fFlags(kShortString) michael@0: { michael@0: doReplace(0, 0, text, 0, textLength); michael@0: } michael@0: michael@0: UnicodeString::UnicodeString(UBool isTerminated, michael@0: const UChar *text, michael@0: int32_t textLength) michael@0: : fShortLength(0), michael@0: fFlags(kReadonlyAlias) michael@0: { michael@0: if(text == NULL) { michael@0: // treat as an empty string, do not alias michael@0: setToEmpty(); michael@0: } else if(textLength < -1 || michael@0: (textLength == -1 && !isTerminated) || michael@0: (textLength >= 0 && isTerminated && text[textLength] != 0) michael@0: ) { michael@0: setToBogus(); michael@0: } else { michael@0: if(textLength == -1) { michael@0: // text is terminated, or else it would have failed the above test michael@0: textLength = u_strlen(text); michael@0: } michael@0: setArray((UChar *)text, textLength, isTerminated ? textLength + 1 : textLength); michael@0: } michael@0: } michael@0: michael@0: UnicodeString::UnicodeString(UChar *buff, michael@0: int32_t buffLength, michael@0: int32_t buffCapacity) michael@0: : fShortLength(0), michael@0: fFlags(kWritableAlias) michael@0: { michael@0: if(buff == NULL) { michael@0: // treat as an empty string, do not alias michael@0: setToEmpty(); michael@0: } else if(buffLength < -1 || buffCapacity < 0 || buffLength > buffCapacity) { michael@0: setToBogus(); michael@0: } else { michael@0: if(buffLength == -1) { michael@0: // fLength = u_strlen(buff); but do not look beyond buffCapacity michael@0: const UChar *p = buff, *limit = buff + buffCapacity; michael@0: while(p != limit && *p != 0) { michael@0: ++p; michael@0: } michael@0: buffLength = (int32_t)(p - buff); michael@0: } michael@0: setArray(buff, buffLength, buffCapacity); michael@0: } michael@0: } michael@0: michael@0: UnicodeString::UnicodeString(const char *src, int32_t length, EInvariant) michael@0: : fShortLength(0), michael@0: fFlags(kShortString) michael@0: { michael@0: if(src==NULL) { michael@0: // treat as an empty string michael@0: } else { michael@0: if(length<0) { michael@0: length=(int32_t)uprv_strlen(src); michael@0: } michael@0: if(cloneArrayIfNeeded(length, length, FALSE)) { michael@0: u_charsToUChars(src, getArrayStart(), length); michael@0: setLength(length); michael@0: } else { michael@0: setToBogus(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: #if U_CHARSET_IS_UTF8 michael@0: michael@0: UnicodeString::UnicodeString(const char *codepageData) michael@0: : fShortLength(0), michael@0: fFlags(kShortString) { michael@0: if(codepageData != 0) { michael@0: setToUTF8(codepageData); michael@0: } michael@0: } michael@0: michael@0: UnicodeString::UnicodeString(const char *codepageData, int32_t dataLength) michael@0: : fShortLength(0), michael@0: fFlags(kShortString) { michael@0: // if there's nothing to convert, do nothing michael@0: if(codepageData == 0 || dataLength == 0 || dataLength < -1) { michael@0: return; michael@0: } michael@0: if(dataLength == -1) { michael@0: dataLength = (int32_t)uprv_strlen(codepageData); michael@0: } michael@0: setToUTF8(StringPiece(codepageData, dataLength)); michael@0: } michael@0: michael@0: // else see unistr_cnv.cpp michael@0: #endif michael@0: michael@0: UnicodeString::UnicodeString(const UnicodeString& that) michael@0: : Replaceable(), michael@0: fShortLength(0), michael@0: fFlags(kShortString) michael@0: { michael@0: copyFrom(that); michael@0: } michael@0: michael@0: UnicodeString::UnicodeString(const UnicodeString& that, michael@0: int32_t srcStart) michael@0: : Replaceable(), michael@0: fShortLength(0), michael@0: fFlags(kShortString) michael@0: { michael@0: setTo(that, srcStart); michael@0: } michael@0: michael@0: UnicodeString::UnicodeString(const UnicodeString& that, michael@0: int32_t srcStart, michael@0: int32_t srcLength) michael@0: : Replaceable(), michael@0: fShortLength(0), michael@0: fFlags(kShortString) michael@0: { michael@0: setTo(that, srcStart, srcLength); michael@0: } michael@0: michael@0: // Replaceable base class clone() default implementation, does not clone michael@0: Replaceable * michael@0: Replaceable::clone() const { michael@0: return NULL; michael@0: } michael@0: michael@0: // UnicodeString overrides clone() with a real implementation michael@0: Replaceable * michael@0: UnicodeString::clone() const { michael@0: return new UnicodeString(*this); michael@0: } michael@0: michael@0: //======================================== michael@0: // array allocation michael@0: //======================================== michael@0: michael@0: UBool michael@0: UnicodeString::allocate(int32_t capacity) { michael@0: if(capacity <= US_STACKBUF_SIZE) { michael@0: fFlags = kShortString; michael@0: } else { michael@0: // count bytes for the refCounter and the string capacity, and michael@0: // round up to a multiple of 16; then divide by 4 and allocate int32_t's michael@0: // to be safely aligned for the refCount michael@0: // the +1 is for the NUL terminator, to avoid reallocation in getTerminatedBuffer() michael@0: int32_t words = (int32_t)(((sizeof(int32_t) + (capacity + 1) * U_SIZEOF_UCHAR + 15) & ~15) >> 2); michael@0: int32_t *array = (int32_t*) uprv_malloc( sizeof(int32_t) * words ); michael@0: if(array != 0) { michael@0: // set initial refCount and point behind the refCount michael@0: *array++ = 1; michael@0: michael@0: // have fArray point to the first UChar michael@0: fUnion.fFields.fArray = (UChar *)array; michael@0: fUnion.fFields.fCapacity = (int32_t)((words - 1) * (sizeof(int32_t) / U_SIZEOF_UCHAR)); michael@0: fFlags = kLongString; michael@0: } else { michael@0: fShortLength = 0; michael@0: fUnion.fFields.fArray = 0; michael@0: fUnion.fFields.fCapacity = 0; michael@0: fFlags = kIsBogus; michael@0: return FALSE; michael@0: } michael@0: } michael@0: return TRUE; michael@0: } michael@0: michael@0: //======================================== michael@0: // Destructor michael@0: //======================================== michael@0: UnicodeString::~UnicodeString() michael@0: { michael@0: releaseArray(); michael@0: } michael@0: michael@0: //======================================== michael@0: // Factory methods michael@0: //======================================== michael@0: michael@0: UnicodeString UnicodeString::fromUTF8(const StringPiece &utf8) { michael@0: UnicodeString result; michael@0: result.setToUTF8(utf8); michael@0: return result; michael@0: } michael@0: michael@0: UnicodeString UnicodeString::fromUTF32(const UChar32 *utf32, int32_t length) { michael@0: UnicodeString result; michael@0: int32_t capacity; michael@0: // Most UTF-32 strings will be BMP-only and result in a same-length michael@0: // UTF-16 string. We overestimate the capacity just slightly, michael@0: // just in case there are a few supplementary characters. michael@0: if(length <= US_STACKBUF_SIZE) { michael@0: capacity = US_STACKBUF_SIZE; michael@0: } else { michael@0: capacity = length + (length >> 4) + 4; michael@0: } michael@0: do { michael@0: UChar *utf16 = result.getBuffer(capacity); michael@0: int32_t length16; michael@0: UErrorCode errorCode = U_ZERO_ERROR; michael@0: u_strFromUTF32WithSub(utf16, result.getCapacity(), &length16, michael@0: utf32, length, michael@0: 0xfffd, // Substitution character. michael@0: NULL, // Don't care about number of substitutions. michael@0: &errorCode); michael@0: result.releaseBuffer(length16); michael@0: if(errorCode == U_BUFFER_OVERFLOW_ERROR) { michael@0: capacity = length16 + 1; // +1 for the terminating NUL. michael@0: continue; michael@0: } else if(U_FAILURE(errorCode)) { michael@0: result.setToBogus(); michael@0: } michael@0: break; michael@0: } while(TRUE); michael@0: return result; michael@0: } michael@0: michael@0: //======================================== michael@0: // Assignment michael@0: //======================================== michael@0: michael@0: UnicodeString & michael@0: UnicodeString::operator=(const UnicodeString &src) { michael@0: return copyFrom(src); michael@0: } michael@0: michael@0: UnicodeString & michael@0: UnicodeString::fastCopyFrom(const UnicodeString &src) { michael@0: return copyFrom(src, TRUE); michael@0: } michael@0: michael@0: UnicodeString & michael@0: UnicodeString::copyFrom(const UnicodeString &src, UBool fastCopy) { michael@0: // if assigning to ourselves, do nothing michael@0: if(this == 0 || this == &src) { michael@0: return *this; michael@0: } michael@0: michael@0: // is the right side bogus? michael@0: if(&src == 0 || src.isBogus()) { michael@0: setToBogus(); michael@0: return *this; michael@0: } michael@0: michael@0: // delete the current contents michael@0: releaseArray(); michael@0: michael@0: if(src.isEmpty()) { michael@0: // empty string - use the stack buffer michael@0: setToEmpty(); michael@0: return *this; michael@0: } michael@0: michael@0: // we always copy the length michael@0: int32_t srcLength = src.length(); michael@0: setLength(srcLength); michael@0: michael@0: // fLength>0 and not an "open" src.getBuffer(minCapacity) michael@0: switch(src.fFlags) { michael@0: case kShortString: michael@0: // short string using the stack buffer, do the same michael@0: fFlags = kShortString; michael@0: uprv_memcpy(fUnion.fStackBuffer, src.fUnion.fStackBuffer, srcLength * U_SIZEOF_UCHAR); michael@0: break; michael@0: case kLongString: michael@0: // src uses a refCounted string buffer, use that buffer with refCount michael@0: // src is const, use a cast - we don't really change it michael@0: ((UnicodeString &)src).addRef(); michael@0: // copy all fields, share the reference-counted buffer michael@0: fUnion.fFields.fArray = src.fUnion.fFields.fArray; michael@0: fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; michael@0: fFlags = src.fFlags; michael@0: break; michael@0: case kReadonlyAlias: michael@0: if(fastCopy) { michael@0: // src is a readonly alias, do the same michael@0: // -> maintain the readonly alias as such michael@0: fUnion.fFields.fArray = src.fUnion.fFields.fArray; michael@0: fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; michael@0: fFlags = src.fFlags; michael@0: break; michael@0: } michael@0: // else if(!fastCopy) fall through to case kWritableAlias michael@0: // -> allocate a new buffer and copy the contents michael@0: case kWritableAlias: michael@0: // src is a writable alias; we make a copy of that instead michael@0: if(allocate(srcLength)) { michael@0: uprv_memcpy(getArrayStart(), src.getArrayStart(), srcLength * U_SIZEOF_UCHAR); michael@0: break; michael@0: } michael@0: // if there is not enough memory, then fall through to setting to bogus michael@0: default: michael@0: // if src is bogus, set ourselves to bogus michael@0: // do not call setToBogus() here because fArray and fFlags are not consistent here michael@0: fShortLength = 0; michael@0: fUnion.fFields.fArray = 0; michael@0: fUnion.fFields.fCapacity = 0; michael@0: fFlags = kIsBogus; michael@0: break; michael@0: } michael@0: michael@0: return *this; michael@0: } michael@0: michael@0: //======================================== michael@0: // Miscellaneous operations michael@0: //======================================== michael@0: michael@0: UnicodeString UnicodeString::unescape() const { michael@0: UnicodeString result(length(), (UChar32)0, (int32_t)0); // construct with capacity michael@0: const UChar *array = getBuffer(); michael@0: int32_t len = length(); michael@0: int32_t prev = 0; michael@0: for (int32_t i=0;;) { michael@0: if (i == len) { michael@0: result.append(array, prev, len - prev); michael@0: break; michael@0: } michael@0: if (array[i++] == 0x5C /*'\\'*/) { michael@0: result.append(array, prev, (i - 1) - prev); michael@0: UChar32 c = unescapeAt(i); // advances i michael@0: if (c < 0) { michael@0: result.remove(); // return empty string michael@0: break; // invalid escape sequence michael@0: } michael@0: result.append(c); michael@0: prev = i; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: UChar32 UnicodeString::unescapeAt(int32_t &offset) const { michael@0: return u_unescapeAt(UnicodeString_charAt, &offset, length(), (void*)this); michael@0: } michael@0: michael@0: //======================================== michael@0: // Read-only implementation michael@0: //======================================== michael@0: UBool michael@0: UnicodeString::doEquals(const UnicodeString &text, int32_t len) const { michael@0: // Requires: this & text not bogus and have same lengths. michael@0: // Byte-wise comparison works for equality regardless of endianness. michael@0: return uprv_memcmp(getArrayStart(), text.getArrayStart(), len * U_SIZEOF_UCHAR) == 0; michael@0: } michael@0: michael@0: int8_t michael@0: UnicodeString::doCompare( int32_t start, michael@0: int32_t length, michael@0: const UChar *srcChars, michael@0: int32_t srcStart, michael@0: int32_t srcLength) const michael@0: { michael@0: // compare illegal string values michael@0: if(isBogus()) { michael@0: return -1; michael@0: } michael@0: michael@0: // pin indices to legal values michael@0: pinIndices(start, length); michael@0: michael@0: if(srcChars == NULL) { michael@0: // treat const UChar *srcChars==NULL as an empty string michael@0: return length == 0 ? 0 : 1; michael@0: } michael@0: michael@0: // get the correct pointer michael@0: const UChar *chars = getArrayStart(); michael@0: michael@0: chars += start; michael@0: srcChars += srcStart; michael@0: michael@0: int32_t minLength; michael@0: int8_t lengthResult; michael@0: michael@0: // get the srcLength if necessary michael@0: if(srcLength < 0) { michael@0: srcLength = u_strlen(srcChars + srcStart); michael@0: } michael@0: michael@0: // are we comparing different lengths? michael@0: if(length != srcLength) { michael@0: if(length < srcLength) { michael@0: minLength = length; michael@0: lengthResult = -1; michael@0: } else { michael@0: minLength = srcLength; michael@0: lengthResult = 1; michael@0: } michael@0: } else { michael@0: minLength = length; michael@0: lengthResult = 0; michael@0: } michael@0: michael@0: /* michael@0: * note that uprv_memcmp() returns an int but we return an int8_t; michael@0: * we need to take care not to truncate the result - michael@0: * one way to do this is to right-shift the value to michael@0: * move the sign bit into the lower 8 bits and making sure that this michael@0: * does not become 0 itself michael@0: */ michael@0: michael@0: if(minLength > 0 && chars != srcChars) { michael@0: int32_t result; michael@0: michael@0: # if U_IS_BIG_ENDIAN michael@0: // big-endian: byte comparison works michael@0: result = uprv_memcmp(chars, srcChars, minLength * sizeof(UChar)); michael@0: if(result != 0) { michael@0: return (int8_t)(result >> 15 | 1); michael@0: } michael@0: # else michael@0: // little-endian: compare UChar units michael@0: do { michael@0: result = ((int32_t)*(chars++) - (int32_t)*(srcChars++)); michael@0: if(result != 0) { michael@0: return (int8_t)(result >> 15 | 1); michael@0: } michael@0: } while(--minLength > 0); michael@0: # endif michael@0: } michael@0: return lengthResult; michael@0: } michael@0: michael@0: /* String compare in code point order - doCompare() compares in code unit order. */ michael@0: int8_t michael@0: UnicodeString::doCompareCodePointOrder(int32_t start, michael@0: int32_t length, michael@0: const UChar *srcChars, michael@0: int32_t srcStart, michael@0: int32_t srcLength) const michael@0: { michael@0: // compare illegal string values michael@0: // treat const UChar *srcChars==NULL as an empty string michael@0: if(isBogus()) { michael@0: return -1; michael@0: } michael@0: michael@0: // pin indices to legal values michael@0: pinIndices(start, length); michael@0: michael@0: if(srcChars == NULL) { michael@0: srcStart = srcLength = 0; michael@0: } michael@0: michael@0: int32_t diff = uprv_strCompare(getArrayStart() + start, length, (srcChars!=NULL)?(srcChars + srcStart):NULL, srcLength, FALSE, TRUE); michael@0: /* translate the 32-bit result into an 8-bit one */ michael@0: if(diff!=0) { michael@0: return (int8_t)(diff >> 15 | 1); michael@0: } else { michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::getLength() const { michael@0: return length(); michael@0: } michael@0: michael@0: UChar michael@0: UnicodeString::getCharAt(int32_t offset) const { michael@0: return charAt(offset); michael@0: } michael@0: michael@0: UChar32 michael@0: UnicodeString::getChar32At(int32_t offset) const { michael@0: return char32At(offset); michael@0: } michael@0: michael@0: UChar32 michael@0: UnicodeString::char32At(int32_t offset) const michael@0: { michael@0: int32_t len = length(); michael@0: if((uint32_t)offset < (uint32_t)len) { michael@0: const UChar *array = getArrayStart(); michael@0: UChar32 c; michael@0: U16_GET(array, 0, offset, len, c); michael@0: return c; michael@0: } else { michael@0: return kInvalidUChar; michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::getChar32Start(int32_t offset) const { michael@0: if((uint32_t)offset < (uint32_t)length()) { michael@0: const UChar *array = getArrayStart(); michael@0: U16_SET_CP_START(array, 0, offset); michael@0: return offset; michael@0: } else { michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::getChar32Limit(int32_t offset) const { michael@0: int32_t len = length(); michael@0: if((uint32_t)offset < (uint32_t)len) { michael@0: const UChar *array = getArrayStart(); michael@0: U16_SET_CP_LIMIT(array, 0, offset, len); michael@0: return offset; michael@0: } else { michael@0: return len; michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::countChar32(int32_t start, int32_t length) const { michael@0: pinIndices(start, length); michael@0: // if(isBogus()) then fArray==0 and start==0 - u_countChar32() checks for NULL michael@0: return u_countChar32(getArrayStart()+start, length); michael@0: } michael@0: michael@0: UBool michael@0: UnicodeString::hasMoreChar32Than(int32_t start, int32_t length, int32_t number) const { michael@0: pinIndices(start, length); michael@0: // if(isBogus()) then fArray==0 and start==0 - u_strHasMoreChar32Than() checks for NULL michael@0: return u_strHasMoreChar32Than(getArrayStart()+start, length, number); michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::moveIndex32(int32_t index, int32_t delta) const { michael@0: // pin index michael@0: int32_t len = length(); michael@0: if(index<0) { michael@0: index=0; michael@0: } else if(index>len) { michael@0: index=len; michael@0: } michael@0: michael@0: const UChar *array = getArrayStart(); michael@0: if(delta>0) { michael@0: U16_FWD_N(array, index, len, delta); michael@0: } else { michael@0: U16_BACK_N(array, 0, index, -delta); michael@0: } michael@0: michael@0: return index; michael@0: } michael@0: michael@0: void michael@0: UnicodeString::doExtract(int32_t start, michael@0: int32_t length, michael@0: UChar *dst, michael@0: int32_t dstStart) const michael@0: { michael@0: // pin indices to legal values michael@0: pinIndices(start, length); michael@0: michael@0: // do not copy anything if we alias dst itself michael@0: const UChar *array = getArrayStart(); michael@0: if(array + start != dst + dstStart) { michael@0: us_arrayCopy(array, start, dst, dstStart, length); michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::extract(UChar *dest, int32_t destCapacity, michael@0: UErrorCode &errorCode) const { michael@0: int32_t len = length(); michael@0: if(U_SUCCESS(errorCode)) { michael@0: if(isBogus() || destCapacity<0 || (destCapacity>0 && dest==0)) { michael@0: errorCode=U_ILLEGAL_ARGUMENT_ERROR; michael@0: } else { michael@0: const UChar *array = getArrayStart(); michael@0: if(len>0 && len<=destCapacity && array!=dest) { michael@0: uprv_memcpy(dest, array, len*U_SIZEOF_UCHAR); michael@0: } michael@0: return u_terminateUChars(dest, destCapacity, len, &errorCode); michael@0: } michael@0: } michael@0: michael@0: return len; michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::extract(int32_t start, michael@0: int32_t length, michael@0: char *target, michael@0: int32_t targetCapacity, michael@0: enum EInvariant) const michael@0: { michael@0: // if the arguments are illegal, then do nothing michael@0: if(targetCapacity < 0 || (targetCapacity > 0 && target == NULL)) { michael@0: return 0; michael@0: } michael@0: michael@0: // pin the indices to legal values michael@0: pinIndices(start, length); michael@0: michael@0: if(length <= targetCapacity) { michael@0: u_UCharsToChars(getArrayStart() + start, target, length); michael@0: } michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: return u_terminateChars(target, targetCapacity, length, &status); michael@0: } michael@0: michael@0: UnicodeString michael@0: UnicodeString::tempSubString(int32_t start, int32_t len) const { michael@0: pinIndices(start, len); michael@0: const UChar *array = getBuffer(); // not getArrayStart() to check kIsBogus & kOpenGetBuffer michael@0: if(array==NULL) { michael@0: array=fUnion.fStackBuffer; // anything not NULL because that would make an empty string michael@0: len=-2; // bogus result string michael@0: } michael@0: return UnicodeString(FALSE, array + start, len); michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::toUTF8(int32_t start, int32_t len, michael@0: char *target, int32_t capacity) const { michael@0: pinIndices(start, len); michael@0: int32_t length8; michael@0: UErrorCode errorCode = U_ZERO_ERROR; michael@0: u_strToUTF8WithSub(target, capacity, &length8, michael@0: getBuffer() + start, len, michael@0: 0xFFFD, // Standard substitution character. michael@0: NULL, // Don't care about number of substitutions. michael@0: &errorCode); michael@0: return length8; michael@0: } michael@0: michael@0: #if U_CHARSET_IS_UTF8 michael@0: michael@0: int32_t michael@0: UnicodeString::extract(int32_t start, int32_t len, michael@0: char *target, uint32_t dstSize) const { michael@0: // if the arguments are illegal, then do nothing michael@0: if(/*dstSize < 0 || */(dstSize > 0 && target == 0)) { michael@0: return 0; michael@0: } michael@0: return toUTF8(start, len, target, dstSize <= 0x7fffffff ? (int32_t)dstSize : 0x7fffffff); michael@0: } michael@0: michael@0: // else see unistr_cnv.cpp michael@0: #endif michael@0: michael@0: void michael@0: UnicodeString::extractBetween(int32_t start, michael@0: int32_t limit, michael@0: UnicodeString& target) const { michael@0: pinIndex(start); michael@0: pinIndex(limit); michael@0: doExtract(start, limit - start, target); michael@0: } michael@0: michael@0: // When converting from UTF-16 to UTF-8, the result will have at most 3 times michael@0: // as many bytes as the source has UChars. michael@0: // The "worst cases" are writing systems like Indic, Thai and CJK with michael@0: // 3:1 bytes:UChars. michael@0: void michael@0: UnicodeString::toUTF8(ByteSink &sink) const { michael@0: int32_t length16 = length(); michael@0: if(length16 != 0) { michael@0: char stackBuffer[1024]; michael@0: int32_t capacity = (int32_t)sizeof(stackBuffer); michael@0: UBool utf8IsOwned = FALSE; michael@0: char *utf8 = sink.GetAppendBuffer(length16 < capacity ? length16 : capacity, michael@0: 3*length16, michael@0: stackBuffer, capacity, michael@0: &capacity); michael@0: int32_t length8 = 0; michael@0: UErrorCode errorCode = U_ZERO_ERROR; michael@0: u_strToUTF8WithSub(utf8, capacity, &length8, michael@0: getBuffer(), length16, michael@0: 0xFFFD, // Standard substitution character. michael@0: NULL, // Don't care about number of substitutions. michael@0: &errorCode); michael@0: if(errorCode == U_BUFFER_OVERFLOW_ERROR) { michael@0: utf8 = (char *)uprv_malloc(length8); michael@0: if(utf8 != NULL) { michael@0: utf8IsOwned = TRUE; michael@0: errorCode = U_ZERO_ERROR; michael@0: u_strToUTF8WithSub(utf8, length8, &length8, michael@0: getBuffer(), length16, michael@0: 0xFFFD, // Standard substitution character. michael@0: NULL, // Don't care about number of substitutions. michael@0: &errorCode); michael@0: } else { michael@0: errorCode = U_MEMORY_ALLOCATION_ERROR; michael@0: } michael@0: } michael@0: if(U_SUCCESS(errorCode)) { michael@0: sink.Append(utf8, length8); michael@0: sink.Flush(); michael@0: } michael@0: if(utf8IsOwned) { michael@0: uprv_free(utf8); michael@0: } michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::toUTF32(UChar32 *utf32, int32_t capacity, UErrorCode &errorCode) const { michael@0: int32_t length32=0; michael@0: if(U_SUCCESS(errorCode)) { michael@0: // getBuffer() and u_strToUTF32WithSub() check for illegal arguments. michael@0: u_strToUTF32WithSub(utf32, capacity, &length32, michael@0: getBuffer(), length(), michael@0: 0xfffd, // Substitution character. michael@0: NULL, // Don't care about number of substitutions. michael@0: &errorCode); michael@0: } michael@0: return length32; michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::indexOf(const UChar *srcChars, michael@0: int32_t srcStart, michael@0: int32_t srcLength, michael@0: int32_t start, michael@0: int32_t length) const michael@0: { michael@0: if(isBogus() || srcChars == 0 || srcStart < 0 || srcLength == 0) { michael@0: return -1; michael@0: } michael@0: michael@0: // UnicodeString does not find empty substrings michael@0: if(srcLength < 0 && srcChars[srcStart] == 0) { michael@0: return -1; michael@0: } michael@0: michael@0: // get the indices within bounds michael@0: pinIndices(start, length); michael@0: michael@0: // find the first occurrence of the substring michael@0: const UChar *array = getArrayStart(); michael@0: const UChar *match = u_strFindFirst(array + start, length, srcChars + srcStart, srcLength); michael@0: if(match == NULL) { michael@0: return -1; michael@0: } else { michael@0: return (int32_t)(match - array); michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::doIndexOf(UChar c, michael@0: int32_t start, michael@0: int32_t length) const michael@0: { michael@0: // pin indices michael@0: pinIndices(start, length); michael@0: michael@0: // find the first occurrence of c michael@0: const UChar *array = getArrayStart(); michael@0: const UChar *match = u_memchr(array + start, c, length); michael@0: if(match == NULL) { michael@0: return -1; michael@0: } else { michael@0: return (int32_t)(match - array); michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::doIndexOf(UChar32 c, michael@0: int32_t start, michael@0: int32_t length) const { michael@0: // pin indices michael@0: pinIndices(start, length); michael@0: michael@0: // find the first occurrence of c michael@0: const UChar *array = getArrayStart(); michael@0: const UChar *match = u_memchr32(array + start, c, length); michael@0: if(match == NULL) { michael@0: return -1; michael@0: } else { michael@0: return (int32_t)(match - array); michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::lastIndexOf(const UChar *srcChars, michael@0: int32_t srcStart, michael@0: int32_t srcLength, michael@0: int32_t start, michael@0: int32_t length) const michael@0: { michael@0: if(isBogus() || srcChars == 0 || srcStart < 0 || srcLength == 0) { michael@0: return -1; michael@0: } michael@0: michael@0: // UnicodeString does not find empty substrings michael@0: if(srcLength < 0 && srcChars[srcStart] == 0) { michael@0: return -1; michael@0: } michael@0: michael@0: // get the indices within bounds michael@0: pinIndices(start, length); michael@0: michael@0: // find the last occurrence of the substring michael@0: const UChar *array = getArrayStart(); michael@0: const UChar *match = u_strFindLast(array + start, length, srcChars + srcStart, srcLength); michael@0: if(match == NULL) { michael@0: return -1; michael@0: } else { michael@0: return (int32_t)(match - array); michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::doLastIndexOf(UChar c, michael@0: int32_t start, michael@0: int32_t length) const michael@0: { michael@0: if(isBogus()) { michael@0: return -1; michael@0: } michael@0: michael@0: // pin indices michael@0: pinIndices(start, length); michael@0: michael@0: // find the last occurrence of c michael@0: const UChar *array = getArrayStart(); michael@0: const UChar *match = u_memrchr(array + start, c, length); michael@0: if(match == NULL) { michael@0: return -1; michael@0: } else { michael@0: return (int32_t)(match - array); michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: UnicodeString::doLastIndexOf(UChar32 c, michael@0: int32_t start, michael@0: int32_t length) const { michael@0: // pin indices michael@0: pinIndices(start, length); michael@0: michael@0: // find the last occurrence of c michael@0: const UChar *array = getArrayStart(); michael@0: const UChar *match = u_memrchr32(array + start, c, length); michael@0: if(match == NULL) { michael@0: return -1; michael@0: } else { michael@0: return (int32_t)(match - array); michael@0: } michael@0: } michael@0: michael@0: //======================================== michael@0: // Write implementation michael@0: //======================================== michael@0: michael@0: UnicodeString& michael@0: UnicodeString::findAndReplace(int32_t start, michael@0: int32_t length, michael@0: const UnicodeString& oldText, michael@0: int32_t oldStart, michael@0: int32_t oldLength, michael@0: const UnicodeString& newText, michael@0: int32_t newStart, michael@0: int32_t newLength) michael@0: { michael@0: if(isBogus() || oldText.isBogus() || newText.isBogus()) { michael@0: return *this; michael@0: } michael@0: michael@0: pinIndices(start, length); michael@0: oldText.pinIndices(oldStart, oldLength); michael@0: newText.pinIndices(newStart, newLength); michael@0: michael@0: if(oldLength == 0) { michael@0: return *this; michael@0: } michael@0: michael@0: while(length > 0 && length >= oldLength) { michael@0: int32_t pos = indexOf(oldText, oldStart, oldLength, start, length); michael@0: if(pos < 0) { michael@0: // no more oldText's here: done michael@0: break; michael@0: } else { michael@0: // we found oldText, replace it by newText and go beyond it michael@0: replace(pos, oldLength, newText, newStart, newLength); michael@0: length -= pos + oldLength - start; michael@0: start = pos + newLength; michael@0: } michael@0: } michael@0: michael@0: return *this; michael@0: } michael@0: michael@0: michael@0: void michael@0: UnicodeString::setToBogus() michael@0: { michael@0: releaseArray(); michael@0: michael@0: fShortLength = 0; michael@0: fUnion.fFields.fArray = 0; michael@0: fUnion.fFields.fCapacity = 0; michael@0: fFlags = kIsBogus; michael@0: } michael@0: michael@0: // turn a bogus string into an empty one michael@0: void michael@0: UnicodeString::unBogus() { michael@0: if(fFlags & kIsBogus) { michael@0: setToEmpty(); michael@0: } michael@0: } michael@0: michael@0: const UChar * michael@0: UnicodeString::getTerminatedBuffer() { michael@0: if(!isWritable()) { michael@0: return 0; michael@0: } michael@0: UChar *array = getArrayStart(); michael@0: int32_t len = length(); michael@0: if(len < getCapacity()) { michael@0: if(fFlags & kBufferIsReadonly) { michael@0: // If len= 0 && isTerminated && text[textLength] != 0) michael@0: ) { michael@0: setToBogus(); michael@0: return *this; michael@0: } michael@0: michael@0: releaseArray(); michael@0: michael@0: if(textLength == -1) { michael@0: // text is terminated, or else it would have failed the above test michael@0: textLength = u_strlen(text); michael@0: } michael@0: setArray((UChar *)text, textLength, isTerminated ? textLength + 1 : textLength); michael@0: michael@0: fFlags = kReadonlyAlias; michael@0: return *this; michael@0: } michael@0: michael@0: // setTo() analogous to the writable-aliasing constructor with the same signature michael@0: UnicodeString & michael@0: UnicodeString::setTo(UChar *buffer, michael@0: int32_t buffLength, michael@0: int32_t buffCapacity) { michael@0: if(fFlags & kOpenGetBuffer) { michael@0: // do not modify a string that has an "open" getBuffer(minCapacity) michael@0: return *this; michael@0: } michael@0: michael@0: if(buffer == NULL) { michael@0: // treat as an empty string, do not alias michael@0: releaseArray(); michael@0: setToEmpty(); michael@0: return *this; michael@0: } michael@0: michael@0: if(buffLength < -1 || buffCapacity < 0 || buffLength > buffCapacity) { michael@0: setToBogus(); michael@0: return *this; michael@0: } else if(buffLength == -1) { michael@0: // buffLength = u_strlen(buff); but do not look beyond buffCapacity michael@0: const UChar *p = buffer, *limit = buffer + buffCapacity; michael@0: while(p != limit && *p != 0) { michael@0: ++p; michael@0: } michael@0: buffLength = (int32_t)(p - buffer); michael@0: } michael@0: michael@0: releaseArray(); michael@0: michael@0: setArray(buffer, buffLength, buffCapacity); michael@0: fFlags = kWritableAlias; michael@0: return *this; michael@0: } michael@0: michael@0: UnicodeString &UnicodeString::setToUTF8(const StringPiece &utf8) { michael@0: unBogus(); michael@0: int32_t length = utf8.length(); michael@0: int32_t capacity; michael@0: // The UTF-16 string will be at most as long as the UTF-8 string. michael@0: if(length <= US_STACKBUF_SIZE) { michael@0: capacity = US_STACKBUF_SIZE; michael@0: } else { michael@0: capacity = length + 1; // +1 for the terminating NUL. michael@0: } michael@0: UChar *utf16 = getBuffer(capacity); michael@0: int32_t length16; michael@0: UErrorCode errorCode = U_ZERO_ERROR; michael@0: u_strFromUTF8WithSub(utf16, getCapacity(), &length16, michael@0: utf8.data(), length, michael@0: 0xfffd, // Substitution character. michael@0: NULL, // Don't care about number of substitutions. michael@0: &errorCode); michael@0: releaseBuffer(length16); michael@0: if(U_FAILURE(errorCode)) { michael@0: setToBogus(); michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: UnicodeString& michael@0: UnicodeString::setCharAt(int32_t offset, michael@0: UChar c) michael@0: { michael@0: int32_t len = length(); michael@0: if(cloneArrayIfNeeded() && len > 0) { michael@0: if(offset < 0) { michael@0: offset = 0; michael@0: } else if(offset >= len) { michael@0: offset = len - 1; michael@0: } michael@0: michael@0: getArrayStart()[offset] = c; michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: UnicodeString& michael@0: UnicodeString::replace(int32_t start, michael@0: int32_t _length, michael@0: UChar32 srcChar) { michael@0: UChar buffer[U16_MAX_LENGTH]; michael@0: int32_t count = 0; michael@0: UBool isError = FALSE; michael@0: U16_APPEND(buffer, count, U16_MAX_LENGTH, srcChar, isError); michael@0: // We test isError so that the compiler does not complain that we don't. michael@0: // If isError (srcChar is not a valid code point) then count==0 which means michael@0: // we remove the source segment rather than replacing it with srcChar. michael@0: return doReplace(start, _length, buffer, 0, isError ? 0 : count); michael@0: } michael@0: michael@0: UnicodeString& michael@0: UnicodeString::append(UChar32 srcChar) { michael@0: UChar buffer[U16_MAX_LENGTH]; michael@0: int32_t _length = 0; michael@0: UBool isError = FALSE; michael@0: U16_APPEND(buffer, _length, U16_MAX_LENGTH, srcChar, isError); michael@0: // We test isError so that the compiler does not complain that we don't. michael@0: // If isError then _length==0 which turns the doReplace() into a no-op anyway. michael@0: return isError ? *this : doReplace(length(), 0, buffer, 0, _length); michael@0: } michael@0: michael@0: UnicodeString& michael@0: UnicodeString::doReplace( int32_t start, michael@0: int32_t length, michael@0: const UnicodeString& src, michael@0: int32_t srcStart, michael@0: int32_t srcLength) michael@0: { michael@0: if(!src.isBogus()) { michael@0: // pin the indices to legal values michael@0: src.pinIndices(srcStart, srcLength); michael@0: michael@0: // get the characters from src michael@0: // and replace the range in ourselves with them michael@0: return doReplace(start, length, src.getArrayStart(), srcStart, srcLength); michael@0: } else { michael@0: // remove the range michael@0: return doReplace(start, length, 0, 0, 0); michael@0: } michael@0: } michael@0: michael@0: UnicodeString& michael@0: UnicodeString::doReplace(int32_t start, michael@0: int32_t length, michael@0: const UChar *srcChars, michael@0: int32_t srcStart, michael@0: int32_t srcLength) michael@0: { michael@0: if(!isWritable()) { michael@0: return *this; michael@0: } michael@0: michael@0: int32_t oldLength = this->length(); michael@0: michael@0: // optimize (read-only alias).remove(0, start) and .remove(start, end) michael@0: if((fFlags&kBufferIsReadonly) && srcLength == 0) { michael@0: if(start == 0) { michael@0: // remove prefix by adjusting the array pointer michael@0: pinIndex(length); michael@0: fUnion.fFields.fArray += length; michael@0: fUnion.fFields.fCapacity -= length; michael@0: setLength(oldLength - length); michael@0: return *this; michael@0: } else { michael@0: pinIndex(start); michael@0: if(length >= (oldLength - start)) { michael@0: // remove suffix by reducing the length (like truncate()) michael@0: setLength(start); michael@0: fUnion.fFields.fCapacity = start; // not NUL-terminated any more michael@0: return *this; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if(srcChars == 0) { michael@0: srcStart = srcLength = 0; michael@0: } else if(srcLength < 0) { michael@0: // get the srcLength if necessary michael@0: srcLength = u_strlen(srcChars + srcStart); michael@0: } michael@0: michael@0: // calculate the size of the string after the replace michael@0: int32_t newLength; michael@0: michael@0: // optimize append() onto a large-enough, owned string michael@0: if(start >= oldLength) { michael@0: if(srcLength == 0) { michael@0: return *this; michael@0: } michael@0: newLength = oldLength + srcLength; michael@0: if(newLength <= getCapacity() && isBufferWritable()) { michael@0: UChar *oldArray = getArrayStart(); michael@0: // Do not copy characters when michael@0: // UChar *buffer=str.getAppendBuffer(...); michael@0: // is followed by michael@0: // str.append(buffer, length); michael@0: // or michael@0: // str.appendString(buffer, length) michael@0: // or similar. michael@0: if(srcChars + srcStart != oldArray + start || start > oldLength) { michael@0: us_arrayCopy(srcChars, srcStart, oldArray, oldLength, srcLength); michael@0: } michael@0: setLength(newLength); michael@0: return *this; michael@0: } else { michael@0: // pin the indices to legal values michael@0: start = oldLength; michael@0: length = 0; michael@0: } michael@0: } else { michael@0: // pin the indices to legal values michael@0: pinIndices(start, length); michael@0: michael@0: newLength = oldLength - length + srcLength; michael@0: } michael@0: michael@0: // the following may change fArray but will not copy the current contents; michael@0: // therefore we need to keep the current fArray michael@0: UChar oldStackBuffer[US_STACKBUF_SIZE]; michael@0: UChar *oldArray; michael@0: if((fFlags&kUsingStackBuffer) && (newLength > US_STACKBUF_SIZE)) { michael@0: // copy the stack buffer contents because it will be overwritten with michael@0: // fUnion.fFields values michael@0: u_memcpy(oldStackBuffer, fUnion.fStackBuffer, oldLength); michael@0: oldArray = oldStackBuffer; michael@0: } else { michael@0: oldArray = getArrayStart(); michael@0: } michael@0: michael@0: // clone our array and allocate a bigger array if needed michael@0: int32_t *bufferToDelete = 0; michael@0: if(!cloneArrayIfNeeded(newLength, newLength + (newLength >> 2) + kGrowSize, michael@0: FALSE, &bufferToDelete) michael@0: ) { michael@0: return *this; michael@0: } michael@0: michael@0: // now do the replace michael@0: michael@0: UChar *newArray = getArrayStart(); michael@0: if(newArray != oldArray) { michael@0: // if fArray changed, then we need to copy everything except what will change michael@0: us_arrayCopy(oldArray, 0, newArray, 0, start); michael@0: us_arrayCopy(oldArray, start + length, michael@0: newArray, start + srcLength, michael@0: oldLength - (start + length)); michael@0: } else if(length != srcLength) { michael@0: // fArray did not change; copy only the portion that isn't changing, leaving a hole michael@0: us_arrayCopy(oldArray, start + length, michael@0: newArray, start + srcLength, michael@0: oldLength - (start + length)); michael@0: } michael@0: michael@0: // now fill in the hole with the new string michael@0: us_arrayCopy(srcChars, srcStart, newArray, start, srcLength); michael@0: michael@0: setLength(newLength); michael@0: michael@0: // delayed delete in case srcChars == fArray when we started, and michael@0: // to keep oldArray alive for the above operations michael@0: if (bufferToDelete) { michael@0: uprv_free(bufferToDelete); michael@0: } michael@0: michael@0: return *this; michael@0: } michael@0: michael@0: /** michael@0: * Replaceable API michael@0: */ michael@0: void michael@0: UnicodeString::handleReplaceBetween(int32_t start, michael@0: int32_t limit, michael@0: const UnicodeString& text) { michael@0: replaceBetween(start, limit, text); michael@0: } michael@0: michael@0: /** michael@0: * Replaceable API michael@0: */ michael@0: void michael@0: UnicodeString::copy(int32_t start, int32_t limit, int32_t dest) { michael@0: if (limit <= start) { michael@0: return; // Nothing to do; avoid bogus malloc call michael@0: } michael@0: UChar* text = (UChar*) uprv_malloc( sizeof(UChar) * (limit - start) ); michael@0: // Check to make sure text is not null. michael@0: if (text != NULL) { michael@0: extractBetween(start, limit, text, 0); michael@0: insert(dest, text, 0, limit - start); michael@0: uprv_free(text); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Replaceable API michael@0: * michael@0: * NOTE: This is for the Replaceable class. There is no rep.cpp, michael@0: * so we implement this function here. michael@0: */ michael@0: UBool Replaceable::hasMetaData() const { michael@0: return TRUE; michael@0: } michael@0: michael@0: /** michael@0: * Replaceable API michael@0: */ michael@0: UBool UnicodeString::hasMetaData() const { michael@0: return FALSE; michael@0: } michael@0: michael@0: UnicodeString& michael@0: UnicodeString::doReverse(int32_t start, int32_t length) { michael@0: if(length <= 1 || !cloneArrayIfNeeded()) { michael@0: return *this; michael@0: } michael@0: michael@0: // pin the indices to legal values michael@0: pinIndices(start, length); michael@0: if(length <= 1) { // pinIndices() might have shrunk the length michael@0: return *this; michael@0: } michael@0: michael@0: UChar *left = getArrayStart() + start; michael@0: UChar *right = left + length - 1; // -1 for inclusive boundary (length>=2) michael@0: UChar swap; michael@0: UBool hasSupplementary = FALSE; michael@0: michael@0: // Before the loop we know left=2. michael@0: do { michael@0: hasSupplementary |= (UBool)U16_IS_LEAD(swap = *left); michael@0: hasSupplementary |= (UBool)U16_IS_LEAD(*left++ = *right); michael@0: *right-- = swap; michael@0: } while(left < right); michael@0: // Make sure to test the middle code unit of an odd-length string. michael@0: // Redundant if the length is even. michael@0: hasSupplementary |= (UBool)U16_IS_LEAD(*left); michael@0: michael@0: /* if there are supplementary code points in the reversed range, then re-swap their surrogates */ michael@0: if(hasSupplementary) { michael@0: UChar swap2; michael@0: michael@0: left = getArrayStart() + start; michael@0: right = left + length - 1; // -1 so that we can look at *(left+1) if left= targetLength || !cloneArrayIfNeeded(targetLength)) { michael@0: return FALSE; michael@0: } else { michael@0: // move contents up by padding width michael@0: UChar *array = getArrayStart(); michael@0: int32_t start = targetLength - oldLength; michael@0: us_arrayCopy(array, 0, array, start, oldLength); michael@0: michael@0: // fill in padding character michael@0: while(--start >= 0) { michael@0: array[start] = padChar; michael@0: } michael@0: setLength(targetLength); michael@0: return TRUE; michael@0: } michael@0: } michael@0: michael@0: UBool michael@0: UnicodeString::padTrailing(int32_t targetLength, michael@0: UChar padChar) michael@0: { michael@0: int32_t oldLength = length(); michael@0: if(oldLength >= targetLength || !cloneArrayIfNeeded(targetLength)) { michael@0: return FALSE; michael@0: } else { michael@0: // fill in padding character michael@0: UChar *array = getArrayStart(); michael@0: int32_t length = targetLength; michael@0: while(--length >= oldLength) { michael@0: array[length] = padChar; michael@0: } michael@0: setLength(targetLength); michael@0: return TRUE; michael@0: } michael@0: } michael@0: michael@0: //======================================== michael@0: // Hashing michael@0: //======================================== michael@0: int32_t michael@0: UnicodeString::doHashCode() const michael@0: { michael@0: /* Delegate hash computation to uhash. This makes UnicodeString michael@0: * hashing consistent with UChar* hashing. */ michael@0: int32_t hashCode = ustr_hashUCharsN(getArrayStart(), length()); michael@0: if (hashCode == kInvalidHashCode) { michael@0: hashCode = kEmptyHashCode; michael@0: } michael@0: return hashCode; michael@0: } michael@0: michael@0: //======================================== michael@0: // External Buffer michael@0: //======================================== michael@0: michael@0: UChar * michael@0: UnicodeString::getBuffer(int32_t minCapacity) { michael@0: if(minCapacity>=-1 && cloneArrayIfNeeded(minCapacity)) { michael@0: fFlags|=kOpenGetBuffer; michael@0: fShortLength=0; michael@0: return getArrayStart(); michael@0: } else { michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: void michael@0: UnicodeString::releaseBuffer(int32_t newLength) { michael@0: if(fFlags&kOpenGetBuffer && newLength>=-1) { michael@0: // set the new fLength michael@0: int32_t capacity=getCapacity(); michael@0: if(newLength==-1) { michael@0: // the new length is the string length, capped by fCapacity michael@0: const UChar *array=getArrayStart(), *p=array, *limit=array+capacity; michael@0: while(pcapacity) { michael@0: newLength=capacity; michael@0: } michael@0: setLength(newLength); michael@0: fFlags&=~kOpenGetBuffer; michael@0: } michael@0: } michael@0: michael@0: //======================================== michael@0: // Miscellaneous michael@0: //======================================== michael@0: UBool michael@0: UnicodeString::cloneArrayIfNeeded(int32_t newCapacity, michael@0: int32_t growCapacity, michael@0: UBool doCopyArray, michael@0: int32_t **pBufferToDelete, michael@0: UBool forceClone) { michael@0: // default parameters need to be static, therefore michael@0: // the defaults are -1 to have convenience defaults michael@0: if(newCapacity == -1) { michael@0: newCapacity = getCapacity(); michael@0: } michael@0: michael@0: // while a getBuffer(minCapacity) is "open", michael@0: // prevent any modifications of the string by returning FALSE here michael@0: // if the string is bogus, then only an assignment or similar can revive it michael@0: if(!isWritable()) { michael@0: return FALSE; michael@0: } michael@0: michael@0: /* michael@0: * We need to make a copy of the array if michael@0: * the buffer is read-only, or michael@0: * the buffer is refCounted (shared), and refCount>1, or michael@0: * the buffer is too small. michael@0: * Return FALSE if memory could not be allocated. michael@0: */ michael@0: if(forceClone || michael@0: fFlags & kBufferIsReadonly || michael@0: (fFlags & kRefCounted && refCount() > 1) || michael@0: newCapacity > getCapacity() michael@0: ) { michael@0: // check growCapacity for default value and use of the stack buffer michael@0: if(growCapacity < 0) { michael@0: growCapacity = newCapacity; michael@0: } else if(newCapacity <= US_STACKBUF_SIZE && growCapacity > US_STACKBUF_SIZE) { michael@0: growCapacity = US_STACKBUF_SIZE; michael@0: } michael@0: michael@0: // save old values michael@0: UChar oldStackBuffer[US_STACKBUF_SIZE]; michael@0: UChar *oldArray; michael@0: uint8_t flags = fFlags; michael@0: michael@0: if(flags&kUsingStackBuffer) { michael@0: U_ASSERT(!(flags&kRefCounted)); /* kRefCounted and kUsingStackBuffer are mutally exclusive */ michael@0: if(doCopyArray && growCapacity > US_STACKBUF_SIZE) { michael@0: // copy the stack buffer contents because it will be overwritten with michael@0: // fUnion.fFields values michael@0: us_arrayCopy(fUnion.fStackBuffer, 0, oldStackBuffer, 0, fShortLength); michael@0: oldArray = oldStackBuffer; michael@0: } else { michael@0: oldArray = 0; // no need to copy from stack buffer to itself michael@0: } michael@0: } else { michael@0: oldArray = fUnion.fFields.fArray; michael@0: U_ASSERT(oldArray!=NULL); /* when stack buffer is not used, oldArray must have a non-NULL reference */ michael@0: } michael@0: michael@0: // allocate a new array michael@0: if(allocate(growCapacity) || michael@0: (newCapacity < growCapacity && allocate(newCapacity)) michael@0: ) { michael@0: if(doCopyArray && oldArray != 0) { michael@0: // copy the contents michael@0: // do not copy more than what fits - it may be smaller than before michael@0: int32_t minLength = length(); michael@0: newCapacity = getCapacity(); michael@0: if(newCapacity < minLength) { michael@0: minLength = newCapacity; michael@0: setLength(minLength); michael@0: } michael@0: us_arrayCopy(oldArray, 0, getArrayStart(), 0, minLength); michael@0: } else { michael@0: fShortLength = 0; michael@0: } michael@0: michael@0: // release the old array michael@0: if(flags & kRefCounted) { michael@0: // the array is refCounted; decrement and release if 0 michael@0: u_atomic_int32_t *pRefCount = ((u_atomic_int32_t *)oldArray - 1); michael@0: if(umtx_atomic_dec(pRefCount) == 0) { michael@0: if(pBufferToDelete == 0) { michael@0: // Note: cast to (void *) is needed with MSVC, where u_atomic_int32_t michael@0: // is defined as volatile. (Volatile has useful non-standard behavior michael@0: // with this compiler.) michael@0: uprv_free((void *)pRefCount); michael@0: } else { michael@0: // the caller requested to delete it himself michael@0: *pBufferToDelete = (int32_t *)pRefCount; michael@0: } michael@0: } michael@0: } michael@0: } else { michael@0: // not enough memory for growCapacity and not even for the smaller newCapacity michael@0: // reset the old values for setToBogus() to release the array michael@0: if(!(flags&kUsingStackBuffer)) { michael@0: fUnion.fFields.fArray = oldArray; michael@0: } michael@0: fFlags = flags; michael@0: setToBogus(); michael@0: return FALSE; michael@0: } michael@0: } michael@0: return TRUE; michael@0: } michael@0: michael@0: // UnicodeStringAppendable ------------------------------------------------- *** michael@0: michael@0: UnicodeStringAppendable::~UnicodeStringAppendable() {} michael@0: michael@0: UBool michael@0: UnicodeStringAppendable::appendCodeUnit(UChar c) { michael@0: return str.doReplace(str.length(), 0, &c, 0, 1).isWritable(); michael@0: } michael@0: michael@0: UBool michael@0: UnicodeStringAppendable::appendCodePoint(UChar32 c) { michael@0: UChar buffer[U16_MAX_LENGTH]; michael@0: int32_t cLength = 0; michael@0: UBool isError = FALSE; michael@0: U16_APPEND(buffer, cLength, U16_MAX_LENGTH, c, isError); michael@0: return !isError && str.doReplace(str.length(), 0, buffer, 0, cLength).isWritable(); michael@0: } michael@0: michael@0: UBool michael@0: UnicodeStringAppendable::appendString(const UChar *s, int32_t length) { michael@0: return str.doReplace(str.length(), 0, s, 0, length).isWritable(); michael@0: } michael@0: michael@0: UBool michael@0: UnicodeStringAppendable::reserveAppendCapacity(int32_t appendCapacity) { michael@0: return str.cloneArrayIfNeeded(str.length() + appendCapacity); michael@0: } michael@0: michael@0: UChar * michael@0: UnicodeStringAppendable::getAppendBuffer(int32_t minCapacity, michael@0: int32_t desiredCapacityHint, michael@0: UChar *scratch, int32_t scratchCapacity, michael@0: int32_t *resultCapacity) { michael@0: if(minCapacity < 1 || scratchCapacity < minCapacity) { michael@0: *resultCapacity = 0; michael@0: return NULL; michael@0: } michael@0: int32_t oldLength = str.length(); michael@0: if(str.cloneArrayIfNeeded(oldLength + minCapacity, oldLength + desiredCapacityHint)) { michael@0: *resultCapacity = str.getCapacity() - oldLength; michael@0: return str.getArrayStart() + oldLength; michael@0: } michael@0: *resultCapacity = scratchCapacity; michael@0: return scratch; michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: U_NAMESPACE_USE michael@0: michael@0: U_CAPI int32_t U_EXPORT2 michael@0: uhash_hashUnicodeString(const UElement key) { michael@0: const UnicodeString *str = (const UnicodeString*) key.pointer; michael@0: return (str == NULL) ? 0 : str->hashCode(); michael@0: } michael@0: michael@0: // Moved here from uhash_us.cpp so that using a UVector of UnicodeString* michael@0: // does not depend on hashtable code. michael@0: U_CAPI UBool U_EXPORT2 michael@0: uhash_compareUnicodeString(const UElement key1, const UElement key2) { michael@0: const UnicodeString *str1 = (const UnicodeString*) key1.pointer; michael@0: const UnicodeString *str2 = (const UnicodeString*) key2.pointer; michael@0: if (str1 == str2) { michael@0: return TRUE; michael@0: } michael@0: if (str1 == NULL || str2 == NULL) { michael@0: return FALSE; michael@0: } michael@0: return *str1 == *str2; michael@0: } michael@0: michael@0: #ifdef U_STATIC_IMPLEMENTATION michael@0: /* michael@0: This should never be called. It is defined here to make sure that the michael@0: virtual vector deleting destructor is defined within unistr.cpp. michael@0: The vector deleting destructor is already a part of UObject, michael@0: but defining it here makes sure that it is included with this object file. michael@0: This makes sure that static library dependencies are kept to a minimum. michael@0: */ michael@0: static void uprv_UnicodeStringDummy(void) { michael@0: delete [] (new UnicodeString[2]); michael@0: } michael@0: #endif