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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla.
17 : *
18 : * The Initial Developer of the Original Code is IBM Corporation.
19 : * Portions created by IBM Corporation are Copyright (C) 2003
20 : * IBM Corporation. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@meer.net>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 : #include "prdtoa.h"
39 :
40 : #ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
41 46173401 : nsTSubstring_CharT::nsTSubstring_CharT( char_type *data, size_type length,
42 : PRUint32 flags)
43 : : mData(data),
44 : mLength(length),
45 46173401 : mFlags(flags)
46 : {
47 46173401 : if (flags & F_OWNED) {
48 42 : STRING_STAT_INCREMENT(Adopt);
49 : #ifdef NS_BUILD_REFCNT_LOGGING
50 42 : NS_LogCtor(mData, "StringAdopt", 1);
51 : #endif
52 : }
53 46173401 : }
54 : #endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */
55 :
56 : /**
57 : * helper function for down-casting a nsTSubstring to a nsTFixedString.
58 : */
59 : inline const nsTFixedString_CharT*
60 16467516 : AsFixedString( const nsTSubstring_CharT* s )
61 : {
62 16467516 : return static_cast<const nsTFixedString_CharT*>(s);
63 : }
64 :
65 :
66 : /**
67 : * this function is called to prepare mData for writing. the given capacity
68 : * indicates the required minimum storage size for mData, in sizeof(char_type)
69 : * increments. this function returns true if the operation succeeds. it also
70 : * returns the old data and old flags members if mData is newly allocated.
71 : * the old data must be released by the caller.
72 : */
73 : bool
74 12118904 : nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, PRUint32* oldFlags )
75 : {
76 : // initialize to no old data
77 12118904 : *oldData = nsnull;
78 12118904 : *oldFlags = 0;
79 :
80 12118904 : size_type curCapacity = Capacity();
81 :
82 : // If |capacity > kMaxCapacity|, then our doubling algorithm may not be
83 : // able to allocate it. Just bail out in cases like that. We don't want
84 : // to be allocating 2GB+ strings anyway.
85 : PR_STATIC_ASSERT((sizeof(nsStringBuffer) & 0x1) == 0);
86 : const size_type kMaxCapacity =
87 12118802 : (size_type(-1)/2 - sizeof(nsStringBuffer)) / sizeof(char_type) - 2;
88 12118802 : if (capacity > kMaxCapacity) {
89 : // Also assert for |capacity| equal to |size_type(-1)|, since we used to
90 : // use that value to flag immutability.
91 0 : NS_ASSERTION(capacity != size_type(-1), "Bogus capacity");
92 0 : return false;
93 : }
94 :
95 : // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
96 : // need to allocate a new buffer. We cannot use the existing buffer even
97 : // though it might be large enough.
98 :
99 12118802 : if (curCapacity != 0)
100 : {
101 3765759 : if (capacity <= curCapacity) {
102 2832574 : mFlags &= ~F_VOIDED; // mutation clears voided flag
103 2832574 : return true;
104 : }
105 :
106 : // Use doubling algorithm when forced to increase available capacity.
107 933185 : size_type temp = curCapacity;
108 2895040 : while (temp < capacity)
109 1028670 : temp <<= 1;
110 933185 : NS_ASSERTION(NS_MIN(temp, kMaxCapacity) >= capacity,
111 : "should have hit the early return at the top");
112 933185 : capacity = NS_MIN(temp, kMaxCapacity);
113 : }
114 :
115 : //
116 : // several cases:
117 : //
118 : // (1) we have a shared buffer (mFlags & F_SHARED)
119 : // (2) we have an owned buffer (mFlags & F_OWNED)
120 : // (3) we have a fixed buffer (mFlags & F_FIXED)
121 : // (4) we have a readonly buffer
122 : //
123 : // requiring that we in some cases preserve the data before creating
124 : // a new buffer complicates things just a bit ;-)
125 : //
126 :
127 9286228 : size_type storageSize = (capacity + 1) * sizeof(char_type);
128 :
129 : // case #1
130 9286228 : if (mFlags & F_SHARED)
131 : {
132 748661 : nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
133 748661 : if (!hdr->IsReadonly())
134 : {
135 557702 : nsStringBuffer *newHdr = nsStringBuffer::Realloc(hdr, storageSize);
136 557702 : if (!newHdr)
137 0 : return false; // out-of-memory (original header left intact)
138 :
139 557702 : hdr = newHdr;
140 557702 : mData = (char_type*) hdr->Data();
141 557702 : mFlags &= ~F_VOIDED; // mutation clears voided flag
142 557702 : return true;
143 : }
144 : }
145 :
146 : char_type* newData;
147 : PRUint32 newDataFlags;
148 :
149 : // if we have a fixed buffer of sufficient size, then use it. this helps
150 : // avoid heap allocations.
151 8728526 : if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity))
152 : {
153 1234227 : newData = AsFixedString(this)->mFixedBuf;
154 1234227 : newDataFlags = F_TERMINATED | F_FIXED;
155 : }
156 : else
157 : {
158 : // if we reach here then, we must allocate a new buffer. we cannot
159 : // make use of our F_OWNED or F_FIXED buffers because they are not
160 : // large enough.
161 :
162 7494299 : nsStringBuffer* newHdr = nsStringBuffer::Alloc(storageSize);
163 7494489 : if (!newHdr)
164 0 : return false; // we are still in a consistent state
165 :
166 7494489 : newData = (char_type*) newHdr->Data();
167 7494487 : newDataFlags = F_TERMINATED | F_SHARED;
168 : }
169 :
170 : // save old data and flags
171 8728714 : *oldData = mData;
172 8728714 : *oldFlags = mFlags;
173 :
174 8728714 : mData = newData;
175 8728714 : SetDataFlags(newDataFlags);
176 :
177 : // mLength does not change
178 :
179 : // though we are not necessarily terminated at the moment, now is probably
180 : // still the best time to set F_TERMINATED.
181 :
182 8728714 : return true;
183 : }
184 :
185 : void
186 71864413 : nsTSubstring_CharT::Finalize()
187 : {
188 71864413 : ::ReleaseData(mData, mFlags);
189 : // mData, mLength, and mFlags are purposefully left dangling
190 71864267 : }
191 :
192 : bool
193 7036240 : nsTSubstring_CharT::ReplacePrepInternal(index_type cutStart, size_type cutLen,
194 : size_type fragLen, size_type newLen)
195 : {
196 : char_type* oldData;
197 : PRUint32 oldFlags;
198 7036240 : if (!MutatePrep(newLen, &oldData, &oldFlags))
199 0 : return false; // out-of-memory
200 :
201 7036282 : if (oldData)
202 : {
203 : // determine whether or not we need to copy part of the old string
204 : // over to the new string.
205 :
206 6267743 : if (cutStart > 0)
207 : {
208 : // copy prefix from old string
209 76070 : char_traits::copy(mData, oldData, cutStart);
210 : }
211 :
212 6267741 : if (cutStart + cutLen < mLength)
213 : {
214 : // copy suffix from old string to new offset
215 3097 : size_type from = cutStart + cutLen;
216 3097 : size_type fromLen = mLength - from;
217 3097 : PRUint32 to = cutStart + fragLen;
218 3097 : char_traits::copy(mData + to, oldData + from, fromLen);
219 : }
220 :
221 6267741 : ::ReleaseData(oldData, oldFlags);
222 : }
223 : else
224 : {
225 : // original data remains intact
226 :
227 : // determine whether or not we need to move part of the existing string
228 : // to make room for the requested hole.
229 768539 : if (fragLen != cutLen && cutStart + cutLen < mLength)
230 : {
231 31783 : PRUint32 from = cutStart + cutLen;
232 31783 : PRUint32 fromLen = mLength - from;
233 31783 : PRUint32 to = cutStart + fragLen;
234 31783 : char_traits::move(mData + to, mData + from, fromLen);
235 : }
236 : }
237 :
238 : // add null terminator (mutable mData always has room for the null-
239 : // terminator).
240 7036272 : mData[newLen] = char_type(0);
241 7036272 : mLength = newLen;
242 :
243 7036272 : return true;
244 : }
245 :
246 : nsTSubstring_CharT::size_type
247 30653458 : nsTSubstring_CharT::Capacity() const
248 : {
249 : // return 0 to indicate an immutable or 0-sized buffer
250 :
251 : size_type capacity;
252 30653458 : if (mFlags & F_SHARED)
253 : {
254 : // if the string is readonly, then we pretend that it has no capacity.
255 3411249 : nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
256 3411249 : if (hdr->IsReadonly())
257 249051 : capacity = 0;
258 : else {
259 3162198 : capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
260 : }
261 : }
262 27242209 : else if (mFlags & F_FIXED)
263 : {
264 12996939 : capacity = AsFixedString(this)->mFixedCapacity;
265 : }
266 14245270 : else if (mFlags & F_OWNED)
267 : {
268 : // we don't store the capacity of an adopted buffer because that would
269 : // require an additional member field. the best we can do is base the
270 : // capacity on our length. remains to be seen if this is the right
271 : // trade-off.
272 1654 : capacity = mLength;
273 : }
274 : else
275 : {
276 14243616 : capacity = 0;
277 : }
278 :
279 30653457 : return capacity;
280 : }
281 :
282 : bool
283 10571804 : nsTSubstring_CharT::EnsureMutable( size_type newLen )
284 : {
285 10571804 : if (newLen == size_type(-1) || newLen == mLength)
286 : {
287 8632047 : if (mFlags & (F_FIXED | F_OWNED))
288 6159579 : return true;
289 2472468 : if ((mFlags & F_SHARED) && !nsStringBuffer::FromData(mData)->IsReadonly())
290 2440475 : return true;
291 :
292 31993 : newLen = mLength;
293 : }
294 1971750 : return SetLength(newLen);
295 : }
296 :
297 : // ---------------------------------------------------------------------------
298 :
299 : // This version of Assign is optimized for single-character assignment.
300 : void
301 600 : nsTSubstring_CharT::Assign( char_type c )
302 : {
303 600 : if (ReplacePrep(0, mLength, 1))
304 600 : *mData = c;
305 600 : }
306 :
307 :
308 : void
309 13930463 : nsTSubstring_CharT::Assign( const char_type* data, size_type length )
310 : {
311 : // unfortunately, some callers pass null :-(
312 13930463 : if (!data)
313 : {
314 6079 : Truncate();
315 6079 : return;
316 : }
317 :
318 13924384 : if (length == size_type(-1))
319 6886134 : length = char_traits::length(data);
320 :
321 13924384 : if (IsDependentOn(data, data + length))
322 : {
323 : // take advantage of sharing here...
324 252 : Assign(string_type(data, length));
325 252 : return;
326 : }
327 :
328 13924098 : if (ReplacePrep(0, mLength, length))
329 13924136 : char_traits::copy(mData, data, length);
330 : }
331 :
332 : void
333 436784 : nsTSubstring_CharT::AssignASCII( const char* data, size_type length )
334 : {
335 : // A Unicode string can't depend on an ASCII string buffer,
336 : // so this dependence check only applies to CStrings.
337 : #ifdef CharT_is_char
338 232241 : if (IsDependentOn(data, data + length))
339 : {
340 : // take advantage of sharing here...
341 0 : Assign(string_type(data, length));
342 0 : return;
343 : }
344 : #endif
345 :
346 436784 : if (ReplacePrep(0, mLength, length))
347 436784 : char_traits::copyASCII(mData, data, length);
348 204543 : }
349 :
350 : void
351 12409 : nsTSubstring_CharT::AssignASCII( const char* data )
352 : {
353 12409 : AssignASCII(data, strlen(data));
354 12409 : }
355 :
356 : void
357 11759329 : nsTSubstring_CharT::Assign( const self_type& str )
358 : {
359 : // |str| could be sharable. we need to check its flags to know how to
360 : // deal with it.
361 :
362 11759329 : if (&str == this)
363 2 : return;
364 :
365 11759327 : if (!str.mLength)
366 : {
367 1824783 : Truncate();
368 1824783 : mFlags |= str.mFlags & F_VOIDED;
369 : }
370 9934544 : else if (str.mFlags & F_SHARED)
371 : {
372 : // nice! we can avoid a string copy :-)
373 :
374 : // |str| should be null-terminated
375 4995236 : NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated");
376 :
377 4995236 : ::ReleaseData(mData, mFlags);
378 :
379 4995236 : mData = str.mData;
380 4995236 : mLength = str.mLength;
381 4995236 : SetDataFlags(F_TERMINATED | F_SHARED);
382 :
383 : // get an owning reference to the mData
384 4995237 : nsStringBuffer::FromData(mData)->AddRef();
385 : }
386 : else
387 : {
388 : // else, treat this like an ordinary assignment.
389 4939308 : Assign(str.Data(), str.Length());
390 : }
391 : }
392 :
393 : void
394 276268 : nsTSubstring_CharT::Assign( const substring_tuple_type& tuple )
395 : {
396 276268 : if (tuple.IsDependentOn(mData, mData + mLength))
397 : {
398 : // take advantage of sharing here...
399 0 : Assign(string_type(tuple));
400 0 : return;
401 : }
402 :
403 276268 : size_type length = tuple.Length();
404 :
405 : // don't use ReplacePrep here because it changes the length
406 : char_type* oldData;
407 : PRUint32 oldFlags;
408 276268 : if (MutatePrep(length, &oldData, &oldFlags)) {
409 276268 : if (oldData)
410 222487 : ::ReleaseData(oldData, oldFlags);
411 :
412 276268 : tuple.WriteTo(mData, length);
413 276268 : mData[length] = 0;
414 276268 : mLength = length;
415 : }
416 : }
417 :
418 : void
419 269562 : nsTSubstring_CharT::Adopt( char_type* data, size_type length )
420 : {
421 269562 : if (data)
422 : {
423 237967 : ::ReleaseData(mData, mFlags);
424 :
425 237967 : if (length == size_type(-1))
426 233514 : length = char_traits::length(data);
427 :
428 237967 : mData = data;
429 237967 : mLength = length;
430 237967 : SetDataFlags(F_TERMINATED | F_OWNED);
431 :
432 237967 : STRING_STAT_INCREMENT(Adopt);
433 : #ifdef NS_BUILD_REFCNT_LOGGING
434 : // Treat this as construction of a "StringAdopt" object for leak
435 : // tracking purposes.
436 237967 : NS_LogCtor(mData, "StringAdopt", 1);
437 : #endif // NS_BUILD_REFCNT_LOGGING
438 : }
439 : else
440 : {
441 31595 : SetIsVoid(true);
442 : }
443 269562 : }
444 :
445 :
446 : // This version of Replace is optimized for single-character replacement.
447 : void
448 760675 : nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c )
449 : {
450 760675 : cutStart = NS_MIN(cutStart, Length());
451 :
452 760675 : if (ReplacePrep(cutStart, cutLength, 1))
453 760675 : mData[cutStart] = c;
454 760675 : }
455 :
456 :
457 : void
458 2731365 : nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length )
459 : {
460 : // unfortunately, some callers pass null :-(
461 2731365 : if (!data)
462 : {
463 0 : length = 0;
464 : }
465 : else
466 : {
467 2731365 : if (length == size_type(-1))
468 708487 : length = char_traits::length(data);
469 :
470 2731365 : if (IsDependentOn(data, data + length))
471 : {
472 0 : nsTAutoString_CharT temp(data, length);
473 0 : Replace(cutStart, cutLength, temp);
474 : return;
475 : }
476 : }
477 :
478 2731365 : cutStart = NS_MIN(cutStart, Length());
479 :
480 2731365 : if (ReplacePrep(cutStart, cutLength, length) && length > 0)
481 2625122 : char_traits::copy(mData + cutStart, data, length);
482 : }
483 :
484 : void
485 333848 : nsTSubstring_CharT::ReplaceASCII( index_type cutStart, size_type cutLength, const char* data, size_type length )
486 : {
487 333848 : if (length == size_type(-1))
488 713 : length = strlen(data);
489 :
490 : // A Unicode string can't depend on an ASCII string buffer,
491 : // so this dependence check only applies to CStrings.
492 : #ifdef CharT_is_char
493 327808 : if (IsDependentOn(data, data + length))
494 : {
495 0 : nsTAutoString_CharT temp(data, length);
496 0 : Replace(cutStart, cutLength, temp);
497 : return;
498 : }
499 : #endif
500 :
501 333848 : cutStart = NS_MIN(cutStart, Length());
502 :
503 333848 : if (ReplacePrep(cutStart, cutLength, length) && length > 0)
504 333848 : char_traits::copyASCII(mData + cutStart, data, length);
505 6040 : }
506 :
507 : void
508 562785 : nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& tuple )
509 : {
510 562785 : if (tuple.IsDependentOn(mData, mData + mLength))
511 : {
512 0 : nsTAutoString_CharT temp(tuple);
513 0 : Replace(cutStart, cutLength, temp);
514 : return;
515 : }
516 :
517 562785 : size_type length = tuple.Length();
518 :
519 562785 : cutStart = NS_MIN(cutStart, Length());
520 :
521 562785 : if (ReplacePrep(cutStart, cutLength, length) && length > 0)
522 562785 : tuple.WriteTo(mData + cutStart, length);
523 : }
524 :
525 : bool
526 11784205 : nsTSubstring_CharT::SetCapacity( size_type capacity )
527 : {
528 : // capacity does not include room for the terminating null char
529 :
530 : // if our capacity is reduced to zero, then free our buffer.
531 11784205 : if (capacity == 0)
532 : {
533 6977763 : ::ReleaseData(mData, mFlags);
534 6977762 : mData = char_traits::sEmptyBuffer;
535 6977762 : mLength = 0;
536 6977762 : SetDataFlags(F_TERMINATED);
537 : }
538 : else
539 : {
540 : char_type* oldData;
541 : PRUint32 oldFlags;
542 4806442 : if (!MutatePrep(capacity, &oldData, &oldFlags))
543 0 : return false; // out-of-memory
544 :
545 : // compute new string length
546 4806442 : size_type newLen = NS_MIN(mLength, capacity);
547 :
548 4806442 : if (oldData)
549 : {
550 : // preserve old data
551 2218856 : if (mLength > 0)
552 132861 : char_traits::copy(mData, oldData, newLen);
553 :
554 2218856 : ::ReleaseData(oldData, oldFlags);
555 : }
556 :
557 : // adjust mLength if our buffer shrunk down in size
558 4806442 : if (newLen < mLength)
559 427217 : mLength = newLen;
560 :
561 : // always null-terminate here, even if the buffer got longer. this is
562 : // for backwards compat with the old string implementation.
563 4806442 : mData[capacity] = char_type(0);
564 : }
565 :
566 11784203 : return true;
567 : }
568 :
569 : bool
570 11711014 : nsTSubstring_CharT::SetLength( size_type length )
571 : {
572 11711014 : if (!SetCapacity(length))
573 0 : return false;
574 :
575 11711013 : mLength = length;
576 11711013 : return true;
577 : }
578 :
579 : void
580 386662 : nsTSubstring_CharT::SetIsVoid( bool val )
581 : {
582 386662 : if (val)
583 : {
584 386646 : Truncate();
585 386646 : mFlags |= F_VOIDED;
586 : }
587 : else
588 : {
589 16 : mFlags &= ~F_VOIDED;
590 : }
591 386662 : }
592 :
593 : bool
594 20985320 : nsTSubstring_CharT::Equals( const self_type& str ) const
595 : {
596 20985320 : return mLength == str.mLength && char_traits::compare(mData, str.mData, mLength) == 0;
597 : }
598 :
599 : bool
600 175121 : nsTSubstring_CharT::Equals( const self_type& str, const comparator_type& comp ) const
601 : {
602 175121 : return mLength == str.mLength && comp(mData, str.mData, mLength, str.mLength) == 0;
603 : }
604 :
605 : bool
606 470841 : nsTSubstring_CharT::Equals( const char_type* data ) const
607 : {
608 : // unfortunately, some callers pass null :-(
609 470841 : if (!data)
610 : {
611 0 : NS_NOTREACHED("null data pointer");
612 0 : return mLength == 0;
613 : }
614 :
615 : // XXX avoid length calculation?
616 470841 : size_type length = char_traits::length(data);
617 470841 : return mLength == length && char_traits::compare(mData, data, mLength) == 0;
618 : }
619 :
620 : bool
621 34 : nsTSubstring_CharT::Equals( const char_type* data, const comparator_type& comp ) const
622 : {
623 : // unfortunately, some callers pass null :-(
624 34 : if (!data)
625 : {
626 0 : NS_NOTREACHED("null data pointer");
627 0 : return mLength == 0;
628 : }
629 :
630 : // XXX avoid length calculation?
631 34 : size_type length = char_traits::length(data);
632 34 : return mLength == length && comp(mData, data, mLength, length) == 0;
633 : }
634 :
635 : bool
636 1564158 : nsTSubstring_CharT::EqualsASCII( const char* data, size_type len ) const
637 : {
638 1564158 : return mLength == len && char_traits::compareASCII(mData, data, len) == 0;
639 : }
640 :
641 : bool
642 7246 : nsTSubstring_CharT::EqualsASCII( const char* data ) const
643 : {
644 7246 : return char_traits::compareASCIINullTerminated(mData, mLength, data) == 0;
645 : }
646 :
647 : bool
648 331597 : nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data, size_type len ) const
649 : {
650 331597 : return mLength == len && char_traits::compareLowerCaseToASCII(mData, data, len) == 0;
651 : }
652 :
653 : bool
654 4061 : nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data ) const
655 : {
656 4061 : return char_traits::compareLowerCaseToASCIINullTerminated(mData, mLength, data) == 0;
657 : }
658 :
659 : nsTSubstring_CharT::size_type
660 25 : nsTSubstring_CharT::CountChar( char_type c ) const
661 : {
662 25 : const char_type *start = mData;
663 25 : const char_type *end = mData + mLength;
664 :
665 25 : return NS_COUNT(start, end, c);
666 : }
667 :
668 : PRInt32
669 1709023 : nsTSubstring_CharT::FindChar( char_type c, index_type offset ) const
670 : {
671 1709023 : if (offset < mLength)
672 : {
673 1698002 : const char_type* result = char_traits::find(mData + offset, mLength - offset, c);
674 1698002 : if (result)
675 71750 : return result - mData;
676 : }
677 1637273 : return -1;
678 : }
679 :
680 : void
681 0 : nsTSubstring_CharT::StripChar( char_type aChar, PRInt32 aOffset )
682 : {
683 0 : if (mLength == 0 || aOffset >= PRInt32(mLength))
684 0 : return;
685 :
686 0 : EnsureMutable(); // XXX do this lazily?
687 :
688 : // XXX(darin): this code should defer writing until necessary.
689 :
690 0 : char_type* to = mData + aOffset;
691 0 : char_type* from = mData + aOffset;
692 0 : char_type* end = mData + mLength;
693 :
694 0 : while (from < end)
695 : {
696 0 : char_type theChar = *from++;
697 0 : if (aChar != theChar)
698 0 : *to++ = theChar;
699 : }
700 0 : *to = char_type(0); // add the null
701 0 : mLength = to - mData;
702 : }
703 :
704 : void
705 2 : nsTSubstring_CharT::StripChars( const char_type* aChars, PRUint32 aOffset )
706 : {
707 2 : if (aOffset >= PRUint32(mLength))
708 2 : return;
709 :
710 0 : EnsureMutable(); // XXX do this lazily?
711 :
712 : // XXX(darin): this code should defer writing until necessary.
713 :
714 0 : char_type* to = mData + aOffset;
715 0 : char_type* from = mData + aOffset;
716 0 : char_type* end = mData + mLength;
717 :
718 0 : while (from < end)
719 : {
720 0 : char_type theChar = *from++;
721 0 : const char_type* test = aChars;
722 :
723 0 : for (; *test && *test != theChar; ++test);
724 :
725 0 : if (!*test) {
726 : // Not stripped, copy this char.
727 0 : *to++ = theChar;
728 : }
729 : }
730 0 : *to = char_type(0); // add the null
731 0 : mLength = to - mData;
732 : }
733 :
734 39118 : void nsTSubstring_CharT::AppendPrintf31( const char* format, ...)
735 : {
736 : char buf[32];
737 : va_list ap;
738 39118 : va_start(ap, format);
739 39118 : PRUint32 len = PR_vsnprintf(buf, sizeof(buf), format, ap);
740 39118 : AppendASCII(buf, len);
741 39118 : va_end(ap);
742 39118 : }
743 :
744 709 : void nsTSubstring_CharT::AppendPrintf( const char* format, ...)
745 : {
746 : char *buf;
747 : va_list ap;
748 709 : va_start(ap, format);
749 709 : buf = PR_vsmprintf(format, ap);
750 709 : AppendASCII(buf);
751 709 : PR_smprintf_free(buf);
752 709 : va_end(ap);
753 709 : }
754 :
755 :
756 : /* hack to make sure we define Modified_cnvtf only once */
757 : #ifdef CharT_is_PRUnichar
758 : /**
759 : * This is a copy of |PR_cnvtf| with a bug fixed. (The second argument
760 : * of PR_dtoa is 2 rather than 1.)
761 : *
762 : * XXX(darin): if this is the right thing, then why wasn't it fixed in NSPR?!?
763 : */
764 : static void
765 0 : Modified_cnvtf(char *buf, int bufsz, int prcsn, double fval)
766 : {
767 : PRIntn decpt, sign, numdigits;
768 : char *num, *nump;
769 0 : char *bufp = buf;
770 : char *endnum;
771 :
772 : /* If anything fails, we store an empty string in 'buf' */
773 0 : num = (char*)malloc(bufsz);
774 0 : if (num == NULL) {
775 0 : buf[0] = '\0';
776 0 : return;
777 : }
778 0 : if (PR_dtoa(fval, 2, prcsn, &decpt, &sign, &endnum, num, bufsz)
779 : == PR_FAILURE) {
780 0 : buf[0] = '\0';
781 0 : goto done;
782 : }
783 0 : numdigits = endnum - num;
784 0 : nump = num;
785 :
786 : /*
787 : * The NSPR code had a fancy way of checking that we weren't dealing
788 : * with -0.0 or -NaN, but I'll just use < instead.
789 : * XXX Should we check !isnan(fval) as well? Is it portable? We
790 : * probably don't need to bother since NAN isn't portable.
791 : */
792 0 : if (sign && fval < 0.0f) {
793 0 : *bufp++ = '-';
794 : }
795 :
796 0 : if (decpt == 9999) {
797 0 : while ((*bufp++ = *nump++) != 0) {} /* nothing to execute */
798 0 : goto done;
799 : }
800 :
801 0 : if (decpt > (prcsn+1) || decpt < -(prcsn-1) || decpt < -5) {
802 0 : *bufp++ = *nump++;
803 0 : if (numdigits != 1) {
804 0 : *bufp++ = '.';
805 : }
806 :
807 0 : while (*nump != '\0') {
808 0 : *bufp++ = *nump++;
809 : }
810 0 : *bufp++ = 'e';
811 0 : PR_snprintf(bufp, bufsz - (bufp - buf), "%+d", decpt-1);
812 : }
813 0 : else if (decpt >= 0) {
814 0 : if (decpt == 0) {
815 0 : *bufp++ = '0';
816 : }
817 : else {
818 0 : while (decpt--) {
819 0 : if (*nump != '\0') {
820 0 : *bufp++ = *nump++;
821 : }
822 : else {
823 0 : *bufp++ = '0';
824 : }
825 : }
826 : }
827 0 : if (*nump != '\0') {
828 0 : *bufp++ = '.';
829 0 : while (*nump != '\0') {
830 0 : *bufp++ = *nump++;
831 : }
832 : }
833 0 : *bufp++ = '\0';
834 : }
835 0 : else if (decpt < 0) {
836 0 : *bufp++ = '0';
837 0 : *bufp++ = '.';
838 0 : while (decpt++) {
839 0 : *bufp++ = '0';
840 : }
841 :
842 0 : while (*nump != '\0') {
843 0 : *bufp++ = *nump++;
844 : }
845 0 : *bufp++ = '\0';
846 : }
847 : done:
848 0 : free(num);
849 : }
850 : #endif /* CharT_is_PRUnichar */
851 :
852 : void
853 0 : nsTSubstring_CharT::DoAppendFloat( double aFloat, int digits )
854 : {
855 : char buf[40];
856 : // Use Modified_cnvtf, which is locale-insensitive, instead of the
857 : // locale-sensitive PR_snprintf or sprintf(3)
858 0 : Modified_cnvtf(buf, sizeof(buf), digits, aFloat);
859 0 : AppendASCII(buf);
860 0 : }
861 :
862 : size_t
863 0 : nsTSubstring_CharT::SizeOfExcludingThisMustBeUnshared(
864 : nsMallocSizeOfFun mallocSizeOf) const
865 : {
866 0 : if (mFlags & F_SHARED) {
867 : return nsStringBuffer::FromData(mData)->
868 0 : SizeOfIncludingThisMustBeUnshared(mallocSizeOf);
869 : }
870 0 : if (mFlags & F_OWNED) {
871 0 : return mallocSizeOf(mData);
872 : }
873 :
874 : // If we reach here, exactly one of the following must be true:
875 : // - F_VOIDED is set, and mData points to sEmptyBuffer;
876 : // - F_FIXED is set, and mData points to a buffer within a string
877 : // object (e.g. nsAutoString);
878 : // - None of F_SHARED, F_OWNED, F_FIXED is set, and mData points to a buffer
879 : // owned by something else.
880 : //
881 : // In all three cases, we don't measure it.
882 0 : return 0;
883 : }
884 :
885 : size_t
886 0 : nsTSubstring_CharT::SizeOfExcludingThisIfUnshared(
887 : nsMallocSizeOfFun mallocSizeOf) const
888 : {
889 : // This is identical to SizeOfExcludingThisMustBeUnshared except for the
890 : // F_SHARED case.
891 0 : if (mFlags & F_SHARED) {
892 : return nsStringBuffer::FromData(mData)->
893 0 : SizeOfIncludingThisIfUnshared(mallocSizeOf);
894 : }
895 0 : if (mFlags & F_OWNED) {
896 0 : return mallocSizeOf(mData);
897 : }
898 0 : return 0;
899 : }
900 :
901 : size_t
902 0 : nsTSubstring_CharT::SizeOfIncludingThisMustBeUnshared(
903 : nsMallocSizeOfFun mallocSizeOf) const
904 : {
905 0 : return mallocSizeOf(this) + SizeOfExcludingThisMustBeUnshared(mallocSizeOf);
906 : }
907 :
908 : size_t
909 0 : nsTSubstring_CharT::SizeOfIncludingThisIfUnshared(
910 : nsMallocSizeOfFun mallocSizeOf) const
911 : {
912 0 : return mallocSizeOf(this) + SizeOfExcludingThisIfUnshared(mallocSizeOf);
913 : }
914 :
|