fune/dom/file/MemoryBlobImpl.cpp
Nika Layzell 4f556b1bea Bug 1748719 - Make nsStringInputStream more flexible as to the backing data buffer, r=mccr8
This change aims to make the way that the nsStringInputStream owns the backing
data buffer more flexible. This has a few impacts, including allowing
arbitrarily large payload sizes on 64-bit platforms, not requiring as complex
checks around borrowed string buffers or nsTArray data storage, and supporting
custom data ownership schemes such as those used by blobs.

The new system uses a separate refcounted object internally to provide the
contiguous backing buffer, with the nsStringInputStream wrapping it and
providing the `nsIInputStream` interface and cursor. This also avoids issues
around the buffer being mutated during reads, as mutating the `nsIInputStream`
no longer mutates the actual data storage object.

Differential Revision: https://phabricator.services.mozilla.com/D135162
2022-01-07 20:34:52 +00:00

170 lines
5.9 KiB
C++

/* -*- 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 "MemoryBlobImpl.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/SHA1.h"
#include "nsIMemoryReporter.h"
#include "nsMemory.h"
#include "nsPrintfCString.h"
#include "nsRFPService.h"
#include "nsStringStream.h"
#include "prtime.h"
namespace mozilla::dom {
// static
already_AddRefed<MemoryBlobImpl> MemoryBlobImpl::CreateWithCustomLastModified(
void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
const nsAString& aContentType, int64_t aLastModifiedDate) {
RefPtr<MemoryBlobImpl> blobImpl = new MemoryBlobImpl(
aMemoryBuffer, aLength, aName, aContentType, aLastModifiedDate);
return blobImpl.forget();
}
// static
already_AddRefed<MemoryBlobImpl> MemoryBlobImpl::CreateWithLastModifiedNow(
void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
const nsAString& aContentType, bool aCrossOriginIsolated) {
int64_t lastModificationDate = nsRFPService::ReduceTimePrecisionAsUSecs(
PR_Now(), 0,
/* aIsSystemPrincipal */ false, aCrossOriginIsolated);
return CreateWithCustomLastModified(aMemoryBuffer, aLength, aName,
aContentType, lastModificationDate);
}
nsresult MemoryBlobImpl::DataOwnerAdapter::Create(DataOwner* aDataOwner,
size_t aStart, size_t aLength,
nsIInputStream** _retval) {
MOZ_ASSERT(aDataOwner, "Uh ...");
Span data{static_cast<const char*>(aDataOwner->mData) + aStart, aLength};
RefPtr adapter = new MemoryBlobImpl::DataOwnerAdapter(aDataOwner, data);
return NS_NewByteInputStream(_retval, adapter);
}
already_AddRefed<BlobImpl> MemoryBlobImpl::CreateSlice(
uint64_t aStart, uint64_t aLength, const nsAString& aContentType,
ErrorResult& aRv) {
RefPtr<BlobImpl> impl =
new MemoryBlobImpl(this, aStart, aLength, aContentType);
return impl.forget();
}
void MemoryBlobImpl::CreateInputStream(nsIInputStream** aStream,
ErrorResult& aRv) {
if (mLength >= INT32_MAX) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
aRv = MemoryBlobImpl::DataOwnerAdapter::Create(mDataOwner, mStart, mLength,
aStream);
}
/* static */
StaticMutex MemoryBlobImpl::DataOwner::sDataOwnerMutex;
/* static */ StaticAutoPtr<LinkedList<MemoryBlobImpl::DataOwner>>
MemoryBlobImpl::DataOwner::sDataOwners;
/* static */
bool MemoryBlobImpl::DataOwner::sMemoryReporterRegistered = false;
MOZ_DEFINE_MALLOC_SIZE_OF(MemoryFileDataOwnerMallocSizeOf)
class MemoryBlobImplDataOwnerMemoryReporter final : public nsIMemoryReporter {
~MemoryBlobImplDataOwnerMemoryReporter() = default;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) override {
using DataOwner = MemoryBlobImpl::DataOwner;
StaticMutexAutoLock lock(DataOwner::sDataOwnerMutex);
if (!DataOwner::sDataOwners) {
return NS_OK;
}
const size_t LARGE_OBJECT_MIN_SIZE = 8 * 1024;
size_t smallObjectsTotal = 0;
for (DataOwner* owner = DataOwner::sDataOwners->getFirst(); owner;
owner = owner->getNext()) {
size_t size = MemoryFileDataOwnerMallocSizeOf(owner->mData);
if (size < LARGE_OBJECT_MIN_SIZE) {
smallObjectsTotal += size;
} else {
SHA1Sum sha1;
sha1.update(owner->mData, owner->mLength);
uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long.
sha1.finish(digest);
nsAutoCString digestString;
for (size_t i = 0; i < sizeof(digest); i++) {
digestString.AppendPrintf("%02x", digest[i]);
}
aHandleReport->Callback(
/* process */ ""_ns,
nsPrintfCString(
"explicit/dom/memory-file-data/large/file(length=%" PRIu64
", sha1=%s)",
owner->mLength,
aAnonymize ? "<anonymized>" : digestString.get()),
KIND_HEAP, UNITS_BYTES, size,
nsPrintfCString(
"Memory used to back a memory file of length %" PRIu64
" bytes. The file "
"has a sha1 of %s.\n\n"
"Note that the allocator may round up a memory file's length "
"-- "
"that is, an N-byte memory file may take up more than N bytes "
"of "
"memory.",
owner->mLength, digestString.get()),
aData);
}
}
if (smallObjectsTotal > 0) {
aHandleReport->Callback(
/* process */ ""_ns, "explicit/dom/memory-file-data/small"_ns,
KIND_HEAP, UNITS_BYTES, smallObjectsTotal,
nsPrintfCString(
"Memory used to back small memory files (i.e. those taking up "
"less "
"than %zu bytes of memory each).\n\n"
"Note that the allocator may round up a memory file's length -- "
"that is, an N-byte memory file may take up more than N bytes of "
"memory.",
LARGE_OBJECT_MIN_SIZE),
aData);
}
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(MemoryBlobImplDataOwnerMemoryReporter, nsIMemoryReporter)
/* static */
void MemoryBlobImpl::DataOwner::EnsureMemoryReporterRegistered() {
sDataOwnerMutex.AssertCurrentThreadOwns();
if (sMemoryReporterRegistered) {
return;
}
RegisterStrongMemoryReporter(new MemoryBlobImplDataOwnerMemoryReporter());
sMemoryReporterRegistered = true;
}
} // namespace mozilla::dom