diff options
Diffstat (limited to 'xpcom/string')
-rw-r--r-- | xpcom/string/moz.build | 3 | ||||
-rw-r--r-- | xpcom/string/nsStringBuffer.cpp | 138 | ||||
-rw-r--r-- | xpcom/string/nsStringBuffer.h | 93 | ||||
-rw-r--r-- | xpcom/string/nsStringStats.cpp | 66 | ||||
-rw-r--r-- | xpcom/string/nsStringStats.h | 32 | ||||
-rw-r--r-- | xpcom/string/nsTLiteralString.h | 12 | ||||
-rw-r--r-- | xpcom/string/nsTSubstring.cpp | 20 | ||||
-rw-r--r-- | xpcom/string/nsTSubstring.h | 27 |
8 files changed, 101 insertions, 290 deletions
diff --git a/xpcom/string/moz.build b/xpcom/string/moz.build index c0f8091b8f..1220a16bc8 100644 --- a/xpcom/string/moz.build +++ b/xpcom/string/moz.build @@ -56,7 +56,4 @@ UNIFIED_SOURCES += [ "RustStringAPI.cpp", ] -if CONFIG["MOZ_DEBUG"]: - UNIFIED_SOURCES += ["nsStringStats.cpp"] - FINAL_LIBRARY = "xul" diff --git a/xpcom/string/nsStringBuffer.cpp b/xpcom/string/nsStringBuffer.cpp index b5d506333f..fbfc91f633 100644 --- a/xpcom/string/nsStringBuffer.cpp +++ b/xpcom/string/nsStringBuffer.cpp @@ -7,74 +7,7 @@ #include "nsStringBuffer.h" #include "mozilla/MemoryReporting.h" -#include "nsISupportsImpl.h" -#include "nsString.h" - -#ifdef DEBUG -# include "nsStringStats.h" -#else -# define STRING_STAT_INCREMENT(_s) -#endif - -void nsStringBuffer::AddRef() { - // Memory synchronization is not required when incrementing a - // reference count. The first increment of a reference count on a - // thread is not important, since the first use of the object on a - // thread can happen before it. What is important is the transfer - // of the pointer to that thread, which may happen prior to the - // first increment on that thread. The necessary memory - // synchronization is done by the mechanism that transfers the - // pointer between threads. -#ifdef NS_BUILD_REFCNT_LOGGING - uint32_t count = -#endif - mRefCount.fetch_add(1, std::memory_order_relaxed) -#ifdef NS_BUILD_REFCNT_LOGGING - + 1 -#endif - ; - STRING_STAT_INCREMENT(Share); - NS_LOG_ADDREF(this, count, "nsStringBuffer", sizeof(*this)); -} - -void nsStringBuffer::Release() { - // Since this may be the last release on this thread, we need - // release semantics so that prior writes on this thread are visible - // to the thread that destroys the object when it reads mValue with - // acquire semantics. - uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1; - NS_LOG_RELEASE(this, count, "nsStringBuffer"); - if (count == 0) { - // We're going to destroy the object on this thread, so we need - // acquire semantics to synchronize with the memory released by - // the last release on other threads, that is, to ensure that - // writes prior to that release are now visible on this thread. - count = mRefCount.load(std::memory_order_acquire); - - STRING_STAT_INCREMENT(Free); - free(this); // we were allocated with |malloc| - } -} - -/** - * Alloc returns a pointer to a new string header with set capacity. - */ -already_AddRefed<nsStringBuffer> nsStringBuffer::Alloc(size_t aSize) { - NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed"); - NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) && - sizeof(nsStringBuffer) + aSize > aSize, - "mStorageSize will truncate"); - - auto* hdr = (nsStringBuffer*)malloc(sizeof(nsStringBuffer) + aSize); - if (hdr) { - STRING_STAT_INCREMENT(Alloc); - - hdr->mRefCount = 1; - hdr->mStorageSize = aSize; - NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr)); - } - return already_AddRefed(hdr); -} +#include "mozilla/RefPtr.h" template <typename CharT> static already_AddRefed<nsStringBuffer> DoCreate(const CharT* aData, @@ -101,80 +34,31 @@ already_AddRefed<nsStringBuffer> nsStringBuffer::Create(const char16_t* aData, } nsStringBuffer* nsStringBuffer::Realloc(nsStringBuffer* aHdr, size_t aSize) { - STRING_STAT_INCREMENT(Realloc); - - NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed"); - NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) && - sizeof(nsStringBuffer) + aSize > aSize, - "mStorageSize will truncate"); + MOZ_ASSERT(aSize != 0, "zero capacity allocation not allowed"); + MOZ_ASSERT(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) && + sizeof(nsStringBuffer) + aSize > aSize, + "mStorageSize will truncate"); // no point in trying to save ourselves if we hit this assertion - NS_ASSERTION(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string"); + MOZ_ASSERT(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string"); // Treat this as a release and addref for refcounting purposes, since we // just asserted that the refcount is 1. If we don't do that, refcount // logging will claim we've leaked all sorts of stuff. - NS_LOG_RELEASE(aHdr, 0, "nsStringBuffer"); + { + mozilla::detail::RefCountLogger::ReleaseLogger logger(aHdr); + logger.logRelease(0); + } aHdr = (nsStringBuffer*)realloc(aHdr, sizeof(nsStringBuffer) + aSize); if (aHdr) { - NS_LOG_ADDREF(aHdr, 1, "nsStringBuffer", sizeof(*aHdr)); + mozilla::detail::RefCountLogger::logAddRef(aHdr, 1); aHdr->mStorageSize = aSize; } return aHdr; } -nsStringBuffer* nsStringBuffer::FromString(const nsAString& aStr) { - if (!(aStr.mDataFlags & nsAString::DataFlags::REFCOUNTED)) { - return nullptr; - } - - return FromData(aStr.mData); -} - -nsStringBuffer* nsStringBuffer::FromString(const nsACString& aStr) { - if (!(aStr.mDataFlags & nsACString::DataFlags::REFCOUNTED)) { - return nullptr; - } - - return FromData(aStr.mData); -} - -void nsStringBuffer::ToString(uint32_t aLen, nsAString& aStr, - bool aMoveOwnership) { - char16_t* data = static_cast<char16_t*>(Data()); - - MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char16_t(0), - "data should be null terminated"); - - nsAString::DataFlags flags = - nsAString::DataFlags::REFCOUNTED | nsAString::DataFlags::TERMINATED; - - if (!aMoveOwnership) { - AddRef(); - } - aStr.Finalize(); - aStr.SetData(data, aLen, flags); -} - -void nsStringBuffer::ToString(uint32_t aLen, nsACString& aStr, - bool aMoveOwnership) { - char* data = static_cast<char*>(Data()); - - MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char(0), - "data should be null terminated"); - - nsACString::DataFlags flags = - nsACString::DataFlags::REFCOUNTED | nsACString::DataFlags::TERMINATED; - - if (!aMoveOwnership) { - AddRef(); - } - aStr.Finalize(); - aStr.SetData(data, aLen, flags); -} - size_t nsStringBuffer::SizeOfIncludingThisIfUnshared( mozilla::MallocSizeOf aMallocSizeOf) const { return IsReadonly() ? 0 : aMallocSizeOf(this); diff --git a/xpcom/string/nsStringBuffer.h b/xpcom/string/nsStringBuffer.h index 43628d6668..dad41e48f7 100644 --- a/xpcom/string/nsStringBuffer.h +++ b/xpcom/string/nsStringBuffer.h @@ -9,10 +9,9 @@ #include <atomic> #include "mozilla/MemoryReporting.h" -#include "nsStringFwd.h" - -template <class T> -struct already_AddRefed; +#include "mozilla/Assertions.h" +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/RefCounted.h" /** * This structure precedes the string buffers "we" allocate. It may be the @@ -25,12 +24,12 @@ struct already_AddRefed; */ class nsStringBuffer { private: - friend class CheckStaticAtomSizes; - std::atomic<uint32_t> mRefCount; uint32_t mStorageSize; public: + MOZ_DECLARE_REFCOUNTED_TYPENAME(nsStringBuffer) + /** * Allocates a new string buffer, with given size in bytes and a * reference count of one. When the string buffer is no longer needed, @@ -43,12 +42,25 @@ class nsStringBuffer { * (i.e., it is not required that the null terminator appear in the last * storage unit of the string buffer's data). * - * This guarantees that StorageSize() returns aStorageSize if the returned + * This guarantees that StorageSize() returns aSize if the returned * buffer is non-null. Some callers like nsAttrValue rely on it. * * @return new string buffer or null if out of memory. */ - static already_AddRefed<nsStringBuffer> Alloc(size_t aStorageSize); + static already_AddRefed<nsStringBuffer> Alloc(size_t aSize) { + MOZ_ASSERT(aSize != 0, "zero capacity allocation not allowed"); + MOZ_ASSERT(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) && + sizeof(nsStringBuffer) + aSize > aSize, + "mStorageSize will truncate"); + + auto* hdr = (nsStringBuffer*)malloc(sizeof(nsStringBuffer) + aSize); + if (hdr) { + hdr->mRefCount = 1; + hdr->mStorageSize = aSize; + mozilla::detail::RefCountLogger::logAddRef(hdr, 1); + } + return already_AddRefed(hdr); + } /** * Returns a string buffer initialized with the given string on it, or null on @@ -74,16 +86,35 @@ class nsStringBuffer { */ static nsStringBuffer* Realloc(nsStringBuffer* aBuf, size_t aStorageSize); - /** - * Increment the reference count on this string buffer. - */ - void NS_FASTCALL AddRef(); + void AddRef() { + // Memory synchronization is not required when incrementing a + // reference count. The first increment of a reference count on a + // thread is not important, since the first use of the object on a + // thread can happen before it. What is important is the transfer + // of the pointer to that thread, which may happen prior to the + // first increment on that thread. The necessary memory + // synchronization is done by the mechanism that transfers the + // pointer between threads. + uint32_t count = mRefCount.fetch_add(1, std::memory_order_relaxed) + 1; + mozilla::detail::RefCountLogger::logAddRef(this, count); + } - /** - * Decrement the reference count on this string buffer. The string - * buffer will be destroyed when its reference count reaches zero. - */ - void NS_FASTCALL Release(); + void Release() { + // Since this may be the last release on this thread, we need release + // semantics so that prior writes on this thread are visible to the thread + // that destroys the object when it reads mValue with acquire semantics. + mozilla::detail::RefCountLogger::ReleaseLogger logger(this); + uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1; + logger.logRelease(count); + if (count == 0) { + // We're going to destroy the object on this thread, so we need acquire + // semantics to synchronize with the memory released by the last release + // on other threads, that is, to ensure that writes prior to that release + // are now visible on this thread. + count = mRefCount.load(std::memory_order_acquire); + free(this); // We were allocated with malloc. + } + } /** * This method returns the string buffer corresponding to the given data @@ -149,34 +180,6 @@ class nsStringBuffer { } /** - * The FromString methods return a string buffer for the given string - * object or null if the string object does not have a string buffer. - * The reference count of the string buffer is NOT incremented by these - * methods. If the caller wishes to hold onto the returned value, then - * the returned string buffer must have its reference count incremented - * via a call to the AddRef method. - */ - static nsStringBuffer* FromString(const nsAString& aStr); - static nsStringBuffer* FromString(const nsACString& aStr); - - /** - * The ToString methods assign this string buffer to a given string - * object. If the string object does not support sharable string - * buffers, then its value will be set to a copy of the given string - * buffer. Otherwise, these methods increment the reference count of the - * given string buffer. It is important to specify the length (in - * storage units) of the string contained in the string buffer since the - * length of the string may be less than its storage size. The string - * must have a null terminator at the offset specified by |len|. - * - * NOTE: storage size is measured in bytes even for wide strings; - * however, string length is always measured in storage units - * (2-byte units for wide strings). - */ - void ToString(uint32_t aLen, nsAString& aStr, bool aMoveOwnership = false); - void ToString(uint32_t aLen, nsACString& aStr, bool aMoveOwnership = false); - - /** * This measures the size only if the StringBuffer is unshared. */ size_t SizeOfIncludingThisIfUnshared( diff --git a/xpcom/string/nsStringStats.cpp b/xpcom/string/nsStringStats.cpp deleted file mode 100644 index 7fc3d82ad5..0000000000 --- a/xpcom/string/nsStringStats.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsStringStats.h" - -#include "mozilla/IntegerPrintfMacros.h" -#include "mozilla/MemoryReporting.h" -#include "nsString.h" - -#include <stdint.h> -#include <stdio.h> - -#ifdef XP_WIN -# include <windows.h> -# include <process.h> -#else -# include <unistd.h> -# include <pthread.h> -#endif - -nsStringStats gStringStats; - -nsStringStats::~nsStringStats() { - // this is a hack to suppress duplicate string stats printing - // in seamonkey as a result of the string code being linked - // into seamonkey and libxpcom! :-( - if (!mAllocCount && !mAdoptCount) { - return; - } - - // Only print the stats if we detect a leak. - if (mAllocCount <= mFreeCount && mAdoptCount <= mAdoptFreeCount) { - return; - } - - printf("nsStringStats\n"); - printf(" => mAllocCount: % 10d\n", int(mAllocCount)); - printf(" => mReallocCount: % 10d\n", int(mReallocCount)); - printf(" => mFreeCount: % 10d", int(mFreeCount)); - if (mAllocCount > mFreeCount) { - printf(" -- LEAKED %d !!!\n", mAllocCount - mFreeCount); - } else { - printf("\n"); - } - printf(" => mShareCount: % 10d\n", int(mShareCount)); - printf(" => mAdoptCount: % 10d\n", int(mAdoptCount)); - printf(" => mAdoptFreeCount: % 10d", int(mAdoptFreeCount)); - if (mAdoptCount > mAdoptFreeCount) { - printf(" -- LEAKED %d !!!\n", mAdoptCount - mAdoptFreeCount); - } else { - printf("\n"); - } - -#ifdef XP_WIN - auto pid = uintptr_t(_getpid()); - auto tid = uintptr_t(GetCurrentThreadId()); -#else - auto pid = uintptr_t(getpid()); - auto tid = uintptr_t(pthread_self()); -#endif - - printf(" => Process ID: %" PRIuPTR ", Thread ID: %" PRIuPTR "\n", pid, tid); -} diff --git a/xpcom/string/nsStringStats.h b/xpcom/string/nsStringStats.h deleted file mode 100644 index a38304c2b7..0000000000 --- a/xpcom/string/nsStringStats.h +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef nsStringStats_h -#define nsStringStats_h - -#include "mozilla/Atomics.h" - -class nsStringStats { - public: - nsStringStats() = default; - - ~nsStringStats(); - - using AtomicInt = mozilla::Atomic<int32_t, mozilla::SequentiallyConsistent>; - - AtomicInt mAllocCount{0}; - AtomicInt mReallocCount{0}; - AtomicInt mFreeCount{0}; - AtomicInt mShareCount{0}; - AtomicInt mAdoptCount{0}; - AtomicInt mAdoptFreeCount{0}; -}; - -extern nsStringStats gStringStats; - -#define STRING_STAT_INCREMENT(_s) (gStringStats.m##_s##Count)++ - -#endif // nsStringStats_h diff --git a/xpcom/string/nsTLiteralString.h b/xpcom/string/nsTLiteralString.h index 38ffd32bdb..0d14614583 100644 --- a/xpcom/string/nsTLiteralString.h +++ b/xpcom/string/nsTLiteralString.h @@ -8,6 +8,7 @@ #define nsTLiteralString_h #include "nsTStringRepr.h" +#include "mozilla/StaticString.h" /** * nsTLiteralString_CharT @@ -78,8 +79,10 @@ class nsTLiteralString : public mozilla::detail::nsTStringRepr<T> { * Prohibit get() on temporaries as in "x"_ns.get(). * These should be written as just "x", using a string literal directly. */ - const typename raw_type<T, int>::type get() const&& = delete; - const typename raw_type<T, int>::type get() const& { return this->mData; } + constexpr const typename raw_type<T, int>::type get() const&& = delete; + constexpr const typename raw_type<T, int>::type get() const& { + return this->mData; + } // At least older gcc versions do not accept these friend declarations, // complaining about an "invalid argument list" here, but not where the actual @@ -110,4 +113,9 @@ class nsTLiteralString : public mozilla::detail::nsTStringRepr<T> { extern template class nsTLiteralString<char>; extern template class nsTLiteralString<char16_t>; +namespace mozilla { +constexpr MOZ_IMPLICIT StaticString::StaticString(nsLiteralCString const& str) + : mStr(str.get()) {} +} // namespace mozilla + #endif diff --git a/xpcom/string/nsTSubstring.cpp b/xpcom/string/nsTSubstring.cpp index cff2031422..c89b6773d6 100644 --- a/xpcom/string/nsTSubstring.cpp +++ b/xpcom/string/nsTSubstring.cpp @@ -17,12 +17,6 @@ #include "nsString.h" #include "nsTArray.h" -#ifdef DEBUG -# include "nsStringStats.h" -#else -# define STRING_STAT_INCREMENT(_s) -#endif - // It's not worthwhile to reallocate the buffer and memcpy the // contents over when the size difference isn't large. With // power-of-two allocation buckets and 64 as the typical inline @@ -63,7 +57,6 @@ static void ReleaseData(void* aData, nsAString::DataFlags aFlags) { MOZ_LOG_DTOR(aData, "StringAdopt", 1); free(aData); - STRING_STAT_INCREMENT(AdoptFree); } // otherwise, nothing to do. } @@ -79,7 +72,6 @@ nsTSubstring<T>::nsTSubstring(char_type* aData, size_type aLength, AssertValid(); if (aDataFlags & DataFlags::OWNED) { - STRING_STAT_INCREMENT(Adopt); MOZ_LOG_CTOR(this->mData, "StringAdopt", 1); } } @@ -412,6 +404,17 @@ void nsTSubstring<T>::Assign(const char_type* aData, size_type aLength) { } template <typename T> +void nsTSubstring<T>::Assign(already_AddRefed<nsStringBuffer> aBuffer, + size_type aLength) { + nsStringBuffer* buffer = aBuffer.take(); + auto* data = reinterpret_cast<char_type*>(buffer->Data()); + MOZ_DIAGNOSTIC_ASSERT(data[aLength] == char_type(0), + "data should be null terminated"); + Finalize(); + SetData(data, aLength, DataFlags::REFCOUNTED | DataFlags::TERMINATED); +} + +template <typename T> bool nsTSubstring<T>::Assign(const char_type* aData, const fallible_t& aFallible) { return Assign(aData, size_type(-1), aFallible); @@ -631,7 +634,6 @@ void nsTSubstring<T>::Adopt(char_type* aData, size_type aLength) { SetData(aData, aLength, DataFlags::TERMINATED | DataFlags::OWNED); - STRING_STAT_INCREMENT(Adopt); // Treat this as construction of a "StringAdopt" object for leak // tracking purposes. MOZ_LOG_CTOR(this->mData, "StringAdopt", 1); diff --git a/xpcom/string/nsTSubstring.h b/xpcom/string/nsTSubstring.h index 622b931afb..7459333dc0 100644 --- a/xpcom/string/nsTSubstring.h +++ b/xpcom/string/nsTSubstring.h @@ -8,20 +8,17 @@ #ifndef nsTSubstring_h #define nsTSubstring_h -#include <iterator> #include <type_traits> -#include "mozilla/Casting.h" +#include "mozilla/Attributes.h" #include "mozilla/DebugOnly.h" -#include "mozilla/IntegerPrintfMacros.h" -#include "mozilla/UniquePtr.h" #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/IntegerTypeTraits.h" #include "mozilla/ResultExtensions.h" #include "mozilla/Span.h" #include "mozilla/Try.h" #include "mozilla/Unused.h" +#include "nsStringBuffer.h" #include "nsTStringRepr.h" @@ -422,6 +419,13 @@ class nsTSubstring : public mozilla::detail::nsTStringRepr<T> { [[nodiscard]] bool NS_FASTCALL Assign(const substring_tuple_type&, const fallible_t&); + void Assign(nsStringBuffer* aBuffer, size_type aLength) { + aBuffer->AddRef(); + Assign(already_AddRefed<nsStringBuffer>(aBuffer), aLength); + } + void NS_FASTCALL Assign(already_AddRefed<nsStringBuffer> aBuffer, + size_type aLength); + #if defined(MOZ_USE_CHAR16_WRAPPER) template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>> void Assign(char16ptr_t aData) { @@ -1142,11 +1146,22 @@ class nsTSubstring : public mozilla::detail::nsTStringRepr<T> { * clears the pointer without releasing the buffer. */ void ForgetSharedBuffer() { - if (base_string_type::mDataFlags & DataFlags::REFCOUNTED) { + if (this->mDataFlags & DataFlags::REFCOUNTED) { SetToEmptyBuffer(); } } + /** + * If the string uses a reference-counted buffer, this method returns a + * pointer to it without incrementing the buffer's refcount. + */ + nsStringBuffer* GetStringBuffer() const { + if (this->mDataFlags & DataFlags::REFCOUNTED) { + return nsStringBuffer::FromData(this->mData); + } + return nullptr; + } + protected: void AssertValid() { MOZ_DIAGNOSTIC_ASSERT(!(this->mClassFlags & ClassFlags::INVALID_MASK)); |