925f2cf8c1
X-SVN-Rev: 191
1268 lines
30 KiB
C++
1268 lines
30 KiB
C++
/*
|
|
*******************************************************************************
|
|
* *
|
|
* 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 <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
|
|
|
|
// 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 <iostream.h>
|
|
#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;
|
|
}
|