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 ***** */
41
* helper function for down-casting a nsTSubstring to a nsTFixedString.
43
inline const nsTFixedString_CharT*
44
AsFixedString( const nsTSubstring_CharT* s )
46
return NS_STATIC_CAST(const nsTFixedString_CharT*, s);
51
* this function is called to prepare mData for writing. the given capacity
52
* indicates the required minimum storage size for mData, in sizeof(char_type)
53
* increments. this function returns true if the operation succeeds. it also
54
* returns the old data and old flags members if mData is newly allocated.
55
* the old data must be released by the caller.
58
nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, PRUint32* oldFlags )
60
// initialize to no old data
64
size_type curCapacity = Capacity();
66
// |curCapacity == size_type(-1)| means that the buffer is immutable, so we
67
// need to allocate a new buffer. we cannot use the existing buffer even
68
// though it might be large enough.
70
if (curCapacity != size_type(-1))
72
if (capacity <= curCapacity)
77
// use doubling algorithm when forced to increase available capacity,
78
// but always start out with exactly the requested amount.
79
PRUint32 temp = curCapacity;
80
while (temp < capacity)
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
94
// requiring that we in some cases preserve the data before creating
95
// a new buffer complicates things just a bit ;-)
98
size_type storageSize = (capacity + 1) * sizeof(char_type);
101
if (mFlags & F_SHARED)
103
nsStringHeader* hdr = nsStringHeader::FromData(mData);
104
if (!hdr->IsReadonly())
106
nsStringHeader *newHdr = nsStringHeader::Realloc(hdr, storageSize);
110
mData = (char_type*) hdr->Data();
114
// out of memory!! put us in a consistent state at least.
115
mData = NS_CONST_CAST(char_type*, char_traits::sEmptyBuffer);
117
SetDataFlags(F_TERMINATED);
123
PRUint32 newDataFlags;
125
// if we have a fixed buffer of sufficient size, then use it. this helps
126
// avoid heap allocations.
127
if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity))
129
newData = AsFixedString(this)->mFixedBuf;
130
newDataFlags = F_TERMINATED | F_FIXED;
134
// if we reach here then, we must allocate a new buffer. we cannot
135
// make use of our F_OWNED or F_FIXED buffers because they are not
138
nsStringHeader* newHdr = nsStringHeader::Alloc(storageSize);
140
return PR_FALSE; // we are still in a consistent state
142
newData = (char_type*) newHdr->Data();
143
newDataFlags = F_TERMINATED | F_SHARED;
146
// save old data and flags
151
SetDataFlags(newDataFlags);
153
// mLength does not change
155
// though we are not necessarily terminated at the moment, now is probably
156
// still the best time to set F_TERMINATED.
162
nsTSubstring_CharT::Finalize()
164
::ReleaseData(mData, mFlags);
165
// mData, mLength, and mFlags are purposefully left dangling
169
nsTSubstring_CharT::ReplacePrep( index_type cutStart, size_type cutLen, size_type fragLen )
172
cutLen = NS_MIN(cutLen, mLength - cutStart);
174
PRUint32 newLen = mLength - cutLen + fragLen;
178
if (!MutatePrep(newLen, &oldData, &oldFlags))
179
return; // XXX out-of-memory error occured!
183
// determine whether or not we need to copy part of the old string
184
// over to the new string.
188
// copy prefix from old string
189
char_traits::copy(mData, oldData, cutStart);
192
if (cutStart + cutLen < mLength)
194
// copy suffix from old string to new offset
195
size_type from = cutStart + cutLen;
196
size_type fromLen = mLength - from;
197
PRUint32 to = cutStart + fragLen;
198
char_traits::copy(mData + to, oldData + from, fromLen);
201
::ReleaseData(oldData, oldFlags);
205
// original data remains intact
207
// determine whether or not we need to move part of the existing string
208
// to make room for the requested hole.
209
if (fragLen != cutLen && cutStart + cutLen < mLength)
211
PRUint32 from = cutStart + cutLen;
212
PRUint32 fromLen = mLength - from;
213
PRUint32 to = cutStart + fragLen;
214
char_traits::move(mData + to, mData + from, fromLen);
218
// add null terminator (mutable mData always has room for the null-
220
mData[newLen] = char_type(0);
224
nsTSubstring_CharT::size_type
225
nsTSubstring_CharT::Capacity() const
227
// return size_type(-1) to indicate an immutable buffer
230
if (mFlags & F_SHARED)
232
// if the string is readonly, then we pretend that it has no capacity.
233
nsStringHeader* hdr = nsStringHeader::FromData(mData);
234
if (hdr->IsReadonly())
235
capacity = size_type(-1);
237
capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
239
else if (mFlags & F_FIXED)
241
capacity = AsFixedString(this)->mFixedCapacity;
243
else if (mFlags & F_OWNED)
245
// we don't store the capacity of an adopted buffer because that would
246
// require an additional member field. the best we can do is base the
247
// capacity on our length. remains to be seen if this is the right
253
capacity = size_type(-1);
260
nsTSubstring_CharT::EnsureMutable()
262
if (mFlags & (F_FIXED | F_OWNED))
264
if ((mFlags & F_SHARED) && !nsStringHeader::FromData(mData)->IsReadonly())
267
// promote to a shared string buffer
268
Assign(string_type(mData, mLength));
271
// ---------------------------------------------------------------------------
274
nsTSubstring_CharT::Assign( const char_type* data, size_type length )
276
// unfortunately, some callers pass null :-(
283
if (length == size_type(-1))
284
length = char_traits::length(data);
286
if (IsDependentOn(data, data + length))
288
// take advantage of sharing here...
289
Assign(string_type(data, length));
293
ReplacePrep(0, mLength, length);
294
char_traits::copy(mData, data, length);
298
nsTSubstring_CharT::AssignASCII( const char* data, size_type length )
300
// A Unicode string can't depend on an ASCII string buffer,
301
// so this dependence check only applies to CStrings.
303
if (IsDependentOn(data, data + length))
305
// take advantage of sharing here...
306
Assign(string_type(data, length));
311
ReplacePrep(0, mLength, length);
312
char_traits::copyASCII(mData, data, length);
316
nsTSubstring_CharT::AssignASCII( const char* data )
318
AssignASCII(data, strlen(data));
322
nsTSubstring_CharT::Assign( const self_type& str )
324
// |str| could be sharable. we need to check its flags to know how to
330
if (str.mFlags & F_SHARED)
332
// nice! we can avoid a string copy :-)
334
// |str| should be null-terminated
335
NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated");
337
::ReleaseData(mData, mFlags);
340
mLength = str.mLength;
341
SetDataFlags(F_TERMINATED | F_SHARED);
343
// get an owning reference to the mData
344
nsStringHeader::FromData(mData)->AddRef();
346
else if (str.mFlags & F_VOIDED)
348
// inherit the F_VOIDED attribute
353
// else, treat this like an ordinary assignment.
354
Assign(str.Data(), str.Length());
359
nsTSubstring_CharT::Assign( const substring_tuple_type& tuple )
361
if (tuple.IsDependentOn(mData, mData + mLength))
363
// take advantage of sharing here...
364
Assign(string_type(tuple));
368
size_type length = tuple.Length();
370
ReplacePrep(0, mLength, length);
372
tuple.WriteTo(mData, length);
375
// this is non-inline to reduce codesize at the callsite
377
nsTSubstring_CharT::Assign( const abstract_string_type& readable )
379
// promote to string if possible to take advantage of sharing
380
if (readable.mVTable == nsTObsoleteAString_CharT::sCanonicalVTable)
381
Assign(*readable.AsSubstring());
383
Assign(readable.ToSubstring());
388
nsTSubstring_CharT::Adopt( char_type* data, size_type length )
392
::ReleaseData(mData, mFlags);
394
if (length == size_type(-1))
395
length = char_traits::length(data);
399
SetDataFlags(F_TERMINATED | F_OWNED);
401
STRING_STAT_INCREMENT(Adopt);
411
nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length )
413
// unfortunately, some callers pass null :-(
420
if (length == size_type(-1))
421
length = char_traits::length(data);
423
if (IsDependentOn(data, data + length))
425
nsTAutoString_CharT temp(data, length);
426
Replace(cutStart, cutLength, temp);
431
cutStart = PR_MIN(cutStart, Length());
433
ReplacePrep(cutStart, cutLength, length);
436
char_traits::copy(mData + cutStart, data, length);
440
nsTSubstring_CharT::ReplaceASCII( index_type cutStart, size_type cutLength, const char* data, size_type length )
442
if (length == size_type(-1))
443
length = strlen(data);
445
// A Unicode string can't depend on an ASCII string buffer,
446
// so this dependence check only applies to CStrings.
448
if (IsDependentOn(data, data + length))
450
nsTAutoString_CharT temp(data, length);
451
Replace(cutStart, cutLength, temp);
456
cutStart = PR_MIN(cutStart, Length());
458
ReplacePrep(cutStart, cutLength, length);
461
char_traits::copyASCII(mData + cutStart, data, length);
465
nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& tuple )
467
if (tuple.IsDependentOn(mData, mData + mLength))
469
nsTAutoString_CharT temp(tuple);
470
Replace(cutStart, cutLength, temp);
474
size_type length = tuple.Length();
476
cutStart = PR_MIN(cutStart, Length());
478
ReplacePrep(cutStart, cutLength, length);
481
tuple.WriteTo(mData + cutStart, length);
485
nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const abstract_string_type& readable )
487
Replace(cutStart, cutLength, readable.ToSubstring());
491
nsTSubstring_CharT::SetCapacity( size_type capacity )
493
// capacity does not include room for the terminating null char
495
// if our capacity is reduced to zero, then free our buffer.
498
::ReleaseData(mData, mFlags);
499
mData = NS_CONST_CAST(char_type*, char_traits::sEmptyBuffer);
501
SetDataFlags(F_TERMINATED);
507
if (!MutatePrep(capacity, &oldData, &oldFlags))
508
return; // XXX out-of-memory error occured!
510
// compute new string length
511
size_type newLen = NS_MIN(mLength, capacity);
517
char_traits::copy(mData, oldData, newLen);
519
::ReleaseData(oldData, oldFlags);
522
// adjust mLength if our buffer shrunk down in size
523
if (newLen < mLength)
526
// always null-terminate here, even if the buffer got longer. this is
527
// for backwards compat with the old string implementation.
528
mData[capacity] = char_type(0);
533
nsTSubstring_CharT::SetLength( size_type length )
540
nsTSubstring_CharT::SetIsVoid( PRBool val )
554
nsTSubstring_CharT::Equals( const self_type& str ) const
556
return mLength == str.mLength && char_traits::compare(mData, str.mData, mLength) == 0;
560
nsTSubstring_CharT::Equals( const self_type& str, const comparator_type& comp ) const
562
return mLength == str.mLength && comp(mData, str.mData, mLength) == 0;
566
nsTSubstring_CharT::Equals( const abstract_string_type& readable ) const
568
const char_type* data;
569
size_type length = readable.GetReadableBuffer(&data);
571
return mLength == length && char_traits::compare(mData, data, mLength) == 0;
575
nsTSubstring_CharT::Equals( const abstract_string_type& readable, const comparator_type& comp ) const
577
const char_type* data;
578
size_type length = readable.GetReadableBuffer(&data);
580
return mLength == length && comp(mData, data, mLength) == 0;
584
nsTSubstring_CharT::Equals( const char_type* data ) const
586
// unfortunately, some callers pass null :-(
589
NS_NOTREACHED("null data pointer");
593
// XXX avoid length calculation?
594
size_type length = char_traits::length(data);
595
return mLength == length && char_traits::compare(mData, data, mLength) == 0;
599
nsTSubstring_CharT::Equals( const char_type* data, const comparator_type& comp ) const
601
// unfortunately, some callers pass null :-(
604
NS_NOTREACHED("null data pointer");
608
// XXX avoid length calculation?
609
size_type length = char_traits::length(data);
610
return mLength == length && comp(mData, data, mLength) == 0;
614
nsTSubstring_CharT::EqualsASCII( const char* data, size_type len ) const
616
return mLength == len && char_traits::compareASCII(mData, data, len) == 0;
620
nsTSubstring_CharT::EqualsASCII( const char* data ) const
622
return char_traits::compareASCIINullTerminated(mData, mLength, data) == 0;
626
nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data, size_type len ) const
628
return mLength == len && char_traits::compareLowerCaseToASCII(mData, data, len) == 0;
632
nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data ) const
634
return char_traits::compareLowerCaseToASCIINullTerminated(mData, mLength, data) == 0;
637
nsTSubstring_CharT::size_type
638
nsTSubstring_CharT::CountChar( char_type c ) const
640
const char_type *start = mData;
641
const char_type *end = mData + mLength;
643
return NS_COUNT(start, end, c);
647
nsTSubstring_CharT::FindChar( char_type c, index_type offset ) const
649
if (offset < mLength)
651
const char_type* result = char_traits::find(mData + offset, mLength - offset, c);
653
return result - mData;