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 :
39 :
40 :
41 : /**
42 : * nsTString::Find
43 : *
44 : * aOffset specifies starting index
45 : * aCount specifies number of string compares (iterations)
46 : */
47 :
48 : PRInt32
49 5306 : nsTString_CharT::Find( const nsCString& aString, bool aIgnoreCase, PRInt32 aOffset, PRInt32 aCount) const
50 : {
51 : // this method changes the meaning of aOffset and aCount:
52 5306 : Find_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
53 :
54 5306 : PRInt32 result = FindSubstring(mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase);
55 5306 : if (result != kNotFound)
56 3558 : result += aOffset;
57 5306 : return result;
58 : }
59 :
60 : PRInt32
61 5306 : nsTString_CharT::Find( const char* aString, bool aIgnoreCase, PRInt32 aOffset, PRInt32 aCount) const
62 : {
63 5306 : return Find(nsDependentCString(aString), aIgnoreCase, aOffset, aCount);
64 : }
65 :
66 :
67 : /**
68 : * nsTString::RFind
69 : *
70 : * aOffset specifies starting index
71 : * aCount specifies number of string compares (iterations)
72 : */
73 :
74 : PRInt32
75 2939 : nsTString_CharT::RFind( const nsCString& aString, bool aIgnoreCase, PRInt32 aOffset, PRInt32 aCount) const
76 : {
77 : // this method changes the meaning of aOffset and aCount:
78 2939 : RFind_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
79 :
80 2939 : PRInt32 result = RFindSubstring(mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase);
81 2939 : if (result != kNotFound)
82 1842 : result += aOffset;
83 2939 : return result;
84 : }
85 :
86 : PRInt32
87 2939 : nsTString_CharT::RFind( const char* aString, bool aIgnoreCase, PRInt32 aOffset, PRInt32 aCount) const
88 : {
89 2939 : return RFind(nsDependentCString(aString), aIgnoreCase, aOffset, aCount);
90 : }
91 :
92 :
93 : /**
94 : * nsTString::RFindChar
95 : */
96 :
97 : PRInt32
98 3079 : nsTString_CharT::RFindChar( PRUnichar aChar, PRInt32 aOffset, PRInt32 aCount) const
99 : {
100 3079 : return nsBufferRoutines<CharT>::rfind_char(mData, mLength, aOffset, aChar, aCount);
101 : }
102 :
103 :
104 : /**
105 : * nsTString::FindCharInSet
106 : */
107 :
108 : PRInt32
109 21993 : nsTString_CharT::FindCharInSet( const char* aSet, PRInt32 aOffset ) const
110 : {
111 21993 : if (aOffset < 0)
112 0 : aOffset = 0;
113 21993 : else if (aOffset >= PRInt32(mLength))
114 3566 : return kNotFound;
115 :
116 18427 : PRInt32 result = ::FindCharInSet(mData + aOffset, mLength - aOffset, aSet);
117 18427 : if (result != kNotFound)
118 1841 : result += aOffset;
119 18427 : return result;
120 : }
121 :
122 :
123 : /**
124 : * nsTString::RFindCharInSet
125 : */
126 :
127 : PRInt32
128 0 : nsTString_CharT::RFindCharInSet( const CharT* aSet, PRInt32 aOffset ) const
129 : {
130 : // We want to pass a "data length" to ::RFindCharInSet
131 0 : if (aOffset < 0 || aOffset > PRInt32(mLength))
132 0 : aOffset = mLength;
133 : else
134 0 : ++aOffset;
135 :
136 0 : return ::RFindCharInSet(mData, aOffset, aSet);
137 : }
138 :
139 :
140 : // it's a shame to replicate this code. it was done this way in the past
141 : // to help performance. this function also gets to keep the rickg style
142 : // indentation :-/
143 : PRInt32
144 10672 : nsTString_CharT::ToInteger( PRInt32* aErrorCode, PRUint32 aRadix ) const
145 : {
146 10672 : CharT* cp=mData;
147 10672 : PRInt32 theRadix=10; // base 10 unless base 16 detected, or overriden (aRadix != kAutoDetect)
148 10672 : PRInt32 result=0;
149 10672 : bool negate=false;
150 10672 : CharT theChar=0;
151 :
152 : //initial value, override if we find an integer
153 10672 : *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
154 :
155 10672 : if(cp) {
156 :
157 : //begin by skipping over leading chars that shouldn't be part of the number...
158 :
159 10672 : CharT* endcp=cp+mLength;
160 10672 : bool done=false;
161 :
162 32139 : while((cp<endcp) && (!done)){
163 10795 : switch(*cp++) {
164 : case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
165 : case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
166 0 : theRadix=16;
167 0 : done=true;
168 0 : break;
169 : case '0': case '1': case '2': case '3': case '4':
170 : case '5': case '6': case '7': case '8': case '9':
171 10672 : done=true;
172 10672 : break;
173 : case '-':
174 123 : negate=true; //fall through...
175 123 : break;
176 : case 'X': case 'x':
177 0 : theRadix=16;
178 0 : break;
179 : default:
180 0 : break;
181 : } //switch
182 : }
183 :
184 10672 : if (done) {
185 :
186 : //integer found
187 10672 : *aErrorCode = NS_OK;
188 :
189 10672 : if (aRadix!=kAutoDetect) theRadix = aRadix; // override
190 :
191 : //now iterate the numeric chars and build our result
192 10672 : CharT* first=--cp; //in case we have to back up.
193 10672 : bool haveValue = false;
194 :
195 55299 : while(cp<endcp){
196 33957 : PRInt32 oldresult = result;
197 :
198 33957 : theChar=*cp++;
199 33957 : if(('0'<=theChar) && (theChar<='9')){
200 33957 : result = (theRadix * result) + (theChar-'0');
201 33957 : haveValue = true;
202 : }
203 0 : else if((theChar>='A') && (theChar<='F')) {
204 0 : if(10==theRadix) {
205 0 : if(kAutoDetect==aRadix){
206 0 : theRadix=16;
207 0 : cp=first; //backup
208 0 : result=0;
209 0 : haveValue = false;
210 : }
211 : else {
212 0 : *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
213 0 : result=0;
214 0 : break;
215 : }
216 : }
217 : else {
218 0 : result = (theRadix * result) + ((theChar-'A')+10);
219 0 : haveValue = true;
220 : }
221 : }
222 0 : else if((theChar>='a') && (theChar<='f')) {
223 0 : if(10==theRadix) {
224 0 : if(kAutoDetect==aRadix){
225 0 : theRadix=16;
226 0 : cp=first; //backup
227 0 : result=0;
228 0 : haveValue = false;
229 : }
230 : else {
231 0 : *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
232 0 : result=0;
233 0 : break;
234 : }
235 : }
236 : else {
237 0 : result = (theRadix * result) + ((theChar-'a')+10);
238 0 : haveValue = true;
239 : }
240 : }
241 0 : else if((('X'==theChar) || ('x'==theChar)) && (!haveValue || result == 0)) {
242 0 : continue;
243 : }
244 0 : else if((('#'==theChar) || ('+'==theChar)) && !haveValue) {
245 0 : continue;
246 : }
247 : else {
248 : //we've encountered a char that's not a legal number or sign
249 0 : break;
250 : }
251 :
252 33957 : if (result < oldresult) {
253 : // overflow!
254 2 : *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
255 2 : result = 0;
256 2 : break;
257 : }
258 : } //while
259 10672 : if(negate)
260 123 : result=-result;
261 : } //if
262 : }
263 10672 : return result;
264 : }
265 :
266 :
267 : /**
268 : * nsTString::Mid
269 : */
270 :
271 : PRUint32
272 21721 : nsTString_CharT::Mid( self_type& aResult, index_type aStartPos, size_type aLengthToCopy ) const
273 : {
274 21721 : if (aStartPos == 0 && aLengthToCopy >= mLength)
275 0 : aResult = *this;
276 : else
277 21721 : aResult = Substring(*this, aStartPos, aLengthToCopy);
278 :
279 21721 : return aResult.mLength;
280 : }
281 :
282 :
283 : /**
284 : * nsTString::SetCharAt
285 : */
286 :
287 : bool
288 5863 : nsTString_CharT::SetCharAt( PRUnichar aChar, PRUint32 aIndex )
289 : {
290 5863 : if (aIndex >= mLength)
291 0 : return false;
292 :
293 5863 : EnsureMutable();
294 :
295 5863 : mData[aIndex] = CharT(aChar);
296 5863 : return true;
297 : }
298 :
299 :
300 : /**
301 : * nsTString::StripChars,StripChar,StripWhitespace
302 : */
303 :
304 : void
305 5252 : nsTString_CharT::StripChars( const char* aSet )
306 : {
307 5252 : EnsureMutable();
308 5252 : mLength = nsBufferRoutines<CharT>::strip_chars(mData, mLength, aSet);
309 5252 : }
310 :
311 : void
312 3247 : nsTString_CharT::StripWhitespace()
313 : {
314 3247 : StripChars(kWhitespace);
315 3247 : }
316 :
317 :
318 : /**
319 : * nsTString::ReplaceChar,ReplaceSubstring
320 : */
321 :
322 : void
323 11045 : nsTString_CharT::ReplaceChar( char_type aOldChar, char_type aNewChar )
324 : {
325 11045 : EnsureMutable(); // XXX do this lazily?
326 :
327 148261 : for (PRUint32 i=0; i<mLength; ++i)
328 : {
329 137216 : if (mData[i] == aOldChar)
330 2032 : mData[i] = aNewChar;
331 : }
332 11045 : }
333 :
334 : void
335 1174 : nsTString_CharT::ReplaceChar( const char* aSet, char_type aNewChar )
336 : {
337 1174 : EnsureMutable(); // XXX do this lazily?
338 :
339 1174 : char_type* data = mData;
340 1174 : PRUint32 lenRemaining = mLength;
341 :
342 2372 : while (lenRemaining)
343 : {
344 160 : PRInt32 i = ::FindCharInSet(data, lenRemaining, aSet);
345 160 : if (i == kNotFound)
346 136 : break;
347 :
348 24 : data[i++] = aNewChar;
349 24 : data += i;
350 24 : lenRemaining -= i;
351 : }
352 1174 : }
353 :
354 : void
355 189067 : nsTString_CharT::ReplaceSubstring( const char_type* aTarget, const char_type* aNewValue )
356 : {
357 189067 : ReplaceSubstring(nsTDependentString_CharT(aTarget),
358 378134 : nsTDependentString_CharT(aNewValue));
359 189067 : }
360 :
361 : void
362 189067 : nsTString_CharT::ReplaceSubstring( const self_type& aTarget, const self_type& aNewValue )
363 : {
364 189067 : if (aTarget.Length() == 0)
365 0 : return;
366 :
367 189067 : PRUint32 i = 0;
368 380429 : while (i < mLength)
369 : {
370 191157 : PRInt32 r = FindSubstring(mData + i, mLength - i, aTarget.Data(), aTarget.Length(), false);
371 191157 : if (r == kNotFound)
372 188862 : break;
373 :
374 2295 : Replace(i + r, aTarget.Length(), aNewValue);
375 2295 : i += r + aNewValue.Length();
376 : }
377 : }
378 :
379 :
380 : /**
381 : * nsTString::Trim
382 : */
383 :
384 : void
385 644877 : nsTString_CharT::Trim( const char* aSet, bool aTrimLeading, bool aTrimTrailing, bool aIgnoreQuotes )
386 : {
387 : // the old implementation worried about aSet being null :-/
388 644877 : if (!aSet)
389 0 : return;
390 :
391 644877 : char_type* start = mData;
392 644877 : char_type* end = mData + mLength;
393 :
394 : // skip over quotes if requested
395 644877 : if (aIgnoreQuotes && mLength > 2 && mData[0] == mData[mLength - 1] &&
396 0 : (mData[0] == '\'' || mData[0] == '"'))
397 : {
398 0 : ++start;
399 0 : --end;
400 : }
401 :
402 644877 : PRUint32 setLen = nsCharTraits<char>::length(aSet);
403 :
404 644877 : if (aTrimLeading)
405 : {
406 26169 : PRUint32 cutStart = start - mData;
407 26169 : PRUint32 cutLength = 0;
408 :
409 : // walk forward from start to end
410 26443 : for (; start != end; ++start, ++cutLength)
411 : {
412 25370 : PRInt32 pos = FindChar1(aSet, setLen, 0, *start, setLen);
413 25370 : if (pos == kNotFound)
414 25096 : break;
415 : }
416 :
417 26169 : if (cutLength)
418 : {
419 274 : Cut(cutStart, cutLength);
420 :
421 : // reset iterators
422 274 : start = mData + cutStart;
423 274 : end = mData + mLength - cutStart;
424 : }
425 : }
426 :
427 644877 : if (aTrimTrailing)
428 : {
429 644877 : PRUint32 cutEnd = end - mData;
430 644877 : PRUint32 cutLength = 0;
431 :
432 : // walk backward from end to start
433 644877 : --end;
434 1918808 : for (; end >= start; --end, ++cutLength)
435 : {
436 1916363 : PRInt32 pos = FindChar1(aSet, setLen, 0, *end, setLen);
437 1916363 : if (pos == kNotFound)
438 642432 : break;
439 : }
440 :
441 644877 : if (cutLength)
442 89895 : Cut(cutEnd - cutLength, cutLength);
443 : }
444 : }
445 :
446 :
447 : /**
448 : * nsTString::CompressWhitespace
449 : */
450 :
451 : void
452 1066 : nsTString_CharT::CompressWhitespace( bool aTrimLeading, bool aTrimTrailing )
453 : {
454 1066 : const char* set = kWhitespace;
455 :
456 1066 : ReplaceChar(set, ' ');
457 1066 : Trim(set, aTrimLeading, aTrimTrailing);
458 :
459 : // this one does some questionable fu... just copying the old code!
460 1066 : mLength = nsBufferRoutines<char_type>::compress_chars(mData, mLength, set);
461 1066 : }
462 :
463 :
464 : /**
465 : * nsTString::AssignWithConversion
466 : */
467 :
468 : void
469 15899 : nsTString_CharT::AssignWithConversion( const incompatible_char_type* aData, PRInt32 aLength )
470 : {
471 : // for compatibility with the old string implementation, we need to allow
472 : // for a NULL input buffer :-(
473 15899 : if (!aData)
474 : {
475 24 : Truncate();
476 : }
477 : else
478 : {
479 15875 : if (aLength < 0)
480 15875 : aLength = nsCharTraits<incompatible_char_type>::length(aData);
481 :
482 15875 : AssignWithConversion(Substring(aData, aLength));
483 : }
484 15899 : }
485 :
486 :
487 : /**
488 : * nsTString::AppendWithConversion
489 : */
490 :
491 : void
492 0 : nsTString_CharT::AppendWithConversion( const incompatible_char_type* aData, PRInt32 aLength )
493 : {
494 : // for compatibility with the old string implementation, we need to allow
495 : // for a NULL input buffer :-(
496 0 : if (aData)
497 : {
498 0 : if (aLength < 0)
499 0 : aLength = nsCharTraits<incompatible_char_type>::length(aData);
500 :
501 0 : AppendWithConversion(Substring(aData, aLength));
502 : }
503 0 : }
|