summaryrefslogtreecommitdiffstats
path: root/xpcom/string/nsSubstring.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/string/nsSubstring.cpp')
-rw-r--r--xpcom/string/nsSubstring.cpp424
1 files changed, 424 insertions, 0 deletions
diff --git a/xpcom/string/nsSubstring.cpp b/xpcom/string/nsSubstring.cpp
new file mode 100644
index 0000000000..6c19463802
--- /dev/null
+++ b/xpcom/string/nsSubstring.cpp
@@ -0,0 +1,424 @@
+/* -*- 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/. */
+
+#ifdef DEBUG
+# define ENABLE_STRING_STATS
+#endif
+
+#include "mozilla/Atomics.h"
+#include "mozilla/MemoryReporting.h"
+
+#ifdef ENABLE_STRING_STATS
+# include <stdio.h>
+#endif
+
+#include <stdlib.h>
+#include "nsAString.h"
+#include "nsString.h"
+#include "nsStringBuffer.h"
+#include "nsDependentString.h"
+#include "nsMemory.h"
+#include "prprf.h"
+#include "nsCOMPtr.h"
+
+#include "mozilla/IntegerPrintfMacros.h"
+#ifdef XP_WIN
+# include <windows.h>
+# include <process.h>
+# define getpid() _getpid()
+# define pthread_self() GetCurrentThreadId()
+#else
+# include <pthread.h>
+# include <unistd.h>
+#endif
+
+using mozilla::Atomic;
+
+// ---------------------------------------------------------------------------
+
+static const char16_t gNullChar = 0;
+
+char* const nsCharTraits<char>::sEmptyBuffer =
+ (char*)const_cast<char16_t*>(&gNullChar);
+char16_t* const nsCharTraits<char16_t>::sEmptyBuffer =
+ const_cast<char16_t*>(&gNullChar);
+
+// ---------------------------------------------------------------------------
+
+#ifdef ENABLE_STRING_STATS
+class nsStringStats {
+ public:
+ nsStringStats()
+ : mAllocCount(0), mReallocCount(0), mFreeCount(0), mShareCount(0) {}
+
+ ~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");
+ }
+ printf(" => Process ID: %" PRIuPTR ", Thread ID: %" PRIuPTR "\n",
+ uintptr_t(getpid()), uintptr_t(pthread_self()));
+ }
+
+ typedef Atomic<int32_t, mozilla::SequentiallyConsistent> AtomicInt;
+
+ AtomicInt mAllocCount;
+ AtomicInt mReallocCount;
+ AtomicInt mFreeCount;
+ AtomicInt mShareCount;
+ AtomicInt mAdoptCount;
+ AtomicInt mAdoptFreeCount;
+};
+static nsStringStats gStringStats;
+# define STRING_STAT_INCREMENT(_s) (gStringStats.m##_s##Count)++
+#else
+# define STRING_STAT_INCREMENT(_s)
+#endif
+
+// ---------------------------------------------------------------------------
+
+void ReleaseData(void* aData, nsAString::DataFlags aFlags) {
+ if (aFlags & nsAString::DataFlags::REFCOUNTED) {
+ nsStringBuffer::FromData(aData)->Release();
+ } else if (aFlags & nsAString::DataFlags::OWNED) {
+ free(aData);
+ STRING_STAT_INCREMENT(AdoptFree);
+ // Treat this as destruction of a "StringAdopt" object for leak
+ // tracking purposes.
+ MOZ_LOG_DTOR(aData, "StringAdopt", 1);
+ }
+ // otherwise, nothing to do.
+}
+
+// ---------------------------------------------------------------------------
+
+// XXX or we could make nsStringBuffer be a friend of nsTAString
+
+class nsAStringAccessor : public nsAString {
+ private:
+ nsAStringAccessor(); // NOT IMPLEMENTED
+
+ public:
+ char_type* data() const { return mData; }
+ size_type length() const { return mLength; }
+ DataFlags flags() const { return mDataFlags; }
+
+ void set(char_type* aData, size_type aLen, DataFlags aDataFlags) {
+ ReleaseData(mData, mDataFlags);
+ SetData(aData, aLen, aDataFlags);
+ }
+};
+
+class nsACStringAccessor : public nsACString {
+ private:
+ nsACStringAccessor(); // NOT IMPLEMENTED
+
+ public:
+ char_type* data() const { return mData; }
+ size_type length() const { return mLength; }
+ DataFlags flags() const { return mDataFlags; }
+
+ void set(char_type* aData, size_type aLen, DataFlags aDataFlags) {
+ ReleaseData(mData, mDataFlags);
+ SetData(aData, aLen, aDataFlags);
+ }
+};
+
+// ---------------------------------------------------------------------------
+
+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");
+
+ nsStringBuffer* 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 dont_AddRef(hdr);
+}
+
+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");
+
+ // no point in trying to save ourselves if we hit this assertion
+ NS_ASSERTION(!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");
+
+ aHdr = (nsStringBuffer*)realloc(aHdr, sizeof(nsStringBuffer) + aSize);
+ if (aHdr) {
+ NS_LOG_ADDREF(aHdr, 1, "nsStringBuffer", sizeof(*aHdr));
+ aHdr->mStorageSize = aSize;
+ }
+
+ return aHdr;
+}
+
+nsStringBuffer* nsStringBuffer::FromString(const nsAString& aStr) {
+ const nsAStringAccessor* accessor =
+ static_cast<const nsAStringAccessor*>(&aStr);
+
+ if (!(accessor->flags() & nsAString::DataFlags::REFCOUNTED)) {
+ return nullptr;
+ }
+
+ return FromData(accessor->data());
+}
+
+nsStringBuffer* nsStringBuffer::FromString(const nsACString& aStr) {
+ const nsACStringAccessor* accessor =
+ static_cast<const nsACStringAccessor*>(&aStr);
+
+ if (!(accessor->flags() & nsACString::DataFlags::REFCOUNTED)) {
+ return nullptr;
+ }
+
+ return FromData(accessor->data());
+}
+
+void nsStringBuffer::ToString(uint32_t aLen, nsAString& aStr,
+ bool aMoveOwnership) {
+ char16_t* data = static_cast<char16_t*>(Data());
+
+ nsAStringAccessor* accessor = static_cast<nsAStringAccessor*>(&aStr);
+ 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();
+ }
+ accessor->set(data, aLen, flags);
+}
+
+void nsStringBuffer::ToString(uint32_t aLen, nsACString& aStr,
+ bool aMoveOwnership) {
+ char* data = static_cast<char*>(Data());
+
+ nsACStringAccessor* accessor = static_cast<nsACStringAccessor*>(&aStr);
+ MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char(0),
+ "data should be null terminated");
+
+ nsACString::DataFlags flags =
+ nsACString::DataFlags::REFCOUNTED | nsACString::DataFlags::TERMINATED;
+
+ if (!aMoveOwnership) {
+ AddRef();
+ }
+ accessor->set(data, aLen, flags);
+}
+
+size_t nsStringBuffer::SizeOfIncludingThisIfUnshared(
+ mozilla::MallocSizeOf aMallocSizeOf) const {
+ return IsReadonly() ? 0 : aMallocSizeOf(this);
+}
+
+size_t nsStringBuffer::SizeOfIncludingThisEvenIfShared(
+ mozilla::MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(this);
+}
+
+// ---------------------------------------------------------------------------
+
+// define nsAString
+#include "nsTSubstring.cpp"
+
+// Provide rust bindings to the nsA[C]String types
+extern "C" {
+
+// This is a no-op on release, so we ifdef it out such that using it in release
+// results in a linker error.
+#ifdef DEBUG
+void Gecko_IncrementStringAdoptCount(void* aData) {
+ MOZ_LOG_CTOR(aData, "StringAdopt", 1);
+}
+#elif defined(MOZ_DEBUG_RUST)
+void Gecko_IncrementStringAdoptCount(void* aData) {}
+#endif
+
+void Gecko_FinalizeCString(nsACString* aThis) { aThis->~nsACString(); }
+
+void Gecko_AssignCString(nsACString* aThis, const nsACString* aOther) {
+ aThis->Assign(*aOther);
+}
+
+void Gecko_TakeFromCString(nsACString* aThis, nsACString* aOther) {
+ aThis->Assign(std::move(*aOther));
+}
+
+void Gecko_AppendCString(nsACString* aThis, const nsACString* aOther) {
+ aThis->Append(*aOther);
+}
+
+void Gecko_SetLengthCString(nsACString* aThis, uint32_t aLength) {
+ aThis->SetLength(aLength);
+}
+
+bool Gecko_FallibleAssignCString(nsACString* aThis, const nsACString* aOther) {
+ return aThis->Assign(*aOther, mozilla::fallible);
+}
+
+bool Gecko_FallibleTakeFromCString(nsACString* aThis, nsACString* aOther) {
+ return aThis->Assign(std::move(*aOther), mozilla::fallible);
+}
+
+bool Gecko_FallibleAppendCString(nsACString* aThis, const nsACString* aOther) {
+ return aThis->Append(*aOther, mozilla::fallible);
+}
+
+bool Gecko_FallibleSetLengthCString(nsACString* aThis, uint32_t aLength) {
+ return aThis->SetLength(aLength, mozilla::fallible);
+}
+
+char* Gecko_BeginWritingCString(nsACString* aThis) {
+ return aThis->BeginWriting();
+}
+
+char* Gecko_FallibleBeginWritingCString(nsACString* aThis) {
+ return aThis->BeginWriting(mozilla::fallible);
+}
+
+uint32_t Gecko_StartBulkWriteCString(nsACString* aThis, uint32_t aCapacity,
+ uint32_t aUnitsToPreserve,
+ bool aAllowShrinking) {
+ return aThis->StartBulkWriteImpl(aCapacity, aUnitsToPreserve, aAllowShrinking)
+ .unwrapOr(UINT32_MAX);
+}
+
+void Gecko_FinalizeString(nsAString* aThis) { aThis->~nsAString(); }
+
+void Gecko_AssignString(nsAString* aThis, const nsAString* aOther) {
+ aThis->Assign(*aOther);
+}
+
+void Gecko_TakeFromString(nsAString* aThis, nsAString* aOther) {
+ aThis->Assign(std::move(*aOther));
+}
+
+void Gecko_AppendString(nsAString* aThis, const nsAString* aOther) {
+ aThis->Append(*aOther);
+}
+
+void Gecko_SetLengthString(nsAString* aThis, uint32_t aLength) {
+ aThis->SetLength(aLength);
+}
+
+bool Gecko_FallibleAssignString(nsAString* aThis, const nsAString* aOther) {
+ return aThis->Assign(*aOther, mozilla::fallible);
+}
+
+bool Gecko_FallibleTakeFromString(nsAString* aThis, nsAString* aOther) {
+ return aThis->Assign(std::move(*aOther), mozilla::fallible);
+}
+
+bool Gecko_FallibleAppendString(nsAString* aThis, const nsAString* aOther) {
+ return aThis->Append(*aOther, mozilla::fallible);
+}
+
+bool Gecko_FallibleSetLengthString(nsAString* aThis, uint32_t aLength) {
+ return aThis->SetLength(aLength, mozilla::fallible);
+}
+
+char16_t* Gecko_BeginWritingString(nsAString* aThis) {
+ return aThis->BeginWriting();
+}
+
+char16_t* Gecko_FallibleBeginWritingString(nsAString* aThis) {
+ return aThis->BeginWriting(mozilla::fallible);
+}
+
+uint32_t Gecko_StartBulkWriteString(nsAString* aThis, uint32_t aCapacity,
+ uint32_t aUnitsToPreserve,
+ bool aAllowShrinking) {
+ return aThis->StartBulkWriteImpl(aCapacity, aUnitsToPreserve, aAllowShrinking)
+ .unwrapOr(UINT32_MAX);
+}
+
+} // extern "C"