|
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 #include "mozilla/MemoryReporting.h" |
|
7 #include "double-conversion.h" |
|
8 |
|
9 using double_conversion::DoubleToStringConverter; |
|
10 |
|
11 #ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE |
|
12 nsTSubstring_CharT::nsTSubstring_CharT( char_type *data, size_type length, |
|
13 uint32_t flags) |
|
14 : mData(data), |
|
15 mLength(length), |
|
16 mFlags(flags) |
|
17 { |
|
18 if (flags & F_OWNED) { |
|
19 STRING_STAT_INCREMENT(Adopt); |
|
20 #ifdef NS_BUILD_REFCNT_LOGGING |
|
21 NS_LogCtor(mData, "StringAdopt", 1); |
|
22 #endif |
|
23 } |
|
24 } |
|
25 #endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */ |
|
26 |
|
27 /** |
|
28 * helper function for down-casting a nsTSubstring to a nsTFixedString. |
|
29 */ |
|
30 inline const nsTFixedString_CharT* |
|
31 AsFixedString( const nsTSubstring_CharT* s ) |
|
32 { |
|
33 return static_cast<const nsTFixedString_CharT*>(s); |
|
34 } |
|
35 |
|
36 |
|
37 /** |
|
38 * this function is called to prepare mData for writing. the given capacity |
|
39 * indicates the required minimum storage size for mData, in sizeof(char_type) |
|
40 * increments. this function returns true if the operation succeeds. it also |
|
41 * returns the old data and old flags members if mData is newly allocated. |
|
42 * the old data must be released by the caller. |
|
43 */ |
|
44 bool |
|
45 nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, uint32_t* oldFlags ) |
|
46 { |
|
47 // initialize to no old data |
|
48 *oldData = nullptr; |
|
49 *oldFlags = 0; |
|
50 |
|
51 size_type curCapacity = Capacity(); |
|
52 |
|
53 // If |capacity > kMaxCapacity|, then our doubling algorithm may not be |
|
54 // able to allocate it. Just bail out in cases like that. We don't want |
|
55 // to be allocating 2GB+ strings anyway. |
|
56 PR_STATIC_ASSERT((sizeof(nsStringBuffer) & 0x1) == 0); |
|
57 const size_type kMaxCapacity = |
|
58 (size_type(-1)/2 - sizeof(nsStringBuffer)) / sizeof(char_type) - 2; |
|
59 if (capacity > kMaxCapacity) { |
|
60 // Also assert for |capacity| equal to |size_type(-1)|, since we used to |
|
61 // use that value to flag immutability. |
|
62 NS_ASSERTION(capacity != size_type(-1), "Bogus capacity"); |
|
63 return false; |
|
64 } |
|
65 |
|
66 // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we |
|
67 // need to allocate a new buffer. We cannot use the existing buffer even |
|
68 // though it might be large enough. |
|
69 |
|
70 if (curCapacity != 0) |
|
71 { |
|
72 if (capacity <= curCapacity) { |
|
73 mFlags &= ~F_VOIDED; // mutation clears voided flag |
|
74 return true; |
|
75 } |
|
76 |
|
77 // Use doubling algorithm when forced to increase available capacity. |
|
78 size_type temp = curCapacity; |
|
79 while (temp < capacity) |
|
80 temp <<= 1; |
|
81 NS_ASSERTION(XPCOM_MIN(temp, kMaxCapacity) >= capacity, |
|
82 "should have hit the early return at the top"); |
|
83 capacity = XPCOM_MIN(temp, kMaxCapacity); |
|
84 } |
|
85 |
|
86 // |
|
87 // several cases: |
|
88 // |
|
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 |
|
93 // |
|
94 // requiring that we in some cases preserve the data before creating |
|
95 // a new buffer complicates things just a bit ;-) |
|
96 // |
|
97 |
|
98 size_type storageSize = (capacity + 1) * sizeof(char_type); |
|
99 |
|
100 // case #1 |
|
101 if (mFlags & F_SHARED) |
|
102 { |
|
103 nsStringBuffer* hdr = nsStringBuffer::FromData(mData); |
|
104 if (!hdr->IsReadonly()) |
|
105 { |
|
106 nsStringBuffer *newHdr = nsStringBuffer::Realloc(hdr, storageSize); |
|
107 if (!newHdr) |
|
108 return false; // out-of-memory (original header left intact) |
|
109 |
|
110 hdr = newHdr; |
|
111 mData = (char_type*) hdr->Data(); |
|
112 mFlags &= ~F_VOIDED; // mutation clears voided flag |
|
113 return true; |
|
114 } |
|
115 } |
|
116 |
|
117 char_type* newData; |
|
118 uint32_t newDataFlags; |
|
119 |
|
120 // if we have a fixed buffer of sufficient size, then use it. this helps |
|
121 // avoid heap allocations. |
|
122 if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity)) |
|
123 { |
|
124 newData = AsFixedString(this)->mFixedBuf; |
|
125 newDataFlags = F_TERMINATED | F_FIXED; |
|
126 } |
|
127 else |
|
128 { |
|
129 // if we reach here then, we must allocate a new buffer. we cannot |
|
130 // make use of our F_OWNED or F_FIXED buffers because they are not |
|
131 // large enough. |
|
132 |
|
133 nsStringBuffer* newHdr = |
|
134 nsStringBuffer::Alloc(storageSize).take(); |
|
135 if (!newHdr) |
|
136 return false; // we are still in a consistent state |
|
137 |
|
138 newData = (char_type*) newHdr->Data(); |
|
139 newDataFlags = F_TERMINATED | F_SHARED; |
|
140 } |
|
141 |
|
142 // save old data and flags |
|
143 *oldData = mData; |
|
144 *oldFlags = mFlags; |
|
145 |
|
146 mData = newData; |
|
147 SetDataFlags(newDataFlags); |
|
148 |
|
149 // mLength does not change |
|
150 |
|
151 // though we are not necessarily terminated at the moment, now is probably |
|
152 // still the best time to set F_TERMINATED. |
|
153 |
|
154 return true; |
|
155 } |
|
156 |
|
157 void |
|
158 nsTSubstring_CharT::Finalize() |
|
159 { |
|
160 ::ReleaseData(mData, mFlags); |
|
161 // mData, mLength, and mFlags are purposefully left dangling |
|
162 } |
|
163 |
|
164 bool |
|
165 nsTSubstring_CharT::ReplacePrepInternal(index_type cutStart, size_type cutLen, |
|
166 size_type fragLen, size_type newLen) |
|
167 { |
|
168 char_type* oldData; |
|
169 uint32_t oldFlags; |
|
170 if (!MutatePrep(newLen, &oldData, &oldFlags)) |
|
171 return false; // out-of-memory |
|
172 |
|
173 if (oldData) |
|
174 { |
|
175 // determine whether or not we need to copy part of the old string |
|
176 // over to the new string. |
|
177 |
|
178 if (cutStart > 0) |
|
179 { |
|
180 // copy prefix from old string |
|
181 char_traits::copy(mData, oldData, cutStart); |
|
182 } |
|
183 |
|
184 if (cutStart + cutLen < mLength) |
|
185 { |
|
186 // copy suffix from old string to new offset |
|
187 size_type from = cutStart + cutLen; |
|
188 size_type fromLen = mLength - from; |
|
189 uint32_t to = cutStart + fragLen; |
|
190 char_traits::copy(mData + to, oldData + from, fromLen); |
|
191 } |
|
192 |
|
193 ::ReleaseData(oldData, oldFlags); |
|
194 } |
|
195 else |
|
196 { |
|
197 // original data remains intact |
|
198 |
|
199 // determine whether or not we need to move part of the existing string |
|
200 // to make room for the requested hole. |
|
201 if (fragLen != cutLen && cutStart + cutLen < mLength) |
|
202 { |
|
203 uint32_t from = cutStart + cutLen; |
|
204 uint32_t fromLen = mLength - from; |
|
205 uint32_t to = cutStart + fragLen; |
|
206 char_traits::move(mData + to, mData + from, fromLen); |
|
207 } |
|
208 } |
|
209 |
|
210 // add null terminator (mutable mData always has room for the null- |
|
211 // terminator). |
|
212 mData[newLen] = char_type(0); |
|
213 mLength = newLen; |
|
214 |
|
215 return true; |
|
216 } |
|
217 |
|
218 nsTSubstring_CharT::size_type |
|
219 nsTSubstring_CharT::Capacity() const |
|
220 { |
|
221 // return 0 to indicate an immutable or 0-sized buffer |
|
222 |
|
223 size_type capacity; |
|
224 if (mFlags & F_SHARED) |
|
225 { |
|
226 // if the string is readonly, then we pretend that it has no capacity. |
|
227 nsStringBuffer* hdr = nsStringBuffer::FromData(mData); |
|
228 if (hdr->IsReadonly()) |
|
229 capacity = 0; |
|
230 else { |
|
231 capacity = (hdr->StorageSize() / sizeof(char_type)) - 1; |
|
232 } |
|
233 } |
|
234 else if (mFlags & F_FIXED) |
|
235 { |
|
236 capacity = AsFixedString(this)->mFixedCapacity; |
|
237 } |
|
238 else if (mFlags & F_OWNED) |
|
239 { |
|
240 // we don't store the capacity of an adopted buffer because that would |
|
241 // require an additional member field. the best we can do is base the |
|
242 // capacity on our length. remains to be seen if this is the right |
|
243 // trade-off. |
|
244 capacity = mLength; |
|
245 } |
|
246 else |
|
247 { |
|
248 capacity = 0; |
|
249 } |
|
250 |
|
251 return capacity; |
|
252 } |
|
253 |
|
254 bool |
|
255 nsTSubstring_CharT::EnsureMutable( size_type newLen ) |
|
256 { |
|
257 if (newLen == size_type(-1) || newLen == mLength) |
|
258 { |
|
259 if (mFlags & (F_FIXED | F_OWNED)) |
|
260 return true; |
|
261 if ((mFlags & F_SHARED) && !nsStringBuffer::FromData(mData)->IsReadonly()) |
|
262 return true; |
|
263 |
|
264 newLen = mLength; |
|
265 } |
|
266 return SetLength(newLen, fallible_t()); |
|
267 } |
|
268 |
|
269 // --------------------------------------------------------------------------- |
|
270 |
|
271 // This version of Assign is optimized for single-character assignment. |
|
272 void |
|
273 nsTSubstring_CharT::Assign( char_type c ) |
|
274 { |
|
275 if (!ReplacePrep(0, mLength, 1)) |
|
276 NS_ABORT_OOM(mLength); |
|
277 |
|
278 *mData = c; |
|
279 } |
|
280 |
|
281 bool |
|
282 nsTSubstring_CharT::Assign( char_type c, const fallible_t& ) |
|
283 { |
|
284 if (!ReplacePrep(0, mLength, 1)) |
|
285 return false; |
|
286 |
|
287 *mData = c; |
|
288 return true; |
|
289 } |
|
290 |
|
291 void |
|
292 nsTSubstring_CharT::Assign( const char_type* data ) |
|
293 { |
|
294 if (!Assign(data, size_type(-1), fallible_t())) |
|
295 NS_ABORT_OOM(char_traits::length(data)); |
|
296 } |
|
297 |
|
298 void |
|
299 nsTSubstring_CharT::Assign( const char_type* data, size_type length ) |
|
300 { |
|
301 if (!Assign(data, length, fallible_t())) |
|
302 NS_ABORT_OOM(length); |
|
303 } |
|
304 |
|
305 bool |
|
306 nsTSubstring_CharT::Assign( const char_type* data, size_type length, const fallible_t& ) |
|
307 { |
|
308 if (!data || length == 0) |
|
309 { |
|
310 Truncate(); |
|
311 return true; |
|
312 } |
|
313 |
|
314 if (length == size_type(-1)) |
|
315 length = char_traits::length(data); |
|
316 |
|
317 if (IsDependentOn(data, data + length)) |
|
318 { |
|
319 return Assign(string_type(data, length), fallible_t()); |
|
320 } |
|
321 |
|
322 if (!ReplacePrep(0, mLength, length)) |
|
323 return false; |
|
324 |
|
325 char_traits::copy(mData, data, length); |
|
326 return true; |
|
327 } |
|
328 |
|
329 void |
|
330 nsTSubstring_CharT::AssignASCII( const char* data, size_type length ) |
|
331 { |
|
332 if (!AssignASCII(data, length, fallible_t())) |
|
333 NS_ABORT_OOM(length); |
|
334 } |
|
335 |
|
336 bool |
|
337 nsTSubstring_CharT::AssignASCII( const char* data, size_type length, const fallible_t& ) |
|
338 { |
|
339 // A Unicode string can't depend on an ASCII string buffer, |
|
340 // so this dependence check only applies to CStrings. |
|
341 #ifdef CharT_is_char |
|
342 if (IsDependentOn(data, data + length)) |
|
343 { |
|
344 return Assign(string_type(data, length), fallible_t()); |
|
345 } |
|
346 #endif |
|
347 |
|
348 if (!ReplacePrep(0, mLength, length)) |
|
349 return false; |
|
350 |
|
351 char_traits::copyASCII(mData, data, length); |
|
352 return true; |
|
353 } |
|
354 |
|
355 void |
|
356 nsTSubstring_CharT::AssignLiteral( const char_type* data, size_type length ) |
|
357 { |
|
358 ::ReleaseData(mData, mFlags); |
|
359 mData = const_cast<char_type*>(data); |
|
360 mLength = length; |
|
361 SetDataFlags(F_TERMINATED | F_LITERAL); |
|
362 } |
|
363 |
|
364 void |
|
365 nsTSubstring_CharT::Assign( const self_type& str ) |
|
366 { |
|
367 if (!Assign(str, fallible_t())) |
|
368 NS_ABORT_OOM(str.Length()); |
|
369 } |
|
370 |
|
371 bool |
|
372 nsTSubstring_CharT::Assign( const self_type& str, const fallible_t& ) |
|
373 { |
|
374 // |str| could be sharable. we need to check its flags to know how to |
|
375 // deal with it. |
|
376 |
|
377 if (&str == this) |
|
378 return true; |
|
379 |
|
380 if (!str.mLength) |
|
381 { |
|
382 Truncate(); |
|
383 mFlags |= str.mFlags & F_VOIDED; |
|
384 return true; |
|
385 } |
|
386 |
|
387 if (str.mFlags & F_SHARED) |
|
388 { |
|
389 // nice! we can avoid a string copy :-) |
|
390 |
|
391 // |str| should be null-terminated |
|
392 NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated"); |
|
393 |
|
394 ::ReleaseData(mData, mFlags); |
|
395 |
|
396 mData = str.mData; |
|
397 mLength = str.mLength; |
|
398 SetDataFlags(F_TERMINATED | F_SHARED); |
|
399 |
|
400 // get an owning reference to the mData |
|
401 nsStringBuffer::FromData(mData)->AddRef(); |
|
402 return true; |
|
403 } |
|
404 else if (str.mFlags & F_LITERAL) |
|
405 { |
|
406 NS_ABORT_IF_FALSE(str.mFlags & F_TERMINATED, "Unterminated literal"); |
|
407 |
|
408 AssignLiteral(str.mData, str.mLength); |
|
409 return true; |
|
410 } |
|
411 |
|
412 // else, treat this like an ordinary assignment. |
|
413 return Assign(str.Data(), str.Length(), fallible_t()); |
|
414 } |
|
415 |
|
416 void |
|
417 nsTSubstring_CharT::Assign( const substring_tuple_type& tuple ) |
|
418 { |
|
419 if (!Assign(tuple, fallible_t())) |
|
420 NS_ABORT_OOM(tuple.Length()); |
|
421 } |
|
422 |
|
423 bool |
|
424 nsTSubstring_CharT::Assign( const substring_tuple_type& tuple, const fallible_t& ) |
|
425 { |
|
426 if (tuple.IsDependentOn(mData, mData + mLength)) |
|
427 { |
|
428 // take advantage of sharing here... |
|
429 return Assign(string_type(tuple), fallible_t()); |
|
430 } |
|
431 |
|
432 size_type length = tuple.Length(); |
|
433 |
|
434 // don't use ReplacePrep here because it changes the length |
|
435 char_type* oldData; |
|
436 uint32_t oldFlags; |
|
437 if (!MutatePrep(length, &oldData, &oldFlags)) |
|
438 return false; |
|
439 |
|
440 if (oldData) |
|
441 ::ReleaseData(oldData, oldFlags); |
|
442 |
|
443 tuple.WriteTo(mData, length); |
|
444 mData[length] = 0; |
|
445 mLength = length; |
|
446 return true; |
|
447 } |
|
448 |
|
449 void |
|
450 nsTSubstring_CharT::Adopt( char_type* data, size_type length ) |
|
451 { |
|
452 if (data) |
|
453 { |
|
454 ::ReleaseData(mData, mFlags); |
|
455 |
|
456 if (length == size_type(-1)) |
|
457 length = char_traits::length(data); |
|
458 |
|
459 mData = data; |
|
460 mLength = length; |
|
461 SetDataFlags(F_TERMINATED | F_OWNED); |
|
462 |
|
463 STRING_STAT_INCREMENT(Adopt); |
|
464 #ifdef NS_BUILD_REFCNT_LOGGING |
|
465 // Treat this as construction of a "StringAdopt" object for leak |
|
466 // tracking purposes. |
|
467 NS_LogCtor(mData, "StringAdopt", 1); |
|
468 #endif // NS_BUILD_REFCNT_LOGGING |
|
469 } |
|
470 else |
|
471 { |
|
472 SetIsVoid(true); |
|
473 } |
|
474 } |
|
475 |
|
476 |
|
477 // This version of Replace is optimized for single-character replacement. |
|
478 void |
|
479 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c ) |
|
480 { |
|
481 cutStart = XPCOM_MIN(cutStart, Length()); |
|
482 |
|
483 if (ReplacePrep(cutStart, cutLength, 1)) |
|
484 mData[cutStart] = c; |
|
485 } |
|
486 |
|
487 bool |
|
488 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c, const mozilla::fallible_t& ) |
|
489 { |
|
490 cutStart = XPCOM_MIN(cutStart, Length()); |
|
491 |
|
492 if (!ReplacePrep(cutStart, cutLength, 1)) |
|
493 return false; |
|
494 |
|
495 mData[cutStart] = c; |
|
496 |
|
497 return true; |
|
498 } |
|
499 |
|
500 void |
|
501 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length ) |
|
502 { |
|
503 if (!Replace(cutStart, cutLength, data, length, mozilla::fallible_t())) |
|
504 { |
|
505 NS_ABORT_OOM(Length() - cutLength + 1); |
|
506 } |
|
507 } |
|
508 |
|
509 bool |
|
510 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length, const mozilla::fallible_t& ) |
|
511 { |
|
512 // unfortunately, some callers pass null :-( |
|
513 if (!data) |
|
514 { |
|
515 length = 0; |
|
516 } |
|
517 else |
|
518 { |
|
519 if (length == size_type(-1)) |
|
520 length = char_traits::length(data); |
|
521 |
|
522 if (IsDependentOn(data, data + length)) |
|
523 { |
|
524 nsTAutoString_CharT temp(data, length); |
|
525 return Replace(cutStart, cutLength, temp, mozilla::fallible_t()); |
|
526 } |
|
527 } |
|
528 |
|
529 cutStart = XPCOM_MIN(cutStart, Length()); |
|
530 |
|
531 bool ok = ReplacePrep(cutStart, cutLength, length); |
|
532 if (!ok) |
|
533 return false; |
|
534 |
|
535 if (length > 0) |
|
536 char_traits::copy(mData + cutStart, data, length); |
|
537 |
|
538 return true; |
|
539 } |
|
540 |
|
541 void |
|
542 nsTSubstring_CharT::ReplaceASCII( index_type cutStart, size_type cutLength, const char* data, size_type length ) |
|
543 { |
|
544 if (length == size_type(-1)) |
|
545 length = strlen(data); |
|
546 |
|
547 // A Unicode string can't depend on an ASCII string buffer, |
|
548 // so this dependence check only applies to CStrings. |
|
549 #ifdef CharT_is_char |
|
550 if (IsDependentOn(data, data + length)) |
|
551 { |
|
552 nsTAutoString_CharT temp(data, length); |
|
553 Replace(cutStart, cutLength, temp); |
|
554 return; |
|
555 } |
|
556 #endif |
|
557 |
|
558 cutStart = XPCOM_MIN(cutStart, Length()); |
|
559 |
|
560 if (ReplacePrep(cutStart, cutLength, length) && length > 0) |
|
561 char_traits::copyASCII(mData + cutStart, data, length); |
|
562 } |
|
563 |
|
564 void |
|
565 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& tuple ) |
|
566 { |
|
567 if (tuple.IsDependentOn(mData, mData + mLength)) |
|
568 { |
|
569 nsTAutoString_CharT temp(tuple); |
|
570 Replace(cutStart, cutLength, temp); |
|
571 return; |
|
572 } |
|
573 |
|
574 size_type length = tuple.Length(); |
|
575 |
|
576 cutStart = XPCOM_MIN(cutStart, Length()); |
|
577 |
|
578 if (ReplacePrep(cutStart, cutLength, length) && length > 0) |
|
579 tuple.WriteTo(mData + cutStart, length); |
|
580 } |
|
581 |
|
582 void |
|
583 nsTSubstring_CharT::ReplaceLiteral( index_type cutStart, size_type cutLength, const char_type* data, size_type length ) |
|
584 { |
|
585 cutStart = XPCOM_MIN(cutStart, Length()); |
|
586 |
|
587 if (!cutStart && cutLength == Length()) |
|
588 AssignLiteral(data, length); |
|
589 else if (ReplacePrep(cutStart, cutLength, length) && length > 0) |
|
590 char_traits::copy(mData + cutStart, data, length); |
|
591 } |
|
592 |
|
593 void |
|
594 nsTSubstring_CharT::SetCapacity( size_type capacity ) |
|
595 { |
|
596 if (!SetCapacity(capacity, fallible_t())) |
|
597 NS_ABORT_OOM(capacity); |
|
598 } |
|
599 |
|
600 bool |
|
601 nsTSubstring_CharT::SetCapacity( size_type capacity, const fallible_t& ) |
|
602 { |
|
603 // capacity does not include room for the terminating null char |
|
604 |
|
605 // if our capacity is reduced to zero, then free our buffer. |
|
606 if (capacity == 0) |
|
607 { |
|
608 ::ReleaseData(mData, mFlags); |
|
609 mData = char_traits::sEmptyBuffer; |
|
610 mLength = 0; |
|
611 SetDataFlags(F_TERMINATED); |
|
612 return true; |
|
613 } |
|
614 |
|
615 char_type* oldData; |
|
616 uint32_t oldFlags; |
|
617 if (!MutatePrep(capacity, &oldData, &oldFlags)) |
|
618 return false; // out-of-memory |
|
619 |
|
620 // compute new string length |
|
621 size_type newLen = XPCOM_MIN(mLength, capacity); |
|
622 |
|
623 if (oldData) |
|
624 { |
|
625 // preserve old data |
|
626 if (mLength > 0) |
|
627 char_traits::copy(mData, oldData, newLen); |
|
628 |
|
629 ::ReleaseData(oldData, oldFlags); |
|
630 } |
|
631 |
|
632 // adjust mLength if our buffer shrunk down in size |
|
633 if (newLen < mLength) |
|
634 mLength = newLen; |
|
635 |
|
636 // always null-terminate here, even if the buffer got longer. this is |
|
637 // for backwards compat with the old string implementation. |
|
638 mData[capacity] = char_type(0); |
|
639 |
|
640 return true; |
|
641 } |
|
642 |
|
643 void |
|
644 nsTSubstring_CharT::SetLength( size_type length ) |
|
645 { |
|
646 SetCapacity(length); |
|
647 mLength = length; |
|
648 } |
|
649 |
|
650 bool |
|
651 nsTSubstring_CharT::SetLength( size_type length, const fallible_t& ) |
|
652 { |
|
653 if (!SetCapacity(length, fallible_t())) |
|
654 return false; |
|
655 |
|
656 mLength = length; |
|
657 return true; |
|
658 } |
|
659 |
|
660 void |
|
661 nsTSubstring_CharT::SetIsVoid( bool val ) |
|
662 { |
|
663 if (val) |
|
664 { |
|
665 Truncate(); |
|
666 mFlags |= F_VOIDED; |
|
667 } |
|
668 else |
|
669 { |
|
670 mFlags &= ~F_VOIDED; |
|
671 } |
|
672 } |
|
673 |
|
674 bool |
|
675 nsTSubstring_CharT::Equals( const self_type& str ) const |
|
676 { |
|
677 return mLength == str.mLength && char_traits::compare(mData, str.mData, mLength) == 0; |
|
678 } |
|
679 |
|
680 bool |
|
681 nsTSubstring_CharT::Equals( const self_type& str, const comparator_type& comp ) const |
|
682 { |
|
683 return mLength == str.mLength && comp(mData, str.mData, mLength, str.mLength) == 0; |
|
684 } |
|
685 |
|
686 bool |
|
687 nsTSubstring_CharT::Equals( const char_type* data ) const |
|
688 { |
|
689 // unfortunately, some callers pass null :-( |
|
690 if (!data) |
|
691 { |
|
692 NS_NOTREACHED("null data pointer"); |
|
693 return mLength == 0; |
|
694 } |
|
695 |
|
696 // XXX avoid length calculation? |
|
697 size_type length = char_traits::length(data); |
|
698 return mLength == length && char_traits::compare(mData, data, mLength) == 0; |
|
699 } |
|
700 |
|
701 bool |
|
702 nsTSubstring_CharT::Equals( const char_type* data, const comparator_type& comp ) const |
|
703 { |
|
704 // unfortunately, some callers pass null :-( |
|
705 if (!data) |
|
706 { |
|
707 NS_NOTREACHED("null data pointer"); |
|
708 return mLength == 0; |
|
709 } |
|
710 |
|
711 // XXX avoid length calculation? |
|
712 size_type length = char_traits::length(data); |
|
713 return mLength == length && comp(mData, data, mLength, length) == 0; |
|
714 } |
|
715 |
|
716 bool |
|
717 nsTSubstring_CharT::EqualsASCII( const char* data, size_type len ) const |
|
718 { |
|
719 return mLength == len && char_traits::compareASCII(mData, data, len) == 0; |
|
720 } |
|
721 |
|
722 bool |
|
723 nsTSubstring_CharT::EqualsASCII( const char* data ) const |
|
724 { |
|
725 return char_traits::compareASCIINullTerminated(mData, mLength, data) == 0; |
|
726 } |
|
727 |
|
728 bool |
|
729 nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data, size_type len ) const |
|
730 { |
|
731 return mLength == len && char_traits::compareLowerCaseToASCII(mData, data, len) == 0; |
|
732 } |
|
733 |
|
734 bool |
|
735 nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data ) const |
|
736 { |
|
737 return char_traits::compareLowerCaseToASCIINullTerminated(mData, mLength, data) == 0; |
|
738 } |
|
739 |
|
740 nsTSubstring_CharT::size_type |
|
741 nsTSubstring_CharT::CountChar( char_type c ) const |
|
742 { |
|
743 const char_type *start = mData; |
|
744 const char_type *end = mData + mLength; |
|
745 |
|
746 return NS_COUNT(start, end, c); |
|
747 } |
|
748 |
|
749 int32_t |
|
750 nsTSubstring_CharT::FindChar( char_type c, index_type offset ) const |
|
751 { |
|
752 if (offset < mLength) |
|
753 { |
|
754 const char_type* result = char_traits::find(mData + offset, mLength - offset, c); |
|
755 if (result) |
|
756 return result - mData; |
|
757 } |
|
758 return -1; |
|
759 } |
|
760 |
|
761 void |
|
762 nsTSubstring_CharT::StripChar( char_type aChar, int32_t aOffset ) |
|
763 { |
|
764 if (mLength == 0 || aOffset >= int32_t(mLength)) |
|
765 return; |
|
766 |
|
767 if (!EnsureMutable()) // XXX do this lazily? |
|
768 NS_ABORT_OOM(mLength); |
|
769 |
|
770 // XXX(darin): this code should defer writing until necessary. |
|
771 |
|
772 char_type* to = mData + aOffset; |
|
773 char_type* from = mData + aOffset; |
|
774 char_type* end = mData + mLength; |
|
775 |
|
776 while (from < end) |
|
777 { |
|
778 char_type theChar = *from++; |
|
779 if (aChar != theChar) |
|
780 *to++ = theChar; |
|
781 } |
|
782 *to = char_type(0); // add the null |
|
783 mLength = to - mData; |
|
784 } |
|
785 |
|
786 void |
|
787 nsTSubstring_CharT::StripChars( const char_type* aChars, uint32_t aOffset ) |
|
788 { |
|
789 if (aOffset >= uint32_t(mLength)) |
|
790 return; |
|
791 |
|
792 if (!EnsureMutable()) // XXX do this lazily? |
|
793 NS_ABORT_OOM(mLength); |
|
794 |
|
795 // XXX(darin): this code should defer writing until necessary. |
|
796 |
|
797 char_type* to = mData + aOffset; |
|
798 char_type* from = mData + aOffset; |
|
799 char_type* end = mData + mLength; |
|
800 |
|
801 while (from < end) |
|
802 { |
|
803 char_type theChar = *from++; |
|
804 const char_type* test = aChars; |
|
805 |
|
806 for (; *test && *test != theChar; ++test); |
|
807 |
|
808 if (!*test) { |
|
809 // Not stripped, copy this char. |
|
810 *to++ = theChar; |
|
811 } |
|
812 } |
|
813 *to = char_type(0); // add the null |
|
814 mLength = to - mData; |
|
815 } |
|
816 |
|
817 int |
|
818 nsTSubstring_CharT::AppendFunc(void* arg, const char* s, uint32_t len) |
|
819 { |
|
820 self_type* self = static_cast<self_type*>(arg); |
|
821 |
|
822 // NSPR sends us the final null terminator even though we don't want it |
|
823 if (len && s[len - 1] == '\0') { |
|
824 --len; |
|
825 } |
|
826 |
|
827 self->AppendASCII(s, len); |
|
828 |
|
829 return len; |
|
830 } |
|
831 |
|
832 void nsTSubstring_CharT::AppendPrintf( const char* format, ...) |
|
833 { |
|
834 va_list ap; |
|
835 va_start(ap, format); |
|
836 uint32_t r = PR_vsxprintf(AppendFunc, this, format, ap); |
|
837 if (r == (uint32_t) -1) |
|
838 NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf"); |
|
839 va_end(ap); |
|
840 } |
|
841 |
|
842 void nsTSubstring_CharT::AppendPrintf( const char* format, va_list ap ) |
|
843 { |
|
844 uint32_t r = PR_vsxprintf(AppendFunc, this, format, ap); |
|
845 if (r == (uint32_t) -1) |
|
846 NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf"); |
|
847 } |
|
848 |
|
849 /* hack to make sure we define FormatWithoutTrailingZeros only once */ |
|
850 #ifdef CharT_is_PRUnichar |
|
851 // Returns the length of the formatted aDouble in buf. |
|
852 static int |
|
853 FormatWithoutTrailingZeros(char (& buf)[40], double aDouble, |
|
854 int precision) |
|
855 { |
|
856 static const DoubleToStringConverter converter(DoubleToStringConverter::UNIQUE_ZERO | |
|
857 DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN, |
|
858 "Infinity", |
|
859 "NaN", |
|
860 'e', |
|
861 -6, 21, |
|
862 6, 1); |
|
863 double_conversion::StringBuilder builder(buf, sizeof(buf)); |
|
864 bool exponential_notation = false; |
|
865 converter.ToPrecision(aDouble, precision, &exponential_notation, &builder); |
|
866 int length = builder.position(); |
|
867 char* formattedDouble = builder.Finalize(); |
|
868 |
|
869 // If we have a shorter string than precision, it means we have a special |
|
870 // value (NaN or Infinity). All other numbers will be formatted with at |
|
871 // least precision digits. |
|
872 if (length <= precision) { |
|
873 return length; |
|
874 } |
|
875 |
|
876 char* end = formattedDouble + length; |
|
877 char* decimalPoint = strchr(buf, '.'); |
|
878 // No trailing zeros to remove. |
|
879 if (decimalPoint == nullptr) { |
|
880 return length; |
|
881 } |
|
882 |
|
883 if (MOZ_UNLIKELY(exponential_notation)) { |
|
884 // We need to check for cases like 1.00000e-10 (yes, this is |
|
885 // disgusting). |
|
886 char* exponent = end - 1; |
|
887 for ( ; ; --exponent) { |
|
888 if (*exponent == 'e') { |
|
889 break; |
|
890 } |
|
891 } |
|
892 char* zerosBeforeExponent = exponent - 1; |
|
893 for ( ; zerosBeforeExponent != decimalPoint; --zerosBeforeExponent) { |
|
894 if (*zerosBeforeExponent != '0') { |
|
895 break; |
|
896 } |
|
897 } |
|
898 if (zerosBeforeExponent == decimalPoint) { |
|
899 --zerosBeforeExponent; |
|
900 } |
|
901 // Slide the exponent to the left over the trailing zeros. Don't |
|
902 // worry about copying the trailing NUL character. |
|
903 size_t exponentSize = end - exponent; |
|
904 memmove(zerosBeforeExponent + 1, exponent, exponentSize); |
|
905 length -= exponent - (zerosBeforeExponent + 1); |
|
906 } else { |
|
907 char* trailingZeros = end - 1; |
|
908 for ( ; trailingZeros != decimalPoint; --trailingZeros) { |
|
909 if (*trailingZeros != '0') { |
|
910 break; |
|
911 } |
|
912 } |
|
913 if (trailingZeros == decimalPoint) { |
|
914 --trailingZeros; |
|
915 } |
|
916 length -= end - (trailingZeros + 1); |
|
917 } |
|
918 |
|
919 return length; |
|
920 } |
|
921 #endif /* CharT_is_PRUnichar */ |
|
922 |
|
923 void |
|
924 nsTSubstring_CharT::AppendFloat( float aFloat ) |
|
925 { |
|
926 char buf[40]; |
|
927 int length = FormatWithoutTrailingZeros(buf, aFloat, 6); |
|
928 AppendASCII(buf, length); |
|
929 } |
|
930 |
|
931 void |
|
932 nsTSubstring_CharT::AppendFloat( double aFloat ) |
|
933 { |
|
934 char buf[40]; |
|
935 int length = FormatWithoutTrailingZeros(buf, aFloat, 15); |
|
936 AppendASCII(buf, length); |
|
937 } |
|
938 |
|
939 size_t |
|
940 nsTSubstring_CharT::SizeOfExcludingThisMustBeUnshared( |
|
941 mozilla::MallocSizeOf mallocSizeOf) const |
|
942 { |
|
943 if (mFlags & F_SHARED) { |
|
944 return nsStringBuffer::FromData(mData)-> |
|
945 SizeOfIncludingThisMustBeUnshared(mallocSizeOf); |
|
946 } |
|
947 if (mFlags & F_OWNED) { |
|
948 return mallocSizeOf(mData); |
|
949 } |
|
950 |
|
951 // If we reach here, exactly one of the following must be true: |
|
952 // - F_VOIDED is set, and mData points to sEmptyBuffer; |
|
953 // - F_FIXED is set, and mData points to a buffer within a string |
|
954 // object (e.g. nsAutoString); |
|
955 // - None of F_SHARED, F_OWNED, F_FIXED is set, and mData points to a buffer |
|
956 // owned by something else. |
|
957 // |
|
958 // In all three cases, we don't measure it. |
|
959 return 0; |
|
960 } |
|
961 |
|
962 size_t |
|
963 nsTSubstring_CharT::SizeOfExcludingThisIfUnshared( |
|
964 mozilla::MallocSizeOf mallocSizeOf) const |
|
965 { |
|
966 // This is identical to SizeOfExcludingThisMustBeUnshared except for the |
|
967 // F_SHARED case. |
|
968 if (mFlags & F_SHARED) { |
|
969 return nsStringBuffer::FromData(mData)-> |
|
970 SizeOfIncludingThisIfUnshared(mallocSizeOf); |
|
971 } |
|
972 if (mFlags & F_OWNED) { |
|
973 return mallocSizeOf(mData); |
|
974 } |
|
975 return 0; |
|
976 } |
|
977 |
|
978 size_t |
|
979 nsTSubstring_CharT::SizeOfExcludingThisEvenIfShared( |
|
980 mozilla::MallocSizeOf mallocSizeOf) const |
|
981 { |
|
982 // This is identical to SizeOfExcludingThisMustBeUnshared except for the |
|
983 // F_SHARED case. |
|
984 if (mFlags & F_SHARED) { |
|
985 return nsStringBuffer::FromData(mData)-> |
|
986 SizeOfIncludingThisEvenIfShared(mallocSizeOf); |
|
987 } |
|
988 if (mFlags & F_OWNED) { |
|
989 return mallocSizeOf(mData); |
|
990 } |
|
991 return 0; |
|
992 } |
|
993 |
|
994 size_t |
|
995 nsTSubstring_CharT::SizeOfIncludingThisMustBeUnshared( |
|
996 mozilla::MallocSizeOf mallocSizeOf) const |
|
997 { |
|
998 return mallocSizeOf(this) + SizeOfExcludingThisMustBeUnshared(mallocSizeOf); |
|
999 } |
|
1000 |
|
1001 size_t |
|
1002 nsTSubstring_CharT::SizeOfIncludingThisIfUnshared( |
|
1003 mozilla::MallocSizeOf mallocSizeOf) const |
|
1004 { |
|
1005 return mallocSizeOf(this) + SizeOfExcludingThisIfUnshared(mallocSizeOf); |
|
1006 } |
|
1007 |
|
1008 size_t |
|
1009 nsTSubstring_CharT::SizeOfIncludingThisEvenIfShared( |
|
1010 mozilla::MallocSizeOf mallocSizeOf) const |
|
1011 { |
|
1012 return mallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(mallocSizeOf); |
|
1013 } |
|
1014 |