michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "double-conversion.h" michael@0: michael@0: using double_conversion::DoubleToStringConverter; michael@0: michael@0: #ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE michael@0: nsTSubstring_CharT::nsTSubstring_CharT( char_type *data, size_type length, michael@0: uint32_t flags) michael@0: : mData(data), michael@0: mLength(length), michael@0: mFlags(flags) michael@0: { michael@0: if (flags & F_OWNED) { michael@0: STRING_STAT_INCREMENT(Adopt); michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: NS_LogCtor(mData, "StringAdopt", 1); michael@0: #endif michael@0: } michael@0: } michael@0: #endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */ michael@0: michael@0: /** michael@0: * helper function for down-casting a nsTSubstring to a nsTFixedString. michael@0: */ michael@0: inline const nsTFixedString_CharT* michael@0: AsFixedString( const nsTSubstring_CharT* s ) michael@0: { michael@0: return static_cast(s); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * this function is called to prepare mData for writing. the given capacity michael@0: * indicates the required minimum storage size for mData, in sizeof(char_type) michael@0: * increments. this function returns true if the operation succeeds. it also michael@0: * returns the old data and old flags members if mData is newly allocated. michael@0: * the old data must be released by the caller. michael@0: */ michael@0: bool michael@0: nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, uint32_t* oldFlags ) michael@0: { michael@0: // initialize to no old data michael@0: *oldData = nullptr; michael@0: *oldFlags = 0; michael@0: michael@0: size_type curCapacity = Capacity(); michael@0: michael@0: // If |capacity > kMaxCapacity|, then our doubling algorithm may not be michael@0: // able to allocate it. Just bail out in cases like that. We don't want michael@0: // to be allocating 2GB+ strings anyway. michael@0: PR_STATIC_ASSERT((sizeof(nsStringBuffer) & 0x1) == 0); michael@0: const size_type kMaxCapacity = michael@0: (size_type(-1)/2 - sizeof(nsStringBuffer)) / sizeof(char_type) - 2; michael@0: if (capacity > kMaxCapacity) { michael@0: // Also assert for |capacity| equal to |size_type(-1)|, since we used to michael@0: // use that value to flag immutability. michael@0: NS_ASSERTION(capacity != size_type(-1), "Bogus capacity"); michael@0: return false; michael@0: } michael@0: michael@0: // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we michael@0: // need to allocate a new buffer. We cannot use the existing buffer even michael@0: // though it might be large enough. michael@0: michael@0: if (curCapacity != 0) michael@0: { michael@0: if (capacity <= curCapacity) { michael@0: mFlags &= ~F_VOIDED; // mutation clears voided flag michael@0: return true; michael@0: } michael@0: michael@0: // Use doubling algorithm when forced to increase available capacity. michael@0: size_type temp = curCapacity; michael@0: while (temp < capacity) michael@0: temp <<= 1; michael@0: NS_ASSERTION(XPCOM_MIN(temp, kMaxCapacity) >= capacity, michael@0: "should have hit the early return at the top"); michael@0: capacity = XPCOM_MIN(temp, kMaxCapacity); michael@0: } michael@0: michael@0: // michael@0: // several cases: michael@0: // michael@0: // (1) we have a shared buffer (mFlags & F_SHARED) michael@0: // (2) we have an owned buffer (mFlags & F_OWNED) michael@0: // (3) we have a fixed buffer (mFlags & F_FIXED) michael@0: // (4) we have a readonly buffer michael@0: // michael@0: // requiring that we in some cases preserve the data before creating michael@0: // a new buffer complicates things just a bit ;-) michael@0: // michael@0: michael@0: size_type storageSize = (capacity + 1) * sizeof(char_type); michael@0: michael@0: // case #1 michael@0: if (mFlags & F_SHARED) michael@0: { michael@0: nsStringBuffer* hdr = nsStringBuffer::FromData(mData); michael@0: if (!hdr->IsReadonly()) michael@0: { michael@0: nsStringBuffer *newHdr = nsStringBuffer::Realloc(hdr, storageSize); michael@0: if (!newHdr) michael@0: return false; // out-of-memory (original header left intact) michael@0: michael@0: hdr = newHdr; michael@0: mData = (char_type*) hdr->Data(); michael@0: mFlags &= ~F_VOIDED; // mutation clears voided flag michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: char_type* newData; michael@0: uint32_t newDataFlags; michael@0: michael@0: // if we have a fixed buffer of sufficient size, then use it. this helps michael@0: // avoid heap allocations. michael@0: if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity)) michael@0: { michael@0: newData = AsFixedString(this)->mFixedBuf; michael@0: newDataFlags = F_TERMINATED | F_FIXED; michael@0: } michael@0: else michael@0: { michael@0: // if we reach here then, we must allocate a new buffer. we cannot michael@0: // make use of our F_OWNED or F_FIXED buffers because they are not michael@0: // large enough. michael@0: michael@0: nsStringBuffer* newHdr = michael@0: nsStringBuffer::Alloc(storageSize).take(); michael@0: if (!newHdr) michael@0: return false; // we are still in a consistent state michael@0: michael@0: newData = (char_type*) newHdr->Data(); michael@0: newDataFlags = F_TERMINATED | F_SHARED; michael@0: } michael@0: michael@0: // save old data and flags michael@0: *oldData = mData; michael@0: *oldFlags = mFlags; michael@0: michael@0: mData = newData; michael@0: SetDataFlags(newDataFlags); michael@0: michael@0: // mLength does not change michael@0: michael@0: // though we are not necessarily terminated at the moment, now is probably michael@0: // still the best time to set F_TERMINATED. michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::Finalize() michael@0: { michael@0: ::ReleaseData(mData, mFlags); michael@0: // mData, mLength, and mFlags are purposefully left dangling michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::ReplacePrepInternal(index_type cutStart, size_type cutLen, michael@0: size_type fragLen, size_type newLen) michael@0: { michael@0: char_type* oldData; michael@0: uint32_t oldFlags; michael@0: if (!MutatePrep(newLen, &oldData, &oldFlags)) michael@0: return false; // out-of-memory michael@0: michael@0: if (oldData) michael@0: { michael@0: // determine whether or not we need to copy part of the old string michael@0: // over to the new string. michael@0: michael@0: if (cutStart > 0) michael@0: { michael@0: // copy prefix from old string michael@0: char_traits::copy(mData, oldData, cutStart); michael@0: } michael@0: michael@0: if (cutStart + cutLen < mLength) michael@0: { michael@0: // copy suffix from old string to new offset michael@0: size_type from = cutStart + cutLen; michael@0: size_type fromLen = mLength - from; michael@0: uint32_t to = cutStart + fragLen; michael@0: char_traits::copy(mData + to, oldData + from, fromLen); michael@0: } michael@0: michael@0: ::ReleaseData(oldData, oldFlags); michael@0: } michael@0: else michael@0: { michael@0: // original data remains intact michael@0: michael@0: // determine whether or not we need to move part of the existing string michael@0: // to make room for the requested hole. michael@0: if (fragLen != cutLen && cutStart + cutLen < mLength) michael@0: { michael@0: uint32_t from = cutStart + cutLen; michael@0: uint32_t fromLen = mLength - from; michael@0: uint32_t to = cutStart + fragLen; michael@0: char_traits::move(mData + to, mData + from, fromLen); michael@0: } michael@0: } michael@0: michael@0: // add null terminator (mutable mData always has room for the null- michael@0: // terminator). michael@0: mData[newLen] = char_type(0); michael@0: mLength = newLen; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: nsTSubstring_CharT::size_type michael@0: nsTSubstring_CharT::Capacity() const michael@0: { michael@0: // return 0 to indicate an immutable or 0-sized buffer michael@0: michael@0: size_type capacity; michael@0: if (mFlags & F_SHARED) michael@0: { michael@0: // if the string is readonly, then we pretend that it has no capacity. michael@0: nsStringBuffer* hdr = nsStringBuffer::FromData(mData); michael@0: if (hdr->IsReadonly()) michael@0: capacity = 0; michael@0: else { michael@0: capacity = (hdr->StorageSize() / sizeof(char_type)) - 1; michael@0: } michael@0: } michael@0: else if (mFlags & F_FIXED) michael@0: { michael@0: capacity = AsFixedString(this)->mFixedCapacity; michael@0: } michael@0: else if (mFlags & F_OWNED) michael@0: { michael@0: // we don't store the capacity of an adopted buffer because that would michael@0: // require an additional member field. the best we can do is base the michael@0: // capacity on our length. remains to be seen if this is the right michael@0: // trade-off. michael@0: capacity = mLength; michael@0: } michael@0: else michael@0: { michael@0: capacity = 0; michael@0: } michael@0: michael@0: return capacity; michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::EnsureMutable( size_type newLen ) michael@0: { michael@0: if (newLen == size_type(-1) || newLen == mLength) michael@0: { michael@0: if (mFlags & (F_FIXED | F_OWNED)) michael@0: return true; michael@0: if ((mFlags & F_SHARED) && !nsStringBuffer::FromData(mData)->IsReadonly()) michael@0: return true; michael@0: michael@0: newLen = mLength; michael@0: } michael@0: return SetLength(newLen, fallible_t()); michael@0: } michael@0: michael@0: // --------------------------------------------------------------------------- michael@0: michael@0: // This version of Assign is optimized for single-character assignment. michael@0: void michael@0: nsTSubstring_CharT::Assign( char_type c ) michael@0: { michael@0: if (!ReplacePrep(0, mLength, 1)) michael@0: NS_ABORT_OOM(mLength); michael@0: michael@0: *mData = c; michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::Assign( char_type c, const fallible_t& ) michael@0: { michael@0: if (!ReplacePrep(0, mLength, 1)) michael@0: return false; michael@0: michael@0: *mData = c; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::Assign( const char_type* data ) michael@0: { michael@0: if (!Assign(data, size_type(-1), fallible_t())) michael@0: NS_ABORT_OOM(char_traits::length(data)); michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::Assign( const char_type* data, size_type length ) michael@0: { michael@0: if (!Assign(data, length, fallible_t())) michael@0: NS_ABORT_OOM(length); michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::Assign( const char_type* data, size_type length, const fallible_t& ) michael@0: { michael@0: if (!data || length == 0) michael@0: { michael@0: Truncate(); michael@0: return true; michael@0: } michael@0: michael@0: if (length == size_type(-1)) michael@0: length = char_traits::length(data); michael@0: michael@0: if (IsDependentOn(data, data + length)) michael@0: { michael@0: return Assign(string_type(data, length), fallible_t()); michael@0: } michael@0: michael@0: if (!ReplacePrep(0, mLength, length)) michael@0: return false; michael@0: michael@0: char_traits::copy(mData, data, length); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::AssignASCII( const char* data, size_type length ) michael@0: { michael@0: if (!AssignASCII(data, length, fallible_t())) michael@0: NS_ABORT_OOM(length); michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::AssignASCII( const char* data, size_type length, const fallible_t& ) michael@0: { michael@0: // A Unicode string can't depend on an ASCII string buffer, michael@0: // so this dependence check only applies to CStrings. michael@0: #ifdef CharT_is_char michael@0: if (IsDependentOn(data, data + length)) michael@0: { michael@0: return Assign(string_type(data, length), fallible_t()); michael@0: } michael@0: #endif michael@0: michael@0: if (!ReplacePrep(0, mLength, length)) michael@0: return false; michael@0: michael@0: char_traits::copyASCII(mData, data, length); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::AssignLiteral( const char_type* data, size_type length ) michael@0: { michael@0: ::ReleaseData(mData, mFlags); michael@0: mData = const_cast(data); michael@0: mLength = length; michael@0: SetDataFlags(F_TERMINATED | F_LITERAL); michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::Assign( const self_type& str ) michael@0: { michael@0: if (!Assign(str, fallible_t())) michael@0: NS_ABORT_OOM(str.Length()); michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::Assign( const self_type& str, const fallible_t& ) michael@0: { michael@0: // |str| could be sharable. we need to check its flags to know how to michael@0: // deal with it. michael@0: michael@0: if (&str == this) michael@0: return true; michael@0: michael@0: if (!str.mLength) michael@0: { michael@0: Truncate(); michael@0: mFlags |= str.mFlags & F_VOIDED; michael@0: return true; michael@0: } michael@0: michael@0: if (str.mFlags & F_SHARED) michael@0: { michael@0: // nice! we can avoid a string copy :-) michael@0: michael@0: // |str| should be null-terminated michael@0: NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated"); michael@0: michael@0: ::ReleaseData(mData, mFlags); michael@0: michael@0: mData = str.mData; michael@0: mLength = str.mLength; michael@0: SetDataFlags(F_TERMINATED | F_SHARED); michael@0: michael@0: // get an owning reference to the mData michael@0: nsStringBuffer::FromData(mData)->AddRef(); michael@0: return true; michael@0: } michael@0: else if (str.mFlags & F_LITERAL) michael@0: { michael@0: NS_ABORT_IF_FALSE(str.mFlags & F_TERMINATED, "Unterminated literal"); michael@0: michael@0: AssignLiteral(str.mData, str.mLength); michael@0: return true; michael@0: } michael@0: michael@0: // else, treat this like an ordinary assignment. michael@0: return Assign(str.Data(), str.Length(), fallible_t()); michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::Assign( const substring_tuple_type& tuple ) michael@0: { michael@0: if (!Assign(tuple, fallible_t())) michael@0: NS_ABORT_OOM(tuple.Length()); michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::Assign( const substring_tuple_type& tuple, const fallible_t& ) michael@0: { michael@0: if (tuple.IsDependentOn(mData, mData + mLength)) michael@0: { michael@0: // take advantage of sharing here... michael@0: return Assign(string_type(tuple), fallible_t()); michael@0: } michael@0: michael@0: size_type length = tuple.Length(); michael@0: michael@0: // don't use ReplacePrep here because it changes the length michael@0: char_type* oldData; michael@0: uint32_t oldFlags; michael@0: if (!MutatePrep(length, &oldData, &oldFlags)) michael@0: return false; michael@0: michael@0: if (oldData) michael@0: ::ReleaseData(oldData, oldFlags); michael@0: michael@0: tuple.WriteTo(mData, length); michael@0: mData[length] = 0; michael@0: mLength = length; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::Adopt( char_type* data, size_type length ) michael@0: { michael@0: if (data) michael@0: { michael@0: ::ReleaseData(mData, mFlags); michael@0: michael@0: if (length == size_type(-1)) michael@0: length = char_traits::length(data); michael@0: michael@0: mData = data; michael@0: mLength = length; michael@0: SetDataFlags(F_TERMINATED | F_OWNED); michael@0: michael@0: STRING_STAT_INCREMENT(Adopt); michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: // Treat this as construction of a "StringAdopt" object for leak michael@0: // tracking purposes. michael@0: NS_LogCtor(mData, "StringAdopt", 1); michael@0: #endif // NS_BUILD_REFCNT_LOGGING michael@0: } michael@0: else michael@0: { michael@0: SetIsVoid(true); michael@0: } michael@0: } michael@0: michael@0: michael@0: // This version of Replace is optimized for single-character replacement. michael@0: void michael@0: nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c ) michael@0: { michael@0: cutStart = XPCOM_MIN(cutStart, Length()); michael@0: michael@0: if (ReplacePrep(cutStart, cutLength, 1)) michael@0: mData[cutStart] = c; michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c, const mozilla::fallible_t& ) michael@0: { michael@0: cutStart = XPCOM_MIN(cutStart, Length()); michael@0: michael@0: if (!ReplacePrep(cutStart, cutLength, 1)) michael@0: return false; michael@0: michael@0: mData[cutStart] = c; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length ) michael@0: { michael@0: if (!Replace(cutStart, cutLength, data, length, mozilla::fallible_t())) michael@0: { michael@0: NS_ABORT_OOM(Length() - cutLength + 1); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length, const mozilla::fallible_t& ) michael@0: { michael@0: // unfortunately, some callers pass null :-( michael@0: if (!data) michael@0: { michael@0: length = 0; michael@0: } michael@0: else michael@0: { michael@0: if (length == size_type(-1)) michael@0: length = char_traits::length(data); michael@0: michael@0: if (IsDependentOn(data, data + length)) michael@0: { michael@0: nsTAutoString_CharT temp(data, length); michael@0: return Replace(cutStart, cutLength, temp, mozilla::fallible_t()); michael@0: } michael@0: } michael@0: michael@0: cutStart = XPCOM_MIN(cutStart, Length()); michael@0: michael@0: bool ok = ReplacePrep(cutStart, cutLength, length); michael@0: if (!ok) michael@0: return false; michael@0: michael@0: if (length > 0) michael@0: char_traits::copy(mData + cutStart, data, length); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::ReplaceASCII( index_type cutStart, size_type cutLength, const char* data, size_type length ) michael@0: { michael@0: if (length == size_type(-1)) michael@0: length = strlen(data); michael@0: michael@0: // A Unicode string can't depend on an ASCII string buffer, michael@0: // so this dependence check only applies to CStrings. michael@0: #ifdef CharT_is_char michael@0: if (IsDependentOn(data, data + length)) michael@0: { michael@0: nsTAutoString_CharT temp(data, length); michael@0: Replace(cutStart, cutLength, temp); michael@0: return; michael@0: } michael@0: #endif michael@0: michael@0: cutStart = XPCOM_MIN(cutStart, Length()); michael@0: michael@0: if (ReplacePrep(cutStart, cutLength, length) && length > 0) michael@0: char_traits::copyASCII(mData + cutStart, data, length); michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& tuple ) michael@0: { michael@0: if (tuple.IsDependentOn(mData, mData + mLength)) michael@0: { michael@0: nsTAutoString_CharT temp(tuple); michael@0: Replace(cutStart, cutLength, temp); michael@0: return; michael@0: } michael@0: michael@0: size_type length = tuple.Length(); michael@0: michael@0: cutStart = XPCOM_MIN(cutStart, Length()); michael@0: michael@0: if (ReplacePrep(cutStart, cutLength, length) && length > 0) michael@0: tuple.WriteTo(mData + cutStart, length); michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::ReplaceLiteral( index_type cutStart, size_type cutLength, const char_type* data, size_type length ) michael@0: { michael@0: cutStart = XPCOM_MIN(cutStart, Length()); michael@0: michael@0: if (!cutStart && cutLength == Length()) michael@0: AssignLiteral(data, length); michael@0: else if (ReplacePrep(cutStart, cutLength, length) && length > 0) michael@0: char_traits::copy(mData + cutStart, data, length); michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::SetCapacity( size_type capacity ) michael@0: { michael@0: if (!SetCapacity(capacity, fallible_t())) michael@0: NS_ABORT_OOM(capacity); michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::SetCapacity( size_type capacity, const fallible_t& ) michael@0: { michael@0: // capacity does not include room for the terminating null char michael@0: michael@0: // if our capacity is reduced to zero, then free our buffer. michael@0: if (capacity == 0) michael@0: { michael@0: ::ReleaseData(mData, mFlags); michael@0: mData = char_traits::sEmptyBuffer; michael@0: mLength = 0; michael@0: SetDataFlags(F_TERMINATED); michael@0: return true; michael@0: } michael@0: michael@0: char_type* oldData; michael@0: uint32_t oldFlags; michael@0: if (!MutatePrep(capacity, &oldData, &oldFlags)) michael@0: return false; // out-of-memory michael@0: michael@0: // compute new string length michael@0: size_type newLen = XPCOM_MIN(mLength, capacity); michael@0: michael@0: if (oldData) michael@0: { michael@0: // preserve old data michael@0: if (mLength > 0) michael@0: char_traits::copy(mData, oldData, newLen); michael@0: michael@0: ::ReleaseData(oldData, oldFlags); michael@0: } michael@0: michael@0: // adjust mLength if our buffer shrunk down in size michael@0: if (newLen < mLength) michael@0: mLength = newLen; michael@0: michael@0: // always null-terminate here, even if the buffer got longer. this is michael@0: // for backwards compat with the old string implementation. michael@0: mData[capacity] = char_type(0); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::SetLength( size_type length ) michael@0: { michael@0: SetCapacity(length); michael@0: mLength = length; michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::SetLength( size_type length, const fallible_t& ) michael@0: { michael@0: if (!SetCapacity(length, fallible_t())) michael@0: return false; michael@0: michael@0: mLength = length; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::SetIsVoid( bool val ) michael@0: { michael@0: if (val) michael@0: { michael@0: Truncate(); michael@0: mFlags |= F_VOIDED; michael@0: } michael@0: else michael@0: { michael@0: mFlags &= ~F_VOIDED; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::Equals( const self_type& str ) const michael@0: { michael@0: return mLength == str.mLength && char_traits::compare(mData, str.mData, mLength) == 0; michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::Equals( const self_type& str, const comparator_type& comp ) const michael@0: { michael@0: return mLength == str.mLength && comp(mData, str.mData, mLength, str.mLength) == 0; michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::Equals( const char_type* data ) const michael@0: { michael@0: // unfortunately, some callers pass null :-( michael@0: if (!data) michael@0: { michael@0: NS_NOTREACHED("null data pointer"); michael@0: return mLength == 0; michael@0: } michael@0: michael@0: // XXX avoid length calculation? michael@0: size_type length = char_traits::length(data); michael@0: return mLength == length && char_traits::compare(mData, data, mLength) == 0; michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::Equals( const char_type* data, const comparator_type& comp ) const michael@0: { michael@0: // unfortunately, some callers pass null :-( michael@0: if (!data) michael@0: { michael@0: NS_NOTREACHED("null data pointer"); michael@0: return mLength == 0; michael@0: } michael@0: michael@0: // XXX avoid length calculation? michael@0: size_type length = char_traits::length(data); michael@0: return mLength == length && comp(mData, data, mLength, length) == 0; michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::EqualsASCII( const char* data, size_type len ) const michael@0: { michael@0: return mLength == len && char_traits::compareASCII(mData, data, len) == 0; michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::EqualsASCII( const char* data ) const michael@0: { michael@0: return char_traits::compareASCIINullTerminated(mData, mLength, data) == 0; michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data, size_type len ) const michael@0: { michael@0: return mLength == len && char_traits::compareLowerCaseToASCII(mData, data, len) == 0; michael@0: } michael@0: michael@0: bool michael@0: nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data ) const michael@0: { michael@0: return char_traits::compareLowerCaseToASCIINullTerminated(mData, mLength, data) == 0; michael@0: } michael@0: michael@0: nsTSubstring_CharT::size_type michael@0: nsTSubstring_CharT::CountChar( char_type c ) const michael@0: { michael@0: const char_type *start = mData; michael@0: const char_type *end = mData + mLength; michael@0: michael@0: return NS_COUNT(start, end, c); michael@0: } michael@0: michael@0: int32_t michael@0: nsTSubstring_CharT::FindChar( char_type c, index_type offset ) const michael@0: { michael@0: if (offset < mLength) michael@0: { michael@0: const char_type* result = char_traits::find(mData + offset, mLength - offset, c); michael@0: if (result) michael@0: return result - mData; michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::StripChar( char_type aChar, int32_t aOffset ) michael@0: { michael@0: if (mLength == 0 || aOffset >= int32_t(mLength)) michael@0: return; michael@0: michael@0: if (!EnsureMutable()) // XXX do this lazily? michael@0: NS_ABORT_OOM(mLength); michael@0: michael@0: // XXX(darin): this code should defer writing until necessary. michael@0: michael@0: char_type* to = mData + aOffset; michael@0: char_type* from = mData + aOffset; michael@0: char_type* end = mData + mLength; michael@0: michael@0: while (from < end) michael@0: { michael@0: char_type theChar = *from++; michael@0: if (aChar != theChar) michael@0: *to++ = theChar; michael@0: } michael@0: *to = char_type(0); // add the null michael@0: mLength = to - mData; michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::StripChars( const char_type* aChars, uint32_t aOffset ) michael@0: { michael@0: if (aOffset >= uint32_t(mLength)) michael@0: return; michael@0: michael@0: if (!EnsureMutable()) // XXX do this lazily? michael@0: NS_ABORT_OOM(mLength); michael@0: michael@0: // XXX(darin): this code should defer writing until necessary. michael@0: michael@0: char_type* to = mData + aOffset; michael@0: char_type* from = mData + aOffset; michael@0: char_type* end = mData + mLength; michael@0: michael@0: while (from < end) michael@0: { michael@0: char_type theChar = *from++; michael@0: const char_type* test = aChars; michael@0: michael@0: for (; *test && *test != theChar; ++test); michael@0: michael@0: if (!*test) { michael@0: // Not stripped, copy this char. michael@0: *to++ = theChar; michael@0: } michael@0: } michael@0: *to = char_type(0); // add the null michael@0: mLength = to - mData; michael@0: } michael@0: michael@0: int michael@0: nsTSubstring_CharT::AppendFunc(void* arg, const char* s, uint32_t len) michael@0: { michael@0: self_type* self = static_cast(arg); michael@0: michael@0: // NSPR sends us the final null terminator even though we don't want it michael@0: if (len && s[len - 1] == '\0') { michael@0: --len; michael@0: } michael@0: michael@0: self->AppendASCII(s, len); michael@0: michael@0: return len; michael@0: } michael@0: michael@0: void nsTSubstring_CharT::AppendPrintf( const char* format, ...) michael@0: { michael@0: va_list ap; michael@0: va_start(ap, format); michael@0: uint32_t r = PR_vsxprintf(AppendFunc, this, format, ap); michael@0: if (r == (uint32_t) -1) michael@0: NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf"); michael@0: va_end(ap); michael@0: } michael@0: michael@0: void nsTSubstring_CharT::AppendPrintf( const char* format, va_list ap ) michael@0: { michael@0: uint32_t r = PR_vsxprintf(AppendFunc, this, format, ap); michael@0: if (r == (uint32_t) -1) michael@0: NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf"); michael@0: } michael@0: michael@0: /* hack to make sure we define FormatWithoutTrailingZeros only once */ michael@0: #ifdef CharT_is_PRUnichar michael@0: // Returns the length of the formatted aDouble in buf. michael@0: static int michael@0: FormatWithoutTrailingZeros(char (& buf)[40], double aDouble, michael@0: int precision) michael@0: { michael@0: static const DoubleToStringConverter converter(DoubleToStringConverter::UNIQUE_ZERO | michael@0: DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN, michael@0: "Infinity", michael@0: "NaN", michael@0: 'e', michael@0: -6, 21, michael@0: 6, 1); michael@0: double_conversion::StringBuilder builder(buf, sizeof(buf)); michael@0: bool exponential_notation = false; michael@0: converter.ToPrecision(aDouble, precision, &exponential_notation, &builder); michael@0: int length = builder.position(); michael@0: char* formattedDouble = builder.Finalize(); michael@0: michael@0: // If we have a shorter string than precision, it means we have a special michael@0: // value (NaN or Infinity). All other numbers will be formatted with at michael@0: // least precision digits. michael@0: if (length <= precision) { michael@0: return length; michael@0: } michael@0: michael@0: char* end = formattedDouble + length; michael@0: char* decimalPoint = strchr(buf, '.'); michael@0: // No trailing zeros to remove. michael@0: if (decimalPoint == nullptr) { michael@0: return length; michael@0: } michael@0: michael@0: if (MOZ_UNLIKELY(exponential_notation)) { michael@0: // We need to check for cases like 1.00000e-10 (yes, this is michael@0: // disgusting). michael@0: char* exponent = end - 1; michael@0: for ( ; ; --exponent) { michael@0: if (*exponent == 'e') { michael@0: break; michael@0: } michael@0: } michael@0: char* zerosBeforeExponent = exponent - 1; michael@0: for ( ; zerosBeforeExponent != decimalPoint; --zerosBeforeExponent) { michael@0: if (*zerosBeforeExponent != '0') { michael@0: break; michael@0: } michael@0: } michael@0: if (zerosBeforeExponent == decimalPoint) { michael@0: --zerosBeforeExponent; michael@0: } michael@0: // Slide the exponent to the left over the trailing zeros. Don't michael@0: // worry about copying the trailing NUL character. michael@0: size_t exponentSize = end - exponent; michael@0: memmove(zerosBeforeExponent + 1, exponent, exponentSize); michael@0: length -= exponent - (zerosBeforeExponent + 1); michael@0: } else { michael@0: char* trailingZeros = end - 1; michael@0: for ( ; trailingZeros != decimalPoint; --trailingZeros) { michael@0: if (*trailingZeros != '0') { michael@0: break; michael@0: } michael@0: } michael@0: if (trailingZeros == decimalPoint) { michael@0: --trailingZeros; michael@0: } michael@0: length -= end - (trailingZeros + 1); michael@0: } michael@0: michael@0: return length; michael@0: } michael@0: #endif /* CharT_is_PRUnichar */ michael@0: michael@0: void michael@0: nsTSubstring_CharT::AppendFloat( float aFloat ) michael@0: { michael@0: char buf[40]; michael@0: int length = FormatWithoutTrailingZeros(buf, aFloat, 6); michael@0: AppendASCII(buf, length); michael@0: } michael@0: michael@0: void michael@0: nsTSubstring_CharT::AppendFloat( double aFloat ) michael@0: { michael@0: char buf[40]; michael@0: int length = FormatWithoutTrailingZeros(buf, aFloat, 15); michael@0: AppendASCII(buf, length); michael@0: } michael@0: michael@0: size_t michael@0: nsTSubstring_CharT::SizeOfExcludingThisMustBeUnshared( michael@0: mozilla::MallocSizeOf mallocSizeOf) const michael@0: { michael@0: if (mFlags & F_SHARED) { michael@0: return nsStringBuffer::FromData(mData)-> michael@0: SizeOfIncludingThisMustBeUnshared(mallocSizeOf); michael@0: } michael@0: if (mFlags & F_OWNED) { michael@0: return mallocSizeOf(mData); michael@0: } michael@0: michael@0: // If we reach here, exactly one of the following must be true: michael@0: // - F_VOIDED is set, and mData points to sEmptyBuffer; michael@0: // - F_FIXED is set, and mData points to a buffer within a string michael@0: // object (e.g. nsAutoString); michael@0: // - None of F_SHARED, F_OWNED, F_FIXED is set, and mData points to a buffer michael@0: // owned by something else. michael@0: // michael@0: // In all three cases, we don't measure it. michael@0: return 0; michael@0: } michael@0: michael@0: size_t michael@0: nsTSubstring_CharT::SizeOfExcludingThisIfUnshared( michael@0: mozilla::MallocSizeOf mallocSizeOf) const michael@0: { michael@0: // This is identical to SizeOfExcludingThisMustBeUnshared except for the michael@0: // F_SHARED case. michael@0: if (mFlags & F_SHARED) { michael@0: return nsStringBuffer::FromData(mData)-> michael@0: SizeOfIncludingThisIfUnshared(mallocSizeOf); michael@0: } michael@0: if (mFlags & F_OWNED) { michael@0: return mallocSizeOf(mData); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: size_t michael@0: nsTSubstring_CharT::SizeOfExcludingThisEvenIfShared( michael@0: mozilla::MallocSizeOf mallocSizeOf) const michael@0: { michael@0: // This is identical to SizeOfExcludingThisMustBeUnshared except for the michael@0: // F_SHARED case. michael@0: if (mFlags & F_SHARED) { michael@0: return nsStringBuffer::FromData(mData)-> michael@0: SizeOfIncludingThisEvenIfShared(mallocSizeOf); michael@0: } michael@0: if (mFlags & F_OWNED) { michael@0: return mallocSizeOf(mData); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: size_t michael@0: nsTSubstring_CharT::SizeOfIncludingThisMustBeUnshared( michael@0: mozilla::MallocSizeOf mallocSizeOf) const michael@0: { michael@0: return mallocSizeOf(this) + SizeOfExcludingThisMustBeUnshared(mallocSizeOf); michael@0: } michael@0: michael@0: size_t michael@0: nsTSubstring_CharT::SizeOfIncludingThisIfUnshared( michael@0: mozilla::MallocSizeOf mallocSizeOf) const michael@0: { michael@0: return mallocSizeOf(this) + SizeOfExcludingThisIfUnshared(mallocSizeOf); michael@0: } michael@0: michael@0: size_t michael@0: nsTSubstring_CharT::SizeOfIncludingThisEvenIfShared( michael@0: mozilla::MallocSizeOf mallocSizeOf) const michael@0: { michael@0: return mallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(mallocSizeOf); michael@0: } michael@0: