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
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.
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
,
45 if (flags
& F_OWNED
) {
46 STRING_STAT_INCREMENT(Adopt
);
47 #ifdef NS_BUILD_REFCNT_LOGGING
48 NS_LogCtor(mData
, "StringAdopt", 1);
53 nsTSubstring_CharT::nsTSubstring_CharT(const substring_tuple_type
& tuple
)
61 nsTSubstring_CharT::nsTSubstring_CharT()
62 : mData(const_cast<char_type
*>(char_traits::sEmptyBuffer
)),
64 mFlags(F_TERMINATED
) {}
66 nsTSubstring_CharT::nsTSubstring_CharT( PRUint32 flags
)
69 nsTSubstring_CharT::nsTSubstring_CharT( const self_type
& str
)
72 mFlags(str
.mFlags
& (F_TERMINATED
| F_VOIDED
)) {}
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
);
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.
92 nsTSubstring_CharT::MutatePrep( size_type capacity
, char_type
** oldData
, PRUint32
* oldFlags
)
94 // initialize to no old data
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");
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
123 // use doubling algorithm when forced to increase available
125 PRUint32 temp
= curCapacity
;
126 while (temp
< capacity
)
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
);
147 if (mFlags
& F_SHARED
)
149 nsStringBuffer
* hdr
= nsStringBuffer::FromData(mData
);
150 if (!hdr
->IsReadonly())
152 nsStringBuffer
*newHdr
= nsStringBuffer::Realloc(hdr
, storageSize
);
154 return PR_FALSE
; // out-of-memory (original header left intact)
157 mData
= (char_type
*) hdr
->Data();
158 mFlags
&= ~F_VOIDED
; // mutation clears voided flag
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
;
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
179 nsStringBuffer
* newHdr
= nsStringBuffer::Alloc(storageSize
);
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
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.
203 nsTSubstring_CharT::Finalize()
205 ::ReleaseData(mData
, mFlags
);
206 // mData, mLength, and mFlags are purposefully left dangling
209 nsTSubstring_CharT::~nsTSubstring_CharT()
215 nsTSubstring_CharT::ReplacePrep( index_type cutStart
, size_type cutLen
, size_type fragLen
)
218 cutLen
= NS_MIN(cutLen
, mLength
- cutStart
);
220 PRUint32 newLen
= mLength
- cutLen
+ fragLen
;
224 if (!MutatePrep(newLen
, &oldData
, &oldFlags
))
225 return PR_FALSE
; // out-of-memory
229 // determine whether or not we need to copy part of the old string
230 // over to the new string.
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
);
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-
266 mData
[newLen
] = char_type(0);
272 nsTSubstring_CharT::size_type
273 nsTSubstring_CharT::Capacity() const
275 // return size_type(-1) to indicate an immutable buffer
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);
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
301 capacity
= size_type(-1);
308 nsTSubstring_CharT::EnsureMutable( size_type newLen
)
310 if (newLen
== size_type(-1) || newLen
== mLength
)
312 if (mFlags
& (F_FIXED
| F_OWNED
))
314 if ((mFlags
& F_SHARED
) && !nsStringBuffer::FromData(mData
)->IsReadonly())
317 // promote to a shared string buffer
318 char_type
* prevData
= mData
;
319 Assign(string_type(mData
, mLength
));
320 return mData
!= prevData
;
325 return mLength
== newLen
;
329 // ---------------------------------------------------------------------------
331 // This version of Assign is optimized for single-character assignment.
333 nsTSubstring_CharT::Assign( char_type c
)
335 if (ReplacePrep(0, mLength
, 1))
341 nsTSubstring_CharT::Assign( const char_type
* data
, size_type length
)
343 // unfortunately, some callers pass null :-(
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
));
360 if (ReplacePrep(0, mLength
, length
))
361 char_traits::copy(mData
, data
, length
);
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.
370 if (IsDependentOn(data
, data
+ length
))
372 // take advantage of sharing here...
373 Assign(string_type(data
, length
));
378 if (ReplacePrep(0, mLength
, length
))
379 char_traits::copyASCII(mData
, data
, length
);
383 nsTSubstring_CharT::AssignASCII( const char* data
)
385 AssignASCII(data
, strlen(data
));
389 nsTSubstring_CharT::Assign( const self_type
& str
)
391 // |str| could be sharable. we need to check its flags to know how to
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
);
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
420 // else, treat this like an ordinary assignment.
421 Assign(str
.Data(), str
.Length());
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
));
435 size_type length
= tuple
.Length();
437 if (ReplacePrep(0, mLength
, length
) && length
)
438 tuple
.WriteTo(mData
, length
);
442 nsTSubstring_CharT::Adopt( char_type
* data
, size_type length
)
446 ::ReleaseData(mData
, mFlags
);
448 if (length
== size_type(-1))
449 length
= char_traits::length(data
);
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
469 // This version of Replace is optimized for single-character replacement.
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))
481 nsTSubstring_CharT::Replace( index_type cutStart
, size_type cutLength
, const char_type
* data
, size_type length
)
483 // unfortunately, some callers pass null :-(
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
);
501 cutStart
= PR_MIN(cutStart
, Length());
503 if (ReplacePrep(cutStart
, cutLength
, length
) && length
> 0)
504 char_traits::copy(mData
+ cutStart
, data
, length
);
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.
516 if (IsDependentOn(data
, data
+ length
))
518 nsTAutoString_CharT
temp(data
, length
);
519 Replace(cutStart
, cutLength
, temp
);
524 cutStart
= PR_MIN(cutStart
, Length());
526 if (ReplacePrep(cutStart
, cutLength
, length
) && length
> 0)
527 char_traits::copyASCII(mData
+ cutStart
, data
, length
);
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
);
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
);
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.
556 ::ReleaseData(mData
, mFlags
);
557 mData
= const_cast<char_type
*>(char_traits::sEmptyBuffer
);
559 SetDataFlags(F_TERMINATED
);
565 if (!MutatePrep(capacity
, &oldData
, &oldFlags
))
566 return; // out-of-memory
568 // compute new string length
569 size_type newLen
= NS_MIN(mLength
, capacity
);
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
)
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);
591 nsTSubstring_CharT::SetLength( size_type length
)
593 if (mLength
== length
) {
594 mFlags
&= ~F_VOIDED
; // mutation clears voided flag
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
)
609 nsTSubstring_CharT::SetIsVoid( PRBool val
)
623 nsTSubstring_CharT::Equals( const self_type
& str
) const
625 return mLength
== str
.mLength
&& char_traits::compare(mData
, str
.mData
, mLength
) == 0;
629 nsTSubstring_CharT::Equals( const self_type
& str
, const comparator_type
& comp
) const
631 return mLength
== str
.mLength
&& comp(mData
, str
.mData
, mLength
) == 0;
635 nsTSubstring_CharT::Equals( const char_type
* data
) const
637 // unfortunately, some callers pass null :-(
640 NS_NOTREACHED("null data pointer");
644 // XXX avoid length calculation?
645 size_type length
= char_traits::length(data
);
646 return mLength
== length
&& char_traits::compare(mData
, data
, mLength
) == 0;
650 nsTSubstring_CharT::Equals( const char_type
* data
, const comparator_type
& comp
) const
652 // unfortunately, some callers pass null :-(
655 NS_NOTREACHED("null data pointer");
659 // XXX avoid length calculation?
660 size_type length
= char_traits::length(data
);
661 return mLength
== length
&& comp(mData
, data
, mLength
) == 0;
665 nsTSubstring_CharT::EqualsASCII( const char* data
, size_type len
) const
667 return mLength
== len
&& char_traits::compareASCII(mData
, data
, len
) == 0;
671 nsTSubstring_CharT::EqualsASCII( const char* data
) const
673 return char_traits::compareASCIINullTerminated(mData
, mLength
, data
) == 0;
677 nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data
, size_type len
) const
679 return mLength
== len
&& char_traits::compareLowerCaseToASCII(mData
, data
, len
) == 0;
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
);
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
);
704 return result
- mData
;
710 nsTSubstring_CharT::StripChar( char_type aChar
, PRInt32 aOffset
)
712 if (mLength
== 0 || aOffset
>= PRInt32(mLength
))
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
;
725 char_type theChar
= *from
++;
726 if (aChar
!= theChar
)
729 *to
= char_type(0); // add the null
730 mLength
= to
- mData
;