/* ******************************************************************************* * * * COPYRIGHT: * * Copyright (C) 1999, International Business Machines Corporation and * * others. All Rights Reserved. * * (C) Copyright International Business Machines Corporation, 1998-1999 * * Licensed Material - Program-Property of IBM - All Rights Reserved. * * US Government Users Restricted Rights - Use, duplication, or disclosure * * restricted by GSA ADP Schedule Contract with IBM Corp. * * * ******************************************************************************* * * 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. ******************************************************************************* */ #include "unistr.h" #include "locid.h" #include "cstring.h" #include "cmemory.h" #include "ustring.h" #include "mutex.h" #if 0 //DEBUGGING #include void print(const UnicodeString& s, const char *name) { UChar 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 UChar *s, int32_t len, const char *name) { UChar 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 // move u_arrayCompare to utypes.h ?? inline int8_t u_arrayCompare(const UChar *src, int32_t srcStart, const UChar *dst, int32_t dstStart, int32_t count) {return icu_memcmp(src+srcStart, dst+dstStart, (size_t)(count*sizeof(*src)));} // need to copy areas that may overlap inline void us_arrayCopy(const UChar *src, int32_t srcStart, UChar *dst, int32_t dstStart, int32_t count) { if(count>0) { icu_memmove(dst+dstStart, src+srcStart, (size_t)(count*sizeof(*src))); } } // static initialization const UChar UnicodeString::fgInvalidUChar = 0xFFFF; const int32_t UnicodeString::kGrowSize = 0x80; const int32_t UnicodeString::kInvalidHashCode = 0; const int32_t UnicodeString::kEmptyHashCode = 1; UConverter* UnicodeString::fgDefaultConverter = 0; //======================================== // Constructors //======================================== UnicodeString::UnicodeString() : fArray(fStackBuffer), fLength(0), fCapacity(US_STACKBUF_SIZE), fRefCounted(FALSE), fHashCode(kEmptyHashCode), fBogus(FALSE) {} UnicodeString::UnicodeString(int32_t capacity) : fArray(0), fLength(0), fCapacity(0), fRefCounted(FALSE), fHashCode(kEmptyHashCode), fBogus(FALSE) { fArray = allocate(capacity, fCapacity); if(! fArray) { setToBogus(); return; } setRefCount(1); } UnicodeString::UnicodeString(UChar ch) : fArray(fStackBuffer), fLength(0), fCapacity(US_STACKBUF_SIZE), fRefCounted(FALSE), fHashCode(kEmptyHashCode), fBogus(FALSE) { doReplace(0, 0, &ch, 0, 1); } UnicodeString::UnicodeString(const UChar *text) : fArray(fStackBuffer), fLength(0), fCapacity(US_STACKBUF_SIZE), fRefCounted(FALSE), fHashCode(kEmptyHashCode), fBogus(FALSE) { doReplace(0, 0, text, 0, u_strlen(text)); } UnicodeString::UnicodeString( const UChar *text, int32_t textLength) : fArray(fStackBuffer), fLength(0), fCapacity(US_STACKBUF_SIZE), fRefCounted(FALSE), fHashCode(kEmptyHashCode), fBogus(FALSE) { doReplace(0, 0, text, 0, textLength); } UnicodeString::UnicodeString(const char *codepageData, const char *codepage) : fArray(fStackBuffer), fLength(0), fCapacity(US_STACKBUF_SIZE), fRefCounted(FALSE), fHashCode(kEmptyHashCode), fBogus(FALSE) { if(codepageData != 0) doCodepageCreate(codepageData, icu_strlen(codepageData), codepage); } UnicodeString::UnicodeString(const char *codepageData, int32_t dataLength, const char *codepage) : fArray(fStackBuffer), fLength(0), fCapacity(US_STACKBUF_SIZE), fRefCounted(FALSE), fHashCode(kEmptyHashCode), fBogus(FALSE) { doCodepageCreate(codepageData, dataLength, codepage); } //======================================== // Destructor //======================================== UnicodeString::~UnicodeString() { // decrement ref count and reclaim storage, if owned if(fRefCounted && removeRef() == 0) delete [] fArray; } //======================================== // Assignment //======================================== UnicodeString& UnicodeString::operator= (const UnicodeString& src) { // if src is bogus, or we're bogus, or assigning to ourselves, do nothing if(fBogus || src.isBogus() || this == &src) return *this; // if src is ref counted, point ourselves at its array if(src.fRefCounted) { // if we're ref counted, decrement our current ref count if(fRefCounted && removeRef() == 0) delete [] fArray; fArray = src.fArray; fLength = src.fLength; fCapacity = src.fCapacity; fHashCode = src.fHashCode; fRefCounted = TRUE; addRef(); } // if src isn't ref counted, just do a replace else { doReplace(0, fLength, src.fArray, 0, src.fLength); fHashCode = src.fHashCode; } return *this; } //======================================== // Miscellaneous operations //======================================== int32_t UnicodeString::numDisplayCells( UTextOffset start, int32_t length, bool_t asian) const { // pin indices to legal values pinIndices(start, length); UChar c; int32_t result = 0; UTextOffset limit = start + length; while(start < limit) { c = getArrayStart()[start]; switch(Unicode::getCellWidth(c)) { case Unicode::ZERO_WIDTH: break;; case Unicode::HALF_WIDTH: result += 1; break; case Unicode::FULL_WIDTH: result += 2; break; case Unicode::NEUTRAL: result += (asian ? 2 : 1); break; } ++start; } return result; } UCharReference UnicodeString::operator[] (UTextOffset pos) { return UCharReference(this, pos); } //======================================== // Read-only implementation //======================================== int8_t UnicodeString::doCompare( UTextOffset start, int32_t length, const UnicodeString& src, UTextOffset srcStart, int32_t srcLength) const { // pin indices to legal values pinIndices(start, length); // get the correct pointer const UChar *chars = getArrayStart(); // compare the characters return (src.compare(srcStart, srcLength, chars, start, length) * -1); } int8_t UnicodeString::doCompare( UTextOffset start, int32_t length, const UChar *srcChars, UTextOffset srcStart, int32_t srcLength) const { // pin indices to legal values pinIndices(start, length); // get the correct pointer const UChar *chars = getArrayStart(); // we're comparing different lengths if(length != srcLength) { // compare the minimum # of characters int32_t minLength = (length < srcLength ? length : srcLength); const UChar *minLimit = chars + minLength; const UChar *limit = chars + length; int8_t result; // adjust for starting offsets chars += start; srcChars += srcStart; while(chars < minLimit) { result = (*chars - *srcChars); if(result != 0) return result; ++chars; ++srcChars; } // if we got here, the leading portions are identical return (chars < limit ? 1 : -1); } // compare two identical lengths, use u_arrayCompare else return u_arrayCompare(chars, start, srcChars, srcStart, length); } void UnicodeString::doExtract(UTextOffset start, int32_t length, UChar *dst, UTextOffset dstStart) const { // pin indices to legal values pinIndices(start, length); us_arrayCopy(getArrayStart(), start, dst, dstStart, length); } UTextOffset UnicodeString::doIndexOf(UChar c, UTextOffset start, int32_t length) const { // pin indices pinIndices(start, length); // find the first occurrence of c const UChar *begin = getArrayStart() + start; const UChar *limit = begin + length; while(begin < limit && *begin != c) ++begin; return (begin == limit ? -1 : begin - getArrayStart()); } UTextOffset UnicodeString::doLastIndexOf(UChar c, UTextOffset start, int32_t length) const { // pin indices pinIndices(start, length); const UChar *begin = getArrayStart() + start + length; const UChar *limit = begin - length; while(begin > limit && *begin != c) --begin; return (begin == limit ? -1 : begin - getArrayStart()); } //======================================== // Write implementation //======================================== UnicodeString& UnicodeString::setCharAt(UTextOffset offset, UChar c) { if(offset < 0) offset = 0; else if(offset >= fLength) offset = fLength - 1; doSetCharAt(offset, c); fHashCode = kInvalidHashCode; return *this; } UnicodeString& UnicodeString::toUpper() { return toUpper(Locale::getDefault()); } UnicodeString& UnicodeString::toLower() { return toLower(Locale::getDefault()); } UnicodeString& UnicodeString::toUpper(const Locale& locale) { UTextOffset start = 0; UTextOffset limit = fLength; UChar c; UnicodeString lang; locale.getLanguage(lang); // The German sharp S character (U+00DF)'s uppercase equivalent is // "SS", making it the only character that expands to two characters // when its case is changed (we don't automatically convert "SS" to // U+00DF going to lowercase because it can only be determined from // knowing the language whether a particular "SS" should map to // U+00DF or "ss"). So we make a preliminary pass through the // string looking for sharp S characters and then go back and make // room for the extra capital Ses if we find any. [For performance, // we only do this extra work if the language is actually German] if(lang == "de") { UChar SS [] = { 0x0053, 0x0053 }; while(start < limit) { c = getArrayStart()[start]; // A sharp s needs to be replaced with two capital S's. if(c == 0x00DF) { doReplace(start, 1, SS, 0, 2); start++; limit++; } // Otherwise, the case conversion can be handled by the Unicode unit. else if(Unicode::isLowerCase(c)) doSetCharAt(start, Unicode::toUpperCase(c)); // If no conversion is necessary, do nothing ++start; } } // If the specfied language is Turkish, then we have to special-case // for the Turkish dotted and dotless Is. The regular lowercase i // maps to the capital I with a dot (U+0130), and the lowercase i // without the dot (U+0131) maps to the regular capital I else if(lang == "tr") { while(start < limit) { c = getArrayStart()[start]; if(c == 0x0069/*'i'*/) doSetCharAt(start, 0x0130); else if(c == 0x0131) doSetCharAt(start, 0x0049/*'I'*/); else if(Unicode::isLowerCase(c)) doSetCharAt(start, Unicode::toUpperCase(c)); ++start; } } else { while(start < limit) { c = getArrayStart()[start]; if(Unicode::isLowerCase(c)) doSetCharAt(start, Unicode::toUpperCase(c)); ++start; } } fHashCode = kInvalidHashCode; return *this; } UnicodeString& UnicodeString::toLower(const Locale& locale) { UTextOffset start = 0; UTextOffset limit = fLength; UChar c; UnicodeString lang; locale.getLanguage(lang); // if the specfied language is Turkish, then we have to special-case // for the Turkish dotted and dotless Is. The capital I with a dot // (U+0130) maps to the regular lowercase i, and the regular capital // I maps to the lowercase i without the dot (U+0131) if(lang == "tr") { while(start < limit) { c = getArrayStart()[start]; if(c == 0x0049) // 'I' doSetCharAt(start, 0x0131); else if(c == 0x0130) doSetCharAt(start, 0x0069); // 'i' else if(Unicode::isUpperCase(c) || Unicode::isTitleCase(c)) doSetCharAt(start, Unicode::toLowerCase(c)); ++start; } } // if the specfied language is Greek, then we have to special-case // for the capital letter sigma (U+3A3), which has two lower-case // forms. If the character following the capital sigma is a letter, // we use the medial form (U+3C3); otherwise, we use the final form // (U+3C2). else if(lang == "el") { while(start < limit) { c = getArrayStart()[start]; if(c == 0x3a3) { if(start + 1 < limit && Unicode::isLetter(getArrayStart()[start + 1])) doSetCharAt(start, 0x3C3); else doSetCharAt(start, 0x3C2); } else if(Unicode::isUpperCase(c) || Unicode::isTitleCase(c)) doSetCharAt(start, Unicode::toLowerCase(c)); ++start; } } // if the specified language is anything other than Turkish or // Greek, we rely on the Unicode class to do all our case mapping-- // there are no other special cases else { while(start < limit) { c = getArrayStart()[start]; if(Unicode::isUpperCase(c) || Unicode::isTitleCase(c)) doSetCharAt(start, Unicode::toLowerCase(c)); ++start; } } fHashCode = kInvalidHashCode; return *this; } // for speed, no bounds checking is performed and the hash code isn't changed UnicodeString& UnicodeString::doSetCharAt(UTextOffset offset, UChar c) { // clone our array, if necessary cloneArrayIfNeeded(); // set the character fArray[ (fRefCounted ? offset + 1 : offset) ] = c; return *this; } UnicodeString& UnicodeString::doReplace( UTextOffset start, int32_t length, const UnicodeString& src, UTextOffset srcStart, int32_t srcLength) { // pin the indices to legal values src.pinIndices(srcStart, srcLength); // get the characters from src const UChar *chars = src.getArrayStart(); // and replace the range in ourselves with them doReplace(start, length, chars, srcStart, srcLength); return *this; } UnicodeString& UnicodeString::doReplace(UTextOffset start, int32_t length, const UChar *srcChars, UTextOffset srcStart, int32_t srcLength) { // if we're bogus, do nothing if(fBogus) return *this; bool_t deleteWhenDone = FALSE; UChar *bufferToDelete = 0; // clone our array, if necessary cloneArrayIfNeeded(); // pin the indices to legal values pinIndices(start, length); // calculate the size of the string after the replace int32_t newSize = fLength - length + srcLength; // allocate a bigger array if needed if( newSize > getCapacity() ) { // allocate at minimum the current capacity + needed space int32_t tempLength; UChar *temp = allocate(fCapacity + srcLength, tempLength); if(! temp) { setToBogus(); return *this; } // if we're not currently ref counted, shift the array right by one if(fRefCounted == FALSE) us_arrayCopy(fArray, 0, temp, 1, fLength); // otherwise, copy the old array into temp, including the ref count else us_arrayCopy(fArray, 0, temp, 0, fLength + 1); // delete the old array if we were ref counted if(fRefCounted && removeRef() == 0) { // if the srcChars array is the same as this object's array, // don't delete it until the end of the method. this can happen // in code like UnicodeString s = "foo"; s += s; if(srcChars != getArrayStart()) delete [] fArray; else { deleteWhenDone = TRUE; bufferToDelete = fArray; } } // use the new array fCapacity = tempLength; fArray = temp; setRefCount(1); } // now do the replace // first copy the portion that isn't changing, leaving a hole us_arrayCopy(getArrayStart(), start + length, getArrayStart(), start + srcLength, fLength - (start + length)); // now fill in the hole with the new string us_arrayCopy(srcChars, srcStart, getArrayStart(), start, srcLength); fLength = newSize; fHashCode = kInvalidHashCode; if(deleteWhenDone) delete [] bufferToDelete; return *this; } /** * Replaceable API */ void UnicodeString::handleReplaceBetween(UTextOffset start, UTextOffset limit, const UnicodeString& text) { replaceBetween(start, limit, text); } UnicodeString& UnicodeString::doReverse(UTextOffset start, int32_t length) { // if we're bogus, do nothing if(fBogus) return *this; // clone our array, if necessary cloneArrayIfNeeded(); // pin the indices to legal values pinIndices(start, length); UChar *left = getArrayStart() + start; UChar *right = getArrayStart() + start + length; UChar swap; while(left < --right) { swap = *left; *left++ = *right; *right = swap; } fHashCode = kInvalidHashCode; return *this; } //======================================== // Hashing //======================================== int32_t UnicodeString::doHashCode() { const UChar *key = getArrayStart(); int32_t len = fLength; int32_t hash = kInvalidHashCode; const UChar *limit = key + len; int32_t inc = (len >= 128 ? len/64 : 1); /* We compute the hash by iterating sparsely over 64 (at most) characters spaced evenly through the string. For each character, we multiply the previous hash value by a prime number and add the new character in, in the manner of an additive linear congruential random number generator, thus producing a pseudorandom deterministic value which should be well distributed over the output range. [LIU] */ while(key < limit) { hash = (hash * 37) + *key; key += inc; } if(hash == kInvalidHashCode) hash = kEmptyHashCode; fHashCode = hash; return fHashCode; } //======================================== // Bogusify? //======================================== void UnicodeString::setToBogus() { fBogus = TRUE; if(fRefCounted) { if(removeRef() == 0) delete [] fArray; fArray = 0; fCapacity = fLength = 0; } fHashCode = kInvalidHashCode; } //======================================== // Codeset conversion //======================================== int32_t UnicodeString::extract(UTextOffset start, int32_t length, char *dst, const char *codepage) const { // if we're bogus or there's nothing to convert, do nothing if(fBogus || length == 0) return 0; // pin the indices to legal values pinIndices(start, length); int32_t convertedLen = 0; // set up the conversion parameters int32_t sourceLen = length; const UChar *mySource = getArrayStart() + start; const UChar *mySourceEnd = mySource + length; char *myTarget = dst; char *myTargetLimit; UErrorCode status = U_ZERO_ERROR; int32_t arraySize = 0x0FFFFFFF; // create the converter UConverter *converter = 0; // if the codepage is the default, use our cache if(codepage == 0) converter = getDefaultConverter(status); else converter = ucnv_open(codepage, &status); // if we failed, set the appropriate flags and return if(U_FAILURE(status)) { // close the converter if(codepage == 0) releaseDefaultConverter(converter); else ucnv_close(converter); return 0; } myTargetLimit = myTarget + arraySize; if(myTargetLimit < myTarget) /* ptr wrapped around: pin to U_MAX_PTR */ myTargetLimit = (char*)U_MAX_PTR; // perform the conversion // there is no loop here since we assume the buffer is large enough ucnv_fromUnicode(converter, &myTarget, myTargetLimit, &mySource, mySourceEnd, NULL, TRUE, &status); // close the converter if(codepage == 0) releaseDefaultConverter(converter); else ucnv_close(converter); return (myTarget - dst); } void UnicodeString::doCodepageCreate(const char *codepageData, int32_t dataLength, const char *codepage) { // if there's nothing to convert, do nothing if(codepageData == 0 || dataLength == 0) return; // set up the conversion parameters int32_t sourceLen = dataLength; const char *mySource = codepageData; const char *mySourceEnd = mySource + sourceLen; UChar *myTarget = getArrayStart(); UErrorCode status = U_ZERO_ERROR; int32_t arraySize = getCapacity(); // create the converter UConverter *converter = 0; // if the codepage is the default, use our cache converter = (codepage == 0 ? getDefaultConverter(status) : ucnv_open(codepage, &status)); // if we failed, set the appropriate flags and return if(U_FAILURE(status)) { // close the converter if(codepage == 0) releaseDefaultConverter(converter); else ucnv_close(converter); setToBogus(); return; } // perform the conversion do { // reset the error code status = U_ZERO_ERROR; // perform the conversion ucnv_toUnicode(converter, &myTarget, myTarget + arraySize, &mySource, mySourceEnd, NULL, TRUE, &status); // update the conversion parameters fLength = myTarget - getArrayStart(); arraySize = getCapacity() - fLength; // allocate more space and copy data, if needed if(fLength < dataLength) { int32_t tempCapacity; UChar *temp = allocate(fCapacity, tempCapacity); if(! temp) { // close the converter if(codepage == 0) releaseDefaultConverter(converter); else ucnv_close(converter); // set flags and return setToBogus(); return; } // if we're not currently ref counted, shift the array right by one if(fRefCounted == FALSE) us_arrayCopy(fArray, 0, temp, 1, fLength); // otherwise, copy the old array into temp, including the ref count else us_arrayCopy(fArray, 0, temp, 0, fLength + 1); if(fRefCounted && removeRef() == 0) delete [] fArray; fArray = temp; fCapacity = tempCapacity; setRefCount(1); myTarget = getArrayStart() + fLength; arraySize = getCapacity() - fLength; } } while(status == U_INDEX_OUTOFBOUNDS_ERROR); fHashCode = kInvalidHashCode; // close the converter if(codepage == 0) releaseDefaultConverter(converter); else ucnv_close(converter); } //======================================== // External Buffer //======================================== UnicodeString::UnicodeString(UChar *buff, int32_t bufLength, int32_t buffCapacity) : fArray(buff), fLength(bufLength), fCapacity(buffCapacity), fRefCounted(FALSE), fHashCode(kInvalidHashCode), fBogus(FALSE) {} const UChar* UnicodeString::getUChars() const { // if we're bogus, do nothing if(fBogus) return 0; // clone our array, if necessary ((UnicodeString*)this)->cloneArrayIfNeeded(); // no room for null, resize if(getCapacity() <= fLength) { // allocate at minimum the current capacity + needed space int32_t tempLength; UChar *temp = allocate(fCapacity + 1, tempLength); if(! temp) { ((UnicodeString*)this)->setToBogus(); return 0; } // if we're not currently ref counted, shift the array right by one if(fRefCounted == FALSE) us_arrayCopy(fArray, 0, temp, 1, fLength); // otherwise, copy the old array into temp, including the ref count else us_arrayCopy(fArray, 0, temp, 0, fLength + 1); // delete the old array if(fRefCounted && ((UnicodeString*)this)->removeRef() == 0) delete [] ((UnicodeString*)this)->fArray; // use the new array ((UnicodeString*)this)->fCapacity = tempLength; ((UnicodeString*)this)->fArray = temp; ((UnicodeString*)this)->setRefCount(1); } // tack on a trailing null fArray[(fRefCounted ? 1 : 0) + fLength] = 0; return getArrayStart(); } UChar* UnicodeString::orphanStorage() { // if we're bogus, do nothing if(fBogus) return 0; // clone our array, if necessary ((UnicodeString*)this)->cloneArrayIfNeeded(); // if we're ref counted, get rid of the leading ref count if(fRefCounted) { us_arrayCopy(getArrayStart(), 0, fArray, 0, fLength); } UChar *retVal = fArray; fArray = fStackBuffer; fLength = 0; fCapacity = US_STACKBUF_SIZE; fHashCode = kEmptyHashCode; return retVal; } //======================================== // Miscellaneous //======================================== void UnicodeString::pinIndices(UTextOffset& start, int32_t& length) const { // pin indices if(length < 0 || start < 0) start = length = 0; else { if(length > (fLength - start)) length = (fLength - start); } } void UnicodeString::cloneArrayIfNeeded() { // if we're ref counted, make a copy of the buffer if necessary if(fRefCounted && refCount() > 1) { UChar *copy = new UChar [ fCapacity ]; if( ! copy ) { setToBogus(); return; } // copy the current shared array into our new array us_arrayCopy(fArray, 0, copy, 0, fLength + 1); // remove a reference from the current shared array // if there are no more references to the current shared array, // after we remove the reference, delete the array if(removeRef() == 0) delete [] fArray; // make our array point to the new copy and set the ref count to one fArray = copy; setRefCount(1); } } // private function for C API U_CFUNC const UChar* T_UnicodeString_getUChars(const UnicodeString *s) { return s->getUChars(); } //======================================== // Default converter caching //======================================== UConverter* UnicodeString::getDefaultConverter(UErrorCode &status) { UConverter *converter = 0; if(fgDefaultConverter != 0) { Mutex lock; // need to check to make sure it wasn't taken out from under us if(fgDefaultConverter != 0) { converter = fgDefaultConverter; fgDefaultConverter = 0; } } // if the cache was empty, create a converter if(converter == 0) { converter = ucnv_open(0, &status); if(U_FAILURE(status)) return 0; } return converter; } void UnicodeString::releaseDefaultConverter(UConverter *converter) { if(fgDefaultConverter == 0) { Mutex lock; if(fgDefaultConverter == 0) { fgDefaultConverter = converter; converter = 0; } } // it's safe to close a NULL converter ucnv_close(converter); } //======================================== // Streaming (to be removed) //======================================== #include #include "unistrm.h" #include "filestrm.h" inline uint8_t icu_hibyte(uint16_t x) { return (uint8_t)(x >> 8); } inline uint8_t icu_lobyte(uint16_t x) { return (uint8_t)(x & 0xff); } inline uint16_t icu_hiword(uint32_t x) { return (uint16_t)(x >> 16); } inline uint16_t icu_loword(uint32_t x) { return (uint16_t)(x & 0xffff); } inline void writeLong(FileStream *os, int32_t x) { uint16_t word = icu_hiword((uint32_t)x); T_FileStream_putc(os, icu_hibyte(word)); T_FileStream_putc(os, icu_lobyte(word)); word = icu_loword((uint32_t)x); T_FileStream_putc(os, icu_hibyte(word)); T_FileStream_putc(os, icu_lobyte(word)); } inline int32_t readLong(FileStream *is) { int32_t x = 0; uint16_t byte; byte = T_FileStream_getc(is); x |= byte; byte = T_FileStream_getc(is); x = (x << 8) | byte; byte = T_FileStream_getc(is); x = (x << 8) | byte; byte = T_FileStream_getc(is); x = (x << 8) | byte; return x; } inline void writeUChar(FileStream *os, UChar c) { T_FileStream_putc(os, icu_hibyte(c)); T_FileStream_putc(os, icu_lobyte(c)); } inline UChar readUChar(FileStream *is) { UChar c = 0; uint16_t byte; byte = T_FileStream_getc(is); c |= byte; byte = T_FileStream_getc(is); c = (c << 8) | byte; return c; } void UnicodeStringStreamer::streamOut(const UnicodeString *s, FileStream *os) { if(!T_FileStream_error(os)) writeLong(os, s->fLength); const UChar *c = s->getArrayStart(); const UChar *end = c + s->fLength; while(c != end && ! T_FileStream_error(os)) writeUChar(os, *c++); } void UnicodeStringStreamer::streamIn(UnicodeString *s, FileStream *is) { int32_t newSize; // handle error conditions if(T_FileStream_error(is) || T_FileStream_eof(is)) { s->setToBogus(); return; } newSize = readLong(is); if((newSize < 0) || T_FileStream_error(is) || ((newSize > 0) && T_FileStream_eof(is))) { s->setToBogus(); //error condition return; } // clone s's array, if needed s->cloneArrayIfNeeded(); // if the string isn't big enough to hold the data, enlarge it if(s->getCapacity() < newSize) { int32_t tempLength; UChar *temp = s->allocate(newSize, tempLength); if(! temp) { s->setToBogus(); return; } // if s is not currently ref counted, shift the array right by one if(s->fRefCounted == FALSE) us_arrayCopy(s->fArray, 0, temp, 1, s->fLength); // otherwise, copy the old array into temp, including the ref count else us_arrayCopy(s->fArray, 0, temp, 0, s->fLength + 1); // delete the old array if s is ref counted if(s->fRefCounted && s->removeRef() == 0) delete [] s->fArray; // use the new array s->fCapacity = tempLength; s->fArray = temp; s->setRefCount(1); } UChar *c = s->getArrayStart(); UChar *end = c + newSize; while(c < end && ! (T_FileStream_error(is) || T_FileStream_eof(is))) *c++ = readUChar(is); // couldn't read all chars if(c < end) { s->setToBogus(); return; } s->fLength = newSize; } // console IO ostream& operator<<(ostream& stream, const UnicodeString& s) { UTextOffset i; UChar c; int32_t saveFlags = stream.flags(); stream << hex; for(i = 0; i < s.length(); i++) { c = s.charAt(i); if((c >= ' ' && c <= '~') || c == '\n') stream << (char)c; else stream << "[0x" << c << "]"; } stream.flush(); stream.setf(saveFlags & ios::basefield, ios::basefield); return stream; }