xpcom/string/src/nsTSubstring.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6 #include "mozilla/MemoryReporting.h"
michael@0 7 #include "double-conversion.h"
michael@0 8
michael@0 9 using double_conversion::DoubleToStringConverter;
michael@0 10
michael@0 11 #ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
michael@0 12 nsTSubstring_CharT::nsTSubstring_CharT( char_type *data, size_type length,
michael@0 13 uint32_t flags)
michael@0 14 : mData(data),
michael@0 15 mLength(length),
michael@0 16 mFlags(flags)
michael@0 17 {
michael@0 18 if (flags & F_OWNED) {
michael@0 19 STRING_STAT_INCREMENT(Adopt);
michael@0 20 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 21 NS_LogCtor(mData, "StringAdopt", 1);
michael@0 22 #endif
michael@0 23 }
michael@0 24 }
michael@0 25 #endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */
michael@0 26
michael@0 27 /**
michael@0 28 * helper function for down-casting a nsTSubstring to a nsTFixedString.
michael@0 29 */
michael@0 30 inline const nsTFixedString_CharT*
michael@0 31 AsFixedString( const nsTSubstring_CharT* s )
michael@0 32 {
michael@0 33 return static_cast<const nsTFixedString_CharT*>(s);
michael@0 34 }
michael@0 35
michael@0 36
michael@0 37 /**
michael@0 38 * this function is called to prepare mData for writing. the given capacity
michael@0 39 * indicates the required minimum storage size for mData, in sizeof(char_type)
michael@0 40 * increments. this function returns true if the operation succeeds. it also
michael@0 41 * returns the old data and old flags members if mData is newly allocated.
michael@0 42 * the old data must be released by the caller.
michael@0 43 */
michael@0 44 bool
michael@0 45 nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, uint32_t* oldFlags )
michael@0 46 {
michael@0 47 // initialize to no old data
michael@0 48 *oldData = nullptr;
michael@0 49 *oldFlags = 0;
michael@0 50
michael@0 51 size_type curCapacity = Capacity();
michael@0 52
michael@0 53 // If |capacity > kMaxCapacity|, then our doubling algorithm may not be
michael@0 54 // able to allocate it. Just bail out in cases like that. We don't want
michael@0 55 // to be allocating 2GB+ strings anyway.
michael@0 56 PR_STATIC_ASSERT((sizeof(nsStringBuffer) & 0x1) == 0);
michael@0 57 const size_type kMaxCapacity =
michael@0 58 (size_type(-1)/2 - sizeof(nsStringBuffer)) / sizeof(char_type) - 2;
michael@0 59 if (capacity > kMaxCapacity) {
michael@0 60 // Also assert for |capacity| equal to |size_type(-1)|, since we used to
michael@0 61 // use that value to flag immutability.
michael@0 62 NS_ASSERTION(capacity != size_type(-1), "Bogus capacity");
michael@0 63 return false;
michael@0 64 }
michael@0 65
michael@0 66 // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
michael@0 67 // need to allocate a new buffer. We cannot use the existing buffer even
michael@0 68 // though it might be large enough.
michael@0 69
michael@0 70 if (curCapacity != 0)
michael@0 71 {
michael@0 72 if (capacity <= curCapacity) {
michael@0 73 mFlags &= ~F_VOIDED; // mutation clears voided flag
michael@0 74 return true;
michael@0 75 }
michael@0 76
michael@0 77 // Use doubling algorithm when forced to increase available capacity.
michael@0 78 size_type temp = curCapacity;
michael@0 79 while (temp < capacity)
michael@0 80 temp <<= 1;
michael@0 81 NS_ASSERTION(XPCOM_MIN(temp, kMaxCapacity) >= capacity,
michael@0 82 "should have hit the early return at the top");
michael@0 83 capacity = XPCOM_MIN(temp, kMaxCapacity);
michael@0 84 }
michael@0 85
michael@0 86 //
michael@0 87 // several cases:
michael@0 88 //
michael@0 89 // (1) we have a shared buffer (mFlags & F_SHARED)
michael@0 90 // (2) we have an owned buffer (mFlags & F_OWNED)
michael@0 91 // (3) we have a fixed buffer (mFlags & F_FIXED)
michael@0 92 // (4) we have a readonly buffer
michael@0 93 //
michael@0 94 // requiring that we in some cases preserve the data before creating
michael@0 95 // a new buffer complicates things just a bit ;-)
michael@0 96 //
michael@0 97
michael@0 98 size_type storageSize = (capacity + 1) * sizeof(char_type);
michael@0 99
michael@0 100 // case #1
michael@0 101 if (mFlags & F_SHARED)
michael@0 102 {
michael@0 103 nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
michael@0 104 if (!hdr->IsReadonly())
michael@0 105 {
michael@0 106 nsStringBuffer *newHdr = nsStringBuffer::Realloc(hdr, storageSize);
michael@0 107 if (!newHdr)
michael@0 108 return false; // out-of-memory (original header left intact)
michael@0 109
michael@0 110 hdr = newHdr;
michael@0 111 mData = (char_type*) hdr->Data();
michael@0 112 mFlags &= ~F_VOIDED; // mutation clears voided flag
michael@0 113 return true;
michael@0 114 }
michael@0 115 }
michael@0 116
michael@0 117 char_type* newData;
michael@0 118 uint32_t newDataFlags;
michael@0 119
michael@0 120 // if we have a fixed buffer of sufficient size, then use it. this helps
michael@0 121 // avoid heap allocations.
michael@0 122 if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity))
michael@0 123 {
michael@0 124 newData = AsFixedString(this)->mFixedBuf;
michael@0 125 newDataFlags = F_TERMINATED | F_FIXED;
michael@0 126 }
michael@0 127 else
michael@0 128 {
michael@0 129 // if we reach here then, we must allocate a new buffer. we cannot
michael@0 130 // make use of our F_OWNED or F_FIXED buffers because they are not
michael@0 131 // large enough.
michael@0 132
michael@0 133 nsStringBuffer* newHdr =
michael@0 134 nsStringBuffer::Alloc(storageSize).take();
michael@0 135 if (!newHdr)
michael@0 136 return false; // we are still in a consistent state
michael@0 137
michael@0 138 newData = (char_type*) newHdr->Data();
michael@0 139 newDataFlags = F_TERMINATED | F_SHARED;
michael@0 140 }
michael@0 141
michael@0 142 // save old data and flags
michael@0 143 *oldData = mData;
michael@0 144 *oldFlags = mFlags;
michael@0 145
michael@0 146 mData = newData;
michael@0 147 SetDataFlags(newDataFlags);
michael@0 148
michael@0 149 // mLength does not change
michael@0 150
michael@0 151 // though we are not necessarily terminated at the moment, now is probably
michael@0 152 // still the best time to set F_TERMINATED.
michael@0 153
michael@0 154 return true;
michael@0 155 }
michael@0 156
michael@0 157 void
michael@0 158 nsTSubstring_CharT::Finalize()
michael@0 159 {
michael@0 160 ::ReleaseData(mData, mFlags);
michael@0 161 // mData, mLength, and mFlags are purposefully left dangling
michael@0 162 }
michael@0 163
michael@0 164 bool
michael@0 165 nsTSubstring_CharT::ReplacePrepInternal(index_type cutStart, size_type cutLen,
michael@0 166 size_type fragLen, size_type newLen)
michael@0 167 {
michael@0 168 char_type* oldData;
michael@0 169 uint32_t oldFlags;
michael@0 170 if (!MutatePrep(newLen, &oldData, &oldFlags))
michael@0 171 return false; // out-of-memory
michael@0 172
michael@0 173 if (oldData)
michael@0 174 {
michael@0 175 // determine whether or not we need to copy part of the old string
michael@0 176 // over to the new string.
michael@0 177
michael@0 178 if (cutStart > 0)
michael@0 179 {
michael@0 180 // copy prefix from old string
michael@0 181 char_traits::copy(mData, oldData, cutStart);
michael@0 182 }
michael@0 183
michael@0 184 if (cutStart + cutLen < mLength)
michael@0 185 {
michael@0 186 // copy suffix from old string to new offset
michael@0 187 size_type from = cutStart + cutLen;
michael@0 188 size_type fromLen = mLength - from;
michael@0 189 uint32_t to = cutStart + fragLen;
michael@0 190 char_traits::copy(mData + to, oldData + from, fromLen);
michael@0 191 }
michael@0 192
michael@0 193 ::ReleaseData(oldData, oldFlags);
michael@0 194 }
michael@0 195 else
michael@0 196 {
michael@0 197 // original data remains intact
michael@0 198
michael@0 199 // determine whether or not we need to move part of the existing string
michael@0 200 // to make room for the requested hole.
michael@0 201 if (fragLen != cutLen && cutStart + cutLen < mLength)
michael@0 202 {
michael@0 203 uint32_t from = cutStart + cutLen;
michael@0 204 uint32_t fromLen = mLength - from;
michael@0 205 uint32_t to = cutStart + fragLen;
michael@0 206 char_traits::move(mData + to, mData + from, fromLen);
michael@0 207 }
michael@0 208 }
michael@0 209
michael@0 210 // add null terminator (mutable mData always has room for the null-
michael@0 211 // terminator).
michael@0 212 mData[newLen] = char_type(0);
michael@0 213 mLength = newLen;
michael@0 214
michael@0 215 return true;
michael@0 216 }
michael@0 217
michael@0 218 nsTSubstring_CharT::size_type
michael@0 219 nsTSubstring_CharT::Capacity() const
michael@0 220 {
michael@0 221 // return 0 to indicate an immutable or 0-sized buffer
michael@0 222
michael@0 223 size_type capacity;
michael@0 224 if (mFlags & F_SHARED)
michael@0 225 {
michael@0 226 // if the string is readonly, then we pretend that it has no capacity.
michael@0 227 nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
michael@0 228 if (hdr->IsReadonly())
michael@0 229 capacity = 0;
michael@0 230 else {
michael@0 231 capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
michael@0 232 }
michael@0 233 }
michael@0 234 else if (mFlags & F_FIXED)
michael@0 235 {
michael@0 236 capacity = AsFixedString(this)->mFixedCapacity;
michael@0 237 }
michael@0 238 else if (mFlags & F_OWNED)
michael@0 239 {
michael@0 240 // we don't store the capacity of an adopted buffer because that would
michael@0 241 // require an additional member field. the best we can do is base the
michael@0 242 // capacity on our length. remains to be seen if this is the right
michael@0 243 // trade-off.
michael@0 244 capacity = mLength;
michael@0 245 }
michael@0 246 else
michael@0 247 {
michael@0 248 capacity = 0;
michael@0 249 }
michael@0 250
michael@0 251 return capacity;
michael@0 252 }
michael@0 253
michael@0 254 bool
michael@0 255 nsTSubstring_CharT::EnsureMutable( size_type newLen )
michael@0 256 {
michael@0 257 if (newLen == size_type(-1) || newLen == mLength)
michael@0 258 {
michael@0 259 if (mFlags & (F_FIXED | F_OWNED))
michael@0 260 return true;
michael@0 261 if ((mFlags & F_SHARED) && !nsStringBuffer::FromData(mData)->IsReadonly())
michael@0 262 return true;
michael@0 263
michael@0 264 newLen = mLength;
michael@0 265 }
michael@0 266 return SetLength(newLen, fallible_t());
michael@0 267 }
michael@0 268
michael@0 269 // ---------------------------------------------------------------------------
michael@0 270
michael@0 271 // This version of Assign is optimized for single-character assignment.
michael@0 272 void
michael@0 273 nsTSubstring_CharT::Assign( char_type c )
michael@0 274 {
michael@0 275 if (!ReplacePrep(0, mLength, 1))
michael@0 276 NS_ABORT_OOM(mLength);
michael@0 277
michael@0 278 *mData = c;
michael@0 279 }
michael@0 280
michael@0 281 bool
michael@0 282 nsTSubstring_CharT::Assign( char_type c, const fallible_t& )
michael@0 283 {
michael@0 284 if (!ReplacePrep(0, mLength, 1))
michael@0 285 return false;
michael@0 286
michael@0 287 *mData = c;
michael@0 288 return true;
michael@0 289 }
michael@0 290
michael@0 291 void
michael@0 292 nsTSubstring_CharT::Assign( const char_type* data )
michael@0 293 {
michael@0 294 if (!Assign(data, size_type(-1), fallible_t()))
michael@0 295 NS_ABORT_OOM(char_traits::length(data));
michael@0 296 }
michael@0 297
michael@0 298 void
michael@0 299 nsTSubstring_CharT::Assign( const char_type* data, size_type length )
michael@0 300 {
michael@0 301 if (!Assign(data, length, fallible_t()))
michael@0 302 NS_ABORT_OOM(length);
michael@0 303 }
michael@0 304
michael@0 305 bool
michael@0 306 nsTSubstring_CharT::Assign( const char_type* data, size_type length, const fallible_t& )
michael@0 307 {
michael@0 308 if (!data || length == 0)
michael@0 309 {
michael@0 310 Truncate();
michael@0 311 return true;
michael@0 312 }
michael@0 313
michael@0 314 if (length == size_type(-1))
michael@0 315 length = char_traits::length(data);
michael@0 316
michael@0 317 if (IsDependentOn(data, data + length))
michael@0 318 {
michael@0 319 return Assign(string_type(data, length), fallible_t());
michael@0 320 }
michael@0 321
michael@0 322 if (!ReplacePrep(0, mLength, length))
michael@0 323 return false;
michael@0 324
michael@0 325 char_traits::copy(mData, data, length);
michael@0 326 return true;
michael@0 327 }
michael@0 328
michael@0 329 void
michael@0 330 nsTSubstring_CharT::AssignASCII( const char* data, size_type length )
michael@0 331 {
michael@0 332 if (!AssignASCII(data, length, fallible_t()))
michael@0 333 NS_ABORT_OOM(length);
michael@0 334 }
michael@0 335
michael@0 336 bool
michael@0 337 nsTSubstring_CharT::AssignASCII( const char* data, size_type length, const fallible_t& )
michael@0 338 {
michael@0 339 // A Unicode string can't depend on an ASCII string buffer,
michael@0 340 // so this dependence check only applies to CStrings.
michael@0 341 #ifdef CharT_is_char
michael@0 342 if (IsDependentOn(data, data + length))
michael@0 343 {
michael@0 344 return Assign(string_type(data, length), fallible_t());
michael@0 345 }
michael@0 346 #endif
michael@0 347
michael@0 348 if (!ReplacePrep(0, mLength, length))
michael@0 349 return false;
michael@0 350
michael@0 351 char_traits::copyASCII(mData, data, length);
michael@0 352 return true;
michael@0 353 }
michael@0 354
michael@0 355 void
michael@0 356 nsTSubstring_CharT::AssignLiteral( const char_type* data, size_type length )
michael@0 357 {
michael@0 358 ::ReleaseData(mData, mFlags);
michael@0 359 mData = const_cast<char_type*>(data);
michael@0 360 mLength = length;
michael@0 361 SetDataFlags(F_TERMINATED | F_LITERAL);
michael@0 362 }
michael@0 363
michael@0 364 void
michael@0 365 nsTSubstring_CharT::Assign( const self_type& str )
michael@0 366 {
michael@0 367 if (!Assign(str, fallible_t()))
michael@0 368 NS_ABORT_OOM(str.Length());
michael@0 369 }
michael@0 370
michael@0 371 bool
michael@0 372 nsTSubstring_CharT::Assign( const self_type& str, const fallible_t& )
michael@0 373 {
michael@0 374 // |str| could be sharable. we need to check its flags to know how to
michael@0 375 // deal with it.
michael@0 376
michael@0 377 if (&str == this)
michael@0 378 return true;
michael@0 379
michael@0 380 if (!str.mLength)
michael@0 381 {
michael@0 382 Truncate();
michael@0 383 mFlags |= str.mFlags & F_VOIDED;
michael@0 384 return true;
michael@0 385 }
michael@0 386
michael@0 387 if (str.mFlags & F_SHARED)
michael@0 388 {
michael@0 389 // nice! we can avoid a string copy :-)
michael@0 390
michael@0 391 // |str| should be null-terminated
michael@0 392 NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated");
michael@0 393
michael@0 394 ::ReleaseData(mData, mFlags);
michael@0 395
michael@0 396 mData = str.mData;
michael@0 397 mLength = str.mLength;
michael@0 398 SetDataFlags(F_TERMINATED | F_SHARED);
michael@0 399
michael@0 400 // get an owning reference to the mData
michael@0 401 nsStringBuffer::FromData(mData)->AddRef();
michael@0 402 return true;
michael@0 403 }
michael@0 404 else if (str.mFlags & F_LITERAL)
michael@0 405 {
michael@0 406 NS_ABORT_IF_FALSE(str.mFlags & F_TERMINATED, "Unterminated literal");
michael@0 407
michael@0 408 AssignLiteral(str.mData, str.mLength);
michael@0 409 return true;
michael@0 410 }
michael@0 411
michael@0 412 // else, treat this like an ordinary assignment.
michael@0 413 return Assign(str.Data(), str.Length(), fallible_t());
michael@0 414 }
michael@0 415
michael@0 416 void
michael@0 417 nsTSubstring_CharT::Assign( const substring_tuple_type& tuple )
michael@0 418 {
michael@0 419 if (!Assign(tuple, fallible_t()))
michael@0 420 NS_ABORT_OOM(tuple.Length());
michael@0 421 }
michael@0 422
michael@0 423 bool
michael@0 424 nsTSubstring_CharT::Assign( const substring_tuple_type& tuple, const fallible_t& )
michael@0 425 {
michael@0 426 if (tuple.IsDependentOn(mData, mData + mLength))
michael@0 427 {
michael@0 428 // take advantage of sharing here...
michael@0 429 return Assign(string_type(tuple), fallible_t());
michael@0 430 }
michael@0 431
michael@0 432 size_type length = tuple.Length();
michael@0 433
michael@0 434 // don't use ReplacePrep here because it changes the length
michael@0 435 char_type* oldData;
michael@0 436 uint32_t oldFlags;
michael@0 437 if (!MutatePrep(length, &oldData, &oldFlags))
michael@0 438 return false;
michael@0 439
michael@0 440 if (oldData)
michael@0 441 ::ReleaseData(oldData, oldFlags);
michael@0 442
michael@0 443 tuple.WriteTo(mData, length);
michael@0 444 mData[length] = 0;
michael@0 445 mLength = length;
michael@0 446 return true;
michael@0 447 }
michael@0 448
michael@0 449 void
michael@0 450 nsTSubstring_CharT::Adopt( char_type* data, size_type length )
michael@0 451 {
michael@0 452 if (data)
michael@0 453 {
michael@0 454 ::ReleaseData(mData, mFlags);
michael@0 455
michael@0 456 if (length == size_type(-1))
michael@0 457 length = char_traits::length(data);
michael@0 458
michael@0 459 mData = data;
michael@0 460 mLength = length;
michael@0 461 SetDataFlags(F_TERMINATED | F_OWNED);
michael@0 462
michael@0 463 STRING_STAT_INCREMENT(Adopt);
michael@0 464 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 465 // Treat this as construction of a "StringAdopt" object for leak
michael@0 466 // tracking purposes.
michael@0 467 NS_LogCtor(mData, "StringAdopt", 1);
michael@0 468 #endif // NS_BUILD_REFCNT_LOGGING
michael@0 469 }
michael@0 470 else
michael@0 471 {
michael@0 472 SetIsVoid(true);
michael@0 473 }
michael@0 474 }
michael@0 475
michael@0 476
michael@0 477 // This version of Replace is optimized for single-character replacement.
michael@0 478 void
michael@0 479 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c )
michael@0 480 {
michael@0 481 cutStart = XPCOM_MIN(cutStart, Length());
michael@0 482
michael@0 483 if (ReplacePrep(cutStart, cutLength, 1))
michael@0 484 mData[cutStart] = c;
michael@0 485 }
michael@0 486
michael@0 487 bool
michael@0 488 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c, const mozilla::fallible_t& )
michael@0 489 {
michael@0 490 cutStart = XPCOM_MIN(cutStart, Length());
michael@0 491
michael@0 492 if (!ReplacePrep(cutStart, cutLength, 1))
michael@0 493 return false;
michael@0 494
michael@0 495 mData[cutStart] = c;
michael@0 496
michael@0 497 return true;
michael@0 498 }
michael@0 499
michael@0 500 void
michael@0 501 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length )
michael@0 502 {
michael@0 503 if (!Replace(cutStart, cutLength, data, length, mozilla::fallible_t()))
michael@0 504 {
michael@0 505 NS_ABORT_OOM(Length() - cutLength + 1);
michael@0 506 }
michael@0 507 }
michael@0 508
michael@0 509 bool
michael@0 510 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length, const mozilla::fallible_t& )
michael@0 511 {
michael@0 512 // unfortunately, some callers pass null :-(
michael@0 513 if (!data)
michael@0 514 {
michael@0 515 length = 0;
michael@0 516 }
michael@0 517 else
michael@0 518 {
michael@0 519 if (length == size_type(-1))
michael@0 520 length = char_traits::length(data);
michael@0 521
michael@0 522 if (IsDependentOn(data, data + length))
michael@0 523 {
michael@0 524 nsTAutoString_CharT temp(data, length);
michael@0 525 return Replace(cutStart, cutLength, temp, mozilla::fallible_t());
michael@0 526 }
michael@0 527 }
michael@0 528
michael@0 529 cutStart = XPCOM_MIN(cutStart, Length());
michael@0 530
michael@0 531 bool ok = ReplacePrep(cutStart, cutLength, length);
michael@0 532 if (!ok)
michael@0 533 return false;
michael@0 534
michael@0 535 if (length > 0)
michael@0 536 char_traits::copy(mData + cutStart, data, length);
michael@0 537
michael@0 538 return true;
michael@0 539 }
michael@0 540
michael@0 541 void
michael@0 542 nsTSubstring_CharT::ReplaceASCII( index_type cutStart, size_type cutLength, const char* data, size_type length )
michael@0 543 {
michael@0 544 if (length == size_type(-1))
michael@0 545 length = strlen(data);
michael@0 546
michael@0 547 // A Unicode string can't depend on an ASCII string buffer,
michael@0 548 // so this dependence check only applies to CStrings.
michael@0 549 #ifdef CharT_is_char
michael@0 550 if (IsDependentOn(data, data + length))
michael@0 551 {
michael@0 552 nsTAutoString_CharT temp(data, length);
michael@0 553 Replace(cutStart, cutLength, temp);
michael@0 554 return;
michael@0 555 }
michael@0 556 #endif
michael@0 557
michael@0 558 cutStart = XPCOM_MIN(cutStart, Length());
michael@0 559
michael@0 560 if (ReplacePrep(cutStart, cutLength, length) && length > 0)
michael@0 561 char_traits::copyASCII(mData + cutStart, data, length);
michael@0 562 }
michael@0 563
michael@0 564 void
michael@0 565 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& tuple )
michael@0 566 {
michael@0 567 if (tuple.IsDependentOn(mData, mData + mLength))
michael@0 568 {
michael@0 569 nsTAutoString_CharT temp(tuple);
michael@0 570 Replace(cutStart, cutLength, temp);
michael@0 571 return;
michael@0 572 }
michael@0 573
michael@0 574 size_type length = tuple.Length();
michael@0 575
michael@0 576 cutStart = XPCOM_MIN(cutStart, Length());
michael@0 577
michael@0 578 if (ReplacePrep(cutStart, cutLength, length) && length > 0)
michael@0 579 tuple.WriteTo(mData + cutStart, length);
michael@0 580 }
michael@0 581
michael@0 582 void
michael@0 583 nsTSubstring_CharT::ReplaceLiteral( index_type cutStart, size_type cutLength, const char_type* data, size_type length )
michael@0 584 {
michael@0 585 cutStart = XPCOM_MIN(cutStart, Length());
michael@0 586
michael@0 587 if (!cutStart && cutLength == Length())
michael@0 588 AssignLiteral(data, length);
michael@0 589 else if (ReplacePrep(cutStart, cutLength, length) && length > 0)
michael@0 590 char_traits::copy(mData + cutStart, data, length);
michael@0 591 }
michael@0 592
michael@0 593 void
michael@0 594 nsTSubstring_CharT::SetCapacity( size_type capacity )
michael@0 595 {
michael@0 596 if (!SetCapacity(capacity, fallible_t()))
michael@0 597 NS_ABORT_OOM(capacity);
michael@0 598 }
michael@0 599
michael@0 600 bool
michael@0 601 nsTSubstring_CharT::SetCapacity( size_type capacity, const fallible_t& )
michael@0 602 {
michael@0 603 // capacity does not include room for the terminating null char
michael@0 604
michael@0 605 // if our capacity is reduced to zero, then free our buffer.
michael@0 606 if (capacity == 0)
michael@0 607 {
michael@0 608 ::ReleaseData(mData, mFlags);
michael@0 609 mData = char_traits::sEmptyBuffer;
michael@0 610 mLength = 0;
michael@0 611 SetDataFlags(F_TERMINATED);
michael@0 612 return true;
michael@0 613 }
michael@0 614
michael@0 615 char_type* oldData;
michael@0 616 uint32_t oldFlags;
michael@0 617 if (!MutatePrep(capacity, &oldData, &oldFlags))
michael@0 618 return false; // out-of-memory
michael@0 619
michael@0 620 // compute new string length
michael@0 621 size_type newLen = XPCOM_MIN(mLength, capacity);
michael@0 622
michael@0 623 if (oldData)
michael@0 624 {
michael@0 625 // preserve old data
michael@0 626 if (mLength > 0)
michael@0 627 char_traits::copy(mData, oldData, newLen);
michael@0 628
michael@0 629 ::ReleaseData(oldData, oldFlags);
michael@0 630 }
michael@0 631
michael@0 632 // adjust mLength if our buffer shrunk down in size
michael@0 633 if (newLen < mLength)
michael@0 634 mLength = newLen;
michael@0 635
michael@0 636 // always null-terminate here, even if the buffer got longer. this is
michael@0 637 // for backwards compat with the old string implementation.
michael@0 638 mData[capacity] = char_type(0);
michael@0 639
michael@0 640 return true;
michael@0 641 }
michael@0 642
michael@0 643 void
michael@0 644 nsTSubstring_CharT::SetLength( size_type length )
michael@0 645 {
michael@0 646 SetCapacity(length);
michael@0 647 mLength = length;
michael@0 648 }
michael@0 649
michael@0 650 bool
michael@0 651 nsTSubstring_CharT::SetLength( size_type length, const fallible_t& )
michael@0 652 {
michael@0 653 if (!SetCapacity(length, fallible_t()))
michael@0 654 return false;
michael@0 655
michael@0 656 mLength = length;
michael@0 657 return true;
michael@0 658 }
michael@0 659
michael@0 660 void
michael@0 661 nsTSubstring_CharT::SetIsVoid( bool val )
michael@0 662 {
michael@0 663 if (val)
michael@0 664 {
michael@0 665 Truncate();
michael@0 666 mFlags |= F_VOIDED;
michael@0 667 }
michael@0 668 else
michael@0 669 {
michael@0 670 mFlags &= ~F_VOIDED;
michael@0 671 }
michael@0 672 }
michael@0 673
michael@0 674 bool
michael@0 675 nsTSubstring_CharT::Equals( const self_type& str ) const
michael@0 676 {
michael@0 677 return mLength == str.mLength && char_traits::compare(mData, str.mData, mLength) == 0;
michael@0 678 }
michael@0 679
michael@0 680 bool
michael@0 681 nsTSubstring_CharT::Equals( const self_type& str, const comparator_type& comp ) const
michael@0 682 {
michael@0 683 return mLength == str.mLength && comp(mData, str.mData, mLength, str.mLength) == 0;
michael@0 684 }
michael@0 685
michael@0 686 bool
michael@0 687 nsTSubstring_CharT::Equals( const char_type* data ) const
michael@0 688 {
michael@0 689 // unfortunately, some callers pass null :-(
michael@0 690 if (!data)
michael@0 691 {
michael@0 692 NS_NOTREACHED("null data pointer");
michael@0 693 return mLength == 0;
michael@0 694 }
michael@0 695
michael@0 696 // XXX avoid length calculation?
michael@0 697 size_type length = char_traits::length(data);
michael@0 698 return mLength == length && char_traits::compare(mData, data, mLength) == 0;
michael@0 699 }
michael@0 700
michael@0 701 bool
michael@0 702 nsTSubstring_CharT::Equals( const char_type* data, const comparator_type& comp ) const
michael@0 703 {
michael@0 704 // unfortunately, some callers pass null :-(
michael@0 705 if (!data)
michael@0 706 {
michael@0 707 NS_NOTREACHED("null data pointer");
michael@0 708 return mLength == 0;
michael@0 709 }
michael@0 710
michael@0 711 // XXX avoid length calculation?
michael@0 712 size_type length = char_traits::length(data);
michael@0 713 return mLength == length && comp(mData, data, mLength, length) == 0;
michael@0 714 }
michael@0 715
michael@0 716 bool
michael@0 717 nsTSubstring_CharT::EqualsASCII( const char* data, size_type len ) const
michael@0 718 {
michael@0 719 return mLength == len && char_traits::compareASCII(mData, data, len) == 0;
michael@0 720 }
michael@0 721
michael@0 722 bool
michael@0 723 nsTSubstring_CharT::EqualsASCII( const char* data ) const
michael@0 724 {
michael@0 725 return char_traits::compareASCIINullTerminated(mData, mLength, data) == 0;
michael@0 726 }
michael@0 727
michael@0 728 bool
michael@0 729 nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data, size_type len ) const
michael@0 730 {
michael@0 731 return mLength == len && char_traits::compareLowerCaseToASCII(mData, data, len) == 0;
michael@0 732 }
michael@0 733
michael@0 734 bool
michael@0 735 nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data ) const
michael@0 736 {
michael@0 737 return char_traits::compareLowerCaseToASCIINullTerminated(mData, mLength, data) == 0;
michael@0 738 }
michael@0 739
michael@0 740 nsTSubstring_CharT::size_type
michael@0 741 nsTSubstring_CharT::CountChar( char_type c ) const
michael@0 742 {
michael@0 743 const char_type *start = mData;
michael@0 744 const char_type *end = mData + mLength;
michael@0 745
michael@0 746 return NS_COUNT(start, end, c);
michael@0 747 }
michael@0 748
michael@0 749 int32_t
michael@0 750 nsTSubstring_CharT::FindChar( char_type c, index_type offset ) const
michael@0 751 {
michael@0 752 if (offset < mLength)
michael@0 753 {
michael@0 754 const char_type* result = char_traits::find(mData + offset, mLength - offset, c);
michael@0 755 if (result)
michael@0 756 return result - mData;
michael@0 757 }
michael@0 758 return -1;
michael@0 759 }
michael@0 760
michael@0 761 void
michael@0 762 nsTSubstring_CharT::StripChar( char_type aChar, int32_t aOffset )
michael@0 763 {
michael@0 764 if (mLength == 0 || aOffset >= int32_t(mLength))
michael@0 765 return;
michael@0 766
michael@0 767 if (!EnsureMutable()) // XXX do this lazily?
michael@0 768 NS_ABORT_OOM(mLength);
michael@0 769
michael@0 770 // XXX(darin): this code should defer writing until necessary.
michael@0 771
michael@0 772 char_type* to = mData + aOffset;
michael@0 773 char_type* from = mData + aOffset;
michael@0 774 char_type* end = mData + mLength;
michael@0 775
michael@0 776 while (from < end)
michael@0 777 {
michael@0 778 char_type theChar = *from++;
michael@0 779 if (aChar != theChar)
michael@0 780 *to++ = theChar;
michael@0 781 }
michael@0 782 *to = char_type(0); // add the null
michael@0 783 mLength = to - mData;
michael@0 784 }
michael@0 785
michael@0 786 void
michael@0 787 nsTSubstring_CharT::StripChars( const char_type* aChars, uint32_t aOffset )
michael@0 788 {
michael@0 789 if (aOffset >= uint32_t(mLength))
michael@0 790 return;
michael@0 791
michael@0 792 if (!EnsureMutable()) // XXX do this lazily?
michael@0 793 NS_ABORT_OOM(mLength);
michael@0 794
michael@0 795 // XXX(darin): this code should defer writing until necessary.
michael@0 796
michael@0 797 char_type* to = mData + aOffset;
michael@0 798 char_type* from = mData + aOffset;
michael@0 799 char_type* end = mData + mLength;
michael@0 800
michael@0 801 while (from < end)
michael@0 802 {
michael@0 803 char_type theChar = *from++;
michael@0 804 const char_type* test = aChars;
michael@0 805
michael@0 806 for (; *test && *test != theChar; ++test);
michael@0 807
michael@0 808 if (!*test) {
michael@0 809 // Not stripped, copy this char.
michael@0 810 *to++ = theChar;
michael@0 811 }
michael@0 812 }
michael@0 813 *to = char_type(0); // add the null
michael@0 814 mLength = to - mData;
michael@0 815 }
michael@0 816
michael@0 817 int
michael@0 818 nsTSubstring_CharT::AppendFunc(void* arg, const char* s, uint32_t len)
michael@0 819 {
michael@0 820 self_type* self = static_cast<self_type*>(arg);
michael@0 821
michael@0 822 // NSPR sends us the final null terminator even though we don't want it
michael@0 823 if (len && s[len - 1] == '\0') {
michael@0 824 --len;
michael@0 825 }
michael@0 826
michael@0 827 self->AppendASCII(s, len);
michael@0 828
michael@0 829 return len;
michael@0 830 }
michael@0 831
michael@0 832 void nsTSubstring_CharT::AppendPrintf( const char* format, ...)
michael@0 833 {
michael@0 834 va_list ap;
michael@0 835 va_start(ap, format);
michael@0 836 uint32_t r = PR_vsxprintf(AppendFunc, this, format, ap);
michael@0 837 if (r == (uint32_t) -1)
michael@0 838 NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf");
michael@0 839 va_end(ap);
michael@0 840 }
michael@0 841
michael@0 842 void nsTSubstring_CharT::AppendPrintf( const char* format, va_list ap )
michael@0 843 {
michael@0 844 uint32_t r = PR_vsxprintf(AppendFunc, this, format, ap);
michael@0 845 if (r == (uint32_t) -1)
michael@0 846 NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf");
michael@0 847 }
michael@0 848
michael@0 849 /* hack to make sure we define FormatWithoutTrailingZeros only once */
michael@0 850 #ifdef CharT_is_PRUnichar
michael@0 851 // Returns the length of the formatted aDouble in buf.
michael@0 852 static int
michael@0 853 FormatWithoutTrailingZeros(char (& buf)[40], double aDouble,
michael@0 854 int precision)
michael@0 855 {
michael@0 856 static const DoubleToStringConverter converter(DoubleToStringConverter::UNIQUE_ZERO |
michael@0 857 DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
michael@0 858 "Infinity",
michael@0 859 "NaN",
michael@0 860 'e',
michael@0 861 -6, 21,
michael@0 862 6, 1);
michael@0 863 double_conversion::StringBuilder builder(buf, sizeof(buf));
michael@0 864 bool exponential_notation = false;
michael@0 865 converter.ToPrecision(aDouble, precision, &exponential_notation, &builder);
michael@0 866 int length = builder.position();
michael@0 867 char* formattedDouble = builder.Finalize();
michael@0 868
michael@0 869 // If we have a shorter string than precision, it means we have a special
michael@0 870 // value (NaN or Infinity). All other numbers will be formatted with at
michael@0 871 // least precision digits.
michael@0 872 if (length <= precision) {
michael@0 873 return length;
michael@0 874 }
michael@0 875
michael@0 876 char* end = formattedDouble + length;
michael@0 877 char* decimalPoint = strchr(buf, '.');
michael@0 878 // No trailing zeros to remove.
michael@0 879 if (decimalPoint == nullptr) {
michael@0 880 return length;
michael@0 881 }
michael@0 882
michael@0 883 if (MOZ_UNLIKELY(exponential_notation)) {
michael@0 884 // We need to check for cases like 1.00000e-10 (yes, this is
michael@0 885 // disgusting).
michael@0 886 char* exponent = end - 1;
michael@0 887 for ( ; ; --exponent) {
michael@0 888 if (*exponent == 'e') {
michael@0 889 break;
michael@0 890 }
michael@0 891 }
michael@0 892 char* zerosBeforeExponent = exponent - 1;
michael@0 893 for ( ; zerosBeforeExponent != decimalPoint; --zerosBeforeExponent) {
michael@0 894 if (*zerosBeforeExponent != '0') {
michael@0 895 break;
michael@0 896 }
michael@0 897 }
michael@0 898 if (zerosBeforeExponent == decimalPoint) {
michael@0 899 --zerosBeforeExponent;
michael@0 900 }
michael@0 901 // Slide the exponent to the left over the trailing zeros. Don't
michael@0 902 // worry about copying the trailing NUL character.
michael@0 903 size_t exponentSize = end - exponent;
michael@0 904 memmove(zerosBeforeExponent + 1, exponent, exponentSize);
michael@0 905 length -= exponent - (zerosBeforeExponent + 1);
michael@0 906 } else {
michael@0 907 char* trailingZeros = end - 1;
michael@0 908 for ( ; trailingZeros != decimalPoint; --trailingZeros) {
michael@0 909 if (*trailingZeros != '0') {
michael@0 910 break;
michael@0 911 }
michael@0 912 }
michael@0 913 if (trailingZeros == decimalPoint) {
michael@0 914 --trailingZeros;
michael@0 915 }
michael@0 916 length -= end - (trailingZeros + 1);
michael@0 917 }
michael@0 918
michael@0 919 return length;
michael@0 920 }
michael@0 921 #endif /* CharT_is_PRUnichar */
michael@0 922
michael@0 923 void
michael@0 924 nsTSubstring_CharT::AppendFloat( float aFloat )
michael@0 925 {
michael@0 926 char buf[40];
michael@0 927 int length = FormatWithoutTrailingZeros(buf, aFloat, 6);
michael@0 928 AppendASCII(buf, length);
michael@0 929 }
michael@0 930
michael@0 931 void
michael@0 932 nsTSubstring_CharT::AppendFloat( double aFloat )
michael@0 933 {
michael@0 934 char buf[40];
michael@0 935 int length = FormatWithoutTrailingZeros(buf, aFloat, 15);
michael@0 936 AppendASCII(buf, length);
michael@0 937 }
michael@0 938
michael@0 939 size_t
michael@0 940 nsTSubstring_CharT::SizeOfExcludingThisMustBeUnshared(
michael@0 941 mozilla::MallocSizeOf mallocSizeOf) const
michael@0 942 {
michael@0 943 if (mFlags & F_SHARED) {
michael@0 944 return nsStringBuffer::FromData(mData)->
michael@0 945 SizeOfIncludingThisMustBeUnshared(mallocSizeOf);
michael@0 946 }
michael@0 947 if (mFlags & F_OWNED) {
michael@0 948 return mallocSizeOf(mData);
michael@0 949 }
michael@0 950
michael@0 951 // If we reach here, exactly one of the following must be true:
michael@0 952 // - F_VOIDED is set, and mData points to sEmptyBuffer;
michael@0 953 // - F_FIXED is set, and mData points to a buffer within a string
michael@0 954 // object (e.g. nsAutoString);
michael@0 955 // - None of F_SHARED, F_OWNED, F_FIXED is set, and mData points to a buffer
michael@0 956 // owned by something else.
michael@0 957 //
michael@0 958 // In all three cases, we don't measure it.
michael@0 959 return 0;
michael@0 960 }
michael@0 961
michael@0 962 size_t
michael@0 963 nsTSubstring_CharT::SizeOfExcludingThisIfUnshared(
michael@0 964 mozilla::MallocSizeOf mallocSizeOf) const
michael@0 965 {
michael@0 966 // This is identical to SizeOfExcludingThisMustBeUnshared except for the
michael@0 967 // F_SHARED case.
michael@0 968 if (mFlags & F_SHARED) {
michael@0 969 return nsStringBuffer::FromData(mData)->
michael@0 970 SizeOfIncludingThisIfUnshared(mallocSizeOf);
michael@0 971 }
michael@0 972 if (mFlags & F_OWNED) {
michael@0 973 return mallocSizeOf(mData);
michael@0 974 }
michael@0 975 return 0;
michael@0 976 }
michael@0 977
michael@0 978 size_t
michael@0 979 nsTSubstring_CharT::SizeOfExcludingThisEvenIfShared(
michael@0 980 mozilla::MallocSizeOf mallocSizeOf) const
michael@0 981 {
michael@0 982 // This is identical to SizeOfExcludingThisMustBeUnshared except for the
michael@0 983 // F_SHARED case.
michael@0 984 if (mFlags & F_SHARED) {
michael@0 985 return nsStringBuffer::FromData(mData)->
michael@0 986 SizeOfIncludingThisEvenIfShared(mallocSizeOf);
michael@0 987 }
michael@0 988 if (mFlags & F_OWNED) {
michael@0 989 return mallocSizeOf(mData);
michael@0 990 }
michael@0 991 return 0;
michael@0 992 }
michael@0 993
michael@0 994 size_t
michael@0 995 nsTSubstring_CharT::SizeOfIncludingThisMustBeUnshared(
michael@0 996 mozilla::MallocSizeOf mallocSizeOf) const
michael@0 997 {
michael@0 998 return mallocSizeOf(this) + SizeOfExcludingThisMustBeUnshared(mallocSizeOf);
michael@0 999 }
michael@0 1000
michael@0 1001 size_t
michael@0 1002 nsTSubstring_CharT::SizeOfIncludingThisIfUnshared(
michael@0 1003 mozilla::MallocSizeOf mallocSizeOf) const
michael@0 1004 {
michael@0 1005 return mallocSizeOf(this) + SizeOfExcludingThisIfUnshared(mallocSizeOf);
michael@0 1006 }
michael@0 1007
michael@0 1008 size_t
michael@0 1009 nsTSubstring_CharT::SizeOfIncludingThisEvenIfShared(
michael@0 1010 mozilla::MallocSizeOf mallocSizeOf) const
michael@0 1011 {
michael@0 1012 return mallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(mallocSizeOf);
michael@0 1013 }
michael@0 1014

mercurial