|
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 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 |
|
8 |
|
9 /** |
|
10 * nsTString::Find |
|
11 * |
|
12 * aOffset specifies starting index |
|
13 * aCount specifies number of string compares (iterations) |
|
14 */ |
|
15 |
|
16 int32_t |
|
17 nsTString_CharT::Find( const nsCString& aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const |
|
18 { |
|
19 // this method changes the meaning of aOffset and aCount: |
|
20 Find_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount); |
|
21 |
|
22 int32_t result = FindSubstring(mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase); |
|
23 if (result != kNotFound) |
|
24 result += aOffset; |
|
25 return result; |
|
26 } |
|
27 |
|
28 int32_t |
|
29 nsTString_CharT::Find( const char* aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const |
|
30 { |
|
31 return Find(nsDependentCString(aString), aIgnoreCase, aOffset, aCount); |
|
32 } |
|
33 |
|
34 |
|
35 /** |
|
36 * nsTString::RFind |
|
37 * |
|
38 * aOffset specifies starting index |
|
39 * aCount specifies number of string compares (iterations) |
|
40 */ |
|
41 |
|
42 int32_t |
|
43 nsTString_CharT::RFind( const nsCString& aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const |
|
44 { |
|
45 // this method changes the meaning of aOffset and aCount: |
|
46 RFind_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount); |
|
47 |
|
48 int32_t result = RFindSubstring(mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase); |
|
49 if (result != kNotFound) |
|
50 result += aOffset; |
|
51 return result; |
|
52 } |
|
53 |
|
54 int32_t |
|
55 nsTString_CharT::RFind( const char* aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const |
|
56 { |
|
57 return RFind(nsDependentCString(aString), aIgnoreCase, aOffset, aCount); |
|
58 } |
|
59 |
|
60 |
|
61 /** |
|
62 * nsTString::RFindChar |
|
63 */ |
|
64 |
|
65 int32_t |
|
66 nsTString_CharT::RFindChar( char16_t aChar, int32_t aOffset, int32_t aCount) const |
|
67 { |
|
68 return nsBufferRoutines<CharT>::rfind_char(mData, mLength, aOffset, aChar, aCount); |
|
69 } |
|
70 |
|
71 |
|
72 /** |
|
73 * nsTString::FindCharInSet |
|
74 */ |
|
75 |
|
76 int32_t |
|
77 nsTString_CharT::FindCharInSet( const char* aSet, int32_t aOffset ) const |
|
78 { |
|
79 if (aOffset < 0) |
|
80 aOffset = 0; |
|
81 else if (aOffset >= int32_t(mLength)) |
|
82 return kNotFound; |
|
83 |
|
84 int32_t result = ::FindCharInSet(mData + aOffset, mLength - aOffset, aSet); |
|
85 if (result != kNotFound) |
|
86 result += aOffset; |
|
87 return result; |
|
88 } |
|
89 |
|
90 |
|
91 /** |
|
92 * nsTString::RFindCharInSet |
|
93 */ |
|
94 |
|
95 int32_t |
|
96 nsTString_CharT::RFindCharInSet( const CharT* aSet, int32_t aOffset ) const |
|
97 { |
|
98 // We want to pass a "data length" to ::RFindCharInSet |
|
99 if (aOffset < 0 || aOffset > int32_t(mLength)) |
|
100 aOffset = mLength; |
|
101 else |
|
102 ++aOffset; |
|
103 |
|
104 return ::RFindCharInSet(mData, aOffset, aSet); |
|
105 } |
|
106 |
|
107 |
|
108 // it's a shame to replicate this code. it was done this way in the past |
|
109 // to help performance. this function also gets to keep the rickg style |
|
110 // indentation :-/ |
|
111 int32_t |
|
112 nsTString_CharT::ToInteger( nsresult* aErrorCode, uint32_t aRadix ) const |
|
113 { |
|
114 CharT* cp=mData; |
|
115 int32_t theRadix=10; // base 10 unless base 16 detected, or overriden (aRadix != kAutoDetect) |
|
116 int32_t result=0; |
|
117 bool negate=false; |
|
118 CharT theChar=0; |
|
119 |
|
120 //initial value, override if we find an integer |
|
121 *aErrorCode=NS_ERROR_ILLEGAL_VALUE; |
|
122 |
|
123 if(cp) { |
|
124 |
|
125 //begin by skipping over leading chars that shouldn't be part of the number... |
|
126 |
|
127 CharT* endcp=cp+mLength; |
|
128 bool done=false; |
|
129 |
|
130 while((cp<endcp) && (!done)){ |
|
131 switch(*cp++) { |
|
132 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': |
|
133 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': |
|
134 theRadix=16; |
|
135 done=true; |
|
136 break; |
|
137 case '0': case '1': case '2': case '3': case '4': |
|
138 case '5': case '6': case '7': case '8': case '9': |
|
139 done=true; |
|
140 break; |
|
141 case '-': |
|
142 negate=true; //fall through... |
|
143 break; |
|
144 case 'X': case 'x': |
|
145 theRadix=16; |
|
146 break; |
|
147 default: |
|
148 break; |
|
149 } //switch |
|
150 } |
|
151 |
|
152 if (done) { |
|
153 |
|
154 //integer found |
|
155 *aErrorCode = NS_OK; |
|
156 |
|
157 if (aRadix!=kAutoDetect) theRadix = aRadix; // override |
|
158 |
|
159 //now iterate the numeric chars and build our result |
|
160 CharT* first=--cp; //in case we have to back up. |
|
161 bool haveValue = false; |
|
162 |
|
163 while(cp<endcp){ |
|
164 int32_t oldresult = result; |
|
165 |
|
166 theChar=*cp++; |
|
167 if(('0'<=theChar) && (theChar<='9')){ |
|
168 result = (theRadix * result) + (theChar-'0'); |
|
169 haveValue = true; |
|
170 } |
|
171 else if((theChar>='A') && (theChar<='F')) { |
|
172 if(10==theRadix) { |
|
173 if(kAutoDetect==aRadix){ |
|
174 theRadix=16; |
|
175 cp=first; //backup |
|
176 result=0; |
|
177 haveValue = false; |
|
178 } |
|
179 else { |
|
180 *aErrorCode=NS_ERROR_ILLEGAL_VALUE; |
|
181 result=0; |
|
182 break; |
|
183 } |
|
184 } |
|
185 else { |
|
186 result = (theRadix * result) + ((theChar-'A')+10); |
|
187 haveValue = true; |
|
188 } |
|
189 } |
|
190 else if((theChar>='a') && (theChar<='f')) { |
|
191 if(10==theRadix) { |
|
192 if(kAutoDetect==aRadix){ |
|
193 theRadix=16; |
|
194 cp=first; //backup |
|
195 result=0; |
|
196 haveValue = false; |
|
197 } |
|
198 else { |
|
199 *aErrorCode=NS_ERROR_ILLEGAL_VALUE; |
|
200 result=0; |
|
201 break; |
|
202 } |
|
203 } |
|
204 else { |
|
205 result = (theRadix * result) + ((theChar-'a')+10); |
|
206 haveValue = true; |
|
207 } |
|
208 } |
|
209 else if((('X'==theChar) || ('x'==theChar)) && (!haveValue || result == 0)) { |
|
210 continue; |
|
211 } |
|
212 else if((('#'==theChar) || ('+'==theChar)) && !haveValue) { |
|
213 continue; |
|
214 } |
|
215 else { |
|
216 //we've encountered a char that's not a legal number or sign |
|
217 break; |
|
218 } |
|
219 |
|
220 if (result < oldresult) { |
|
221 // overflow! |
|
222 *aErrorCode = NS_ERROR_ILLEGAL_VALUE; |
|
223 result = 0; |
|
224 break; |
|
225 } |
|
226 } //while |
|
227 if(negate) |
|
228 result=-result; |
|
229 } //if |
|
230 } |
|
231 return result; |
|
232 } |
|
233 |
|
234 |
|
235 /** |
|
236 * nsTString::ToInteger64 |
|
237 */ |
|
238 int64_t |
|
239 nsTString_CharT::ToInteger64( nsresult* aErrorCode, uint32_t aRadix ) const |
|
240 { |
|
241 CharT* cp=mData; |
|
242 int32_t theRadix=10; // base 10 unless base 16 detected, or overriden (aRadix != kAutoDetect) |
|
243 int64_t result=0; |
|
244 bool negate=false; |
|
245 CharT theChar=0; |
|
246 |
|
247 //initial value, override if we find an integer |
|
248 *aErrorCode=NS_ERROR_ILLEGAL_VALUE; |
|
249 |
|
250 if(cp) { |
|
251 |
|
252 //begin by skipping over leading chars that shouldn't be part of the number... |
|
253 |
|
254 CharT* endcp=cp+mLength; |
|
255 bool done=false; |
|
256 |
|
257 while((cp<endcp) && (!done)){ |
|
258 switch(*cp++) { |
|
259 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': |
|
260 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': |
|
261 theRadix=16; |
|
262 done=true; |
|
263 break; |
|
264 case '0': case '1': case '2': case '3': case '4': |
|
265 case '5': case '6': case '7': case '8': case '9': |
|
266 done=true; |
|
267 break; |
|
268 case '-': |
|
269 negate=true; //fall through... |
|
270 break; |
|
271 case 'X': case 'x': |
|
272 theRadix=16; |
|
273 break; |
|
274 default: |
|
275 break; |
|
276 } //switch |
|
277 } |
|
278 |
|
279 if (done) { |
|
280 |
|
281 //integer found |
|
282 *aErrorCode = NS_OK; |
|
283 |
|
284 if (aRadix!=kAutoDetect) theRadix = aRadix; // override |
|
285 |
|
286 //now iterate the numeric chars and build our result |
|
287 CharT* first=--cp; //in case we have to back up. |
|
288 bool haveValue = false; |
|
289 |
|
290 while(cp<endcp){ |
|
291 int64_t oldresult = result; |
|
292 |
|
293 theChar=*cp++; |
|
294 if(('0'<=theChar) && (theChar<='9')){ |
|
295 result = (theRadix * result) + (theChar-'0'); |
|
296 haveValue = true; |
|
297 } |
|
298 else if((theChar>='A') && (theChar<='F')) { |
|
299 if(10==theRadix) { |
|
300 if(kAutoDetect==aRadix){ |
|
301 theRadix=16; |
|
302 cp=first; //backup |
|
303 result=0; |
|
304 haveValue = false; |
|
305 } |
|
306 else { |
|
307 *aErrorCode=NS_ERROR_ILLEGAL_VALUE; |
|
308 result=0; |
|
309 break; |
|
310 } |
|
311 } |
|
312 else { |
|
313 result = (theRadix * result) + ((theChar-'A')+10); |
|
314 haveValue = true; |
|
315 } |
|
316 } |
|
317 else if((theChar>='a') && (theChar<='f')) { |
|
318 if(10==theRadix) { |
|
319 if(kAutoDetect==aRadix){ |
|
320 theRadix=16; |
|
321 cp=first; //backup |
|
322 result=0; |
|
323 haveValue = false; |
|
324 } |
|
325 else { |
|
326 *aErrorCode=NS_ERROR_ILLEGAL_VALUE; |
|
327 result=0; |
|
328 break; |
|
329 } |
|
330 } |
|
331 else { |
|
332 result = (theRadix * result) + ((theChar-'a')+10); |
|
333 haveValue = true; |
|
334 } |
|
335 } |
|
336 else if((('X'==theChar) || ('x'==theChar)) && (!haveValue || result == 0)) { |
|
337 continue; |
|
338 } |
|
339 else if((('#'==theChar) || ('+'==theChar)) && !haveValue) { |
|
340 continue; |
|
341 } |
|
342 else { |
|
343 //we've encountered a char that's not a legal number or sign |
|
344 break; |
|
345 } |
|
346 |
|
347 if (result < oldresult) { |
|
348 // overflow! |
|
349 *aErrorCode = NS_ERROR_ILLEGAL_VALUE; |
|
350 result = 0; |
|
351 break; |
|
352 } |
|
353 } //while |
|
354 if(negate) |
|
355 result=-result; |
|
356 } //if |
|
357 } |
|
358 return result; |
|
359 } |
|
360 |
|
361 |
|
362 /** |
|
363 * nsTString::Mid |
|
364 */ |
|
365 |
|
366 uint32_t |
|
367 nsTString_CharT::Mid( self_type& aResult, index_type aStartPos, size_type aLengthToCopy ) const |
|
368 { |
|
369 if (aStartPos == 0 && aLengthToCopy >= mLength) |
|
370 aResult = *this; |
|
371 else |
|
372 aResult = Substring(*this, aStartPos, aLengthToCopy); |
|
373 |
|
374 return aResult.mLength; |
|
375 } |
|
376 |
|
377 |
|
378 /** |
|
379 * nsTString::SetCharAt |
|
380 */ |
|
381 |
|
382 bool |
|
383 nsTString_CharT::SetCharAt( char16_t aChar, uint32_t aIndex ) |
|
384 { |
|
385 if (aIndex >= mLength) |
|
386 return false; |
|
387 |
|
388 if (!EnsureMutable()) |
|
389 NS_ABORT_OOM(mLength); |
|
390 |
|
391 mData[aIndex] = CharT(aChar); |
|
392 return true; |
|
393 } |
|
394 |
|
395 |
|
396 /** |
|
397 * nsTString::StripChars,StripChar,StripWhitespace |
|
398 */ |
|
399 |
|
400 void |
|
401 nsTString_CharT::StripChars( const char* aSet ) |
|
402 { |
|
403 if (!EnsureMutable()) |
|
404 NS_ABORT_OOM(mLength); |
|
405 |
|
406 mLength = nsBufferRoutines<CharT>::strip_chars(mData, mLength, aSet); |
|
407 } |
|
408 |
|
409 void |
|
410 nsTString_CharT::StripWhitespace() |
|
411 { |
|
412 StripChars(kWhitespace); |
|
413 } |
|
414 |
|
415 |
|
416 /** |
|
417 * nsTString::ReplaceChar,ReplaceSubstring |
|
418 */ |
|
419 |
|
420 void |
|
421 nsTString_CharT::ReplaceChar( char_type aOldChar, char_type aNewChar ) |
|
422 { |
|
423 if (!EnsureMutable()) // XXX do this lazily? |
|
424 NS_ABORT_OOM(mLength); |
|
425 |
|
426 for (uint32_t i=0; i<mLength; ++i) |
|
427 { |
|
428 if (mData[i] == aOldChar) |
|
429 mData[i] = aNewChar; |
|
430 } |
|
431 } |
|
432 |
|
433 void |
|
434 nsTString_CharT::ReplaceChar( const char* aSet, char_type aNewChar ) |
|
435 { |
|
436 if (!EnsureMutable()) // XXX do this lazily? |
|
437 NS_ABORT_OOM(mLength); |
|
438 |
|
439 char_type* data = mData; |
|
440 uint32_t lenRemaining = mLength; |
|
441 |
|
442 while (lenRemaining) |
|
443 { |
|
444 int32_t i = ::FindCharInSet(data, lenRemaining, aSet); |
|
445 if (i == kNotFound) |
|
446 break; |
|
447 |
|
448 data[i++] = aNewChar; |
|
449 data += i; |
|
450 lenRemaining -= i; |
|
451 } |
|
452 } |
|
453 |
|
454 void |
|
455 nsTString_CharT::ReplaceSubstring( const char_type* aTarget, const char_type* aNewValue ) |
|
456 { |
|
457 ReplaceSubstring(nsTDependentString_CharT(aTarget), |
|
458 nsTDependentString_CharT(aNewValue)); |
|
459 } |
|
460 |
|
461 void |
|
462 nsTString_CharT::ReplaceSubstring( const self_type& aTarget, const self_type& aNewValue ) |
|
463 { |
|
464 if (aTarget.Length() == 0) |
|
465 return; |
|
466 |
|
467 uint32_t i = 0; |
|
468 while (i < mLength) |
|
469 { |
|
470 int32_t r = FindSubstring(mData + i, mLength - i, static_cast<const char_type*>(aTarget.Data()), aTarget.Length(), false); |
|
471 if (r == kNotFound) |
|
472 break; |
|
473 |
|
474 Replace(i + r, aTarget.Length(), aNewValue); |
|
475 i += r + aNewValue.Length(); |
|
476 } |
|
477 } |
|
478 |
|
479 |
|
480 /** |
|
481 * nsTString::Trim |
|
482 */ |
|
483 |
|
484 void |
|
485 nsTString_CharT::Trim( const char* aSet, bool aTrimLeading, bool aTrimTrailing, bool aIgnoreQuotes ) |
|
486 { |
|
487 // the old implementation worried about aSet being null :-/ |
|
488 if (!aSet) |
|
489 return; |
|
490 |
|
491 char_type* start = mData; |
|
492 char_type* end = mData + mLength; |
|
493 |
|
494 // skip over quotes if requested |
|
495 if (aIgnoreQuotes && mLength > 2 && mData[0] == mData[mLength - 1] && |
|
496 (mData[0] == '\'' || mData[0] == '"')) |
|
497 { |
|
498 ++start; |
|
499 --end; |
|
500 } |
|
501 |
|
502 uint32_t setLen = nsCharTraits<char>::length(aSet); |
|
503 |
|
504 if (aTrimLeading) |
|
505 { |
|
506 uint32_t cutStart = start - mData; |
|
507 uint32_t cutLength = 0; |
|
508 |
|
509 // walk forward from start to end |
|
510 for (; start != end; ++start, ++cutLength) |
|
511 { |
|
512 int32_t pos = FindChar1(aSet, setLen, 0, *start, setLen); |
|
513 if (pos == kNotFound) |
|
514 break; |
|
515 } |
|
516 |
|
517 if (cutLength) |
|
518 { |
|
519 Cut(cutStart, cutLength); |
|
520 |
|
521 // reset iterators |
|
522 start = mData + cutStart; |
|
523 end = mData + mLength - cutStart; |
|
524 } |
|
525 } |
|
526 |
|
527 if (aTrimTrailing) |
|
528 { |
|
529 uint32_t cutEnd = end - mData; |
|
530 uint32_t cutLength = 0; |
|
531 |
|
532 // walk backward from end to start |
|
533 --end; |
|
534 for (; end >= start; --end, ++cutLength) |
|
535 { |
|
536 int32_t pos = FindChar1(aSet, setLen, 0, *end, setLen); |
|
537 if (pos == kNotFound) |
|
538 break; |
|
539 } |
|
540 |
|
541 if (cutLength) |
|
542 Cut(cutEnd - cutLength, cutLength); |
|
543 } |
|
544 } |
|
545 |
|
546 |
|
547 /** |
|
548 * nsTString::CompressWhitespace |
|
549 */ |
|
550 |
|
551 void |
|
552 nsTString_CharT::CompressWhitespace( bool aTrimLeading, bool aTrimTrailing ) |
|
553 { |
|
554 const char* set = kWhitespace; |
|
555 |
|
556 ReplaceChar(set, ' '); |
|
557 Trim(set, aTrimLeading, aTrimTrailing); |
|
558 |
|
559 // this one does some questionable fu... just copying the old code! |
|
560 mLength = nsBufferRoutines<char_type>::compress_chars(mData, mLength, set); |
|
561 } |
|
562 |
|
563 |
|
564 /** |
|
565 * nsTString::AssignWithConversion |
|
566 */ |
|
567 |
|
568 void |
|
569 nsTString_CharT::AssignWithConversion( const incompatible_char_type* aData, int32_t aLength ) |
|
570 { |
|
571 // for compatibility with the old string implementation, we need to allow |
|
572 // for a nullptr input buffer :-( |
|
573 if (!aData) |
|
574 { |
|
575 Truncate(); |
|
576 } |
|
577 else |
|
578 { |
|
579 if (aLength < 0) |
|
580 aLength = nsCharTraits<incompatible_char_type>::length(aData); |
|
581 |
|
582 AssignWithConversion(Substring(aData, aLength)); |
|
583 } |
|
584 } |