X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/9dae56ea45a0f5f8136a5c93d6f3a7f99399ca73..fb8617cde5834786bd4e4afd579883e4acf5666e:/runtime/UString.cpp diff --git a/runtime/UString.cpp b/runtime/UString.cpp index c7777b8..54eac6a 100644 --- a/runtime/UString.cpp +++ b/runtime/UString.cpp @@ -1,8 +1,8 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) - * Copyright (c) 2009, Google Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -30,20 +30,20 @@ #include "Identifier.h" #include "Operations.h" #include -#include #include +#include #include #include #include +#include #include #include #include +#include #include #include +#include -#if HAVE(STRING_H) -#include -#endif #if HAVE(STRINGS_H) #include #endif @@ -52,49 +52,11 @@ using namespace WTF; using namespace WTF::Unicode; using namespace std; -// This can be tuned differently per platform by putting platform #ifs right here. -// If you don't define this macro at all, then copyChars will just call directly -// to memcpy. -#define USTRING_COPY_CHARS_INLINE_CUTOFF 20 - namespace JSC { extern const double NaN; extern const double Inf; -static inline size_t overflowIndicator() { return std::numeric_limits::max(); } -static inline size_t maxUChars() { return std::numeric_limits::max() / sizeof(UChar); } - -static inline UChar* allocChars(size_t length) -{ - ASSERT(length); - if (length > maxUChars()) - return 0; - return static_cast(tryFastMalloc(sizeof(UChar) * length)); -} - -static inline UChar* reallocChars(UChar* buffer, size_t length) -{ - ASSERT(length); - if (length > maxUChars()) - return 0; - return static_cast(tryFastRealloc(buffer, sizeof(UChar) * length)); -} - -static inline void copyChars(UChar* destination, const UChar* source, unsigned numCharacters) -{ -#ifdef USTRING_COPY_CHARS_INLINE_CUTOFF - if (numCharacters <= USTRING_COPY_CHARS_INLINE_CUTOFF) { - for (unsigned i = 0; i < numCharacters; ++i) - destination[i] = source[i]; - return; - } -#endif - memcpy(destination, source, numCharacters * sizeof(UChar)); -} - -COMPILE_ASSERT(sizeof(UChar) == 2, uchar_is_2_bytes) - CString::CString(const char* c) : m_length(strlen(c)) , m_data(new char[m_length + 1]) @@ -187,333 +149,52 @@ bool operator==(const CString& c1, const CString& c2) // These static strings are immutable, except for rc, whose initial value is chosen to // reduce the possibility of it becoming zero due to ref/deref not being thread-safe. static UChar sharedEmptyChar; -UString::BaseString* UString::Rep::nullBaseString; -UString::BaseString* UString::Rep::emptyBaseString; +UStringImpl* UStringImpl::s_null; +UStringImpl* UStringImpl::s_empty; UString* UString::nullUString; -static void initializeStaticBaseString(int len, UChar* buf, UString::BaseString& base) -{ - base.offset = 0; - base.len = len; - base.rc = INT_MAX / 2; - base._hash = 0; - base.m_identifierTableAndFlags.setFlag(UString::Rep::StaticFlag); - base.m_baseString = 0; - base.buf = buf; - base.preCapacity = 0; - base.usedPreCapacity = 0; - base.capacity = 0; - base.usedCapacity = 0; - base.reportedCost = 0; - base.checkConsistency(); -} - void initializeUString() { - UString::Rep::nullBaseString = new UString::BaseString; - initializeStaticBaseString(0, 0, *UString::Rep::nullBaseString); - - UString::Rep::emptyBaseString = new UString::BaseString; - initializeStaticBaseString(0, &sharedEmptyChar, *UString::Rep::emptyBaseString); - + UStringImpl::s_null = new UStringImpl(0, 0, UStringImpl::ConstructStaticString); + UStringImpl::s_empty = new UStringImpl(&sharedEmptyChar, 0, UStringImpl::ConstructStaticString); UString::nullUString = new UString; } -static char* statBuffer = 0; // Only used for debugging via UString::ascii(). - -PassRefPtr UString::Rep::createCopying(const UChar* d, int l) -{ - UChar* copyD = static_cast(fastMalloc(l * sizeof(UChar))); - copyChars(copyD, d, l); - return create(copyD, l); -} - -PassRefPtr UString::Rep::create(UChar* d, int l) -{ - BaseString* r = new BaseString; - r->offset = 0; - r->len = l; - r->rc = 1; - r->_hash = 0; - r->m_baseString = 0; - r->reportedCost = 0; - r->buf = d; - r->usedCapacity = l; - r->capacity = l; - r->usedPreCapacity = 0; - r->preCapacity = 0; - - r->checkConsistency(); - - // steal the single reference this Rep was created with - return adoptRef(r); -} - -PassRefPtr UString::Rep::create(PassRefPtr rep, int offset, int length) -{ - ASSERT(rep); - rep->checkConsistency(); - - int repOffset = rep->offset; - - PassRefPtr base = rep->baseString(); - - ASSERT(-(offset + repOffset) <= base->usedPreCapacity); - ASSERT(offset + repOffset + length <= base->usedCapacity); - - Rep* r = new Rep; - r->offset = repOffset + offset; - r->len = length; - r->rc = 1; - r->_hash = 0; - r->setBaseString(base); - - r->checkConsistency(); - - // steal the single reference this Rep was created with - return adoptRef(r); -} - -PassRefPtr UString::Rep::createFromUTF8(const char* string) +static PassRefPtr createRep(const char* c) { - if (!string) - return &UString::Rep::null(); - - size_t length = strlen(string); - Vector buffer(length); - UChar* p = buffer.data(); - if (conversionOK != convertUTF8ToUTF16(&string, string + length, &p, p + length)) + if (!c) return &UString::Rep::null(); - return UString::Rep::createCopying(buffer.data(), p - buffer.data()); -} - -void UString::Rep::destroy() -{ - checkConsistency(); - - // Static null and empty strings can never be destroyed, but we cannot rely on - // reference counting, because ref/deref are not thread-safe. - if (!isStatic()) { - if (identifierTable()) - Identifier::remove(this); - UString::BaseString* base = baseString(); - if (base == this) - fastFree(base->buf); - else - base->deref(); - - delete this; - } -} - -// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's -// or anything like that. -const unsigned PHI = 0x9e3779b9U; - -// Paul Hsieh's SuperFastHash -// http://www.azillionmonkeys.com/qed/hash.html -unsigned UString::Rep::computeHash(const UChar* s, int len) -{ - unsigned l = len; - uint32_t hash = PHI; - uint32_t tmp; - - int rem = l & 1; - l >>= 1; - - // Main loop - for (; l > 0; l--) { - hash += s[0]; - tmp = (s[1] << 11) ^ hash; - hash = (hash << 16) ^ tmp; - s += 2; - hash += hash >> 11; - } - - // Handle end case - if (rem) { - hash += s[0]; - hash ^= hash << 11; - hash += hash >> 17; - } - - // Force "avalanching" of final 127 bits - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 2; - hash += hash >> 15; - hash ^= hash << 10; - - // this avoids ever returning a hash code of 0, since that is used to - // signal "hash not computed yet", using a value that is likely to be - // effectively the same as 0 when the low bits are masked - if (hash == 0) - hash = 0x80000000; - - return hash; -} - -// Paul Hsieh's SuperFastHash -// http://www.azillionmonkeys.com/qed/hash.html -unsigned UString::Rep::computeHash(const char* s, int l) -{ - // This hash is designed to work on 16-bit chunks at a time. But since the normal case - // (above) is to hash UTF-16 characters, we just treat the 8-bit chars as if they - // were 16-bit chunks, which should give matching results - - uint32_t hash = PHI; - uint32_t tmp; - - size_t rem = l & 1; - l >>= 1; - - // Main loop - for (; l > 0; l--) { - hash += static_cast(s[0]); - tmp = (static_cast(s[1]) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - s += 2; - hash += hash >> 11; - } - - // Handle end case - if (rem) { - hash += static_cast(s[0]); - hash ^= hash << 11; - hash += hash >> 17; - } - - // Force "avalanching" of final 127 bits - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 2; - hash += hash >> 15; - hash ^= hash << 10; - - // this avoids ever returning a hash code of 0, since that is used to - // signal "hash not computed yet", using a value that is likely to be - // effectively the same as 0 when the low bits are masked - if (hash == 0) - hash = 0x80000000; - - return hash; -} - -#ifndef NDEBUG -void UString::Rep::checkConsistency() const -{ - const UString::BaseString* base = baseString(); - - // There is no recursion for base strings. - ASSERT(base == base->baseString()); - - if (isStatic()) { - // There are only two static strings: null and empty. - ASSERT(!len); - - // Static strings cannot get in identifier tables, because they are globally shared. - ASSERT(!identifierTable()); - } - - // The string fits in buffer. - ASSERT(base->usedPreCapacity <= base->preCapacity); - ASSERT(base->usedCapacity <= base->capacity); - ASSERT(-offset <= base->usedPreCapacity); - ASSERT(offset + len <= base->usedCapacity); -} -#endif - -// put these early so they can be inlined -static inline size_t expandedSize(size_t size, size_t otherSize) -{ - // Do the size calculation in two parts, returning overflowIndicator if - // we overflow the maximum value that we can handle. - - if (size > maxUChars()) - return overflowIndicator(); - - size_t expandedSize = ((size + 10) / 10 * 11) + 1; - if (maxUChars() - expandedSize < otherSize) - return overflowIndicator(); - - return expandedSize + otherSize; -} - -static inline bool expandCapacity(UString::Rep* rep, int requiredLength) -{ - rep->checkConsistency(); - - UString::BaseString* base = rep->baseString(); - - if (requiredLength > base->capacity) { - size_t newCapacity = expandedSize(requiredLength, base->preCapacity); - UChar* oldBuf = base->buf; - base->buf = reallocChars(base->buf, newCapacity); - if (!base->buf) { - base->buf = oldBuf; - return false; - } - base->capacity = newCapacity - base->preCapacity; - } - if (requiredLength > base->usedCapacity) - base->usedCapacity = requiredLength; - - rep->checkConsistency(); - return true; -} - -void UString::expandCapacity(int requiredLength) -{ - if (!JSC::expandCapacity(m_rep.get(), requiredLength)) - makeNull(); -} - -void UString::expandPreCapacity(int requiredPreCap) -{ - m_rep->checkConsistency(); - - BaseString* base = m_rep->baseString(); - - if (requiredPreCap > base->preCapacity) { - size_t newCapacity = expandedSize(requiredPreCap, base->capacity); - int delta = newCapacity - base->capacity - base->preCapacity; - - UChar* newBuf = allocChars(newCapacity); - if (!newBuf) { - makeNull(); - return; - } - copyChars(newBuf + delta, base->buf, base->capacity + base->preCapacity); - fastFree(base->buf); - base->buf = newBuf; + if (!c[0]) + return &UString::Rep::empty(); - base->preCapacity = newCapacity - base->capacity; - } - if (requiredPreCap > base->usedPreCapacity) - base->usedPreCapacity = requiredPreCap; + size_t length = strlen(c); + UChar* d; + PassRefPtr result = UStringImpl::tryCreateUninitialized(length, d); + if (!result) + return &UString::Rep::null(); - m_rep->checkConsistency(); + for (size_t i = 0; i < length; i++) + d[i] = static_cast(c[i]); // use unsigned char to zero-extend instead of sign-extend + return result; } -static PassRefPtr createRep(const char* c) +static inline PassRefPtr createRep(const char* c, int length) { if (!c) return &UString::Rep::null(); - if (!c[0]) + if (!length) return &UString::Rep::empty(); - size_t length = strlen(c); - UChar* d = allocChars(length); - if (!d) + UChar* d; + PassRefPtr result = UStringImpl::tryCreateUninitialized(length, d); + if (!result) return &UString::Rep::null(); - else { - for (size_t i = 0; i < length; i++) - d[i] = static_cast(c[i]); // use unsigned char to zero-extend instead of sign-extend - return UString::Rep::create(d, static_cast(length)); - } + for (int i = 0; i < length; i++) + d[i] = static_cast(c[i]); // use unsigned char to zero-extend instead of sign-extend + return result; } UString::UString(const char* c) @@ -521,239 +202,34 @@ UString::UString(const char* c) { } -UString::UString(const UChar* c, int length) +UString::UString(const char* c, int length) + : m_rep(createRep(c, length)) { - if (length == 0) - m_rep = &Rep::empty(); - else - m_rep = Rep::createCopying(c, length); } -UString::UString(UChar* c, int length, bool copy) +UString::UString(const UChar* c, int length) { - if (length == 0) + if (length == 0) m_rep = &Rep::empty(); - else if (copy) - m_rep = Rep::createCopying(c, length); else m_rep = Rep::create(c, length); } -UString::UString(const Vector& buffer) +UString UString::createFromUTF8(const char* string) { - if (!buffer.size()) - m_rep = &Rep::empty(); - else - m_rep = Rep::createCopying(buffer.data(), buffer.size()); -} - -static ALWAYS_INLINE int newCapacityWithOverflowCheck(const int currentCapacity, const int extendLength, const bool plusOne = false) -{ - ASSERT_WITH_MESSAGE(extendLength >= 0, "extendedLength = %d", extendLength); - - const int plusLength = plusOne ? 1 : 0; - if (currentCapacity > std::numeric_limits::max() - extendLength - plusLength) - CRASH(); - - return currentCapacity + extendLength + plusLength; -} - -static ALWAYS_INLINE PassRefPtr concatenate(PassRefPtr r, const UChar* tData, int tSize) -{ - RefPtr rep = r; - - rep->checkConsistency(); - - int thisSize = rep->size(); - int thisOffset = rep->offset; - int length = thisSize + tSize; - UString::BaseString* base = rep->baseString(); - - // possible cases: - if (tSize == 0) { - // t is empty - } else if (thisSize == 0) { - // this is empty - rep = UString::Rep::createCopying(tData, tSize); - } else if (rep == base && rep->rc == 1) { - // this is direct and has refcount of 1 (so we can just alter it directly) - if (!expandCapacity(rep.get(), newCapacityWithOverflowCheck(thisOffset, length))) - rep = &UString::Rep::null(); - if (rep->data()) { - copyChars(rep->data() + thisSize, tData, tSize); - rep->len = length; - rep->_hash = 0; - } - } else if (thisOffset + thisSize == base->usedCapacity && thisSize >= minShareSize) { - // this reaches the end of the buffer - extend it if it's long enough to append to - if (!expandCapacity(rep.get(), newCapacityWithOverflowCheck(thisOffset, length))) - rep = &UString::Rep::null(); - if (rep->data()) { - copyChars(rep->data() + thisSize, tData, tSize); - rep = UString::Rep::create(rep, 0, length); - } - } else { - // this is shared with someone using more capacity, gotta make a whole new string - size_t newCapacity = expandedSize(length, 0); - UChar* d = allocChars(newCapacity); - if (!d) - rep = &UString::Rep::null(); - else { - copyChars(d, rep->data(), thisSize); - copyChars(d + thisSize, tData, tSize); - rep = UString::Rep::create(d, length); - rep->baseString()->capacity = newCapacity; - } - } - - rep->checkConsistency(); - - return rep.release(); -} - -static ALWAYS_INLINE PassRefPtr concatenate(PassRefPtr r, const char* t) -{ - RefPtr rep = r; - - rep->checkConsistency(); - - int thisSize = rep->size(); - int thisOffset = rep->offset; - int tSize = static_cast(strlen(t)); - int length = thisSize + tSize; - UString::BaseString* base = rep->baseString(); - - // possible cases: - if (thisSize == 0) { - // this is empty - rep = createRep(t); - } else if (tSize == 0) { - // t is empty, we'll just return *this below. - } else if (rep == base && rep->rc == 1) { - // this is direct and has refcount of 1 (so we can just alter it directly) - expandCapacity(rep.get(), newCapacityWithOverflowCheck(thisOffset, length)); - UChar* d = rep->data(); - if (d) { - for (int i = 0; i < tSize; ++i) - d[thisSize + i] = static_cast(t[i]); // use unsigned char to zero-extend instead of sign-extend - rep->len = length; - rep->_hash = 0; - } - } else if (thisOffset + thisSize == base->usedCapacity && thisSize >= minShareSize) { - // this string reaches the end of the buffer - extend it - expandCapacity(rep.get(), newCapacityWithOverflowCheck(thisOffset, length)); - UChar* d = rep->data(); - if (d) { - for (int i = 0; i < tSize; ++i) - d[thisSize + i] = static_cast(t[i]); // use unsigned char to zero-extend instead of sign-extend - rep = UString::Rep::create(rep, 0, length); - } - } else { - // this is shared with someone using more capacity, gotta make a whole new string - size_t newCapacity = expandedSize(length, 0); - UChar* d = allocChars(newCapacity); - if (!d) - rep = &UString::Rep::null(); - else { - copyChars(d, rep->data(), thisSize); - for (int i = 0; i < tSize; ++i) - d[thisSize + i] = static_cast(t[i]); // use unsigned char to zero-extend instead of sign-extend - rep = UString::Rep::create(d, length); - rep->baseString()->capacity = newCapacity; - } - } - - rep->checkConsistency(); - - return rep.release(); -} - -PassRefPtr concatenate(UString::Rep* a, UString::Rep* b) -{ - a->checkConsistency(); - b->checkConsistency(); - - int aSize = a->size(); - int aOffset = a->offset; - int bSize = b->size(); - int bOffset = b->offset; - int length = aSize + bSize; - - // possible cases: - - // a is empty - if (aSize == 0) - return b; - // b is empty - if (bSize == 0) - return a; - - UString::BaseString* aBase = a->baseString(); - if (bSize == 1 && aOffset + aSize == aBase->usedCapacity && aOffset + length <= aBase->capacity) { - // b is a single character (common fast case) - aBase->usedCapacity = aOffset + length; - a->data()[aSize] = b->data()[0]; - return UString::Rep::create(a, 0, length); - } - - UString::BaseString* bBase = b->baseString(); - if (aOffset + aSize == aBase->usedCapacity && aSize >= minShareSize && 4 * aSize >= bSize - && (-bOffset != bBase->usedPreCapacity || aSize >= bSize)) { - // - a reaches the end of its buffer so it qualifies for shared append - // - also, it's at least a quarter the length of b - appending to a much shorter - // string does more harm than good - // - however, if b qualifies for prepend and is longer than a, we'd rather prepend - - UString x(a); - x.expandCapacity(newCapacityWithOverflowCheck(aOffset, length)); - if (!a->data() || !x.data()) - return 0; - copyChars(a->data() + aSize, b->data(), bSize); - PassRefPtr result = UString::Rep::create(a, 0, length); - - a->checkConsistency(); - b->checkConsistency(); - result->checkConsistency(); - - return result; - } - - if (-bOffset == bBase->usedPreCapacity && bSize >= minShareSize && 4 * bSize >= aSize) { - // - b reaches the beginning of its buffer so it qualifies for shared prepend - // - also, it's at least a quarter the length of a - prepending to a much shorter - // string does more harm than good - UString y(b); - y.expandPreCapacity(-bOffset + aSize); - if (!b->data() || !y.data()) - return 0; - copyChars(b->data() - aSize, a->data(), aSize); - PassRefPtr result = UString::Rep::create(b, -aSize, length); - - a->checkConsistency(); - b->checkConsistency(); - result->checkConsistency(); - - return result; - } - - // a does not qualify for append, and b does not qualify for prepend, gotta make a whole new string - size_t newCapacity = expandedSize(length, 0); - UChar* d = allocChars(newCapacity); - if (!d) - return 0; - copyChars(d, a->data(), aSize); - copyChars(d + aSize, b->data(), bSize); - PassRefPtr result = UString::Rep::create(d, length); - result->baseString()->capacity = newCapacity; + if (!string) + return null(); - a->checkConsistency(); - b->checkConsistency(); - result->checkConsistency(); + size_t length = strlen(string); + Vector buffer(length); + UChar* p = buffer.data(); + if (conversionOK != convertUTF8ToUTF16(&string, string + length, &p, p + length)) + return null(); - return result; + return UString(buffer.data(), p - buffer.data()); } -PassRefPtr concatenate(UString::Rep* rep, int i) +UString UString::from(int i) { UChar buf[1 + sizeof(i) * 3]; UChar* end = buf + sizeof(buf) / sizeof(UChar); @@ -763,8 +239,8 @@ PassRefPtr concatenate(UString::Rep* rep, int i) *--p = '0'; else if (i == INT_MIN) { char minBuf[1 + sizeof(i) * 3]; - sprintf(minBuf, "%d", INT_MIN); - return concatenate(rep, minBuf); + snprintf(minBuf, sizeof(minBuf), "%d", INT_MIN); + return UString(minBuf); } else { bool negative = false; if (i < 0) { @@ -779,90 +255,26 @@ PassRefPtr concatenate(UString::Rep* rep, int i) *--p = '-'; } - return concatenate(rep, p, static_cast(end - p)); - -} - -PassRefPtr concatenate(UString::Rep* rep, double d) -{ - // avoid ever printing -NaN, in JS conceptually there is only one NaN value - if (isnan(d)) - return concatenate(rep, "NaN"); - - if (d == 0.0) // stringify -0 as 0 - d = 0.0; - - char buf[80]; - int decimalPoint; - int sign; - - char* result = WTF::dtoa(d, 0, &decimalPoint, &sign, NULL); - int length = static_cast(strlen(result)); - - int i = 0; - if (sign) - buf[i++] = '-'; - - if (decimalPoint <= 0 && decimalPoint > -6) { - buf[i++] = '0'; - buf[i++] = '.'; - for (int j = decimalPoint; j < 0; j++) - buf[i++] = '0'; - strcpy(buf + i, result); - } else if (decimalPoint <= 21 && decimalPoint > 0) { - if (length <= decimalPoint) { - strcpy(buf + i, result); - i += length; - for (int j = 0; j < decimalPoint - length; j++) - buf[i++] = '0'; - buf[i] = '\0'; - } else { - strncpy(buf + i, result, decimalPoint); - i += decimalPoint; - buf[i++] = '.'; - strcpy(buf + i, result + decimalPoint); - } - } else if (result[0] < '0' || result[0] > '9') - strcpy(buf + i, result); - else { - buf[i++] = result[0]; - if (length > 1) { - buf[i++] = '.'; - strcpy(buf + i, result + 1); - i += length - 1; - } - - buf[i++] = 'e'; - buf[i++] = (decimalPoint >= 0) ? '+' : '-'; - // decimalPoint can't be more than 3 digits decimal given the - // nature of float representation - int exponential = decimalPoint - 1; - if (exponential < 0) - exponential = -exponential; - if (exponential >= 100) - buf[i++] = static_cast('0' + exponential / 100); - if (exponential >= 10) - buf[i++] = static_cast('0' + (exponential % 100) / 10); - buf[i++] = static_cast('0' + exponential % 10); - buf[i++] = '\0'; - } - - WTF::freedtoa(result); - - return concatenate(rep, buf); + return UString(p, static_cast(end - p)); } -UString UString::from(int i) +UString UString::from(long long i) { UChar buf[1 + sizeof(i) * 3]; UChar* end = buf + sizeof(buf) / sizeof(UChar); UChar* p = end; - + if (i == 0) *--p = '0'; - else if (i == INT_MIN) { + else if (i == std::numeric_limits::min()) { char minBuf[1 + sizeof(i) * 3]; - snprintf(minBuf, 1 + sizeof(i) * 3, "%d", INT_MIN); +#if OS(WINDOWS) + snprintf(minBuf, sizeof(minBuf) - 1, "%I64d", std::numeric_limits::min()); +#elif PLATFORM(IPHONE) + snprintf(minBuf, sizeof(minBuf), "%lld", std::numeric_limits::min()); +#else + snprintf(minBuf, sizeof(minBuf) - 1, "%lld", std::numeric_limits::min()); +#endif return UString(minBuf); } else { bool negative = false; @@ -909,7 +321,7 @@ UString UString::from(long l) *--p = '0'; else if (l == LONG_MIN) { char minBuf[1 + sizeof(l) * 3]; - snprintf(minBuf, 1 + sizeof(l) * 3, "%ld", LONG_MIN); + snprintf(minBuf, sizeof(minBuf), "%ld", LONG_MIN); return UString(minBuf); } else { bool negative = false; @@ -930,70 +342,10 @@ UString UString::from(long l) UString UString::from(double d) { - // avoid ever printing -NaN, in JS conceptually there is only one NaN value - if (isnan(d)) - return "NaN"; - - char buf[80]; - int decimalPoint; - int sign; - - char* result = WTF::dtoa(d, 0, &decimalPoint, &sign, NULL); - int length = static_cast(strlen(result)); - - int i = 0; - if (sign) - buf[i++] = '-'; - - if (decimalPoint <= 0 && decimalPoint > -6) { - buf[i++] = '0'; - buf[i++] = '.'; - for (int j = decimalPoint; j < 0; j++) - buf[i++] = '0'; - strlcpy(buf + i, result, sizeof(buf) - i); - } else if (decimalPoint <= 21 && decimalPoint > 0) { - if (length <= decimalPoint) { - strlcpy(buf + i, result, sizeof(buf) - i); - i += length; - for (int j = 0; j < decimalPoint - length; j++) - buf[i++] = '0'; - buf[i] = '\0'; - } else { - int len = (decimalPoint <= static_cast(sizeof(buf)) - i ? decimalPoint : sizeof(buf) - i); - strncpy(buf + i, result, len); - i += len; - buf[i++] = '.'; - strlcpy(buf + i, result + decimalPoint, sizeof(buf) - i); - } - } else if (result[0] < '0' || result[0] > '9') - strlcpy(buf + i, result, sizeof(buf) - i); - else { - buf[i++] = result[0]; - if (length > 1) { - buf[i++] = '.'; - strlcpy(buf + i, result + 1, sizeof(buf) - i); - i += length - 1; - } - - buf[i++] = 'e'; - buf[i++] = (decimalPoint >= 0) ? '+' : '-'; - // decimalPoint can't be more than 3 digits decimal given the - // nature of float representation - int exponential = decimalPoint - 1; - if (exponential < 0) - exponential = -exponential; - if (exponential >= 100) - buf[i++] = static_cast('0' + exponential / 100); - if (exponential >= 10) - buf[i++] = static_cast('0' + (exponential % 100) / 10); - buf[i++] = static_cast('0' + exponential % 10); - buf[i++] = '\0'; - ASSERT(i <= static_cast(sizeof(buf))); - } - - WTF::freedtoa(result); - - return UString(buf); + DtoaBuffer buffer; + unsigned length; + doubleToStringInJavaScriptFormat(d, buffer, &length); + return UString(buffer, length); } UString UString::spliceSubstringsWithSeparators(const Range* substringRanges, int rangeCount, const UString* separators, int separatorCount) const @@ -1018,144 +370,47 @@ UString UString::spliceSubstringsWithSeparators(const Range* substringRanges, in if (totalLength == 0) return ""; - UChar* buffer = allocChars(totalLength); - if (!buffer) + UChar* buffer; + PassRefPtr rep = Rep::tryCreateUninitialized(totalLength, buffer); + if (!rep) return null(); int maxCount = max(rangeCount, separatorCount); int bufferPos = 0; for (int i = 0; i < maxCount; i++) { if (i < rangeCount) { - copyChars(buffer + bufferPos, data() + substringRanges[i].position, substringRanges[i].length); + UStringImpl::copyChars(buffer + bufferPos, data() + substringRanges[i].position, substringRanges[i].length); bufferPos += substringRanges[i].length; } if (i < separatorCount) { - copyChars(buffer + bufferPos, separators[i].data(), separators[i].size()); + UStringImpl::copyChars(buffer + bufferPos, separators[i].data(), separators[i].size()); bufferPos += separators[i].size(); } } - return UString::Rep::create(buffer, totalLength); + return rep; } -UString& UString::append(const UString &t) +UString UString::replaceRange(int rangeStart, int rangeLength, const UString& replacement) const { m_rep->checkConsistency(); - t.rep()->checkConsistency(); - - int thisSize = size(); - int thisOffset = m_rep->offset; - int tSize = t.size(); - int length = thisSize + tSize; - BaseString* base = m_rep->baseString(); - - // possible cases: - if (thisSize == 0) { - // this is empty - *this = t; - } else if (tSize == 0) { - // t is empty - } else if (m_rep == base && m_rep->rc == 1) { - // this is direct and has refcount of 1 (so we can just alter it directly) - expandCapacity(newCapacityWithOverflowCheck(thisOffset, length)); - if (data()) { - copyChars(m_rep->data() + thisSize, t.data(), tSize); - m_rep->len = length; - m_rep->_hash = 0; - } - } else if (thisOffset + thisSize == base->usedCapacity && thisSize >= minShareSize) { - // this reaches the end of the buffer - extend it if it's long enough to append to - expandCapacity(newCapacityWithOverflowCheck(thisOffset, length)); - if (data()) { - copyChars(m_rep->data() + thisSize, t.data(), tSize); - m_rep = Rep::create(m_rep, 0, length); - } - } else { - // this is shared with someone using more capacity, gotta make a whole new string - size_t newCapacity = expandedSize(length, 0); - UChar* d = allocChars(newCapacity); - if (!d) - makeNull(); - else { - copyChars(d, data(), thisSize); - copyChars(d + thisSize, t.data(), tSize); - m_rep = Rep::create(d, length); - m_rep->baseString()->capacity = newCapacity; - } - } - - m_rep->checkConsistency(); - t.rep()->checkConsistency(); - - return *this; -} - -UString& UString::append(const UChar* tData, int tSize) -{ - m_rep = concatenate(m_rep.release(), tData, tSize); - return *this; -} -UString& UString::append(const char* t) -{ - m_rep = concatenate(m_rep.release(), t); - return *this; -} + int replacementLength = replacement.size(); + int totalLength = size() - rangeLength + replacementLength; + if (totalLength == 0) + return ""; -UString& UString::append(UChar c) -{ - m_rep->checkConsistency(); + UChar* buffer; + PassRefPtr rep = Rep::tryCreateUninitialized(totalLength, buffer); + if (!rep) + return null(); - int thisOffset = m_rep->offset; - int length = size(); - BaseString* base = m_rep->baseString(); - - // possible cases: - if (length == 0) { - // this is empty - must make a new m_rep because we don't want to pollute the shared empty one - size_t newCapacity = expandedSize(1, 0); - UChar* d = allocChars(newCapacity); - if (!d) - makeNull(); - else { - d[0] = c; - m_rep = Rep::create(d, 1); - m_rep->baseString()->capacity = newCapacity; - } - } else if (m_rep == base && m_rep->rc == 1) { - // this is direct and has refcount of 1 (so we can just alter it directly) - expandCapacity(newCapacityWithOverflowCheck(thisOffset, length, true)); - UChar* d = m_rep->data(); - if (d) { - d[length] = c; - m_rep->len = length + 1; - m_rep->_hash = 0; - } - } else if (thisOffset + length == base->usedCapacity && length >= minShareSize) { - // this reaches the end of the string - extend it and share - expandCapacity(newCapacityWithOverflowCheck(thisOffset, length, true)); - UChar* d = m_rep->data(); - if (d) { - d[length] = c; - m_rep = Rep::create(m_rep, 0, length + 1); - } - } else { - // this is shared with someone using more capacity, gotta make a whole new string - size_t newCapacity = expandedSize(length + 1, 0); - UChar* d = allocChars(newCapacity); - if (!d) - makeNull(); - else { - copyChars(d, data(), length); - d[length] = c; - m_rep = Rep::create(d, length + 1); - m_rep->baseString()->capacity = newCapacity; - } - } + UStringImpl::copyChars(buffer, data(), rangeStart); + UStringImpl::copyChars(buffer + rangeStart, replacement.data(), replacementLength); + int rangeEnd = rangeStart + rangeLength; + UStringImpl::copyChars(buffer + rangeStart + replacementLength, data() + rangeEnd, size() - rangeEnd); - m_rep->checkConsistency(); - - return *this; + return rep; } bool UString::getCString(CStringBuffer& buffer) const @@ -1183,13 +438,15 @@ bool UString::getCString(CStringBuffer& buffer) const char* UString::ascii() const { + static char* asciiBuffer = 0; + int length = size(); int neededSize = length + 1; - delete[] statBuffer; - statBuffer = new char[neededSize]; + delete[] asciiBuffer; + asciiBuffer = new char[neededSize]; const UChar* p = data(); - char* q = statBuffer; + char* q = asciiBuffer; const UChar* limit = p + length; while (p != limit) { *q = static_cast(p[0]); @@ -1198,7 +455,7 @@ char* UString::ascii() const } *q = '\0'; - return statBuffer; + return asciiBuffer; } UString& UString::operator=(const char* c) @@ -1214,22 +471,13 @@ UString& UString::operator=(const char* c) } int l = static_cast(strlen(c)); - UChar* d; - BaseString* base = m_rep->baseString(); - if (m_rep->rc == 1 && l <= base->capacity && m_rep == base && m_rep->offset == 0 && base->preCapacity == 0) { - d = base->buf; - m_rep->_hash = 0; - m_rep->len = l; - } else { - d = allocChars(l); - if (!d) { - makeNull(); - return *this; - } - m_rep = Rep::create(d, l); - } - for (int i = 0; i < l; i++) - d[i] = static_cast(c[i]); // use unsigned char to zero-extend instead of sign-extend + UChar* d = 0; + m_rep = Rep::tryCreateUninitialized(l, d); + if (m_rep) { + for (int i = 0; i < l; i++) + d[i] = static_cast(c[i]); // use unsigned char to zero-extend instead of sign-extend + } else + makeNull(); return *this; } @@ -1387,7 +635,7 @@ uint32_t UString::toStrictUInt32(bool* ok) const *ok = false; // Empty string is not OK. - int len = m_rep->len; + int len = m_rep->size(); if (len == 0) return 0; const UChar* p = m_rep->data(); @@ -1534,19 +782,6 @@ UString UString::substr(int pos, int len) const return UString(Rep::create(m_rep, pos, len)); } -bool operator==(const UString& s1, const UString& s2) -{ - int size = s1.size(); - switch (size) { - case 0: - return !s2.size(); - case 1: - return s2.size() == 1 && s1.data()[0] == s2.data()[0]; - default: - return s2.size() == size && memcmp(s1.data(), s2.data(), size * sizeof(UChar)) == 0; - } -} - bool operator==(const UString& s1, const char *s2) { if (s2 == 0) @@ -1627,8 +862,8 @@ int compare(const UString& s1, const UString& s2) bool equal(const UString::Rep* r, const UString::Rep* b) { - int length = r->len; - if (length != b->len) + int length = r->size(); + if (length != b->size()) return false; const UChar* d = r->data(); const UChar* s = b->data();