1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/string/src/nsTSubstring.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1014 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 +#include "mozilla/MemoryReporting.h" 1.10 +#include "double-conversion.h" 1.11 + 1.12 +using double_conversion::DoubleToStringConverter; 1.13 + 1.14 +#ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE 1.15 +nsTSubstring_CharT::nsTSubstring_CharT( char_type *data, size_type length, 1.16 + uint32_t flags) 1.17 + : mData(data), 1.18 + mLength(length), 1.19 + mFlags(flags) 1.20 + { 1.21 + if (flags & F_OWNED) { 1.22 + STRING_STAT_INCREMENT(Adopt); 1.23 +#ifdef NS_BUILD_REFCNT_LOGGING 1.24 + NS_LogCtor(mData, "StringAdopt", 1); 1.25 +#endif 1.26 + } 1.27 + } 1.28 +#endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */ 1.29 + 1.30 + /** 1.31 + * helper function for down-casting a nsTSubstring to a nsTFixedString. 1.32 + */ 1.33 +inline const nsTFixedString_CharT* 1.34 +AsFixedString( const nsTSubstring_CharT* s ) 1.35 + { 1.36 + return static_cast<const nsTFixedString_CharT*>(s); 1.37 + } 1.38 + 1.39 + 1.40 + /** 1.41 + * this function is called to prepare mData for writing. the given capacity 1.42 + * indicates the required minimum storage size for mData, in sizeof(char_type) 1.43 + * increments. this function returns true if the operation succeeds. it also 1.44 + * returns the old data and old flags members if mData is newly allocated. 1.45 + * the old data must be released by the caller. 1.46 + */ 1.47 +bool 1.48 +nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, uint32_t* oldFlags ) 1.49 + { 1.50 + // initialize to no old data 1.51 + *oldData = nullptr; 1.52 + *oldFlags = 0; 1.53 + 1.54 + size_type curCapacity = Capacity(); 1.55 + 1.56 + // If |capacity > kMaxCapacity|, then our doubling algorithm may not be 1.57 + // able to allocate it. Just bail out in cases like that. We don't want 1.58 + // to be allocating 2GB+ strings anyway. 1.59 + PR_STATIC_ASSERT((sizeof(nsStringBuffer) & 0x1) == 0); 1.60 + const size_type kMaxCapacity = 1.61 + (size_type(-1)/2 - sizeof(nsStringBuffer)) / sizeof(char_type) - 2; 1.62 + if (capacity > kMaxCapacity) { 1.63 + // Also assert for |capacity| equal to |size_type(-1)|, since we used to 1.64 + // use that value to flag immutability. 1.65 + NS_ASSERTION(capacity != size_type(-1), "Bogus capacity"); 1.66 + return false; 1.67 + } 1.68 + 1.69 + // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we 1.70 + // need to allocate a new buffer. We cannot use the existing buffer even 1.71 + // though it might be large enough. 1.72 + 1.73 + if (curCapacity != 0) 1.74 + { 1.75 + if (capacity <= curCapacity) { 1.76 + mFlags &= ~F_VOIDED; // mutation clears voided flag 1.77 + return true; 1.78 + } 1.79 + 1.80 + // Use doubling algorithm when forced to increase available capacity. 1.81 + size_type temp = curCapacity; 1.82 + while (temp < capacity) 1.83 + temp <<= 1; 1.84 + NS_ASSERTION(XPCOM_MIN(temp, kMaxCapacity) >= capacity, 1.85 + "should have hit the early return at the top"); 1.86 + capacity = XPCOM_MIN(temp, kMaxCapacity); 1.87 + } 1.88 + 1.89 + // 1.90 + // several cases: 1.91 + // 1.92 + // (1) we have a shared buffer (mFlags & F_SHARED) 1.93 + // (2) we have an owned buffer (mFlags & F_OWNED) 1.94 + // (3) we have a fixed buffer (mFlags & F_FIXED) 1.95 + // (4) we have a readonly buffer 1.96 + // 1.97 + // requiring that we in some cases preserve the data before creating 1.98 + // a new buffer complicates things just a bit ;-) 1.99 + // 1.100 + 1.101 + size_type storageSize = (capacity + 1) * sizeof(char_type); 1.102 + 1.103 + // case #1 1.104 + if (mFlags & F_SHARED) 1.105 + { 1.106 + nsStringBuffer* hdr = nsStringBuffer::FromData(mData); 1.107 + if (!hdr->IsReadonly()) 1.108 + { 1.109 + nsStringBuffer *newHdr = nsStringBuffer::Realloc(hdr, storageSize); 1.110 + if (!newHdr) 1.111 + return false; // out-of-memory (original header left intact) 1.112 + 1.113 + hdr = newHdr; 1.114 + mData = (char_type*) hdr->Data(); 1.115 + mFlags &= ~F_VOIDED; // mutation clears voided flag 1.116 + return true; 1.117 + } 1.118 + } 1.119 + 1.120 + char_type* newData; 1.121 + uint32_t newDataFlags; 1.122 + 1.123 + // if we have a fixed buffer of sufficient size, then use it. this helps 1.124 + // avoid heap allocations. 1.125 + if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity)) 1.126 + { 1.127 + newData = AsFixedString(this)->mFixedBuf; 1.128 + newDataFlags = F_TERMINATED | F_FIXED; 1.129 + } 1.130 + else 1.131 + { 1.132 + // if we reach here then, we must allocate a new buffer. we cannot 1.133 + // make use of our F_OWNED or F_FIXED buffers because they are not 1.134 + // large enough. 1.135 + 1.136 + nsStringBuffer* newHdr = 1.137 + nsStringBuffer::Alloc(storageSize).take(); 1.138 + if (!newHdr) 1.139 + return false; // we are still in a consistent state 1.140 + 1.141 + newData = (char_type*) newHdr->Data(); 1.142 + newDataFlags = F_TERMINATED | F_SHARED; 1.143 + } 1.144 + 1.145 + // save old data and flags 1.146 + *oldData = mData; 1.147 + *oldFlags = mFlags; 1.148 + 1.149 + mData = newData; 1.150 + SetDataFlags(newDataFlags); 1.151 + 1.152 + // mLength does not change 1.153 + 1.154 + // though we are not necessarily terminated at the moment, now is probably 1.155 + // still the best time to set F_TERMINATED. 1.156 + 1.157 + return true; 1.158 + } 1.159 + 1.160 +void 1.161 +nsTSubstring_CharT::Finalize() 1.162 + { 1.163 + ::ReleaseData(mData, mFlags); 1.164 + // mData, mLength, and mFlags are purposefully left dangling 1.165 + } 1.166 + 1.167 +bool 1.168 +nsTSubstring_CharT::ReplacePrepInternal(index_type cutStart, size_type cutLen, 1.169 + size_type fragLen, size_type newLen) 1.170 + { 1.171 + char_type* oldData; 1.172 + uint32_t oldFlags; 1.173 + if (!MutatePrep(newLen, &oldData, &oldFlags)) 1.174 + return false; // out-of-memory 1.175 + 1.176 + if (oldData) 1.177 + { 1.178 + // determine whether or not we need to copy part of the old string 1.179 + // over to the new string. 1.180 + 1.181 + if (cutStart > 0) 1.182 + { 1.183 + // copy prefix from old string 1.184 + char_traits::copy(mData, oldData, cutStart); 1.185 + } 1.186 + 1.187 + if (cutStart + cutLen < mLength) 1.188 + { 1.189 + // copy suffix from old string to new offset 1.190 + size_type from = cutStart + cutLen; 1.191 + size_type fromLen = mLength - from; 1.192 + uint32_t to = cutStart + fragLen; 1.193 + char_traits::copy(mData + to, oldData + from, fromLen); 1.194 + } 1.195 + 1.196 + ::ReleaseData(oldData, oldFlags); 1.197 + } 1.198 + else 1.199 + { 1.200 + // original data remains intact 1.201 + 1.202 + // determine whether or not we need to move part of the existing string 1.203 + // to make room for the requested hole. 1.204 + if (fragLen != cutLen && cutStart + cutLen < mLength) 1.205 + { 1.206 + uint32_t from = cutStart + cutLen; 1.207 + uint32_t fromLen = mLength - from; 1.208 + uint32_t to = cutStart + fragLen; 1.209 + char_traits::move(mData + to, mData + from, fromLen); 1.210 + } 1.211 + } 1.212 + 1.213 + // add null terminator (mutable mData always has room for the null- 1.214 + // terminator). 1.215 + mData[newLen] = char_type(0); 1.216 + mLength = newLen; 1.217 + 1.218 + return true; 1.219 + } 1.220 + 1.221 +nsTSubstring_CharT::size_type 1.222 +nsTSubstring_CharT::Capacity() const 1.223 + { 1.224 + // return 0 to indicate an immutable or 0-sized buffer 1.225 + 1.226 + size_type capacity; 1.227 + if (mFlags & F_SHARED) 1.228 + { 1.229 + // if the string is readonly, then we pretend that it has no capacity. 1.230 + nsStringBuffer* hdr = nsStringBuffer::FromData(mData); 1.231 + if (hdr->IsReadonly()) 1.232 + capacity = 0; 1.233 + else { 1.234 + capacity = (hdr->StorageSize() / sizeof(char_type)) - 1; 1.235 + } 1.236 + } 1.237 + else if (mFlags & F_FIXED) 1.238 + { 1.239 + capacity = AsFixedString(this)->mFixedCapacity; 1.240 + } 1.241 + else if (mFlags & F_OWNED) 1.242 + { 1.243 + // we don't store the capacity of an adopted buffer because that would 1.244 + // require an additional member field. the best we can do is base the 1.245 + // capacity on our length. remains to be seen if this is the right 1.246 + // trade-off. 1.247 + capacity = mLength; 1.248 + } 1.249 + else 1.250 + { 1.251 + capacity = 0; 1.252 + } 1.253 + 1.254 + return capacity; 1.255 + } 1.256 + 1.257 +bool 1.258 +nsTSubstring_CharT::EnsureMutable( size_type newLen ) 1.259 + { 1.260 + if (newLen == size_type(-1) || newLen == mLength) 1.261 + { 1.262 + if (mFlags & (F_FIXED | F_OWNED)) 1.263 + return true; 1.264 + if ((mFlags & F_SHARED) && !nsStringBuffer::FromData(mData)->IsReadonly()) 1.265 + return true; 1.266 + 1.267 + newLen = mLength; 1.268 + } 1.269 + return SetLength(newLen, fallible_t()); 1.270 + } 1.271 + 1.272 +// --------------------------------------------------------------------------- 1.273 + 1.274 + // This version of Assign is optimized for single-character assignment. 1.275 +void 1.276 +nsTSubstring_CharT::Assign( char_type c ) 1.277 + { 1.278 + if (!ReplacePrep(0, mLength, 1)) 1.279 + NS_ABORT_OOM(mLength); 1.280 + 1.281 + *mData = c; 1.282 + } 1.283 + 1.284 +bool 1.285 +nsTSubstring_CharT::Assign( char_type c, const fallible_t& ) 1.286 + { 1.287 + if (!ReplacePrep(0, mLength, 1)) 1.288 + return false; 1.289 + 1.290 + *mData = c; 1.291 + return true; 1.292 + } 1.293 + 1.294 +void 1.295 +nsTSubstring_CharT::Assign( const char_type* data ) 1.296 + { 1.297 + if (!Assign(data, size_type(-1), fallible_t())) 1.298 + NS_ABORT_OOM(char_traits::length(data)); 1.299 + } 1.300 + 1.301 +void 1.302 +nsTSubstring_CharT::Assign( const char_type* data, size_type length ) 1.303 + { 1.304 + if (!Assign(data, length, fallible_t())) 1.305 + NS_ABORT_OOM(length); 1.306 + } 1.307 + 1.308 +bool 1.309 +nsTSubstring_CharT::Assign( const char_type* data, size_type length, const fallible_t& ) 1.310 + { 1.311 + if (!data || length == 0) 1.312 + { 1.313 + Truncate(); 1.314 + return true; 1.315 + } 1.316 + 1.317 + if (length == size_type(-1)) 1.318 + length = char_traits::length(data); 1.319 + 1.320 + if (IsDependentOn(data, data + length)) 1.321 + { 1.322 + return Assign(string_type(data, length), fallible_t()); 1.323 + } 1.324 + 1.325 + if (!ReplacePrep(0, mLength, length)) 1.326 + return false; 1.327 + 1.328 + char_traits::copy(mData, data, length); 1.329 + return true; 1.330 + } 1.331 + 1.332 +void 1.333 +nsTSubstring_CharT::AssignASCII( const char* data, size_type length ) 1.334 + { 1.335 + if (!AssignASCII(data, length, fallible_t())) 1.336 + NS_ABORT_OOM(length); 1.337 + } 1.338 + 1.339 +bool 1.340 +nsTSubstring_CharT::AssignASCII( const char* data, size_type length, const fallible_t& ) 1.341 + { 1.342 + // A Unicode string can't depend on an ASCII string buffer, 1.343 + // so this dependence check only applies to CStrings. 1.344 +#ifdef CharT_is_char 1.345 + if (IsDependentOn(data, data + length)) 1.346 + { 1.347 + return Assign(string_type(data, length), fallible_t()); 1.348 + } 1.349 +#endif 1.350 + 1.351 + if (!ReplacePrep(0, mLength, length)) 1.352 + return false; 1.353 + 1.354 + char_traits::copyASCII(mData, data, length); 1.355 + return true; 1.356 + } 1.357 + 1.358 +void 1.359 +nsTSubstring_CharT::AssignLiteral( const char_type* data, size_type length ) 1.360 + { 1.361 + ::ReleaseData(mData, mFlags); 1.362 + mData = const_cast<char_type*>(data); 1.363 + mLength = length; 1.364 + SetDataFlags(F_TERMINATED | F_LITERAL); 1.365 + } 1.366 + 1.367 +void 1.368 +nsTSubstring_CharT::Assign( const self_type& str ) 1.369 +{ 1.370 + if (!Assign(str, fallible_t())) 1.371 + NS_ABORT_OOM(str.Length()); 1.372 +} 1.373 + 1.374 +bool 1.375 +nsTSubstring_CharT::Assign( const self_type& str, const fallible_t& ) 1.376 + { 1.377 + // |str| could be sharable. we need to check its flags to know how to 1.378 + // deal with it. 1.379 + 1.380 + if (&str == this) 1.381 + return true; 1.382 + 1.383 + if (!str.mLength) 1.384 + { 1.385 + Truncate(); 1.386 + mFlags |= str.mFlags & F_VOIDED; 1.387 + return true; 1.388 + } 1.389 + 1.390 + if (str.mFlags & F_SHARED) 1.391 + { 1.392 + // nice! we can avoid a string copy :-) 1.393 + 1.394 + // |str| should be null-terminated 1.395 + NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated"); 1.396 + 1.397 + ::ReleaseData(mData, mFlags); 1.398 + 1.399 + mData = str.mData; 1.400 + mLength = str.mLength; 1.401 + SetDataFlags(F_TERMINATED | F_SHARED); 1.402 + 1.403 + // get an owning reference to the mData 1.404 + nsStringBuffer::FromData(mData)->AddRef(); 1.405 + return true; 1.406 + } 1.407 + else if (str.mFlags & F_LITERAL) 1.408 + { 1.409 + NS_ABORT_IF_FALSE(str.mFlags & F_TERMINATED, "Unterminated literal"); 1.410 + 1.411 + AssignLiteral(str.mData, str.mLength); 1.412 + return true; 1.413 + } 1.414 + 1.415 + // else, treat this like an ordinary assignment. 1.416 + return Assign(str.Data(), str.Length(), fallible_t()); 1.417 + } 1.418 + 1.419 +void 1.420 +nsTSubstring_CharT::Assign( const substring_tuple_type& tuple ) 1.421 + { 1.422 + if (!Assign(tuple, fallible_t())) 1.423 + NS_ABORT_OOM(tuple.Length()); 1.424 + } 1.425 + 1.426 +bool 1.427 +nsTSubstring_CharT::Assign( const substring_tuple_type& tuple, const fallible_t& ) 1.428 + { 1.429 + if (tuple.IsDependentOn(mData, mData + mLength)) 1.430 + { 1.431 + // take advantage of sharing here... 1.432 + return Assign(string_type(tuple), fallible_t()); 1.433 + } 1.434 + 1.435 + size_type length = tuple.Length(); 1.436 + 1.437 + // don't use ReplacePrep here because it changes the length 1.438 + char_type* oldData; 1.439 + uint32_t oldFlags; 1.440 + if (!MutatePrep(length, &oldData, &oldFlags)) 1.441 + return false; 1.442 + 1.443 + if (oldData) 1.444 + ::ReleaseData(oldData, oldFlags); 1.445 + 1.446 + tuple.WriteTo(mData, length); 1.447 + mData[length] = 0; 1.448 + mLength = length; 1.449 + return true; 1.450 + } 1.451 + 1.452 +void 1.453 +nsTSubstring_CharT::Adopt( char_type* data, size_type length ) 1.454 + { 1.455 + if (data) 1.456 + { 1.457 + ::ReleaseData(mData, mFlags); 1.458 + 1.459 + if (length == size_type(-1)) 1.460 + length = char_traits::length(data); 1.461 + 1.462 + mData = data; 1.463 + mLength = length; 1.464 + SetDataFlags(F_TERMINATED | F_OWNED); 1.465 + 1.466 + STRING_STAT_INCREMENT(Adopt); 1.467 +#ifdef NS_BUILD_REFCNT_LOGGING 1.468 + // Treat this as construction of a "StringAdopt" object for leak 1.469 + // tracking purposes. 1.470 + NS_LogCtor(mData, "StringAdopt", 1); 1.471 +#endif // NS_BUILD_REFCNT_LOGGING 1.472 + } 1.473 + else 1.474 + { 1.475 + SetIsVoid(true); 1.476 + } 1.477 + } 1.478 + 1.479 + 1.480 + // This version of Replace is optimized for single-character replacement. 1.481 +void 1.482 +nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c ) 1.483 + { 1.484 + cutStart = XPCOM_MIN(cutStart, Length()); 1.485 + 1.486 + if (ReplacePrep(cutStart, cutLength, 1)) 1.487 + mData[cutStart] = c; 1.488 + } 1.489 + 1.490 +bool 1.491 +nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c, const mozilla::fallible_t& ) 1.492 + { 1.493 + cutStart = XPCOM_MIN(cutStart, Length()); 1.494 + 1.495 + if (!ReplacePrep(cutStart, cutLength, 1)) 1.496 + return false; 1.497 + 1.498 + mData[cutStart] = c; 1.499 + 1.500 + return true; 1.501 + } 1.502 + 1.503 +void 1.504 +nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length ) 1.505 + { 1.506 + if (!Replace(cutStart, cutLength, data, length, mozilla::fallible_t())) 1.507 + { 1.508 + NS_ABORT_OOM(Length() - cutLength + 1); 1.509 + } 1.510 + } 1.511 + 1.512 +bool 1.513 +nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length, const mozilla::fallible_t& ) 1.514 + { 1.515 + // unfortunately, some callers pass null :-( 1.516 + if (!data) 1.517 + { 1.518 + length = 0; 1.519 + } 1.520 + else 1.521 + { 1.522 + if (length == size_type(-1)) 1.523 + length = char_traits::length(data); 1.524 + 1.525 + if (IsDependentOn(data, data + length)) 1.526 + { 1.527 + nsTAutoString_CharT temp(data, length); 1.528 + return Replace(cutStart, cutLength, temp, mozilla::fallible_t()); 1.529 + } 1.530 + } 1.531 + 1.532 + cutStart = XPCOM_MIN(cutStart, Length()); 1.533 + 1.534 + bool ok = ReplacePrep(cutStart, cutLength, length); 1.535 + if (!ok) 1.536 + return false; 1.537 + 1.538 + if (length > 0) 1.539 + char_traits::copy(mData + cutStart, data, length); 1.540 + 1.541 + return true; 1.542 + } 1.543 + 1.544 +void 1.545 +nsTSubstring_CharT::ReplaceASCII( index_type cutStart, size_type cutLength, const char* data, size_type length ) 1.546 + { 1.547 + if (length == size_type(-1)) 1.548 + length = strlen(data); 1.549 + 1.550 + // A Unicode string can't depend on an ASCII string buffer, 1.551 + // so this dependence check only applies to CStrings. 1.552 +#ifdef CharT_is_char 1.553 + if (IsDependentOn(data, data + length)) 1.554 + { 1.555 + nsTAutoString_CharT temp(data, length); 1.556 + Replace(cutStart, cutLength, temp); 1.557 + return; 1.558 + } 1.559 +#endif 1.560 + 1.561 + cutStart = XPCOM_MIN(cutStart, Length()); 1.562 + 1.563 + if (ReplacePrep(cutStart, cutLength, length) && length > 0) 1.564 + char_traits::copyASCII(mData + cutStart, data, length); 1.565 + } 1.566 + 1.567 +void 1.568 +nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& tuple ) 1.569 + { 1.570 + if (tuple.IsDependentOn(mData, mData + mLength)) 1.571 + { 1.572 + nsTAutoString_CharT temp(tuple); 1.573 + Replace(cutStart, cutLength, temp); 1.574 + return; 1.575 + } 1.576 + 1.577 + size_type length = tuple.Length(); 1.578 + 1.579 + cutStart = XPCOM_MIN(cutStart, Length()); 1.580 + 1.581 + if (ReplacePrep(cutStart, cutLength, length) && length > 0) 1.582 + tuple.WriteTo(mData + cutStart, length); 1.583 + } 1.584 + 1.585 +void 1.586 +nsTSubstring_CharT::ReplaceLiteral( index_type cutStart, size_type cutLength, const char_type* data, size_type length ) 1.587 + { 1.588 + cutStart = XPCOM_MIN(cutStart, Length()); 1.589 + 1.590 + if (!cutStart && cutLength == Length()) 1.591 + AssignLiteral(data, length); 1.592 + else if (ReplacePrep(cutStart, cutLength, length) && length > 0) 1.593 + char_traits::copy(mData + cutStart, data, length); 1.594 + } 1.595 + 1.596 +void 1.597 +nsTSubstring_CharT::SetCapacity( size_type capacity ) 1.598 + { 1.599 + if (!SetCapacity(capacity, fallible_t())) 1.600 + NS_ABORT_OOM(capacity); 1.601 + } 1.602 + 1.603 +bool 1.604 +nsTSubstring_CharT::SetCapacity( size_type capacity, const fallible_t& ) 1.605 + { 1.606 + // capacity does not include room for the terminating null char 1.607 + 1.608 + // if our capacity is reduced to zero, then free our buffer. 1.609 + if (capacity == 0) 1.610 + { 1.611 + ::ReleaseData(mData, mFlags); 1.612 + mData = char_traits::sEmptyBuffer; 1.613 + mLength = 0; 1.614 + SetDataFlags(F_TERMINATED); 1.615 + return true; 1.616 + } 1.617 + 1.618 + char_type* oldData; 1.619 + uint32_t oldFlags; 1.620 + if (!MutatePrep(capacity, &oldData, &oldFlags)) 1.621 + return false; // out-of-memory 1.622 + 1.623 + // compute new string length 1.624 + size_type newLen = XPCOM_MIN(mLength, capacity); 1.625 + 1.626 + if (oldData) 1.627 + { 1.628 + // preserve old data 1.629 + if (mLength > 0) 1.630 + char_traits::copy(mData, oldData, newLen); 1.631 + 1.632 + ::ReleaseData(oldData, oldFlags); 1.633 + } 1.634 + 1.635 + // adjust mLength if our buffer shrunk down in size 1.636 + if (newLen < mLength) 1.637 + mLength = newLen; 1.638 + 1.639 + // always null-terminate here, even if the buffer got longer. this is 1.640 + // for backwards compat with the old string implementation. 1.641 + mData[capacity] = char_type(0); 1.642 + 1.643 + return true; 1.644 + } 1.645 + 1.646 +void 1.647 +nsTSubstring_CharT::SetLength( size_type length ) 1.648 + { 1.649 + SetCapacity(length); 1.650 + mLength = length; 1.651 + } 1.652 + 1.653 +bool 1.654 +nsTSubstring_CharT::SetLength( size_type length, const fallible_t& ) 1.655 + { 1.656 + if (!SetCapacity(length, fallible_t())) 1.657 + return false; 1.658 + 1.659 + mLength = length; 1.660 + return true; 1.661 + } 1.662 + 1.663 +void 1.664 +nsTSubstring_CharT::SetIsVoid( bool val ) 1.665 + { 1.666 + if (val) 1.667 + { 1.668 + Truncate(); 1.669 + mFlags |= F_VOIDED; 1.670 + } 1.671 + else 1.672 + { 1.673 + mFlags &= ~F_VOIDED; 1.674 + } 1.675 + } 1.676 + 1.677 +bool 1.678 +nsTSubstring_CharT::Equals( const self_type& str ) const 1.679 + { 1.680 + return mLength == str.mLength && char_traits::compare(mData, str.mData, mLength) == 0; 1.681 + } 1.682 + 1.683 +bool 1.684 +nsTSubstring_CharT::Equals( const self_type& str, const comparator_type& comp ) const 1.685 + { 1.686 + return mLength == str.mLength && comp(mData, str.mData, mLength, str.mLength) == 0; 1.687 + } 1.688 + 1.689 +bool 1.690 +nsTSubstring_CharT::Equals( const char_type* data ) const 1.691 + { 1.692 + // unfortunately, some callers pass null :-( 1.693 + if (!data) 1.694 + { 1.695 + NS_NOTREACHED("null data pointer"); 1.696 + return mLength == 0; 1.697 + } 1.698 + 1.699 + // XXX avoid length calculation? 1.700 + size_type length = char_traits::length(data); 1.701 + return mLength == length && char_traits::compare(mData, data, mLength) == 0; 1.702 + } 1.703 + 1.704 +bool 1.705 +nsTSubstring_CharT::Equals( const char_type* data, const comparator_type& comp ) const 1.706 + { 1.707 + // unfortunately, some callers pass null :-( 1.708 + if (!data) 1.709 + { 1.710 + NS_NOTREACHED("null data pointer"); 1.711 + return mLength == 0; 1.712 + } 1.713 + 1.714 + // XXX avoid length calculation? 1.715 + size_type length = char_traits::length(data); 1.716 + return mLength == length && comp(mData, data, mLength, length) == 0; 1.717 + } 1.718 + 1.719 +bool 1.720 +nsTSubstring_CharT::EqualsASCII( const char* data, size_type len ) const 1.721 + { 1.722 + return mLength == len && char_traits::compareASCII(mData, data, len) == 0; 1.723 + } 1.724 + 1.725 +bool 1.726 +nsTSubstring_CharT::EqualsASCII( const char* data ) const 1.727 + { 1.728 + return char_traits::compareASCIINullTerminated(mData, mLength, data) == 0; 1.729 + } 1.730 + 1.731 +bool 1.732 +nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data, size_type len ) const 1.733 + { 1.734 + return mLength == len && char_traits::compareLowerCaseToASCII(mData, data, len) == 0; 1.735 + } 1.736 + 1.737 +bool 1.738 +nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data ) const 1.739 + { 1.740 + return char_traits::compareLowerCaseToASCIINullTerminated(mData, mLength, data) == 0; 1.741 + } 1.742 + 1.743 +nsTSubstring_CharT::size_type 1.744 +nsTSubstring_CharT::CountChar( char_type c ) const 1.745 + { 1.746 + const char_type *start = mData; 1.747 + const char_type *end = mData + mLength; 1.748 + 1.749 + return NS_COUNT(start, end, c); 1.750 + } 1.751 + 1.752 +int32_t 1.753 +nsTSubstring_CharT::FindChar( char_type c, index_type offset ) const 1.754 + { 1.755 + if (offset < mLength) 1.756 + { 1.757 + const char_type* result = char_traits::find(mData + offset, mLength - offset, c); 1.758 + if (result) 1.759 + return result - mData; 1.760 + } 1.761 + return -1; 1.762 + } 1.763 + 1.764 +void 1.765 +nsTSubstring_CharT::StripChar( char_type aChar, int32_t aOffset ) 1.766 + { 1.767 + if (mLength == 0 || aOffset >= int32_t(mLength)) 1.768 + return; 1.769 + 1.770 + if (!EnsureMutable()) // XXX do this lazily? 1.771 + NS_ABORT_OOM(mLength); 1.772 + 1.773 + // XXX(darin): this code should defer writing until necessary. 1.774 + 1.775 + char_type* to = mData + aOffset; 1.776 + char_type* from = mData + aOffset; 1.777 + char_type* end = mData + mLength; 1.778 + 1.779 + while (from < end) 1.780 + { 1.781 + char_type theChar = *from++; 1.782 + if (aChar != theChar) 1.783 + *to++ = theChar; 1.784 + } 1.785 + *to = char_type(0); // add the null 1.786 + mLength = to - mData; 1.787 + } 1.788 + 1.789 +void 1.790 +nsTSubstring_CharT::StripChars( const char_type* aChars, uint32_t aOffset ) 1.791 + { 1.792 + if (aOffset >= uint32_t(mLength)) 1.793 + return; 1.794 + 1.795 + if (!EnsureMutable()) // XXX do this lazily? 1.796 + NS_ABORT_OOM(mLength); 1.797 + 1.798 + // XXX(darin): this code should defer writing until necessary. 1.799 + 1.800 + char_type* to = mData + aOffset; 1.801 + char_type* from = mData + aOffset; 1.802 + char_type* end = mData + mLength; 1.803 + 1.804 + while (from < end) 1.805 + { 1.806 + char_type theChar = *from++; 1.807 + const char_type* test = aChars; 1.808 + 1.809 + for (; *test && *test != theChar; ++test); 1.810 + 1.811 + if (!*test) { 1.812 + // Not stripped, copy this char. 1.813 + *to++ = theChar; 1.814 + } 1.815 + } 1.816 + *to = char_type(0); // add the null 1.817 + mLength = to - mData; 1.818 + } 1.819 + 1.820 +int 1.821 +nsTSubstring_CharT::AppendFunc(void* arg, const char* s, uint32_t len) 1.822 + { 1.823 + self_type* self = static_cast<self_type*>(arg); 1.824 + 1.825 + // NSPR sends us the final null terminator even though we don't want it 1.826 + if (len && s[len - 1] == '\0') { 1.827 + --len; 1.828 + } 1.829 + 1.830 + self->AppendASCII(s, len); 1.831 + 1.832 + return len; 1.833 + } 1.834 + 1.835 +void nsTSubstring_CharT::AppendPrintf( const char* format, ...) 1.836 + { 1.837 + va_list ap; 1.838 + va_start(ap, format); 1.839 + uint32_t r = PR_vsxprintf(AppendFunc, this, format, ap); 1.840 + if (r == (uint32_t) -1) 1.841 + NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf"); 1.842 + va_end(ap); 1.843 + } 1.844 + 1.845 +void nsTSubstring_CharT::AppendPrintf( const char* format, va_list ap ) 1.846 + { 1.847 + uint32_t r = PR_vsxprintf(AppendFunc, this, format, ap); 1.848 + if (r == (uint32_t) -1) 1.849 + NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf"); 1.850 + } 1.851 + 1.852 +/* hack to make sure we define FormatWithoutTrailingZeros only once */ 1.853 +#ifdef CharT_is_PRUnichar 1.854 +// Returns the length of the formatted aDouble in buf. 1.855 +static int 1.856 +FormatWithoutTrailingZeros(char (& buf)[40], double aDouble, 1.857 + int precision) 1.858 +{ 1.859 + static const DoubleToStringConverter converter(DoubleToStringConverter::UNIQUE_ZERO | 1.860 + DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN, 1.861 + "Infinity", 1.862 + "NaN", 1.863 + 'e', 1.864 + -6, 21, 1.865 + 6, 1); 1.866 + double_conversion::StringBuilder builder(buf, sizeof(buf)); 1.867 + bool exponential_notation = false; 1.868 + converter.ToPrecision(aDouble, precision, &exponential_notation, &builder); 1.869 + int length = builder.position(); 1.870 + char* formattedDouble = builder.Finalize(); 1.871 + 1.872 + // If we have a shorter string than precision, it means we have a special 1.873 + // value (NaN or Infinity). All other numbers will be formatted with at 1.874 + // least precision digits. 1.875 + if (length <= precision) { 1.876 + return length; 1.877 + } 1.878 + 1.879 + char* end = formattedDouble + length; 1.880 + char* decimalPoint = strchr(buf, '.'); 1.881 + // No trailing zeros to remove. 1.882 + if (decimalPoint == nullptr) { 1.883 + return length; 1.884 + } 1.885 + 1.886 + if (MOZ_UNLIKELY(exponential_notation)) { 1.887 + // We need to check for cases like 1.00000e-10 (yes, this is 1.888 + // disgusting). 1.889 + char* exponent = end - 1; 1.890 + for ( ; ; --exponent) { 1.891 + if (*exponent == 'e') { 1.892 + break; 1.893 + } 1.894 + } 1.895 + char* zerosBeforeExponent = exponent - 1; 1.896 + for ( ; zerosBeforeExponent != decimalPoint; --zerosBeforeExponent) { 1.897 + if (*zerosBeforeExponent != '0') { 1.898 + break; 1.899 + } 1.900 + } 1.901 + if (zerosBeforeExponent == decimalPoint) { 1.902 + --zerosBeforeExponent; 1.903 + } 1.904 + // Slide the exponent to the left over the trailing zeros. Don't 1.905 + // worry about copying the trailing NUL character. 1.906 + size_t exponentSize = end - exponent; 1.907 + memmove(zerosBeforeExponent + 1, exponent, exponentSize); 1.908 + length -= exponent - (zerosBeforeExponent + 1); 1.909 + } else { 1.910 + char* trailingZeros = end - 1; 1.911 + for ( ; trailingZeros != decimalPoint; --trailingZeros) { 1.912 + if (*trailingZeros != '0') { 1.913 + break; 1.914 + } 1.915 + } 1.916 + if (trailingZeros == decimalPoint) { 1.917 + --trailingZeros; 1.918 + } 1.919 + length -= end - (trailingZeros + 1); 1.920 + } 1.921 + 1.922 + return length; 1.923 +} 1.924 +#endif /* CharT_is_PRUnichar */ 1.925 + 1.926 +void 1.927 +nsTSubstring_CharT::AppendFloat( float aFloat ) 1.928 +{ 1.929 + char buf[40]; 1.930 + int length = FormatWithoutTrailingZeros(buf, aFloat, 6); 1.931 + AppendASCII(buf, length); 1.932 +} 1.933 + 1.934 +void 1.935 +nsTSubstring_CharT::AppendFloat( double aFloat ) 1.936 +{ 1.937 + char buf[40]; 1.938 + int length = FormatWithoutTrailingZeros(buf, aFloat, 15); 1.939 + AppendASCII(buf, length); 1.940 +} 1.941 + 1.942 +size_t 1.943 +nsTSubstring_CharT::SizeOfExcludingThisMustBeUnshared( 1.944 + mozilla::MallocSizeOf mallocSizeOf) const 1.945 +{ 1.946 + if (mFlags & F_SHARED) { 1.947 + return nsStringBuffer::FromData(mData)-> 1.948 + SizeOfIncludingThisMustBeUnshared(mallocSizeOf); 1.949 + } 1.950 + if (mFlags & F_OWNED) { 1.951 + return mallocSizeOf(mData); 1.952 + } 1.953 + 1.954 + // If we reach here, exactly one of the following must be true: 1.955 + // - F_VOIDED is set, and mData points to sEmptyBuffer; 1.956 + // - F_FIXED is set, and mData points to a buffer within a string 1.957 + // object (e.g. nsAutoString); 1.958 + // - None of F_SHARED, F_OWNED, F_FIXED is set, and mData points to a buffer 1.959 + // owned by something else. 1.960 + // 1.961 + // In all three cases, we don't measure it. 1.962 + return 0; 1.963 +} 1.964 + 1.965 +size_t 1.966 +nsTSubstring_CharT::SizeOfExcludingThisIfUnshared( 1.967 + mozilla::MallocSizeOf mallocSizeOf) const 1.968 +{ 1.969 + // This is identical to SizeOfExcludingThisMustBeUnshared except for the 1.970 + // F_SHARED case. 1.971 + if (mFlags & F_SHARED) { 1.972 + return nsStringBuffer::FromData(mData)-> 1.973 + SizeOfIncludingThisIfUnshared(mallocSizeOf); 1.974 + } 1.975 + if (mFlags & F_OWNED) { 1.976 + return mallocSizeOf(mData); 1.977 + } 1.978 + return 0; 1.979 +} 1.980 + 1.981 +size_t 1.982 +nsTSubstring_CharT::SizeOfExcludingThisEvenIfShared( 1.983 + mozilla::MallocSizeOf mallocSizeOf) const 1.984 +{ 1.985 + // This is identical to SizeOfExcludingThisMustBeUnshared except for the 1.986 + // F_SHARED case. 1.987 + if (mFlags & F_SHARED) { 1.988 + return nsStringBuffer::FromData(mData)-> 1.989 + SizeOfIncludingThisEvenIfShared(mallocSizeOf); 1.990 + } 1.991 + if (mFlags & F_OWNED) { 1.992 + return mallocSizeOf(mData); 1.993 + } 1.994 + return 0; 1.995 +} 1.996 + 1.997 +size_t 1.998 +nsTSubstring_CharT::SizeOfIncludingThisMustBeUnshared( 1.999 + mozilla::MallocSizeOf mallocSizeOf) const 1.1000 +{ 1.1001 + return mallocSizeOf(this) + SizeOfExcludingThisMustBeUnshared(mallocSizeOf); 1.1002 +} 1.1003 + 1.1004 +size_t 1.1005 +nsTSubstring_CharT::SizeOfIncludingThisIfUnshared( 1.1006 + mozilla::MallocSizeOf mallocSizeOf) const 1.1007 +{ 1.1008 + return mallocSizeOf(this) + SizeOfExcludingThisIfUnshared(mallocSizeOf); 1.1009 +} 1.1010 + 1.1011 +size_t 1.1012 +nsTSubstring_CharT::SizeOfIncludingThisEvenIfShared( 1.1013 + mozilla::MallocSizeOf mallocSizeOf) const 1.1014 +{ 1.1015 + return mallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(mallocSizeOf); 1.1016 +} 1.1017 +