xpcom/string/src/nsTSubstring.cpp

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

mercurial