nsTAdoptingString_CharT& nsTAdoptingString_CharT::operator=(const self_type& str) { // This'll violate the constness of this argument, that's just // the nature of this class... self_type* mutable_str = const_cast<self_type*>(&str); if (str.mFlags & F_OWNED) { // We want to do what Adopt() does, but without actually incrementing // the Adopt count. Note that we can be a little more straightforward // about this than Adopt() is, because we know that str.mData is // non-null. Should we be able to assert that str is not void here? NS_ASSERTION(str.mData, "String with null mData?"); Finalize(); mData = str.mData; mLength = str.mLength; SetDataFlags(F_TERMINATED | F_OWNED); // Make str forget the buffer we just took ownership of. new(mutable_str) self_type(); } else { Assign(str); mutable_str->Truncate(); } return *this; }
void nsTSubstring_CharT::Adopt( char_type* data, size_type length ) { if (data) { ::ReleaseData(mData, mFlags); if (length == size_type(-1)) length = char_traits::length(data); mData = data; mLength = length; SetDataFlags(F_TERMINATED | F_OWNED); STRING_STAT_INCREMENT(Adopt); #ifdef NS_BUILD_REFCNT_LOGGING // Treat this as construction of a "StringAdopt" object for leak // tracking purposes. NS_LogCtor(mData, "StringAdopt", 1); #endif // NS_BUILD_REFCNT_LOGGING } else { SetIsVoid(true); } }
void nsTDependentString_CharT::Rebind( const char_type* data, size_type length ) { // If we currently own a buffer, release it. Finalize(); mData = const_cast<char_type*>(data); mLength = length; SetDataFlags(F_TERMINATED); AssertValid(); }
void nsTDependentSubstring_CharT::Rebind( const char_type* data, size_type length ) { NS_ASSERTION(data, "nsTDependentSubstring must wrap a non-NULL buffer"); // If we currently own a buffer, release it. Finalize(); mData = const_cast<char_type*>(static_cast<const char_type*>(data)); mLength = length; SetDataFlags(F_NONE); }
void nsTDependentSubstring_CharT::Rebind( const char_type* start, const char_type* end ) { NS_ASSERTION(start && end, "nsTDependentSubstring must wrap a non-NULL buffer"); // If we currently own a buffer, release it. Finalize(); mData = NS_CONST_CAST(char_type*, start); mLength = end - start; SetDataFlags(F_NONE); }
void nsTDependentString_CharT::Rebind( const string_type& str, PRUint32 startPos ) { // If we currently own a buffer, release it. Finalize(); size_type strLength = str.Length(); if (startPos > strLength) startPos = strLength; mData = const_cast<char_type*>(str.Data()) + startPos; mLength = strLength - startPos; SetDataFlags(F_TERMINATED); }
void nsTDependentSubstring_CharT::Rebind( const substring_type& str, uint32_t startPos, uint32_t length ) { // If we currently own a buffer, release it. Finalize(); size_type strLength = str.Length(); if (startPos > strLength) startPos = strLength; mData = const_cast<char_type*>(static_cast<const char_type*>(str.Data())) + startPos; mLength = XPCOM_MIN(length, strLength - startPos); SetDataFlags(F_NONE); }
void nsTDependentSubstring_CharT::Rebind( const substring_type& str, PRUint32 startPos, PRUint32 length ) { // If we currently own a buffer, release it. Finalize(); size_type strLength = str.Length(); if (startPos > strLength) startPos = strLength; mData = NS_CONST_CAST(char_type*, str.Data()) + startPos; mLength = NS_MIN(length, strLength - startPos); SetDataFlags(F_NONE); }
void nsTDependentSubstring_CharT::Rebind( const abstract_string_type& readable, PRUint32 startPos, PRUint32 length ) { // If we currently own a buffer, release it. Finalize(); size_type strLength = readable.GetReadableBuffer((const char_type**) &mData); if (startPos > strLength) startPos = strLength; mData += startPos; mLength = NS_MIN(length, strLength - startPos); SetDataFlags(F_NONE); }
PRBool nsTSubstring_CharT::SetCapacity( size_type capacity ) { // capacity does not include room for the terminating null char // if our capacity is reduced to zero, then free our buffer. if (capacity == 0) { ::ReleaseData(mData, mFlags); mData = char_traits::sEmptyBuffer; mLength = 0; SetDataFlags(F_TERMINATED); } else { char_type* oldData; PRUint32 oldFlags; if (!MutatePrep(capacity, &oldData, &oldFlags)) return PR_FALSE; // out-of-memory // compute new string length size_type newLen = NS_MIN(mLength, capacity); if (oldData) { // preserve old data if (mLength > 0) char_traits::copy(mData, oldData, newLen); ::ReleaseData(oldData, oldFlags); } // adjust mLength if our buffer shrunk down in size if (newLen < mLength) mLength = newLen; // always null-terminate here, even if the buffer got longer. this is // for backwards compat with the old string implementation. mData[capacity] = char_type(0); } return PR_TRUE; }
void nsTDependentString_CharT::Rebind(const string_type& str, uint32_t startPos) { NS_ABORT_IF_FALSE(str.Flags() & F_TERMINATED, "Unterminated flat string"); // If we currently own a buffer, release it. Finalize(); size_type strLength = str.Length(); if (startPos > strLength) { startPos = strLength; } mData = const_cast<char_type*>(static_cast<const char_type*>(str.Data())) + startPos; mLength = strLength - startPos; SetDataFlags(str.Flags() & (F_TERMINATED | F_LITERAL)); }
bool nsTSubstring_CharT::Assign( const self_type& str, const fallible_t& ) { // |str| could be sharable. we need to check its flags to know how to // deal with it. if (&str == this) return true; if (!str.mLength) { Truncate(); mFlags |= str.mFlags & F_VOIDED; return true; } if (str.mFlags & F_SHARED) { // nice! we can avoid a string copy :-) // |str| should be null-terminated NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated"); ::ReleaseData(mData, mFlags); mData = str.mData; mLength = str.mLength; SetDataFlags(F_TERMINATED | F_SHARED); // get an owning reference to the mData nsStringBuffer::FromData(mData)->AddRef(); return true; } // else, treat this like an ordinary assignment. return Assign(str.Data(), str.Length(), fallible_t()); }
/** * this function is called to prepare mData for writing. the given capacity * indicates the required minimum storage size for mData, in sizeof(char_type) * increments. this function returns true if the operation succeeds. it also * returns the old data and old flags members if mData is newly allocated. * the old data must be released by the caller. */ bool nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, uint32_t* oldFlags ) { // initialize to no old data *oldData = nullptr; *oldFlags = 0; size_type curCapacity = Capacity(); // If |capacity > kMaxCapacity|, then our doubling algorithm may not be // able to allocate it. Just bail out in cases like that. We don't want // to be allocating 2GB+ strings anyway. PR_STATIC_ASSERT((sizeof(nsStringBuffer) & 0x1) == 0); const size_type kMaxCapacity = (size_type(-1)/2 - sizeof(nsStringBuffer)) / sizeof(char_type) - 2; if (capacity > kMaxCapacity) { // Also assert for |capacity| equal to |size_type(-1)|, since we used to // use that value to flag immutability. NS_ASSERTION(capacity != size_type(-1), "Bogus capacity"); return false; } // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we // need to allocate a new buffer. We cannot use the existing buffer even // though it might be large enough. if (curCapacity != 0) { if (capacity <= curCapacity) { mFlags &= ~F_VOIDED; // mutation clears voided flag return true; } // Use doubling algorithm when forced to increase available capacity. size_type temp = curCapacity; while (temp < capacity) temp <<= 1; NS_ASSERTION(XPCOM_MIN(temp, kMaxCapacity) >= capacity, "should have hit the early return at the top"); capacity = XPCOM_MIN(temp, kMaxCapacity); } // // several cases: // // (1) we have a shared buffer (mFlags & F_SHARED) // (2) we have an owned buffer (mFlags & F_OWNED) // (3) we have a fixed buffer (mFlags & F_FIXED) // (4) we have a readonly buffer // // requiring that we in some cases preserve the data before creating // a new buffer complicates things just a bit ;-) // size_type storageSize = (capacity + 1) * sizeof(char_type); // case #1 if (mFlags & F_SHARED) { nsStringBuffer* hdr = nsStringBuffer::FromData(mData); if (!hdr->IsReadonly()) { nsStringBuffer *newHdr = nsStringBuffer::Realloc(hdr, storageSize); if (!newHdr) return false; // out-of-memory (original header left intact) hdr = newHdr; mData = (char_type*) hdr->Data(); mFlags &= ~F_VOIDED; // mutation clears voided flag return true; } } char_type* newData; uint32_t newDataFlags; // if we have a fixed buffer of sufficient size, then use it. this helps // avoid heap allocations. if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity)) { newData = AsFixedString(this)->mFixedBuf; newDataFlags = F_TERMINATED | F_FIXED; } else { // if we reach here then, we must allocate a new buffer. we cannot // make use of our F_OWNED or F_FIXED buffers because they are not // large enough. nsStringBuffer* newHdr = nsStringBuffer::Alloc(storageSize); if (!newHdr) return false; // we are still in a consistent state newData = (char_type*) newHdr->Data(); newDataFlags = F_TERMINATED | F_SHARED; } // save old data and flags *oldData = mData; *oldFlags = mFlags; mData = newData; SetDataFlags(newDataFlags); // mLength does not change // though we are not necessarily terminated at the moment, now is probably // still the best time to set F_TERMINATED. return true; }
/** * this function is called to prepare mData for writing. the given capacity * indicates the required minimum storage size for mData, in sizeof(char_type) * increments. this function returns true if the operation succeeds. it also * returns the old data and old flags members if mData is newly allocated. * the old data must be released by the caller. */ PRBool nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, PRUint32* oldFlags ) { // initialize to no old data *oldData = nsnull; *oldFlags = 0; size_type curCapacity = Capacity(); // |curCapacity == size_type(-1)| means that the buffer is immutable, so we // need to allocate a new buffer. we cannot use the existing buffer even // though it might be large enough. if (curCapacity != size_type(-1)) { if (capacity <= curCapacity) return PR_TRUE; if (curCapacity > 0) { // use doubling algorithm when forced to increase available capacity, // but always start out with exactly the requested amount. PRUint32 temp = curCapacity; while (temp < capacity) temp <<= 1; capacity = temp; } } // // several cases: // // (1) we have a shared buffer (mFlags & F_SHARED) // (2) we have an owned buffer (mFlags & F_OWNED) // (3) we have a fixed buffer (mFlags & F_FIXED) // (4) we have a readonly buffer // // requiring that we in some cases preserve the data before creating // a new buffer complicates things just a bit ;-) // size_type storageSize = (capacity + 1) * sizeof(char_type); // case #1 if (mFlags & F_SHARED) { nsStringHeader* hdr = nsStringHeader::FromData(mData); if (!hdr->IsReadonly()) { nsStringHeader *newHdr = nsStringHeader::Realloc(hdr, storageSize); if (newHdr) { hdr = newHdr; mData = (char_type*) hdr->Data(); return PR_TRUE; } hdr->Release(); // out of memory!! put us in a consistent state at least. mData = NS_CONST_CAST(char_type*, char_traits::sEmptyBuffer); mLength = 0; SetDataFlags(F_TERMINATED); return PR_FALSE; }