Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / xpcom / string / src / nsTSubstring.cpp
blobeab6a6fabda3b5f72ff83ab0dcd230718c97d1df
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
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/
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.
16 * The Original Code is Mozilla.
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.
22 * Contributor(s):
23 * Darin Fisher <darin@meer.net>
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.
37 * ***** END LICENSE BLOCK ***** */
39 nsTSubstring_CharT::nsTSubstring_CharT( char_type *data, size_type length,
40 PRUint32 flags)
41 : mData(data),
42 mLength(length),
43 mFlags(flags)
45 if (flags & F_OWNED) {
46 STRING_STAT_INCREMENT(Adopt);
47 #ifdef NS_BUILD_REFCNT_LOGGING
48 NS_LogCtor(mData, "StringAdopt", 1);
49 #endif
53 nsTSubstring_CharT::nsTSubstring_CharT(const substring_tuple_type& tuple)
54 : mData(nsnull),
55 mLength(0),
56 mFlags(F_NONE)
58 Assign(tuple);
61 nsTSubstring_CharT::nsTSubstring_CharT()
62 : mData(const_cast<char_type*>(char_traits::sEmptyBuffer)),
63 mLength(0),
64 mFlags(F_TERMINATED) {}
66 nsTSubstring_CharT::nsTSubstring_CharT( PRUint32 flags )
67 : mFlags(flags) {}
69 nsTSubstring_CharT::nsTSubstring_CharT( const self_type& str )
70 : mData(str.mData),
71 mLength(str.mLength),
72 mFlags(str.mFlags & (F_TERMINATED | F_VOIDED)) {}
74 /**
75 * helper function for down-casting a nsTSubstring to a nsTFixedString.
77 inline const nsTFixedString_CharT*
78 AsFixedString( const nsTSubstring_CharT* s )
80 return static_cast<const nsTFixedString_CharT*>(s);
84 /**
85 * this function is called to prepare mData for writing. the given capacity
86 * indicates the required minimum storage size for mData, in sizeof(char_type)
87 * increments. this function returns true if the operation succeeds. it also
88 * returns the old data and old flags members if mData is newly allocated.
89 * the old data must be released by the caller.
91 PRBool
92 nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, PRUint32* oldFlags )
94 // initialize to no old data
95 *oldData = nsnull;
96 *oldFlags = 0;
98 size_type curCapacity = Capacity();
100 // If |capacity > size_type(-1)/2|, then our doubling algorithm may not be
101 // able to allocate it. Just bail out in cases like that. We don't want
102 // to be allocating 2GB+ strings anyway.
103 if (capacity > size_type(-1)/2) {
104 // Also assert for |capacity| equal to |size_type(-1)|, since we use that value to
105 // flag immutability.
106 NS_ASSERTION(capacity != size_type(-1), "Bogus capacity");
107 return PR_FALSE;
110 // |curCapacity == size_type(-1)| means that the buffer is immutable, so we
111 // need to allocate a new buffer. we cannot use the existing buffer even
112 // though it might be large enough.
114 if (curCapacity != size_type(-1))
116 if (capacity <= curCapacity) {
117 mFlags &= ~F_VOIDED; // mutation clears voided flag
118 return PR_TRUE;
121 if (curCapacity > 0)
123 // use doubling algorithm when forced to increase available
124 // capacity.
125 PRUint32 temp = curCapacity;
126 while (temp < capacity)
127 temp <<= 1;
128 capacity = temp;
133 // several cases:
135 // (1) we have a shared buffer (mFlags & F_SHARED)
136 // (2) we have an owned buffer (mFlags & F_OWNED)
137 // (3) we have a fixed buffer (mFlags & F_FIXED)
138 // (4) we have a readonly buffer
140 // requiring that we in some cases preserve the data before creating
141 // a new buffer complicates things just a bit ;-)
144 size_type storageSize = (capacity + 1) * sizeof(char_type);
146 // case #1
147 if (mFlags & F_SHARED)
149 nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
150 if (!hdr->IsReadonly())
152 nsStringBuffer *newHdr = nsStringBuffer::Realloc(hdr, storageSize);
153 if (!newHdr)
154 return PR_FALSE; // out-of-memory (original header left intact)
156 hdr = newHdr;
157 mData = (char_type*) hdr->Data();
158 mFlags &= ~F_VOIDED; // mutation clears voided flag
159 return PR_TRUE;
163 char_type* newData;
164 PRUint32 newDataFlags;
166 // if we have a fixed buffer of sufficient size, then use it. this helps
167 // avoid heap allocations.
168 if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity))
170 newData = AsFixedString(this)->mFixedBuf;
171 newDataFlags = F_TERMINATED | F_FIXED;
173 else
175 // if we reach here then, we must allocate a new buffer. we cannot
176 // make use of our F_OWNED or F_FIXED buffers because they are not
177 // large enough.
179 nsStringBuffer* newHdr = nsStringBuffer::Alloc(storageSize);
180 if (!newHdr)
181 return PR_FALSE; // we are still in a consistent state
183 newData = (char_type*) newHdr->Data();
184 newDataFlags = F_TERMINATED | F_SHARED;
187 // save old data and flags
188 *oldData = mData;
189 *oldFlags = mFlags;
191 mData = newData;
192 SetDataFlags(newDataFlags);
194 // mLength does not change
196 // though we are not necessarily terminated at the moment, now is probably
197 // still the best time to set F_TERMINATED.
199 return PR_TRUE;
202 void
203 nsTSubstring_CharT::Finalize()
205 ::ReleaseData(mData, mFlags);
206 // mData, mLength, and mFlags are purposefully left dangling
209 nsTSubstring_CharT::~nsTSubstring_CharT()
211 Finalize();
214 PRBool
215 nsTSubstring_CharT::ReplacePrep( index_type cutStart, size_type cutLen, size_type fragLen )
217 // bound cut length
218 cutLen = NS_MIN(cutLen, mLength - cutStart);
220 PRUint32 newLen = mLength - cutLen + fragLen;
222 char_type* oldData;
223 PRUint32 oldFlags;
224 if (!MutatePrep(newLen, &oldData, &oldFlags))
225 return PR_FALSE; // out-of-memory
227 if (oldData)
229 // determine whether or not we need to copy part of the old string
230 // over to the new string.
232 if (cutStart > 0)
234 // copy prefix from old string
235 char_traits::copy(mData, oldData, cutStart);
238 if (cutStart + cutLen < mLength)
240 // copy suffix from old string to new offset
241 size_type from = cutStart + cutLen;
242 size_type fromLen = mLength - from;
243 PRUint32 to = cutStart + fragLen;
244 char_traits::copy(mData + to, oldData + from, fromLen);
247 ::ReleaseData(oldData, oldFlags);
249 else
251 // original data remains intact
253 // determine whether or not we need to move part of the existing string
254 // to make room for the requested hole.
255 if (fragLen != cutLen && cutStart + cutLen < mLength)
257 PRUint32 from = cutStart + cutLen;
258 PRUint32 fromLen = mLength - from;
259 PRUint32 to = cutStart + fragLen;
260 char_traits::move(mData + to, mData + from, fromLen);
264 // add null terminator (mutable mData always has room for the null-
265 // terminator).
266 mData[newLen] = char_type(0);
267 mLength = newLen;
269 return PR_TRUE;
272 nsTSubstring_CharT::size_type
273 nsTSubstring_CharT::Capacity() const
275 // return size_type(-1) to indicate an immutable buffer
277 size_type capacity;
278 if (mFlags & F_SHARED)
280 // if the string is readonly, then we pretend that it has no capacity.
281 nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
282 if (hdr->IsReadonly())
283 capacity = size_type(-1);
284 else
285 capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
287 else if (mFlags & F_FIXED)
289 capacity = AsFixedString(this)->mFixedCapacity;
291 else if (mFlags & F_OWNED)
293 // we don't store the capacity of an adopted buffer because that would
294 // require an additional member field. the best we can do is base the
295 // capacity on our length. remains to be seen if this is the right
296 // trade-off.
297 capacity = mLength;
299 else
301 capacity = size_type(-1);
304 return capacity;
307 PRBool
308 nsTSubstring_CharT::EnsureMutable( size_type newLen )
310 if (newLen == size_type(-1) || newLen == mLength)
312 if (mFlags & (F_FIXED | F_OWNED))
313 return PR_TRUE;
314 if ((mFlags & F_SHARED) && !nsStringBuffer::FromData(mData)->IsReadonly())
315 return PR_TRUE;
317 // promote to a shared string buffer
318 char_type* prevData = mData;
319 Assign(string_type(mData, mLength));
320 return mData != prevData;
322 else
324 SetLength(newLen);
325 return mLength == newLen;
329 // ---------------------------------------------------------------------------
331 // This version of Assign is optimized for single-character assignment.
332 void
333 nsTSubstring_CharT::Assign( char_type c )
335 if (ReplacePrep(0, mLength, 1))
336 *mData = c;
340 void
341 nsTSubstring_CharT::Assign( const char_type* data, size_type length )
343 // unfortunately, some callers pass null :-(
344 if (!data)
346 Truncate();
347 return;
350 if (length == size_type(-1))
351 length = char_traits::length(data);
353 if (IsDependentOn(data, data + length))
355 // take advantage of sharing here...
356 Assign(string_type(data, length));
357 return;
360 if (ReplacePrep(0, mLength, length))
361 char_traits::copy(mData, data, length);
364 void
365 nsTSubstring_CharT::AssignASCII( const char* data, size_type length )
367 // A Unicode string can't depend on an ASCII string buffer,
368 // so this dependence check only applies to CStrings.
369 #ifdef CharT_is_char
370 if (IsDependentOn(data, data + length))
372 // take advantage of sharing here...
373 Assign(string_type(data, length));
374 return;
376 #endif
378 if (ReplacePrep(0, mLength, length))
379 char_traits::copyASCII(mData, data, length);
382 void
383 nsTSubstring_CharT::AssignASCII( const char* data )
385 AssignASCII(data, strlen(data));
388 void
389 nsTSubstring_CharT::Assign( const self_type& str )
391 // |str| could be sharable. we need to check its flags to know how to
392 // deal with it.
394 if (&str == this)
395 return;
397 if (str.mFlags & F_SHARED)
399 // nice! we can avoid a string copy :-)
401 // |str| should be null-terminated
402 NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated");
404 ::ReleaseData(mData, mFlags);
406 mData = str.mData;
407 mLength = str.mLength;
408 SetDataFlags(F_TERMINATED | F_SHARED);
410 // get an owning reference to the mData
411 nsStringBuffer::FromData(mData)->AddRef();
413 else if (str.mFlags & F_VOIDED)
415 // inherit the F_VOIDED attribute
416 SetIsVoid(PR_TRUE);
418 else
420 // else, treat this like an ordinary assignment.
421 Assign(str.Data(), str.Length());
425 void
426 nsTSubstring_CharT::Assign( const substring_tuple_type& tuple )
428 if (tuple.IsDependentOn(mData, mData + mLength))
430 // take advantage of sharing here...
431 Assign(string_type(tuple));
432 return;
435 size_type length = tuple.Length();
437 if (ReplacePrep(0, mLength, length) && length)
438 tuple.WriteTo(mData, length);
441 void
442 nsTSubstring_CharT::Adopt( char_type* data, size_type length )
444 if (data)
446 ::ReleaseData(mData, mFlags);
448 if (length == size_type(-1))
449 length = char_traits::length(data);
451 mData = data;
452 mLength = length;
453 SetDataFlags(F_TERMINATED | F_OWNED);
455 STRING_STAT_INCREMENT(Adopt);
456 #ifdef NS_BUILD_REFCNT_LOGGING
457 // Treat this as construction of a "StringAdopt" object for leak
458 // tracking purposes.
459 NS_LogCtor(mData, "StringAdopt", 1);
460 #endif // NS_BUILD_REFCNT_LOGGING
462 else
464 SetIsVoid(PR_TRUE);
469 // This version of Replace is optimized for single-character replacement.
470 void
471 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c )
473 cutStart = PR_MIN(cutStart, Length());
475 if (ReplacePrep(cutStart, cutLength, 1))
476 mData[cutStart] = c;
480 void
481 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length )
483 // unfortunately, some callers pass null :-(
484 if (!data)
486 length = 0;
488 else
490 if (length == size_type(-1))
491 length = char_traits::length(data);
493 if (IsDependentOn(data, data + length))
495 nsTAutoString_CharT temp(data, length);
496 Replace(cutStart, cutLength, temp);
497 return;
501 cutStart = PR_MIN(cutStart, Length());
503 if (ReplacePrep(cutStart, cutLength, length) && length > 0)
504 char_traits::copy(mData + cutStart, data, length);
507 void
508 nsTSubstring_CharT::ReplaceASCII( index_type cutStart, size_type cutLength, const char* data, size_type length )
510 if (length == size_type(-1))
511 length = strlen(data);
513 // A Unicode string can't depend on an ASCII string buffer,
514 // so this dependence check only applies to CStrings.
515 #ifdef CharT_is_char
516 if (IsDependentOn(data, data + length))
518 nsTAutoString_CharT temp(data, length);
519 Replace(cutStart, cutLength, temp);
520 return;
522 #endif
524 cutStart = PR_MIN(cutStart, Length());
526 if (ReplacePrep(cutStart, cutLength, length) && length > 0)
527 char_traits::copyASCII(mData + cutStart, data, length);
530 void
531 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& tuple )
533 if (tuple.IsDependentOn(mData, mData + mLength))
535 nsTAutoString_CharT temp(tuple);
536 Replace(cutStart, cutLength, temp);
537 return;
540 size_type length = tuple.Length();
542 cutStart = PR_MIN(cutStart, Length());
544 if (ReplacePrep(cutStart, cutLength, length) && length > 0)
545 tuple.WriteTo(mData + cutStart, length);
548 void
549 nsTSubstring_CharT::SetCapacity( size_type capacity )
551 // capacity does not include room for the terminating null char
553 // if our capacity is reduced to zero, then free our buffer.
554 if (capacity == 0)
556 ::ReleaseData(mData, mFlags);
557 mData = const_cast<char_type*>(char_traits::sEmptyBuffer);
558 mLength = 0;
559 SetDataFlags(F_TERMINATED);
561 else
563 char_type* oldData;
564 PRUint32 oldFlags;
565 if (!MutatePrep(capacity, &oldData, &oldFlags))
566 return; // out-of-memory
568 // compute new string length
569 size_type newLen = NS_MIN(mLength, capacity);
571 if (oldData)
573 // preserve old data
574 if (mLength > 0)
575 char_traits::copy(mData, oldData, newLen);
577 ::ReleaseData(oldData, oldFlags);
580 // adjust mLength if our buffer shrunk down in size
581 if (newLen < mLength)
582 mLength = newLen;
584 // always null-terminate here, even if the buffer got longer. this is
585 // for backwards compat with the old string implementation.
586 mData[capacity] = char_type(0);
590 void
591 nsTSubstring_CharT::SetLength( size_type length )
593 if (mLength == length) {
594 mFlags &= ~F_VOIDED; // mutation clears voided flag
595 return;
598 SetCapacity(length);
600 // XXX(darin): SetCapacity may fail, but it doesn't give us a way to find
601 // out. We should improve that. For now we just verify that the capacity
602 // changed as expected as a means of error checking.
604 if (Capacity() >= length)
605 mLength = length;
608 void
609 nsTSubstring_CharT::SetIsVoid( PRBool val )
611 if (val)
613 Truncate();
614 mFlags |= F_VOIDED;
616 else
618 mFlags &= ~F_VOIDED;
622 PRBool
623 nsTSubstring_CharT::Equals( const self_type& str ) const
625 return mLength == str.mLength && char_traits::compare(mData, str.mData, mLength) == 0;
628 PRBool
629 nsTSubstring_CharT::Equals( const self_type& str, const comparator_type& comp ) const
631 return mLength == str.mLength && comp(mData, str.mData, mLength) == 0;
634 PRBool
635 nsTSubstring_CharT::Equals( const char_type* data ) const
637 // unfortunately, some callers pass null :-(
638 if (!data)
640 NS_NOTREACHED("null data pointer");
641 return mLength == 0;
644 // XXX avoid length calculation?
645 size_type length = char_traits::length(data);
646 return mLength == length && char_traits::compare(mData, data, mLength) == 0;
649 PRBool
650 nsTSubstring_CharT::Equals( const char_type* data, const comparator_type& comp ) const
652 // unfortunately, some callers pass null :-(
653 if (!data)
655 NS_NOTREACHED("null data pointer");
656 return mLength == 0;
659 // XXX avoid length calculation?
660 size_type length = char_traits::length(data);
661 return mLength == length && comp(mData, data, mLength) == 0;
664 PRBool
665 nsTSubstring_CharT::EqualsASCII( const char* data, size_type len ) const
667 return mLength == len && char_traits::compareASCII(mData, data, len) == 0;
670 PRBool
671 nsTSubstring_CharT::EqualsASCII( const char* data ) const
673 return char_traits::compareASCIINullTerminated(mData, mLength, data) == 0;
676 PRBool
677 nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data, size_type len ) const
679 return mLength == len && char_traits::compareLowerCaseToASCII(mData, data, len) == 0;
682 PRBool
683 nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data ) const
685 return char_traits::compareLowerCaseToASCIINullTerminated(mData, mLength, data) == 0;
688 nsTSubstring_CharT::size_type
689 nsTSubstring_CharT::CountChar( char_type c ) const
691 const char_type *start = mData;
692 const char_type *end = mData + mLength;
694 return NS_COUNT(start, end, c);
697 PRInt32
698 nsTSubstring_CharT::FindChar( char_type c, index_type offset ) const
700 if (offset < mLength)
702 const char_type* result = char_traits::find(mData + offset, mLength - offset, c);
703 if (result)
704 return result - mData;
706 return -1;
709 void
710 nsTSubstring_CharT::StripChar( char_type aChar, PRInt32 aOffset )
712 if (mLength == 0 || aOffset >= PRInt32(mLength))
713 return;
715 EnsureMutable(); // XXX do this lazily?
717 // XXX(darin): this code should defer writing until necessary.
719 char_type* to = mData + aOffset;
720 char_type* from = mData + aOffset;
721 char_type* end = mData + mLength;
723 while (from < end)
725 char_type theChar = *from++;
726 if (aChar != theChar)
727 *to++ = theChar;
729 *to = char_type(0); // add the null
730 mLength = to - mData;