Files
avmplus/core/StringObject.cpp
T
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

3065 lines
115 KiB
C++

/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "avmplus.h"
// This is the maximum value for the characters Left field in the
// m_bitsAndFlags field of the String instance. Strings grow by
// by doubling the available buffer size until the characters left
// exceeds the size of the field. From there on, the buffer grows
// linear. Note that this number is divided by two to make sure that
// the counter actually, spans the entire buffer area; if this would
// not be done, the calculation of characters left (using GC::Size())
// would return a wrong number.
#define TSTR_MAX_CHARSLEFT ((uint32_t)TSTR_CHARSLEFT_MASK >> TSTR_CHARSLEFT_SHIFT >> 1)
// This is the maximum value of extra bytes to allocate during a
// string concatenation operation if PleaseAlloc() fails. The number
// is quite small to not kill the app just because of a large extra
// chunk of RAM being allocated just in case there are more concatenation
// operations.
#define TSTR_MAX_LOMEM_EXTRABYTES 16384
// Never allocate dynamic strings smaller than this (in bytes)
#define TSTR_MIN_DYNAMIC_ALLOCATION 32
// in fixDependentString, don't bother going dependent->dynamic if the memory
// we would save is < this (in bytes)
#define TSTR_DEPENDENT_STRING_NUISANCE_SAVINGS int32_t(sizeof(String))
using namespace MMgc;
namespace avmplus
{
// Use this constant for 16 and 32 bit strings of zero length
// we do not want any string to contain NULL buffer pointers
union Zero
{
uint32_t u32;
wchar u16;
uint8_t u8;
char c8;
};
static const Zero k_zero = { 0 };
/////////////////////////// Helpers: Widening //////////////////////////////
REALLY_INLINE void _widen8_16(const uint8_t* src, wchar* dst, int32_t len)
{
while (len-- > 0)
*dst++ = wchar(*src++);
}
/////////////////////////// Helpers: Narrowing //////////////////////////////
REALLY_INLINE void _narrow16_8(const wchar* src, uint8_t* dst, int32_t len)
{
while (len-- > 0)
{
AvmAssert(*src <= 0xFF);
*dst++ = (uint8_t) *src++;
}
}
/////////////////////////// Helpers: Copying ///////////////////////////////
// Copy the contents of the given buffer to another buffer, given two widths.
static void* _copyBuffers(const void* src, void* dst, int32_t srcLen, String::Width srcWidth, String::Width dstWidth)
{
if (srcWidth == dstWidth)
VMPI_memcpy(dst, src, srcLen << srcWidth);
else if (srcWidth == String::k8)
_widen8_16((const uint8_t*) src, (wchar*) dst, srcLen);
else
_narrow16_8((const wchar*) src, (uint8_t*) dst, srcLen);
// return the new buffer pointer
return (char*) dst + (srcLen << dstWidth);
}
#ifdef _DEBUG
void String::verify7bit() const
{
if (!(m_bitsAndFlags & TSTR_7BIT_FLAG))
return;
if (getWidth() == k8)
{
Pointers ptrs(this);
for (int32_t i = 0; i < m_length; ++i)
{
if (ptrs.p8[i] > 127)
{
AvmAssert(!"hi bit found on string with TSTR_7BIT_FLAG set");
}
}
}
else
{
AvmAssert(!"only k8 strings should set TSTR_7BIT_FLAG");
}
}
#endif
/////////////////////////// Helper: get AvmCore /////////////////////////////
REALLY_INLINE MMgc::GC* _gc(const String* s)
{
return MMgc::GC::GetGC(s);
}
REALLY_INLINE AvmCore* _core(const String* s)
{
MMgc::GC *gc = MMgc::GC::GetGC(s);
return gc->core();
}
/////////////////////////// Helpers: templated functions /////////////////////////////
//
// use typetraits to generate a compile-time error if you attempt to use a
// non-unsigned char type as an argument to these.
//
// Note that we deliberately are using a bitwise &, not a logical && here...
// GCC 4.3 and later, with -Wlogical-op enabled, will complain about the &&
// construct with
//
// "warning: logical '&&' with non-zero constant will always evaluate as true"
//
// and since many embedders compile with warnings-as-errors enabled, this crushes
// the build. This seems like a highly questionable warning to me, but since there
// is a valid workaround, we'll go with it rather than requiring embedders to disable
// that warning.
//
#define PREVENT_SIGNED_CHAR_PTR(TYPE) \
{ \
typedef MMgc::is_same<char, uint8_t> is_char_unsigned; \
typedef MMgc::is_same<char, TYPE> is_char; \
MMGC_STATIC_ASSERT(!(is_char::value & !is_char_unsigned::value)); \
}
// apparently SunPro compiler doesn't like combining REALLY_INLINE with static functions.
template <typename STR1, typename STR2>
/*static*/ REALLY_INLINE bool equalsImpl(const STR1* str1, const STR2* str2, int32_t len)
{
PREVENT_SIGNED_CHAR_PTR(STR1)
PREVENT_SIGNED_CHAR_PTR(STR2)
for (int32_t j = 0; j < len; j++)
{
if (str1[j] != str2[j])
return false;
}
return true;
}
// NOTE: this routine cannot use memcmp(), as the test
// ecma3/String/localeCompare_rt.as depends on the difference
// in character values, and memcmp() does not guarantee to
// return this value
// apparently SunPro compiler doesn't like combining REALLY_INLINE with static functions.
template <typename STR1, typename STR2>
/*static*/ REALLY_INLINE int32_t compareImpl(const STR1* str1, const STR2* str2, int32_t len)
{
PREVENT_SIGNED_CHAR_PTR(STR1)
PREVENT_SIGNED_CHAR_PTR(STR2)
int32_t res = 0;
while (len-- > 0 && !res)
{
res = int32_t(*str2++ - *str1++);
}
return res;
}
// apparently SunPro compiler doesn't like combining REALLY_INLINE with static functions.
template <typename STR, typename PATTERN>
/*static*/ REALLY_INLINE int32_t indexOfImpl(const STR* str, int32_t start, int32_t right, const PATTERN* pat, int32_t patlen)
{
PREVENT_SIGNED_CHAR_PTR(STR)
PREVENT_SIGNED_CHAR_PTR(PATTERN)
AvmAssert(patlen > 0);
// even with REALLY_INLINE, some compilers will be reluctant to inline equalsImpl here,
// so we explicitly repeat the code here.
const PATTERN pat0 = pat[0];
const STR* const end = str + right;
for (const STR* probe = str + start; probe <= end; ++probe)
{
if (probe[0] == pat0)
{
for (int32_t j = 1; j < patlen; j++)
{
if (probe[j] != pat[j])
goto no_match;
}
return int32_t(uintptr_t(probe - str));
}
no_match:
// some compilers will complain about a label with no statement following,
// hence the continue
continue;
}
return -1;
}
// apparently SunPro compiler doesn't like combining REALLY_INLINE with static functions.
template <typename STR>
/*static*/ REALLY_INLINE int32_t indexOfCharCodeImpl(const STR* str, int32_t start, int32_t right, wchar c)
{
PREVENT_SIGNED_CHAR_PTR(STR)
const STR* p = str + start - 1;
const STR* const end = str + right;
while (++p <= end)
if (*p == c)
return int32_t(p - str);
return -1;
}
// apparently SunPro compiler doesn't like combining REALLY_INLINE with static functions.
template <typename STR, typename PATTERN>
/*static*/ REALLY_INLINE int32_t lastIndexOfImpl(const STR* str, int32_t const start, const PATTERN* pat, int32_t patlen)
{
PREVENT_SIGNED_CHAR_PTR(STR)
PREVENT_SIGNED_CHAR_PTR(PATTERN)
AvmAssert(patlen > 0);
AvmAssert(start >= 0);
// even with REALLY_INLINE, some compilers will be reluctant to inline equalsImpl here,
// so we explicitly repeat the code here.
const PATTERN pat0 = pat[0];
for (const STR* probe = str + start; probe >= str; --probe)
{
if (probe[0] == pat0)
{
for (int32_t j = 1; j < patlen; j++)
{
if (pat[j] != probe[j])
goto no_match;
}
return int32_t(uintptr_t(probe - str));
}
no_match:
// some compilers will complain about a label with no statement following,
// hence the continue
continue;
}
return -1;
}
// apparently SunPro compiler doesn't like combining REALLY_INLINE with static functions.
template <typename STR>
/*static*/ REALLY_INLINE int32_t hashCodeImpl(const STR* str, int32_t len)
{
PREVENT_SIGNED_CHAR_PTR(STR)
// must be same signed-ness as other hashcode functions.
// experimentation shows better results from signed (vs unsigned).
int32_t hashCode = 0;
while (len--)
hashCode = (hashCode >> 28) ^ (hashCode << 4) ^ *str++;
return hashCode;
}
/*static*/ REALLY_INLINE int32_t hashCodeAddChar(int32_t hashCode, char val)
{
// must be same signed-ness as other hashcode functions.
// (match sign propagation in right-shift >> 28, for better or worse)
return ((hashCode >> 28) ^ (hashCode << 4) ^ val);
}
/*static*/ REALLY_INLINE int32_t stringLengthUInt(uint32_t value)
{
if (value < 10) return 1;
else if (value < 100) return 2;
else if (value < 1000) return 3;
else if (value < 10000) return 4;
else if (value < 100000) return 5;
else if (value < 1000000) return 6;
else if (value < 10000000) return 7;
else if (value < 100000000) return 8;
else if (value < 1000000000) return 9;
else return 10;
}
////////////////////////////// Constructors ////////////////////////////////
// ctor for a static string.
REALLY_INLINE String::String(const void* buffer, Width w, int32_t length, bool is7bit) :
#ifdef DEBUGGER
AvmPlusScriptableObject(sotString()),
#endif
m_buffer(buffer), // static data - no WB() needed
m_extra(NULL),
m_length(length),
m_bitsAndFlags(w | (kStatic << TSTR_TYPE_SHIFT) | (uint32_t(is7bit) << TSTR_7BIT_SHIFT))
{
AvmAssert(m_length >= 0);
AvmAssert((uint64_t(m_length) << getWidth()) <= 0x7FFFFFFFU);
}
// ctor for a dynamic string.
REALLY_INLINE String::String(MMgc::GC* gc, void* buffer, Width w, int32_t length, int32_t charsLeft, bool is7bit) :
#ifdef DEBUGGER
AvmPlusScriptableObject(sotString()),
m_buffer((void*)NULL),
#else
m_buffer(buffer),
#endif
m_extra(NULL),
m_length(length),
m_bitsAndFlags(w | (kDynamic << TSTR_TYPE_SHIFT) | (charsLeft << TSTR_CHARSLEFT_SHIFT) | (uint32_t(is7bit) << TSTR_7BIT_SHIFT))
{
AvmAssert(m_length >= 0);
AvmAssert((uint64_t(m_length) << getWidth()) <= 0x7FFFFFFFU);
#ifdef DEBUGGER
/*
an explicit WB is only necessary if there's the possibility of an allocation between the time
the container (String) is allocated and time we set the field. In nondebugger builds, there is no
such possibility, but in DEBUGGER builds, the superclass ctor (AvmPlusScriptableObject) can
cause an allocation if sampling is enabled. ("Icky and brittle", but that's how it is.)
*/
WB(gc, this, &this->m_buffer.pv, buffer);
#else
(void)gc;
#endif
}
// ctor for a dependent string.
REALLY_INLINE String::String(MMgc::GC* gc, Stringp master, int32_t start, int32_t length) :
#ifdef DEBUGGER
AvmPlusScriptableObject(sotString()),
#endif
m_buffer(uintptr_t(start << master->getWidth())),
#ifdef DEBUGGER
m_extra(NULL),
#else
m_extra(master),
#endif
m_length(length),
// note that we propagate TSTR_7BIT_FLAG: if the entire master is TSTR_7BIT_FLAG, then so is the dependent string.
// @todo: a dependent string could qualify for TSTR_7BIT_FLAG even if it's master is not. worth checking for?
m_bitsAndFlags((master->m_bitsAndFlags & (TSTR_WIDTH_MASK | TSTR_7BIT_FLAG)) | (kDependent << TSTR_TYPE_SHIFT))
{
AvmAssert(m_length >= 0);
AvmAssert((uint64_t(m_length) << getWidth()) <= 0x7FFFFFFFU);
#ifdef DEBUGGER
WBRC(gc, this, &this->m_extra.master, master);
#else
/*
an explicit WBRC is only necessary if there's the possibility of an allocation between the time
the container (String) is allocated and time we set the field. In nondebugger builds, there is no
such possibility, but in DEBUGGER builds, the superclass ctor (AvmPlusScriptableObject) can
cause an allocation if sampling is enabled. ("Icky and brittle", but that's how it is.)
*/
AvmAssert(master != NULL);
master->IncrementRef();
(void)gc;
#endif
}
// add a and b and check for overflow
static int32_t int32AddChecked(int32_t a, int32_t b)
{
if ((a | b) >= 0) // both nonnegative?
{
uint64_t x = uint64_t(a) + uint64_t(b);
if (x <= 0x7FFFFFFFU)
return int32_t(x);
}
GCHeap::SignalObjectTooLarge();
/*NOTREACHED*/
return 0;
}
// shift a left by b and check for overflow
static int32_t int32ShlChecked(int32_t a, int32_t b)
{
AvmAssert(b < 32); // ok for this to be DEBUG-only, the shift amounts are known constants for practical purposes
if ((a | b) >= 0) // both nonnegative?
{
uint64_t x = uint64_t(a) << uint64_t(b);
if (x <= 0x7FFFFFFFU)
return int32_t(x);
}
GCHeap::SignalObjectTooLarge();
/*NOTREACHED*/
return 0;
}
static uint32_t uint32ShlChecked(uint32_t a, uint32_t b)
{
AvmAssert(b < 32); // ok for this to be DEBUG-only, the shift amounts are known constants for practical purposes
uint64_t x = uint64_t(a) << uint64_t(b);
if (x <= 0xFFFFFFFFU)
return uint32_t(x);
GCHeap::SignalObjectTooLarge();
/*NOTREACHED*/
return 0;
}
// Private static method to create a dependent string
// in nondebug builds this is a trivial wrapper around the ctor (which is itself trivial), so REALLY_INLINE it
REALLY_INLINE /*static*/ Stringp String::createDependent(GC* gc, Stringp master, int32_t start, int32_t len)
{
AvmAssert(len >= 0);
// master cannot be a dependent string
AvmAssert(!master->isDependent());
MMGC_MEM_TAG( "Strings" );
Stringp s = new(gc, MMgc::kExact) String(gc, master, start, len);
VERIFY_7BIT(s);
return s;
}
// Private static method to create a dynamic string, given a buffer and its size in characters
/*static*/ Stringp String::createDynamic(GC* gc, const void* data, int32_t len, Width w, bool is7bit, int32_t extra)
{
AvmAssert(w != kAuto);
AvmAssert(len >= 0);
// only 8-bit strings should set the 7-bit flag
if (w != k8)
is7bit = false;
// a zero-length dynamic string is legal, but a zero-length GC allocation is not.
int32_t alloc = int32AddChecked(len, extra);
MMGC_MEM_TAG( "Strings" );
// First, use PleaseAlloc(), and if the call fails, reduce the amount of extra data
// to TSTR_MAX_EXTRA_BYTES_IN_LOW_MEMORY and do an Alloc(), which may fail.
void* buffer = gc->PleaseAlloc(int32ShlChecked(alloc, w), 0);
if (buffer == NULL)
{
if (extra > (TSTR_MAX_LOMEM_EXTRABYTES >> w))
extra = TSTR_MAX_LOMEM_EXTRABYTES >> w;
alloc = len + extra; // This is safe because the new value of 'extra' is smaller than the old, and the old was checked
buffer = gc->Alloc(alloc << w, 0); // Ditto
}
int32_t bufLen = (int32_t) (GC::Size(buffer) >> w); // Note bufLen may be larger than (alloc << w)
int32_t charsLeft = bufLen - len;
// the extra character must not exceed the available field size.
//
// ok for this to be an assertion because we will not have problems with mmgc rounding up
// to a 4KB boundary; the max chars left is about 4KB.
AvmAssert(charsLeft <= int32_t((uint32_t) TSTR_CHARSLEFT_MASK >> TSTR_CHARSLEFT_SHIFT));
Stringp s = new(gc, MMgc::kExact) String(gc, buffer, w, len, charsLeft, is7bit);
if (data != NULL && len != 0)
VMPI_memcpy(buffer, data, size_t(len << w)); // This is safe because alloc >= len and buffer size is alloc << w and that has been checked.
#ifdef _DEBUG
// Terminate string with 0 for better debugging display
if (charsLeft)
{
if (w == k8)
s->m_buffer.p8[len] = 0;
else
s->m_buffer.p16[len] = 0;
}
if (data != NULL)
{
// don't try to verify if data==null, buffer will be random! (caller will verify)
VERIFY_7BIT(s);
}
#endif
return s;
}
// Private static method to create a string, given a static buffer and its size in characters
/*static*/ Stringp String::createStatic(GC* gc, const void* data, int32_t len, Width w, bool is7bit)
{
AvmAssert(w != kAuto);
AvmAssert(len >= 0);
MMGC_MEM_TAG( "Strings" );
// only 8-bit strings should set the 7-bit flag
if (w != k8)
is7bit = false;
Stringp s = new (gc, MMgc::kExact) String(data, w, len, is7bit);
VERIFY_7BIT(s);
return s;
}
// Create a string out of an 8bit buffer. Characters are just widened and copied, not interpreted as UTF8.
Stringp String::createLatin1(AvmCore* core, const char* buffer, int32_t len, Width desiredWidth, bool staticBuf)
{
if (buffer == NULL)
{
len = 0;
buffer = &k_zero.c8;
staticBuf = true;
}
if (len < 0)
len = Length(buffer);
if (desiredWidth == kAuto)
desiredWidth = k8;
if (desiredWidth == k8 && core->kEmptyString != NULL)
{
// core has been initialized, check for cached characters
if (len == 0)
return core->kEmptyString;
if (len == 1 && *((uint8_t*) buffer) < 128)
return core->cachedChars[*((uint8_t*) buffer)];
}
const bool is7bit = false; // actually, might be, we just haven't checked yet.
Stringp s = NULL;
GC* gc = core->GetGC();
if (staticBuf && desiredWidth == k8)
{
s = createStatic(gc, buffer, len, k8, is7bit);
}
else
{
s = createDynamic(gc, NULL, len, desiredWidth, is7bit);
_copyBuffers(buffer, s->m_buffer.p8, len, k8, desiredWidth);
VERIFY_7BIT(s);
}
return s;
}
Stringp String::getFixedWidthString(Width w) const
{
if (w == getWidth())
return (Stringp) this;
if (w == kAuto)
return NULL;
const bool is7bit = false;
Stringp newStr = createDynamic(_gc(this), NULL, m_length, w, is7bit);
Pointers ptrs(this);
Pointers new_ptrs(newStr);
_copyBuffers(ptrs.pv, new_ptrs.pv, m_length, getWidth(), w);
VERIFY_7BIT(newStr);
return newStr;
}
/////////////////////////////// Destructors & tracers ////////////////////////////////
String::~String()
{
switch (getType())
{
case kDynamic: {
// Never necessary to WB() when we store NULL over a non-RC pointer
void* pv = m_buffer.pv;
m_buffer.pv = NULL; // Avoid dangling the pointer
_gc(this)->FreeNotNull(pv);
break;
}
case kDependent:
// WBRC() is however necessary when we store NULL over an RC pointer
WBRC_NULL(&m_extra.master);
break;
default: ; // kStatic
}
m_extra.master = NULL; // might have already been cleared above, but that's ok
m_buffer.p8 = NULL; // might have already been cleared above, but that's ok
m_length = 0;
m_bitsAndFlags = 0;
}
/*virtual*/
bool String::gcTrace(GC* gc, size_t cursor)
{
(void)cursor;
switch (getType())
{
case kDynamic:
gc->TraceLocation(&m_buffer.pv);
break;
case kDependent:
gc->TraceLocation(&m_extra.master);
break;
}
return false;
}
/////////////////////////////// Conversions ////////////////////////////////
void String::makeDynamic(const uint8_t* dataStart, uint32_t dataSize)
{
AvmAssert(dataStart != NULL);
AvmAssert(dataSize > 0);
switch(getType())
{
case kStatic:
// do not convert if range is given and data falls within
if ((uint32_t) (m_buffer.p8 - dataStart) >= dataSize)
break;
// else fall thru
case kDependent:
convertToDynamic();
break;
default:;
}
}
void String::fixDependentString()
{
if (!isDependent())
return;
// If the length of this instance is > the master length, this
// string is the result of an auto-concat; we keep the master in
// in that case - there is little to no memory gain to be expected.
// Also, forget about conversion if the memory to be gained is minimal.
if (m_length >= (m_extra.master->m_length - (TSTR_DEPENDENT_STRING_NUISANCE_SAVINGS >> getWidth())))
return;
// Do not convert to a static string here, because a static string
// may be converted to a dynamic string, which would leave leave an interior
// pointer to the static content in the string instance.
convertToDynamic();
}
void String::convertToDynamic()
{
AvmAssert(getType() != kDynamic);
// Convert this string to be a dynamic string
int32_t bytes = m_length << getWidth(); // No overflow by definition
GC* gc = _gc(this);
MMGC_MEM_TYPE( this );
void* buf = gc->Alloc(bytes, 0);
VMPI_memcpy(buf, Pointers(this).pv, bytes);
WB(gc, this, &this->m_buffer.pv, buf);
if (isDependent())
WBRC_NULL(&m_extra.master);
setType(kDynamic);
}
/////////////////////////////// Comparison /////////////////////////////////
int32_t String::Compare(String& other, int32_t other_start, int32_t other_length) const
{
if (this == &other)
return 0;
if (other_start >= other.m_length)
return -1;
if (other_length < 1 || other_length > other.m_length)
other_length = other.m_length;
if (other_start < 0)
other_start = 0;
int32_t result = 0;
int32_t len = (m_length < other_length) ? m_length : other_length; // choose smaller of two
if (len > 0)
{
Pointers ptrs(this);
Pointers other_ptrs(&other);
Width const w1 = this->getWidth();
Width const w2 = other.getWidth();
switch ((w1 << 1) + w2)
{
case (k8 << 1) + k8:
result = compareImpl(ptrs.p8, other_ptrs.p8 + other_start, len);
break;
case (k8 << 1) + k16:
result = compareImpl(ptrs.p8, other_ptrs.p16 + other_start, len);
break;
case (k16 << 1) + k8:
result = compareImpl(ptrs.p16, other_ptrs.p8 + other_start, len);
break;
case (k16 << 1) + k16:
result = compareImpl(ptrs.p16, other_ptrs.p16 + other_start, len);
break;
}
}
if (result == 0)
{
// catch substring compares
result = (other_length - m_length);
if (result < 0)
result = -1;
else if (result > 0)
result = 1;
}
return result;
}
bool String::equalsLatin1(const char* p, int32_t len) const
{
if (len < 0)
len = Length(p);
if (len != length())
return false;
Pointers ptrs(this);
if (getWidth() == k8)
{
return equalsImpl(ptrs.p8, (const uint8_t*)p, len);
}
else
{
return equalsImpl(ptrs.p16, (const uint8_t*)p, len);
}
}
bool String::equalsUTF16(const wchar* p, int32_t len) const
{
if (len != length())
return false;
Pointers ptrs(this);
if (getWidth() == k8)
{
return equalsImpl(ptrs.p8, p, len);
}
else
{
return equalsImpl(ptrs.p16, p, len);
}
}
bool String::equals(Stringp that) const
{
if (this == that)
return true;
int32_t const len1 = this->length();
int32_t const len2 = that->length();
if (len1 != len2)
return false;
Width const w1 = this->getWidth();
Width const w2 = that->getWidth();
Pointers thisbuf(this);
Pointers thatbuf(that);
switch ((w1 << 1) + w2)
{
case (k8 << 1) + k8:
return equalsImpl(thisbuf.p8, thatbuf.p8, len1);
case (k8 << 1) + k16:
return equalsImpl(thisbuf.p8, thatbuf.p16, len1);
case (k16 << 1) + k8:
return equalsImpl(thisbuf.p16, thatbuf.p8, len1);
case (k16 << 1) + k16:
return equalsImpl(thisbuf.p16, thatbuf.p16, len1);
}
AvmAssert(0);
return true;
}
#ifdef VMCFG_NANOJIT
/*static*/ int32_t String::equalsWithNullChecks(Stringp s1, Stringp s2)
{
if (s1 == s2)
return true;
if (!s1 || !s2)
return false;
int32_t const len1 = s1->length();
int32_t const len2 = s2->length();
if (len1 != len2)
return false;
Width const w1 = s1->getWidth();
Width const w2 = s2->getWidth();
Pointers thisbuf(s1);
Pointers thatbuf(s2);
switch ((w1 << 1) + w2)
{
case (k8 << 1) + k8:
return equalsImpl(thisbuf.p8, thatbuf.p8, len1);
case (k8 << 1) + k16:
return equalsImpl(thisbuf.p8, thatbuf.p16, len1);
case (k16 << 1) + k8:
return equalsImpl(thisbuf.p16, thatbuf.p8, len1);
case (k16 << 1) + k16:
return equalsImpl(thisbuf.p16, thatbuf.p16, len1);
}
AvmAssert(0);
return true;
}
#endif // VMCFG_NANOJIT
/////////////////////////////// Hash Codes /////////////////////////////////
// The hashing algorithm uses the full character width
int32_t String::hashCodeLatin1(const char* buf, int32_t len)
{
return hashCodeImpl((const utf8_t*)buf, len);
}
int32_t String::hashCodeUTF16(const wchar* buf, int32_t len)
{
return hashCodeImpl(buf, len);
}
int32_t String::hashCodeUInt(uint32_t value)
{
// Below is largely partial-evaluated composition of
// MathUtils::convertIntegerToStringBuffer and
// String::hashCodeLatin1
int32_t radix = 10;
int32_t hashCode = 0;
int32_t len = stringLengthUInt(value);
if (value == 0)
{
hashCode = hashCodeAddChar(hashCode, '0');
}
else
{
uintptr_t uVal = (uintptr_t)value;
uintptr_t factor = 1;
uintptr_t cursor = len;
while (--cursor)
factor *= radix;
cursor = len;
while (cursor > 0)
{
uintptr_t j = uVal;
j /= factor;
uVal %= factor;
factor /= radix;
AvmAssert( j < 10 );
hashCode = hashCodeAddChar(hashCode, (char)(j+'0'));
cursor--;
}
}
return hashCode;
}
int32_t String::hashCode() const
{
if (m_length != 0)
{
Pointers ptrs(this);
if (getWidth() == k8)
{
return hashCodeImpl(ptrs.p8, m_length);
}
else
{
return hashCodeImpl(ptrs.p16, m_length);
}
}
return 0;
}
//////////////////////////////// Accessors /////////////////////////////////
wchar FASTCALL String::charAt(int32_t index) const
{
AvmAssert(index >= 0 && index < m_length);
Pointers ptrs(this);
return (getWidth() == k8) ? ptrs.p8[index] : ptrs.p16[index];
}
int32_t String::indexOf(Stringp substr, int32_t start) const
{
if (substr == NULL)
return -1;
// bug 78346: argv[1] might be less than zero.
// We clamp it to zero for two reasons:
// 1. A movie created prior to this fix with a small negative value probably worked,
// so let's fix it without breaking them.
// 2. I am told this is what java does.
if (start < 0)
start = 0;
int32_t len = this->length();
if (start > len)
start = len;
int32_t sublen = substr->length();
if (sublen == 0)
return start;
// right is the last character in selfString subStr could be found at
// (and further, and there isn't enough of selfString remaining for a match to be possible)
const int32_t right = len - sublen;
if (right < 0)
return -1;
Width const w1 = getWidth();
Width const w2 = substr->getWidth();
Pointers selfBuf(this);
Pointers subBuf(substr);
// For maximum performance, use different cases for k8/k16 combinations
if (sublen == 1)
{
switch ((w1 << 1) + w2)
{
case (k8 << 1) + k8:
return indexOfCharCodeImpl(selfBuf.p8, start, right, subBuf.p8[0]);
case (k8 << 1) + k16:
return indexOfCharCodeImpl(selfBuf.p8, start, right, subBuf.p16[0]);
case (k16 << 1) + k8:
return indexOfCharCodeImpl(selfBuf.p16, start, right, subBuf.p8[0]);
case (k16 << 1) + k16:
return indexOfCharCodeImpl(selfBuf.p16, start, right, subBuf.p16[0]);
}
}
else
{
switch ((w1 << 1) + w2)
{
case (k8 << 1) + k8:
return indexOfImpl(selfBuf.p8, start, right, subBuf.p8, sublen);
case (k8 << 1) + k16:
return indexOfImpl(selfBuf.p8, start, right, subBuf.p16, sublen);
case (k16 << 1) + k8:
return indexOfImpl(selfBuf.p16, start, right, subBuf.p8, sublen);
case (k16 << 1) + k16:
return indexOfImpl(selfBuf.p16, start, right, subBuf.p16, sublen);
}
}
AvmAssert(0);
return -1;
}
int32_t String::lastIndexOf(Stringp substr, int32_t start) const
{
if (substr == NULL)
return -1;
// lastIndexOf("anything", negative-number) can't match anything, ever:
// match FP10's behavior and return -1 immediately.
if (start < 0)
return -1;
int32_t len = this->length();
if (start > len)
start = len;
int32_t sublen = substr->length();
if (sublen == 0)
return start;
// Any match starting after right must necessarily fail, as there will be
// insufficient characters remaining to match.
const int32_t right = len - sublen;
// The substring is longer than selfString, and therefore no match is possible.
// We must avoid setting start to a negative value below.
if (right < 0)
return -1;
// bug 78346: argv[1] might be greater than right
// (similar reasons to above apply).
if (start > right)
start = right;
Width w1 = getWidth();
Width w2 = substr->getWidth();
Pointers selfBuf(this);
Pointers subBuf(substr);
// For maximum performance, use different cases for k8/k16 combinations
switch ((w1 << 1) + w2)
{
case (k8 << 1) + k8:
return lastIndexOfImpl(selfBuf.p8, start, subBuf.p8, sublen);
case (k8 << 1) + k16:
return lastIndexOfImpl(selfBuf.p8, start, subBuf.p16, sublen);
case (k16 << 1) + k8:
return lastIndexOfImpl(selfBuf.p16, start, subBuf.p8, sublen);
case (k16 << 1) + k16:
return lastIndexOfImpl(selfBuf.p16, start, subBuf.p16, sublen);
}
AvmAssert(0);
return -1;
}
int32_t String::indexOfLatin1(const char* p, int32_t sublen, int32_t start, int32_t end) const
{
if (start < 0)
start = 0;
if (end < 0)
end = 0;
if (end > m_length)
end = m_length;
if (p == NULL || end <= start)
return -1;
if (sublen < 0)
sublen = Length(p);
if (sublen == 0)
return start;
int32_t right = end - sublen;
if (right < 0)
return -1;
Width w = getWidth();
Pointers selfBuf(this);
if (w == k8)
{
return indexOfImpl(selfBuf.p8, start, right, (const uint8_t*)p, sublen);
}
else
{
return indexOfImpl(selfBuf.p16, start, right, (const uint8_t*)p, sublen);
}
}
int32_t String::indexOfCharCode(wchar c, int32_t start, int32_t end) const
{
if (start < 0)
start = 0;
if (end < 0)
end = 0;
if (end > m_length)
end = m_length;
if (end <= start)
return -1;
int32_t right = end - 1;
if (right < 0)
return -1;
Pointers ptrs(this);
if (getWidth() == String::k8)
{
return indexOfCharCodeImpl(ptrs.p8, start, right, c);
}
else
{
return indexOfCharCodeImpl(ptrs.p16, start, right, c);
}
}
bool String::matchesLatin1(const char* p, int32_t len, int32_t pos)
{
if (p == NULL || pos >= m_length)
return false;
if (pos < 0)
pos = 0;
if (len < 0)
len = Length(p);
Pointers ptrs(this);
if (getWidth() == k8)
{
return equalsImpl(ptrs.p8 + pos, (const uint8_t*)p, len);
}
else
{
return equalsImpl(ptrs.p16 + pos, (const uint8_t*)p, len);
}
}
bool String::matchesLatin1_caseless(const char* p, int32_t len, int32_t pos)
{
if (p == NULL || pos >= m_length)
return false;
if (pos < 0)
pos = 0;
if (len < 0)
len = Length(p);
StringIndexer self(this);
while (len--)
{
wchar const ch1 = wCharToUpper(self[pos++]);
wchar const ch2 = wCharToUpper(uint8_t(*p++));
if (ch1 != ch2)
return false;
}
return true;
}
///////////////////////////// Concatenation ////////////////////////////////
/*static*/ Stringp String::concatStrings(Stringp leftStr, Stringp rightStr)
{
if (leftStr == NULL || leftStr->m_length == 0)
return rightStr;
return leftStr->_append(rightStr, Pointers(rightStr), rightStr->length(), rightStr->getWidth());
}
Stringp String::append(Stringp rightStr)
{
if (rightStr == NULL || rightStr->m_length == 0)
return this;
return _append(rightStr, Pointers(rightStr), rightStr->length(), rightStr->getWidth());
}
// NB: if rightStrPtr is nonnull, it is assumed to be the source of rightStr.
// In this case, rightStr is assumed to be invalidate by any possible GC activity,
// and will be re-created from rightStrPtr.
Stringp String::_append(Stringp rightStrPtr, const Pointers& rightStr, int32_t numChars, Width charWidth)
{
if (numChars <= 0)
return this;
AvmAssert(charWidth != kAuto);
MMgc::GC* gc = _gc(this);
// fromCharCode() optimization: If this string is empty, and the buffer is a single
// ASCII character, return the cached character
if (m_length == 0 && numChars == 1)
{
// Sun studio generated wrong code for the following Conditional operator.
// Add type conversion to wchar as a workaround.
wchar ch = (charWidth == k8) ? (wchar)rightStr.p8[0] : (wchar)rightStr.p16[0];
if (ch < 128)
return gc->core()->cachedChars[ch];
}
Width thisWidth = getWidth();
Width newWidth = (thisWidth < charWidth) ? charWidth : thisWidth;
int32_t newLen = int32AddChecked(m_length, numChars);
Stringp master = (isDependent()) ? m_extra.master : this;
// check for characters left in leftStr's buffer, or if leftStr has spent its padding already
// string types other than kDynamic have charsLeft == 0
int32_t charsLeft = 0;
int32_t charsUsed = 0;
if (thisWidth >= charWidth)
{
// in-place append only if rightStr's width fits into leftStr
charsLeft = master->getCharsLeft();
if (!master->isStatic())
// charsUsed is the number of chars already spent behind m_length
// Usage of m_buffer is OK - master can never be dependent
charsUsed = int32_t((GC::Size(master->m_buffer.pv) >> thisWidth) - master->m_length - charsLeft);
}
int32_t start = 0; // string start for dependent strings
// it is possible to append in-place if
// 1) this is a kDynamic string and charsUsed == 0
// 2) this is a kDependent string and the end matches the master's real end
switch (getType())
{
case kDynamic:
if (charsUsed != 0)
// someone else has already appended in-place
charsLeft = 0;
break;
case kDependent:
start = (int32_t) m_buffer.offset_bytes >> thisWidth;
if ((start + m_length) != master->m_length + charsUsed)
charsLeft = 0;
break;
default:; // kStatic
}
// the big check: are there enough chars left?
if (numChars <= charsLeft)
{
// the right-hand string fits into the buffer end
_copyBuffers(rightStr.pv,
Pointers(this).p8 + (m_length << thisWidth), // m_length << thiswidth is safe by definition
numChars, charWidth, newWidth);
charsUsed += numChars;
charsLeft -= numChars;
master->setCharsLeft(charsLeft);
#ifdef _DEBUG
// Terminate string with 0 for better debugging display
if (charsLeft && !master->isStatic())
{
int32_t end = master->m_length + charsUsed;
if (newWidth == k8)
master->m_buffer.p8[end] = 0;
else
master->m_buffer.p16[end] = 0;
}
#endif
Stringp s = createDependent(gc, master, start, newLen);
// createDependent propagates the 7-bit flag, which is a good idea for substrings.
// however, if we are adding data, it may no longer qualify. rather than checking
// the appended data, just clear the flag (it will be lazily recalculated as needed)
s->m_bitsAndFlags &= ~TSTR_7BIT_FLAG;
return s;
}
// fall thru - string does not fit
// create a new kDynamic string containing the concatenated string
// See the definition of TSTR_MAX_CHARSLEFT above for an explanation
// of this algorithm
int32_t newSize = (newLen < TSTR_MIN_DYNAMIC_ALLOCATION) ? TSTR_MIN_DYNAMIC_ALLOCATION : int32ShlChecked(newLen, 1);
int32_t extra = newSize - newLen;
if (extra > (int32_t) TSTR_MAX_CHARSLEFT)
extra = (int32_t) TSTR_MAX_CHARSLEFT;
const bool is7bit = false; // actually, might be, we just haven't checked yet.
Stringp newStr = createDynamic(gc, NULL, newLen, newWidth, is7bit, extra);
// note that createDynamic has invalidated any existing Pointers structs...
const void* srcLeft = Pointers(this).pv;
const void* srcRight = rightStrPtr ? Pointers(rightStrPtr).pv : rightStr.pv;
// copy leftStr
void* ptr = _copyBuffers(srcLeft,
newStr->m_buffer.pv,
m_length,
thisWidth, newWidth);
// append src
_copyBuffers(srcRight,
ptr,
numChars,
charWidth, newWidth);
#ifdef _DEBUG
// Terminate string with 0 for better debugging display
if (newStr->getCharsLeft())
{
if (newWidth == k8)
newStr->m_buffer.p8[newStr->m_length] = 0;
else
newStr->m_buffer.p16[newStr->m_length] = 0;
}
#endif
VERIFY_7BIT(newStr);
return newStr;
}
/////////////////////////////// substrings /////////////////////////////////
// Remember: string runs from start up to, but not including, the end!
Stringp String::substring(int32_t start, int32_t end)
{
if (start < 0)
start = 0;
else if (start > m_length)
start = m_length;
if (end < 0)
end = 0;
else if (end > m_length)
end = m_length;
// does the substring span this string?
if (start == 0 && end == m_length)
return this;
MMgc::GC* gc = _gc(this);
AvmCore* core = gc->core();
// for empty strings, return the standard Empty String instance
if (end <= start)
return core->kEmptyString;
// for single characters < 128, return the cached string
if (end == (start + 1))
{
wchar const ch = charAt(start);
if (ch < 128)
return core->cachedChars[ch];
}
// otherwise, create a dependent string
Stringp master = this;
if (isDependent())
{
// get the string offset
master = m_extra.master;
// TODO: possible 32-bit overflow for a very huge dependent string
int32_t offset = (int32_t) m_buffer.offset_bytes >> getWidth();
start += (int32_t) offset;
end += (int32_t) offset;
}
AvmAssert(!master->isDependent());
// you may be tempted to optimize for static strings, by creating
// a new static string pointing to the substring... do not do this.
// it will break static strings that point directly into ABC buffers.
return createDependent(gc, master, start, end - start);
}
Stringp String::clone(AvmCore* targetCore)
{
// FIXME can this work? ensure GC is not running for the duration of this call?
// FIXME ensure that client code interns the return value in targetCore appropriately.
int32_t len = length();
String::Pointers p(this);
// hopefully _core() is not used in here
if (getWidth() == String::k8) {
return createLatin1(targetCore, (const char*) p.p8, len, String::k8, false);
} else {
return createUTF16(targetCore, p.p16, len, String::k16, false, false);
}
}
Stringp String::substr(int32_t start, int32_t len)
{
start = (int32_t) NativeObjectHelpers::ClampIndexInt(start, m_length);
len = (int32_t) NativeObjectHelpers::ClampIndexInt(len, m_length);
int32_t end = (m_length-len<=start)? m_length:start+len; // no need to further clamp
return substring(start, end);
}
Stringp String::slice(int32_t start, int32_t end)
{
start = (int32_t) NativeObjectHelpers::ClampIndexInt(start, m_length);
end = (int32_t) NativeObjectHelpers::ClampIndexInt(end, m_length);
if (end < start)
end = start;
return substr(start, end-start);
}
////////////////////////////////// Parsing ////////////////////////////////////
// This routine is a very specific parser to generate a positive integer from a string.
// The following are supported:
// "0" - one single digit for zero - NOT "00" or any other form of zero
//[1-9]+[0-9]* up to 2^32-2 (4294967294)
// 2^32-1 (4294967295) is not supported (see ECMA quote below).
// The ECMA that we're supporting with this routine is...
// cn: the ES3 test for a valid array index is
// "A property name P (in the form of a string value) is an array index if and
// only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 2^32-1."
// Don't support 000000 as 0.
// We don't support 0x1234 as 1234 in hex since string(1234) doesn't equal '0x1234')
// No leading zeros are supported
bool String::parseIndex(uint32_t& result) const
{
// avoid parsing if this string is definitely not a candidate
// A string that is more than 10 digits (and does not start with 0)
// will always be greater than 2^32-1 (4294967295) or not a numeric string
if (m_bitsAndFlags & TSTR_NOUINT_FLAG)
return false;
if (m_bitsAndFlags & (TSTR_UINT28_FLAG | TSTR_UINT32_FLAG))
{
result = m_extra.index;
return true;
}
int64_t n = 0;
wchar wch;
Pointers ptrs(this);
Width w = getWidth();
if (m_length == 0 || m_length > 10)
goto bad;
// collect the value
for (int32_t i = 0; i < m_length; i++)
{
if (w == k8)
wch = wchar(*ptrs.p8++);
else
wch = *ptrs.p16++;
// bad character, or leading zero?
if (wch < '0' || wch > '9' || (i == 1 && n == 0))
goto bad;
n = (n * 10) + wch - '0';
}
// out of range?
if (n >= 0xFFFFFFFF)
goto bad;
if (n & ScriptObject::MAX_INTEGER_MASK)
m_bitsAndFlags |= TSTR_NOINT_FLAG;
if (!isDependent())
{
m_bitsAndFlags |= TSTR_UINT32_FLAG;
m_extra.index = uint32_t(n);
}
result = uint32_t(n);
return true;
bad:
m_bitsAndFlags |= TSTR_NOINT_FLAG | TSTR_NOUINT_FLAG;
return false;
}
Atom String::getIntAtom() const
{
// The call uses a simplified version of parseIndex with 32-bit arithmetic,
// since we can only fit 28 bits.
if (m_bitsAndFlags & TSTR_NOINT_FLAG)
return 0;
if (m_bitsAndFlags & TSTR_UINT28_FLAG)
return Atom((m_extra.index << 3) | AtomConstants::kIntptrType);
int32_t n = 0;
wchar wch;
Pointers ptrs(this);
Width w = getWidth();
if (m_length == 0 || m_length > 10)
goto bad;
// collect the value
for (int32_t i = 0; i < m_length; i++)
{
if (w == k8)
wch = wchar(*ptrs.p8++);
else
wch = *ptrs.p16++;
// bad character, or leading zero?
if (wch < '0' || wch > '9' || (i == 1 && n == 0))
goto bad;
n = (n * 10) + wch - '0';
if (n & ScriptObject::MAX_INTEGER_MASK)
goto bad;
}
if (!isDependent())
{
m_bitsAndFlags |= TSTR_UINT28_FLAG;
m_extra.index = n;
}
return Atom((n << 3) | AtomConstants::kIntptrType);
bad:
m_bitsAndFlags |= TSTR_NOINT_FLAG;
return 0;
}
/////////////////////////////// Case conversion /////////////////////////////////
static const wchar lowerCaseBase[] =
{
/*0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A,
0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074,
0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A,*/ 0x00B5, /*0x00E0, 0x00E1, 0x00E2,
0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC,
0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6,
0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE,*/ 0x00FF, /*0x0101, 0x0103,
0x0105, 0x0107, 0x0109, 0x010B, 0x010D, 0x010F, 0x0111, 0x0113, 0x0115, 0x0117,
0x0119, 0x011B, 0x011D, 0x011F, 0x0121, 0x0123, 0x0125, 0x0127, 0x0129, 0x012B,
0x012D, 0x012F,*/ 0x0131, 0x0133, 0x0135, 0x0137, /*0x013A, 0x013C, 0x013E, 0x0140,
0x0142, 0x0144, 0x0146, 0x0148,*/ /*0x014B, 0x014D, 0x014F, 0x0151, 0x0153, 0x0155,
0x0157, 0x0159, 0x015B, 0x015D, 0x015F, 0x0161, 0x0163, 0x0165, 0x0167, 0x0169,
0x016B, 0x016D, 0x016F, 0x0171, 0x0173, 0x0175, 0x0177,*/ 0x017A, 0x017C, 0x017E,
0x017F, 0x0183, 0x0185, 0x0188, 0x018C, 0x0192, 0x0195, 0x0199, 0x01A1, 0x01A3,
0x01A5, 0x01A8, 0x01AD, 0x01B0, 0x01B4, 0x01B6, 0x01B9, 0x01BD, 0x01BF, 0x01C5,
0x01C6, 0x01C8, 0x01C9, 0x01CB, 0x01CC, 0x01CE, 0x01D0, 0x01D2, 0x01D4, 0x01D6,
0x01D8, 0x01DA, 0x01DC, 0x01DD, 0x01DF, 0x01E1, 0x01E3, 0x01E5, 0x01E7, 0x01E9,
0x01EB, 0x01ED, 0x01EF, 0x01F2, 0x01F3, 0x01F5, 0x01F9, 0x01FB, 0x01FD, 0x01FF,
/*0x0201, 0x0203, 0x0205, 0x0207, 0x0209, 0x020B, 0x020D, 0x020F, 0x0211, 0x0213,
0x0215, 0x0217, 0x0219, 0x021B, 0x021D, 0x021F, 0x0223, 0x0225, 0x0227, 0x0229,
0x022B, 0x022D, 0x022F, 0x0231, 0x0233,*/ 0x0253, 0x0254, 0x0256, 0x0257, 0x0259,
0x025B, 0x0260, 0x0263, 0x0268, 0x0269, 0x026F, 0x0272, 0x0275, 0x0280, 0x0283,
0x0288, 0x028A, 0x028B, 0x0292, 0x0345, 0x03AC, 0x03AD, 0x03AE, 0x03AF, /*0x03B1,
0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB,
0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5,
0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB,*/ 0x03CC, 0x03CD, 0x03CE, 0x03D0,
0x03D1, 0x03D5, 0x03D6, /*0x03DB, 0x03DD, 0x03DF, 0x03E1, 0x03E3, 0x03E5, 0x03E7,
0x03E9, 0x03EB, 0x03ED, 0x03EF,*/ 0x03F0, 0x03F1, 0x03F2, 0x03F5, /*0x0430, 0x0431,
0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B,
0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445,
0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,*/
/*0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459,
0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,*/ /*0x0461, 0x0463, 0x0465, 0x0467,
0x0469, 0x046B, 0x046D, 0x046F, 0x0471, 0x0473, 0x0475, 0x0477, 0x0479, 0x047B,
0x047D, 0x047F, 0x0481, 0x048D, 0x048F, 0x0491, 0x0493, 0x0495, 0x0497, 0x0499,
0x049B, 0x049D, 0x049F, 0x04A1, 0x04A3, 0x04A5, 0x04A7, 0x04A9, 0x04AB, 0x04AD,
0x04AF, 0x04B1, 0x04B3, 0x04B5, 0x04B7, 0x04B9, 0x04BB, 0x04BD, 0x04BF,*/ 0x04C2,
0x04C4, 0x04C8, 0x04CC, /*0x04D1, 0x04D3, 0x04D5, 0x04D7, 0x04D9, 0x04DB, 0x04DD,
0x04DF, 0x04E1, 0x04E3, 0x04E5, 0x04E7, 0x04E9, 0x04EB, 0x04ED, 0x04EF, 0x04F1,
0x04F3, 0x04F5, 0x04F9, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, 0x0570, 0x0571,
0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057A, 0x057B,
0x057C, 0x057D, 0x057E, 0x057F, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585,
0x0586, 0x1E01, 0x1E03, 0x1E05, 0x1E07, 0x1E09, 0x1E0B, 0x1E0D, 0x1E0F, 0x1E11,
0x1E13, 0x1E15, 0x1E17, 0x1E19, 0x1E1B, 0x1E1D, 0x1E1F, 0x1E21, 0x1E23, 0x1E25,
0x1E27, 0x1E29, 0x1E2B, 0x1E2D, 0x1E2F, 0x1E31, 0x1E33, 0x1E35, 0x1E37, 0x1E39,
0x1E3B, 0x1E3D, 0x1E3F, 0x1E41, 0x1E43, 0x1E45, 0x1E47, 0x1E49, 0x1E4B, 0x1E4D,
0x1E4F, 0x1E51, 0x1E53, 0x1E55, 0x1E57, 0x1E59, 0x1E5B, 0x1E5D, 0x1E5F, 0x1E61,
0x1E63, 0x1E65, 0x1E67, 0x1E69, 0x1E6B, 0x1E6D, 0x1E6F, 0x1E71, 0x1E73, 0x1E75,
0x1E77, 0x1E79, 0x1E7B, 0x1E7D, 0x1E7F, 0x1E81, 0x1E83, 0x1E85, 0x1E87, 0x1E89,
0x1E8B, 0x1E8D, 0x1E8F, 0x1E91, 0x1E93, 0x1E95,*/ 0x1E9B, /*0x1EA1, 0x1EA3, 0x1EA5,
0x1EA7, 0x1EA9, 0x1EAB, 0x1EAD, 0x1EAF, 0x1EB1, 0x1EB3, 0x1EB5, 0x1EB7, 0x1EB9,
0x1EBB, 0x1EBD, 0x1EBF, 0x1EC1, 0x1EC3, 0x1EC5, 0x1EC7, 0x1EC9, 0x1ECB, 0x1ECD,
0x1ECF, 0x1ED1, 0x1ED3, 0x1ED5, 0x1ED7, 0x1ED9, 0x1EDB, 0x1EDD, 0x1EDF, 0x1EE1,
0x1EE3, 0x1EE5, 0x1EE7, 0x1EE9, 0x1EEB, 0x1EED, 0x1EEF, 0x1EF1, 0x1EF3, 0x1EF5,
0x1EF7, 0x1EF9,*/ /*0x1F00, 0x1F01, 0x1F02, 0x1F03, 0x1F04, 0x1F05, 0x1F06, 0x1F07,*/
/*0x1F10, 0x1F11, 0x1F12, 0x1F13, 0x1F14, 0x1F15, 0x1F20, 0x1F21, 0x1F22, 0x1F23,
0x1F24, 0x1F25, 0x1F26, 0x1F27, 0x1F30, 0x1F31, 0x1F32, 0x1F33, 0x1F34, 0x1F35,
0x1F36, 0x1F37,*/ 0x1F40, 0x1F41, 0x1F42, 0x1F43, 0x1F44, 0x1F45, 0x1F51, 0x1F53,
0x1F55, 0x1F57, 0x1F60, 0x1F61, 0x1F62, 0x1F63, 0x1F64, 0x1F65, 0x1F66, 0x1F67,
0x1F70, 0x1F71, 0x1F72, 0x1F73, 0x1F74, 0x1F75, 0x1F76, 0x1F77, 0x1F78, 0x1F79,
0x1F7A, 0x1F7B, 0x1F7C, 0x1F7D, 0x1F80, 0x1F81, 0x1F82, 0x1F83, 0x1F84, 0x1F85,
0x1F86, 0x1F87, 0x1F90, 0x1F91, 0x1F92, 0x1F93, 0x1F94, 0x1F95, 0x1F96, 0x1F97,
0x1FA0, 0x1FA1, 0x1FA2, 0x1FA3, 0x1FA4, 0x1FA5, 0x1FA6, 0x1FA7, 0x1FB0, 0x1FB1,
0x1FB3, 0x1FBE, 0x1FC3, 0x1FD0, 0x1FD1, 0x1FE0, 0x1FE1, 0x1FE5, 0x1FF3 /*0x2170,
0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A,
0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0x24D0, 0x24D1, 0x24D2, 0x24D3, 0x24D4,
0x24D5, 0x24D6, 0x24D7, 0x24D8, 0x24D9, 0x24DA, 0x24DB, 0x24DC, 0x24DD, 0x24DE,
0x24DF, 0x24E0, 0x24E1, 0x24E2, 0x24E3, 0x24E4, 0x24E5, 0x24E6, 0x24E7, 0x24E8,
0x24E9 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49,
0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53,
0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A*/
};
static const wchar upperCaseConversion[] =
{
/*0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A,
0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054,
0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A,*/ 0x039C, /*0x00C0, 0x00C1, 0x00C2,
0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC,
0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6,
0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE,*/ 0x0178, /*0x0100, 0x0102,
0x0104, 0x0106, 0x0108, 0x010A, 0x010C, 0x010E, 0x0110, 0x0112, 0x0114, 0x0116,
0x0118, 0x011A, 0x011C, 0x011E, 0x0120, 0x0122, 0x0124, 0x0126, 0x0128, 0x012A,
0x012C, 0x012E,*/ 0x0049, 0x0132, 0x0134, 0x0136, /*0x0139, 0x013B, 0x013D, 0x013F,
0x0141, 0x0143, 0x0145, 0x0147,*/ /*0x014A, 0x014C, 0x014E, 0x0150, 0x0152, 0x0154,
0x0156, 0x0158, 0x015A, 0x015C, 0x015E, 0x0160, 0x0162, 0x0164, 0x0166, 0x0168,
0x016A, 0x016C, 0x016E, 0x0170, 0x0172, 0x0174, 0x0176,*/ 0x0179, 0x017B, 0x017D,
0x0053, 0x0182, 0x0184, 0x0187, 0x018B, 0x0191, 0x01F6, 0x0198, 0x01A0, 0x01A2,
0x01A4, 0x01A7, 0x01AC, 0x01AF, 0x01B3, 0x01B5, 0x01B8, 0x01BC, 0x01F7, 0x01C4,
0x01C4, 0x01C7, 0x01C7, 0x01CA, 0x01CA, 0x01CD, 0x01CF, 0x01D1, 0x01D3, 0x01D5,
0x01D7, 0x01D9, 0x01DB, 0x018E, 0x01DE, 0x01E0, 0x01E2, 0x01E4, 0x01E6, 0x01E8,
0x01EA, 0x01EC, 0x01EE, 0x01F1, 0x01F1, 0x01F4, 0x01F8, 0x01FA, 0x01FC, 0x01FE,
/*0x0200, 0x0202, 0x0204, 0x0206, 0x0208, 0x020A, 0x020C, 0x020E, 0x0210, 0x0212,
0x0214, 0x0216, 0x0218, 0x021A, 0x021C, 0x021E, 0x0222, 0x0224, 0x0226, 0x0228,
0x022A, 0x022C, 0x022E, 0x0230, 0x0232,*/ 0x0181, 0x0186, 0x0189, 0x018A, 0x018F,
0x0190, 0x0193, 0x0194, 0x0197, 0x0196, 0x019C, 0x019D, 0x019F, 0x01A6, 0x01A9,
0x01AE, 0x01B1, 0x01B2, 0x01B7, 0x0399, 0x0386, 0x0388, 0x0389, 0x038A, /*0x0391,
0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B,
0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A3, 0x03A4, 0x03A5,
0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB,*/ 0x038C, 0x038E, 0x038F, 0x0392,
0x0398, 0x03A6, 0x03A0, /*0x03DA, 0x03DC, 0x03DE, 0x03E0, 0x03E2, 0x03E4, 0x03E6,
0x03E8, 0x03EA, 0x03EC, 0x03EE,*/ 0x039A, 0x03A1, 0x03A3, 0x0395, /*0x0410, 0x0411,
0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B,
0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425,
0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,*/
/*0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409,
0x040A, 0x040B, 0x040C, 0x040D, 0x040E, 0x040F,*//*0x0460, 0x0462, 0x0464, 0x0466,
0x0468, 0x046A, 0x046C, 0x046E, 0x0470, 0x0472, 0x0474, 0x0476, 0x0478, 0x047A,
0x047C, 0x047E, 0x0480, 0x048C, 0x048E, 0x0490, 0x0492, 0x0494, 0x0496, 0x0498,
0x049A, 0x049C, 0x049E, 0x04A0, 0x04A2, 0x04A4, 0x04A6, 0x04A8, 0x04AA, 0x04AC,
0x04AE, 0x04B0, 0x04B2, 0x04B4, 0x04B6, 0x04B8, 0x04BA, 0x04BC, 0x04BE,*/ 0x04C1,
0x04C3, 0x04C7, 0x04CB, /*0x04D0, 0x04D2, 0x04D4, 0x04D6, 0x04D8, 0x04DA, 0x04DC,
0x04DE, 0x04E0, 0x04E2, 0x04E4, 0x04E6, 0x04E8, 0x04EA, 0x04EC, 0x04EE, 0x04F0,
0x04F2, 0x04F4, 0x04F8, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537,
0x0538, 0x0539, 0x053A, 0x053B, 0x053C, 0x053D, 0x053E, 0x053F, 0x0540, 0x0541,
0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, 0x0548, 0x0549, 0x054A, 0x054B,
0x054C, 0x054D, 0x054E, 0x054F, 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555,
0x0556, 0x1E00, 0x1E02, 0x1E04, 0x1E06, 0x1E08, 0x1E0A, 0x1E0C, 0x1E0E, 0x1E10,
0x1E12, 0x1E14, 0x1E16, 0x1E18, 0x1E1A, 0x1E1C, 0x1E1E, 0x1E20, 0x1E22, 0x1E24,
0x1E26, 0x1E28, 0x1E2A, 0x1E2C, 0x1E2E, 0x1E30, 0x1E32, 0x1E34, 0x1E36, 0x1E38,
0x1E3A, 0x1E3C, 0x1E3E, 0x1E40, 0x1E42, 0x1E44, 0x1E46, 0x1E48, 0x1E4A, 0x1E4C,
0x1E4E, 0x1E50, 0x1E52, 0x1E54, 0x1E56, 0x1E58, 0x1E5A, 0x1E5C, 0x1E5E, 0x1E60,
0x1E62, 0x1E64, 0x1E66, 0x1E68, 0x1E6A, 0x1E6C, 0x1E6E, 0x1E70, 0x1E72, 0x1E74,
0x1E76, 0x1E78, 0x1E7A, 0x1E7C, 0x1E7E, 0x1E80, 0x1E82, 0x1E84, 0x1E86, 0x1E88,
0x1E8A, 0x1E8C, 0x1E8E, 0x1E90, 0x1E92, 0x1E94,*/ 0x1E60, /*0x1EA0, 0x1EA2, 0x1EA4,
0x1EA6, 0x1EA8, 0x1EAA, 0x1EAC, 0x1EAE, 0x1EB0, 0x1EB2, 0x1EB4, 0x1EB6, 0x1EB8,
0x1EBA, 0x1EBC, 0x1EBE, 0x1EC0, 0x1EC2, 0x1EC4, 0x1EC6, 0x1EC8, 0x1ECA, 0x1ECC,
0x1ECE, 0x1ED0, 0x1ED2, 0x1ED4, 0x1ED6, 0x1ED8, 0x1EDA, 0x1EDC, 0x1EDE, 0x1EE0,
0x1EE2, 0x1EE4, 0x1EE6, 0x1EE8, 0x1EEA, 0x1EEC, 0x1EEE, 0x1EF0, 0x1EF2, 0x1EF4,
0x1EF6, 0x1EF8,*/ /*0x1F08, 0x1F09, 0x1F0A, 0x1F0B, 0x1F0C, 0x1F0D, 0x1F0E, 0x1F0F,*/
/*0x1F18, 0x1F19, 0x1F1A, 0x1F1B, 0x1F1C, 0x1F1D, 0x1F28, 0x1F29, 0x1F2A, 0x1F2B,
0x1F2C, 0x1F2D, 0x1F2E, 0x1F2F, 0x1F38, 0x1F39, 0x1F3A, 0x1F3B, 0x1F3C, 0x1F3D,
0x1F3E, 0x1F3F,*/ 0x1F48, 0x1F49, 0x1F4A, 0x1F4B, 0x1F4C, 0x1F4D, 0x1F59, 0x1F5B,
0x1F5D, 0x1F5F, 0x1F68, 0x1F69, 0x1F6A, 0x1F6B, 0x1F6C, 0x1F6D, 0x1F6E, 0x1F6F,
0x1FBA, 0x1FBB, 0x1FC8, 0x1FC9, 0x1FCA, 0x1FCB, 0x1FDA, 0x1FDB, 0x1FF8, 0x1FF9,
0x1FEA, 0x1FEB, 0x1FFA, 0x1FFB, 0x1F88, 0x1F89, 0x1F8A, 0x1F8B, 0x1F8C, 0x1F8D,
0x1F8E, 0x1F8F, 0x1F98, 0x1F99, 0x1F9A, 0x1F9B, 0x1F9C, 0x1F9D, 0x1F9E, 0x1F9F,
0x1FA8, 0x1FA9, 0x1FAA, 0x1FAB, 0x1FAC, 0x1FAD, 0x1FAE, 0x1FAF, 0x1FB8, 0x1FB9,
0x1FBC, 0x0399, 0x1FCC, 0x1FD8, 0x1FD9, 0x1FE8, 0x1FE9, 0x1FEC, 0x1FFC /*0x2160,
0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A,
0x216B, 0x216C, 0x216D, 0x216E, 0x216F 0x24B6, 0x24B7, 0x24B8, 0x24B9, 0x24BA,
0x24BB, 0x24BC, 0x24BD, 0x24BE, 0x24BF, 0x24C0, 0x24C1, 0x24C2, 0x24C3, 0x24C4,
0x24C5, 0x24C6, 0x24C7, 0x24C8, 0x24C9, 0x24CA, 0x24CB, 0x24CC, 0x24CD, 0x24CE,
0x24CF 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29,
0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33,
0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A*/
};
// 12sep02 grandma : table driven inline function is 14x faster than original function,
// Using first 100 movies of ATS, HashKey alone calls CharToUpper() 360,000 times.
// Only entries 0-254 are used in these tables in unicharToUpper and unicharToLower
// so entry 255 has been removed. Entry 255 is handled by the lowerCaseBase code.
static const uint8_t tolower_map[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //0-15
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //16-31
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //32-47
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //48-63
0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, //64-79
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, //80-95
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //96-111
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //112-127
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //128-143
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //144-159
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //160-175
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //176-191
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, //192-207
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, //208-223
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //224-239
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //240-254
};
static const uint8_t toupper_map[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //0-15
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //16-31
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //32-47
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //48-63
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //64-79
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //80-95
0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, //96-111
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, //112-127
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //128-143
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //144-159
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //160-175
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //176-191
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //192-207
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //208-223
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, //224-239
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 //240-254
};
// WARNING: This is used by the core flash code. Any change to this utility, or the tables
// it relies on, will break legacy Flash content.
/*static*/ uint32_t String::unicharToUpper(uint32_t ch)
{
if (ch < 0xFF)
return toupper_map[ch] ^ ch;
// offset 0x1C60
/*
if ( (ch>=0x2D00 && ch<=0x2D25) ) // Georgian
{
return (ch - 0x1C60);
}
*/
// offset 80
if ( (ch>=0x0450 && ch<=0x045F) )
{
return (ch - 0x50);
}
// offset 48
if ( (ch>=0x0561 && ch<=0x0586) )
{
return (ch - 0x30);
}
// offset -32
if ( (ch>=0x03B1 && ch<=0x03CB) ||
(ch>=0x0430 && ch<=0x044F) ||
(ch>=0xFF41 && ch<=0xFF5A) )
{
return (ch - 0x20);
}
// offset 26
if ( (ch>=0x24D0 && ch<=0x24E9) )
{
return (ch - 0x1A);
}
// offset 16
if ( (ch>=0x2170 && ch<=0x217F) )
{
return (ch - 0x10);
}
// offset +8(positive)
if ( (ch>=0x1F00 && ch<=0X1F07) ||
(ch>=0x1F10 && ch<=0x1F15) ||
(ch>=0x1F20 && ch<=0x1F27) ||
(ch>=0x1F30 && ch<=0x1F37) )
{
return (ch + 0x8);
}
// offset -1
if ( (ch>=0x0101 && ch<=0x0233) )
{
if ( ((ch<=0x012F) && (ch&0x1)) || // odd only
((ch>=0x013A && ch<=0x0148) && !(ch&0x1)) || // even only
((ch>=0x014B && ch<=0x0177) && (ch&1)) || // odd only
((ch>=0x0201 && ch<=0x0233) && (ch&1) && ch!=0x0221) ) // odd only
{
return (ch - 1);
}
}
if ( ch&1 ) // only looking for odd chars here
{
// GREEK
if ( ch>=0x03D9 && ch<=0x03EF )
return (ch - 1);
// CYRILLIC
if ( ((ch>=0x0461 && ch<=0x04BF) && !(ch==0x0483 || ch==0x0485 || ch==0x487 || ch==0x0489)) ||
(ch>=0x04D1 && ch<=0x04F9) )
{
if (ch!=0x0483 && ch!=0x0485 && ch!=0x487 && ch!=0x0489)
return (ch - 1);
}
// LATIN_EXT_ADD
if ( (ch>=0x1E01 && ch<=0x1E95) || (ch>=0x1EA1 && ch<=0x1EF9) )
return (ch - 1);
}
AvmAssert(sizeof(lowerCaseBase) == sizeof(upperCaseConversion));
uint32_t result = ch;
// Do a binary search in lowerCaseBase for char
int32_t lo = 0;
int32_t hi = (sizeof(lowerCaseBase) / sizeof(lowerCaseBase[0])) - 1;
while (lo <= hi)
{
int32_t pivot = (lo+hi)>>1;
uint32_t testChar = lowerCaseBase[pivot];
if (ch == testChar)
{
// Use that index into lowerCaseConversion for a return value
result = upperCaseConversion[pivot];
break;
}
else if (ch < testChar)
{
hi = pivot-1;
}
else
{
lo = pivot+1;
}
}
return result;
}
static const wchar upperCaseBase[] =
{
/*0x0100, 0x0102, 0x0104, 0x0106,
0x0108, 0x010A, 0x010C, 0x010E, 0x0110, 0x0112, 0x0114, 0x0116, 0x0118, 0x011A,
0x011C, 0x011E, 0x0120, 0x0122, 0x0124, 0x0126, 0x0128, 0x012A, 0x012C, 0x012E,*/
0x0130, 0x0132, 0x0134, 0x0136, /*0x0139, 0x013B, 0x013D, 0x013F, 0x0141, 0x0143,
0x0145, 0x0147, 0x014A, 0x014C, 0x014E, 0x0150, 0x0152, 0x0154, 0x0156, 0x0158,
0x015A, 0x015C, 0x015E, 0x0160, 0x0162, 0x0164, 0x0166, 0x0168, 0x016A, 0x016C,
0x016E, 0x0170, 0x0172, 0x0174, 0x0176,*/ 0x0178, 0x0179, 0x017B, 0x017D, 0x0181,
0x0182, 0x0184, 0x0186, 0x0187, 0x0189, 0x018A, 0x018B, 0x018E, 0x018F, 0x0190,
0x0191, 0x0193, 0x0194, 0x0196, 0x0197, 0x0198, 0x019C, 0x019D, 0x019F, 0x01A0,
0x01A2, 0x01A4, 0x01A6, 0x01A7, 0x01A9, 0x01AC, 0x01AE, 0x01AF, 0x01B1, 0x01B2,
0x01B3, 0x01B5, 0x01B7, 0x01B8, 0x01BC, 0x01C4, 0x01C5, 0x01C7, 0x01C8, 0x01CA,
0x01CB, 0x01CD, 0x01CF, 0x01D1, 0x01D3, 0x01D5, 0x01D7, 0x01D9, 0x01DB, 0x01DE,
0x01E0, 0x01E2, 0x01E4, 0x01E6, 0x01E8, 0x01EA, 0x01EC, 0x01EE, 0x01F1, 0x01F2,
0x01F4, 0x01F6, 0x01F7, 0x01F8, 0x01FA, 0x01FC, 0x01FE, /*0x0200, 0x0202, 0x0204,
0x0206, 0x0208, 0x020A, 0x020C, 0x020E, 0x0210, 0x0212, 0x0214, 0x0216, 0x0218,
0x021A, 0x021C, 0x021E, 0x0222, 0x0224, 0x0226, 0x0228, 0x022A, 0x022C, 0x022E,
0x0230, 0x0232,*/ 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, 0x038F, /*0x0391,
0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B,
0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6,
0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03DA, 0x03DC, 0x03DE, 0x03E0, 0x03E2,
0x03E4, 0x03E6, 0x03E8, 0x03EA, 0x03EC, 0x03EE,*/ 0x03F4, /*0x0400, 0x0401, 0x0402,
0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C,
0x040D, 0x040E, 0x040F,*/ /*0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416,
0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420,
0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A,
0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0460, 0x0462, 0x0464, 0x0466, 0x0468,
0x046A, 0x046C, 0x046E, 0x0470, 0x0472, 0x0474, 0x0476, 0x0478, 0x047A, 0x047C,
0x047E, 0x0480, 0x048C, 0x048E, 0x0490, 0x0492, 0x0494, 0x0496, 0x0498, 0x049A,
0x049C, 0x049E, 0x04A0, 0x04A2, 0x04A4, 0x04A6, 0x04A8, 0x04AA, 0x04AC, 0x04AE,
0x04B0, 0x04B2, 0x04B4, 0x04B6, 0x04B8, 0x04BA, 0x04BC, 0x04BE,*/ 0x04C1, 0x04C3,
0x04C7, 0x04CB, /*0x04D0, 0x04D2, 0x04D4, 0x04D6, 0x04D8, 0x04DA, 0x04DC, 0x04DE,
0x04E0, 0x04E2, 0x04E4, 0x04E6, 0x04E8, 0x04EA, 0x04EC, 0x04EE, 0x04F0, 0x04F2,
0x04F4, 0x04F8, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, 0x0538,
0x0539, 0x053A, 0x053B, 0x053C, 0x053D, 0x053E, 0x053F, 0x0540, 0x0541, 0x0542,
0x0543, 0x0544, 0x0545, 0x0546, 0x0547, 0x0548, 0x0549, 0x054A, 0x054B, 0x054C,
0x054D, 0x054E, 0x054F, 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556,*/
/*
// cn: added Georgian. Not in the UnicodeData-3.2.0 spreadsheet, but was added later
0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8, 0x10a9,
0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0, 0x10b1, 0x10b2, 0x10b3,
0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd,
0x10be, 0x10bf, 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5,
// cn: end Georgian.
*/
/*0x1E00, 0x1E02, 0x1E04, 0x1E06, 0x1E08, 0x1E0A, 0x1E0C, 0x1E0E, 0x1E10, 0x1E12,
0x1E14, 0x1E16, 0x1E18, 0x1E1A, 0x1E1C, 0x1E1E, 0x1E20, 0x1E22, 0x1E24, 0x1E26,
0x1E28, 0x1E2A, 0x1E2C, 0x1E2E, 0x1E30, 0x1E32, 0x1E34, 0x1E36, 0x1E38, 0x1E3A,
0x1E3C, 0x1E3E, 0x1E40, 0x1E42, 0x1E44, 0x1E46, 0x1E48, 0x1E4A, 0x1E4C, 0x1E4E,
0x1E50, 0x1E52, 0x1E54, 0x1E56, 0x1E58, 0x1E5A, 0x1E5C, 0x1E5E, 0x1E60, 0x1E62,
0x1E64, 0x1E66, 0x1E68, 0x1E6A, 0x1E6C, 0x1E6E, 0x1E70, 0x1E72, 0x1E74, 0x1E76,
0x1E78, 0x1E7A, 0x1E7C, 0x1E7E, 0x1E80, 0x1E82, 0x1E84, 0x1E86, 0x1E88, 0x1E8A,
0x1E8C, 0x1E8E, 0x1E90, 0x1E92, 0x1E94, 0x1EA0, 0x1EA2, 0x1EA4, 0x1EA6, 0x1EA8,
0x1EAA, 0x1EAC, 0x1EAE, 0x1EB0, 0x1EB2, 0x1EB4, 0x1EB6, 0x1EB8, 0x1EBA, 0x1EBC,
0x1EBE, 0x1EC0, 0x1EC2, 0x1EC4, 0x1EC6, 0x1EC8, 0x1ECA, 0x1ECC, 0x1ECE, 0x1ED0,
0x1ED2, 0x1ED4, 0x1ED6, 0x1ED8, 0x1EDA, 0x1EDC, 0x1EDE, 0x1EE0, 0x1EE2, 0x1EE4,
0x1EE6, 0x1EE8, 0x1EEA, 0x1EEC, 0x1EEE, 0x1EF0, 0x1EF2, 0x1EF4, 0x1EF6, 0x1EF8,
0x1F08, 0x1F09, 0x1F0A, 0x1F0B, 0x1F0C, 0x1F0D, 0x1F0E, 0x1F0F, 0x1F18, 0x1F19,
0x1F1A, 0x1F1B, 0x1F1C, 0x1F1D, 0x1F28, 0x1F29, 0x1F2A, 0x1F2B, 0x1F2C, 0x1F2D,
0x1F2E, 0x1F2F, 0x1F38, 0x1F39, 0x1F3A, 0x1F3B, 0x1F3C, 0x1F3D, 0x1F3E, 0x1F3F,*/
0x1F48, 0x1F49, 0x1F4A, 0x1F4B, 0x1F4C, 0x1F4D, 0x1F59, 0x1F5B, 0x1F5D, 0x1F5F,
0x1F68, 0x1F69, 0x1F6A, 0x1F6B, 0x1F6C, 0x1F6D, 0x1F6E, 0x1F6F, 0x1F88, 0x1F89,
0x1F8A, 0x1F8B, 0x1F8C, 0x1F8D, 0x1F8E, 0x1F8F, 0x1F98, 0x1F99, 0x1F9A, 0x1F9B,
0x1F9C, 0x1F9D, 0x1F9E, 0x1F9F, 0x1FA8, 0x1FA9, 0x1FAA, 0x1FAB, 0x1FAC, 0x1FAD,
0x1FAE, 0x1FAF, 0x1FB8, 0x1FB9, 0x1FBA, 0x1FBB, 0x1FBC, 0x1FC8, 0x1FC9, 0x1FCA,
0x1FCB, 0x1FCC, 0x1FD8, 0x1FD9, 0x1FDA, 0x1FDB, 0x1FE8, 0x1FE9, 0x1FEA, 0x1FEB,
0x1FEC, 0x1FF8, 0x1FF9, 0x1FFA, 0x1FFB, 0x1FFC, 0x2126, 0x212A, 0x212B /*0x2160,
0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A,
0x216B, 0x216C, 0x216D, 0x216E, 0x216F, 0x24B6, 0x24B7, 0x24B8, 0x24B9, 0x24BA,
0x24BB, 0x24BC, 0x24BD, 0x24BE, 0x24BF, 0x24C0, 0x24C1, 0x24C2, 0x24C3, 0x24C4,
0x24C5, 0x24C6, 0x24C7, 0x24C8, 0x24C9, 0x24CA, 0x24CB, 0x24CC, 0x24CD, 0x24CE,
0x24CF, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29,
0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33,
0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A*/
};
static const wchar lowerCaseConversion[] =
{
/*0x0101, 0x0103, 0x0105, 0x0107,
0x0109, 0x010B, 0x010D, 0x010F, 0x0111, 0x0113, 0x0115, 0x0117, 0x0119, 0x011B,
0x011D, 0x011F, 0x0121, 0x0123, 0x0125, 0x0127, 0x0129, 0x012B, 0x012D, 0x012F,*/
0x0069, 0x0133, 0x0135, 0x0137, /*0x013A, 0x013C, 0x013E, 0x0140, 0x0142, 0x0144,
0x0146, 0x0148, 0x014B, 0x014D, 0x014F, 0x0151, 0x0153, 0x0155, 0x0157, 0x0159,
0x015B, 0x015D, 0x015F, 0x0161, 0x0163, 0x0165, 0x0167, 0x0169, 0x016B, 0x016D,
0x016F, 0x0171, 0x0173, 0x0175, 0x0177,*/ 0x00FF, 0x017A, 0x017C, 0x017E, 0x0253,
0x0183, 0x0185, 0x0254, 0x0188, 0x0256, 0x0257, 0x018C, 0x01DD, 0x0259, 0x025B,
0x0192, 0x0260, 0x0263, 0x0269, 0x0268, 0x0199, 0x026F, 0x0272, 0x0275, 0x01A1,
0x01A3, 0x01A5, 0x0280, 0x01A8, 0x0283, 0x01AD, 0x0288, 0x01B0, 0x028A, 0x028B,
0x01B4, 0x01B6, 0x0292, 0x01B9, 0x01BD, 0x01C6, 0x01C6, 0x01C9, 0x01C9, 0x01CC,
0x01CC, 0x01CE, 0x01D0, 0x01D2, 0x01D4, 0x01D6, 0x01D8, 0x01DA, 0x01DC, 0x01DF,
0x01E1, 0x01E3, 0x01E5, 0x01E7, 0x01E9, 0x01EB, 0x01ED, 0x01EF, 0x01F3, 0x01F3,
0x01F5, 0x0195, 0x01BF, 0x01F9, 0x01FB, 0x01FD, 0x01FF, /*0x0201, 0x0203, 0x0205,
0x0207, 0x0209, 0x020B, 0x020D, 0x020F, 0x0211, 0x0213, 0x0215, 0x0217, 0x0219,
0x021B, 0x021D, 0x021F, 0x0223, 0x0225, 0x0227, 0x0229, 0x022B, 0x022D, 0x022F,
0x0231, 0x0233,*/ 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03CC, 0x03CD, 0x03CE, /*0x03B1,
0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB,
0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6,
0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03DB, 0x03DD, 0x03DF, 0x03E1, 0x03E3,
0x03E5, 0x03E7, 0x03E9, 0x03EB, 0x03ED, 0x03EF,*/ 0x03B8, /*0x0450, 0x0451, 0x0452,
0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C,
0x045D, 0x045E, 0x045F,*/ /*0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436,
0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440,
0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A,
0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x0461, 0x0463, 0x0465, 0x0467, 0x0469,
0x046B, 0x046D, 0x046F, 0x0471, 0x0473, 0x0475, 0x0477, 0x0479, 0x047B, 0x047D,
0x047F, 0x0481, 0x048D, 0x048F, 0x0491, 0x0493, 0x0495, 0x0497, 0x0499, 0x049B,
0x049D, 0x049F, 0x04A1, 0x04A3, 0x04A5, 0x04A7, 0x04A9, 0x04AB, 0x04AD, 0x04AF,
0x04B1, 0x04B3, 0x04B5, 0x04B7, 0x04B9, 0x04BB, 0x04BD, 0x04BF,*/ 0x04C2, 0x04C4,
0x04C8, 0x04CC, /*0x04D1, 0x04D3, 0x04D5, 0x04D7, 0x04D9, 0x04DB, 0x04DD, 0x04DF,
0x04E1, 0x04E3, 0x04E5, 0x04E7, 0x04E9, 0x04EB, 0x04ED, 0x04EF, 0x04F1, 0x04F3,
0x04F5, 0x04F9, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568,
0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, 0x0570, 0x0571, 0x0572,
0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057A, 0x057B, 0x057C,
0x057D, 0x057E, 0x057F, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586,*/
// cn: added Georgian. Not in the UnicodeData-3.2.0 spreadsheet, but was added later
/*
0x10d0, 0x10d1, 0x10d2, 0x10d3, 0x10d4, 0x10d5, 0x10d6, 0x10d7, 0x10d8, 0x10d9,
0x10da, 0x10db, 0x10dc, 0x10dd, 0x10de, 0x10df, 0x10e0, 0x10e1, 0x10e2, 0x10e3,
0x10e4, 0x10e5, 0x10e6, 0x10e7, 0x10e8, 0x10e9, 0x10ea, 0x10eb, 0x10ec, 0x10ed,
0x10ee, 0x10ef, 0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5,
// cn: end Georgian.
*/
/*0x1E01, 0x1E03, 0x1E05, 0x1E07, 0x1E09, 0x1E0B, 0x1E0D, 0x1E0F, 0x1E11, 0x1E13,
0x1E15, 0x1E17, 0x1E19, 0x1E1B, 0x1E1D, 0x1E1F, 0x1E21, 0x1E23, 0x1E25, 0x1E27,
0x1E29, 0x1E2B, 0x1E2D, 0x1E2F, 0x1E31, 0x1E33, 0x1E35, 0x1E37, 0x1E39, 0x1E3B,
0x1E3D, 0x1E3F, 0x1E41, 0x1E43, 0x1E45, 0x1E47, 0x1E49, 0x1E4B, 0x1E4D, 0x1E4F,
0x1E51, 0x1E53, 0x1E55, 0x1E57, 0x1E59, 0x1E5B, 0x1E5D, 0x1E5F, 0x1E61, 0x1E63,
0x1E65, 0x1E67, 0x1E69, 0x1E6B, 0x1E6D, 0x1E6F, 0x1E71, 0x1E73, 0x1E75, 0x1E77,
0x1E79, 0x1E7B, 0x1E7D, 0x1E7F, 0x1E81, 0x1E83, 0x1E85, 0x1E87, 0x1E89, 0x1E8B,
0x1E8D, 0x1E8F, 0x1E91, 0x1E93, 0x1E95, 0x1EA1, 0x1EA3, 0x1EA5, 0x1EA7, 0x1EA9,
0x1EAB, 0x1EAD, 0x1EAF, 0x1EB1, 0x1EB3, 0x1EB5, 0x1EB7, 0x1EB9, 0x1EBB, 0x1EBD,
0x1EBF, 0x1EC1, 0x1EC3, 0x1EC5, 0x1EC7, 0x1EC9, 0x1ECB, 0x1ECD, 0x1ECF, 0x1ED1,
0x1ED3, 0x1ED5, 0x1ED7, 0x1ED9, 0x1EDB, 0x1EDD, 0x1EDF, 0x1EE1, 0x1EE3, 0x1EE5,
0x1EE7, 0x1EE9, 0x1EEB, 0x1EED, 0x1EEF, 0x1EF1, 0x1EF3, 0x1EF5, 0x1EF7, 0x1EF9,
0x1F00, 0x1F01, 0x1F02, 0x1F03, 0x1F04, 0x1F05, 0x1F06, 0x1F07, 0x1F10, 0x1F11,
0x1F12, 0x1F13, 0x1F14, 0x1F15, 0x1F20, 0x1F21, 0x1F22, 0x1F23, 0x1F24, 0x1F25,
0x1F26, 0x1F27, 0x1F30, 0x1F31, 0x1F32, 0x1F33, 0x1F34, 0x1F35, 0x1F36, 0x1F37,*/
0x1F40, 0x1F41, 0x1F42, 0x1F43, 0x1F44, 0x1F45, 0x1F51, 0x1F53, 0x1F55, 0x1F57,
0x1F60, 0x1F61, 0x1F62, 0x1F63, 0x1F64, 0x1F65, 0x1F66, 0x1F67, 0x1F80, 0x1F81,
0x1F82, 0x1F83, 0x1F84, 0x1F85, 0x1F86, 0x1F87, 0x1F90, 0x1F91, 0x1F92, 0x1F93,
0x1F94, 0x1F95, 0x1F96, 0x1F97, 0x1FA0, 0x1FA1, 0x1FA2, 0x1FA3, 0x1FA4, 0x1FA5,
0x1FA6, 0x1FA7, 0x1FB0, 0x1FB1, 0x1F70, 0x1F71, 0x1FB3, 0x1F72, 0x1F73, 0x1F74,
0x1F75, 0x1FC3, 0x1FD0, 0x1FD1, 0x1F76, 0x1F77, 0x1FE0, 0x1FE1, 0x1F7A, 0x1F7B,
0x1FE5, 0x1F78, 0x1F79, 0x1F7C, 0x1F7D, 0x1FF3, 0x03C9, 0x006B, 0x00E5 /*0x2170,
0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A,
0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0x24D0, 0x24D1, 0x24D2, 0x24D3, 0x24D4,
0x24D5, 0x24D6, 0x24D7, 0x24D8, 0x24D9, 0x24DA, 0x24DB, 0x24DC, 0x24DD, 0x24DE,
0x24DF, 0x24E0, 0x24E1, 0x24E2, 0x24E3, 0x24E4, 0x24E5, 0x24E6, 0x24E7, 0x24E8,
0x24E9, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49,
0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53,
0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A*/
};
// WARNING: This is used by the core flash code. Any change to this utility, or the tables
// it relies on, will break legacy Flash content.
/*static*/ uint32_t String::unicharToLower(uint32_t ch)
{
if (ch < 0xFF)
return tolower_map[ch] ^ ch;
// offset x1C60
if( ch>=0x10A0 && ch<=0x10C5 ) // Georgian
{
return (ch + 48); // WRONG
//return (ch + 0x1C60); // CORRECT
}
// offset 80
if ( (ch>=0x0400 && ch<=0x040F) )
{
return (ch + 0x50);
}
// offset 48
if ( (ch>=0x0531 && ch<=0x0556) )
{
return (ch + 0x30);
}
// offset 32
if ( (ch>=0x0391 && ch<=0x03AB) ||
(ch>=0x0410 && ch<=0x042F) ||
(ch>=0xFF21 && ch<=0xFF3A) )
{
return (ch + 0x20);
}
// offset 26
if ( (ch>=0x24B6 && ch<=0x24CF) )
{
return (ch + 0x1A);
}
// offset 16
if ( (ch>=0x2160 && ch<=0x216F) )
{
return (ch + 0x10);
}
// offset -8(negative)
if ( (ch>=0x1F08 && ch<=0X1F0F) ||
(ch>=0x1F18 && ch<=0x1F1D) ||
(ch>=0x1F28 && ch<=0x1F2F) ||
(ch>=0x1F38 && ch<=0x1F3F) )
{
return (ch - 0x8);
}
// offset +1
if ( (ch>=0x0100 && ch<=0x0232) )
{
// only even numbers
if ( ((ch<=0x012E) && !(ch&0x1)) ||
((ch>=0x0139 && ch<=0x0147) && (ch&0x1)) ||
((ch>=0x014A && ch<=0x0176) && !(ch&1)) ||
((ch>=0x0200 && ch<=0x0232) && !(ch&1) && (ch!=0x0220)) )
{
return (ch + 1);
}
}
if ( !(ch&1) ) // only looking for even chars
{
if ( (ch>=0x03D8 && ch<=0x03EE) )
return (ch + 1);
if ( ((ch>=0x0460 && ch<=0x04BE) && !(ch==0x0482 || ch==0x0484 || ch==0x486 || ch==0x0488)) ||
(ch>=0x04D0 && ch<=0x04F8) )
{
if (ch!=0x0482 && ch!=0x0484 && ch!=0x486 && ch!=0x0488)
return (ch + 1);
}
if ( (ch>=0x1E00 && ch<=0x1E94) || (ch>=0x1EA0 && ch<=0x1EF8) )
return (ch + 1);
}
AvmAssert(sizeof(upperCaseBase) == sizeof(lowerCaseConversion));
uint32_t result = ch;
// Do a binary search in upperCaseBase for char
int32_t lo = 0;
int32_t hi = (sizeof(upperCaseBase) / sizeof(upperCaseBase[0])) - 1;
while (lo <= hi)
{
int32_t pivot = (lo+hi)>>1;
uint32_t testChar = upperCaseBase[pivot];
if (ch == testChar)
{
// Use that index into lowerCaseConversion for a return value
result = lowerCaseConversion[pivot];
break;
}
else if (ch < testChar)
{
hi = pivot-1;
}
else
{
lo = pivot+1;
}
}
return result;
}
Stringp String::toUpperCase()
{
return caseChange(unicharToUpper);
}
Stringp String::toLowerCase()
{
return caseChange(unicharToLower);
}
Stringp String::caseChange(uint32_t(*unimapper)(uint32_t))
{
if (!this->length())
return this;
// Flag to detect whether any changes were made
bool changed = false;
// 0xFF is a special case: ToUpper(0xFF) == 0x178, so we need a wider string
// if the string contains 0xFF
Width w = getWidth();
if (w == k8 && VMPI_memchr(Pointers(this).p8, 0xFF, m_length) != 0)
w = k16;
GC* gc = _gc(this);
const bool is7bit = false;
Stringp newStr = createDynamic(gc, NULL, m_length, w, is7bit);
Pointers src(this); // can't re-use the Pointers from VMPI_memchr; createDynamic may have invalidated it
Pointers dst(newStr);
int32_t i;
uint32_t ch1, ch2;
if (getWidth() == k8)
{
for (i = 0; i < m_length; i++)
{
ch1 = uint32_t(*src.p8++);
ch2 = unimapper (ch1);
if (w == k16)
*dst.p16++ = wchar(ch2);
else
*dst.p8++ = uint8_t(ch2);
if (ch1 != ch2)
changed = true;
}
}
else
{
for (i = 0; i < m_length; i++)
{
ch1 = uint32_t(*src.p16++);
ch2 = unimapper(ch1);
*dst.p16++ = wchar(ch2);
if (ch1 != ch2)
changed = true;
}
}
VERIFY_7BIT(this);
VERIFY_7BIT(newStr);
return changed ? newStr : this;
}
double String::toNumber()
{
return MathUtils::convertStringToNumber(this);
}
bool String::isWhitespace() const
{
StringIndexer self((Stringp) this);
for (int32_t i = 0 ; i < length(); i++)
{
wchar ch = self[i];
if (!isSpace(ch))
return false;
}
return true;
}
/*static*/ int32_t FASTCALL String::Length(const wchar* str)
{
AvmAssert(str != NULL);
const wchar* s = str;
while (*s)
s++;
ptrdiff_t len = s - str;
if (len <= 0x7FFFFFFF)
return int32_t(len);
GCHeap::SignalObjectTooLarge();
/*NOTREACHED*/
return 0;
}
/*static*/ int32_t FASTCALL String::Length(const char* str)
{
AvmAssert(str != NULL);
size_t len = VMPI_strlen(str);
if (len <= 0x7FFFFFFF)
return int32_t(len);
GCHeap::SignalObjectTooLarge();
/*NOTREACHED*/
return 0;
}
int String::_indexOf(Stringp substr, int startPos)
{
return (int) indexOf(substr, (int32_t) startPos);
}
// Coded as a macro for best performance with MSVC 2008 32-bit
// -1 (or negative infinity) - position equals -1 (out of bounds)
// >length (or positive infinity) - position equals length
// non-NaN - integer cast to determine position
// NaN - use NaNValue (usually zero but length for lastIndexOf)
#define INTCLAMP(iPos, dPos, length, NaNValue) \
{ \
if (dPos <= -1) \
iPos = -1; \
else if (dPos >= length) \
iPos = length; \
else if (!MathUtils::isNaNInline(dPos)) { \
iPos = (int32_t) dPos; \
} \
else { \
iPos = NaNValue; \
} \
}
int32_t String::AS3_indexOf(Stringp substr, double dStartPos)
{
int32_t iPos;
INTCLAMP(iPos, dStartPos, this->length(), 0);
return indexOf(substr, iPos);
}
int String::_lastIndexOf(Stringp substr, int iStartPos)
{
return (int) lastIndexOf(substr, (int32_t) iStartPos);
}
int32_t String::AS3_lastIndexOf(Stringp substr, double dStartPos)
{
// unlike most other calls, this one has nan->length rather than nan->0
int32_t iStartPos;
INTCLAMP(iStartPos, dStartPos, this->length(), this->length());
return lastIndexOf(substr, iStartPos);
}
Stringp String::_substr(int start, int count)
{
int len = this->length();
start = (int)NativeObjectHelpers::ClampIndexInt(start, len);
// ClampIndex takes a double (not int or uint) for first parm...
// we must cast these to double before addition, otherwise we
// can have numeric overflow with the default arg (end=0x7fffffff)
// and wrap to negative, which would be bad...
// Do some sanity checks on our ints to see if they will fall within a valid integer range
// !!@what about negative values?
int end;
if (count == 0x7fffffff) // largest positive int value, also the default arg
{
end = len;
}
else if ((count > 0x3fffffff) || (start > 0x3fffffff)) // might overflow - use doubles
{
end = (int)NativeObjectHelpers::ClampIndex(double(count) + double(start), len);
}
else
{
end = (int)NativeObjectHelpers::ClampIndexInt(count + start, len);
}
if (end < start)
end = start;
return substr(start, end-start);
}
Stringp String::AS3_substr(double d_start, double d_count)
{
int32_t len = this->length();
int32_t start = (int32_t) NativeObjectHelpers::ClampIndex(MathUtils::toInt(d_start), len);
// ClampIndex takes a double (not int or uint) for first parm...
// we must cast these to double before addition, otherwise we
// can have numeric overflow with the default arg (end=0x7fffffff)
// and wrap to negative, which would be bad...
int32_t end = (int32_t)NativeObjectHelpers::ClampIndex(MathUtils::toInt(d_count) + (double)start, len);
if (end < start)
end = start;
return substr(start, end-start);
}
Stringp String::_substring(int start, int end)
{
NativeObjectHelpers::ClampBInt(start, end, this->length());
return substring((int32_t) start, (int32_t) end);
}
Stringp String::AS3_substring(double d_start, double d_end)
{
double start = MathUtils::toInt(d_start);
double end = MathUtils::toInt(d_end);
NativeObjectHelpers::ClampB(start, end, length());
return substring((int32_t) start, (int32_t) end);
}
Stringp String::_slice(int start, int end)
{
return slice((int32_t) start, (int32_t) end);
}
Stringp String::AS3_slice(double d_start, double d_end)
{
int32_t len = this->length();
int32_t start = (int32_t)NativeObjectHelpers::ClampIndex(MathUtils::toInt(d_start), len);
int32_t end = (int32_t)NativeObjectHelpers::ClampIndex(MathUtils::toInt(d_end), len);
if (end < start)
end = start;
return substring(start, end);
}
Stringp String::_charAtI(int32_t iPos)
{
AvmCore* core = _core(this);
// use unsigned compare as faster equivalent to >= 0 && < m_length
if (uint32_t(iPos) < uint32_t(m_length)) {
Pointers ptrs(this);
wchar const ch = (wchar) (getWidth() == k8) ? ptrs.p8[iPos] : ptrs.p16[iPos];
// newStringUTF16 does the cachedChar optimization internally, but short-circuiting the test
// here is worthwhile because it's a pretty common case
if (ch < 128)
return core->cachedChars[ch];
return core->newStringUTF16(&ch, 1);
}
else {
return core->kEmptyString;
}
}
Stringp String::_charAtU(uint32_t iPos)
{
AvmCore* core = _core(this);
if (iPos < uint32_t(m_length)) {
Pointers ptrs(this);
wchar const ch = (wchar) (getWidth() == k8) ? ptrs.p8[iPos] : ptrs.p16[iPos];
// newStringUTF16 does the cachedChar optimization internally, but short-circuiting the test
// here is worthwhile because it's a pretty common case
if (ch < 128)
return core->cachedChars[ch];
return core->newStringUTF16(&ch, 1);
}
else {
return core->kEmptyString;
}
}
Stringp String::AS3_charAt(double dPos)
{
int32_t iPos;
INTCLAMP(iPos, dPos, this->length(), 0);
AvmCore* core = _core(this);
// use unsigned compare as faster equivalent to >= 0 && < m_length
if (uint32_t(iPos) < uint32_t(m_length)) {
Pointers ptrs(this);
wchar const ch = (wchar) (getWidth() == k8) ? ptrs.p8[iPos] : ptrs.p16[iPos];
// newStringUTF16 does the cachedChar optimization internally, but short-circuiting the test
// here is worthwhile because it's a pretty common case
if (ch < 128)
return core->cachedChars[ch];
return core->newStringUTF16(&ch, 1);
}
else {
return core->kEmptyString;
}
}
Stringp String::AS3_toUpperCase()
{
return toUpperCase();
}
Stringp String::AS3_toLowerCase()
{
return toLowerCase();
}
double String::_charCodeAtDI(int32_t iPos)
{
double d;
// use unsigned compare as faster equivalent to >= 0 && < m_length
if (uint32_t(iPos) < uint32_t(m_length)) {
Pointers ptrs(this);
d = (double) ((getWidth() == k8) ? ptrs.p8[iPos] : ptrs.p16[iPos]);
}
else
d = MathUtils::kNaN;
return d;
}
int32_t String::_charCodeAtII(int32_t iPos)
{
// use unsigned compare as faster equivalent to >= 0 && < m_length
if (uint32_t(iPos) < uint32_t(m_length)) {
Pointers ptrs(this);
return (getWidth() == k8) ? ptrs.p8[iPos] : ptrs.p16[iPos];
}
else
return 0;
}
double String::_charCodeAtDU(uint32_t iPos)
{
double d;
if (iPos < uint32_t(m_length)) {
Pointers ptrs(this);
d = (double) ((getWidth() == k8) ? ptrs.p8[iPos] : ptrs.p16[iPos]);
}
else
d = MathUtils::kNaN;
return d;
}
int32_t String::_charCodeAtIU(uint32_t iPos)
{
if (iPos < uint32_t(m_length)) {
Pointers ptrs(this);
return (getWidth() == k8) ? ptrs.p8[iPos] : ptrs.p16[iPos];
}
else
return 0;
}
double String::AS3_charCodeAt(double dPos)
{
int32_t iPos;
INTCLAMP(iPos, dPos, m_length, 0);
// use unsigned compare as faster equivalent to >= 0 && < m_length
if (uint32_t(iPos) < uint32_t(m_length)) {
Pointers ptrs(this);
return (double) ((getWidth() == k8) ? ptrs.p8[iPos] : ptrs.p16[iPos]);
}
else
return MathUtils::kNaN;
}
int32_t String::_charCodeAtID(double dPos)
{
int32_t iPos;
INTCLAMP(iPos, dPos, m_length, 0);
// use unsigned compare as faster equivalent to >= 0 && < m_length
if (uint32_t(iPos) < uint32_t(m_length)) {
Pointers ptrs(this);
return (getWidth() == k8) ? ptrs.p8[iPos] : ptrs.p16[iPos];
}
else
return 0;
}
int32_t String::AS3_localeCompare(Atom other)
{
Stringp otherStr;
if (AvmCore::isString(other))
{
otherStr = AvmCore::atomToString(other);
}
else
{
AvmCore* core = _core(this);
// legacy behavior special-cases NULL (including undefined).
if (AvmCore::isNullOrUndefined(other))
{
if (!core->currentBugCompatibility()->bugzilla585791)
return (m_length == 0) ? 1 : 0;
}
// Proper ECMAScript behavior:
// The 'this' object is converted to string, call it a.
// The argument is converted to string, call it b.
// Those strings are compared and a string comparison value is returned.
otherStr = core->string(other);
}
return otherStr->Compare(*this);
}
#ifdef DEBUGGER
uint64_t String::bytesUsed() const
{
uint64_t bytesUsed = sizeof(String);
// If getType() == kDependent, the buffer's memory is account for by
// the string upon which this string depends, so we should not include
// it here.
//
// If getType() == kStatic, the buffer's memory is not on the GC heap,
// and freeing the string would not free the buffer, so we should not
// include its size here.
if (getType() == kDynamic)
bytesUsed += GC::Size(m_buffer.pv);
return bytesUsed;
}
Stringp String::getMasterString() const
{
if (isDependent())
return m_extra.master;
else
return NULL;
}
#endif
////////////////////////////// Helpers: Width Analysis /////////////////////////////////
// The character widths structure used by the _analyze() functions below
struct StringWidths
{
int32_t ascii, w8, w16, w32;
};
// Analyze a UTF-8 string buffer and find out about the character widths.
// Return false if the string contains malformed or too big sequences,
// and strict mode is requested.
// RFC 2279
// 0000 0000-0000 007F 0xxxxxxx
// 0000 0080-0000 07FF 110xxxxx 10xxxxxx
// 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
// 0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
// 0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
// 0400 0000-7FFF FFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
static bool _analyzeUtf8(const utf8_t* in, int32_t inLen, StringWidths& r, bool strict)
{
r.ascii = r.w8 = r.w16 = r.w32 = 0;
while (inLen > 0)
{
uint32_t c = uint32_t (*in);
switch (c >> 4)
{
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
r.w8++;
r.ascii++;
in++;
inLen--;
break;
case 12: case 13:
// 110xxxxx 10xxxxxx
if (inLen < 2) {
// Invalid
goto invalid;
}
if ((in[1]&0xC0) != 0x80) {
// Invalid
goto invalid;
}
c = (((c<<6) & 0x7C0) | (in[1] & 0x3F));
if (c < 0x80) {
// Overlong sequence, reject as invalid.
goto invalid;
}
in += 2;
inLen -= 2;
if (c < 0xFF)
r.w8++;
else
r.w16++;
break;
case 14:
// 1110xxxx 10xxxxxx 10xxxxxx
if (inLen < 3) {
// Invalid
goto invalid;
}
if ((in[1]&0xC0) != 0x80 || (in[2]&0xC0) != 0x80) {
// Invalid
goto invalid;
}
c = (((c<<12) & 0xF000) | ((in[1]<<6) & 0xFC0) | (in[2] & 0x3F));
if (c < 0x800) {
// Overlong sequence, reject as invalid.
goto invalid;
}
in += 3;
inLen -= 3;
r.w16++;
break;
case 15:
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
// 111110xx ... is always invalid
// 1111110x ... is always invalid
// note: when 'strict' is false, we need to mimic the behavior of FP9/FP10,
// which did not do the c&8 test. so skip it for bug-compatibility.
if ((strict && (c & 0x08)) || (inLen < 4)) {
// Invalid
goto invalid;
}
if ((in[1]&0xC0) != 0x80 ||
(in[2]&0xC0) != 0x80 ||
(in[3]&0xC0) != 0x80)
{
goto invalid;
}
c = (((c<<18) & 0x1C0000) |
((in[1]<<12) & 0x3F000) |
((in[2]<<6) & 0xFC0) |
(in[3] & 0x3F));
if (c < 0x10000) {
// Overlong sequence, reject as invalid.
goto invalid;
}
in += 4;
inLen -= 4;
r.w32++;
break;
default:
invalid:
// if non-strict, simply treat as character
if (strict)
return false;
else
{
r.w8++;
in++;
inLen--;
}
}
}
return true;
}
// Analyze a UTF-16 string buffer and find out about the character widths.
// chars >= 0x10000 are encoded as 0xD800 + (upper 10 bits) and 0xDC00 + (lower 10 bits)
// Returns false in strict mode if a starting surrogate pair character is not followed
// by a correct 2nd pair character
static bool _analyzeUtf16(const wchar* p, int32_t len, StringWidths& r, bool strict)
{
r.ascii = r.w8 = r.w16 = r.w32 = 0;
while (len-- > 0)
{
wchar ch = *p++;
if (ch <= 0x7F)
r.ascii++;
if (ch <= 0xFF)
r.w8++;
else if (ch < 0xD800 || ch > 0xDBFF)
r.w16++;
else
{
if (len == 0 || *p < 0xDC00 || *p > 0xDFFF)
{
if (strict)
return false;
// found one or two characters depending on whether the 2nd surrpgate character was present
r.w16 += len ? 2 : 1;
}
else
r.w32++;
p++; len--;
}
}
return true;
}
// Create a string out of an UTF-8 buffer.
Stringp String::createUTF8
(AvmCore* core, const utf8_t* buffer, int32_t len, Width desiredWidth, bool staticBuf, bool strict)
{
if (buffer == NULL)
{
len = 0;
buffer = &k_zero.u8;
staticBuf = true;
}
if (len < 0)
len = Length((const char*)buffer);
// determine the string width to use
StringWidths widths;
if (!_analyzeUtf8(buffer, len, widths, strict))
{
// TODO: bad character sequence error
AvmAssert(!"bad UTF8 character sequence");
return NULL;
}
if (desiredWidth == kAuto)
{
if (widths.w32 != 0 || widths.w16 != 0)
desiredWidth = String::k16;
else
desiredWidth = String::k8;
}
if (desiredWidth == String::k8 && core->kEmptyString != NULL)
{
// core has been initialized, check for cached characters
if (len == 0)
return core->kEmptyString;
if (len == 1 && *buffer < 128)
return core->cachedChars[*buffer];
}
const bool is7bit = (widths.ascii == widths.w8) && !widths.w16 && !widths.w32;
Stringp s = NULL;
GC* gc = core->GetGC();
if (desiredWidth == k8)
{
if (widths.w16 != 0 || widths.w32 != 0)
// cannot do 8-bit string with this data
// TODO: string-too-wide error
s = NULL;
else if (staticBuf && is7bit)
{
// works, because we only have 7-bit ASCII
s = createStatic(gc, buffer, widths.w8, String::k8, is7bit);
}
else
{
s = createDynamic(gc, buffer, widths.w8, String::k8, is7bit);
uint32_t uch;
int32_t bytesRead;
Pointers dst(s); // NB, we assume that Utf8ToUcs4 doesn't allocate memory!
bool bugzilla609416 = core->currentBugCompatibility()->bugzilla609416;
while (len > 0)
{
if (*((int8_t*) buffer) > 0)
{
// ASCII
*dst.p8++ = char (*buffer++);
len--;
}
else
{
bytesRead = UnicodeUtils::Utf8ToUcs4(buffer, len, &uch, bugzilla609416);
if (bytesRead == 0)
{
// invalid sequence (only if strict was false)
*dst.p8++ = char (*buffer++);
len--;
}
else
{
buffer += bytesRead;
len -= bytesRead;
*dst.p8++ = char (uch);
}
}
}
}
}
else
{
// surrogate pairs need 2 characters
s = createDynamic(gc, NULL, int32AddChecked(int32AddChecked(widths.w8, widths.w16), int32AddChecked(widths.w32, widths.w32)), String::k16, is7bit);
int32_t const convertedlen = UnicodeUtils::Utf8ToUtf16(buffer, len, s->m_buffer.p16, s->m_length, strict);
// if this assert fires, _analyzeUtf8 didn't agree with UnicodeUtils::Utf8ToUtf16, which is bad.
AvmAssert(convertedlen == s->m_length);
if (convertedlen < 0)
return NULL;
}
VERIFY_7BIT(s);
return s;
}
// Create a string out of an UTF-16 buffer.
Stringp String::createUTF16
(AvmCore* core, const wchar* buffer, int32_t len, Width desiredWidth, bool staticBuf, bool strict)
{
if (buffer == NULL)
{
len = 0;
buffer = &k_zero.u16;
staticBuf = true;
}
if (len < 0)
len = Length(buffer);
bool is7bit = false;
int32_t stringLength = len;
if (desiredWidth != k16)
{
// determine the string width to use
StringWidths widths;
if (!_analyzeUtf16(buffer, len, widths, strict))
{
// TODO: bad character sequence error
return NULL;
}
if (desiredWidth == kAuto)
{
if (widths.w32 != 0 || widths.w16 != 0)
desiredWidth = k16;
else
desiredWidth = k8;
}
else if (desiredWidth == k8 && (widths.w16 != 0 || widths.w32 != 0))
// cannot do 8-bit string with this data
// TODO: string-too-wide error
return NULL;
is7bit = (widths.ascii == widths.w8) && !widths.w16 && !widths.w32;
}
if (desiredWidth == k8 && core->kEmptyString != NULL)
{
// core has been initialized, check for cached characters
if (len == 0)
return core->kEmptyString;
if (len == 1 && *buffer < 128)
return core->cachedChars[*buffer];
}
if (desiredWidth == k16 && staticBuf)
{
return createStatic(core->GetGC(), buffer, len, k16, is7bit);
}
// found the width to use, now create that string
Stringp s = createDynamic(core->GetGC(), NULL, stringLength, desiredWidth, is7bit);
String::Pointers ptrs(s);
if (desiredWidth == k8)
{
while (len-- > 0)
*ptrs.p8++ = (char) *buffer++;
}
else
{
VMPI_memcpy(ptrs.pv, buffer, len << desiredWidth);
}
VERIFY_7BIT(s);
return s;
}
PrintWriter& String::print(PrintWriter& prw) const
{
if (getWidth() == k8 && (m_bitsAndFlags & TSTR_7BIT_FLAG))
{
// fast form
Pointers ptrs(this);
prw.writeN( (const char*)ptrs.p8, m_length );
}
else
{
for (int32_t i=0, n=length(); i<n; i++)
{
prw << (wchar)charAt(i);
}
}
return prw;
}
///////////////////////////////////////////////////////////////////////////////////
// ---------------------------------------------------------------------------
// 0000 0000-0000 007F 0xxxxxxx
// 0000 0080-0000 07FF 110xxxxx 10xxxxxx
// 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
// 0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
// 0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
// 0400 0000-7FFF FFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
StUTF8String::StUTF8String(Stringp str)
{
if (!str || !str->length())
{
m_buffer = &k_zero.c8;
m_length = 0;
return;
}
VERIFY_7BIT(str);
int32_t len = 0;
MMgc::GC* gc = _gc(str);
if (str->getWidth() == String::k8)
{
len = str->m_length;
// if we know it's 7-bit, we don't need to walk it.
if (!(str->m_bitsAndFlags & String::TSTR_7BIT_FLAG))
{
const uint8_t* srcBuf = String::Pointers(str).p8;
int32_t auxchars = 0;
for (int32_t i = str->m_length; i--;)
auxchars += (*srcBuf++ > 127);
len = int32AddChecked(len, auxchars);
// no hi bits? set the bit!
if (len == str->m_length)
str->m_bitsAndFlags |= String::TSTR_7BIT_FLAG;
}
// Deliberately using gc'ed memory here (not mmfx, unmanaged memory)
// so that longjmp's past our dtor won't cause a long-term leak
char* dstBuf = (char*)gc->Alloc(uint32_t(len)+1, 0);
// can't re-use the Pointers from 7BIT above; gc->Alloc may have invalidated it
const uint8_t* srcBuf = String::Pointers(str).p8;
m_buffer = dstBuf;
m_length = len;
if (len == str->m_length)
{
VMPI_memcpy(dstBuf, srcBuf, len);
dstBuf[len] = 0;
}
else
{
for (int32_t i = str->m_length; i--;)
{
wchar ch = wchar(*srcBuf++);
if (ch >= 128)
{
*dstBuf++ = char(0xC0 + ((ch >> 6) & 0x3));
ch = 0x80 + (ch & 0x3F);
// fall thru
}
*dstBuf++ = char(ch);
}
AvmAssert(dstBuf - m_buffer == len);
*dstBuf = 0;
}
}
else
{
len = UnicodeUtils::Utf16ToUtf8(String::Pointers(str).p16, str->length(), NULL, 0);
if (len < 0)
len = 0;
char* dstBuf = (char*) gc->Alloc(uint32_t(len)+1, 0);
m_buffer = dstBuf;
m_length = len;
dstBuf[len] = 0;
// can't re-use the Pointers from Utf16ToUtf8 above; gc->Alloc may have invalidated it
UnicodeUtils::Utf16ToUtf8(String::Pointers(str).p16, str->length(), (uint8_t*) dstBuf, len);
}
}
StUTF8String::~StUTF8String()
{
if (m_buffer != &k_zero.c8) {
// StrUTF8String is only ever stack allocated so it's OK just to free m_buffer without NULLing it first.
GC::GetGC(m_buffer)->Free(m_buffer);
}
}
StUTF16String::StUTF16String(Stringp str)
{
if (!str || !str->length())
{
m_length = 0;
m_buffer = &k_zero.u16;
return;
}
VERIFY_7BIT(str);
MMgc::GC* gc = _gc(str);
m_length = str->m_length;
// Deliberately using gc'ed memory here (not mmfx, unmanaged memory)
// so that longjmp's past our dtor won't cause a long-term leak
wchar* dst = (wchar*) gc->Alloc(uint32ShlChecked(uint32_t(m_length)+1, String::k16), 0);
m_buffer = dst;
dst[m_length] = 0;
String::Pointers ptrs(str);
_copyBuffers(ptrs.pv, dst, m_length, str->getWidth(), String::k16);
}
StUTF16String::~StUTF16String()
{
if (m_buffer != &k_zero.u16) {
// StrUTF16String is only ever stack allocated so it's OK just to free m_buffer without NULLing it first.
GC::GetGC(m_buffer)->Free(m_buffer);
}
}
// The following table is the length of an UTF-8 sequence, indexed by the
// first character masked with 0x3F. It is assumed that the data is good
// UTF-8, which is true, since we just created it :) The table is only
// used for characters >= 0x80.
// 0000 0080-0000 07FF 000xxxxx
// 0000 0800-0000 FFFF 0010xxxx
// 0001 0000-001F FFFF 00110xxx
// 0020 0000-03FF FFFF 001110xx
// 0400 0000-7FFF FFFF 0011110x
static const uint8_t utf8Lengths[64] =
{
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6
};
StIndexableUTF8String::StIndexableUTF8String(Stringp s) :
StUTF8String(s),
m_lastPos(0),
m_lastUtf8Pos(0)
{
m_indexable = (s->length() == this->length()); // can't init in init-list, generates warning :-(
}
int32_t StIndexableUTF8String::toUtf8Index(int32_t pos)
{
if (pos <= 0 || pos >= length() || m_indexable)
return pos;
// optimization: these two members kick in if this method
// is called for positions in ascending order (which pcre usually does)
if (pos < m_lastPos)
m_lastPos = m_lastUtf8Pos = 0;
int32_t utf8Pos = m_lastUtf8Pos;
const utf8_t* p = (const utf8_t*) this->c_str() + m_lastUtf8Pos;
for (int32_t i = m_lastPos; i < pos; i++)
{
utf8_t ch = *p;
if (ch < 128)
p++, utf8Pos++; // ASCII
else
{
int32_t len = utf8Lengths[ch & 0x3F];
p += len;
utf8Pos += len;
}
}
m_lastPos = pos;
m_lastUtf8Pos = utf8Pos;
return utf8Pos;
}
int32_t StIndexableUTF8String::toIndex(int32_t utf8Pos)
{
if (utf8Pos <= 0 || m_indexable)
return utf8Pos;
// optimization: these two members kick in if this method
// is called for positions in ascending order (which pcre usually does)
if (utf8Pos < m_lastUtf8Pos)
m_lastPos = m_lastUtf8Pos = 0;
int32_t i = m_lastUtf8Pos;
int32_t pos = m_lastPos;
const utf8_t* p = (const utf8_t*) this->c_str() + m_lastUtf8Pos;
while (i < utf8Pos)
{
if (i >= length())
break;
utf8_t ch = *p;
if (!(ch & 0x80))
i++, p++; // ASCII
else
{
int32_t len = utf8Lengths[ch & 0x3F];
i += len;
p += len;
}
pos++;
}
m_lastPos = pos;
m_lastUtf8Pos = utf8Pos;
return pos;
}
} // end namespace avmplus