blob: 5e95cb30d3524f49e4406af1dc658cf65ab6e9f7 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 1999, 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.
*******************************************************************************
*/
#include "unicode/utypes.h"
#include "unicode/putil.h"
#include "unicode/locid.h"
#include "cstring.h"
#include "cmemory.h"
#include "unicode/ustring.h"
#include "mutex.h"
#include "unicode/unistr.h"
#if 0
//DEBUGGING
#include <iostream.h>
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
// 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) {
uprv_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(bool_t isTerminated,
UChar *text,
int32_t textLength)
: fArray(text),
fLength(textLength != -1 || !isTerminated ? textLength : u_strlen(text)),
fCapacity(isTerminated ? fLength + 1 : fLength),
fRefCounted(FALSE),
fHashCode(kInvalidHashCode),
fBogus(FALSE)
{
if(fLength < 0) {
setToBogus();
}
}
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, uprv_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)
{
if(codepageData != 0) {
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 assigning to ourselves, do nothing
if(this == &src) {
return *this;
}
// if src is bogus, set ourselves to bogus
if(src.isBogus()) {
setToBogus();
return *this;
}
// if src is aliased or ref counted, point ourselves at its array
if(src.fArray != src.fStackBuffer) {
// 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 = src.fRefCounted;
if(fRefCounted) {
addRef();
}
fBogus = FALSE;
}
// 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 UChar *srcChars,
UTextOffset srcStart,
int32_t srcLength) const
{
// compare illegal string values
if(isBogus()) {
if(srcChars==0) {
return 0;
} else {
return -1;
}
} else if(srcChars==0) {
return 1;
}
// pin indices to legal values
pinIndices(start, length);
// get the correct pointer
const UChar *chars = getArrayStart();
UTextOffset minLength;
int8_t lengthResult;
// 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) {
int32_t result;
if(U_IS_BIG_ENDIAN) {
// big-endian: byte comparison works
result = uprv_memcmp(chars + start, srcChars + srcStart, minLength * sizeof(UChar));
if(result != 0) {
return (int8_t)(result >> 15 | 1);
}
} else {
// little-endian: compare UChar units
chars += start;
srcChars += srcStart;
do {
result = ((int32_t)*chars - (int32_t)*srcChars);
if(result != 0) {
return (int8_t)(result >> 15 | 1);
}
++chars;
++srcChars;
} while(--minLength > 0);
}
}
return lengthResult;
}
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::indexOf(const UChar *srcChars,
UTextOffset srcStart,
int32_t srcLength,
UTextOffset start,
int32_t length) const
{
if(isBogus() || srcChars == 0 || srcStart < 0 || srcLength <= 0) {
return -1;
}
// now we will only work with srcLength-1
--srcLength;
// get the indices within bounds
pinIndices(start, length);
// set length for the last possible match start position
// note the --srcLength above
length -= srcLength;
if(length <= 0) {
return -1;
}
const UChar *array = getArrayStart();
UTextOffset limit = start + length;
// search for the first char, then compare the rest of the string
// increment srcStart here for that, matching the --srcLength above
UChar ch = srcChars[srcStart++];
do {
if(array[start] == ch && (srcLength == 0 || compare(start + 1, srcLength, srcChars, srcStart, srcLength) == 0)) {
return start;
}
} while(++start < limit);
return -1;
}
UTextOffset
UnicodeString::doIndexOf(UChar c,
UTextOffset start,
int32_t length) const
{
// pin indices
pinIndices(start, length);
if(length == 0) {
return -1;
}
// find the first occurrence of c
const UChar *begin = getArrayStart() + start;
const UChar *limit = begin + length;
do {
if(*begin == c) {
return begin - getArrayStart();
}
} while(++begin < limit);
return -1;
}
UTextOffset
UnicodeString::lastIndexOf(const UChar *srcChars,
UTextOffset srcStart,
int32_t srcLength,
UTextOffset start,
int32_t length) const
{
if(isBogus() || srcChars == 0 || srcStart < 0 || srcLength <= 0) {
return -1;
}
// now we will only work with srcLength-1
--srcLength;
// get the indices within bounds
pinIndices(start, length);
// set length for the last possible match start position
// note the --srcLength above
length -= srcLength;
if(length <= 0) {
return -1;
}
const UChar *array = getArrayStart();
UTextOffset pos;
// search for the first char, then compare the rest of the string
// increment srcStart here for that, matching the --srcLength above
UChar ch = srcChars[srcStart++];
pos = start + length;
do {
if(array[--pos] == ch && (srcLength == 0 || compare(pos + 1, srcLength, srcChars, srcStart, srcLength) == 0)) {
return pos;
}
} while(pos > start);
return -1;
}
UTextOffset
UnicodeString::doLastIndexOf(UChar c,
UTextOffset start,
int32_t length) const
{
if(isBogus()) {
return -1;
}
// pin indices
pinIndices(start, length);
if(length == 0) {
return -1;
}
const UChar *begin = getArrayStart() + start;
const UChar *limit = begin + length;
do {
if(*--limit == c) {
return limit - getArrayStart();
}
} while(limit > begin);
return -1;
}
UnicodeString&
UnicodeString::findAndReplace(UTextOffset start,
int32_t length,
const UnicodeString& oldText,
UTextOffset oldStart,
int32_t oldLength,
const UnicodeString& newText,
UTextOffset 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 || newLength == 0) {
return *this;
}
while(length >= oldLength) {
UTextOffset 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;
}
//========================================
// 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 {
// clone our array, if necessary
cloneArrayIfNeeded();
UChar *array = getArrayStart();
while(start < limit) {
c = array[start];
if(Unicode::isLowerCase(c)) {
array[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 {
// clone our array, if necessary
cloneArrayIfNeeded();
UChar *array = getArrayStart();
while(start < limit) {
c = array[start];
if(Unicode::isUpperCase(c) || Unicode::isTitleCase(c)) {
array[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)
{
if(!src.isBogus()) {
// 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);
} else {
// remove the range
return doReplace(start, length, 0, 0, 0);
}
}
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;
if(srcChars == 0) {
srcStart = srcLength = 0;
}
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 needed space
int32_t tempLength;
UChar *temp = allocate(newSize + 1, 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
if(length != srcLength) {
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()
{
if(fRefCounted && removeRef() == 0) {
delete [] fArray;
}
fArray = 0;
fCapacity = fLength = 0;
fHashCode = kInvalidHashCode;
fRefCounted = FALSE;
fBogus = TRUE;
}
//========================================
// 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;
// if the codepage is the default, use our cache
if(codepage == 0) {
converter = getDefaultConverter(status);
} else if(*codepage == 0) {
converter = 0;
} else {
converter = ucnv_open(codepage, &status);
}
// if we failed, set the appropriate flags and return
// if it is an empty string, then use the "invariant character" conversion
if(U_FAILURE(status)) {
// close the converter
if(codepage == 0)
releaseDefaultConverter(converter);
else
ucnv_close(converter);
return 0;
}
// perform the conversion
if(converter == 0) {
// use the "invariant characters" conversion
if(length > fLength - start) {
length = fLength - start;
}
u_UCharsToChars(mySource, myTarget, length);
return length;
}
// there is no loop here since we assume the buffer is large enough
myTargetLimit = myTarget + arraySize;
/* Pin the limit to U_MAX_PTR. NULL check is for AS/400. */
if((myTargetLimit < myTarget) || (myTargetLimit == NULL))
myTargetLimit = (char*)U_MAX_PTR;
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;
UErrorCode status = U_ZERO_ERROR;
int32_t arraySize = getCapacity();
// create the converter
UConverter *converter = 0;
// if the codepage is the default, use our cache
// if it is an empty string, then use the "invariant character" conversion
converter = (codepage == 0 ?
getDefaultConverter(status) :
*codepage == 0 ?
0 :
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;
}
fHashCode = kInvalidHashCode;
// perform the conversion
if(converter == 0) {
// use the "invariant characters" conversion
if(arraySize < dataLength) {
int32_t tempCapacity;
// allocate enough space for the dataLength, the refCount, and a NUL
UChar *temp = allocate(dataLength + 2, tempCapacity);
if(temp == 0) {
// set flags and return
setToBogus();
return;
}
fArray = temp;
fCapacity = tempCapacity;
setRefCount(1);
u_charsToUChars(codepageData, fArray + 1, dataLength);
fArray[dataLength + 1] = 0;
} else {
u_charsToUChars(codepageData, getArrayStart(), dataLength);
}
fLength = dataLength;
return;
}
myTarget = getArrayStart();
for(;;) {
// 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();
// allocate more space and copy data, if needed
if(status == U_INDEX_OUTOFBOUNDS_ERROR) {
int32_t tempCapacity;
UChar *temp = allocate(fCapacity, tempCapacity);
if(! temp) {
// set flags and return
setToBogus();
break;
}
if(fRefCounted) {
// copy the old array into temp
us_arrayCopy(fArray, 1, temp, 1, fLength);
delete [] fArray;
} else {
// if we're not currently ref counted, shift the array right by one
us_arrayCopy(fArray, 0, temp, 1, fLength);
}
fArray = temp;
fCapacity = tempCapacity;
setRefCount(1);
myTarget = getArrayStart() + fLength;
arraySize = getCapacity() - fLength;
} else {
break;
}
}
// 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;
// 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);
}
if(getArrayStart()[fLength] != 0) {
// tack on a trailing null
((UChar *)getArrayStart())[fLength] = 0;
}
return getArrayStart();
}
UChar*
UnicodeString::orphanStorage()
{
// if we're bogus, do nothing
if(fBogus)
return 0;
UChar *retVal;
// if we're ref counted, get rid of the leading ref count
if(fRefCounted && removeRef() == 0) {
retVal = fArray;
} else {
// if we don't own the memory, then we have to allocate it
retVal = new UChar[fLength + 1];
if(retVal == 0) {
return 0;
}
}
// shift or copy characters
us_arrayCopy(getArrayStart(), 0, retVal, 0, fLength);
retVal[fLength] = 0;
// set self to empty
fArray = fStackBuffer;
fLength = 0;
fCapacity = US_STACKBUF_SIZE;
fHashCode = kEmptyHashCode;
fRefCounted = FALSE;
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 aliased or ref counted, make a copy of the buffer if necessary
if(fArray != fStackBuffer && (!fRefCounted || refCount() > 1)) {
UChar *copy;
bool_t refCounted;
if(fLength <= US_STACKBUF_SIZE) {
// a small string does not need allocation
fCapacity = US_STACKBUF_SIZE;
copy = fStackBuffer;
refCounted = FALSE;
} else {
if(!fRefCounted) {
// make room for the ref count
++fCapacity;
}
if(fCapacity - 1 <= fLength) {
// make room for a terminating NUL
fCapacity = fLength + 2;
}
copy = new UChar [ fCapacity ];
if(copy == 0) {
setToBogus();
return;
}
refCounted = TRUE;
}
// copy the current shared array into our new array
us_arrayCopy(getArrayStart(), 0, copy, refCounted ? 1 : 0, fLength);
// 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(fRefCounted && removeRef() == 0) {
delete [] fArray;
}
// make our array point to the new copy and set the ref count to one
fArray = copy;
fRefCounted = refCounted;
if(refCounted) {
setRefCount(1);
}
}
}
// private function for C API
U_CFUNC const UChar*
T_UnicodeString_getUChars(const UnicodeString *s)
{
return s->getUChars();
}
// private function for C API
U_CFUNC int32_t
T_UnicodeString_extract(const UnicodeString *s, char *dst)
{
return s->extract(0, s->length(), dst, "");
}
//========================================
// 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 <iostream.h>
#include "unistrm.h"
#include "filestrm.h"
inline uint8_t
uprv_hibyte(uint16_t x)
{ return (uint8_t)(x >> 8); }
inline uint8_t
uprv_lobyte(uint16_t x)
{ return (uint8_t)(x & 0xff); }
inline uint16_t
uprv_hiword(uint32_t x)
{ return (uint16_t)(x >> 16); }
inline uint16_t
uprv_loword(uint32_t x)
{ return (uint16_t)(x & 0xffff); }
inline void
writeLong(FileStream *os,
int32_t x)
{
uint16_t word = uprv_hiword((uint32_t)x);
T_FileStream_putc(os, uprv_hibyte(word));
T_FileStream_putc(os, uprv_lobyte(word));
word = uprv_loword((uint32_t)x);
T_FileStream_putc(os, uprv_hibyte(word));
T_FileStream_putc(os, uprv_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, uprv_hibyte(c));
T_FileStream_putc(os, uprv_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;
}