Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsASCIIMask.h"
8 : #include "mozilla/CheckedInt.h"
9 : #include "mozilla/double-conversion.h"
10 : #include "mozilla/MemoryReporting.h"
11 : #include "mozilla/Printf.h"
12 :
13 : using double_conversion::DoubleToStringConverter;
14 :
15 : const nsTSubstring_CharT::size_type nsTSubstring_CharT::kMaxCapacity =
16 : (nsTSubstring_CharT::size_type(-1) /
17 : 2 - sizeof(nsStringBuffer)) /
18 : sizeof(nsTSubstring_CharT::char_type) - 2;
19 :
20 : #ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
21 367514 : nsTSubstring_CharT::nsTSubstring_CharT(char_type* aData, size_type aLength,
22 : DataFlags aDataFlags,
23 367514 : ClassFlags aClassFlags)
24 367514 : : nsTStringRepr_CharT(aData, aLength, aDataFlags, aClassFlags)
25 : {
26 367514 : MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "String is too large.");
27 :
28 367514 : if (aDataFlags & DataFlags::OWNED) {
29 0 : STRING_STAT_INCREMENT(Adopt);
30 0 : MOZ_LOG_CTOR(mData, "StringAdopt", 1);
31 : }
32 367514 : }
33 : #endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */
34 :
35 : /**
36 : * helper function for down-casting a nsTSubstring to a nsTFixedString.
37 : */
38 : inline const nsTFixedString_CharT*
39 281973 : AsFixedString(const nsTSubstring_CharT* aStr)
40 : {
41 281973 : return static_cast<const nsTFixedString_CharT*>(aStr);
42 : }
43 :
44 : /**
45 : * this function is called to prepare mData for writing. the given capacity
46 : * indicates the required minimum storage size for mData, in sizeof(char_type)
47 : * increments. this function returns true if the operation succeeds. it also
48 : * returns the old data and old flags members if mData is newly allocated.
49 : * the old data must be released by the caller.
50 : */
51 : bool
52 199877 : nsTSubstring_CharT::MutatePrep(size_type aCapacity, char_type** aOldData,
53 : DataFlags* aOldDataFlags)
54 : {
55 : // initialize to no old data
56 199877 : *aOldData = nullptr;
57 199877 : *aOldDataFlags = DataFlags(0);
58 :
59 199877 : size_type curCapacity = Capacity();
60 :
61 : // If |aCapacity > kMaxCapacity|, then our doubling algorithm may not be
62 : // able to allocate it. Just bail out in cases like that. We don't want
63 : // to be allocating 2GB+ strings anyway.
64 : static_assert((sizeof(nsStringBuffer) & 0x1) == 0,
65 : "bad size for nsStringBuffer");
66 199876 : if (!CheckCapacity(aCapacity)) {
67 0 : return false;
68 : }
69 :
70 : // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
71 : // need to allocate a new buffer. We cannot use the existing buffer even
72 : // though it might be large enough.
73 :
74 199876 : if (curCapacity != 0) {
75 61900 : if (aCapacity <= curCapacity) {
76 52203 : mDataFlags &= ~DataFlags::VOIDED; // mutation clears voided flag
77 52203 : return true;
78 : }
79 : }
80 :
81 147673 : if (curCapacity < aCapacity) {
82 : // We increase our capacity so that the allocated buffer grows
83 : // exponentially, which gives us amortized O(1) appending. Below the
84 : // threshold, we use powers-of-two. Above the threshold, we grow by at
85 : // least 1.125, rounding up to the nearest MiB.
86 147357 : const size_type slowGrowthThreshold = 8 * 1024 * 1024;
87 :
88 : // nsStringBuffer allocates sizeof(nsStringBuffer) + passed size, and
89 : // storageSize below wants extra 1 * sizeof(char_type).
90 : const size_type neededExtraSpace =
91 147357 : sizeof(nsStringBuffer) / sizeof(char_type) + 1;
92 :
93 : size_type temp;
94 147357 : if (aCapacity >= slowGrowthThreshold) {
95 0 : size_type minNewCapacity = curCapacity + (curCapacity >> 3); // multiply by 1.125
96 0 : temp = XPCOM_MAX(aCapacity, minNewCapacity) + neededExtraSpace;
97 :
98 : // Round up to the next multiple of MiB, but ensure the expected
99 : // capacity doesn't include the extra space required by nsStringBuffer
100 : // and null-termination.
101 0 : const size_t MiB = 1 << 20;
102 0 : temp = (MiB * ((temp + MiB - 1) / MiB)) - neededExtraSpace;
103 : } else {
104 : // Round up to the next power of two.
105 147357 : temp =
106 147357 : mozilla::RoundUpPow2(aCapacity + neededExtraSpace) - neededExtraSpace;
107 : }
108 :
109 147357 : MOZ_ASSERT(XPCOM_MIN(temp, kMaxCapacity) >= aCapacity,
110 : "should have hit the early return at the top");
111 147357 : aCapacity = XPCOM_MIN(temp, kMaxCapacity);
112 : }
113 :
114 : //
115 : // several cases:
116 : //
117 : // (1) we have a shared buffer (mDataFlags & DataFlags::SHARED)
118 : // (2) we have an owned buffer (mDataFlags & DataFlags::OWNED)
119 : // (3) we have a fixed buffer (mDataFlags & DataFlags::FIXED)
120 : // (4) we have a readonly buffer
121 : //
122 : // requiring that we in some cases preserve the data before creating
123 : // a new buffer complicates things just a bit ;-)
124 : //
125 :
126 147674 : size_type storageSize = (aCapacity + 1) * sizeof(char_type);
127 :
128 : // case #1
129 147674 : if (mDataFlags & DataFlags::SHARED) {
130 10099 : nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
131 10099 : if (!hdr->IsReadonly()) {
132 2400 : nsStringBuffer* newHdr = nsStringBuffer::Realloc(hdr, storageSize);
133 2400 : if (!newHdr) {
134 0 : return false; // out-of-memory (original header left intact)
135 : }
136 :
137 2400 : hdr = newHdr;
138 2400 : mData = (char_type*)hdr->Data();
139 2400 : mDataFlags &= ~DataFlags::VOIDED; // mutation clears voided flag
140 2400 : return true;
141 : }
142 : }
143 :
144 : char_type* newData;
145 : DataFlags newDataFlags;
146 :
147 : // if we have a fixed buffer of sufficient size, then use it. this helps
148 : // avoid heap allocations.
149 371204 : if ((mClassFlags & ClassFlags::FIXED) &&
150 225930 : (aCapacity < AsFixedString(this)->mFixedCapacity)) {
151 57732 : newData = AsFixedString(this)->mFixedBuf;
152 57732 : newDataFlags = DataFlags::TERMINATED | DataFlags::FIXED;
153 : } else {
154 : // if we reach here then, we must allocate a new buffer. we cannot
155 : // make use of our DataFlags::OWNED or DataFlags::FIXED buffers because they are not
156 : // large enough.
157 :
158 : nsStringBuffer* newHdr =
159 87541 : nsStringBuffer::Alloc(storageSize).take();
160 87543 : if (!newHdr) {
161 0 : return false; // we are still in a consistent state
162 : }
163 :
164 87543 : newData = (char_type*)newHdr->Data();
165 87543 : newDataFlags = DataFlags::TERMINATED | DataFlags::SHARED;
166 : }
167 :
168 : // save old data and flags
169 145274 : *aOldData = mData;
170 145274 : *aOldDataFlags = mDataFlags;
171 :
172 145274 : mData = newData;
173 145274 : mDataFlags = newDataFlags;
174 :
175 : // mLength does not change
176 :
177 : // though we are not necessarily terminated at the moment, now is probably
178 : // still the best time to set DataFlags::TERMINATED.
179 :
180 145274 : return true;
181 : }
182 :
183 : void
184 784294 : nsTSubstring_CharT::Finalize()
185 : {
186 784294 : ::ReleaseData(mData, mDataFlags);
187 : // mData, mLength, and mDataFlags are purposefully left dangling
188 784295 : }
189 :
190 : bool
191 227761 : nsTSubstring_CharT::ReplacePrep(index_type aCutStart,
192 : size_type aCutLength,
193 : size_type aNewLength)
194 : {
195 227761 : aCutLength = XPCOM_MIN(aCutLength, mLength - aCutStart);
196 :
197 227761 : mozilla::CheckedInt<size_type> newTotalLen = mLength;
198 227761 : newTotalLen += aNewLength;
199 227761 : newTotalLen -= aCutLength;
200 227761 : if (!newTotalLen.isValid()) {
201 0 : return false;
202 : }
203 :
204 227761 : if (aCutStart == mLength && Capacity() > newTotalLen.value()) {
205 113100 : mDataFlags &= ~DataFlags::VOIDED;
206 113100 : mData[newTotalLen.value()] = char_type(0);
207 113100 : mLength = newTotalLen.value();
208 113100 : return true;
209 : }
210 :
211 114661 : return ReplacePrepInternal(aCutStart, aCutLength, aNewLength,
212 114661 : newTotalLen.value());
213 : }
214 :
215 : bool
216 114661 : nsTSubstring_CharT::ReplacePrepInternal(index_type aCutStart, size_type aCutLen,
217 : size_type aFragLen, size_type aNewLen)
218 : {
219 : char_type* oldData;
220 : DataFlags oldFlags;
221 114661 : if (!MutatePrep(aNewLen, &oldData, &oldFlags)) {
222 0 : return false; // out-of-memory
223 : }
224 :
225 114660 : if (oldData) {
226 : // determine whether or not we need to copy part of the old string
227 : // over to the new string.
228 :
229 109823 : if (aCutStart > 0) {
230 : // copy prefix from old string
231 5059 : char_traits::copy(mData, oldData, aCutStart);
232 : }
233 :
234 109824 : if (aCutStart + aCutLen < mLength) {
235 : // copy suffix from old string to new offset
236 29 : size_type from = aCutStart + aCutLen;
237 29 : size_type fromLen = mLength - from;
238 29 : uint32_t to = aCutStart + aFragLen;
239 29 : char_traits::copy(mData + to, oldData + from, fromLen);
240 : }
241 :
242 109824 : ::ReleaseData(oldData, oldFlags);
243 : } else {
244 : // original data remains intact
245 :
246 : // determine whether or not we need to move part of the existing string
247 : // to make room for the requested hole.
248 4837 : if (aFragLen != aCutLen && aCutStart + aCutLen < mLength) {
249 956 : uint32_t from = aCutStart + aCutLen;
250 956 : uint32_t fromLen = mLength - from;
251 956 : uint32_t to = aCutStart + aFragLen;
252 956 : char_traits::move(mData + to, mData + from, fromLen);
253 : }
254 : }
255 :
256 : // add null terminator (mutable mData always has room for the null-
257 : // terminator).
258 114661 : mData[aNewLen] = char_type(0);
259 114661 : mLength = aNewLen;
260 :
261 114661 : return true;
262 : }
263 :
264 : nsTSubstring_CharT::size_type
265 424646 : nsTSubstring_CharT::Capacity() const
266 : {
267 : // return 0 to indicate an immutable or 0-sized buffer
268 :
269 : size_type capacity;
270 424646 : if (mDataFlags & DataFlags::SHARED) {
271 : // if the string is readonly, then we pretend that it has no capacity.
272 47452 : nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
273 47452 : if (hdr->IsReadonly()) {
274 9794 : capacity = 0;
275 : } else {
276 37652 : capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
277 : }
278 377198 : } else if (mDataFlags & DataFlags::FIXED) {
279 143584 : capacity = AsFixedString(this)->mFixedCapacity;
280 233614 : } else if (mDataFlags & DataFlags::OWNED) {
281 : // we don't store the capacity of an adopted buffer because that would
282 : // require an additional member field. the best we can do is base the
283 : // capacity on our length. remains to be seen if this is the right
284 : // trade-off.
285 0 : capacity = mLength;
286 : } else {
287 233614 : capacity = 0;
288 : }
289 :
290 424644 : return capacity;
291 : }
292 :
293 : bool
294 108572 : nsTSubstring_CharT::EnsureMutable(size_type aNewLen)
295 : {
296 108572 : if (aNewLen == size_type(-1) || aNewLen == mLength) {
297 108566 : if (mDataFlags & (DataFlags::FIXED | DataFlags::OWNED)) {
298 71376 : return true;
299 : }
300 109669 : if ((mDataFlags & DataFlags::SHARED) &&
301 72479 : !nsStringBuffer::FromData(mData)->IsReadonly()) {
302 34360 : return true;
303 : }
304 :
305 2830 : aNewLen = mLength;
306 : }
307 2836 : return SetLength(aNewLen, mozilla::fallible);
308 : }
309 :
310 : // ---------------------------------------------------------------------------
311 :
312 : // This version of Assign is optimized for single-character assignment.
313 : void
314 143 : nsTSubstring_CharT::Assign(char_type aChar)
315 : {
316 143 : if (!ReplacePrep(0, mLength, 1)) {
317 0 : AllocFailed(mLength);
318 : }
319 :
320 143 : *mData = aChar;
321 143 : }
322 :
323 : bool
324 0 : nsTSubstring_CharT::Assign(char_type aChar, const fallible_t&)
325 : {
326 0 : if (!ReplacePrep(0, mLength, 1)) {
327 0 : return false;
328 : }
329 :
330 0 : *mData = aChar;
331 0 : return true;
332 : }
333 :
334 : void
335 8938 : nsTSubstring_CharT::Assign(const char_type* aData)
336 : {
337 8938 : if (!Assign(aData, mozilla::fallible)) {
338 0 : AllocFailed(char_traits::length(aData));
339 : }
340 8938 : }
341 :
342 : bool
343 8944 : nsTSubstring_CharT::Assign(const char_type* aData, const fallible_t&)
344 : {
345 8944 : return Assign(aData, size_type(-1), mozilla::fallible);
346 : }
347 :
348 : void
349 43591 : nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength)
350 : {
351 43591 : if (!Assign(aData, aLength, mozilla::fallible)) {
352 0 : AllocFailed(aLength == size_type(-1) ? char_traits::length(aData)
353 0 : : aLength);
354 : }
355 43591 : }
356 :
357 : bool
358 134682 : nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength,
359 : const fallible_t& aFallible)
360 : {
361 134682 : if (!aData || aLength == 0) {
362 10677 : Truncate();
363 10677 : return true;
364 : }
365 :
366 124005 : if (aLength == size_type(-1)) {
367 39792 : aLength = char_traits::length(aData);
368 : }
369 :
370 124005 : if (IsDependentOn(aData, aData + aLength)) {
371 2 : return Assign(string_type(aData, aLength), aFallible);
372 : }
373 :
374 124003 : if (!ReplacePrep(0, mLength, aLength)) {
375 0 : return false;
376 : }
377 :
378 124003 : char_traits::copy(mData, aData, aLength);
379 124003 : return true;
380 : }
381 :
382 : void
383 2057 : nsTSubstring_CharT::AssignASCII(const char* aData, size_type aLength)
384 : {
385 2057 : if (!AssignASCII(aData, aLength, mozilla::fallible)) {
386 0 : AllocFailed(aLength);
387 : }
388 2057 : }
389 :
390 : bool
391 2057 : nsTSubstring_CharT::AssignASCII(const char* aData, size_type aLength,
392 : const fallible_t& aFallible)
393 : {
394 : // A Unicode string can't depend on an ASCII string buffer,
395 : // so this dependence check only applies to CStrings.
396 : #ifdef CharT_is_char
397 1117 : if (IsDependentOn(aData, aData + aLength)) {
398 0 : return Assign(string_type(aData, aLength), aFallible);
399 : }
400 : #endif
401 :
402 2057 : if (!ReplacePrep(0, mLength, aLength)) {
403 0 : return false;
404 : }
405 :
406 2057 : char_traits::copyASCII(mData, aData, aLength);
407 2057 : return true;
408 : }
409 :
410 : void
411 6899 : nsTSubstring_CharT::AssignLiteral(const char_type* aData, size_type aLength)
412 : {
413 6899 : ::ReleaseData(mData, mDataFlags);
414 6899 : mData = const_cast<char_type*>(aData);
415 6899 : mLength = aLength;
416 6899 : mDataFlags = DataFlags::TERMINATED | DataFlags::LITERAL;
417 6899 : }
418 :
419 : void
420 192452 : nsTSubstring_CharT::Assign(const self_type& aStr)
421 : {
422 192452 : if (!Assign(aStr, mozilla::fallible)) {
423 0 : AllocFailed(aStr.Length());
424 : }
425 192453 : }
426 :
427 : bool
428 195257 : nsTSubstring_CharT::Assign(const self_type& aStr, const fallible_t& aFallible)
429 : {
430 : // |aStr| could be sharable. We need to check its flags to know how to
431 : // deal with it.
432 :
433 195257 : if (&aStr == this) {
434 0 : return true;
435 : }
436 :
437 195257 : if (!aStr.mLength) {
438 53069 : Truncate();
439 53069 : mDataFlags |= aStr.mDataFlags & DataFlags::VOIDED;
440 53069 : return true;
441 : }
442 :
443 142188 : if (aStr.mDataFlags & DataFlags::SHARED) {
444 : // nice! we can avoid a string copy :-)
445 :
446 : // |aStr| should be null-terminated
447 57095 : NS_ASSERTION(aStr.mDataFlags & DataFlags::TERMINATED, "shared, but not terminated");
448 :
449 57095 : ::ReleaseData(mData, mDataFlags);
450 :
451 57095 : mData = aStr.mData;
452 57095 : mLength = aStr.mLength;
453 57095 : mDataFlags = DataFlags::TERMINATED | DataFlags::SHARED;
454 :
455 : // get an owning reference to the mData
456 57095 : nsStringBuffer::FromData(mData)->AddRef();
457 57095 : return true;
458 85094 : } else if (aStr.mDataFlags & DataFlags::LITERAL) {
459 2947 : MOZ_ASSERT(aStr.mDataFlags & DataFlags::TERMINATED, "Unterminated literal");
460 :
461 2947 : AssignLiteral(aStr.mData, aStr.mLength);
462 2947 : return true;
463 : }
464 :
465 : // else, treat this like an ordinary assignment.
466 82147 : return Assign(aStr.Data(), aStr.Length(), aFallible);
467 : }
468 :
469 : void
470 3441 : nsTSubstring_CharT::Assign(const substring_tuple_type& aTuple)
471 : {
472 3441 : if (!Assign(aTuple, mozilla::fallible)) {
473 0 : AllocFailed(aTuple.Length());
474 : }
475 3441 : }
476 :
477 : bool
478 3441 : nsTSubstring_CharT::Assign(const substring_tuple_type& aTuple,
479 : const fallible_t& aFallible)
480 : {
481 3441 : if (aTuple.IsDependentOn(mData, mData + mLength)) {
482 : // take advantage of sharing here...
483 0 : return Assign(string_type(aTuple), aFallible);
484 : }
485 :
486 3441 : size_type length = aTuple.Length();
487 :
488 : // don't use ReplacePrep here because it changes the length
489 : char_type* oldData;
490 : DataFlags oldFlags;
491 3441 : if (!MutatePrep(length, &oldData, &oldFlags)) {
492 0 : return false;
493 : }
494 :
495 3441 : if (oldData) {
496 2963 : ::ReleaseData(oldData, oldFlags);
497 : }
498 :
499 3441 : aTuple.WriteTo(mData, length);
500 3441 : mData[length] = 0;
501 3441 : mLength = length;
502 3441 : return true;
503 : }
504 :
505 : void
506 5757 : nsTSubstring_CharT::Adopt(char_type* aData, size_type aLength)
507 : {
508 5757 : if (aData) {
509 5362 : ::ReleaseData(mData, mDataFlags);
510 :
511 5362 : if (aLength == size_type(-1)) {
512 5286 : aLength = char_traits::length(aData);
513 : }
514 :
515 5362 : MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "adopting a too-long string");
516 :
517 5362 : mData = aData;
518 5362 : mLength = aLength;
519 5362 : mDataFlags = DataFlags::TERMINATED | DataFlags::OWNED;
520 :
521 5362 : STRING_STAT_INCREMENT(Adopt);
522 : // Treat this as construction of a "StringAdopt" object for leak
523 : // tracking purposes.
524 5362 : MOZ_LOG_CTOR(mData, "StringAdopt", 1);
525 : } else {
526 395 : SetIsVoid(true);
527 : }
528 5757 : }
529 :
530 :
531 : // This version of Replace is optimized for single-character replacement.
532 : void
533 13128 : nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
534 : char_type aChar)
535 : {
536 13128 : aCutStart = XPCOM_MIN(aCutStart, Length());
537 :
538 13128 : if (ReplacePrep(aCutStart, aCutLength, 1)) {
539 13128 : mData[aCutStart] = aChar;
540 : }
541 13128 : }
542 :
543 : bool
544 0 : nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
545 : char_type aChar,
546 : const fallible_t&)
547 : {
548 0 : aCutStart = XPCOM_MIN(aCutStart, Length());
549 :
550 0 : if (!ReplacePrep(aCutStart, aCutLength, 1)) {
551 0 : return false;
552 : }
553 :
554 0 : mData[aCutStart] = aChar;
555 :
556 0 : return true;
557 : }
558 :
559 : void
560 71990 : nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
561 : const char_type* aData, size_type aLength)
562 : {
563 71990 : if (!Replace(aCutStart, aCutLength, aData, aLength,
564 : mozilla::fallible)) {
565 0 : AllocFailed(Length() - aCutLength + 1);
566 : }
567 71990 : }
568 :
569 : bool
570 80697 : nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
571 : const char_type* aData, size_type aLength,
572 : const fallible_t& aFallible)
573 : {
574 : // unfortunately, some callers pass null :-(
575 80697 : if (!aData) {
576 0 : aLength = 0;
577 : } else {
578 80697 : if (aLength == size_type(-1)) {
579 8192 : aLength = char_traits::length(aData);
580 : }
581 :
582 80697 : if (IsDependentOn(aData, aData + aLength)) {
583 0 : nsTAutoString_CharT temp(aData, aLength);
584 0 : return Replace(aCutStart, aCutLength, temp, aFallible);
585 : }
586 : }
587 :
588 80697 : aCutStart = XPCOM_MIN(aCutStart, Length());
589 :
590 80697 : bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
591 80697 : if (!ok) {
592 0 : return false;
593 : }
594 :
595 80697 : if (aLength > 0) {
596 78355 : char_traits::copy(mData + aCutStart, aData, aLength);
597 : }
598 :
599 80697 : return true;
600 : }
601 :
602 : void
603 6249 : nsTSubstring_CharT::ReplaceASCII(index_type aCutStart, size_type aCutLength,
604 : const char* aData, size_type aLength)
605 : {
606 6249 : if (!ReplaceASCII(aCutStart, aCutLength, aData, aLength, mozilla::fallible)) {
607 0 : AllocFailed(Length() - aCutLength + 1);
608 : }
609 6249 : }
610 :
611 : bool
612 6249 : nsTSubstring_CharT::ReplaceASCII(index_type aCutStart, size_type aCutLength,
613 : const char* aData, size_type aLength,
614 : const fallible_t& aFallible)
615 : {
616 6249 : if (aLength == size_type(-1)) {
617 489 : aLength = strlen(aData);
618 : }
619 :
620 : // A Unicode string can't depend on an ASCII string buffer,
621 : // so this dependence check only applies to CStrings.
622 : #ifdef CharT_is_char
623 5336 : if (IsDependentOn(aData, aData + aLength)) {
624 0 : nsTAutoString_CharT temp(aData, aLength);
625 0 : return Replace(aCutStart, aCutLength, temp, aFallible);
626 : }
627 : #endif
628 :
629 6249 : aCutStart = XPCOM_MIN(aCutStart, Length());
630 :
631 6249 : bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
632 6249 : if (!ok) {
633 0 : return false;
634 : }
635 :
636 6249 : if (aLength > 0) {
637 6249 : char_traits::copyASCII(mData + aCutStart, aData, aLength);
638 : }
639 :
640 6249 : return true;
641 : }
642 :
643 : void
644 9 : nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
645 : const substring_tuple_type& aTuple)
646 : {
647 9 : if (aTuple.IsDependentOn(mData, mData + mLength)) {
648 0 : nsTAutoString_CharT temp(aTuple);
649 0 : Replace(aCutStart, aCutLength, temp);
650 0 : return;
651 : }
652 :
653 9 : size_type length = aTuple.Length();
654 :
655 9 : aCutStart = XPCOM_MIN(aCutStart, Length());
656 :
657 9 : if (ReplacePrep(aCutStart, aCutLength, length) && length > 0) {
658 9 : aTuple.WriteTo(mData + aCutStart, length);
659 : }
660 : }
661 :
662 : void
663 1495 : nsTSubstring_CharT::ReplaceLiteral(index_type aCutStart, size_type aCutLength,
664 : const char_type* aData, size_type aLength)
665 : {
666 1495 : aCutStart = XPCOM_MIN(aCutStart, Length());
667 :
668 1495 : if (!aCutStart && aCutLength == Length()) {
669 20 : AssignLiteral(aData, aLength);
670 1475 : } else if (ReplacePrep(aCutStart, aCutLength, aLength) && aLength > 0) {
671 1475 : char_traits::copy(mData + aCutStart, aData, aLength);
672 : }
673 1495 : }
674 :
675 : void
676 312508 : nsTSubstring_CharT::SetCapacity(size_type aCapacity)
677 : {
678 312508 : if (!SetCapacity(aCapacity, mozilla::fallible)) {
679 0 : AllocFailed(aCapacity);
680 : }
681 312509 : }
682 :
683 : bool
684 353526 : nsTSubstring_CharT::SetCapacity(size_type aCapacity, const fallible_t&)
685 : {
686 : // capacity does not include room for the terminating null char
687 :
688 : // if our capacity is reduced to zero, then free our buffer.
689 353526 : if (aCapacity == 0) {
690 271751 : ::ReleaseData(mData, mDataFlags);
691 271751 : mData = char_traits::sEmptyBuffer;
692 271751 : mLength = 0;
693 271751 : mDataFlags = DataFlags::TERMINATED;
694 271751 : return true;
695 : }
696 :
697 : char_type* oldData;
698 : DataFlags oldFlags;
699 81775 : if (!MutatePrep(aCapacity, &oldData, &oldFlags)) {
700 0 : return false; // out-of-memory
701 : }
702 :
703 : // compute new string length
704 81776 : size_type newLen = XPCOM_MIN(mLength, aCapacity);
705 :
706 81776 : if (oldData) {
707 : // preserve old data
708 32166 : if (mLength > 0) {
709 5554 : char_traits::copy(mData, oldData, newLen);
710 : }
711 :
712 32166 : ::ReleaseData(oldData, oldFlags);
713 : }
714 :
715 : // adjust mLength if our buffer shrunk down in size
716 81776 : if (newLen < mLength) {
717 8057 : mLength = newLen;
718 : }
719 :
720 : // always null-terminate here, even if the buffer got longer. this is
721 : // for backwards compat with the old string implementation.
722 81776 : mData[aCapacity] = char_type(0);
723 :
724 81776 : return true;
725 : }
726 :
727 : void
728 309845 : nsTSubstring_CharT::SetLength(size_type aLength)
729 : {
730 309845 : SetCapacity(aLength);
731 309846 : mLength = aLength;
732 309846 : }
733 :
734 : bool
735 36639 : nsTSubstring_CharT::SetLength(size_type aLength, const fallible_t& aFallible)
736 : {
737 36639 : if (!SetCapacity(aLength, aFallible)) {
738 0 : return false;
739 : }
740 :
741 36639 : mLength = aLength;
742 36639 : return true;
743 : }
744 :
745 : void
746 861 : nsTSubstring_CharT::SetIsVoid(bool aVal)
747 : {
748 861 : if (aVal) {
749 861 : Truncate();
750 861 : mDataFlags |= DataFlags::VOIDED;
751 : } else {
752 0 : mDataFlags &= ~DataFlags::VOIDED;
753 : }
754 861 : }
755 :
756 : namespace mozilla {
757 : namespace detail {
758 :
759 : nsTStringRepr_CharT::char_type
760 8373 : nsTStringRepr_CharT::First() const
761 : {
762 8373 : MOZ_RELEASE_ASSERT(mLength > 0, "|First()| called on an empty string");
763 8373 : return mData[0];
764 : }
765 :
766 : nsTStringRepr_CharT::char_type
767 1426 : nsTStringRepr_CharT::Last() const
768 : {
769 1426 : MOZ_RELEASE_ASSERT(mLength > 0, "|Last()| called on an empty string");
770 1426 : return mData[mLength - 1];
771 : }
772 :
773 : bool
774 73027 : nsTStringRepr_CharT::Equals(const self_type& aStr) const
775 : {
776 144576 : return mLength == aStr.mLength &&
777 144576 : char_traits::compare(mData, aStr.mData, mLength) == 0;
778 : }
779 :
780 : bool
781 628 : nsTStringRepr_CharT::Equals(const self_type& aStr,
782 : const comparator_type& aComp) const
783 : {
784 903 : return mLength == aStr.mLength &&
785 903 : aComp(mData, aStr.mData, mLength, aStr.mLength) == 0;
786 : }
787 :
788 : bool
789 0 : nsTStringRepr_CharT::Equals(const substring_tuple_type& aTuple) const
790 : {
791 0 : return Equals(substring_type(aTuple));
792 : }
793 :
794 : bool
795 0 : nsTStringRepr_CharT::Equals(const substring_tuple_type& aTuple,
796 : const comparator_type& aComp) const
797 : {
798 0 : return Equals(substring_type(aTuple), aComp);
799 : }
800 :
801 : bool
802 6832 : nsTStringRepr_CharT::Equals(const char_type* aData) const
803 : {
804 : // unfortunately, some callers pass null :-(
805 6832 : if (!aData) {
806 0 : NS_NOTREACHED("null data pointer");
807 0 : return mLength == 0;
808 : }
809 :
810 : // XXX avoid length calculation?
811 6832 : size_type length = char_traits::length(aData);
812 10025 : return mLength == length &&
813 10025 : char_traits::compare(mData, aData, mLength) == 0;
814 : }
815 :
816 : bool
817 0 : nsTStringRepr_CharT::Equals(const char_type* aData,
818 : const comparator_type& aComp) const
819 : {
820 : // unfortunately, some callers pass null :-(
821 0 : if (!aData) {
822 0 : NS_NOTREACHED("null data pointer");
823 0 : return mLength == 0;
824 : }
825 :
826 : // XXX avoid length calculation?
827 0 : size_type length = char_traits::length(aData);
828 0 : return mLength == length && aComp(mData, aData, mLength, length) == 0;
829 : }
830 :
831 : bool
832 27024 : nsTStringRepr_CharT::EqualsASCII(const char* aData, size_type aLen) const
833 : {
834 39749 : return mLength == aLen &&
835 39749 : char_traits::compareASCII(mData, aData, aLen) == 0;
836 : }
837 :
838 : bool
839 116 : nsTStringRepr_CharT::EqualsASCII(const char* aData) const
840 : {
841 116 : return char_traits::compareASCIINullTerminated(mData, mLength, aData) == 0;
842 : }
843 :
844 : bool
845 71002 : nsTStringRepr_CharT::LowerCaseEqualsASCII(const char* aData,
846 : size_type aLen) const
847 : {
848 105412 : return mLength == aLen &&
849 105412 : char_traits::compareLowerCaseToASCII(mData, aData, aLen) == 0;
850 : }
851 :
852 : bool
853 71670 : nsTStringRepr_CharT::LowerCaseEqualsASCII(const char* aData) const
854 : {
855 71670 : return char_traits::compareLowerCaseToASCIINullTerminated(mData,
856 71670 : mLength,
857 71670 : aData) == 0;
858 : }
859 :
860 : nsTStringRepr_CharT::size_type
861 14 : nsTStringRepr_CharT::CountChar(char_type aChar) const
862 : {
863 14 : const char_type* start = mData;
864 14 : const char_type* end = mData + mLength;
865 :
866 14 : return NS_COUNT(start, end, aChar);
867 : }
868 :
869 : int32_t
870 9262 : nsTStringRepr_CharT::FindChar(char_type aChar, index_type aOffset) const
871 : {
872 9262 : if (aOffset < mLength) {
873 18122 : const char_type* result = char_traits::find(mData + aOffset,
874 18122 : mLength - aOffset, aChar);
875 9061 : if (result) {
876 1048 : return result - mData;
877 : }
878 : }
879 8214 : return -1;
880 : }
881 :
882 : } // namespace detail
883 : } // namespace mozilla
884 :
885 : void
886 0 : nsTSubstring_CharT::StripChar(char_type aChar)
887 : {
888 0 : if (mLength == 0) {
889 0 : return;
890 : }
891 :
892 0 : if (!EnsureMutable()) { // XXX do this lazily?
893 0 : AllocFailed(mLength);
894 : }
895 :
896 : // XXX(darin): this code should defer writing until necessary.
897 :
898 0 : char_type* to = mData;
899 0 : char_type* from = mData;
900 0 : char_type* end = mData + mLength;
901 :
902 0 : while (from < end) {
903 0 : char_type theChar = *from++;
904 0 : if (aChar != theChar) {
905 0 : *to++ = theChar;
906 : }
907 : }
908 0 : *to = char_type(0); // add the null
909 0 : mLength = to - mData;
910 : }
911 :
912 : void
913 1 : nsTSubstring_CharT::StripChars(const char_type* aChars)
914 : {
915 1 : if (mLength == 0) {
916 0 : return;
917 : }
918 :
919 1 : if (!EnsureMutable()) { // XXX do this lazily?
920 0 : AllocFailed(mLength);
921 : }
922 :
923 : // XXX(darin): this code should defer writing until necessary.
924 :
925 1 : char_type* to = mData;
926 1 : char_type* from = mData;
927 1 : char_type* end = mData + mLength;
928 :
929 237 : while (from < end) {
930 118 : char_type theChar = *from++;
931 118 : const char_type* test = aChars;
932 :
933 351 : for (; *test && *test != theChar; ++test);
934 :
935 118 : if (!*test) {
936 : // Not stripped, copy this char.
937 116 : *to++ = theChar;
938 : }
939 : }
940 1 : *to = char_type(0); // add the null
941 1 : mLength = to - mData;
942 : }
943 :
944 : void
945 6445 : nsTSubstring_CharT::StripTaggedASCII(const ASCIIMaskArray& aToStrip)
946 : {
947 6445 : if (mLength == 0) {
948 165 : return;
949 : }
950 :
951 6280 : if (!EnsureMutable()) {
952 0 : AllocFailed(mLength);
953 : }
954 :
955 6280 : char_type* to = mData;
956 6280 : char_type* from = mData;
957 6280 : char_type* end = mData + mLength;
958 :
959 69684 : while (from < end) {
960 31702 : uint32_t theChar = (uint32_t)*from++;
961 : // Replacing this with a call to ASCIIMask::IsMasked
962 : // regresses performance somewhat, so leaving it inlined.
963 31702 : if (!mozilla::ASCIIMask::IsMasked(aToStrip, theChar)) {
964 : // Not stripped, copy this char.
965 31696 : *to++ = (char_type)theChar;
966 : }
967 : }
968 6280 : *to = char_type(0); // add the null
969 6280 : mLength = to - mData;
970 : }
971 :
972 : void
973 60 : nsTSubstring_CharT::StripCRLF()
974 : {
975 : // Expanding this call to copy the code from StripTaggedASCII
976 : // instead of just calling it does somewhat help with performance
977 : // but it is not worth it given the duplicated code.
978 60 : StripTaggedASCII(mozilla::ASCIIMask::MaskCRLF());
979 60 : }
980 :
981 843 : struct MOZ_STACK_CLASS PrintfAppend_CharT : public mozilla::PrintfTarget
982 : {
983 843 : explicit PrintfAppend_CharT(nsTSubstring_CharT* aString)
984 843 : : mString(aString)
985 : {
986 843 : }
987 :
988 5743 : bool append(const char* aStr, size_t aLen) override {
989 5743 : if (aLen == 0) {
990 0 : return true;
991 : }
992 :
993 5743 : mString->AppendASCII(aStr, aLen);
994 5743 : return true;
995 : }
996 :
997 : private:
998 :
999 : nsTSubstring_CharT* mString;
1000 : };
1001 :
1002 : void
1003 370 : nsTSubstring_CharT::AppendPrintf(const char* aFormat, ...)
1004 : {
1005 740 : PrintfAppend_CharT appender(this);
1006 : va_list ap;
1007 370 : va_start(ap, aFormat);
1008 370 : bool r = appender.vprint(aFormat, ap);
1009 370 : if (!r) {
1010 0 : MOZ_CRASH("Allocation or other failure in PrintfTarget::print");
1011 : }
1012 370 : va_end(ap);
1013 370 : }
1014 :
1015 : void
1016 473 : nsTSubstring_CharT::AppendPrintf(const char* aFormat, va_list aAp)
1017 : {
1018 946 : PrintfAppend_CharT appender(this);
1019 473 : bool r = appender.vprint(aFormat, aAp);
1020 473 : if (!r) {
1021 0 : MOZ_CRASH("Allocation or other failure in PrintfTarget::print");
1022 : }
1023 473 : }
1024 :
1025 : /* hack to make sure we define FormatWithoutTrailingZeros only once */
1026 : #ifdef CharT_is_PRUnichar
1027 : // Returns the length of the formatted aDouble in aBuf.
1028 : static int
1029 0 : FormatWithoutTrailingZeros(char (&aBuf)[40], double aDouble,
1030 : int aPrecision)
1031 : {
1032 : static const DoubleToStringConverter converter(DoubleToStringConverter::UNIQUE_ZERO |
1033 : DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
1034 : "Infinity",
1035 : "NaN",
1036 : 'e',
1037 : -6, 21,
1038 0 : 6, 1);
1039 0 : double_conversion::StringBuilder builder(aBuf, sizeof(aBuf));
1040 0 : bool exponential_notation = false;
1041 0 : converter.ToPrecision(aDouble, aPrecision, &exponential_notation, &builder);
1042 0 : int length = builder.position();
1043 0 : char* formattedDouble = builder.Finalize();
1044 :
1045 : // If we have a shorter string than aPrecision, it means we have a special
1046 : // value (NaN or Infinity). All other numbers will be formatted with at
1047 : // least aPrecision digits.
1048 0 : if (length <= aPrecision) {
1049 0 : return length;
1050 : }
1051 :
1052 0 : char* end = formattedDouble + length;
1053 0 : char* decimalPoint = strchr(aBuf, '.');
1054 : // No trailing zeros to remove.
1055 0 : if (!decimalPoint) {
1056 0 : return length;
1057 : }
1058 :
1059 0 : if (MOZ_UNLIKELY(exponential_notation)) {
1060 : // We need to check for cases like 1.00000e-10 (yes, this is
1061 : // disgusting).
1062 0 : char* exponent = end - 1;
1063 0 : for (; ; --exponent) {
1064 0 : if (*exponent == 'e') {
1065 0 : break;
1066 : }
1067 : }
1068 0 : char* zerosBeforeExponent = exponent - 1;
1069 0 : for (; zerosBeforeExponent != decimalPoint; --zerosBeforeExponent) {
1070 0 : if (*zerosBeforeExponent != '0') {
1071 0 : break;
1072 : }
1073 : }
1074 0 : if (zerosBeforeExponent == decimalPoint) {
1075 0 : --zerosBeforeExponent;
1076 : }
1077 : // Slide the exponent to the left over the trailing zeros. Don't
1078 : // worry about copying the trailing NUL character.
1079 0 : size_t exponentSize = end - exponent;
1080 0 : memmove(zerosBeforeExponent + 1, exponent, exponentSize);
1081 0 : length -= exponent - (zerosBeforeExponent + 1);
1082 : } else {
1083 0 : char* trailingZeros = end - 1;
1084 0 : for (; trailingZeros != decimalPoint; --trailingZeros) {
1085 0 : if (*trailingZeros != '0') {
1086 0 : break;
1087 : }
1088 : }
1089 0 : if (trailingZeros == decimalPoint) {
1090 0 : --trailingZeros;
1091 : }
1092 0 : length -= end - (trailingZeros + 1);
1093 : }
1094 :
1095 0 : return length;
1096 : }
1097 : #endif /* CharT_is_PRUnichar */
1098 :
1099 : void
1100 0 : nsTSubstring_CharT::AppendFloat(float aFloat)
1101 : {
1102 : char buf[40];
1103 0 : int length = FormatWithoutTrailingZeros(buf, aFloat, 6);
1104 0 : AppendASCII(buf, length);
1105 0 : }
1106 :
1107 : void
1108 0 : nsTSubstring_CharT::AppendFloat(double aFloat)
1109 : {
1110 : char buf[40];
1111 0 : int length = FormatWithoutTrailingZeros(buf, aFloat, 15);
1112 0 : AppendASCII(buf, length);
1113 0 : }
1114 :
1115 : size_t
1116 31 : nsTSubstring_CharT::SizeOfExcludingThisIfUnshared(
1117 : mozilla::MallocSizeOf aMallocSizeOf) const
1118 : {
1119 31 : if (mDataFlags & DataFlags::SHARED) {
1120 0 : return nsStringBuffer::FromData(mData)->
1121 0 : SizeOfIncludingThisIfUnshared(aMallocSizeOf);
1122 : }
1123 31 : if (mDataFlags & DataFlags::OWNED) {
1124 0 : return aMallocSizeOf(mData);
1125 : }
1126 :
1127 : // If we reach here, exactly one of the following must be true:
1128 : // - DataFlags::VOIDED is set, and mData points to sEmptyBuffer;
1129 : // - DataFlags::FIXED is set, and mData points to a buffer within a string
1130 : // object (e.g. nsAutoString);
1131 : // - None of DataFlags::SHARED, DataFlags::OWNED, DataFlags::FIXED is set, and mData points to a buffer
1132 : // owned by something else.
1133 : //
1134 : // In all three cases, we don't measure it.
1135 31 : return 0;
1136 : }
1137 :
1138 : size_t
1139 0 : nsTSubstring_CharT::SizeOfExcludingThisEvenIfShared(
1140 : mozilla::MallocSizeOf aMallocSizeOf) const
1141 : {
1142 : // This is identical to SizeOfExcludingThisIfUnshared except for the
1143 : // DataFlags::SHARED case.
1144 0 : if (mDataFlags & DataFlags::SHARED) {
1145 0 : return nsStringBuffer::FromData(mData)->
1146 0 : SizeOfIncludingThisEvenIfShared(aMallocSizeOf);
1147 : }
1148 0 : if (mDataFlags & DataFlags::OWNED) {
1149 0 : return aMallocSizeOf(mData);
1150 : }
1151 0 : return 0;
1152 : }
1153 :
1154 : size_t
1155 0 : nsTSubstring_CharT::SizeOfIncludingThisIfUnshared(
1156 : mozilla::MallocSizeOf aMallocSizeOf) const
1157 : {
1158 0 : return aMallocSizeOf(this) + SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1159 : }
1160 :
1161 : size_t
1162 0 : nsTSubstring_CharT::SizeOfIncludingThisEvenIfShared(
1163 : mozilla::MallocSizeOf aMallocSizeOf) const
1164 : {
1165 0 : return aMallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
1166 : }
1167 :
1168 : inline
1169 5 : nsTSubstringSplitter_CharT::nsTSubstringSplitter_CharT(
1170 5 : const nsTSubstring_CharT* aStr, char_type aDelim)
1171 : : mStr(aStr)
1172 : , mArray(nullptr)
1173 5 : , mDelim(aDelim)
1174 : {
1175 5 : if (mStr->IsEmpty()) {
1176 0 : mArraySize = 0;
1177 0 : return;
1178 : }
1179 :
1180 5 : size_type delimCount = mStr->CountChar(aDelim);
1181 5 : mArraySize = delimCount + 1;
1182 10 : mArray.reset(new nsTDependentSubstring_CharT[mArraySize]);
1183 :
1184 5 : size_t seenParts = 0;
1185 5 : size_type start = 0;
1186 7 : do {
1187 12 : MOZ_ASSERT(seenParts < mArraySize);
1188 12 : int32_t offset = mStr->FindChar(aDelim, start);
1189 12 : if (offset != -1) {
1190 7 : size_type length = static_cast<size_type>(offset) - start;
1191 7 : mArray[seenParts++].Rebind(mStr->Data() + start, length);
1192 7 : start = static_cast<size_type>(offset) + 1;
1193 : } else {
1194 : // Get the remainder
1195 5 : mArray[seenParts++].Rebind(mStr->Data() + start, mStr->Length() - start);
1196 5 : break;
1197 : }
1198 7 : } while (start < mStr->Length());
1199 : }
1200 :
1201 : nsTSubstringSplitter_CharT
1202 5 : nsTSubstring_CharT::Split(const char_type aChar) const
1203 : {
1204 5 : return nsTSubstringSplitter_CharT(this, aChar);
1205 : }
1206 :
1207 : const nsTDependentSubstring_CharT&
1208 12 : nsTSubstringSplitter_CharT::nsTSubstringSplit_Iter::operator* () const
1209 : {
1210 12 : return mObj.Get(mPos);
1211 : }
|