diff options
Diffstat (limited to 'xpcom/string/nsSubstring.cpp')
-rw-r--r-- | xpcom/string/nsSubstring.cpp | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/xpcom/string/nsSubstring.cpp b/xpcom/string/nsSubstring.cpp new file mode 100644 index 000000000..5bc69f741 --- /dev/null +++ b/xpcom/string/nsSubstring.cpp @@ -0,0 +1,388 @@ +/* -*- 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 "nsSubstring.h" +#include "nsString.h" +#include "nsStringBuffer.h" +#include "nsDependentString.h" +#include "nsMemory.h" +#include "prprf.h" +#include "nsStaticAtom.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; + } + + 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())); + } + + Atomic<int32_t> mAllocCount; + Atomic<int32_t> mReallocCount; + Atomic<int32_t> mFreeCount; + Atomic<int32_t> mShareCount; + Atomic<int32_t> mAdoptCount; + Atomic<int32_t> mAdoptFreeCount; +}; +static nsStringStats gStringStats; +#define STRING_STAT_INCREMENT(_s) (gStringStats.m ## _s ## Count)++ +#else +#define STRING_STAT_INCREMENT(_s) +#endif + +// --------------------------------------------------------------------------- + +void +ReleaseData(void* aData, uint32_t aFlags) +{ + if (aFlags & nsSubstring::F_SHARED) { + nsStringBuffer::FromData(aData)->Release(); + } else if (aFlags & nsSubstring::F_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; + } + uint32_t flags() const + { + return mFlags; + } + + void set(char_type* aData, size_type aLen, uint32_t aFlags) + { + ReleaseData(mData, mFlags); + mData = aData; + mLength = aLen; + mFlags = aFlags; + } +}; + +class nsACStringAccessor : public nsACString +{ +private: + nsACStringAccessor(); // NOT IMPLEMENTED + +public: + char_type* data() const + { + return mData; + } + size_type length() const + { + return mLength; + } + uint32_t flags() const + { + return mFlags; + } + + void set(char_type* aData, size_type aLen, uint32_t aFlags) + { + ReleaseData(mData, mFlags); + mData = aData; + mLength = aLen; + mFlags = aFlags; + } +}; + +// --------------------------------------------------------------------------- + +void +nsStringBuffer::AddRef() +{ + ++mRefCount; + STRING_STAT_INCREMENT(Share); + NS_LOG_ADDREF(this, mRefCount, "nsStringBuffer", sizeof(*this)); +} + +void +nsStringBuffer::Release() +{ + int32_t count = --mRefCount; + NS_LOG_RELEASE(this, count, "nsStringBuffer"); + if (count == 0) { + 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() & nsSubstring::F_SHARED)) { + return nullptr; + } + + return FromData(accessor->data()); +} + +nsStringBuffer* +nsStringBuffer::FromString(const nsACString& aStr) +{ + const nsACStringAccessor* accessor = + static_cast<const nsACStringAccessor*>(&aStr); + + if (!(accessor->flags() & nsCSubstring::F_SHARED)) { + 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"); + + // preserve class flags + uint32_t flags = accessor->flags(); + flags = (flags & 0xFFFF0000) | nsSubstring::F_SHARED | nsSubstring::F_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"); + + // preserve class flags + uint32_t flags = accessor->flags(); + flags = (flags & 0xFFFF0000) | nsCSubstring::F_SHARED | nsCSubstring::F_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 nsSubstring +#include "string-template-def-unichar.h" +#include "nsTSubstring.cpp" +#include "string-template-undef.h" + +// define nsCSubstring +#include "string-template-def-char.h" +#include "nsTSubstring.cpp" +#include "string-template-undef.h" + +// Check that internal and external strings have the same size. +// See https://bugzilla.mozilla.org/show_bug.cgi?id=430581 + +#include "mozilla/Logging.h" +#include "nsXPCOMStrings.h" + +static_assert(sizeof(nsStringContainer_base) == sizeof(nsSubstring), + "internal and external strings must have the same size"); + +// Provide rust bindings to the nsA[C]String types +extern "C" { + +void Gecko_FinalizeCString(nsACString* aThis) +{ + aThis->~nsACString(); +} + +void Gecko_AssignCString(nsACString* aThis, const nsACString* aOther) +{ + aThis->Assign(*aOther); +} + +void Gecko_AppendCString(nsACString* aThis, const nsACString* aOther) +{ + aThis->Append(*aOther); +} + +void Gecko_FinalizeString(nsAString* aThis) +{ + aThis->~nsAString(); +} + +void Gecko_AssignString(nsAString* aThis, const nsAString* aOther) +{ + aThis->Assign(*aOther); +} + +void Gecko_AppendString(nsAString* aThis, const nsAString* aOther) +{ + aThis->Append(*aOther); +} + +} // extern "C" |