source: webkit/trunk/JavaScriptCore/runtime/UString.cpp@ 43339

Last change on this file since 43339 was 43339, checked in by [email protected], 16 years ago

2009-05-07 Gavin Barraclough <[email protected]>

Reviewed by NOBODY (OOPS!).

Enable op_strcat across += assignments. This patch allows the lhs of a read/modify node
to be included within the concatenation operation, and also modifies the implementation
of the concatenation to attempt to reuse and cat onto the leftmost string, rather than
always allocating a new empty output string to copy into (as was previously the behaviour).

~0.5% progression, due to a 3%-3.5% progression on the string tests (particularly validate).

  • parser/Nodes.cpp: (JSC::BinaryOpNode::emitStrcat): (JSC::emitReadModifyAssignment): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::ReadModifyDotNode::emitBytecode): (JSC::ReadModifyBracketNode::emitBytecode):
  • parser/Nodes.h:
  • runtime/Operations.h: (JSC::concatenateStrings):
  • runtime/UString.cpp: (JSC::UString::reserveCapacity):
  • runtime/UString.h:
  • Property svn:eol-style set to native
File size: 45.5 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Copyright (C) 2007 Cameron Zwarich ([email protected])
5 * Copyright (c) 2009, Google Inc. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24#include "config.h"
25#include "UString.h"
26
27#include "JSGlobalObjectFunctions.h"
28#include "Collector.h"
29#include "dtoa.h"
30#include "Identifier.h"
31#include "Operations.h"
32#include <ctype.h>
33#include <float.h>
34#include <limits.h>
35#include <math.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <wtf/ASCIICType.h>
39#include <wtf/Assertions.h>
40#include <wtf/MathExtras.h>
41#include <wtf/Vector.h>
42#include <wtf/unicode/UTF8.h>
43
44#if HAVE(STRING_H)
45#include <string.h>
46#endif
47#if HAVE(STRINGS_H)
48#include <strings.h>
49#endif
50
51using namespace WTF;
52using namespace WTF::Unicode;
53using namespace std;
54
55// This can be tuned differently per platform by putting platform #ifs right here.
56// If you don't define this macro at all, then copyChars will just call directly
57// to memcpy.
58#define USTRING_COPY_CHARS_INLINE_CUTOFF 20
59
60namespace JSC {
61
62extern const double NaN;
63extern const double Inf;
64
65static inline size_t overflowIndicator() { return std::numeric_limits<size_t>::max(); }
66static inline size_t maxUChars() { return std::numeric_limits<size_t>::max() / sizeof(UChar); }
67
68static inline UChar* allocChars(size_t length)
69{
70 ASSERT(length);
71 if (length > maxUChars())
72 return 0;
73 return static_cast<UChar*>(tryFastMalloc(sizeof(UChar) * length));
74}
75
76static inline UChar* reallocChars(UChar* buffer, size_t length)
77{
78 ASSERT(length);
79 if (length > maxUChars())
80 return 0;
81 return static_cast<UChar*>(tryFastRealloc(buffer, sizeof(UChar) * length));
82}
83
84static inline void copyChars(UChar* destination, const UChar* source, unsigned numCharacters)
85{
86#ifdef USTRING_COPY_CHARS_INLINE_CUTOFF
87 if (numCharacters <= USTRING_COPY_CHARS_INLINE_CUTOFF) {
88 for (unsigned i = 0; i < numCharacters; ++i)
89 destination[i] = source[i];
90 return;
91 }
92#endif
93 memcpy(destination, source, numCharacters * sizeof(UChar));
94}
95
96COMPILE_ASSERT(sizeof(UChar) == 2, uchar_is_2_bytes);
97
98CString::CString(const char* c)
99 : m_length(strlen(c))
100 , m_data(new char[m_length + 1])
101{
102 memcpy(m_data, c, m_length + 1);
103}
104
105CString::CString(const char* c, size_t length)
106 : m_length(length)
107 , m_data(new char[length + 1])
108{
109 memcpy(m_data, c, m_length);
110 m_data[m_length] = 0;
111}
112
113CString::CString(const CString& b)
114{
115 m_length = b.m_length;
116 if (b.m_data) {
117 m_data = new char[m_length + 1];
118 memcpy(m_data, b.m_data, m_length + 1);
119 } else
120 m_data = 0;
121}
122
123CString::~CString()
124{
125 delete [] m_data;
126}
127
128CString CString::adopt(char* c, size_t length)
129{
130 CString s;
131 s.m_data = c;
132 s.m_length = length;
133 return s;
134}
135
136CString& CString::append(const CString& t)
137{
138 char* n;
139 n = new char[m_length + t.m_length + 1];
140 if (m_length)
141 memcpy(n, m_data, m_length);
142 if (t.m_length)
143 memcpy(n + m_length, t.m_data, t.m_length);
144 m_length += t.m_length;
145 n[m_length] = 0;
146
147 delete [] m_data;
148 m_data = n;
149
150 return *this;
151}
152
153CString& CString::operator=(const char* c)
154{
155 if (m_data)
156 delete [] m_data;
157 m_length = strlen(c);
158 m_data = new char[m_length + 1];
159 memcpy(m_data, c, m_length + 1);
160
161 return *this;
162}
163
164CString& CString::operator=(const CString& str)
165{
166 if (this == &str)
167 return *this;
168
169 if (m_data)
170 delete [] m_data;
171 m_length = str.m_length;
172 if (str.m_data) {
173 m_data = new char[m_length + 1];
174 memcpy(m_data, str.m_data, m_length + 1);
175 } else
176 m_data = 0;
177
178 return *this;
179}
180
181bool operator==(const CString& c1, const CString& c2)
182{
183 size_t len = c1.size();
184 return len == c2.size() && (len == 0 || memcmp(c1.c_str(), c2.c_str(), len) == 0);
185}
186
187// These static strings are immutable, except for rc, whose initial value is chosen to
188// reduce the possibility of it becoming zero due to ref/deref not being thread-safe.
189static UChar sharedEmptyChar;
190UString::BaseString* UString::Rep::nullBaseString;
191UString::BaseString* UString::Rep::emptyBaseString;
192UString* UString::nullUString;
193
194static void initializeStaticBaseString(UString::BaseString& base)
195{
196 base.rc = INT_MAX / 2;
197 base.m_identifierTableAndFlags.setFlag(UString::Rep::StaticFlag);
198 base.checkConsistency();
199}
200
201void initializeUString()
202{
203 UString::Rep::nullBaseString = new UString::BaseString(0, 0);
204 initializeStaticBaseString(*UString::Rep::nullBaseString);
205
206 UString::Rep::emptyBaseString = new UString::BaseString(&sharedEmptyChar, 0);
207 initializeStaticBaseString(*UString::Rep::emptyBaseString);
208
209 UString::nullUString = new UString;
210}
211
212static char* statBuffer = 0; // Only used for debugging via UString::ascii().
213
214PassRefPtr<UString::Rep> UString::Rep::createCopying(const UChar* d, int l)
215{
216 UChar* copyD = static_cast<UChar*>(fastMalloc(l * sizeof(UChar)));
217 copyChars(copyD, d, l);
218 return create(copyD, l);
219}
220
221PassRefPtr<UString::Rep> UString::Rep::createFromUTF8(const char* string)
222{
223 if (!string)
224 return &UString::Rep::null();
225
226 size_t length = strlen(string);
227 Vector<UChar, 1024> buffer(length);
228 UChar* p = buffer.data();
229 if (conversionOK != convertUTF8ToUTF16(&string, string + length, &p, p + length))
230 return &UString::Rep::null();
231
232 return UString::Rep::createCopying(buffer.data(), p - buffer.data());
233}
234
235void UString::Rep::destroy()
236{
237 checkConsistency();
238
239 // Static null and empty strings can never be destroyed, but we cannot rely on
240 // reference counting, because ref/deref are not thread-safe.
241 if (!isStatic()) {
242 if (identifierTable())
243 Identifier::remove(this);
244 UString::BaseString* base = baseString();
245 if (base == this)
246 fastFree(base->buf);
247 else
248 base->deref();
249
250 delete this;
251 }
252}
253
254// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's
255// or anything like that.
256const unsigned PHI = 0x9e3779b9U;
257
258// Paul Hsieh's SuperFastHash
259// http://www.azillionmonkeys.com/qed/hash.html
260unsigned UString::Rep::computeHash(const UChar* s, int len)
261{
262 unsigned l = len;
263 uint32_t hash = PHI;
264 uint32_t tmp;
265
266 int rem = l & 1;
267 l >>= 1;
268
269 // Main loop
270 for (; l > 0; l--) {
271 hash += s[0];
272 tmp = (s[1] << 11) ^ hash;
273 hash = (hash << 16) ^ tmp;
274 s += 2;
275 hash += hash >> 11;
276 }
277
278 // Handle end case
279 if (rem) {
280 hash += s[0];
281 hash ^= hash << 11;
282 hash += hash >> 17;
283 }
284
285 // Force "avalanching" of final 127 bits
286 hash ^= hash << 3;
287 hash += hash >> 5;
288 hash ^= hash << 2;
289 hash += hash >> 15;
290 hash ^= hash << 10;
291
292 // this avoids ever returning a hash code of 0, since that is used to
293 // signal "hash not computed yet", using a value that is likely to be
294 // effectively the same as 0 when the low bits are masked
295 if (hash == 0)
296 hash = 0x80000000;
297
298 return hash;
299}
300
301// Paul Hsieh's SuperFastHash
302// http://www.azillionmonkeys.com/qed/hash.html
303unsigned UString::Rep::computeHash(const char* s, int l)
304{
305 // This hash is designed to work on 16-bit chunks at a time. But since the normal case
306 // (above) is to hash UTF-16 characters, we just treat the 8-bit chars as if they
307 // were 16-bit chunks, which should give matching results
308
309 uint32_t hash = PHI;
310 uint32_t tmp;
311
312 size_t rem = l & 1;
313 l >>= 1;
314
315 // Main loop
316 for (; l > 0; l--) {
317 hash += static_cast<unsigned char>(s[0]);
318 tmp = (static_cast<unsigned char>(s[1]) << 11) ^ hash;
319 hash = (hash << 16) ^ tmp;
320 s += 2;
321 hash += hash >> 11;
322 }
323
324 // Handle end case
325 if (rem) {
326 hash += static_cast<unsigned char>(s[0]);
327 hash ^= hash << 11;
328 hash += hash >> 17;
329 }
330
331 // Force "avalanching" of final 127 bits
332 hash ^= hash << 3;
333 hash += hash >> 5;
334 hash ^= hash << 2;
335 hash += hash >> 15;
336 hash ^= hash << 10;
337
338 // this avoids ever returning a hash code of 0, since that is used to
339 // signal "hash not computed yet", using a value that is likely to be
340 // effectively the same as 0 when the low bits are masked
341 if (hash == 0)
342 hash = 0x80000000;
343
344 return hash;
345}
346
347#ifndef NDEBUG
348void UString::Rep::checkConsistency() const
349{
350 const UString::BaseString* base = baseString();
351
352 // There is no recursion for base strings.
353 ASSERT(base == base->baseString());
354
355 if (isStatic()) {
356 // There are only two static strings: null and empty.
357 ASSERT(!len);
358
359 // Static strings cannot get in identifier tables, because they are globally shared.
360 ASSERT(!identifierTable());
361 }
362
363 // The string fits in buffer.
364 ASSERT(base->usedPreCapacity <= base->preCapacity);
365 ASSERT(base->usedCapacity <= base->capacity);
366 ASSERT(-offset <= base->usedPreCapacity);
367 ASSERT(offset + len <= base->usedCapacity);
368}
369#endif
370
371// put these early so they can be inlined
372static inline size_t expandedSize(size_t size, size_t otherSize)
373{
374 // Do the size calculation in two parts, returning overflowIndicator if
375 // we overflow the maximum value that we can handle.
376
377 if (size > maxUChars())
378 return overflowIndicator();
379
380 size_t expandedSize = ((size + 10) / 10 * 11) + 1;
381
382 // If 'size' was originally just less than maxUChars() then we may have just overflowed.
383 // The next check won't catch this, since the subtraction will underflow. Alternatively,
384 // rather than failing we could just back off from the expansion here?
385 if (expandedSize > maxUChars())
386 return overflowIndicator();
387
388 if (maxUChars() - expandedSize < otherSize)
389 return overflowIndicator();
390
391 return expandedSize + otherSize;
392}
393
394static inline bool expandCapacity(UString::Rep* rep, int requiredLength)
395{
396 rep->checkConsistency();
397
398 UString::BaseString* base = rep->baseString();
399
400 if (requiredLength > base->capacity) {
401 size_t newCapacity = expandedSize(requiredLength, base->preCapacity);
402 UChar* oldBuf = base->buf;
403 base->buf = reallocChars(base->buf, newCapacity);
404 if (!base->buf) {
405 base->buf = oldBuf;
406 return false;
407 }
408 base->capacity = newCapacity - base->preCapacity;
409 }
410 if (requiredLength > base->usedCapacity)
411 base->usedCapacity = requiredLength;
412
413 rep->checkConsistency();
414 return true;
415}
416
417bool UString::Rep::reserveCapacity(int capacity)
418{
419 // If this is an empty string there is no point 'growing' it - just allocate a new one.
420 // If the BaseString is shared with another string that is using more capacity than this
421 // string is, then growing the buffer won't help.
422 if (!m_baseString->buf || !m_baseString->capacity || (offset + len) != m_baseString->usedCapacity)
423 return false;
424
425 // If there is already sufficient capacity, no need to grow!
426 if (capacity <= m_baseString->capacity)
427 return true;
428
429 checkConsistency();
430
431 size_t newCapacity = expandedSize(capacity, m_baseString->preCapacity);
432 UChar* oldBuf = m_baseString->buf;
433 m_baseString->buf = reallocChars(m_baseString->buf, newCapacity);
434 if (!m_baseString->buf) {
435 m_baseString->buf = oldBuf;
436 return false;
437 }
438 m_baseString->capacity = newCapacity - m_baseString->preCapacity;
439
440 checkConsistency();
441 return true;
442}
443
444void UString::expandCapacity(int requiredLength)
445{
446 if (!JSC::expandCapacity(m_rep.get(), requiredLength))
447 makeNull();
448}
449
450void UString::expandPreCapacity(int requiredPreCap)
451{
452 m_rep->checkConsistency();
453
454 BaseString* base = m_rep->baseString();
455
456 if (requiredPreCap > base->preCapacity) {
457 size_t newCapacity = expandedSize(requiredPreCap, base->capacity);
458 int delta = newCapacity - base->capacity - base->preCapacity;
459
460 UChar* newBuf = allocChars(newCapacity);
461 if (!newBuf) {
462 makeNull();
463 return;
464 }
465 copyChars(newBuf + delta, base->buf, base->capacity + base->preCapacity);
466 fastFree(base->buf);
467 base->buf = newBuf;
468
469 base->preCapacity = newCapacity - base->capacity;
470 }
471 if (requiredPreCap > base->usedPreCapacity)
472 base->usedPreCapacity = requiredPreCap;
473
474 m_rep->checkConsistency();
475}
476
477static PassRefPtr<UString::Rep> createRep(const char* c)
478{
479 if (!c)
480 return &UString::Rep::null();
481
482 if (!c[0])
483 return &UString::Rep::empty();
484
485 size_t length = strlen(c);
486 UChar* d = allocChars(length);
487 if (!d)
488 return &UString::Rep::null();
489 else {
490 for (size_t i = 0; i < length; i++)
491 d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend
492 return UString::Rep::create(d, static_cast<int>(length));
493 }
494
495}
496
497UString::UString(const char* c)
498 : m_rep(createRep(c))
499{
500}
501
502UString::UString(const UChar* c, int length)
503{
504 if (length == 0)
505 m_rep = &Rep::empty();
506 else
507 m_rep = Rep::createCopying(c, length);
508}
509
510UString::UString(UChar* c, int length, bool copy)
511{
512 if (length == 0)
513 m_rep = &Rep::empty();
514 else if (copy)
515 m_rep = Rep::createCopying(c, length);
516 else
517 m_rep = Rep::create(c, length);
518}
519
520UString::UString(const Vector<UChar>& buffer)
521{
522 if (!buffer.size())
523 m_rep = &Rep::empty();
524 else
525 m_rep = Rep::createCopying(buffer.data(), buffer.size());
526}
527
528static ALWAYS_INLINE int newCapacityWithOverflowCheck(const int currentCapacity, const int extendLength, const bool plusOne = false)
529{
530 ASSERT_WITH_MESSAGE(extendLength >= 0, "extendedLength = %d", extendLength);
531
532 const int plusLength = plusOne ? 1 : 0;
533 if (currentCapacity > std::numeric_limits<int>::max() - extendLength - plusLength)
534 CRASH();
535
536 return currentCapacity + extendLength + plusLength;
537}
538
539static ALWAYS_INLINE PassRefPtr<UString::Rep> concatenate(PassRefPtr<UString::Rep> r, const UChar* tData, int tSize)
540{
541 RefPtr<UString::Rep> rep = r;
542
543 rep->checkConsistency();
544
545 int thisSize = rep->size();
546 int thisOffset = rep->offset;
547 int length = thisSize + tSize;
548 UString::BaseString* base = rep->baseString();
549
550 // possible cases:
551 if (tSize == 0) {
552 // t is empty
553 } else if (thisSize == 0) {
554 // this is empty
555 rep = UString::Rep::createCopying(tData, tSize);
556 } else if (rep == base && !base->isShared()) {
557 // this is direct and has refcount of 1 (so we can just alter it directly)
558 if (!expandCapacity(rep.get(), newCapacityWithOverflowCheck(thisOffset, length)))
559 rep = &UString::Rep::null();
560 if (rep->data()) {
561 copyChars(rep->data() + thisSize, tData, tSize);
562 rep->len = length;
563 rep->_hash = 0;
564 }
565 } else if (thisOffset + thisSize == base->usedCapacity && thisSize >= minShareSize) {
566 // this reaches the end of the buffer - extend it if it's long enough to append to
567 if (!expandCapacity(rep.get(), newCapacityWithOverflowCheck(thisOffset, length)))
568 rep = &UString::Rep::null();
569 if (rep->data()) {
570 copyChars(rep->data() + thisSize, tData, tSize);
571 rep = UString::Rep::create(rep, 0, length);
572 }
573 } else {
574 // this is shared with someone using more capacity, gotta make a whole new string
575 size_t newCapacity = expandedSize(length, 0);
576 UChar* d = allocChars(newCapacity);
577 if (!d)
578 rep = &UString::Rep::null();
579 else {
580 copyChars(d, rep->data(), thisSize);
581 copyChars(d + thisSize, tData, tSize);
582 rep = UString::Rep::create(d, length);
583 rep->baseString()->capacity = newCapacity;
584 }
585 }
586
587 rep->checkConsistency();
588
589 return rep.release();
590}
591
592static ALWAYS_INLINE PassRefPtr<UString::Rep> concatenate(PassRefPtr<UString::Rep> r, const char* t)
593{
594 RefPtr<UString::Rep> rep = r;
595
596 rep->checkConsistency();
597
598 int thisSize = rep->size();
599 int thisOffset = rep->offset;
600 int tSize = static_cast<int>(strlen(t));
601 int length = thisSize + tSize;
602 UString::BaseString* base = rep->baseString();
603
604 // possible cases:
605 if (thisSize == 0) {
606 // this is empty
607 rep = createRep(t);
608 } else if (tSize == 0) {
609 // t is empty, we'll just return *this below.
610 } else if (rep == base && !base->isShared()) {
611 // this is direct and has refcount of 1 (so we can just alter it directly)
612 expandCapacity(rep.get(), newCapacityWithOverflowCheck(thisOffset, length));
613 UChar* d = rep->data();
614 if (d) {
615 for (int i = 0; i < tSize; ++i)
616 d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
617 rep->len = length;
618 rep->_hash = 0;
619 }
620 } else if (thisOffset + thisSize == base->usedCapacity && thisSize >= minShareSize) {
621 // this string reaches the end of the buffer - extend it
622 expandCapacity(rep.get(), newCapacityWithOverflowCheck(thisOffset, length));
623 UChar* d = rep->data();
624 if (d) {
625 for (int i = 0; i < tSize; ++i)
626 d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
627 rep = UString::Rep::create(rep, 0, length);
628 }
629 } else {
630 // this is shared with someone using more capacity, gotta make a whole new string
631 size_t newCapacity = expandedSize(length, 0);
632 UChar* d = allocChars(newCapacity);
633 if (!d)
634 rep = &UString::Rep::null();
635 else {
636 copyChars(d, rep->data(), thisSize);
637 for (int i = 0; i < tSize; ++i)
638 d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
639 rep = UString::Rep::create(d, length);
640 rep->baseString()->capacity = newCapacity;
641 }
642 }
643
644 rep->checkConsistency();
645
646 return rep.release();
647}
648
649PassRefPtr<UString::Rep> concatenate(UString::Rep* a, UString::Rep* b)
650{
651 a->checkConsistency();
652 b->checkConsistency();
653
654 int aSize = a->size();
655 int bSize = b->size();
656 int aOffset = a->offset;
657
658 // possible cases:
659
660 UString::BaseString* aBase = a->baseString();
661 if (bSize == 1 && aOffset + aSize == aBase->usedCapacity && aOffset + aSize < aBase->capacity) {
662 // b is a single character (common fast case)
663 ++aBase->usedCapacity;
664 a->data()[aSize] = b->data()[0];
665 return UString::Rep::create(a, 0, aSize + 1);
666 }
667
668 // a is empty
669 if (aSize == 0)
670 return b;
671 // b is empty
672 if (bSize == 0)
673 return a;
674
675 int bOffset = b->offset;
676 int length = aSize + bSize;
677
678 UString::BaseString* bBase = b->baseString();
679 if (aOffset + aSize == aBase->usedCapacity && aSize >= minShareSize && 4 * aSize >= bSize
680 && (-bOffset != bBase->usedPreCapacity || aSize >= bSize)) {
681 // - a reaches the end of its buffer so it qualifies for shared append
682 // - also, it's at least a quarter the length of b - appending to a much shorter
683 // string does more harm than good
684 // - however, if b qualifies for prepend and is longer than a, we'd rather prepend
685
686 UString x(a);
687 x.expandCapacity(newCapacityWithOverflowCheck(aOffset, length));
688 if (!a->data() || !x.data())
689 return 0;
690 copyChars(a->data() + aSize, b->data(), bSize);
691 PassRefPtr<UString::Rep> result = UString::Rep::create(a, 0, length);
692
693 a->checkConsistency();
694 b->checkConsistency();
695 result->checkConsistency();
696
697 return result;
698 }
699
700 if (-bOffset == bBase->usedPreCapacity && bSize >= minShareSize && 4 * bSize >= aSize) {
701 // - b reaches the beginning of its buffer so it qualifies for shared prepend
702 // - also, it's at least a quarter the length of a - prepending to a much shorter
703 // string does more harm than good
704 UString y(b);
705 y.expandPreCapacity(-bOffset + aSize);
706 if (!b->data() || !y.data())
707 return 0;
708 copyChars(b->data() - aSize, a->data(), aSize);
709 PassRefPtr<UString::Rep> result = UString::Rep::create(b, -aSize, length);
710
711 a->checkConsistency();
712 b->checkConsistency();
713 result->checkConsistency();
714
715 return result;
716 }
717
718 // a does not qualify for append, and b does not qualify for prepend, gotta make a whole new string
719 size_t newCapacity = expandedSize(length, 0);
720 UChar* d = allocChars(newCapacity);
721 if (!d)
722 return 0;
723 copyChars(d, a->data(), aSize);
724 copyChars(d + aSize, b->data(), bSize);
725 PassRefPtr<UString::Rep> result = UString::Rep::create(d, length);
726 result->baseString()->capacity = newCapacity;
727
728 a->checkConsistency();
729 b->checkConsistency();
730 result->checkConsistency();
731
732 return result;
733}
734
735PassRefPtr<UString::Rep> concatenate(UString::Rep* rep, int i)
736{
737 UChar buf[1 + sizeof(i) * 3];
738 UChar* end = buf + sizeof(buf) / sizeof(UChar);
739 UChar* p = end;
740
741 if (i == 0)
742 *--p = '0';
743 else if (i == INT_MIN) {
744 char minBuf[1 + sizeof(i) * 3];
745 sprintf(minBuf, "%d", INT_MIN);
746 return concatenate(rep, minBuf);
747 } else {
748 bool negative = false;
749 if (i < 0) {
750 negative = true;
751 i = -i;
752 }
753 while (i) {
754 *--p = static_cast<unsigned short>((i % 10) + '0');
755 i /= 10;
756 }
757 if (negative)
758 *--p = '-';
759 }
760
761 return concatenate(rep, p, static_cast<int>(end - p));
762
763}
764
765PassRefPtr<UString::Rep> concatenate(UString::Rep* rep, double d)
766{
767 // avoid ever printing -NaN, in JS conceptually there is only one NaN value
768 if (isnan(d))
769 return concatenate(rep, "NaN");
770
771 if (d == 0.0) // stringify -0 as 0
772 d = 0.0;
773
774 char buf[80];
775 int decimalPoint;
776 int sign;
777
778 char* result = WTF::dtoa(d, 0, &decimalPoint, &sign, NULL);
779 int length = static_cast<int>(strlen(result));
780
781 int i = 0;
782 if (sign)
783 buf[i++] = '-';
784
785 if (decimalPoint <= 0 && decimalPoint > -6) {
786 buf[i++] = '0';
787 buf[i++] = '.';
788 for (int j = decimalPoint; j < 0; j++)
789 buf[i++] = '0';
790 strcpy(buf + i, result);
791 } else if (decimalPoint <= 21 && decimalPoint > 0) {
792 if (length <= decimalPoint) {
793 strcpy(buf + i, result);
794 i += length;
795 for (int j = 0; j < decimalPoint - length; j++)
796 buf[i++] = '0';
797 buf[i] = '\0';
798 } else {
799 strncpy(buf + i, result, decimalPoint);
800 i += decimalPoint;
801 buf[i++] = '.';
802 strcpy(buf + i, result + decimalPoint);
803 }
804 } else if (result[0] < '0' || result[0] > '9')
805 strcpy(buf + i, result);
806 else {
807 buf[i++] = result[0];
808 if (length > 1) {
809 buf[i++] = '.';
810 strcpy(buf + i, result + 1);
811 i += length - 1;
812 }
813
814 buf[i++] = 'e';
815 buf[i++] = (decimalPoint >= 0) ? '+' : '-';
816 // decimalPoint can't be more than 3 digits decimal given the
817 // nature of float representation
818 int exponential = decimalPoint - 1;
819 if (exponential < 0)
820 exponential = -exponential;
821 if (exponential >= 100)
822 buf[i++] = static_cast<char>('0' + exponential / 100);
823 if (exponential >= 10)
824 buf[i++] = static_cast<char>('0' + (exponential % 100) / 10);
825 buf[i++] = static_cast<char>('0' + exponential % 10);
826 buf[i++] = '\0';
827 }
828
829 WTF::freedtoa(result);
830
831 return concatenate(rep, buf);
832}
833
834UString UString::from(int i)
835{
836 UChar buf[1 + sizeof(i) * 3];
837 UChar* end = buf + sizeof(buf) / sizeof(UChar);
838 UChar* p = end;
839
840 if (i == 0)
841 *--p = '0';
842 else if (i == INT_MIN) {
843 char minBuf[1 + sizeof(i) * 3];
844 sprintf(minBuf, "%d", INT_MIN);
845 return UString(minBuf);
846 } else {
847 bool negative = false;
848 if (i < 0) {
849 negative = true;
850 i = -i;
851 }
852 while (i) {
853 *--p = static_cast<unsigned short>((i % 10) + '0');
854 i /= 10;
855 }
856 if (negative)
857 *--p = '-';
858 }
859
860 return UString(p, static_cast<int>(end - p));
861}
862
863UString UString::from(unsigned int u)
864{
865 UChar buf[sizeof(u) * 3];
866 UChar* end = buf + sizeof(buf) / sizeof(UChar);
867 UChar* p = end;
868
869 if (u == 0)
870 *--p = '0';
871 else {
872 while (u) {
873 *--p = static_cast<unsigned short>((u % 10) + '0');
874 u /= 10;
875 }
876 }
877
878 return UString(p, static_cast<int>(end - p));
879}
880
881UString UString::from(long l)
882{
883 UChar buf[1 + sizeof(l) * 3];
884 UChar* end = buf + sizeof(buf) / sizeof(UChar);
885 UChar* p = end;
886
887 if (l == 0)
888 *--p = '0';
889 else if (l == LONG_MIN) {
890 char minBuf[1 + sizeof(l) * 3];
891 sprintf(minBuf, "%ld", LONG_MIN);
892 return UString(minBuf);
893 } else {
894 bool negative = false;
895 if (l < 0) {
896 negative = true;
897 l = -l;
898 }
899 while (l) {
900 *--p = static_cast<unsigned short>((l % 10) + '0');
901 l /= 10;
902 }
903 if (negative)
904 *--p = '-';
905 }
906
907 return UString(p, static_cast<int>(end - p));
908}
909
910UString UString::from(double d)
911{
912 // avoid ever printing -NaN, in JS conceptually there is only one NaN value
913 if (isnan(d))
914 return "NaN";
915
916 char buf[80];
917 int decimalPoint;
918 int sign;
919
920 char* result = WTF::dtoa(d, 0, &decimalPoint, &sign, NULL);
921 int length = static_cast<int>(strlen(result));
922
923 int i = 0;
924 if (sign)
925 buf[i++] = '-';
926
927 if (decimalPoint <= 0 && decimalPoint > -6) {
928 buf[i++] = '0';
929 buf[i++] = '.';
930 for (int j = decimalPoint; j < 0; j++)
931 buf[i++] = '0';
932 strcpy(buf + i, result);
933 } else if (decimalPoint <= 21 && decimalPoint > 0) {
934 if (length <= decimalPoint) {
935 strcpy(buf + i, result);
936 i += length;
937 for (int j = 0; j < decimalPoint - length; j++)
938 buf[i++] = '0';
939 buf[i] = '\0';
940 } else {
941 strncpy(buf + i, result, decimalPoint);
942 i += decimalPoint;
943 buf[i++] = '.';
944 strcpy(buf + i, result + decimalPoint);
945 }
946 } else if (result[0] < '0' || result[0] > '9')
947 strcpy(buf + i, result);
948 else {
949 buf[i++] = result[0];
950 if (length > 1) {
951 buf[i++] = '.';
952 strcpy(buf + i, result + 1);
953 i += length - 1;
954 }
955
956 buf[i++] = 'e';
957 buf[i++] = (decimalPoint >= 0) ? '+' : '-';
958 // decimalPoint can't be more than 3 digits decimal given the
959 // nature of float representation
960 int exponential = decimalPoint - 1;
961 if (exponential < 0)
962 exponential = -exponential;
963 if (exponential >= 100)
964 buf[i++] = static_cast<char>('0' + exponential / 100);
965 if (exponential >= 10)
966 buf[i++] = static_cast<char>('0' + (exponential % 100) / 10);
967 buf[i++] = static_cast<char>('0' + exponential % 10);
968 buf[i++] = '\0';
969 }
970
971 WTF::freedtoa(result);
972
973 return UString(buf);
974}
975
976UString UString::spliceSubstringsWithSeparators(const Range* substringRanges, int rangeCount, const UString* separators, int separatorCount) const
977{
978 m_rep->checkConsistency();
979
980 if (rangeCount == 1 && separatorCount == 0) {
981 int thisSize = size();
982 int position = substringRanges[0].position;
983 int length = substringRanges[0].length;
984 if (position <= 0 && length >= thisSize)
985 return *this;
986 return UString::Rep::create(m_rep, max(0, position), min(thisSize, length));
987 }
988
989 int totalLength = 0;
990 for (int i = 0; i < rangeCount; i++)
991 totalLength += substringRanges[i].length;
992 for (int i = 0; i < separatorCount; i++)
993 totalLength += separators[i].size();
994
995 if (totalLength == 0)
996 return "";
997
998 UChar* buffer = allocChars(totalLength);
999 if (!buffer)
1000 return null();
1001
1002 int maxCount = max(rangeCount, separatorCount);
1003 int bufferPos = 0;
1004 for (int i = 0; i < maxCount; i++) {
1005 if (i < rangeCount) {
1006 copyChars(buffer + bufferPos, data() + substringRanges[i].position, substringRanges[i].length);
1007 bufferPos += substringRanges[i].length;
1008 }
1009 if (i < separatorCount) {
1010 copyChars(buffer + bufferPos, separators[i].data(), separators[i].size());
1011 bufferPos += separators[i].size();
1012 }
1013 }
1014
1015 return UString::Rep::create(buffer, totalLength);
1016}
1017
1018UString UString::replaceRange(int rangeStart, int rangeLength, const UString& replacement) const
1019{
1020 m_rep->checkConsistency();
1021
1022 int replacementLength = replacement.size();
1023 int totalLength = size() - rangeLength + replacementLength;
1024 if (totalLength == 0)
1025 return "";
1026
1027 UChar* buffer = allocChars(totalLength);
1028 if (!buffer)
1029 return null();
1030
1031 copyChars(buffer, data(), rangeStart);
1032 copyChars(buffer + rangeStart, replacement.data(), replacementLength);
1033 int rangeEnd = rangeStart + rangeLength;
1034 copyChars(buffer + rangeStart + replacementLength, data() + rangeEnd, size() - rangeEnd);
1035
1036 return UString::Rep::create(buffer, totalLength);
1037}
1038
1039
1040UString& UString::append(const UString &t)
1041{
1042 m_rep->checkConsistency();
1043 t.rep()->checkConsistency();
1044
1045 int thisSize = size();
1046 int thisOffset = m_rep->offset;
1047 int tSize = t.size();
1048 int length = thisSize + tSize;
1049 BaseString* base = m_rep->baseString();
1050
1051 // possible cases:
1052 if (thisSize == 0) {
1053 // this is empty
1054 *this = t;
1055 } else if (tSize == 0) {
1056 // t is empty
1057 } else if (m_rep == base && !base->isShared()) {
1058 // this is direct and has refcount of 1 (so we can just alter it directly)
1059 expandCapacity(newCapacityWithOverflowCheck(thisOffset, length));
1060 if (data()) {
1061 copyChars(m_rep->data() + thisSize, t.data(), tSize);
1062 m_rep->len = length;
1063 m_rep->_hash = 0;
1064 }
1065 } else if (thisOffset + thisSize == base->usedCapacity && thisSize >= minShareSize) {
1066 // this reaches the end of the buffer - extend it if it's long enough to append to
1067 expandCapacity(newCapacityWithOverflowCheck(thisOffset, length));
1068 if (data()) {
1069 copyChars(m_rep->data() + thisSize, t.data(), tSize);
1070 m_rep = Rep::create(m_rep, 0, length);
1071 }
1072 } else {
1073 // this is shared with someone using more capacity, gotta make a whole new string
1074 size_t newCapacity = expandedSize(length, 0);
1075 UChar* d = allocChars(newCapacity);
1076 if (!d)
1077 makeNull();
1078 else {
1079 copyChars(d, data(), thisSize);
1080 copyChars(d + thisSize, t.data(), tSize);
1081 m_rep = Rep::create(d, length);
1082 m_rep->baseString()->capacity = newCapacity;
1083 }
1084 }
1085
1086 m_rep->checkConsistency();
1087 t.rep()->checkConsistency();
1088
1089 return *this;
1090}
1091
1092UString& UString::append(const UChar* tData, int tSize)
1093{
1094 m_rep = concatenate(m_rep.release(), tData, tSize);
1095 return *this;
1096}
1097
1098UString& UString::appendNumeric(int i)
1099{
1100 m_rep = concatenate(rep(), i);
1101 return *this;
1102}
1103
1104UString& UString::appendNumeric(double d)
1105{
1106 m_rep = concatenate(rep(), d);
1107 return *this;
1108}
1109
1110UString& UString::append(const char* t)
1111{
1112 m_rep = concatenate(m_rep.release(), t);
1113 return *this;
1114}
1115
1116UString& UString::append(UChar c)
1117{
1118 m_rep->checkConsistency();
1119
1120 int thisOffset = m_rep->offset;
1121 int length = size();
1122 BaseString* base = m_rep->baseString();
1123
1124 // possible cases:
1125 if (length == 0) {
1126 // this is empty - must make a new m_rep because we don't want to pollute the shared empty one
1127 size_t newCapacity = expandedSize(1, 0);
1128 UChar* d = allocChars(newCapacity);
1129 if (!d)
1130 makeNull();
1131 else {
1132 d[0] = c;
1133 m_rep = Rep::create(d, 1);
1134 m_rep->baseString()->capacity = newCapacity;
1135 }
1136 } else if (m_rep == base && !base->isShared()) {
1137 // this is direct and has refcount of 1 (so we can just alter it directly)
1138 expandCapacity(newCapacityWithOverflowCheck(thisOffset, length, true));
1139 UChar* d = m_rep->data();
1140 if (d) {
1141 d[length] = c;
1142 m_rep->len = length + 1;
1143 m_rep->_hash = 0;
1144 }
1145 } else if (thisOffset + length == base->usedCapacity && length >= minShareSize) {
1146 // this reaches the end of the string - extend it and share
1147 expandCapacity(newCapacityWithOverflowCheck(thisOffset, length, true));
1148 UChar* d = m_rep->data();
1149 if (d) {
1150 d[length] = c;
1151 m_rep = Rep::create(m_rep, 0, length + 1);
1152 }
1153 } else {
1154 // this is shared with someone using more capacity, gotta make a whole new string
1155 size_t newCapacity = expandedSize(length + 1, 0);
1156 UChar* d = allocChars(newCapacity);
1157 if (!d)
1158 makeNull();
1159 else {
1160 copyChars(d, data(), length);
1161 d[length] = c;
1162 m_rep = Rep::create(d, length + 1);
1163 m_rep->baseString()->capacity = newCapacity;
1164 }
1165 }
1166
1167 m_rep->checkConsistency();
1168
1169 return *this;
1170}
1171
1172bool UString::getCString(CStringBuffer& buffer) const
1173{
1174 int length = size();
1175 int neededSize = length + 1;
1176 buffer.resize(neededSize);
1177 char* buf = buffer.data();
1178
1179 UChar ored = 0;
1180 const UChar* p = data();
1181 char* q = buf;
1182 const UChar* limit = p + length;
1183 while (p != limit) {
1184 UChar c = p[0];
1185 ored |= c;
1186 *q = static_cast<char>(c);
1187 ++p;
1188 ++q;
1189 }
1190 *q = '\0';
1191
1192 return !(ored & 0xFF00);
1193}
1194
1195char* UString::ascii() const
1196{
1197 int length = size();
1198 int neededSize = length + 1;
1199 delete[] statBuffer;
1200 statBuffer = new char[neededSize];
1201
1202 const UChar* p = data();
1203 char* q = statBuffer;
1204 const UChar* limit = p + length;
1205 while (p != limit) {
1206 *q = static_cast<char>(p[0]);
1207 ++p;
1208 ++q;
1209 }
1210 *q = '\0';
1211
1212 return statBuffer;
1213}
1214
1215UString& UString::operator=(const char* c)
1216{
1217 if (!c) {
1218 m_rep = &Rep::null();
1219 return *this;
1220 }
1221
1222 if (!c[0]) {
1223 m_rep = &Rep::empty();
1224 return *this;
1225 }
1226
1227 int l = static_cast<int>(strlen(c));
1228 UChar* d;
1229 BaseString* base = m_rep->baseString();
1230 if (!base->isShared() && l <= base->capacity && m_rep == base && m_rep->offset == 0 && base->preCapacity == 0) {
1231 d = base->buf;
1232 m_rep->_hash = 0;
1233 m_rep->len = l;
1234 } else {
1235 d = allocChars(l);
1236 if (!d) {
1237 makeNull();
1238 return *this;
1239 }
1240 m_rep = Rep::create(d, l);
1241 }
1242 for (int i = 0; i < l; i++)
1243 d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend
1244
1245 return *this;
1246}
1247
1248bool UString::is8Bit() const
1249{
1250 const UChar* u = data();
1251 const UChar* limit = u + size();
1252 while (u < limit) {
1253 if (u[0] > 0xFF)
1254 return false;
1255 ++u;
1256 }
1257
1258 return true;
1259}
1260
1261UChar UString::operator[](int pos) const
1262{
1263 if (pos >= size())
1264 return '\0';
1265 return data()[pos];
1266}
1267
1268double UString::toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const
1269{
1270 if (size() == 1) {
1271 UChar c = data()[0];
1272 if (isASCIIDigit(c))
1273 return c - '0';
1274 if (isASCIISpace(c) && tolerateEmptyString)
1275 return 0;
1276 return NaN;
1277 }
1278
1279 // FIXME: If tolerateTrailingJunk is true, then we want to tolerate non-8-bit junk
1280 // after the number, so this is too strict a check.
1281 CStringBuffer s;
1282 if (!getCString(s))
1283 return NaN;
1284 const char* c = s.data();
1285
1286 // skip leading white space
1287 while (isASCIISpace(*c))
1288 c++;
1289
1290 // empty string ?
1291 if (*c == '\0')
1292 return tolerateEmptyString ? 0.0 : NaN;
1293
1294 double d;
1295
1296 // hex number ?
1297 if (*c == '0' && (*(c + 1) == 'x' || *(c + 1) == 'X')) {
1298 const char* firstDigitPosition = c + 2;
1299 c++;
1300 d = 0.0;
1301 while (*(++c)) {
1302 if (*c >= '0' && *c <= '9')
1303 d = d * 16.0 + *c - '0';
1304 else if ((*c >= 'A' && *c <= 'F') || (*c >= 'a' && *c <= 'f'))
1305 d = d * 16.0 + (*c & 0xdf) - 'A' + 10.0;
1306 else
1307 break;
1308 }
1309
1310 if (d >= mantissaOverflowLowerBound)
1311 d = parseIntOverflow(firstDigitPosition, c - firstDigitPosition, 16);
1312 } else {
1313 // regular number ?
1314 char* end;
1315 d = WTF::strtod(c, &end);
1316 if ((d != 0.0 || end != c) && d != Inf && d != -Inf) {
1317 c = end;
1318 } else {
1319 double sign = 1.0;
1320
1321 if (*c == '+')
1322 c++;
1323 else if (*c == '-') {
1324 sign = -1.0;
1325 c++;
1326 }
1327
1328 // We used strtod() to do the conversion. However, strtod() handles
1329 // infinite values slightly differently than JavaScript in that it
1330 // converts the string "inf" with any capitalization to infinity,
1331 // whereas the ECMA spec requires that it be converted to NaN.
1332
1333 if (c[0] == 'I' && c[1] == 'n' && c[2] == 'f' && c[3] == 'i' && c[4] == 'n' && c[5] == 'i' && c[6] == 't' && c[7] == 'y') {
1334 d = sign * Inf;
1335 c += 8;
1336 } else if ((d == Inf || d == -Inf) && *c != 'I' && *c != 'i')
1337 c = end;
1338 else
1339 return NaN;
1340 }
1341 }
1342
1343 // allow trailing white space
1344 while (isASCIISpace(*c))
1345 c++;
1346 // don't allow anything after - unless tolerant=true
1347 if (!tolerateTrailingJunk && *c != '\0')
1348 d = NaN;
1349
1350 return d;
1351}
1352
1353double UString::toDouble(bool tolerateTrailingJunk) const
1354{
1355 return toDouble(tolerateTrailingJunk, true);
1356}
1357
1358double UString::toDouble() const
1359{
1360 return toDouble(false, true);
1361}
1362
1363uint32_t UString::toUInt32(bool* ok) const
1364{
1365 double d = toDouble();
1366 bool b = true;
1367
1368 if (d != static_cast<uint32_t>(d)) {
1369 b = false;
1370 d = 0;
1371 }
1372
1373 if (ok)
1374 *ok = b;
1375
1376 return static_cast<uint32_t>(d);
1377}
1378
1379uint32_t UString::toUInt32(bool* ok, bool tolerateEmptyString) const
1380{
1381 double d = toDouble(false, tolerateEmptyString);
1382 bool b = true;
1383
1384 if (d != static_cast<uint32_t>(d)) {
1385 b = false;
1386 d = 0;
1387 }
1388
1389 if (ok)
1390 *ok = b;
1391
1392 return static_cast<uint32_t>(d);
1393}
1394
1395uint32_t UString::toStrictUInt32(bool* ok) const
1396{
1397 if (ok)
1398 *ok = false;
1399
1400 // Empty string is not OK.
1401 int len = m_rep->len;
1402 if (len == 0)
1403 return 0;
1404 const UChar* p = m_rep->data();
1405 unsigned short c = p[0];
1406
1407 // If the first digit is 0, only 0 itself is OK.
1408 if (c == '0') {
1409 if (len == 1 && ok)
1410 *ok = true;
1411 return 0;
1412 }
1413
1414 // Convert to UInt32, checking for overflow.
1415 uint32_t i = 0;
1416 while (1) {
1417 // Process character, turning it into a digit.
1418 if (c < '0' || c > '9')
1419 return 0;
1420 const unsigned d = c - '0';
1421
1422 // Multiply by 10, checking for overflow out of 32 bits.
1423 if (i > 0xFFFFFFFFU / 10)
1424 return 0;
1425 i *= 10;
1426
1427 // Add in the digit, checking for overflow out of 32 bits.
1428 const unsigned max = 0xFFFFFFFFU - d;
1429 if (i > max)
1430 return 0;
1431 i += d;
1432
1433 // Handle end of string.
1434 if (--len == 0) {
1435 if (ok)
1436 *ok = true;
1437 return i;
1438 }
1439
1440 // Get next character.
1441 c = *(++p);
1442 }
1443}
1444
1445int UString::find(const UString& f, int pos) const
1446{
1447 int fsz = f.size();
1448
1449 if (pos < 0)
1450 pos = 0;
1451
1452 if (fsz == 1) {
1453 UChar ch = f[0];
1454 const UChar* end = data() + size();
1455 for (const UChar* c = data() + pos; c < end; c++) {
1456 if (*c == ch)
1457 return static_cast<int>(c - data());
1458 }
1459 return -1;
1460 }
1461
1462 int sz = size();
1463 if (sz < fsz)
1464 return -1;
1465 if (fsz == 0)
1466 return pos;
1467 const UChar* end = data() + sz - fsz;
1468 int fsizeminusone = (fsz - 1) * sizeof(UChar);
1469 const UChar* fdata = f.data();
1470 unsigned short fchar = fdata[0];
1471 ++fdata;
1472 for (const UChar* c = data() + pos; c <= end; c++) {
1473 if (c[0] == fchar && !memcmp(c + 1, fdata, fsizeminusone))
1474 return static_cast<int>(c - data());
1475 }
1476
1477 return -1;
1478}
1479
1480int UString::find(UChar ch, int pos) const
1481{
1482 if (pos < 0)
1483 pos = 0;
1484 const UChar* end = data() + size();
1485 for (const UChar* c = data() + pos; c < end; c++) {
1486 if (*c == ch)
1487 return static_cast<int>(c - data());
1488 }
1489
1490 return -1;
1491}
1492
1493int UString::rfind(const UString& f, int pos) const
1494{
1495 int sz = size();
1496 int fsz = f.size();
1497 if (sz < fsz)
1498 return -1;
1499 if (pos < 0)
1500 pos = 0;
1501 if (pos > sz - fsz)
1502 pos = sz - fsz;
1503 if (fsz == 0)
1504 return pos;
1505 int fsizeminusone = (fsz - 1) * sizeof(UChar);
1506 const UChar* fdata = f.data();
1507 for (const UChar* c = data() + pos; c >= data(); c--) {
1508 if (*c == *fdata && !memcmp(c + 1, fdata + 1, fsizeminusone))
1509 return static_cast<int>(c - data());
1510 }
1511
1512 return -1;
1513}
1514
1515int UString::rfind(UChar ch, int pos) const
1516{
1517 if (isEmpty())
1518 return -1;
1519 if (pos + 1 >= size())
1520 pos = size() - 1;
1521 for (const UChar* c = data() + pos; c >= data(); c--) {
1522 if (*c == ch)
1523 return static_cast<int>(c - data());
1524 }
1525
1526 return -1;
1527}
1528
1529UString UString::substr(int pos, int len) const
1530{
1531 int s = size();
1532
1533 if (pos < 0)
1534 pos = 0;
1535 else if (pos >= s)
1536 pos = s;
1537 if (len < 0)
1538 len = s;
1539 if (pos + len >= s)
1540 len = s - pos;
1541
1542 if (pos == 0 && len == s)
1543 return *this;
1544
1545 return UString(Rep::create(m_rep, pos, len));
1546}
1547
1548bool operator==(const UString& s1, const UString& s2)
1549{
1550 int size = s1.size();
1551 switch (size) {
1552 case 0:
1553 return !s2.size();
1554 case 1:
1555 return s2.size() == 1 && s1.data()[0] == s2.data()[0];
1556 default:
1557 return s2.size() == size && memcmp(s1.data(), s2.data(), size * sizeof(UChar)) == 0;
1558 }
1559}
1560
1561bool operator==(const UString& s1, const char *s2)
1562{
1563 if (s2 == 0)
1564 return s1.isEmpty();
1565
1566 const UChar* u = s1.data();
1567 const UChar* uend = u + s1.size();
1568 while (u != uend && *s2) {
1569 if (u[0] != (unsigned char)*s2)
1570 return false;
1571 s2++;
1572 u++;
1573 }
1574
1575 return u == uend && *s2 == 0;
1576}
1577
1578bool operator<(const UString& s1, const UString& s2)
1579{
1580 const int l1 = s1.size();
1581 const int l2 = s2.size();
1582 const int lmin = l1 < l2 ? l1 : l2;
1583 const UChar* c1 = s1.data();
1584 const UChar* c2 = s2.data();
1585 int l = 0;
1586 while (l < lmin && *c1 == *c2) {
1587 c1++;
1588 c2++;
1589 l++;
1590 }
1591 if (l < lmin)
1592 return (c1[0] < c2[0]);
1593
1594 return (l1 < l2);
1595}
1596
1597bool operator>(const UString& s1, const UString& s2)
1598{
1599 const int l1 = s1.size();
1600 const int l2 = s2.size();
1601 const int lmin = l1 < l2 ? l1 : l2;
1602 const UChar* c1 = s1.data();
1603 const UChar* c2 = s2.data();
1604 int l = 0;
1605 while (l < lmin && *c1 == *c2) {
1606 c1++;
1607 c2++;
1608 l++;
1609 }
1610 if (l < lmin)
1611 return (c1[0] > c2[0]);
1612
1613 return (l1 > l2);
1614}
1615
1616int compare(const UString& s1, const UString& s2)
1617{
1618 const int l1 = s1.size();
1619 const int l2 = s2.size();
1620 const int lmin = l1 < l2 ? l1 : l2;
1621 const UChar* c1 = s1.data();
1622 const UChar* c2 = s2.data();
1623 int l = 0;
1624 while (l < lmin && *c1 == *c2) {
1625 c1++;
1626 c2++;
1627 l++;
1628 }
1629
1630 if (l < lmin)
1631 return (c1[0] > c2[0]) ? 1 : -1;
1632
1633 if (l1 == l2)
1634 return 0;
1635
1636 return (l1 > l2) ? 1 : -1;
1637}
1638
1639bool equal(const UString::Rep* r, const UString::Rep* b)
1640{
1641 int length = r->len;
1642 if (length != b->len)
1643 return false;
1644 const UChar* d = r->data();
1645 const UChar* s = b->data();
1646 for (int i = 0; i != length; ++i) {
1647 if (d[i] != s[i])
1648 return false;
1649 }
1650 return true;
1651}
1652
1653CString UString::UTF8String(bool strict) const
1654{
1655 // Allocate a buffer big enough to hold all the characters.
1656 const int length = size();
1657 Vector<char, 1024> buffer(length * 3);
1658
1659 // Convert to runs of 8-bit characters.
1660 char* p = buffer.data();
1661 const UChar* d = reinterpret_cast<const UChar*>(&data()[0]);
1662 ConversionResult result = convertUTF16ToUTF8(&d, d + length, &p, p + buffer.size(), strict);
1663 if (result != conversionOK)
1664 return CString();
1665
1666 return CString(buffer.data(), p - buffer.data());
1667}
1668
1669// For use in error handling code paths -- having this not be inlined helps avoid PIC branches to fetch the global on Mac OS X.
1670NEVER_INLINE void UString::makeNull()
1671{
1672 m_rep = &Rep::null();
1673}
1674
1675// For use in error handling code paths -- having this not be inlined helps avoid PIC branches to fetch the global on Mac OS X.
1676NEVER_INLINE UString::Rep* UString::nullRep()
1677{
1678 return &Rep::null();
1679}
1680
1681} // namespace JSC
Note: See TracBrowser for help on using the repository browser.