diff options
Diffstat (limited to 'intl/icu/source/common/unistr.cpp')
-rw-r--r-- | intl/icu/source/common/unistr.cpp | 2030 |
1 files changed, 2030 insertions, 0 deletions
diff --git a/intl/icu/source/common/unistr.cpp b/intl/icu/source/common/unistr.cpp new file mode 100644 index 0000000000..04f01cfa16 --- /dev/null +++ b/intl/icu/source/common/unistr.cpp @@ -0,0 +1,2030 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1999-2016, International Business Machines Corporation and +* others. All Rights Reserved. +****************************************************************************** +* +* File unistr.cpp +* +* Modification History: +* +* Date Name Description +* 09/25/98 stephen Creation. +* 04/20/99 stephen Overhauled per 4/16 code review. +* 07/09/99 stephen Renamed {hi,lo},{byte,word} to icu_X for HP/UX +* 11/18/99 aliu Added handleReplaceBetween() to make inherit from +* Replaceable. +* 06/25/01 grhoten Removed the dependency on iostream +****************************************************************************** +*/ + +#include "unicode/utypes.h" +#include "unicode/appendable.h" +#include "unicode/putil.h" +#include "cstring.h" +#include "cmemory.h" +#include "unicode/ustring.h" +#include "unicode/unistr.h" +#include "unicode/utf.h" +#include "unicode/utf16.h" +#include "uelement.h" +#include "ustr_imp.h" +#include "umutex.h" +#include "uassert.h" + +#if 0 + +#include <iostream> +using namespace std; + +//DEBUGGING +void +print(const UnicodeString& s, + const char *name) +{ + char16_t c; + cout << name << ":|"; + for(int i = 0; i < s.length(); ++i) { + c = s[i]; + if(c>= 0x007E || c < 0x0020) + cout << "[0x" << hex << s[i] << "]"; + else + cout << (char) s[i]; + } + cout << '|' << endl; +} + +void +print(const char16_t *s, + int32_t len, + const char *name) +{ + char16_t c; + cout << name << ":|"; + for(int i = 0; i < len; ++i) { + c = s[i]; + if(c>= 0x007E || c < 0x0020) + cout << "[0x" << hex << s[i] << "]"; + else + cout << (char) s[i]; + } + cout << '|' << endl; +} +// END DEBUGGING +#endif + +// Local function definitions for now + +// need to copy areas that may overlap +static +inline void +us_arrayCopy(const char16_t *src, int32_t srcStart, + char16_t *dst, int32_t dstStart, int32_t count) +{ + if(count>0) { + uprv_memmove(dst+dstStart, src+srcStart, (size_t)count*sizeof(*src)); + } +} + +// u_unescapeAt() callback to get a char16_t from a UnicodeString +U_CDECL_BEGIN +static char16_t U_CALLCONV +UnicodeString_charAt(int32_t offset, void *context) { + return ((icu::UnicodeString*) context)->charAt(offset); +} +U_CDECL_END + +U_NAMESPACE_BEGIN + +/* The Replaceable virtual destructor can't be defined in the header + due to how AIX works with multiple definitions of virtual functions. +*/ +Replaceable::~Replaceable() {} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UnicodeString) + +UnicodeString U_EXPORT2 +operator+ (const UnicodeString &s1, const UnicodeString &s2) { + return + UnicodeString(s1.length()+s2.length()+1, (UChar32)0, 0). + append(s1). + append(s2); +} + +//======================================== +// Reference Counting functions, put at top of file so that optimizing compilers +// have a chance to automatically inline. +//======================================== + +void +UnicodeString::addRef() { + umtx_atomic_inc((u_atomic_int32_t *)fUnion.fFields.fArray - 1); +} + +int32_t +UnicodeString::removeRef() { + return umtx_atomic_dec((u_atomic_int32_t *)fUnion.fFields.fArray - 1); +} + +int32_t +UnicodeString::refCount() const { + return umtx_loadAcquire(*((u_atomic_int32_t *)fUnion.fFields.fArray - 1)); +} + +void +UnicodeString::releaseArray() { + if((fUnion.fFields.fLengthAndFlags & kRefCounted) && removeRef() == 0) { + uprv_free((int32_t *)fUnion.fFields.fArray - 1); + } +} + + + +//======================================== +// Constructors +//======================================== + +// The default constructor is inline in unistr.h. + +UnicodeString::UnicodeString(int32_t capacity, UChar32 c, int32_t count) { + fUnion.fFields.fLengthAndFlags = 0; + if(count <= 0 || (uint32_t)c > 0x10ffff) { + // just allocate and do not do anything else + allocate(capacity); + } else if(c <= 0xffff) { + int32_t length = count; + if(capacity < length) { + capacity = length; + } + if(allocate(capacity)) { + char16_t *array = getArrayStart(); + char16_t unit = (char16_t)c; + for(int32_t i = 0; i < length; ++i) { + array[i] = unit; + } + setLength(length); + } + } else { // supplementary code point, write surrogate pairs + if(count > (INT32_MAX / 2)) { + // We would get more than 2G UChars. + allocate(capacity); + return; + } + int32_t length = count * 2; + if(capacity < length) { + capacity = length; + } + if(allocate(capacity)) { + char16_t *array = getArrayStart(); + char16_t lead = U16_LEAD(c); + char16_t trail = U16_TRAIL(c); + for(int32_t i = 0; i < length; i += 2) { + array[i] = lead; + array[i + 1] = trail; + } + setLength(length); + } + } +} + +UnicodeString::UnicodeString(char16_t ch) { + fUnion.fFields.fLengthAndFlags = kLength1 | kShortString; + fUnion.fStackFields.fBuffer[0] = ch; +} + +UnicodeString::UnicodeString(UChar32 ch) { + fUnion.fFields.fLengthAndFlags = kShortString; + int32_t i = 0; + UBool isError = false; + U16_APPEND(fUnion.fStackFields.fBuffer, i, US_STACKBUF_SIZE, ch, isError); + // We test isError so that the compiler does not complain that we don't. + // If isError then i==0 which is what we want anyway. + if(!isError) { + setShortLength(i); + } +} + +UnicodeString::UnicodeString(const char16_t *text) { + fUnion.fFields.fLengthAndFlags = kShortString; + doAppend(text, 0, -1); +} + +UnicodeString::UnicodeString(const char16_t *text, + int32_t textLength) { + fUnion.fFields.fLengthAndFlags = kShortString; + doAppend(text, 0, textLength); +} + +UnicodeString::UnicodeString(UBool isTerminated, + ConstChar16Ptr textPtr, + int32_t textLength) { + fUnion.fFields.fLengthAndFlags = kReadonlyAlias; + const char16_t *text = textPtr; + if(text == nullptr) { + // treat as an empty string, do not alias + setToEmpty(); + } else if(textLength < -1 || + (textLength == -1 && !isTerminated) || + (textLength >= 0 && isTerminated && text[textLength] != 0) + ) { + setToBogus(); + } else { + if(textLength == -1) { + // text is terminated, or else it would have failed the above test + textLength = u_strlen(text); + } + setArray(const_cast<char16_t *>(text), textLength, + isTerminated ? textLength + 1 : textLength); + } +} + +UnicodeString::UnicodeString(char16_t *buff, + int32_t buffLength, + int32_t buffCapacity) { + fUnion.fFields.fLengthAndFlags = kWritableAlias; + if(buff == nullptr) { + // treat as an empty string, do not alias + setToEmpty(); + } else if(buffLength < -1 || buffCapacity < 0 || buffLength > buffCapacity) { + setToBogus(); + } else { + if(buffLength == -1) { + // fLength = u_strlen(buff); but do not look beyond buffCapacity + const char16_t *p = buff, *limit = buff + buffCapacity; + while(p != limit && *p != 0) { + ++p; + } + buffLength = (int32_t)(p - buff); + } + setArray(buff, buffLength, buffCapacity); + } +} + +UnicodeString::UnicodeString(const char *src, int32_t length, EInvariant) { + fUnion.fFields.fLengthAndFlags = kShortString; + if(src==nullptr) { + // treat as an empty string + } else { + if(length<0) { + length=(int32_t)uprv_strlen(src); + } + if(cloneArrayIfNeeded(length, length, false)) { + u_charsToUChars(src, getArrayStart(), length); + setLength(length); + } else { + setToBogus(); + } + } +} + +#if U_CHARSET_IS_UTF8 + +UnicodeString::UnicodeString(const char *codepageData) { + fUnion.fFields.fLengthAndFlags = kShortString; + if(codepageData != 0) { + setToUTF8(codepageData); + } +} + +UnicodeString::UnicodeString(const char *codepageData, int32_t dataLength) { + fUnion.fFields.fLengthAndFlags = kShortString; + // if there's nothing to convert, do nothing + if(codepageData == 0 || dataLength == 0 || dataLength < -1) { + return; + } + if(dataLength == -1) { + dataLength = (int32_t)uprv_strlen(codepageData); + } + setToUTF8(StringPiece(codepageData, dataLength)); +} + +// else see unistr_cnv.cpp +#endif + +UnicodeString::UnicodeString(const UnicodeString& that) { + fUnion.fFields.fLengthAndFlags = kShortString; + copyFrom(that); +} + +UnicodeString::UnicodeString(UnicodeString &&src) noexcept { + copyFieldsFrom(src, true); +} + +UnicodeString::UnicodeString(const UnicodeString& that, + int32_t srcStart) { + fUnion.fFields.fLengthAndFlags = kShortString; + setTo(that, srcStart); +} + +UnicodeString::UnicodeString(const UnicodeString& that, + int32_t srcStart, + int32_t srcLength) { + fUnion.fFields.fLengthAndFlags = kShortString; + setTo(that, srcStart, srcLength); +} + +// Replaceable base class clone() default implementation, does not clone +Replaceable * +Replaceable::clone() const { + return nullptr; +} + +// UnicodeString overrides clone() with a real implementation +UnicodeString * +UnicodeString::clone() const { + LocalPointer<UnicodeString> clonedString(new UnicodeString(*this)); + return clonedString.isValid() && !clonedString->isBogus() ? clonedString.orphan() : nullptr; +} + +//======================================== +// array allocation +//======================================== + +namespace { + +const int32_t kGrowSize = 128; + +// The number of bytes for one int32_t reference counter and capacity UChars +// must fit into a 32-bit size_t (at least when on a 32-bit platform). +// We also add one for the NUL terminator, to avoid reallocation in getTerminatedBuffer(), +// and round up to a multiple of 16 bytes. +// This means that capacity must be at most (0xfffffff0 - 4) / 2 - 1 = 0x7ffffff5. +// (With more complicated checks we could go up to 0x7ffffffd without rounding up, +// but that does not seem worth it.) +const int32_t kMaxCapacity = 0x7ffffff5; + +int32_t getGrowCapacity(int32_t newLength) { + int32_t growSize = (newLength >> 2) + kGrowSize; + if(growSize <= (kMaxCapacity - newLength)) { + return newLength + growSize; + } else { + return kMaxCapacity; + } +} + +} // namespace + +UBool +UnicodeString::allocate(int32_t capacity) { + if(capacity <= US_STACKBUF_SIZE) { + fUnion.fFields.fLengthAndFlags = kShortString; + return true; + } + if(capacity <= kMaxCapacity) { + ++capacity; // for the NUL + // Switch to size_t which is unsigned so that we can allocate up to 4GB. + // Reference counter + UChars. + size_t numBytes = sizeof(int32_t) + (size_t)capacity * U_SIZEOF_UCHAR; + // Round up to a multiple of 16. + numBytes = (numBytes + 15) & ~15; + int32_t *array = (int32_t *) uprv_malloc(numBytes); + if(array != nullptr) { + // set initial refCount and point behind the refCount + *array++ = 1; + numBytes -= sizeof(int32_t); + + // have fArray point to the first char16_t + fUnion.fFields.fArray = (char16_t *)array; + fUnion.fFields.fCapacity = (int32_t)(numBytes / U_SIZEOF_UCHAR); + fUnion.fFields.fLengthAndFlags = kLongString; + return true; + } + } + fUnion.fFields.fLengthAndFlags = kIsBogus; + fUnion.fFields.fArray = 0; + fUnion.fFields.fCapacity = 0; + return false; +} + +//======================================== +// Destructor +//======================================== + +#ifdef UNISTR_COUNT_FINAL_STRING_LENGTHS +static u_atomic_int32_t finalLengthCounts[0x400]; // UnicodeString::kMaxShortLength+1 +static u_atomic_int32_t beyondCount(0); + +U_CAPI void unistr_printLengths() { + int32_t i; + for(i = 0; i <= 59; ++i) { + printf("%2d, %9d\n", i, (int32_t)finalLengthCounts[i]); + } + int32_t beyond = beyondCount; + for(; i < UPRV_LENGTHOF(finalLengthCounts); ++i) { + beyond += finalLengthCounts[i]; + } + printf(">59, %9d\n", beyond); +} +#endif + +UnicodeString::~UnicodeString() +{ +#ifdef UNISTR_COUNT_FINAL_STRING_LENGTHS + // Count lengths of strings at the end of their lifetime. + // Useful for discussion of a desirable stack buffer size. + // Count the contents length, not the optional NUL terminator nor further capacity. + // Ignore open-buffer strings and strings which alias external storage. + if((fUnion.fFields.fLengthAndFlags&(kOpenGetBuffer|kReadonlyAlias|kWritableAlias)) == 0) { + if(hasShortLength()) { + umtx_atomic_inc(finalLengthCounts + getShortLength()); + } else { + umtx_atomic_inc(&beyondCount); + } + } +#endif + + releaseArray(); +} + +//======================================== +// Factory methods +//======================================== + +UnicodeString UnicodeString::fromUTF8(StringPiece utf8) { + UnicodeString result; + result.setToUTF8(utf8); + return result; +} + +UnicodeString UnicodeString::fromUTF32(const UChar32 *utf32, int32_t length) { + UnicodeString result; + int32_t capacity; + // Most UTF-32 strings will be BMP-only and result in a same-length + // UTF-16 string. We overestimate the capacity just slightly, + // just in case there are a few supplementary characters. + if(length <= US_STACKBUF_SIZE) { + capacity = US_STACKBUF_SIZE; + } else { + capacity = length + (length >> 4) + 4; + } + do { + char16_t *utf16 = result.getBuffer(capacity); + int32_t length16; + UErrorCode errorCode = U_ZERO_ERROR; + u_strFromUTF32WithSub(utf16, result.getCapacity(), &length16, + utf32, length, + 0xfffd, // Substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + result.releaseBuffer(length16); + if(errorCode == U_BUFFER_OVERFLOW_ERROR) { + capacity = length16 + 1; // +1 for the terminating NUL. + continue; + } else if(U_FAILURE(errorCode)) { + result.setToBogus(); + } + break; + } while(true); + return result; +} + +//======================================== +// Assignment +//======================================== + +UnicodeString & +UnicodeString::operator=(const UnicodeString &src) { + return copyFrom(src); +} + +UnicodeString & +UnicodeString::fastCopyFrom(const UnicodeString &src) { + return copyFrom(src, true); +} + +UnicodeString & +UnicodeString::copyFrom(const UnicodeString &src, UBool fastCopy) { + // if assigning to ourselves, do nothing + if(this == &src) { + return *this; + } + + // is the right side bogus? + if(src.isBogus()) { + setToBogus(); + return *this; + } + + // delete the current contents + releaseArray(); + + if(src.isEmpty()) { + // empty string - use the stack buffer + setToEmpty(); + return *this; + } + + // fLength>0 and not an "open" src.getBuffer(minCapacity) + fUnion.fFields.fLengthAndFlags = src.fUnion.fFields.fLengthAndFlags; + switch(src.fUnion.fFields.fLengthAndFlags & kAllStorageFlags) { + case kShortString: + // short string using the stack buffer, do the same + uprv_memcpy(fUnion.fStackFields.fBuffer, src.fUnion.fStackFields.fBuffer, + getShortLength() * U_SIZEOF_UCHAR); + break; + case kLongString: + // src uses a refCounted string buffer, use that buffer with refCount + // src is const, use a cast - we don't actually change it + const_cast<UnicodeString &>(src).addRef(); + // copy all fields, share the reference-counted buffer + fUnion.fFields.fArray = src.fUnion.fFields.fArray; + fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; + if(!hasShortLength()) { + fUnion.fFields.fLength = src.fUnion.fFields.fLength; + } + break; + case kReadonlyAlias: + if(fastCopy) { + // src is a readonly alias, do the same + // -> maintain the readonly alias as such + fUnion.fFields.fArray = src.fUnion.fFields.fArray; + fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; + if(!hasShortLength()) { + fUnion.fFields.fLength = src.fUnion.fFields.fLength; + } + break; + } + // else if(!fastCopy) fall through to case kWritableAlias + // -> allocate a new buffer and copy the contents + U_FALLTHROUGH; + case kWritableAlias: { + // src is a writable alias; we make a copy of that instead + int32_t srcLength = src.length(); + if(allocate(srcLength)) { + u_memcpy(getArrayStart(), src.getArrayStart(), srcLength); + setLength(srcLength); + break; + } + // if there is not enough memory, then fall through to setting to bogus + U_FALLTHROUGH; + } + default: + // if src is bogus, set ourselves to bogus + // do not call setToBogus() here because fArray and flags are not consistent here + fUnion.fFields.fLengthAndFlags = kIsBogus; + fUnion.fFields.fArray = 0; + fUnion.fFields.fCapacity = 0; + break; + } + + return *this; +} + +UnicodeString &UnicodeString::operator=(UnicodeString &&src) noexcept { + // No explicit check for self move assignment, consistent with standard library. + // Self move assignment causes no crash nor leak but might make the object bogus. + releaseArray(); + copyFieldsFrom(src, true); + return *this; +} + +// Same as move assignment except without memory management. +void UnicodeString::copyFieldsFrom(UnicodeString &src, UBool setSrcToBogus) noexcept { + int16_t lengthAndFlags = fUnion.fFields.fLengthAndFlags = src.fUnion.fFields.fLengthAndFlags; + if(lengthAndFlags & kUsingStackBuffer) { + // Short string using the stack buffer, copy the contents. + // Check for self assignment to prevent "overlap in memcpy" warnings, + // although it should be harmless to copy a buffer to itself exactly. + if(this != &src) { + uprv_memcpy(fUnion.fStackFields.fBuffer, src.fUnion.fStackFields.fBuffer, + getShortLength() * U_SIZEOF_UCHAR); + } + } else { + // In all other cases, copy all fields. + fUnion.fFields.fArray = src.fUnion.fFields.fArray; + fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; + if(!hasShortLength()) { + fUnion.fFields.fLength = src.fUnion.fFields.fLength; + } + if(setSrcToBogus) { + // Set src to bogus without releasing any memory. + src.fUnion.fFields.fLengthAndFlags = kIsBogus; + src.fUnion.fFields.fArray = nullptr; + src.fUnion.fFields.fCapacity = 0; + } + } +} + +void UnicodeString::swap(UnicodeString &other) noexcept { + UnicodeString temp; // Empty short string: Known not to need releaseArray(). + // Copy fields without resetting source values in between. + temp.copyFieldsFrom(*this, false); + this->copyFieldsFrom(other, false); + other.copyFieldsFrom(temp, false); + // Set temp to an empty string so that other's memory is not released twice. + temp.fUnion.fFields.fLengthAndFlags = kShortString; +} + +//======================================== +// Miscellaneous operations +//======================================== + +UnicodeString UnicodeString::unescape() const { + UnicodeString result(length(), (UChar32)0, (int32_t)0); // construct with capacity + if (result.isBogus()) { + return result; + } + const char16_t *array = getBuffer(); + int32_t len = length(); + int32_t prev = 0; + for (int32_t i=0;;) { + if (i == len) { + result.append(array, prev, len - prev); + break; + } + if (array[i++] == 0x5C /*'\\'*/) { + result.append(array, prev, (i - 1) - prev); + UChar32 c = unescapeAt(i); // advances i + if (c < 0) { + result.remove(); // return empty string + break; // invalid escape sequence + } + result.append(c); + prev = i; + } + } + return result; +} + +UChar32 UnicodeString::unescapeAt(int32_t &offset) const { + return u_unescapeAt(UnicodeString_charAt, &offset, length(), (void*)this); +} + +//======================================== +// Read-only implementation +//======================================== +UBool +UnicodeString::doEquals(const UnicodeString &text, int32_t len) const { + // Requires: this & text not bogus and have same lengths. + // Byte-wise comparison works for equality regardless of endianness. + return uprv_memcmp(getArrayStart(), text.getArrayStart(), len * U_SIZEOF_UCHAR) == 0; +} + +UBool +UnicodeString::doEqualsSubstring( int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const +{ + // compare illegal string values + if(isBogus()) { + return false; + } + + // pin indices to legal values + pinIndices(start, length); + + if(srcChars == nullptr) { + // treat const char16_t *srcChars==nullptr as an empty string + return length == 0 ? true : false; + } + + // get the correct pointer + const char16_t *chars = getArrayStart(); + + chars += start; + srcChars += srcStart; + + // get the srcLength if necessary + if(srcLength < 0) { + srcLength = u_strlen(srcChars + srcStart); + } + + if (length != srcLength) { + return false; + } + + if(length == 0 || chars == srcChars) { + return true; + } + + return u_memcmp(chars, srcChars, srcLength) == 0; +} + +int8_t +UnicodeString::doCompare( int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const +{ + // compare illegal string values + if(isBogus()) { + return -1; + } + + // pin indices to legal values + pinIndices(start, length); + + if(srcChars == nullptr) { + // treat const char16_t *srcChars==nullptr as an empty string + return length == 0 ? 0 : 1; + } + + // get the correct pointer + const char16_t *chars = getArrayStart(); + + chars += start; + srcChars += srcStart; + + int32_t minLength; + int8_t lengthResult; + + // get the srcLength if necessary + if(srcLength < 0) { + srcLength = u_strlen(srcChars + srcStart); + } + + // are we comparing different lengths? + if(length != srcLength) { + if(length < srcLength) { + minLength = length; + lengthResult = -1; + } else { + minLength = srcLength; + lengthResult = 1; + } + } else { + minLength = length; + lengthResult = 0; + } + + /* + * note that uprv_memcmp() returns an int but we return an int8_t; + * we need to take care not to truncate the result - + * one way to do this is to right-shift the value to + * move the sign bit into the lower 8 bits and making sure that this + * does not become 0 itself + */ + + if(minLength > 0 && chars != srcChars) { + int32_t result; + +# if U_IS_BIG_ENDIAN + // big-endian: byte comparison works + result = uprv_memcmp(chars, srcChars, minLength * sizeof(char16_t)); + if(result != 0) { + return (int8_t)(result >> 15 | 1); + } +# else + // little-endian: compare char16_t units + do { + result = ((int32_t)*(chars++) - (int32_t)*(srcChars++)); + if(result != 0) { + return (int8_t)(result >> 15 | 1); + } + } while(--minLength > 0); +# endif + } + return lengthResult; +} + +/* String compare in code point order - doCompare() compares in code unit order. */ +int8_t +UnicodeString::doCompareCodePointOrder(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const +{ + // compare illegal string values + // treat const char16_t *srcChars==nullptr as an empty string + if(isBogus()) { + return -1; + } + + // pin indices to legal values + pinIndices(start, length); + + if(srcChars == nullptr) { + srcStart = srcLength = 0; + } + + int32_t diff = uprv_strCompare(getArrayStart() + start, length, (srcChars!=nullptr)?(srcChars + srcStart):nullptr, srcLength, false, true); + /* translate the 32-bit result into an 8-bit one */ + if(diff!=0) { + return (int8_t)(diff >> 15 | 1); + } else { + return 0; + } +} + +int32_t +UnicodeString::getLength() const { + return length(); +} + +char16_t +UnicodeString::getCharAt(int32_t offset) const { + return charAt(offset); +} + +UChar32 +UnicodeString::getChar32At(int32_t offset) const { + return char32At(offset); +} + +UChar32 +UnicodeString::char32At(int32_t offset) const +{ + int32_t len = length(); + if((uint32_t)offset < (uint32_t)len) { + const char16_t *array = getArrayStart(); + UChar32 c; + U16_GET(array, 0, offset, len, c); + return c; + } else { + return kInvalidUChar; + } +} + +int32_t +UnicodeString::getChar32Start(int32_t offset) const { + if((uint32_t)offset < (uint32_t)length()) { + const char16_t *array = getArrayStart(); + U16_SET_CP_START(array, 0, offset); + return offset; + } else { + return 0; + } +} + +int32_t +UnicodeString::getChar32Limit(int32_t offset) const { + int32_t len = length(); + if((uint32_t)offset < (uint32_t)len) { + const char16_t *array = getArrayStart(); + U16_SET_CP_LIMIT(array, 0, offset, len); + return offset; + } else { + return len; + } +} + +int32_t +UnicodeString::countChar32(int32_t start, int32_t length) const { + pinIndices(start, length); + // if(isBogus()) then fArray==0 and start==0 - u_countChar32() checks for nullptr + return u_countChar32(getArrayStart()+start, length); +} + +UBool +UnicodeString::hasMoreChar32Than(int32_t start, int32_t length, int32_t number) const { + pinIndices(start, length); + // if(isBogus()) then fArray==0 and start==0 - u_strHasMoreChar32Than() checks for nullptr + return u_strHasMoreChar32Than(getArrayStart()+start, length, number); +} + +int32_t +UnicodeString::moveIndex32(int32_t index, int32_t delta) const { + // pin index + int32_t len = length(); + if(index<0) { + index=0; + } else if(index>len) { + index=len; + } + + const char16_t *array = getArrayStart(); + if(delta>0) { + U16_FWD_N(array, index, len, delta); + } else { + U16_BACK_N(array, 0, index, -delta); + } + + return index; +} + +void +UnicodeString::doExtract(int32_t start, + int32_t length, + char16_t *dst, + int32_t dstStart) const +{ + // pin indices to legal values + pinIndices(start, length); + + // do not copy anything if we alias dst itself + const char16_t *array = getArrayStart(); + if(array + start != dst + dstStart) { + us_arrayCopy(array, start, dst, dstStart, length); + } +} + +int32_t +UnicodeString::extract(Char16Ptr dest, int32_t destCapacity, + UErrorCode &errorCode) const { + int32_t len = length(); + if(U_SUCCESS(errorCode)) { + if(isBogus() || destCapacity<0 || (destCapacity>0 && dest==0)) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + } else { + const char16_t *array = getArrayStart(); + if(len>0 && len<=destCapacity && array!=dest) { + u_memcpy(dest, array, len); + } + return u_terminateUChars(dest, destCapacity, len, &errorCode); + } + } + + return len; +} + +int32_t +UnicodeString::extract(int32_t start, + int32_t length, + char *target, + int32_t targetCapacity, + enum EInvariant) const +{ + // if the arguments are illegal, then do nothing + if(targetCapacity < 0 || (targetCapacity > 0 && target == nullptr)) { + return 0; + } + + // pin the indices to legal values + pinIndices(start, length); + + if(length <= targetCapacity) { + u_UCharsToChars(getArrayStart() + start, target, length); + } + UErrorCode status = U_ZERO_ERROR; + return u_terminateChars(target, targetCapacity, length, &status); +} + +UnicodeString +UnicodeString::tempSubString(int32_t start, int32_t len) const { + pinIndices(start, len); + const char16_t *array = getBuffer(); // not getArrayStart() to check kIsBogus & kOpenGetBuffer + if(array==nullptr) { + array=fUnion.fStackFields.fBuffer; // anything not nullptr because that would make an empty string + len=-2; // bogus result string + } + return UnicodeString(false, array + start, len); +} + +int32_t +UnicodeString::toUTF8(int32_t start, int32_t len, + char *target, int32_t capacity) const { + pinIndices(start, len); + int32_t length8; + UErrorCode errorCode = U_ZERO_ERROR; + u_strToUTF8WithSub(target, capacity, &length8, + getBuffer() + start, len, + 0xFFFD, // Standard substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + return length8; +} + +#if U_CHARSET_IS_UTF8 + +int32_t +UnicodeString::extract(int32_t start, int32_t len, + char *target, uint32_t dstSize) const { + // if the arguments are illegal, then do nothing + if(/*dstSize < 0 || */(dstSize > 0 && target == 0)) { + return 0; + } + return toUTF8(start, len, target, dstSize <= 0x7fffffff ? (int32_t)dstSize : 0x7fffffff); +} + +// else see unistr_cnv.cpp +#endif + +void +UnicodeString::extractBetween(int32_t start, + int32_t limit, + UnicodeString& target) const { + pinIndex(start); + pinIndex(limit); + doExtract(start, limit - start, target); +} + +// When converting from UTF-16 to UTF-8, the result will have at most 3 times +// as many bytes as the source has UChars. +// The "worst cases" are writing systems like Indic, Thai and CJK with +// 3:1 bytes:UChars. +void +UnicodeString::toUTF8(ByteSink &sink) const { + int32_t length16 = length(); + if(length16 != 0) { + char stackBuffer[1024]; + int32_t capacity = (int32_t)sizeof(stackBuffer); + UBool utf8IsOwned = false; + char *utf8 = sink.GetAppendBuffer(length16 < capacity ? length16 : capacity, + 3*length16, + stackBuffer, capacity, + &capacity); + int32_t length8 = 0; + UErrorCode errorCode = U_ZERO_ERROR; + u_strToUTF8WithSub(utf8, capacity, &length8, + getBuffer(), length16, + 0xFFFD, // Standard substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + if(errorCode == U_BUFFER_OVERFLOW_ERROR) { + utf8 = (char *)uprv_malloc(length8); + if(utf8 != nullptr) { + utf8IsOwned = true; + errorCode = U_ZERO_ERROR; + u_strToUTF8WithSub(utf8, length8, &length8, + getBuffer(), length16, + 0xFFFD, // Standard substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + } else { + errorCode = U_MEMORY_ALLOCATION_ERROR; + } + } + if(U_SUCCESS(errorCode)) { + sink.Append(utf8, length8); + sink.Flush(); + } + if(utf8IsOwned) { + uprv_free(utf8); + } + } +} + +int32_t +UnicodeString::toUTF32(UChar32 *utf32, int32_t capacity, UErrorCode &errorCode) const { + int32_t length32=0; + if(U_SUCCESS(errorCode)) { + // getBuffer() and u_strToUTF32WithSub() check for illegal arguments. + u_strToUTF32WithSub(utf32, capacity, &length32, + getBuffer(), length(), + 0xfffd, // Substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + } + return length32; +} + +int32_t +UnicodeString::indexOf(const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength, + int32_t start, + int32_t length) const +{ + if(isBogus() || srcChars == 0 || srcStart < 0 || srcLength == 0) { + return -1; + } + + // UnicodeString does not find empty substrings + if(srcLength < 0 && srcChars[srcStart] == 0) { + return -1; + } + + // get the indices within bounds + pinIndices(start, length); + + // find the first occurrence of the substring + const char16_t *array = getArrayStart(); + const char16_t *match = u_strFindFirst(array + start, length, srcChars + srcStart, srcLength); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +int32_t +UnicodeString::doIndexOf(char16_t c, + int32_t start, + int32_t length) const +{ + // pin indices + pinIndices(start, length); + + // find the first occurrence of c + const char16_t *array = getArrayStart(); + const char16_t *match = u_memchr(array + start, c, length); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +int32_t +UnicodeString::doIndexOf(UChar32 c, + int32_t start, + int32_t length) const { + // pin indices + pinIndices(start, length); + + // find the first occurrence of c + const char16_t *array = getArrayStart(); + const char16_t *match = u_memchr32(array + start, c, length); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +int32_t +UnicodeString::lastIndexOf(const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength, + int32_t start, + int32_t length) const +{ + if(isBogus() || srcChars == 0 || srcStart < 0 || srcLength == 0) { + return -1; + } + + // UnicodeString does not find empty substrings + if(srcLength < 0 && srcChars[srcStart] == 0) { + return -1; + } + + // get the indices within bounds + pinIndices(start, length); + + // find the last occurrence of the substring + const char16_t *array = getArrayStart(); + const char16_t *match = u_strFindLast(array + start, length, srcChars + srcStart, srcLength); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +int32_t +UnicodeString::doLastIndexOf(char16_t c, + int32_t start, + int32_t length) const +{ + if(isBogus()) { + return -1; + } + + // pin indices + pinIndices(start, length); + + // find the last occurrence of c + const char16_t *array = getArrayStart(); + const char16_t *match = u_memrchr(array + start, c, length); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +int32_t +UnicodeString::doLastIndexOf(UChar32 c, + int32_t start, + int32_t length) const { + // pin indices + pinIndices(start, length); + + // find the last occurrence of c + const char16_t *array = getArrayStart(); + const char16_t *match = u_memrchr32(array + start, c, length); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +//======================================== +// Write implementation +//======================================== + +UnicodeString& +UnicodeString::findAndReplace(int32_t start, + int32_t length, + const UnicodeString& oldText, + int32_t oldStart, + int32_t oldLength, + const UnicodeString& newText, + int32_t newStart, + int32_t newLength) +{ + if(isBogus() || oldText.isBogus() || newText.isBogus()) { + return *this; + } + + pinIndices(start, length); + oldText.pinIndices(oldStart, oldLength); + newText.pinIndices(newStart, newLength); + + if(oldLength == 0) { + return *this; + } + + while(length > 0 && length >= oldLength) { + int32_t pos = indexOf(oldText, oldStart, oldLength, start, length); + if(pos < 0) { + // no more oldText's here: done + break; + } else { + // we found oldText, replace it by newText and go beyond it + replace(pos, oldLength, newText, newStart, newLength); + length -= pos + oldLength - start; + start = pos + newLength; + } + } + + return *this; +} + + +void +UnicodeString::setToBogus() +{ + releaseArray(); + + fUnion.fFields.fLengthAndFlags = kIsBogus; + fUnion.fFields.fArray = 0; + fUnion.fFields.fCapacity = 0; +} + +// turn a bogus string into an empty one +void +UnicodeString::unBogus() { + if(fUnion.fFields.fLengthAndFlags & kIsBogus) { + setToEmpty(); + } +} + +const char16_t * +UnicodeString::getTerminatedBuffer() { + if(!isWritable()) { + return nullptr; + } + char16_t *array = getArrayStart(); + int32_t len = length(); + if(len < getCapacity()) { + if(fUnion.fFields.fLengthAndFlags & kBufferIsReadonly) { + // If len<capacity on a read-only alias, then array[len] is + // either the original NUL (if constructed with (true, s, length)) + // or one of the original string contents characters (if later truncated), + // therefore we can assume that array[len] is initialized memory. + if(array[len] == 0) { + return array; + } + } else if(((fUnion.fFields.fLengthAndFlags & kRefCounted) == 0 || refCount() == 1)) { + // kRefCounted: Do not write the NUL if the buffer is shared. + // That is mostly safe, except when the length of one copy was modified + // without copy-on-write, e.g., via truncate(newLength) or remove(). + // Then the NUL would be written into the middle of another copy's string. + + // Otherwise, the buffer is fully writable and it is anyway safe to write the NUL. + // Do not test if there is a NUL already because it might be uninitialized memory. + // (That would be safe, but tools like valgrind & Purify would complain.) + array[len] = 0; + return array; + } + } + if(len<INT32_MAX && cloneArrayIfNeeded(len+1)) { + array = getArrayStart(); + array[len] = 0; + return array; + } else { + return nullptr; + } +} + +// setTo() analogous to the readonly-aliasing constructor with the same signature +UnicodeString & +UnicodeString::setTo(UBool isTerminated, + ConstChar16Ptr textPtr, + int32_t textLength) +{ + if(fUnion.fFields.fLengthAndFlags & kOpenGetBuffer) { + // do not modify a string that has an "open" getBuffer(minCapacity) + return *this; + } + + const char16_t *text = textPtr; + if(text == nullptr) { + // treat as an empty string, do not alias + releaseArray(); + setToEmpty(); + return *this; + } + + if( textLength < -1 || + (textLength == -1 && !isTerminated) || + (textLength >= 0 && isTerminated && text[textLength] != 0) + ) { + setToBogus(); + return *this; + } + + releaseArray(); + + if(textLength == -1) { + // text is terminated, or else it would have failed the above test + textLength = u_strlen(text); + } + fUnion.fFields.fLengthAndFlags = kReadonlyAlias; + setArray((char16_t *)text, textLength, isTerminated ? textLength + 1 : textLength); + return *this; +} + +// setTo() analogous to the writable-aliasing constructor with the same signature +UnicodeString & +UnicodeString::setTo(char16_t *buffer, + int32_t buffLength, + int32_t buffCapacity) { + if(fUnion.fFields.fLengthAndFlags & kOpenGetBuffer) { + // do not modify a string that has an "open" getBuffer(minCapacity) + return *this; + } + + if(buffer == nullptr) { + // treat as an empty string, do not alias + releaseArray(); + setToEmpty(); + return *this; + } + + if(buffLength < -1 || buffCapacity < 0 || buffLength > buffCapacity) { + setToBogus(); + return *this; + } else if(buffLength == -1) { + // buffLength = u_strlen(buff); but do not look beyond buffCapacity + const char16_t *p = buffer, *limit = buffer + buffCapacity; + while(p != limit && *p != 0) { + ++p; + } + buffLength = (int32_t)(p - buffer); + } + + releaseArray(); + + fUnion.fFields.fLengthAndFlags = kWritableAlias; + setArray(buffer, buffLength, buffCapacity); + return *this; +} + +UnicodeString &UnicodeString::setToUTF8(StringPiece utf8) { + unBogus(); + int32_t length = utf8.length(); + int32_t capacity; + // The UTF-16 string will be at most as long as the UTF-8 string. + if(length <= US_STACKBUF_SIZE) { + capacity = US_STACKBUF_SIZE; + } else { + capacity = length + 1; // +1 for the terminating NUL. + } + char16_t *utf16 = getBuffer(capacity); + int32_t length16; + UErrorCode errorCode = U_ZERO_ERROR; + u_strFromUTF8WithSub(utf16, getCapacity(), &length16, + utf8.data(), length, + 0xfffd, // Substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + releaseBuffer(length16); + if(U_FAILURE(errorCode)) { + setToBogus(); + } + return *this; +} + +UnicodeString& +UnicodeString::setCharAt(int32_t offset, + char16_t c) +{ + int32_t len = length(); + if(cloneArrayIfNeeded() && len > 0) { + if(offset < 0) { + offset = 0; + } else if(offset >= len) { + offset = len - 1; + } + + getArrayStart()[offset] = c; + } + return *this; +} + +UnicodeString& +UnicodeString::replace(int32_t start, + int32_t _length, + UChar32 srcChar) { + char16_t buffer[U16_MAX_LENGTH]; + int32_t count = 0; + UBool isError = false; + U16_APPEND(buffer, count, U16_MAX_LENGTH, srcChar, isError); + // We test isError so that the compiler does not complain that we don't. + // If isError (srcChar is not a valid code point) then count==0 which means + // we remove the source segment rather than replacing it with srcChar. + return doReplace(start, _length, buffer, 0, isError ? 0 : count); +} + +UnicodeString& +UnicodeString::append(UChar32 srcChar) { + char16_t buffer[U16_MAX_LENGTH]; + int32_t _length = 0; + UBool isError = false; + U16_APPEND(buffer, _length, U16_MAX_LENGTH, srcChar, isError); + // We test isError so that the compiler does not complain that we don't. + // If isError then _length==0 which turns the doAppend() into a no-op anyway. + return isError ? *this : doAppend(buffer, 0, _length); +} + +UnicodeString& +UnicodeString::doReplace( int32_t start, + int32_t length, + const UnicodeString& src, + int32_t srcStart, + int32_t srcLength) +{ + // pin the indices to legal values + src.pinIndices(srcStart, srcLength); + + // get the characters from src + // and replace the range in ourselves with them + return doReplace(start, length, src.getArrayStart(), srcStart, srcLength); +} + +UnicodeString& +UnicodeString::doReplace(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) +{ + if(!isWritable()) { + return *this; + } + + int32_t oldLength = this->length(); + + // optimize (read-only alias).remove(0, start) and .remove(start, end) + if((fUnion.fFields.fLengthAndFlags&kBufferIsReadonly) && srcLength == 0) { + if(start == 0) { + // remove prefix by adjusting the array pointer + pinIndex(length); + fUnion.fFields.fArray += length; + fUnion.fFields.fCapacity -= length; + setLength(oldLength - length); + return *this; + } else { + pinIndex(start); + if(length >= (oldLength - start)) { + // remove suffix by reducing the length (like truncate()) + setLength(start); + fUnion.fFields.fCapacity = start; // not NUL-terminated any more + return *this; + } + } + } + + if(start == oldLength) { + return doAppend(srcChars, srcStart, srcLength); + } + + if(srcChars == 0) { + srcLength = 0; + } else { + // Perform all remaining operations relative to srcChars + srcStart. + // From this point forward, do not use srcStart. + srcChars += srcStart; + if (srcLength < 0) { + // get the srcLength if necessary + srcLength = u_strlen(srcChars); + } + } + + // pin the indices to legal values + pinIndices(start, length); + + // Calculate the size of the string after the replace. + // Avoid int32_t overflow. + int32_t newLength = oldLength - length; + if(srcLength > (INT32_MAX - newLength)) { + setToBogus(); + return *this; + } + newLength += srcLength; + + // Check for insertion into ourself + const char16_t *oldArray = getArrayStart(); + if (isBufferWritable() && + oldArray < srcChars + srcLength && + srcChars < oldArray + oldLength) { + // Copy into a new UnicodeString and start over + UnicodeString copy(srcChars, srcLength); + if (copy.isBogus()) { + setToBogus(); + return *this; + } + return doReplace(start, length, copy.getArrayStart(), 0, srcLength); + } + + // cloneArrayIfNeeded(doCopyArray=false) may change fArray but will not copy the current contents; + // therefore we need to keep the current fArray + char16_t oldStackBuffer[US_STACKBUF_SIZE]; + if((fUnion.fFields.fLengthAndFlags&kUsingStackBuffer) && (newLength > US_STACKBUF_SIZE)) { + // copy the stack buffer contents because it will be overwritten with + // fUnion.fFields values + u_memcpy(oldStackBuffer, oldArray, oldLength); + oldArray = oldStackBuffer; + } + + // clone our array and allocate a bigger array if needed + int32_t *bufferToDelete = 0; + if(!cloneArrayIfNeeded(newLength, getGrowCapacity(newLength), + false, &bufferToDelete) + ) { + return *this; + } + + // now do the replace + + char16_t *newArray = getArrayStart(); + if(newArray != oldArray) { + // if fArray changed, then we need to copy everything except what will change + us_arrayCopy(oldArray, 0, newArray, 0, start); + us_arrayCopy(oldArray, start + length, + newArray, start + srcLength, + oldLength - (start + length)); + } else if(length != srcLength) { + // fArray did not change; copy only the portion that isn't changing, leaving a hole + us_arrayCopy(oldArray, start + length, + newArray, start + srcLength, + oldLength - (start + length)); + } + + // now fill in the hole with the new string + us_arrayCopy(srcChars, 0, newArray, start, srcLength); + + setLength(newLength); + + // delayed delete in case srcChars == fArray when we started, and + // to keep oldArray alive for the above operations + if (bufferToDelete) { + uprv_free(bufferToDelete); + } + + return *this; +} + +// Versions of doReplace() only for append() variants. +// doReplace() and doAppend() optimize for different cases. + +UnicodeString& +UnicodeString::doAppend(const UnicodeString& src, int32_t srcStart, int32_t srcLength) { + if(srcLength == 0) { + return *this; + } + + // pin the indices to legal values + src.pinIndices(srcStart, srcLength); + return doAppend(src.getArrayStart(), srcStart, srcLength); +} + +UnicodeString& +UnicodeString::doAppend(const char16_t *srcChars, int32_t srcStart, int32_t srcLength) { + if(!isWritable() || srcLength == 0 || srcChars == nullptr) { + return *this; + } + + // Perform all remaining operations relative to srcChars + srcStart. + // From this point forward, do not use srcStart. + srcChars += srcStart; + + if(srcLength < 0) { + // get the srcLength if necessary + if((srcLength = u_strlen(srcChars)) == 0) { + return *this; + } + } + + int32_t oldLength = length(); + int32_t newLength; + if (uprv_add32_overflow(oldLength, srcLength, &newLength)) { + setToBogus(); + return *this; + } + + // Check for append onto ourself + const char16_t* oldArray = getArrayStart(); + if (isBufferWritable() && + oldArray < srcChars + srcLength && + srcChars < oldArray + oldLength) { + // Copy into a new UnicodeString and start over + UnicodeString copy(srcChars, srcLength); + if (copy.isBogus()) { + setToBogus(); + return *this; + } + return doAppend(copy.getArrayStart(), 0, srcLength); + } + + // optimize append() onto a large-enough, owned string + if((newLength <= getCapacity() && isBufferWritable()) || + cloneArrayIfNeeded(newLength, getGrowCapacity(newLength))) { + char16_t *newArray = getArrayStart(); + // Do not copy characters when + // char16_t *buffer=str.getAppendBuffer(...); + // is followed by + // str.append(buffer, length); + // or + // str.appendString(buffer, length) + // or similar. + if(srcChars != newArray + oldLength) { + us_arrayCopy(srcChars, 0, newArray, oldLength, srcLength); + } + setLength(newLength); + } + return *this; +} + +/** + * Replaceable API + */ +void +UnicodeString::handleReplaceBetween(int32_t start, + int32_t limit, + const UnicodeString& text) { + replaceBetween(start, limit, text); +} + +/** + * Replaceable API + */ +void +UnicodeString::copy(int32_t start, int32_t limit, int32_t dest) { + if (limit <= start) { + return; // Nothing to do; avoid bogus malloc call + } + char16_t* text = (char16_t*) uprv_malloc( sizeof(char16_t) * (limit - start) ); + // Check to make sure text is not null. + if (text != nullptr) { + extractBetween(start, limit, text, 0); + insert(dest, text, 0, limit - start); + uprv_free(text); + } +} + +/** + * Replaceable API + * + * NOTE: This is for the Replaceable class. There is no rep.cpp, + * so we implement this function here. + */ +UBool Replaceable::hasMetaData() const { + return true; +} + +/** + * Replaceable API + */ +UBool UnicodeString::hasMetaData() const { + return false; +} + +UnicodeString& +UnicodeString::doReverse(int32_t start, int32_t length) { + if(length <= 1 || !cloneArrayIfNeeded()) { + return *this; + } + + // pin the indices to legal values + pinIndices(start, length); + if(length <= 1) { // pinIndices() might have shrunk the length + return *this; + } + + char16_t *left = getArrayStart() + start; + char16_t *right = left + length - 1; // -1 for inclusive boundary (length>=2) + char16_t swap; + UBool hasSupplementary = false; + + // Before the loop we know left<right because length>=2. + do { + hasSupplementary |= (UBool)U16_IS_LEAD(swap = *left); + hasSupplementary |= (UBool)U16_IS_LEAD(*left++ = *right); + *right-- = swap; + } while(left < right); + // Make sure to test the middle code unit of an odd-length string. + // Redundant if the length is even. + hasSupplementary |= (UBool)U16_IS_LEAD(*left); + + /* if there are supplementary code points in the reversed range, then re-swap their surrogates */ + if(hasSupplementary) { + char16_t swap2; + + left = getArrayStart() + start; + right = left + length - 1; // -1 so that we can look at *(left+1) if left<right + while(left < right) { + if(U16_IS_TRAIL(swap = *left) && U16_IS_LEAD(swap2 = *(left + 1))) { + *left++ = swap2; + *left++ = swap; + } else { + ++left; + } + } + } + + return *this; +} + +UBool +UnicodeString::padLeading(int32_t targetLength, + char16_t padChar) +{ + int32_t oldLength = length(); + if(oldLength >= targetLength || !cloneArrayIfNeeded(targetLength)) { + return false; + } else { + // move contents up by padding width + char16_t *array = getArrayStart(); + int32_t start = targetLength - oldLength; + us_arrayCopy(array, 0, array, start, oldLength); + + // fill in padding character + while(--start >= 0) { + array[start] = padChar; + } + setLength(targetLength); + return true; + } +} + +UBool +UnicodeString::padTrailing(int32_t targetLength, + char16_t padChar) +{ + int32_t oldLength = length(); + if(oldLength >= targetLength || !cloneArrayIfNeeded(targetLength)) { + return false; + } else { + // fill in padding character + char16_t *array = getArrayStart(); + int32_t length = targetLength; + while(--length >= oldLength) { + array[length] = padChar; + } + setLength(targetLength); + return true; + } +} + +//======================================== +// Hashing +//======================================== +int32_t +UnicodeString::doHashCode() const +{ + /* Delegate hash computation to uhash. This makes UnicodeString + * hashing consistent with char16_t* hashing. */ + int32_t hashCode = ustr_hashUCharsN(getArrayStart(), length()); + if (hashCode == kInvalidHashCode) { + hashCode = kEmptyHashCode; + } + return hashCode; +} + +//======================================== +// External Buffer +//======================================== + +char16_t * +UnicodeString::getBuffer(int32_t minCapacity) { + if(minCapacity>=-1 && cloneArrayIfNeeded(minCapacity)) { + fUnion.fFields.fLengthAndFlags|=kOpenGetBuffer; + setZeroLength(); + return getArrayStart(); + } else { + return nullptr; + } +} + +void +UnicodeString::releaseBuffer(int32_t newLength) { + if(fUnion.fFields.fLengthAndFlags&kOpenGetBuffer && newLength>=-1) { + // set the new fLength + int32_t capacity=getCapacity(); + if(newLength==-1) { + // the new length is the string length, capped by fCapacity + const char16_t *array=getArrayStart(), *p=array, *limit=array+capacity; + while(p<limit && *p!=0) { + ++p; + } + newLength=(int32_t)(p-array); + } else if(newLength>capacity) { + newLength=capacity; + } + setLength(newLength); + fUnion.fFields.fLengthAndFlags&=~kOpenGetBuffer; + } +} + +//======================================== +// Miscellaneous +//======================================== +UBool +UnicodeString::cloneArrayIfNeeded(int32_t newCapacity, + int32_t growCapacity, + UBool doCopyArray, + int32_t **pBufferToDelete, + UBool forceClone) { + // default parameters need to be static, therefore + // the defaults are -1 to have convenience defaults + if(newCapacity == -1) { + newCapacity = getCapacity(); + } + + // while a getBuffer(minCapacity) is "open", + // prevent any modifications of the string by returning false here + // if the string is bogus, then only an assignment or similar can revive it + if(!isWritable()) { + return false; + } + + /* + * We need to make a copy of the array if + * the buffer is read-only, or + * the buffer is refCounted (shared), and refCount>1, or + * the buffer is too small. + * Return false if memory could not be allocated. + */ + if(forceClone || + fUnion.fFields.fLengthAndFlags & kBufferIsReadonly || + (fUnion.fFields.fLengthAndFlags & kRefCounted && refCount() > 1) || + newCapacity > getCapacity() + ) { + // check growCapacity for default value and use of the stack buffer + if(growCapacity < 0) { + growCapacity = newCapacity; + } else if(newCapacity <= US_STACKBUF_SIZE && growCapacity > US_STACKBUF_SIZE) { + growCapacity = US_STACKBUF_SIZE; + } + + // save old values + char16_t oldStackBuffer[US_STACKBUF_SIZE]; + char16_t *oldArray; + int32_t oldLength = length(); + int16_t flags = fUnion.fFields.fLengthAndFlags; + + if(flags&kUsingStackBuffer) { + U_ASSERT(!(flags&kRefCounted)); /* kRefCounted and kUsingStackBuffer are mutally exclusive */ + if(doCopyArray && growCapacity > US_STACKBUF_SIZE) { + // copy the stack buffer contents because it will be overwritten with + // fUnion.fFields values + us_arrayCopy(fUnion.fStackFields.fBuffer, 0, oldStackBuffer, 0, oldLength); + oldArray = oldStackBuffer; + } else { + oldArray = nullptr; // no need to copy from the stack buffer to itself + } + } else { + oldArray = fUnion.fFields.fArray; + U_ASSERT(oldArray!=nullptr); /* when stack buffer is not used, oldArray must have a non-nullptr reference */ + } + + // allocate a new array + if(allocate(growCapacity) || + (newCapacity < growCapacity && allocate(newCapacity)) + ) { + if(doCopyArray) { + // copy the contents + // do not copy more than what fits - it may be smaller than before + int32_t minLength = oldLength; + newCapacity = getCapacity(); + if(newCapacity < minLength) { + minLength = newCapacity; + } + if(oldArray != nullptr) { + us_arrayCopy(oldArray, 0, getArrayStart(), 0, minLength); + } + setLength(minLength); + } else { + setZeroLength(); + } + + // release the old array + if(flags & kRefCounted) { + // the array is refCounted; decrement and release if 0 + u_atomic_int32_t *pRefCount = ((u_atomic_int32_t *)oldArray - 1); + if(umtx_atomic_dec(pRefCount) == 0) { + if(pBufferToDelete == 0) { + // Note: cast to (void *) is needed with MSVC, where u_atomic_int32_t + // is defined as volatile. (Volatile has useful non-standard behavior + // with this compiler.) + uprv_free((void *)pRefCount); + } else { + // the caller requested to delete it himself + *pBufferToDelete = (int32_t *)pRefCount; + } + } + } + } else { + // not enough memory for growCapacity and not even for the smaller newCapacity + // reset the old values for setToBogus() to release the array + if(!(flags&kUsingStackBuffer)) { + fUnion.fFields.fArray = oldArray; + } + fUnion.fFields.fLengthAndFlags = flags; + setToBogus(); + return false; + } + } + return true; +} + +// UnicodeStringAppendable ------------------------------------------------- *** + +UnicodeStringAppendable::~UnicodeStringAppendable() {} + +UBool +UnicodeStringAppendable::appendCodeUnit(char16_t c) { + return str.doAppend(&c, 0, 1).isWritable(); +} + +UBool +UnicodeStringAppendable::appendCodePoint(UChar32 c) { + char16_t buffer[U16_MAX_LENGTH]; + int32_t cLength = 0; + UBool isError = false; + U16_APPEND(buffer, cLength, U16_MAX_LENGTH, c, isError); + return !isError && str.doAppend(buffer, 0, cLength).isWritable(); +} + +UBool +UnicodeStringAppendable::appendString(const char16_t *s, int32_t length) { + return str.doAppend(s, 0, length).isWritable(); +} + +UBool +UnicodeStringAppendable::reserveAppendCapacity(int32_t appendCapacity) { + return str.cloneArrayIfNeeded(str.length() + appendCapacity); +} + +char16_t * +UnicodeStringAppendable::getAppendBuffer(int32_t minCapacity, + int32_t desiredCapacityHint, + char16_t *scratch, int32_t scratchCapacity, + int32_t *resultCapacity) { + if(minCapacity < 1 || scratchCapacity < minCapacity) { + *resultCapacity = 0; + return nullptr; + } + int32_t oldLength = str.length(); + if(minCapacity <= (kMaxCapacity - oldLength) && + desiredCapacityHint <= (kMaxCapacity - oldLength) && + str.cloneArrayIfNeeded(oldLength + minCapacity, oldLength + desiredCapacityHint)) { + *resultCapacity = str.getCapacity() - oldLength; + return str.getArrayStart() + oldLength; + } + *resultCapacity = scratchCapacity; + return scratch; +} + +U_NAMESPACE_END + +U_NAMESPACE_USE + +U_CAPI int32_t U_EXPORT2 +uhash_hashUnicodeString(const UElement key) { + const UnicodeString *str = (const UnicodeString*) key.pointer; + return (str == nullptr) ? 0 : str->hashCode(); +} + +// Moved here from uhash_us.cpp so that using a UVector of UnicodeString* +// does not depend on hashtable code. +U_CAPI UBool U_EXPORT2 +uhash_compareUnicodeString(const UElement key1, const UElement key2) { + const UnicodeString *str1 = (const UnicodeString*) key1.pointer; + const UnicodeString *str2 = (const UnicodeString*) key2.pointer; + if (str1 == str2) { + return true; + } + if (str1 == nullptr || str2 == nullptr) { + return false; + } + return *str1 == *str2; +} + +#ifdef U_STATIC_IMPLEMENTATION +/* +This should never be called. It is defined here to make sure that the +virtual vector deleting destructor is defined within unistr.cpp. +The vector deleting destructor is already a part of UObject, +but defining it here makes sure that it is included with this object file. +This makes sure that static library dependencies are kept to a minimum. +*/ +#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 1100 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +static void uprv_UnicodeStringDummy() { + delete [] (new UnicodeString[2]); +} +#pragma GCC diagnostic pop +#endif +#endif |