diff options
Diffstat (limited to 'xpcom/string/nsStringBuffer.cpp')
-rw-r--r-- | xpcom/string/nsStringBuffer.cpp | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/xpcom/string/nsStringBuffer.cpp b/xpcom/string/nsStringBuffer.cpp new file mode 100644 index 0000000000..b5d506333f --- /dev/null +++ b/xpcom/string/nsStringBuffer.cpp @@ -0,0 +1,186 @@ +/* -*- 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 "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); +} + +template <typename CharT> +static already_AddRefed<nsStringBuffer> DoCreate(const CharT* aData, + size_t aLength) { + RefPtr<nsStringBuffer> buffer = + nsStringBuffer::Alloc((aLength + 1) * sizeof(CharT)); + if (MOZ_UNLIKELY(!buffer)) { + return nullptr; + } + auto* data = reinterpret_cast<CharT*>(buffer->Data()); + memcpy(data, aData, aLength * sizeof(CharT)); + data[aLength] = 0; + return buffer.forget(); +} + +already_AddRefed<nsStringBuffer> nsStringBuffer::Create(const char* aData, + size_t aLength) { + return DoCreate(aData, aLength); +} + +already_AddRefed<nsStringBuffer> nsStringBuffer::Create(const char16_t* aData, + size_t aLength) { + return DoCreate(aData, aLength); +} + +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) { + 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); +} + +size_t nsStringBuffer::SizeOfIncludingThisEvenIfShared( + mozilla::MallocSizeOf aMallocSizeOf) const { + return aMallocSizeOf(this); +} |