mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 05:37:11 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1262671 - IPC sentinel checking (r=froydnj) (2bd39988fe) - Bug 1262671 - IPC ReadData/ReadBytes elimination (r=froydnj) (85b47baffd) - Bug 1272415: Don't include task.h everywhere. r=froydnj (6198aedfed) - Bug 1273312 - Add task.h to ipdl unit tests (r=khuey) (42b8d3bb90) - Bug 1268616 - Part 1: Check max message size before resizing. r=billm (6889a43e16) - Bug 1268616 - Part 2: Stop sending messages that are too large. r=billm (a68e6624be) - Bug 1268616 - Part 3: Reduce the maxmimum IPC message size. r=billm (581076632e) - Bug 1262671 - Use BufferList for Pickle (r=froydnj) (f61d8b233d) - Bug 1262671 - Introduce MFBT BufferList class (r=froydnj) (cb1aca1708) - mfbt: BufferList: VC2013 fix (93da0d98f7)
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
#include "mozilla/dom/ipc/BlobChild.h"
|
||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "base/task.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
using namespace mozilla::jsipc;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "base/task.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
using namespace mozilla::jsipc;
|
||||
|
||||
@@ -98,8 +98,7 @@ StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const
|
||||
WriteParam(aMsg, DataLength());
|
||||
|
||||
if (DataLength()) {
|
||||
// Structured clone data must be 64-bit aligned.
|
||||
aMsg->WriteBytes(Data(), DataLength(), sizeof(uint64_t));
|
||||
aMsg->WriteBytes(Data(), DataLength());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,18 +117,14 @@ StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg,
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t* dataBuffer = nullptr;
|
||||
const char** buffer =
|
||||
const_cast<const char**>(reinterpret_cast<char**>(&dataBuffer));
|
||||
// Structured clone data must be 64-bit aligned.
|
||||
if (!aMsg->ReadBytes(aIter, buffer, dataLength, sizeof(uint64_t))) {
|
||||
mSharedData = SharedJSAllocatedData::AllocateForExternalData(dataLength);
|
||||
NS_ENSURE_TRUE(mSharedData, false);
|
||||
|
||||
if (!aMsg->ReadBytesInto(aIter, mSharedData->Data(), dataLength)) {
|
||||
mSharedData = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
mSharedData = SharedJSAllocatedData::CreateFromExternalData(dataBuffer,
|
||||
dataLength);
|
||||
NS_ENSURE_TRUE(mSharedData, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,19 +31,27 @@ public:
|
||||
}
|
||||
|
||||
static already_AddRefed<SharedJSAllocatedData>
|
||||
CreateFromExternalData(const void* aData, size_t aDataLength)
|
||||
AllocateForExternalData(size_t aDataLength)
|
||||
{
|
||||
uint64_t* data = Allocate64bitSafely(aDataLength);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memcpy(data, aData, aDataLength);
|
||||
RefPtr<SharedJSAllocatedData> sharedData =
|
||||
new SharedJSAllocatedData(data, aDataLength);
|
||||
return sharedData.forget();
|
||||
}
|
||||
|
||||
static already_AddRefed<SharedJSAllocatedData>
|
||||
CreateFromExternalData(const void* aData, size_t aDataLength)
|
||||
{
|
||||
RefPtr<SharedJSAllocatedData> sharedData =
|
||||
AllocateForExternalData(aDataLength);
|
||||
memcpy(sharedData->Data(), aData, aDataLength);
|
||||
return sharedData.forget();
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(SharedJSAllocatedData)
|
||||
|
||||
uint64_t* Data() const { return mData; }
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <fstream>
|
||||
#include "GMPUtils.h"
|
||||
#include "prio.h"
|
||||
#include "base/task.h"
|
||||
#ifdef MOZ_WIDEVINE_EME
|
||||
#include "widevine-adapter/WidevineAdapter.h"
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "GMPDecryptorChild.h"
|
||||
#include "GMPVideoDecoderChild.h"
|
||||
#include "GMPVideoEncoderChild.h"
|
||||
#include "base/task.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "base/task.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "GMPDecryptorChild.h"
|
||||
#include "GMPContentChild.h"
|
||||
#include "GMPChild.h"
|
||||
#include "base/task.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "runnable_utils.h"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "nsXPCOMPrivate.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "runnable_utils.h"
|
||||
#include "base/task.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "GMPServiceParent.h"
|
||||
#include "GMPService.h"
|
||||
#include "prio.h"
|
||||
#include "base/task.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "GMPParent.h"
|
||||
#include "GMPVideoDecoderParent.h"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "GMPStorageChild.h"
|
||||
#include "GMPChild.h"
|
||||
#include "gmp-storage.h"
|
||||
#include "base/task.h"
|
||||
|
||||
#define ON_GMP_THREAD() (mPlugin->GMPMessageLoop() == MessageLoop::current())
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "mozilla/plugins/PBrowserStreamChild.h"
|
||||
#include "mozilla/plugins/AStream.h"
|
||||
#include "base/task.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
@@ -39,14 +39,7 @@ struct ParamTraits<mozilla::plugins::NPRemoteEvent>
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
const char* bytes = 0;
|
||||
|
||||
if (!aMsg->ReadBytes(aIter, &bytes, sizeof(paramType))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(aResult, bytes, sizeof(paramType));
|
||||
return true;
|
||||
return aMsg->ReadBytesInto(aIter, aResult, sizeof(paramType));
|
||||
}
|
||||
|
||||
static void Log(const paramType& aParam, std::wstring* aLog)
|
||||
|
||||
@@ -57,14 +57,10 @@ struct ParamTraits<mozilla::plugins::NPRemoteEvent> // synonym for XEvent
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
const char* bytes = 0;
|
||||
|
||||
if (!aMsg->ReadBytes(aIter, &bytes, sizeof(paramType))) {
|
||||
if (!aMsg->ReadBytesInto(aIter, aResult, sizeof(paramType))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(aResult, bytes, sizeof(paramType));
|
||||
|
||||
#ifdef MOZ_X11
|
||||
SetXDisplay(aResult->event);
|
||||
#endif
|
||||
|
||||
@@ -132,12 +132,9 @@ struct ParamTraits<mozilla::plugins::NPRemoteEvent>
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
const char* bytes = 0;
|
||||
|
||||
if (!aMsg->ReadBytes(aIter, &bytes, sizeof(paramType))) {
|
||||
if (!aMsg->ReadBytesInto(aIter, aResult, sizeof(paramType))) {
|
||||
return false;
|
||||
}
|
||||
memcpy(aResult, bytes, sizeof(paramType));
|
||||
|
||||
if (aResult->event.event == WM_PAINT) {
|
||||
// restore the lParam to point at the RECT
|
||||
|
||||
@@ -459,15 +459,19 @@ struct ParamTraits<NPNSString*>
|
||||
return false;
|
||||
}
|
||||
|
||||
UniChar* buffer = nullptr;
|
||||
// Avoid integer multiplication overflow.
|
||||
if (length > INT_MAX / static_cast<long>(sizeof(UniChar))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto chars = mozilla::MakeUnique<UniChar[]>(length);
|
||||
if (length != 0) {
|
||||
if (!aMsg->ReadBytes(aIter, (const char**)&buffer, length * sizeof(UniChar)) ||
|
||||
!buffer) {
|
||||
if (!aMsg->ReadBytesInto(aIter, chars.get(), length * sizeof(UniChar))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*aResult = (NPNSString*)::CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)buffer,
|
||||
*aResult = (NPNSString*)::CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)chars.get(),
|
||||
length * sizeof(UniChar),
|
||||
kCFStringEncodingUTF16, false);
|
||||
if (!*aResult) {
|
||||
@@ -525,16 +529,16 @@ struct ParamTraits<NSCursorInfo>
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* data = nullptr;
|
||||
auto data = mozilla::MakeUnique<uint8_t[]>(dataLength);
|
||||
if (dataLength != 0) {
|
||||
if (!aMsg->ReadBytes(aIter, (const char**)&data, dataLength) || !data) {
|
||||
if (!aMsg->ReadBytesInto(aIter, data.get(), dataLength)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
aResult->SetType(type);
|
||||
aResult->SetHotSpot(nsPoint(hotSpotX, hotSpotY));
|
||||
aResult->SetCustomImageData(data, dataLength);
|
||||
aResult->SetCustomImageData(data.get(), dataLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -56,11 +56,9 @@ bool
|
||||
ParamTraits<DxgiAdapterDesc>::Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
#if defined(XP_WIN)
|
||||
const char* description = nullptr;
|
||||
if (!aMsg->ReadBytes(aIter, &description, sizeof(aResult->Description))) {
|
||||
if (!aMsg->ReadBytesInto(aIter, aResult->Description, sizeof(aResult->Description))) {
|
||||
return false;
|
||||
}
|
||||
memcpy(aResult->Description, description, sizeof(aResult->Description));
|
||||
|
||||
if (ReadParam(aMsg, aIter, &aResult->VendorId) &&
|
||||
ReadParam(aMsg, aIter, &aResult->DeviceId) &&
|
||||
|
||||
@@ -9,7 +9,6 @@ include(libevent_path_prefix + '/libeventcommon.mozbuild')
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'src/base/at_exit.cc',
|
||||
'src/base/buffer.cc',
|
||||
'src/base/command_line.cc',
|
||||
'src/base/file_path.cc',
|
||||
'src/base/file_util.cc',
|
||||
|
||||
@@ -1,130 +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 "buffer.h"
|
||||
#include "nsDebug.h"
|
||||
|
||||
Buffer::Buffer()
|
||||
: mBuffer(nullptr),
|
||||
mSize(0),
|
||||
mReserved(0)
|
||||
{
|
||||
}
|
||||
|
||||
Buffer::~Buffer()
|
||||
{
|
||||
if (mBuffer) {
|
||||
free(mBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Buffer::empty() const
|
||||
{
|
||||
return mSize == 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
Buffer::size() const
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
const char*
|
||||
Buffer::data() const
|
||||
{
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::clear()
|
||||
{
|
||||
free(mBuffer);
|
||||
mBuffer = nullptr;
|
||||
mSize = 0;
|
||||
mReserved = 0;
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::try_realloc(size_t newlength)
|
||||
{
|
||||
char* buffer = (char*)realloc(mBuffer, newlength);
|
||||
if (buffer || !newlength) {
|
||||
mBuffer = buffer;
|
||||
mReserved = newlength;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're growing the buffer, crash. If we're shrinking, then we continue to
|
||||
// use the old (larger) buffer.
|
||||
if (newlength > mReserved) {
|
||||
NS_ABORT_OOM(newlength);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::append(const char* bytes, size_t length)
|
||||
{
|
||||
if (mSize + length > mReserved) {
|
||||
try_realloc(mSize + length);
|
||||
}
|
||||
|
||||
memcpy(mBuffer + mSize, bytes, length);
|
||||
mSize += length;
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::assign(const char* bytes, size_t length)
|
||||
{
|
||||
if (bytes >= mBuffer && bytes < mBuffer + mReserved) {
|
||||
MOZ_RELEASE_ASSERT(bytes + length <= mBuffer + mSize);
|
||||
memmove(mBuffer, bytes, length);
|
||||
mSize = length;
|
||||
try_realloc(length);
|
||||
} else {
|
||||
try_realloc(length);
|
||||
mSize = length;
|
||||
memcpy(mBuffer, bytes, length);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::erase(size_t start, size_t count)
|
||||
{
|
||||
mSize -= count;
|
||||
memmove(mBuffer + start, mBuffer + start + count, mSize - start);
|
||||
try_realloc(mSize);
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::reserve(size_t size)
|
||||
{
|
||||
if (mReserved < size) {
|
||||
try_realloc(size);
|
||||
}
|
||||
}
|
||||
|
||||
char*
|
||||
Buffer::trade_bytes(size_t count)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(count);
|
||||
|
||||
char* result = mBuffer;
|
||||
mSize = mReserved = mSize - count;
|
||||
mBuffer = mReserved ? (char*)malloc(mReserved) : nullptr;
|
||||
MOZ_RELEASE_ASSERT(!mReserved || mBuffer);
|
||||
if (mSize) {
|
||||
memcpy(mBuffer, result + count, mSize);
|
||||
}
|
||||
|
||||
// Try to resize the buffer down, but ignore failure. This can cause extra
|
||||
// copies, but so be it.
|
||||
char* resized = (char*)realloc(result, count);
|
||||
if (resized) {
|
||||
return resized;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1,44 +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 CHROME_BASE_BUFFER_H_
|
||||
#define CHROME_BASE_BUFFER_H_
|
||||
|
||||
// Buffer is a simple std::string-like class for buffering up IPC messages. Its
|
||||
// main distinguishing characteristic is the trade_bytes function.
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer();
|
||||
~Buffer();
|
||||
|
||||
bool empty() const;
|
||||
const char* data() const;
|
||||
size_t size() const;
|
||||
|
||||
void clear();
|
||||
void append(const char* bytes, size_t length);
|
||||
void assign(const char* bytes, size_t length);
|
||||
void erase(size_t start, size_t count);
|
||||
|
||||
void reserve(size_t size);
|
||||
|
||||
// This function should be used by a caller who wants to extract the first
|
||||
// |count| bytes from the buffer. Rather than copying the bytes out, this
|
||||
// function returns the entire buffer. The bytes in range [count, size()) are
|
||||
// copied out to a new buffer which becomes the current buffer. The
|
||||
// presumption is that |count| is very large and approximately equal to size()
|
||||
// so not much needs to be copied.
|
||||
char* trade_bytes(size_t count);
|
||||
|
||||
private:
|
||||
void try_realloc(size_t newlength);
|
||||
|
||||
char* mBuffer;
|
||||
size_t mSize;
|
||||
size_t mReserved;
|
||||
};
|
||||
|
||||
#endif // CHROME_BASE_BUFFER_H_
|
||||
@@ -240,99 +240,6 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
|
||||
DCHECK_EQ(sample_count, past);
|
||||
}
|
||||
|
||||
// static
|
||||
std::string Histogram::SerializeHistogramInfo(const Histogram& histogram,
|
||||
const SampleSet& snapshot) {
|
||||
DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type());
|
||||
|
||||
Pickle pickle;
|
||||
pickle.WriteString(histogram.histogram_name());
|
||||
pickle.WriteInt(histogram.declared_min());
|
||||
pickle.WriteInt(histogram.declared_max());
|
||||
pickle.WriteSize(histogram.bucket_count());
|
||||
pickle.WriteUInt32(histogram.range_checksum());
|
||||
pickle.WriteInt(histogram.histogram_type());
|
||||
pickle.WriteInt(histogram.flags());
|
||||
|
||||
snapshot.Serialize(&pickle);
|
||||
return std::string(static_cast<const char*>(pickle.data()), pickle.size());
|
||||
}
|
||||
|
||||
// static
|
||||
bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) {
|
||||
if (histogram_info.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Pickle pickle(histogram_info.data(),
|
||||
static_cast<int>(histogram_info.size()));
|
||||
std::string histogram_name;
|
||||
int declared_min;
|
||||
int declared_max;
|
||||
size_t bucket_count;
|
||||
uint32_t range_checksum;
|
||||
int histogram_type;
|
||||
int pickle_flags;
|
||||
SampleSet sample;
|
||||
|
||||
PickleIterator iter(pickle);
|
||||
if (!pickle.ReadString(&iter, &histogram_name) ||
|
||||
!pickle.ReadInt(&iter, &declared_min) ||
|
||||
!pickle.ReadInt(&iter, &declared_max) ||
|
||||
!pickle.ReadSize(&iter, &bucket_count) ||
|
||||
!pickle.ReadUInt32(&iter, &range_checksum) ||
|
||||
!pickle.ReadInt(&iter, &histogram_type) ||
|
||||
!pickle.ReadInt(&iter, &pickle_flags) ||
|
||||
!sample.Histogram::SampleSet::Deserialize(&iter, pickle)) {
|
||||
CHROMIUM_LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name;
|
||||
return false;
|
||||
}
|
||||
DCHECK(pickle_flags & kIPCSerializationSourceFlag);
|
||||
// Since these fields may have come from an untrusted renderer, do additional
|
||||
// checks above and beyond those in Histogram::Initialize()
|
||||
if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min ||
|
||||
INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) {
|
||||
CHROMIUM_LOG(ERROR) << "Values error decoding Histogram: " << histogram_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag);
|
||||
|
||||
DCHECK_NE(NOT_VALID_IN_RENDERER, histogram_type);
|
||||
|
||||
Histogram* render_histogram(NULL);
|
||||
|
||||
if (histogram_type == HISTOGRAM) {
|
||||
render_histogram = Histogram::FactoryGet(
|
||||
histogram_name, declared_min, declared_max, bucket_count, flags);
|
||||
} else if (histogram_type == LINEAR_HISTOGRAM) {
|
||||
render_histogram = LinearHistogram::FactoryGet(
|
||||
histogram_name, declared_min, declared_max, bucket_count, flags);
|
||||
} else if (histogram_type == BOOLEAN_HISTOGRAM) {
|
||||
render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags);
|
||||
} else {
|
||||
CHROMIUM_LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: "
|
||||
<< histogram_type;
|
||||
return false;
|
||||
}
|
||||
|
||||
DCHECK_EQ(render_histogram->declared_min(), declared_min);
|
||||
DCHECK_EQ(render_histogram->declared_max(), declared_max);
|
||||
DCHECK_EQ(render_histogram->bucket_count(), bucket_count);
|
||||
DCHECK_EQ(render_histogram->range_checksum(), range_checksum);
|
||||
DCHECK_EQ(render_histogram->histogram_type(), histogram_type);
|
||||
|
||||
if (render_histogram->flags() & kIPCSerializationSourceFlag) {
|
||||
DVLOG(1) << "Single process mode, histogram observed and not copied: "
|
||||
<< histogram_name;
|
||||
} else {
|
||||
DCHECK_EQ(flags & render_histogram->flags(), flags);
|
||||
render_histogram->AddSampleSet(sample);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Methods for the validating a sample and a related histogram.
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -773,48 +680,6 @@ void Histogram::SampleSet::Add(const SampleSet& other) {
|
||||
counts_[index] += other.counts_[index];
|
||||
}
|
||||
|
||||
bool Histogram::SampleSet::Serialize(Pickle* pickle) const {
|
||||
OffTheBooksMutexAutoLock locker(mutex_);
|
||||
pickle->WriteInt64(sum_);
|
||||
pickle->WriteInt64(redundant_count_);
|
||||
pickle->WriteSize(counts_.size());
|
||||
|
||||
for (size_t index = 0; index < counts_.size(); ++index) {
|
||||
pickle->WriteInt(counts_[index]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Histogram::SampleSet::Deserialize(PickleIterator* iter, const Pickle& pickle) {
|
||||
OffTheBooksMutexAutoLock locker(mutex_);
|
||||
DCHECK_EQ(counts_.size(), 0u);
|
||||
DCHECK_EQ(sum_, 0);
|
||||
DCHECK_EQ(redundant_count_, 0);
|
||||
|
||||
size_t counts_size;
|
||||
|
||||
if (!pickle.ReadInt64(iter, &sum_) ||
|
||||
!pickle.ReadInt64(iter, &redundant_count_) ||
|
||||
!pickle.ReadSize(iter, &counts_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (counts_size == 0)
|
||||
return false;
|
||||
|
||||
int count = 0;
|
||||
for (size_t index = 0; index < counts_size; ++index) {
|
||||
int i;
|
||||
if (!pickle.ReadInt(iter, &i))
|
||||
return false;
|
||||
counts_.push_back(i);
|
||||
count += i;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// LinearHistogram: This histogram uses a traditional set of evenly spaced
|
||||
// buckets.
|
||||
|
||||
@@ -54,9 +54,6 @@
|
||||
#include "base/time.h"
|
||||
#include "base/lock.h"
|
||||
|
||||
class Pickle;
|
||||
class PickleIterator;
|
||||
|
||||
namespace base {
|
||||
|
||||
using mozilla::OffTheBooksMutex;
|
||||
@@ -302,13 +299,6 @@ class Histogram {
|
||||
kNoFlags = 0,
|
||||
kUmaTargetedHistogramFlag = 0x1, // Histogram should be UMA uploaded.
|
||||
|
||||
// Indicate that the histogram was pickled to be sent across an IPC Channel.
|
||||
// If we observe this flag on a histogram being aggregated into after IPC,
|
||||
// then we are running in a single process mode, and the aggregation should
|
||||
// not take place (as we would be aggregating back into the source
|
||||
// histogram!).
|
||||
kIPCSerializationSourceFlag = 0x10,
|
||||
|
||||
kHexRangePrintingFlag = 0x8000 // Fancy bucket-naming supported.
|
||||
};
|
||||
|
||||
@@ -361,9 +351,6 @@ class Histogram {
|
||||
// Arithmetic manipulation of corresponding elements of the set.
|
||||
void Add(const SampleSet& other);
|
||||
|
||||
bool Serialize(Pickle* pickle) const;
|
||||
bool Deserialize(PickleIterator* iter, const Pickle& pickle);
|
||||
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
//---------------- THREAD UNSAFE METHODS ----------------//
|
||||
@@ -474,21 +461,6 @@ class Histogram {
|
||||
void ClearFlags(Flags flags) { flags_ = static_cast<Flags>(flags_ & ~flags); }
|
||||
int flags() const { return flags_; }
|
||||
|
||||
// Convenience methods for serializing/deserializing the histograms.
|
||||
// Histograms from Renderer process are serialized and sent to the browser.
|
||||
// Browser process reconstructs the histogram from the pickled version
|
||||
// accumulates the browser-side shadow copy of histograms (that mirror
|
||||
// histograms created in the renderer).
|
||||
|
||||
// Serialize the given snapshot of a Histogram into a String. Uses
|
||||
// Pickle class to flatten the object.
|
||||
static std::string SerializeHistogramInfo(const Histogram& histogram,
|
||||
const SampleSet& snapshot);
|
||||
// The following method accepts a list of pickled histograms and
|
||||
// builds a histogram and updates shadow copy of histogram data in the
|
||||
// browser process.
|
||||
static bool DeserializeHistogramInfo(const std::string& histogram_info);
|
||||
|
||||
// Check to see if bucket ranges, counts and tallies in the snapshot are
|
||||
// consistent with the bucket ranges and checksums in our histogram. This can
|
||||
// produce a false-alarm if a race occurred in the reading of the data during
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
#include "base/lock.h"
|
||||
#include "base/message_pump.h"
|
||||
#include "base/observer_list.h"
|
||||
#include "base/task.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// We need this to declare base::MessagePumpWin::Dispatcher, which we should
|
||||
@@ -28,6 +26,7 @@
|
||||
#endif
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
class nsIThread;
|
||||
|
||||
|
||||
+217
-242
@@ -7,6 +7,7 @@
|
||||
#include "base/pickle.h"
|
||||
|
||||
#include "mozilla/Alignment.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/Endian.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
@@ -18,18 +19,18 @@
|
||||
|
||||
#include "nsDebug.h"
|
||||
|
||||
#if !defined(RELEASE_BUILD) || defined(DEBUG)
|
||||
#define SENTINEL_CHECKING
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static_assert(MOZ_ALIGNOF(Pickle::memberAlignmentType) >= MOZ_ALIGNOF(uint32_t),
|
||||
"Insufficient alignment");
|
||||
|
||||
// static
|
||||
const int Pickle::kPayloadUnit = 64;
|
||||
static const uint32_t kHeaderSegmentCapacity = 64;
|
||||
|
||||
const uint32_t kFastGrowthCap = 128 * 1024;
|
||||
|
||||
// We mark a read only pickle with a special capacity_.
|
||||
static const uint32_t kCapacityReadOnly = (uint32_t) -1;
|
||||
static const uint32_t kDefaultSegmentCapacity = 4096;
|
||||
|
||||
static const char kBytePaddingMarker = char(0xbf);
|
||||
|
||||
@@ -48,8 +49,8 @@ namespace {
|
||||
template<typename T, size_t size, bool hasSufficientAlignment>
|
||||
struct Copier
|
||||
{
|
||||
static void Copy(T* dest, void** iter) {
|
||||
memcpy(dest, *iter, sizeof(T));
|
||||
static void Copy(T* dest, const char* iter) {
|
||||
memcpy(dest, iter, sizeof(T));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -61,7 +62,7 @@ struct Copier
|
||||
template<typename T>
|
||||
struct Copier<T, sizeof(uint64_t), false>
|
||||
{
|
||||
static void Copy(T* dest, void** iter) {
|
||||
static void Copy(T* dest, const char* iter) {
|
||||
#if MOZ_LITTLE_ENDIAN
|
||||
static const int loIndex = 0, hiIndex = 1;
|
||||
#else
|
||||
@@ -69,7 +70,7 @@ struct Copier<T, sizeof(uint64_t), false>
|
||||
#endif
|
||||
static_assert(MOZ_ALIGNOF(uint32_t*) == MOZ_ALIGNOF(void*),
|
||||
"Pointers have different alignments");
|
||||
uint32_t* src = *reinterpret_cast<uint32_t**>(iter);
|
||||
const uint32_t* src = reinterpret_cast<const uint32_t*>(iter);
|
||||
uint32_t* uint32dest = reinterpret_cast<uint32_t*>(dest);
|
||||
uint32dest[loIndex] = src[loIndex];
|
||||
uint32dest[hiIndex] = src[hiIndex];
|
||||
@@ -80,96 +81,81 @@ struct Copier<T, sizeof(uint64_t), false>
|
||||
template<typename T, size_t size>
|
||||
struct Copier<T, size, true>
|
||||
{
|
||||
static void Copy(T* dest, void** iter) {
|
||||
// The reinterpret_cast is only safe if two conditions hold:
|
||||
// (1) If the alignment of T* is the same as void*;
|
||||
// (2) The alignment of the data in *iter is at least as
|
||||
// big as MOZ_ALIGNOF(T).
|
||||
// Check the first condition, as the second condition is already
|
||||
// known to be true, or we wouldn't be here.
|
||||
static_assert(MOZ_ALIGNOF(T*) == MOZ_ALIGNOF(void*),
|
||||
"Pointers have different alignments");
|
||||
*dest = *(*reinterpret_cast<T**>(iter));
|
||||
static void Copy(T* dest, const char* iter) {
|
||||
*dest = *reinterpret_cast<const T*>(iter);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
PickleIterator::CopyFrom(T* dest) {
|
||||
static_assert(mozilla::IsPod<T>::value, "Copied type must be a POD type");
|
||||
Copier<T, sizeof(T), (MOZ_ALIGNOF(T) <= sizeof(Pickle::memberAlignmentType))>::Copy(dest, &iter_);
|
||||
PickleIterator::PickleIterator(const Pickle& pickle)
|
||||
: iter_(pickle.buffers_.Iter()) {
|
||||
iter_.Advance(pickle.buffers_, pickle.header_size_);
|
||||
}
|
||||
|
||||
PickleIterator::PickleIterator(const Pickle& pickle)
|
||||
: iter_(const_cast<char*>(pickle.payload())) {
|
||||
template<typename T>
|
||||
void
|
||||
PickleIterator::CopyInto(T* dest) {
|
||||
static_assert(mozilla::IsPod<T>::value, "Copied type must be a POD type");
|
||||
Copier<T, sizeof(T), (MOZ_ALIGNOF(T) <= sizeof(Pickle::memberAlignmentType))>::Copy(dest, iter_.Data());
|
||||
}
|
||||
|
||||
bool Pickle::IteratorHasRoomFor(const PickleIterator& iter, uint32_t len) const {
|
||||
// Make sure we don't get into trouble where AlignInt(len) == 0.
|
||||
MOZ_RELEASE_ASSERT(len < 64);
|
||||
|
||||
return iter.iter_.HasRoomFor(AlignInt(len));
|
||||
}
|
||||
|
||||
void Pickle::UpdateIter(PickleIterator* iter, uint32_t bytes) const {
|
||||
// Make sure we don't get into trouble where AlignInt(bytes) == 0.
|
||||
MOZ_RELEASE_ASSERT(bytes < 64);
|
||||
|
||||
iter->iter_.Advance(buffers_, AlignInt(bytes));
|
||||
}
|
||||
|
||||
// Payload is sizeof(Pickle::memberAlignmentType) aligned.
|
||||
|
||||
Pickle::Pickle()
|
||||
: header_(NULL),
|
||||
header_size_(sizeof(Header)),
|
||||
capacity_(0),
|
||||
variable_buffer_offset_(0) {
|
||||
Resize(kPayloadUnit);
|
||||
header_->payload_size = 0;
|
||||
}
|
||||
|
||||
Pickle::Pickle(int header_size)
|
||||
: header_(NULL),
|
||||
header_size_(AlignInt(header_size)),
|
||||
capacity_(0),
|
||||
variable_buffer_offset_(0) {
|
||||
Pickle::Pickle(uint32_t header_size)
|
||||
: buffers_(AlignInt(header_size), kHeaderSegmentCapacity, kDefaultSegmentCapacity),
|
||||
header_(nullptr),
|
||||
header_size_(AlignInt(header_size)) {
|
||||
DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header));
|
||||
DCHECK(header_size <= kPayloadUnit);
|
||||
Resize(kPayloadUnit);
|
||||
if (!header_) {
|
||||
NS_ABORT_OOM(kPayloadUnit);
|
||||
}
|
||||
DCHECK(header_size_ <= kHeaderSegmentCapacity);
|
||||
header_ = reinterpret_cast<Header*>(buffers_.Start());
|
||||
header_->payload_size = 0;
|
||||
}
|
||||
|
||||
Pickle::Pickle(const char* data, int data_len, Ownership ownership)
|
||||
: header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
|
||||
header_size_(0),
|
||||
capacity_(ownership == BORROWS ? kCapacityReadOnly : data_len),
|
||||
variable_buffer_offset_(0) {
|
||||
if (data_len >= static_cast<int>(sizeof(Header)))
|
||||
header_size_ = data_len - header_->payload_size;
|
||||
Pickle::Pickle(uint32_t header_size, const char* data, uint32_t length)
|
||||
: buffers_(length, AlignCapacity(length), kDefaultSegmentCapacity),
|
||||
header_(nullptr),
|
||||
header_size_(AlignInt(header_size)) {
|
||||
DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header));
|
||||
DCHECK(header_size <= kHeaderSegmentCapacity);
|
||||
MOZ_RELEASE_ASSERT(header_size <= length);
|
||||
|
||||
if (header_size_ > static_cast<unsigned int>(data_len))
|
||||
header_size_ = 0;
|
||||
|
||||
if (header_size_ != AlignInt(header_size_))
|
||||
header_size_ = 0;
|
||||
|
||||
// If there is anything wrong with the data, we're not going to use it.
|
||||
if (!header_size_)
|
||||
header_ = nullptr;
|
||||
header_ = reinterpret_cast<Header*>(buffers_.Start());
|
||||
memcpy(header_, data, length);
|
||||
}
|
||||
|
||||
Pickle::Pickle(Pickle&& other)
|
||||
: header_(other.header_),
|
||||
header_size_(other.header_size_),
|
||||
capacity_(other.capacity_),
|
||||
variable_buffer_offset_(other.variable_buffer_offset_) {
|
||||
other.header_ = NULL;
|
||||
other.capacity_ = 0;
|
||||
other.variable_buffer_offset_ = 0;
|
||||
: buffers_(mozilla::Move(other.buffers_)),
|
||||
header_(other.header_),
|
||||
header_size_(other.header_size_) {
|
||||
other.header_ = nullptr;
|
||||
}
|
||||
|
||||
Pickle::~Pickle() {
|
||||
if (capacity_ != kCapacityReadOnly)
|
||||
free(header_);
|
||||
}
|
||||
|
||||
Pickle& Pickle::operator=(Pickle&& other) {
|
||||
BufferList tmp = mozilla::Move(other.buffers_);
|
||||
other.buffers_ = mozilla::Move(buffers_);
|
||||
buffers_ = mozilla::Move(tmp);
|
||||
|
||||
//std::swap(buffers_, other.buffers_);
|
||||
std::swap(header_, other.header_);
|
||||
std::swap(header_size_, other.header_size_);
|
||||
std::swap(capacity_, other.capacity_);
|
||||
std::swap(variable_buffer_offset_, other.variable_buffer_offset_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -188,9 +174,9 @@ bool Pickle::ReadInt16(PickleIterator* iter, int16_t* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
|
||||
return false;
|
||||
return ReadBytesInto(iter, result, sizeof(*result));
|
||||
|
||||
iter->CopyFrom(result);
|
||||
iter->CopyInto(result);
|
||||
|
||||
UpdateIter(iter, sizeof(*result));
|
||||
return true;
|
||||
@@ -200,9 +186,9 @@ bool Pickle::ReadUInt16(PickleIterator* iter, uint16_t* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
|
||||
return false;
|
||||
return ReadBytesInto(iter, result, sizeof(*result));
|
||||
|
||||
iter->CopyFrom(result);
|
||||
iter->CopyInto(result);
|
||||
|
||||
UpdateIter(iter, sizeof(*result));
|
||||
return true;
|
||||
@@ -212,9 +198,9 @@ bool Pickle::ReadInt(PickleIterator* iter, int* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
|
||||
return false;
|
||||
return ReadBytesInto(iter, result, sizeof(*result));
|
||||
|
||||
iter->CopyFrom(result);
|
||||
iter->CopyInto(result);
|
||||
|
||||
UpdateIter(iter, sizeof(*result));
|
||||
return true;
|
||||
@@ -225,15 +211,18 @@ bool Pickle::ReadInt(PickleIterator* iter, int* result) const {
|
||||
bool Pickle::ReadLong(PickleIterator* iter, long* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
int64_t bigResult = 0;
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(bigResult)))
|
||||
return false;
|
||||
int64_t big_result = 0;
|
||||
if (IteratorHasRoomFor(*iter, sizeof(big_result))) {
|
||||
iter->CopyInto(&big_result);
|
||||
UpdateIter(iter, sizeof(big_result));
|
||||
} else {
|
||||
if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
DCHECK(big_result <= LONG_MAX && big_result >= LONG_MIN);
|
||||
*result = static_cast<long>(big_result);
|
||||
|
||||
iter->CopyFrom(&bigResult);
|
||||
DCHECK(bigResult <= LONG_MAX && bigResult >= LONG_MIN);
|
||||
*result = static_cast<long>(bigResult);
|
||||
|
||||
UpdateIter(iter, sizeof(bigResult));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -242,15 +231,18 @@ bool Pickle::ReadLong(PickleIterator* iter, long* result) const {
|
||||
bool Pickle::ReadULong(PickleIterator* iter, unsigned long* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
uint64_t bigResult = 0;
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(bigResult)))
|
||||
return false;
|
||||
uint64_t big_result = 0;
|
||||
if (IteratorHasRoomFor(*iter, sizeof(big_result))) {
|
||||
iter->CopyInto(&big_result);
|
||||
UpdateIter(iter, sizeof(big_result));
|
||||
} else {
|
||||
if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
DCHECK(big_result <= ULONG_MAX);
|
||||
*result = static_cast<unsigned long>(big_result);
|
||||
|
||||
iter->CopyFrom(&bigResult);
|
||||
DCHECK(bigResult <= ULONG_MAX);
|
||||
*result = static_cast<unsigned long>(bigResult);
|
||||
|
||||
UpdateIter(iter, sizeof(bigResult));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -265,15 +257,18 @@ bool Pickle::ReadLength(PickleIterator* iter, int* result) const {
|
||||
bool Pickle::ReadSize(PickleIterator* iter, size_t* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
uint64_t bigResult = 0;
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(bigResult)))
|
||||
return false;
|
||||
uint64_t big_result = 0;
|
||||
if (IteratorHasRoomFor(*iter, sizeof(big_result))) {
|
||||
iter->CopyInto(&big_result);
|
||||
UpdateIter(iter, sizeof(big_result));
|
||||
} else {
|
||||
if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
DCHECK(big_result <= std::numeric_limits<size_t>::max());
|
||||
*result = static_cast<size_t>(big_result);
|
||||
|
||||
iter->CopyFrom(&bigResult);
|
||||
DCHECK(bigResult <= std::numeric_limits<size_t>::max());
|
||||
*result = static_cast<size_t>(bigResult);
|
||||
|
||||
UpdateIter(iter, sizeof(bigResult));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -281,9 +276,9 @@ bool Pickle::ReadInt32(PickleIterator* iter, int32_t* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
|
||||
return false;
|
||||
return ReadBytesInto(iter, result, sizeof(*result));
|
||||
|
||||
iter->CopyFrom(result);
|
||||
iter->CopyInto(result);
|
||||
|
||||
UpdateIter(iter, sizeof(*result));
|
||||
return true;
|
||||
@@ -293,9 +288,9 @@ bool Pickle::ReadUInt32(PickleIterator* iter, uint32_t* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
|
||||
return false;
|
||||
return ReadBytesInto(iter, result, sizeof(*result));
|
||||
|
||||
iter->CopyFrom(result);
|
||||
iter->CopyInto(result);
|
||||
|
||||
UpdateIter(iter, sizeof(*result));
|
||||
return true;
|
||||
@@ -305,9 +300,9 @@ bool Pickle::ReadInt64(PickleIterator* iter, int64_t* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
|
||||
return false;
|
||||
return ReadBytesInto(iter, result, sizeof(*result));
|
||||
|
||||
iter->CopyFrom(result);
|
||||
iter->CopyInto(result);
|
||||
|
||||
UpdateIter(iter, sizeof(*result));
|
||||
return true;
|
||||
@@ -317,9 +312,9 @@ bool Pickle::ReadUInt64(PickleIterator* iter, uint64_t* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
|
||||
return false;
|
||||
return ReadBytesInto(iter, result, sizeof(*result));
|
||||
|
||||
iter->CopyFrom(result);
|
||||
iter->CopyInto(result);
|
||||
|
||||
UpdateIter(iter, sizeof(*result));
|
||||
return true;
|
||||
@@ -329,9 +324,9 @@ bool Pickle::ReadDouble(PickleIterator* iter, double* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
|
||||
return false;
|
||||
return ReadBytesInto(iter, result, sizeof(*result));
|
||||
|
||||
iter->CopyFrom(result);
|
||||
iter->CopyInto(result);
|
||||
|
||||
UpdateIter(iter, sizeof(*result));
|
||||
return true;
|
||||
@@ -342,15 +337,19 @@ bool Pickle::ReadDouble(PickleIterator* iter, double* result) const {
|
||||
bool Pickle::ReadIntPtr(PickleIterator* iter, intptr_t* result) const {
|
||||
DCHECK(iter);
|
||||
|
||||
int64_t bigResult = 0;
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(bigResult)))
|
||||
return false;
|
||||
int64_t big_result = 0;
|
||||
if (IteratorHasRoomFor(*iter, sizeof(big_result))) {
|
||||
iter->CopyInto(&big_result);
|
||||
UpdateIter(iter, sizeof(big_result));
|
||||
} else {
|
||||
if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
iter->CopyFrom(&bigResult);
|
||||
DCHECK(bigResult <= std::numeric_limits<intptr_t>::max() && bigResult >= std::numeric_limits<intptr_t>::min());
|
||||
*result = static_cast<intptr_t>(bigResult);
|
||||
DCHECK(big_result <= std::numeric_limits<intptr_t>::max() && big_result >= std::numeric_limits<intptr_t>::min());
|
||||
*result = static_cast<intptr_t>(big_result);
|
||||
|
||||
UpdateIter(iter, sizeof(bigResult));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -358,9 +357,9 @@ bool Pickle::ReadUnsignedChar(PickleIterator* iter, unsigned char* result) const
|
||||
DCHECK(iter);
|
||||
|
||||
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
|
||||
return false;
|
||||
return ReadBytesInto(iter, result, sizeof(*result));
|
||||
|
||||
iter->CopyFrom(result);
|
||||
iter->CopyInto(result);
|
||||
|
||||
UpdateIter(iter, sizeof(*result));
|
||||
return true;
|
||||
@@ -372,13 +371,13 @@ bool Pickle::ReadString(PickleIterator* iter, std::string* result) const {
|
||||
int len;
|
||||
if (!ReadLength(iter, &len))
|
||||
return false;
|
||||
if (!IteratorHasRoomFor(*iter, len))
|
||||
|
||||
auto chars = mozilla::MakeUnique<char[]>(len);
|
||||
if (!ReadBytesInto(iter, chars.get(), len)) {
|
||||
return false;
|
||||
}
|
||||
result->assign(chars.get(), len);
|
||||
|
||||
char* chars = reinterpret_cast<char*>(iter->iter_);
|
||||
result->assign(chars, len);
|
||||
|
||||
UpdateIter(iter, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -391,70 +390,85 @@ bool Pickle::ReadWString(PickleIterator* iter, std::wstring* result) const {
|
||||
// Avoid integer multiplication overflow.
|
||||
if (len > INT_MAX / static_cast<int>(sizeof(wchar_t)))
|
||||
return false;
|
||||
if (!IteratorHasRoomFor(*iter, len * sizeof(wchar_t)))
|
||||
|
||||
auto chars = mozilla::MakeUnique<wchar_t[]>(len);
|
||||
if (!ReadBytesInto(iter, chars.get(), len * sizeof(wchar_t))) {
|
||||
return false;
|
||||
}
|
||||
result->assign(chars.get(), len);
|
||||
|
||||
wchar_t* chars = reinterpret_cast<wchar_t*>(iter->iter_);
|
||||
result->assign(chars, len);
|
||||
|
||||
UpdateIter(iter, len * sizeof(wchar_t));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Pickle::ReadBytes(PickleIterator* iter, const char** data, int length,
|
||||
uint32_t alignment) const {
|
||||
bool Pickle::FlattenBytes(PickleIterator* iter, const char** data, uint32_t length,
|
||||
uint32_t alignment) {
|
||||
DCHECK(iter);
|
||||
DCHECK(data);
|
||||
DCHECK(alignment == 4 || alignment == 8);
|
||||
DCHECK(intptr_t(header_) % alignment == 0);
|
||||
|
||||
uint32_t paddingLen = intptr_t(iter->iter_) % alignment;
|
||||
if (paddingLen) {
|
||||
#ifdef DEBUG
|
||||
{
|
||||
const char* padding = static_cast<const char*>(iter->iter_);
|
||||
for (uint32_t i = 0; i < paddingLen; i++) {
|
||||
DCHECK(*(padding + i) == kBytePaddingMarker);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
length += paddingLen;
|
||||
if (AlignInt(length) < length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IteratorHasRoomFor(*iter, length))
|
||||
uint32_t padding_len = intptr_t(iter->iter_.Data()) % alignment;
|
||||
if (!iter->iter_.AdvanceAcrossSegments(buffers_, padding_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*data = static_cast<const char*>(iter->iter_) + paddingLen;
|
||||
DCHECK(intptr_t(*data) % alignment == 0);
|
||||
if (!buffers_.FlattenBytes(iter->iter_, data, length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateIter(iter, length);
|
||||
header_ = reinterpret_cast<Header*>(buffers_.Start());
|
||||
|
||||
return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
|
||||
}
|
||||
|
||||
bool Pickle::ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const {
|
||||
if (AlignInt(length) < length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!buffers_.ReadBytes(iter->iter_, reinterpret_cast<char*>(data), length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
|
||||
}
|
||||
|
||||
bool Pickle::ReadSentinel(PickleIterator* iter, uint32_t sentinel) const {
|
||||
#ifdef SENTINEL_CHECKING
|
||||
uint32_t found;
|
||||
if (!ReadUInt32(iter, &found)) {
|
||||
return false;
|
||||
}
|
||||
return found == sentinel;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Pickle::ReadData(PickleIterator* iter, const char** data, int* length) const {
|
||||
DCHECK(iter);
|
||||
DCHECK(data);
|
||||
DCHECK(length);
|
||||
|
||||
if (!ReadLength(iter, length))
|
||||
return false;
|
||||
|
||||
return ReadBytes(iter, data, *length);
|
||||
bool Pickle::WriteSentinel(uint32_t sentinel) {
|
||||
#ifdef SENTINEL_CHECKING
|
||||
return WriteUInt32(sentinel);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
char* Pickle::BeginWrite(uint32_t length, uint32_t alignment) {
|
||||
void Pickle::EndRead(PickleIterator& iter) const {
|
||||
DCHECK(iter.iter_.Done());
|
||||
}
|
||||
|
||||
void Pickle::BeginWrite(uint32_t length, uint32_t alignment) {
|
||||
DCHECK(alignment % 4 == 0) << "Must be at least 32-bit aligned!";
|
||||
|
||||
// write at an alignment-aligned offset from the beginning of the header
|
||||
uint32_t offset = AlignInt(header_->payload_size);
|
||||
uint32_t padding = (header_size_ + offset) % alignment;
|
||||
uint32_t padding = (header_size_ + offset) % alignment;
|
||||
uint32_t new_size = offset + padding + AlignInt(length);
|
||||
uint32_t needed_size = header_size_ + new_size;
|
||||
|
||||
if (needed_size > capacity_) {
|
||||
double growth_rate = capacity_ < kFastGrowthCap ? 2.0 : 1.4;
|
||||
Resize(std::max(static_cast<uint32_t>(capacity_ * growth_rate), needed_size));
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(new_size >= header_->payload_size);
|
||||
|
||||
DCHECK(intptr_t(header_) % alignment == 0);
|
||||
|
||||
@@ -462,49 +476,42 @@ char* Pickle::BeginWrite(uint32_t length, uint32_t alignment) {
|
||||
DCHECK_LE(length, std::numeric_limits<uint32_t>::max());
|
||||
#endif
|
||||
|
||||
char* buffer = payload() + offset;
|
||||
|
||||
if (padding) {
|
||||
memset(buffer, kBytePaddingMarker, padding);
|
||||
buffer += padding;
|
||||
MOZ_RELEASE_ASSERT(padding <= 8);
|
||||
static const char padding_data[8] = {
|
||||
kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker,
|
||||
kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker,
|
||||
};
|
||||
buffers_.WriteBytes(padding_data, padding);
|
||||
}
|
||||
|
||||
DCHECK(intptr_t(buffer) % alignment == 0);
|
||||
DCHECK((header_size_ + header_->payload_size + padding) % alignment == 0);
|
||||
|
||||
header_->payload_size = new_size;
|
||||
|
||||
#ifdef MOZ_VALGRIND
|
||||
// pad the trailing end as well, so that valgrind
|
||||
// doesn't complain when we write the buffer
|
||||
padding = AlignInt(length) - length;
|
||||
if (padding) {
|
||||
memset(buffer + length, kBytePaddingMarker, padding);
|
||||
}
|
||||
#endif
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void Pickle::EndWrite(char* dest, int length) {
|
||||
void Pickle::EndWrite(uint32_t length) {
|
||||
// Zero-pad to keep tools like purify from complaining about uninitialized
|
||||
// memory.
|
||||
if (length % sizeof(memberAlignmentType))
|
||||
memset(dest + length, 0,
|
||||
sizeof(memberAlignmentType) - (length % sizeof(memberAlignmentType)));
|
||||
uint32_t padding = AlignInt(length) - length;
|
||||
if (padding) {
|
||||
MOZ_RELEASE_ASSERT(padding <= 4);
|
||||
static const char padding_data[4] = {
|
||||
kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker,
|
||||
};
|
||||
buffers_.WriteBytes(padding_data, padding);
|
||||
}
|
||||
}
|
||||
|
||||
bool Pickle::WriteBytes(const void* data, int data_len, uint32_t alignment) {
|
||||
DCHECK(capacity_ != kCapacityReadOnly) << "oops: pickle is readonly";
|
||||
bool Pickle::WriteBytes(const void* data, uint32_t data_len, uint32_t alignment) {
|
||||
DCHECK(alignment == 4 || alignment == 8);
|
||||
DCHECK(intptr_t(header_) % alignment == 0);
|
||||
|
||||
char* dest = BeginWrite(data_len, alignment);
|
||||
if (!dest)
|
||||
return false;
|
||||
BeginWrite(data_len, alignment);
|
||||
|
||||
memcpy(dest, data, data_len);
|
||||
buffers_.WriteBytes(reinterpret_cast<const char*>(data), data_len);
|
||||
|
||||
EndWrite(dest, data_len);
|
||||
EndWrite(data_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -523,65 +530,27 @@ bool Pickle::WriteWString(const std::wstring& value) {
|
||||
static_cast<int>(value.size() * sizeof(wchar_t)));
|
||||
}
|
||||
|
||||
bool Pickle::WriteData(const char* data, int length) {
|
||||
bool Pickle::WriteData(const char* data, uint32_t length) {
|
||||
return WriteInt(length) && WriteBytes(data, length);
|
||||
}
|
||||
|
||||
char* Pickle::BeginWriteData(int length) {
|
||||
DCHECK_EQ(variable_buffer_offset_, 0U) <<
|
||||
"There can only be one variable buffer in a Pickle";
|
||||
|
||||
if (!WriteInt(length))
|
||||
return NULL;
|
||||
|
||||
char *data_ptr = BeginWrite(length, sizeof(memberAlignmentType));
|
||||
if (!data_ptr)
|
||||
return NULL;
|
||||
|
||||
variable_buffer_offset_ =
|
||||
data_ptr - reinterpret_cast<char*>(header_) - sizeof(int);
|
||||
|
||||
// EndWrite doesn't necessarily have to be called after the write operation,
|
||||
// so we call it here to pad out what the caller will eventually write.
|
||||
EndWrite(data_ptr, length);
|
||||
return data_ptr;
|
||||
void Pickle::InputBytes(const char* data, uint32_t length) {
|
||||
buffers_.WriteBytes(data, length);
|
||||
}
|
||||
|
||||
void Pickle::Resize(uint32_t new_capacity) {
|
||||
new_capacity = ConstantAligner<kPayloadUnit>::align(new_capacity);
|
||||
|
||||
void* p = moz_xrealloc(header_, new_capacity);
|
||||
|
||||
header_ = reinterpret_cast<Header*>(p);
|
||||
capacity_ = new_capacity;
|
||||
int32_t* Pickle::GetInt32PtrForTest(uint32_t offset) {
|
||||
size_t pos = buffers_.Size() - offset;
|
||||
BufferList::IterImpl iter(buffers_);
|
||||
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(buffers_, pos));
|
||||
return reinterpret_cast<int32_t*>(iter.Data());
|
||||
}
|
||||
|
||||
// static
|
||||
const char* Pickle::FindNext(uint32_t header_size,
|
||||
uint32_t Pickle::MessageSize(uint32_t header_size,
|
||||
const char* start,
|
||||
const char* end) {
|
||||
DCHECK(header_size == AlignInt(header_size));
|
||||
DCHECK(header_size <= static_cast<memberAlignmentType>(kPayloadUnit));
|
||||
|
||||
if (end < start)
|
||||
return nullptr;
|
||||
size_t length = static_cast<size_t>(end - start);
|
||||
if (length < sizeof(Header))
|
||||
return nullptr;
|
||||
|
||||
const Header* hdr = reinterpret_cast<const Header*>(start);
|
||||
if (length < header_size || length - header_size < hdr->payload_size)
|
||||
return nullptr;
|
||||
|
||||
return start + header_size + hdr->payload_size;
|
||||
}
|
||||
|
||||
// static
|
||||
uint32_t Pickle::GetLength(uint32_t header_size,
|
||||
const char* start,
|
||||
const char* end) {
|
||||
DCHECK(header_size == AlignInt(header_size));
|
||||
DCHECK(header_size <= static_cast<memberAlignmentType>(kPayloadUnit));
|
||||
DCHECK(header_size <= static_cast<memberAlignmentType>(kHeaderSegmentCapacity));
|
||||
|
||||
if (end < start)
|
||||
return 0;
|
||||
@@ -593,5 +562,11 @@ uint32_t Pickle::GetLength(uint32_t header_size,
|
||||
if (length < header_size)
|
||||
return 0;
|
||||
|
||||
return header_size + hdr->payload_size;
|
||||
mozilla::CheckedInt<uint32_t> sum(header_size);
|
||||
sum += hdr->payload_size;
|
||||
|
||||
if (!sum.isValid())
|
||||
return 0;
|
||||
|
||||
return sum.value();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "base/string16.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/BufferList.h"
|
||||
#include "mozilla/mozalloc.h"
|
||||
|
||||
class Pickle;
|
||||
|
||||
@@ -24,10 +26,10 @@ public:
|
||||
private:
|
||||
friend class Pickle;
|
||||
|
||||
template<typename T>
|
||||
void CopyFrom(T* dest);
|
||||
mozilla::BufferList<InfallibleAllocPolicy>::IterImpl iter_;
|
||||
|
||||
void* iter_;
|
||||
template<typename T>
|
||||
void CopyInto(T* dest);
|
||||
};
|
||||
|
||||
// This class provides facilities for basic binary value packing and unpacking.
|
||||
@@ -49,28 +51,16 @@ private:
|
||||
//
|
||||
class Pickle {
|
||||
public:
|
||||
enum Ownership {
|
||||
BORROWS,
|
||||
OWNS,
|
||||
};
|
||||
|
||||
~Pickle();
|
||||
|
||||
// Initialize a Pickle object using the default header size.
|
||||
Pickle();
|
||||
Pickle() = delete;
|
||||
|
||||
// Initialize a Pickle object with the specified header size in bytes, which
|
||||
// must be greater-than-or-equal-to sizeof(Pickle::Header). The header size
|
||||
// will be rounded up to ensure that the header size is 32bit-aligned.
|
||||
explicit Pickle(int header_size);
|
||||
explicit Pickle(uint32_t header_size);
|
||||
|
||||
// Initializes a Pickle from a const block of data. If ownership == BORROWS,
|
||||
// the data is not copied; instead the data is merely referenced by this
|
||||
// Pickle. Only const methods should be used on the Pickle when initialized
|
||||
// this way. The header padding size is deduced from the data length. If
|
||||
// ownership == OWNS, then again no copying takes place. However, the buffer
|
||||
// is writable and will be freed when this Pickle is destroyed.
|
||||
Pickle(const char* data, int data_len, Ownership ownership = BORROWS);
|
||||
Pickle(uint32_t header_size, const char* data, uint32_t length);
|
||||
|
||||
Pickle(const Pickle& other) = delete;
|
||||
|
||||
@@ -82,16 +72,13 @@ class Pickle {
|
||||
Pickle& operator=(Pickle&& other);
|
||||
|
||||
// Returns the size of the Pickle's data.
|
||||
int size() const { return static_cast<int>(header_size_ +
|
||||
header_->payload_size); }
|
||||
uint32_t size() const { return header_size_ + header_->payload_size; }
|
||||
|
||||
// Return the full size of the memory allocated for this Pickle's data.
|
||||
uint32_t capacity() const {
|
||||
return capacity_;
|
||||
}
|
||||
typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList;
|
||||
|
||||
// Returns the data for this Pickle.
|
||||
const void* data() const { return header_; }
|
||||
const BufferList& Buffers() const { return buffers_; }
|
||||
|
||||
uint32_t CurrentSize() const { return buffers_.Size(); }
|
||||
|
||||
// Methods for reading the payload of the Pickle. To read from the start of
|
||||
// the Pickle, initialize *iter to NULL. If successful, these methods return
|
||||
@@ -114,17 +101,17 @@ class Pickle {
|
||||
MOZ_MUST_USE bool ReadUnsignedChar(PickleIterator* iter, unsigned char* result) const;
|
||||
MOZ_MUST_USE bool ReadString(PickleIterator* iter, std::string* result) const;
|
||||
MOZ_MUST_USE bool ReadWString(PickleIterator* iter, std::wstring* result) const;
|
||||
MOZ_MUST_USE bool ReadData(PickleIterator* iter, const char** data, int* length) const;
|
||||
MOZ_MUST_USE bool ReadBytes(PickleIterator* iter, const char** data, int length,
|
||||
uint32_t alignment = sizeof(memberAlignmentType)) const;
|
||||
MOZ_MUST_USE bool ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const;
|
||||
MOZ_MUST_USE bool FlattenBytes(PickleIterator* iter, const char** data, uint32_t length,
|
||||
uint32_t alignment = sizeof(memberAlignmentType));
|
||||
|
||||
// Safer version of ReadInt() checks for the result not being negative.
|
||||
// Use it for reading the object sizes.
|
||||
MOZ_MUST_USE bool ReadLength(PickleIterator* iter, int* result) const;
|
||||
|
||||
void EndRead(PickleIterator& iter) const {
|
||||
DCHECK(iter.iter_ == end_of_payload());
|
||||
}
|
||||
MOZ_MUST_USE bool ReadSentinel(PickleIterator* iter, uint32_t sentinel) const;
|
||||
|
||||
void EndRead(PickleIterator& iter) const;
|
||||
|
||||
// Methods for adding to the payload of the Pickle. These values are
|
||||
// appended to the end of the Pickle's payload. When reading values from a
|
||||
@@ -182,19 +169,15 @@ class Pickle {
|
||||
}
|
||||
bool WriteString(const std::string& value);
|
||||
bool WriteWString(const std::wstring& value);
|
||||
bool WriteData(const char* data, int length);
|
||||
bool WriteBytes(const void* data, int data_len,
|
||||
bool WriteData(const char* data, uint32_t length);
|
||||
bool WriteBytes(const void* data, uint32_t data_len,
|
||||
uint32_t alignment = sizeof(memberAlignmentType));
|
||||
|
||||
// Same as WriteData, but allows the caller to write directly into the
|
||||
// Pickle. This saves a copy in cases where the data is not already
|
||||
// available in a buffer. The caller should take care to not write more
|
||||
// than the length it declares it will. Use ReadData to get the data.
|
||||
// Returns NULL on failure.
|
||||
//
|
||||
// The returned pointer will only be valid until the next write operation
|
||||
// on this Pickle.
|
||||
char* BeginWriteData(int length);
|
||||
bool WriteSentinel(uint32_t sentinel);
|
||||
|
||||
int32_t* GetInt32PtrForTest(uint32_t offset);
|
||||
|
||||
void InputBytes(const char* data, uint32_t length);
|
||||
|
||||
// Payload follows after allocation of Header (header size is customizable).
|
||||
struct Header {
|
||||
@@ -215,56 +198,21 @@ class Pickle {
|
||||
return static_cast<const T*>(header_);
|
||||
}
|
||||
|
||||
// Returns true if the given iterator could point to data with the given
|
||||
// length. If there is no room for the given data before the end of the
|
||||
// payload, returns false.
|
||||
bool IteratorHasRoomFor(const PickleIterator& iter, int len) const {
|
||||
if ((len < 0) || (iter.iter_ < header_) || iter.iter_ > end_of_payload())
|
||||
return false;
|
||||
const char* end_of_region = reinterpret_cast<const char*>(iter.iter_) + len;
|
||||
// Watch out for overflow in pointer calculation, which wraps.
|
||||
return (iter.iter_ <= end_of_region) && (end_of_region <= end_of_payload());
|
||||
}
|
||||
|
||||
typedef uint32_t memberAlignmentType;
|
||||
|
||||
protected:
|
||||
uint32_t payload_size() const { return header_->payload_size; }
|
||||
|
||||
char* payload() {
|
||||
return reinterpret_cast<char*>(header_) + header_size_;
|
||||
}
|
||||
const char* payload() const {
|
||||
return reinterpret_cast<const char*>(header_) + header_size_;
|
||||
}
|
||||
|
||||
// Returns the address of the byte immediately following the currently valid
|
||||
// header + payload.
|
||||
char* end_of_payload() {
|
||||
// We must have a valid header_.
|
||||
return payload() + payload_size();
|
||||
}
|
||||
const char* end_of_payload() const {
|
||||
// This object may be invalid.
|
||||
return header_ ? payload() + payload_size() : nullptr;
|
||||
}
|
||||
|
||||
// Resizes the buffer for use when writing the specified amount of data. The
|
||||
// location that the data should be written at is returned, or NULL if there
|
||||
// was an error. Call EndWrite with the returned offset and the given length
|
||||
// to pad out for the next write.
|
||||
char* BeginWrite(uint32_t length, uint32_t alignment);
|
||||
void BeginWrite(uint32_t length, uint32_t alignment);
|
||||
|
||||
// Completes the write operation by padding the data with NULL bytes until it
|
||||
// is padded. Should be paired with BeginWrite, but it does not necessarily
|
||||
// have to be called after the data is written.
|
||||
void EndWrite(char* dest, int length);
|
||||
|
||||
// Resize the capacity, note that the input value should include the size of
|
||||
// the header: new_capacity = sizeof(Header) + desired_payload_capacity.
|
||||
// A realloc() failure will cause a Resize failure... and caller should check
|
||||
// the return result for true (i.e., successful resizing).
|
||||
void Resize(uint32_t new_capacity);
|
||||
void EndWrite(uint32_t length);
|
||||
|
||||
// Round 'bytes' up to the next multiple of 'alignment'. 'alignment' must be
|
||||
// a power of 2.
|
||||
@@ -280,35 +228,37 @@ class Pickle {
|
||||
return ConstantAligner<sizeof(memberAlignmentType)>::align(bytes);
|
||||
}
|
||||
|
||||
static uint32_t AlignCapacity(int bytes) {
|
||||
return ConstantAligner<kSegmentAlignment>::align(bytes);
|
||||
}
|
||||
|
||||
// Returns true if the given iterator could point to data with the given
|
||||
// length. If there is no room for the given data before the end of the
|
||||
// payload, returns false.
|
||||
bool IteratorHasRoomFor(const PickleIterator& iter, uint32_t len) const;
|
||||
|
||||
// Moves the iterator by the given number of bytes, making sure it is aligned.
|
||||
// Pointer (iterator) is NOT aligned, but the change in the pointer
|
||||
// is guaranteed to be a multiple of sizeof(memberAlignmentType).
|
||||
static void UpdateIter(PickleIterator* iter, int bytes) {
|
||||
iter->iter_ = static_cast<char*>(iter->iter_) + AlignInt(bytes);
|
||||
}
|
||||
void UpdateIter(PickleIterator* iter, uint32_t bytes) const;
|
||||
|
||||
// Find the end of the pickled data that starts at range_start. Returns NULL
|
||||
// if the entire Pickle is not found in the given data range.
|
||||
static const char* FindNext(uint32_t header_size,
|
||||
// Figure out how big the message starting at range_start is. Returns 0 if
|
||||
// there's no enough data to determine (i.e., if [range_start, range_end) does
|
||||
// not contain enough of the message header to know the size).
|
||||
static uint32_t MessageSize(uint32_t header_size,
|
||||
const char* range_start,
|
||||
const char* range_end);
|
||||
|
||||
// If the given range contains at least header_size bytes, return the length
|
||||
// of the pickled data including the header.
|
||||
static uint32_t GetLength(uint32_t header_size,
|
||||
const char* range_start,
|
||||
const char* range_end);
|
||||
|
||||
// The allocation granularity of the payload.
|
||||
static const int kPayloadUnit;
|
||||
// Segments capacities are aligned to 8 bytes to ensure that all reads/writes
|
||||
// at 8-byte aligned offsets will be on 8-byte aligned pointers.
|
||||
static const uint32_t kSegmentAlignment = 8;
|
||||
|
||||
private:
|
||||
friend class PickleIterator;
|
||||
|
||||
BufferList buffers_;
|
||||
Header* header_;
|
||||
uint32_t header_size_;
|
||||
uint32_t capacity_;
|
||||
uint32_t variable_buffer_offset_;
|
||||
};
|
||||
|
||||
#endif // BASE_PICKLE_H__
|
||||
|
||||
@@ -50,7 +50,7 @@ class Channel : public Message::Sender {
|
||||
enum {
|
||||
// The maximum message size in bytes. Attempting to receive a
|
||||
// message of this size or bigger results in a channel error.
|
||||
kMaximumMessageSize = 256 * 1024 * 1024,
|
||||
kMaximumMessageSize = 128 * 1024 * 1024,
|
||||
|
||||
// Ammount of data to read at once from the pipe.
|
||||
kReadBufferSize = 4 * 1024,
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
#include "mozilla/ipc/ProtocolUtils.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
// Work around possible OS limitations.
|
||||
static const size_t kMaxIOVecSize = 256;
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
#include "GeckoTaskTracerImpl.h"
|
||||
using namespace mozilla::tasktracer;
|
||||
@@ -185,7 +188,8 @@ void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
|
||||
|
||||
mode_ = mode;
|
||||
is_blocked_on_write_ = false;
|
||||
message_send_bytes_written_ = 0;
|
||||
partial_write_iter_.reset();
|
||||
input_buf_offset_ = 0;
|
||||
server_listen_pipe_ = -1;
|
||||
pipe_ = -1;
|
||||
client_pipe_ = -1;
|
||||
@@ -263,11 +267,6 @@ bool Channel::ChannelImpl::EnqueueHelloMessage() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Channel::ChannelImpl::ClearAndShrinkInputOverflowBuf()
|
||||
{
|
||||
input_overflow_buf_.clear();
|
||||
}
|
||||
|
||||
bool Channel::ChannelImpl::Connect() {
|
||||
if (pipe_ == -1) {
|
||||
return false;
|
||||
@@ -287,10 +286,8 @@ bool Channel::ChannelImpl::Connect() {
|
||||
}
|
||||
|
||||
bool Channel::ChannelImpl::ProcessIncomingMessages() {
|
||||
ssize_t bytes_read = 0;
|
||||
|
||||
struct msghdr msg = {0};
|
||||
struct iovec iov = {input_buf_, Channel::kReadBufferSize};
|
||||
struct iovec iov;
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
@@ -299,27 +296,30 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
|
||||
for (;;) {
|
||||
msg.msg_controllen = sizeof(input_cmsg_buf_);
|
||||
|
||||
if (bytes_read == 0) {
|
||||
if (pipe_ == -1)
|
||||
return false;
|
||||
if (pipe_ == -1)
|
||||
return false;
|
||||
|
||||
// Read from pipe.
|
||||
// recvmsg() returns 0 if the connection has closed or EAGAIN if no data
|
||||
// is waiting on the pipe.
|
||||
bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT));
|
||||
// In some cases the beginning of a message will be stored in input_buf_. We
|
||||
// don't want to overwrite that, so we store the new data after it.
|
||||
iov.iov_base = input_buf_ + input_buf_offset_;
|
||||
iov.iov_len = Channel::kReadBufferSize - input_buf_offset_;
|
||||
|
||||
if (bytes_read < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
return true;
|
||||
} else {
|
||||
CHROMIUM_LOG(ERROR) << "pipe error (" << pipe_ << "): " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
} else if (bytes_read == 0) {
|
||||
// The pipe has closed...
|
||||
Close();
|
||||
// Read from pipe.
|
||||
// recvmsg() returns 0 if the connection has closed or EAGAIN if no data
|
||||
// is waiting on the pipe.
|
||||
ssize_t bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT));
|
||||
|
||||
if (bytes_read < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
return true;
|
||||
} else {
|
||||
CHROMIUM_LOG(ERROR) << "pipe error (" << pipe_ << "): " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
} else if (bytes_read == 0) {
|
||||
// The pipe has closed...
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
DCHECK(bytes_read);
|
||||
|
||||
@@ -373,38 +373,8 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
|
||||
}
|
||||
|
||||
// Process messages from input buffer.
|
||||
const char *p;
|
||||
const char *overflowp;
|
||||
const char *end;
|
||||
if (input_overflow_buf_.empty()) {
|
||||
overflowp = NULL;
|
||||
p = input_buf_;
|
||||
end = p + bytes_read;
|
||||
} else {
|
||||
if (input_overflow_buf_.size() >
|
||||
static_cast<size_t>(kMaximumMessageSize - bytes_read)) {
|
||||
ClearAndShrinkInputOverflowBuf();
|
||||
CHROMIUM_LOG(ERROR) << "IPC message is too big";
|
||||
return false;
|
||||
}
|
||||
|
||||
input_overflow_buf_.append(input_buf_, bytes_read);
|
||||
overflowp = p = input_overflow_buf_.data();
|
||||
end = p + input_overflow_buf_.size();
|
||||
|
||||
// If we've received the entire header, then we know the message
|
||||
// length. In that case, reserve enough space to hold the entire
|
||||
// message. This is more efficient than repeatedly enlarging the buffer as
|
||||
// more data comes in.
|
||||
uint32_t length = Message::GetLength(p, end);
|
||||
if (length) {
|
||||
input_overflow_buf_.reserve(length + kReadBufferSize);
|
||||
|
||||
// Recompute these pointers in case the buffer moved.
|
||||
overflowp = p = input_overflow_buf_.data();
|
||||
end = p + input_overflow_buf_.size();
|
||||
}
|
||||
}
|
||||
const char *p = input_buf_;
|
||||
const char *end = input_buf_ + input_buf_offset_ + bytes_read;
|
||||
|
||||
// A pointer to an array of |num_fds| file descriptors which includes any
|
||||
// fds that have spilled over from a previous read.
|
||||
@@ -424,131 +394,154 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
|
||||
num_fds = input_overflow_fds_.size();
|
||||
}
|
||||
|
||||
// The data for the message we're currently reading consists of any data
|
||||
// stored in incoming_message_ followed by data in input_buf_ (followed by
|
||||
// other messages).
|
||||
|
||||
while (p < end) {
|
||||
const char* message_tail = Message::FindNext(p, end);
|
||||
if (message_tail) {
|
||||
int len = static_cast<int>(message_tail - p);
|
||||
char* buf;
|
||||
// Try to figure out how big the message is. Size is 0 if we haven't read
|
||||
// enough of the header to know the size.
|
||||
uint32_t message_length = 0;
|
||||
if (incoming_message_.isSome()) {
|
||||
message_length = incoming_message_.ref().size();
|
||||
} else {
|
||||
message_length = Message::MessageSize(p, end);
|
||||
}
|
||||
|
||||
// The Message |m| allocated below needs to own its data. We can either
|
||||
// copy the data out of the buffer or else steal the buffer and move the
|
||||
// remaining data elsewhere. If len is large enough, we steal. Otherwise
|
||||
// we copy.
|
||||
if (len > kMaxCopySize) {
|
||||
// Since len > kMaxCopySize > kReadBufferSize, we know that we must be
|
||||
// using the overflow buffer. And since we always shift everything to
|
||||
// the left at the end of a read, we must be at the start of the
|
||||
// overflow buffer.
|
||||
MOZ_RELEASE_ASSERT(p == overflowp);
|
||||
buf = input_overflow_buf_.trade_bytes(len);
|
||||
if (!message_length) {
|
||||
// We haven't seen the full message header.
|
||||
MOZ_ASSERT(incoming_message_.isNothing());
|
||||
|
||||
// At this point the remaining data is at the front of
|
||||
// input_overflow_buf_. p will get fixed up at the end of the
|
||||
// loop. Set it to null here to make sure no one uses it.
|
||||
p = nullptr;
|
||||
overflowp = message_tail = input_overflow_buf_.data();
|
||||
end = overflowp + input_overflow_buf_.size();
|
||||
} else {
|
||||
buf = (char*)moz_xmalloc(len);
|
||||
memcpy(buf, p, len);
|
||||
// Move everything we have to the start of the buffer. We'll finish
|
||||
// reading this message when we get more data. For now we leave it in
|
||||
// input_buf_.
|
||||
memmove(input_buf_, p, end - p);
|
||||
input_buf_offset_ = end - p;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
input_buf_offset_ = 0;
|
||||
|
||||
bool partial;
|
||||
if (incoming_message_.isSome()) {
|
||||
// We already have some data for this message stored in
|
||||
// incoming_message_. We want to append the new data there.
|
||||
Message& m = incoming_message_.ref();
|
||||
|
||||
// How much data from this message remains to be added to
|
||||
// incoming_message_?
|
||||
MOZ_ASSERT(message_length > m.CurrentSize());
|
||||
uint32_t remaining = message_length - m.CurrentSize();
|
||||
|
||||
// How much data from this message is stored in input_buf_?
|
||||
uint32_t in_buf = std::min(remaining, uint32_t(end - p));
|
||||
|
||||
m.InputBytes(p, in_buf);
|
||||
p += in_buf;
|
||||
|
||||
// Are we done reading this message?
|
||||
partial = in_buf != remaining;
|
||||
} else {
|
||||
// How much data from this message is stored in input_buf_?
|
||||
uint32_t in_buf = std::min(message_length, uint32_t(end - p));
|
||||
|
||||
incoming_message_.emplace(p, in_buf);
|
||||
p += in_buf;
|
||||
|
||||
// Are we done reading this message?
|
||||
partial = in_buf != message_length;
|
||||
}
|
||||
|
||||
if (partial) {
|
||||
break;
|
||||
}
|
||||
|
||||
Message& m = incoming_message_.ref();
|
||||
|
||||
if (m.header()->num_fds) {
|
||||
// the message has file descriptors
|
||||
const char* error = NULL;
|
||||
if (m.header()->num_fds > num_fds - fds_i) {
|
||||
// the message has been completely received, but we didn't get
|
||||
// enough file descriptors.
|
||||
error = "Message needs unreceived descriptors";
|
||||
}
|
||||
Message m(buf, len, Message::OWNS);
|
||||
if (m.header()->num_fds) {
|
||||
// the message has file descriptors
|
||||
const char* error = NULL;
|
||||
if (m.header()->num_fds > num_fds - fds_i) {
|
||||
// the message has been completely received, but we didn't get
|
||||
// enough file descriptors.
|
||||
error = "Message needs unreceived descriptors";
|
||||
}
|
||||
|
||||
if (m.header()->num_fds >
|
||||
FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) {
|
||||
// There are too many descriptors in this message
|
||||
error = "Message requires an excessive number of descriptors";
|
||||
}
|
||||
if (m.header()->num_fds >
|
||||
FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) {
|
||||
// There are too many descriptors in this message
|
||||
error = "Message requires an excessive number of descriptors";
|
||||
}
|
||||
|
||||
if (error) {
|
||||
CHROMIUM_LOG(WARNING) << error
|
||||
<< " channel:" << this
|
||||
<< " message-type:" << m.type()
|
||||
<< " header()->num_fds:" << m.header()->num_fds
|
||||
<< " num_fds:" << num_fds
|
||||
<< " fds_i:" << fds_i;
|
||||
// close the existing file descriptors so that we don't leak them
|
||||
for (unsigned i = fds_i; i < num_fds; ++i)
|
||||
HANDLE_EINTR(close(fds[i]));
|
||||
input_overflow_fds_.clear();
|
||||
// abort the connection
|
||||
return false;
|
||||
}
|
||||
if (error) {
|
||||
CHROMIUM_LOG(WARNING) << error
|
||||
<< " channel:" << this
|
||||
<< " message-type:" << m.type()
|
||||
<< " header()->num_fds:" << m.header()->num_fds
|
||||
<< " num_fds:" << num_fds
|
||||
<< " fds_i:" << fds_i;
|
||||
// close the existing file descriptors so that we don't leak them
|
||||
for (unsigned i = fds_i; i < num_fds; ++i)
|
||||
HANDLE_EINTR(close(fds[i]));
|
||||
input_overflow_fds_.clear();
|
||||
// abort the connection
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// Send a message to the other side, indicating that we are now
|
||||
// responsible for closing the descriptor.
|
||||
Message *fdAck = new Message(MSG_ROUTING_NONE,
|
||||
RECEIVED_FDS_MESSAGE_TYPE,
|
||||
IPC::Message::PRIORITY_NORMAL);
|
||||
DCHECK(m.fd_cookie() != 0);
|
||||
fdAck->set_fd_cookie(m.fd_cookie());
|
||||
OutputQueuePush(fdAck);
|
||||
// Send a message to the other side, indicating that we are now
|
||||
// responsible for closing the descriptor.
|
||||
Message *fdAck = new Message(MSG_ROUTING_NONE,
|
||||
RECEIVED_FDS_MESSAGE_TYPE,
|
||||
IPC::Message::PRIORITY_NORMAL);
|
||||
DCHECK(m.fd_cookie() != 0);
|
||||
fdAck->set_fd_cookie(m.fd_cookie());
|
||||
OutputQueuePush(fdAck);
|
||||
#endif
|
||||
|
||||
m.file_descriptor_set()->SetDescriptors(
|
||||
&fds[fds_i], m.header()->num_fds);
|
||||
fds_i += m.header()->num_fds;
|
||||
}
|
||||
m.file_descriptor_set()->SetDescriptors(
|
||||
&fds[fds_i], m.header()->num_fds);
|
||||
fds_i += m.header()->num_fds;
|
||||
}
|
||||
#ifdef IPC_MESSAGE_DEBUG_EXTRA
|
||||
DLOG(INFO) << "received message on channel @" << this <<
|
||||
" with type " << m.type();
|
||||
DLOG(INFO) << "received message on channel @" << this <<
|
||||
" with type " << m.type();
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
AutoSaveCurTraceInfo saveCurTraceInfo;
|
||||
SetCurTraceInfo(m.header()->source_event_id,
|
||||
m.header()->parent_task_id,
|
||||
m.header()->source_event_type);
|
||||
AutoSaveCurTraceInfo saveCurTraceInfo;
|
||||
SetCurTraceInfo(m.header()->source_event_id,
|
||||
m.header()->parent_task_id,
|
||||
m.header()->source_event_type);
|
||||
#endif
|
||||
|
||||
if (m.routing_id() == MSG_ROUTING_NONE &&
|
||||
m.type() == HELLO_MESSAGE_TYPE) {
|
||||
// The Hello message contains only the process id.
|
||||
listener_->OnChannelConnected(MessageIterator(m).NextInt());
|
||||
if (m.routing_id() == MSG_ROUTING_NONE &&
|
||||
m.type() == HELLO_MESSAGE_TYPE) {
|
||||
// The Hello message contains only the process id.
|
||||
listener_->OnChannelConnected(MessageIterator(m).NextInt());
|
||||
#if defined(OS_MACOSX)
|
||||
} else if (m.routing_id() == MSG_ROUTING_NONE &&
|
||||
m.type() == RECEIVED_FDS_MESSAGE_TYPE) {
|
||||
DCHECK(m.fd_cookie() != 0);
|
||||
CloseDescriptors(m.fd_cookie());
|
||||
} else if (m.routing_id() == MSG_ROUTING_NONE &&
|
||||
m.type() == RECEIVED_FDS_MESSAGE_TYPE) {
|
||||
DCHECK(m.fd_cookie() != 0);
|
||||
CloseDescriptors(m.fd_cookie());
|
||||
#endif
|
||||
} else {
|
||||
listener_->OnMessageReceived(mozilla::Move(m));
|
||||
}
|
||||
p = message_tail;
|
||||
} else {
|
||||
// Last message is partial.
|
||||
break;
|
||||
listener_->OnMessageReceived(mozilla::Move(m));
|
||||
}
|
||||
|
||||
incoming_message_.reset();
|
||||
}
|
||||
if (end == p) {
|
||||
ClearAndShrinkInputOverflowBuf();
|
||||
} else if (!overflowp) {
|
||||
// p is from input_buf_
|
||||
input_overflow_buf_.assign(p, end - p);
|
||||
} else if (p > overflowp) {
|
||||
// p is from input_overflow_buf_
|
||||
input_overflow_buf_.erase(0, p - overflowp);
|
||||
}
|
||||
|
||||
input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]);
|
||||
|
||||
// When the input data buffer is empty, the overflow fds should be too. If
|
||||
// this is not the case, we probably have a rogue renderer which is trying
|
||||
// to fill our descriptor table.
|
||||
if (input_overflow_buf_.empty() && !input_overflow_fds_.empty()) {
|
||||
if (incoming_message_.isNothing() && input_buf_offset_ == 0 && !input_overflow_fds_.empty()) {
|
||||
// We close these descriptors in Close()
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes_read = 0; // Get more data.
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -576,7 +569,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
|
||||
int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE]));
|
||||
char buf[tmp];
|
||||
|
||||
if (message_send_bytes_written_ == 0 &&
|
||||
if (partial_write_iter_.isNothing() &&
|
||||
!msg->file_descriptor_set()->empty()) {
|
||||
// This is the first chunk of a message which has descriptors to send
|
||||
struct cmsghdr *cmsg;
|
||||
@@ -604,16 +597,46 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t amt_to_write = msg->size() - message_send_bytes_written_;
|
||||
DCHECK(amt_to_write != 0);
|
||||
const char *out_bytes = reinterpret_cast<const char*>(msg->data()) +
|
||||
message_send_bytes_written_;
|
||||
struct iovec iov[kMaxIOVecSize];
|
||||
size_t iov_count = 0;
|
||||
size_t amt_to_write = 0;
|
||||
|
||||
struct iovec iov = {const_cast<char*>(out_bytes), amt_to_write};
|
||||
msgh.msg_iov = &iov;
|
||||
msgh.msg_iovlen = 1;
|
||||
if (partial_write_iter_.isNothing()) {
|
||||
Pickle::BufferList::IterImpl iter(msg->Buffers());
|
||||
partial_write_iter_.emplace(iter);
|
||||
}
|
||||
|
||||
// How much of this message have we written so far?
|
||||
Pickle::BufferList::IterImpl iter = partial_write_iter_.value();
|
||||
|
||||
// Store the unwritten part of the first segment to write into the iovec.
|
||||
iov[0].iov_base = const_cast<char*>(iter.Data());
|
||||
iov[0].iov_len = iter.RemainingInSegment();
|
||||
amt_to_write += iov[0].iov_len;
|
||||
iter.Advance(msg->Buffers(), iov[0].iov_len);
|
||||
iov_count++;
|
||||
|
||||
// Store remaining segments to write into iovec.
|
||||
while (!iter.Done()) {
|
||||
char* data = iter.Data();
|
||||
size_t size = iter.RemainingInSegment();
|
||||
|
||||
// Don't add more than kMaxIOVecSize to the iovec so that we avoid
|
||||
// OS-dependent limits.
|
||||
if (iov_count < kMaxIOVecSize) {
|
||||
iov[iov_count].iov_base = data;
|
||||
iov[iov_count].iov_len = size;
|
||||
iov_count++;
|
||||
}
|
||||
amt_to_write += size;
|
||||
iter.Advance(msg->Buffers(), size);
|
||||
}
|
||||
|
||||
msgh.msg_iov = iov;
|
||||
msgh.msg_iovlen = iov_count;
|
||||
|
||||
ssize_t bytes_written = HANDLE_EINTR(sendmsg(pipe_, &msgh, MSG_DONTWAIT));
|
||||
|
||||
#if !defined(OS_MACOSX)
|
||||
// On OSX CommitAll gets called later, once we get the RECEIVED_FDS_MESSAGE_TYPE
|
||||
// message.
|
||||
@@ -657,9 +680,9 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(bytes_written) != amt_to_write) {
|
||||
// If write() fails with EAGAIN then bytes_written will be -1.
|
||||
if (bytes_written > 0) {
|
||||
// If write() fails with EAGAIN then bytes_written will be -1.
|
||||
message_send_bytes_written_ += bytes_written;
|
||||
partial_write_iter_.ref().AdvanceAcrossSegments(msg->Buffers(), bytes_written);
|
||||
}
|
||||
|
||||
// Tell libevent to call us back once things are unblocked.
|
||||
@@ -672,7 +695,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
|
||||
this);
|
||||
return true;
|
||||
} else {
|
||||
message_send_bytes_written_ = 0;
|
||||
partial_write_iter_.reset();
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
if (!msg->file_descriptor_set()->empty())
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include "base/buffer.h"
|
||||
#include "base/message_loop.h"
|
||||
#include "base/task.h"
|
||||
#include "chrome/common/file_descriptor_set_posix.h"
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
// An implementation of ChannelImpl for POSIX systems that works via
|
||||
@@ -62,8 +64,6 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
|
||||
bool ProcessIncomingMessages();
|
||||
bool ProcessOutgoingMessages();
|
||||
|
||||
void ClearAndShrinkInputOverflowBuf();
|
||||
|
||||
// MessageLoopForIO::Watcher implementation.
|
||||
virtual void OnFileCanReadWithoutBlocking(int fd);
|
||||
virtual void OnFileCanWriteWithoutBlocking(int fd);
|
||||
@@ -86,9 +86,10 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
|
||||
// Indicates whether we're currently blocked waiting for a write to complete.
|
||||
bool is_blocked_on_write_;
|
||||
|
||||
// If sending a message blocks then we use this variable
|
||||
// to keep track of where we are.
|
||||
size_t message_send_bytes_written_;
|
||||
// If sending a message blocks then we use this iterator to keep track of
|
||||
// where in the message we are. It gets reset when the message is finished
|
||||
// sending.
|
||||
mozilla::Maybe<Pickle::BufferList::IterImpl> partial_write_iter_;
|
||||
|
||||
int server_listen_pipe_;
|
||||
int pipe_;
|
||||
@@ -105,6 +106,7 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
|
||||
|
||||
// We read from the pipe into this buffer
|
||||
char input_buf_[Channel::kReadBufferSize];
|
||||
size_t input_buf_offset_;
|
||||
|
||||
// We want input_cmsg_buf_ to be big enough to hold
|
||||
// CMSG_SPACE(Channel::kReadBufferSize) bytes (see the comment below for an
|
||||
@@ -128,9 +130,9 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
|
||||
// for the control header.
|
||||
char input_cmsg_buf_[Channel::kReadBufferSize + kControlBufferSlopBytes];
|
||||
|
||||
// Large messages that span multiple pipe buffers, get built-up using
|
||||
// this buffer.
|
||||
Buffer input_overflow_buf_;
|
||||
// Large incoming messages that span multiple pipe buffers get built-up in the
|
||||
// buffers of this message.
|
||||
mozilla::Maybe<Message> incoming_message_;
|
||||
std::vector<int> input_overflow_fds_;
|
||||
|
||||
// In server-mode, we have to wait for the client to connect before we
|
||||
|
||||
@@ -88,6 +88,7 @@ void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
|
||||
processing_incoming_ = false;
|
||||
closed_ = false;
|
||||
output_queue_length_ = 0;
|
||||
input_buf_offset_ = 0;
|
||||
}
|
||||
|
||||
void Channel::ChannelImpl::OutputQueuePush(Message* msg)
|
||||
@@ -332,8 +333,8 @@ bool Channel::ChannelImpl::ProcessIncomingMessages(
|
||||
|
||||
// Read from pipe...
|
||||
BOOL ok = ReadFile(pipe_,
|
||||
input_buf_,
|
||||
Channel::kReadBufferSize,
|
||||
input_buf_ + input_buf_offset_,
|
||||
Channel::kReadBufferSize - input_buf_offset_,
|
||||
&bytes_read,
|
||||
&input_state_.context.overlapped);
|
||||
if (!ok) {
|
||||
@@ -352,95 +353,94 @@ bool Channel::ChannelImpl::ProcessIncomingMessages(
|
||||
|
||||
// Process messages from input buffer.
|
||||
|
||||
const char* p, *end;
|
||||
if (input_overflow_buf_.empty()) {
|
||||
p = input_buf_;
|
||||
end = p + bytes_read;
|
||||
} else {
|
||||
if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) {
|
||||
input_overflow_buf_.clear();
|
||||
CHROMIUM_LOG(ERROR) << "IPC message is too big";
|
||||
return false;
|
||||
}
|
||||
|
||||
input_overflow_buf_.append(input_buf_, bytes_read);
|
||||
p = input_overflow_buf_.data();
|
||||
end = p + input_overflow_buf_.size();
|
||||
|
||||
// If we've received the entire header, then we know the message
|
||||
// length. In that case, reserve enough space to hold the entire
|
||||
// message. This is more efficient than repeatedly enlarging the buffer as
|
||||
// more data comes in.
|
||||
uint32_t length = Message::GetLength(p, end);
|
||||
if (length) {
|
||||
input_overflow_buf_.reserve(length + kReadBufferSize);
|
||||
|
||||
// Recompute these pointers in case the buffer moved.
|
||||
p = input_overflow_buf_.data();
|
||||
end = p + input_overflow_buf_.size();
|
||||
}
|
||||
}
|
||||
const char *p = input_buf_;
|
||||
const char *end = input_buf_ + input_buf_offset_ + bytes_read;
|
||||
|
||||
while (p < end) {
|
||||
const char* message_tail = Message::FindNext(p, end);
|
||||
if (message_tail) {
|
||||
int len = static_cast<int>(message_tail - p);
|
||||
char* buf;
|
||||
|
||||
// The Message |m| allocated below needs to own its data. We can either
|
||||
// copy the data out of the buffer or else steal the buffer and move the
|
||||
// remaining data elsewhere. If len is large enough, we steal. Otherwise
|
||||
// we copy.
|
||||
if (len > kMaxCopySize) {
|
||||
// Since len > kMaxCopySize > kReadBufferSize, we know that we must be
|
||||
// using the overflow buffer. And since we always shift everything to
|
||||
// the left at the end of a read, we must be at the start of the
|
||||
// overflow buffer.
|
||||
buf = input_overflow_buf_.trade_bytes(len);
|
||||
|
||||
// At this point the remaining data is at the front of
|
||||
// input_overflow_buf_. p will get fixed up at the end of the
|
||||
// loop. Set it to null here to make sure no one uses it.
|
||||
p = nullptr;
|
||||
message_tail = input_overflow_buf_.data();
|
||||
end = message_tail + input_overflow_buf_.size();
|
||||
} else {
|
||||
buf = (char*)moz_xmalloc(len);
|
||||
memcpy(buf, p, len);
|
||||
}
|
||||
Message m(buf, len, Message::OWNS);
|
||||
#ifdef IPC_MESSAGE_DEBUG_EXTRA
|
||||
DLOG(INFO) << "received message on channel @" << this <<
|
||||
" with type " << m.type();
|
||||
#endif
|
||||
if (m.routing_id() == MSG_ROUTING_NONE &&
|
||||
m.type() == HELLO_MESSAGE_TYPE) {
|
||||
// The Hello message contains the process id and must include the
|
||||
// shared secret, if we are waiting for it.
|
||||
MessageIterator it = MessageIterator(m);
|
||||
int32_t claimed_pid = it.NextInt();
|
||||
if (waiting_for_shared_secret_ && (it.NextInt() != shared_secret_)) {
|
||||
NOTREACHED();
|
||||
// Something went wrong. Abort connection.
|
||||
Close();
|
||||
listener_->OnChannelError();
|
||||
return false;
|
||||
}
|
||||
waiting_for_shared_secret_ = false;
|
||||
listener_->OnChannelConnected(claimed_pid);
|
||||
} else {
|
||||
listener_->OnMessageReceived(mozilla::Move(m));
|
||||
}
|
||||
p = message_tail;
|
||||
// Try to figure out how big the message is. Size is 0 if we haven't read
|
||||
// enough of the header to know the size.
|
||||
uint32_t message_length = 0;
|
||||
if (incoming_message_.isSome()) {
|
||||
message_length = incoming_message_.ref().size();
|
||||
} else {
|
||||
// Last message is partial.
|
||||
message_length = Message::MessageSize(p, end);
|
||||
}
|
||||
|
||||
if (!message_length) {
|
||||
// We haven't seen the full message header.
|
||||
MOZ_ASSERT(incoming_message_.isNothing());
|
||||
|
||||
// Move everything we have to the start of the buffer. We'll finish
|
||||
// reading this message when we get more data. For now we leave it in
|
||||
// input_buf_.
|
||||
memmove(input_buf_, p, end - p);
|
||||
input_buf_offset_ = end - p;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p != input_overflow_buf_.data()) {
|
||||
// Don't assign unless we have to since this will throw away any memory we
|
||||
// might have reserved.
|
||||
input_overflow_buf_.assign(p, end - p);
|
||||
|
||||
input_buf_offset_ = 0;
|
||||
|
||||
bool partial;
|
||||
if (incoming_message_.isSome()) {
|
||||
// We already have some data for this message stored in
|
||||
// incoming_message_. We want to append the new data there.
|
||||
Message& m = incoming_message_.ref();
|
||||
|
||||
// How much data from this message remains to be added to
|
||||
// incoming_message_?
|
||||
MOZ_ASSERT(message_length > m.CurrentSize());
|
||||
uint32_t remaining = message_length - m.CurrentSize();
|
||||
|
||||
// How much data from this message is stored in input_buf_?
|
||||
uint32_t in_buf = std::min(remaining, uint32_t(end - p));
|
||||
|
||||
m.InputBytes(p, in_buf);
|
||||
p += in_buf;
|
||||
|
||||
// Are we done reading this message?
|
||||
partial = in_buf != remaining;
|
||||
} else {
|
||||
// How much data from this message is stored in input_buf_?
|
||||
uint32_t in_buf = std::min(message_length, uint32_t(end - p));
|
||||
|
||||
incoming_message_.emplace(p, in_buf);
|
||||
p += in_buf;
|
||||
|
||||
// Are we done reading this message?
|
||||
partial = in_buf != message_length;
|
||||
}
|
||||
|
||||
if (partial) {
|
||||
break;
|
||||
}
|
||||
|
||||
Message& m = incoming_message_.ref();
|
||||
|
||||
#ifdef IPC_MESSAGE_DEBUG_EXTRA
|
||||
DLOG(INFO) << "received message on channel @" << this <<
|
||||
" with type " << m.type();
|
||||
#endif
|
||||
if (m.routing_id() == MSG_ROUTING_NONE &&
|
||||
m.type() == HELLO_MESSAGE_TYPE) {
|
||||
// The Hello message contains the process id and must include the
|
||||
// shared secret, if we are waiting for it.
|
||||
MessageIterator it = MessageIterator(m);
|
||||
int32_t claimed_pid = it.NextInt();
|
||||
if (waiting_for_shared_secret_ && (it.NextInt() != shared_secret_)) {
|
||||
NOTREACHED();
|
||||
// Something went wrong. Abort connection.
|
||||
Close();
|
||||
listener_->OnChannelError();
|
||||
return false;
|
||||
}
|
||||
waiting_for_shared_secret_ = false;
|
||||
listener_->OnChannelConnected(claimed_pid);
|
||||
} else {
|
||||
listener_->OnMessageReceived(mozilla::Move(m));
|
||||
}
|
||||
|
||||
incoming_message_.reset();
|
||||
}
|
||||
|
||||
bytes_read = 0; // Get more data.
|
||||
@@ -467,8 +467,15 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages(
|
||||
// Message was sent.
|
||||
DCHECK(!output_queue_.empty());
|
||||
Message* m = output_queue_.front();
|
||||
OutputQueuePop();
|
||||
delete m;
|
||||
|
||||
MOZ_RELEASE_ASSERT(partial_write_iter_.isSome());
|
||||
Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
|
||||
iter.Advance(m->Buffers(), bytes_written);
|
||||
if (iter.Done()) {
|
||||
partial_write_iter_.reset();
|
||||
OutputQueuePop();
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
|
||||
if (output_queue_.empty())
|
||||
@@ -479,9 +486,16 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages(
|
||||
|
||||
// Write to pipe...
|
||||
Message* m = output_queue_.front();
|
||||
|
||||
if (partial_write_iter_.isNothing()) {
|
||||
Pickle::BufferList::IterImpl iter(m->Buffers());
|
||||
partial_write_iter_.emplace(iter);
|
||||
}
|
||||
|
||||
Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
|
||||
BOOL ok = WriteFile(pipe_,
|
||||
m->data(),
|
||||
m->size(),
|
||||
iter.Data(),
|
||||
iter.RemainingInSegment(),
|
||||
&bytes_written,
|
||||
&output_state_.context.overlapped);
|
||||
if (!ok) {
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
#include <queue>
|
||||
#include <string>
|
||||
|
||||
#include "base/buffer.h"
|
||||
#include "base/message_loop.h"
|
||||
#include "base/task.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
|
||||
@@ -82,12 +84,18 @@ class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
|
||||
// Messages to be sent are queued here.
|
||||
std::queue<Message*> output_queue_;
|
||||
|
||||
// If sending a message blocks then we use this iterator to keep track of
|
||||
// where in the message we are. It gets reset when the message is finished
|
||||
// sending.
|
||||
mozilla::Maybe<Pickle::BufferList::IterImpl> partial_write_iter_;
|
||||
|
||||
// We read from the pipe into this buffer
|
||||
char input_buf_[Channel::kReadBufferSize];
|
||||
size_t input_buf_offset_;
|
||||
|
||||
// Large messages that span multiple pipe buffers, get built-up using
|
||||
// this buffer.
|
||||
Buffer input_overflow_buf_;
|
||||
// Large incoming messages that span multiple pipe buffers get built-up in the
|
||||
// buffers of this message.
|
||||
mozilla::Maybe<Message> incoming_message_;
|
||||
|
||||
// In server-mode, we have to wait for the client to connect before we
|
||||
// can begin reading. We make use of the input_state_ when performing
|
||||
|
||||
@@ -73,8 +73,8 @@ Message::Message(int32_t routing_id, msgid_t type, PriorityValue priority,
|
||||
InitLoggingVariables(aName);
|
||||
}
|
||||
|
||||
Message::Message(const char* data, int data_len, Ownership ownership)
|
||||
: Pickle(data, data_len, ownership)
|
||||
Message::Message(const char* data, int data_len)
|
||||
: Pickle(sizeof(Header), data, data_len)
|
||||
{
|
||||
MOZ_COUNT_CTOR(IPC::Message);
|
||||
InitLoggingVariables();
|
||||
|
||||
@@ -72,12 +72,7 @@ class Message : public Pickle {
|
||||
MessageCompression compression = COMPRESSION_NONE,
|
||||
const char* const name="???");
|
||||
|
||||
// Initializes a message from a const block of data. If ownership == BORROWS,
|
||||
// the data is not copied; instead the data is merely referenced by this
|
||||
// message. Only const methods should be used on the message when initialized
|
||||
// this way. If ownership == OWNS, then again no copying takes place. However,
|
||||
// the buffer is writable and will be freed when the message is destroyed.
|
||||
Message(const char* data, int data_len, Ownership ownership = BORROWS);
|
||||
Message(const char* data, int data_len);
|
||||
|
||||
Message(const Message& other) = delete;
|
||||
Message(Message&& other);
|
||||
@@ -240,16 +235,11 @@ class Message : public Pickle {
|
||||
static void Log(const Message* msg, std::wstring* l) {
|
||||
}
|
||||
|
||||
// Find the end of the message data that starts at range_start. Returns NULL
|
||||
// if the entire message is not found in the given data range.
|
||||
static const char* FindNext(const char* range_start, const char* range_end) {
|
||||
return Pickle::FindNext(sizeof(Header), range_start, range_end);
|
||||
}
|
||||
|
||||
// If the given range contains at least header_size bytes, return the length
|
||||
// of the message including the header.
|
||||
static uint32_t GetLength(const char* range_start, const char* range_end) {
|
||||
return Pickle::GetLength(sizeof(Header), range_start, range_end);
|
||||
// Figure out how big the message starting at range_start is. Returns 0 if
|
||||
// there's no enough data to determine (i.e., if [range_start, range_end) does
|
||||
// not contain enough of the message header to know the size).
|
||||
static uint32_t MessageSize(const char* range_start, const char* range_end) {
|
||||
return Pickle::MessageSize(sizeof(Header), range_start, range_end);
|
||||
}
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
|
||||
@@ -55,11 +55,6 @@ class MessageIterator {
|
||||
NOTREACHED();
|
||||
return val;
|
||||
}
|
||||
void NextData(const char** data, int* length) const {
|
||||
if (!msg_.ReadData(&iter_, data, length)) {
|
||||
NOTREACHED();
|
||||
}
|
||||
}
|
||||
private:
|
||||
const Message& msg_;
|
||||
mutable PickleIterator iter_;
|
||||
@@ -192,19 +187,10 @@ template <>
|
||||
struct ParamTraitsFundamental<long long> {
|
||||
typedef long long param_type;
|
||||
static void Write(Message* m, const param_type& p) {
|
||||
m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type));
|
||||
}
|
||||
m->WriteBytes(&p, sizeof(param_type));
|
||||
}
|
||||
static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
|
||||
const char *data;
|
||||
int data_size = 0;
|
||||
bool result = m->ReadData(iter, &data, &data_size);
|
||||
if (result && data_size == sizeof(param_type)) {
|
||||
memcpy(r, data, sizeof(param_type));
|
||||
} else {
|
||||
result = false;
|
||||
NOTREACHED();
|
||||
}
|
||||
return result;
|
||||
return m->ReadBytesInto(iter, r, sizeof(*r));
|
||||
}
|
||||
static void Log(const param_type& p, std::wstring* l) {
|
||||
l->append(StringPrintf(L"%ll", p));
|
||||
@@ -215,19 +201,10 @@ template <>
|
||||
struct ParamTraitsFundamental<unsigned long long> {
|
||||
typedef unsigned long long param_type;
|
||||
static void Write(Message* m, const param_type& p) {
|
||||
m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type));
|
||||
}
|
||||
m->WriteBytes(&p, sizeof(param_type));
|
||||
}
|
||||
static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
|
||||
const char *data;
|
||||
int data_size = 0;
|
||||
bool result = m->ReadData(iter, &data, &data_size);
|
||||
if (result && data_size == sizeof(param_type)) {
|
||||
memcpy(r, data, sizeof(param_type));
|
||||
} else {
|
||||
result = false;
|
||||
NOTREACHED();
|
||||
}
|
||||
return result;
|
||||
return m->ReadBytesInto(iter, r, sizeof(*r));
|
||||
}
|
||||
static void Log(const param_type& p, std::wstring* l) {
|
||||
l->append(StringPrintf(L"%ull", p));
|
||||
@@ -238,20 +215,10 @@ template <>
|
||||
struct ParamTraitsFundamental<double> {
|
||||
typedef double param_type;
|
||||
static void Write(Message* m, const param_type& p) {
|
||||
m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type));
|
||||
m->WriteDouble(p);
|
||||
}
|
||||
static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
|
||||
const char *data;
|
||||
int data_size = 0;
|
||||
bool result = m->ReadData(iter, &data, &data_size);
|
||||
if (result && data_size == sizeof(param_type)) {
|
||||
memcpy(r, data, sizeof(param_type));
|
||||
} else {
|
||||
result = false;
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
return result;
|
||||
return m->ReadDouble(iter, r);
|
||||
}
|
||||
static void Log(const param_type& p, std::wstring* l) {
|
||||
l->append(StringPrintf(L"e", p));
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "BackgroundChildImpl.h"
|
||||
#include "BackgroundParentImpl.h"
|
||||
#include "base/process_util.h"
|
||||
#include "base/task.h"
|
||||
#include "FileDescriptor.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "InputStreamUtils.h"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/task.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "chrome/common/process_watcher.h"
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
|
||||
+25
-50
@@ -232,12 +232,7 @@ struct ParamTraits<int8_t>
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
const char* outp;
|
||||
if (!aMsg->ReadBytes(aIter, &outp, sizeof(*aResult)))
|
||||
return false;
|
||||
|
||||
*aResult = *reinterpret_cast<const paramType*>(outp);
|
||||
return true;
|
||||
return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -253,12 +248,7 @@ struct ParamTraits<uint8_t>
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
const char* outp;
|
||||
if (!aMsg->ReadBytes(aIter, &outp, sizeof(*aResult)))
|
||||
return false;
|
||||
|
||||
*aResult = *reinterpret_cast<const paramType*>(outp);
|
||||
return true;
|
||||
return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -309,14 +299,12 @@ struct ParamTraits<nsACString>
|
||||
}
|
||||
|
||||
uint32_t length;
|
||||
if (ReadParam(aMsg, aIter, &length)) {
|
||||
const char* buf;
|
||||
if (aMsg->ReadBytes(aIter, &buf, length)) {
|
||||
aResult->Assign(buf, length);
|
||||
return true;
|
||||
}
|
||||
if (!ReadParam(aMsg, aIter, &length)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
aResult->SetLength(length);
|
||||
|
||||
return aMsg->ReadBytesInto(aIter, aResult->BeginWriting(), length);
|
||||
}
|
||||
|
||||
static void Log(const paramType& aParam, std::wstring* aLog)
|
||||
@@ -359,15 +347,12 @@ struct ParamTraits<nsAString>
|
||||
}
|
||||
|
||||
uint32_t length;
|
||||
if (ReadParam(aMsg, aIter, &length)) {
|
||||
const char16_t* buf;
|
||||
if (aMsg->ReadBytes(aIter, reinterpret_cast<const char**>(&buf),
|
||||
length * sizeof(char16_t))) {
|
||||
aResult->Assign(buf, length);
|
||||
return true;
|
||||
}
|
||||
if (!ReadParam(aMsg, aIter, &length)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
aResult->SetLength(length);
|
||||
|
||||
return aMsg->ReadBytesInto(aIter, aResult->BeginWriting(), length * sizeof(char16_t));
|
||||
}
|
||||
|
||||
static void Log(const paramType& aParam, std::wstring* aLog)
|
||||
@@ -474,14 +459,8 @@ struct ParamTraits<nsTArray<E>>
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* outdata;
|
||||
if (!aMsg->ReadBytes(aIter, &outdata, pickledLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
E* elements = aResult->AppendElements(length);
|
||||
|
||||
memcpy(elements, outdata, pickledLength);
|
||||
return aMsg->ReadBytesInto(aIter, elements, pickledLength);
|
||||
} else {
|
||||
aResult->SetCapacity(length);
|
||||
|
||||
@@ -491,9 +470,8 @@ struct ParamTraits<nsTArray<E>>
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Log(const paramType& aParam, std::wstring* aLog)
|
||||
@@ -552,11 +530,7 @@ struct ParamTraits<float>
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
const char* outFloat;
|
||||
if (!aMsg->ReadBytes(aIter, &outFloat, sizeof(float)))
|
||||
return false;
|
||||
*aResult = *reinterpret_cast<const float*>(outFloat);
|
||||
return true;
|
||||
return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
|
||||
}
|
||||
|
||||
static void Log(const paramType& aParam, std::wstring* aLog)
|
||||
@@ -749,16 +723,17 @@ struct ParamTraits<mozilla::SerializedStructuredCloneBuffer>
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aResult->dataLength) {
|
||||
const char** buffer =
|
||||
const_cast<const char**>(reinterpret_cast<char**>(&aResult->data));
|
||||
// Structured clone data must be 64-bit aligned.
|
||||
if (!aMsg->ReadBytes(aIter, buffer, aResult->dataLength,
|
||||
sizeof(uint64_t))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!aResult->dataLength) {
|
||||
aResult->data = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char** buffer =
|
||||
const_cast<const char**>(reinterpret_cast<char**>(&aResult->data));
|
||||
// Structured clone data must be 64-bit aligned.
|
||||
if (!const_cast<Message*>(aMsg)->FlattenBytes(aIter, buffer, aResult->dataLength,
|
||||
sizeof(uint64_t))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -752,9 +752,9 @@ MessageChannel::Echo(Message* aMsg)
|
||||
bool
|
||||
MessageChannel::Send(Message* aMsg)
|
||||
{
|
||||
if (aMsg->capacity() >= kMinTelemetryMessageSize) {
|
||||
if (aMsg->size() >= kMinTelemetryMessageSize) {
|
||||
Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE,
|
||||
nsDependentCString(aMsg->name()), aMsg->capacity());
|
||||
nsDependentCString(aMsg->name()), aMsg->size());
|
||||
}
|
||||
|
||||
CxxStackFrame frame(*this, OUT_MESSAGE, aMsg);
|
||||
@@ -1053,9 +1053,9 @@ MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction)
|
||||
bool
|
||||
MessageChannel::Send(Message* aMsg, Message* aReply)
|
||||
{
|
||||
if (aMsg->capacity() >= kMinTelemetryMessageSize) {
|
||||
if (aMsg->size() >= kMinTelemetryMessageSize) {
|
||||
Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE,
|
||||
nsDependentCString(aMsg->name()), aMsg->capacity());
|
||||
nsDependentCString(aMsg->name()), aMsg->size());
|
||||
}
|
||||
|
||||
nsAutoPtr<Message> msg(aMsg);
|
||||
@@ -1243,9 +1243,9 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
|
||||
MOZ_RELEASE_ASSERT(reply->is_sync());
|
||||
|
||||
*aReply = Move(*reply);
|
||||
if (aReply->capacity() >= kMinTelemetryMessageSize) {
|
||||
if (aReply->size() >= kMinTelemetryMessageSize) {
|
||||
Telemetry::Accumulate(Telemetry::IPC_REPLY_SIZE,
|
||||
nsDependentCString(msgName), aReply->capacity());
|
||||
nsDependentCString(msgName), aReply->size());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "mozilla/ipc/MessageChannel.h"
|
||||
#include "mozilla/ipc/BrowserProcessSubThread.h"
|
||||
#include "mozilla/ipc/ProtocolUtils.h"
|
||||
#include "chrome/common/ipc_channel.h"
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
@@ -157,6 +158,7 @@ ProcessLink::EchoMessage(Message *msg)
|
||||
void
|
||||
ProcessLink::SendMessage(Message *msg)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(msg->size() < IPC::Channel::kMaximumMessageSize);
|
||||
mChan->AssertWorkerThread();
|
||||
mChan->mMonitor->AssertCurrentThreadOwns();
|
||||
|
||||
|
||||
+64
-20
@@ -51,6 +51,11 @@ lowered form of |tu|'''
|
||||
## Helper code
|
||||
##
|
||||
|
||||
def hashfunc(value):
|
||||
h = hash(value) % 2**32
|
||||
if h < 0: h += 2**32
|
||||
return h
|
||||
|
||||
_NULL_ACTOR_ID = ExprLiteral.ZERO
|
||||
_FREED_ACTOR_ID = ExprLiteral.ONE
|
||||
|
||||
@@ -696,6 +701,7 @@ class StructDecl(ipdl.ast.StructDecl, HasFQName):
|
||||
|
||||
class _StructField(_CompoundTypeComponent):
|
||||
def __init__(self, ipdltype, name, sd, side=None):
|
||||
self.basename = name
|
||||
fname = name
|
||||
special = _hasVisibleActor(ipdltype)
|
||||
if special:
|
||||
@@ -4403,12 +4409,13 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
ExprLiteral.ZERO),
|
||||
cond=ExprBinary(ivar, '<', lenvar),
|
||||
update=ExprPrefixUnop(ivar, '++'))
|
||||
forwrite.addstmt(StmtExpr(
|
||||
self.write(eltipdltype, ExprIndex(var, ivar), msgvar)))
|
||||
forwrite.addstmt(
|
||||
self.checkedWrite(eltipdltype, ExprIndex(var, ivar), msgvar,
|
||||
sentinelKey=arraytype.name()))
|
||||
write.addstmts([
|
||||
StmtDecl(Decl(Type.UINT32, lenvar.name),
|
||||
init=_callCxxArrayLength(var)),
|
||||
StmtExpr(self.write(None, lenvar, msgvar)),
|
||||
self.checkedWrite(None, lenvar, msgvar, sentinelKey=('length', arraytype.name())),
|
||||
Whitespace.NL,
|
||||
forwrite
|
||||
])
|
||||
@@ -4422,13 +4429,15 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
forread.addstmt(
|
||||
self.checkedRead(eltipdltype, ExprAddrOf(ExprIndex(favar, ivar)),
|
||||
msgvar, itervar, errfnRead,
|
||||
'\'' + eltipdltype.name() + '[i]\''))
|
||||
'\'' + eltipdltype.name() + '[i]\'',
|
||||
sentinelKey=arraytype.name()))
|
||||
read.addstmts([
|
||||
StmtDecl(Decl(_cxxArrayType(_cxxBareType(arraytype.basetype, self.side)), favar.name)),
|
||||
StmtDecl(Decl(Type.UINT32, lenvar.name)),
|
||||
self.checkedRead(None, ExprAddrOf(lenvar),
|
||||
msgvar, itervar, errfnArrayLength,
|
||||
[ arraytype.name() ]),
|
||||
[ arraytype.name() ],
|
||||
sentinelKey=('length', arraytype.name())),
|
||||
Whitespace.NL,
|
||||
StmtExpr(_callCxxArraySetLength(favar, lenvar)),
|
||||
forread,
|
||||
@@ -4560,10 +4569,10 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
for f in sd.fields:
|
||||
desc = '\'' + f.getMethod().name + '\' (' + f.ipdltype.name() + \
|
||||
') member of \'' + intype.name + '\''
|
||||
writefield = StmtExpr(self.write(f.ipdltype, get('.', f), msgvar))
|
||||
writefield = self.checkedWrite(f.ipdltype, get('.', f), msgvar, sentinelKey=f.basename)
|
||||
readfield = self.checkedRead(f.ipdltype,
|
||||
ExprAddrOf(get('->', f)),
|
||||
msgvar, itervar, errfnRead, desc)
|
||||
msgvar, itervar, errfnRead, desc, sentinelKey=f.basename)
|
||||
if f.special and f.side != self.side:
|
||||
writefield = Whitespace(
|
||||
"// skipping actor field that's meaningless on this side\n", indent=1)
|
||||
@@ -4596,13 +4605,14 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
ct = c.ipdltype
|
||||
isactor = (ct.isIPDL() and ct.isActor())
|
||||
caselabel = CaseLabel(typename +'::'+ c.enum())
|
||||
origenum = c.enum()
|
||||
|
||||
writecase = StmtBlock()
|
||||
if c.special and c.side != self.side:
|
||||
writecase.addstmt(_fatalError('wrong side!'))
|
||||
else:
|
||||
wexpr = ExprCall(ExprSelect(var, '.', c.getTypeName()))
|
||||
writecase.addstmt(StmtExpr(self.write(ct, wexpr, msgvar)))
|
||||
writecase.addstmt(self.checkedWrite(ct, wexpr, msgvar, sentinelKey=c.enum()))
|
||||
|
||||
writecase.addstmt(StmtReturn())
|
||||
writeswitch.addcase(caselabel, writecase)
|
||||
@@ -4622,11 +4632,12 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
readcase.addstmts([
|
||||
StmtDecl(Decl(ct, tmpvar.name), init=c.defaultValue()),
|
||||
StmtExpr(ExprAssn(ExprDeref(var), tmpvar)),
|
||||
StmtReturn(self.read(
|
||||
self.checkedRead(
|
||||
c.ipdltype,
|
||||
ExprAddrOf(ExprCall(ExprSelect(var, '->',
|
||||
c.getTypeName()))),
|
||||
msgvar, itervar))
|
||||
msgvar, itervar, errfnRead, 'Union type', sentinelKey=origenum),
|
||||
StmtReturn(ExprLiteral.TRUE)
|
||||
])
|
||||
|
||||
readswitch.addcase(caselabel, readcase)
|
||||
@@ -4640,8 +4651,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
write = MethodDefn(self.writeMethodDecl(intype, var))
|
||||
write.addstmts([
|
||||
uniontdef,
|
||||
StmtExpr(self.write(
|
||||
None, ExprCall(Type.INT, args=[ ud.callType(var) ]), msgvar)),
|
||||
self.checkedWrite(
|
||||
None, ExprCall(Type.INT, args=[ ud.callType(var) ]), msgvar,
|
||||
sentinelKey=uniontype.name()),
|
||||
Whitespace.NL,
|
||||
writeswitch
|
||||
])
|
||||
@@ -4652,7 +4664,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
StmtDecl(Decl(Type.INT, typevar.name)),
|
||||
self.checkedRead(
|
||||
None, ExprAddrOf(typevar), msgvar, itervar, errfnUnionType,
|
||||
[ uniontype.name() ]),
|
||||
[ uniontype.name() ],
|
||||
sentinelKey=uniontype.name()),
|
||||
Whitespace.NL,
|
||||
readswitch,
|
||||
])
|
||||
@@ -4698,6 +4711,20 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
return self.maybeAddNullabilityArg(
|
||||
ipdltype, ExprCall(read, args=[ expr, from_, iterexpr ]))
|
||||
|
||||
def checkedWrite(self, ipdltype, expr, msgvar, sentinelKey, this=None):
|
||||
assert sentinelKey
|
||||
|
||||
write = StmtExpr(self.write(ipdltype, expr, msgvar, this))
|
||||
|
||||
sentinel = StmtExpr(ExprCall(ExprSelect(msgvar, '->', 'WriteSentinel'),
|
||||
args=[ ExprLiteral.Int(hashfunc(sentinelKey)) ]))
|
||||
block = Block()
|
||||
block.addstmts([
|
||||
write,
|
||||
Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n', indent=1),
|
||||
sentinel ])
|
||||
return block
|
||||
|
||||
|
||||
def visitMessageDecl(self, md):
|
||||
isctor = md.decl.type.isCtor()
|
||||
@@ -5075,7 +5102,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
init=ExprCall(ExprVar(md.pqMsgCtorFunc()),
|
||||
args=[ routingId ])) ]
|
||||
+ [ Whitespace.NL ]
|
||||
+ [ StmtExpr(self.write(p.ipdltype, p.var(), msgvar, this))
|
||||
+ [ self.checkedWrite(p.ipdltype, p.var(), msgvar, sentinelKey=p.name, this=this)
|
||||
for p in md.params ]
|
||||
+ [ Whitespace.NL ]
|
||||
+ self.setMessageFlags(md, msgvar, reply=0))
|
||||
@@ -5094,7 +5121,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
[ StmtExpr(ExprAssn(
|
||||
replyvar, ExprCall(ExprVar(md.pqReplyCtorFunc()), args=[ routingId ]))),
|
||||
Whitespace.NL ]
|
||||
+ [ StmtExpr(self.write(r.ipdltype, r.var(), replyvar))
|
||||
+ [ self.checkedWrite(r.ipdltype, r.var(), replyvar, sentinelKey=r.name)
|
||||
for r in md.returns ]
|
||||
+ self.setMessageFlags(md, replyvar, reply=1)
|
||||
+ [ self.logMessage(md, replyvar, 'Sending reply ') ])
|
||||
@@ -5150,7 +5177,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
decls = [ StmtDecl(Decl(handletype, handlevar.name)) ]
|
||||
reads = [ self.checkedRead(None, ExprAddrOf(handlevar), msgexpr,
|
||||
ExprAddrOf(self.itervar),
|
||||
errfn, "'%s'" % handletype.name) ]
|
||||
errfn, "'%s'" % handletype.name,
|
||||
sentinelKey='actor') ]
|
||||
start = 1
|
||||
|
||||
stmts.extend((
|
||||
@@ -5162,7 +5190,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
+ [ Whitespace.NL ]
|
||||
+ reads + [ self.checkedRead(p.ipdltype, ExprAddrOf(p.var()),
|
||||
msgexpr, ExprAddrOf(itervar),
|
||||
errfn, "'%s'" % p.bareType(side).name)
|
||||
errfn, "'%s'" % p.bareType(side).name,
|
||||
sentinelKey=p.name)
|
||||
for p in md.params[start:] ]
|
||||
+ [ self.endRead(msgvar, itervar) ]))
|
||||
|
||||
@@ -5185,7 +5214,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
+ [ self.checkedRead(r.ipdltype, r.var(),
|
||||
ExprAddrOf(self.replyvar),
|
||||
ExprAddrOf(self.itervar),
|
||||
errfn, "'%s'" % r.bareType(side).name)
|
||||
errfn, "'%s'" % r.bareType(side).name,
|
||||
sentinelKey=r.name)
|
||||
for r in md.returns ]
|
||||
+ [ self.endRead(self.replyvar, itervar) ])
|
||||
|
||||
@@ -5336,14 +5366,28 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
ifbad.addifstmts(_badTransition())
|
||||
return [ ifbad ]
|
||||
|
||||
def checkedRead(self, ipdltype, expr, msgexpr, iterexpr, errfn, paramtype):
|
||||
def checkedRead(self, ipdltype, expr, msgexpr, iterexpr, errfn, paramtype, sentinelKey, sentinel=True):
|
||||
ifbad = StmtIf(ExprNot(self.read(ipdltype, expr, msgexpr, iterexpr)))
|
||||
if isinstance(paramtype, list):
|
||||
errorcall = errfn(*paramtype)
|
||||
else:
|
||||
errorcall = errfn('Error deserializing ' + paramtype)
|
||||
ifbad.addifstmts(errorcall)
|
||||
return ifbad
|
||||
|
||||
block = Block()
|
||||
block.addstmt(ifbad)
|
||||
|
||||
if sentinel:
|
||||
assert sentinelKey
|
||||
|
||||
block.addstmt(Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n', indent=1))
|
||||
read = ExprCall(ExprSelect(msgexpr, '->', 'ReadSentinel'),
|
||||
args=[ iterexpr, ExprLiteral.Int(hashfunc(sentinelKey)) ])
|
||||
ifsentinel = StmtIf(ExprNot(read))
|
||||
ifsentinel.addifstmts(errorcall)
|
||||
block.addstmt(ifsentinel)
|
||||
|
||||
return block
|
||||
|
||||
def endRead(self, msgexpr, iterexpr):
|
||||
return StmtExpr(ExprCall(ExprSelect(msgexpr, '.', 'EndRead'),
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/task.h"
|
||||
#include "base/thread.h"
|
||||
|
||||
#include "nsRegion.h"
|
||||
|
||||
@@ -110,9 +110,8 @@ using namespace mozilla::ipc;
|
||||
/*static*/ void
|
||||
ParamTraits<Bad>::Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
char* ptr = aMsg->BeginWriteData(4);
|
||||
ptr -= intptr_t(sizeof(int));
|
||||
ptr -= intptr_t(sizeof(ActorHandle));
|
||||
// Skip past the sentinel for the actor as well as the actor.
|
||||
int32_t* ptr = aMsg->GetInt32PtrForTest(2 * sizeof(int32_t));
|
||||
ActorHandle* ah = reinterpret_cast<ActorHandle*>(ptr);
|
||||
if (ah->mId != -3)
|
||||
fail("guessed wrong offset (value is %d, should be -3)", ah->mId);
|
||||
@@ -122,9 +121,6 @@ ParamTraits<Bad>::Write(Message* aMsg, const paramType& aParam)
|
||||
/*static*/ bool
|
||||
ParamTraits<Bad>::Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
const char* ptr;
|
||||
int len;
|
||||
mozilla::Unused << aMsg->ReadData(aIter, &ptr, &len);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "TestBridgeMain.h"
|
||||
|
||||
#include "base/task.h"
|
||||
#include "IPDLUnitTests.h" // fail etc.
|
||||
#include "IPDLUnitTestSubprocess.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "TestCrashCleanup.h"
|
||||
|
||||
#include "base/task.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "TestEndpointBridgeMain.h"
|
||||
|
||||
#include "base/task.h"
|
||||
#include "IPDLUnitTests.h" // fail etc.
|
||||
#include "IPDLUnitTestSubprocess.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
|
||||
#include "base/task.h"
|
||||
#include "base/thread.h"
|
||||
|
||||
#include "TestEndpointOpens.h"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "TestInterruptErrorCleanup.h"
|
||||
|
||||
#include "base/task.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "TestInterruptShutdownRace.h"
|
||||
|
||||
#include "base/task.h"
|
||||
#include "IPDLUnitTests.h" // fail etc.
|
||||
#include "IPDLUnitTestSubprocess.h"
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "base/task.h"
|
||||
#include "base/thread.h"
|
||||
|
||||
#include "TestOpens.h"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "TestStackHooks.h"
|
||||
|
||||
#include "base/task.h"
|
||||
#include "IPDLUnitTests.h" // fail etc.
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "TestSyncHang.h"
|
||||
#include "base/task.h"
|
||||
#include "mozilla/ipc/GeckoChildProcessHost.h"
|
||||
|
||||
#include "IPDLUnitTests.h" // fail etc.
|
||||
|
||||
@@ -329,6 +329,11 @@ public:
|
||||
void reportAllocOverflow() const
|
||||
{
|
||||
}
|
||||
|
||||
bool checkSimulatedOOM() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* ifdef __cplusplus */
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#ifndef mozilla_AllocPolicy_h
|
||||
#define mozilla_AllocPolicy_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/TemplateLib.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
/* -*- 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 mozilla_BufferList_h
|
||||
#define mozilla_BufferList_h
|
||||
|
||||
#include <algorithm>
|
||||
#include "mozilla/AllocPolicy.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Types.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include <string.h>
|
||||
|
||||
// BufferList represents a sequence of buffers of data. A BufferList can choose
|
||||
// to own its buffers or not. The class handles writing to the buffers,
|
||||
// iterating over them, and reading data out. Unlike SegmentedVector, the
|
||||
// buffers may be of unequal size. Like SegmentedVector, BufferList is a nice
|
||||
// way to avoid large contiguous allocations (which can trigger OOMs).
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template<typename AllocPolicy>
|
||||
class BufferList : private AllocPolicy
|
||||
{
|
||||
// Each buffer in a BufferList has a size and a capacity. The first mSize
|
||||
// bytes are initialized and the remaining |mCapacity - mSize| bytes are free.
|
||||
struct Segment
|
||||
{
|
||||
char* mData;
|
||||
size_t mSize;
|
||||
size_t mCapacity;
|
||||
|
||||
Segment(char* aData, size_t aSize, size_t aCapacity)
|
||||
: mData(aData),
|
||||
mSize(aSize),
|
||||
mCapacity(aCapacity)
|
||||
{
|
||||
}
|
||||
|
||||
Segment(const Segment&) = delete;
|
||||
Segment& operator=(const Segment&) = delete;
|
||||
|
||||
Segment(Segment&& aOther)
|
||||
: mData(Move(aOther.mData)),
|
||||
mSize(Move(aOther.mSize)),
|
||||
mCapacity(Move(aOther.mCapacity))
|
||||
{
|
||||
}
|
||||
|
||||
Segment& operator=(Segment&& aRhs) {
|
||||
MOZ_ASSERT(&aRhs != this, "self-move-assignment not allowed");
|
||||
this->~Segment();
|
||||
new(this) Segment(Move(aRhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
char* Start() const { return mData; }
|
||||
char* End() const { return mData + mSize; }
|
||||
};
|
||||
|
||||
public:
|
||||
// For the convenience of callers, all segments are required to be a multiple
|
||||
// of 8 bytes in capacity. Also, every buffer except the last one is required
|
||||
// to be full (i.e., size == capacity). Therefore, a byte at offset N within
|
||||
// the BufferList and stored in memory at an address A will satisfy
|
||||
// (N % Align == A % Align) if Align == 2, 4, or 8.
|
||||
//
|
||||
// NB: FlattenBytes can create non-full segments in the middle of the
|
||||
// list. However, it ensures that these buffers are 8-byte aligned, so the
|
||||
// offset invariant is not violated.
|
||||
static const size_t kSegmentAlignment = 8;
|
||||
|
||||
// Allocate a BufferList. The BufferList will free all its buffers when it is
|
||||
// destroyed. An initial buffer of size aInitialSize and capacity
|
||||
// aInitialCapacity is allocated automatically. This data will be contiguous
|
||||
// an can be accessed via |Start()|. Subsequent buffers will be allocated with
|
||||
// capacity aStandardCapacity.
|
||||
BufferList(size_t aInitialSize,
|
||||
size_t aInitialCapacity,
|
||||
size_t aStandardCapacity,
|
||||
AllocPolicy aAP = AllocPolicy())
|
||||
: AllocPolicy(aAP),
|
||||
mOwning(true),
|
||||
mSize(0),
|
||||
mStandardCapacity(aStandardCapacity)
|
||||
{
|
||||
MOZ_ASSERT(aInitialCapacity % kSegmentAlignment == 0);
|
||||
MOZ_ASSERT(aStandardCapacity % kSegmentAlignment == 0);
|
||||
|
||||
if (aInitialCapacity) {
|
||||
AllocateSegment(aInitialSize, aInitialCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
BufferList(const BufferList& aOther) = delete;
|
||||
|
||||
BufferList(BufferList&& aOther)
|
||||
: mOwning(aOther.mOwning),
|
||||
mSegments(Move(aOther.mSegments)),
|
||||
mSize(aOther.mSize),
|
||||
mStandardCapacity(aOther.mStandardCapacity)
|
||||
{
|
||||
aOther.mSegments.clear();
|
||||
aOther.mSize = 0;
|
||||
}
|
||||
|
||||
BufferList& operator=(const BufferList& aOther) = delete;
|
||||
|
||||
BufferList& operator=(BufferList&& aOther)
|
||||
{
|
||||
Clear();
|
||||
|
||||
mOwning = aOther.mOwning;
|
||||
mSegments = Move(aOther.mSegments);
|
||||
mSize = aOther.mSize;
|
||||
aOther.mSegments.clear();
|
||||
aOther.mSize = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~BufferList() { Clear(); }
|
||||
|
||||
// Returns the sum of the sizes of all the buffers.
|
||||
size_t Size() const { return mSize; }
|
||||
|
||||
void Clear()
|
||||
{
|
||||
if (mOwning) {
|
||||
for (Segment& segment : mSegments) {
|
||||
this->free_(segment.mData);
|
||||
}
|
||||
}
|
||||
mSegments.clear();
|
||||
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
// Iterates over bytes in the segments. You can advance it by as many bytes as
|
||||
// you choose.
|
||||
class IterImpl
|
||||
{
|
||||
// Invariants:
|
||||
// (0) mSegment <= bufferList.mSegments.size()
|
||||
// (1) mData <= mDataEnd
|
||||
// (2) If mSegment is not the last segment, mData < mDataEnd
|
||||
uintptr_t mSegment;
|
||||
char* mData;
|
||||
char* mDataEnd;
|
||||
|
||||
friend class BufferList;
|
||||
|
||||
public:
|
||||
explicit IterImpl(const BufferList& aBuffers)
|
||||
: mSegment(0),
|
||||
mData(nullptr),
|
||||
mDataEnd(nullptr)
|
||||
{
|
||||
if (!aBuffers.mSegments.empty()) {
|
||||
mData = aBuffers.mSegments[0].Start();
|
||||
mDataEnd = aBuffers.mSegments[0].End();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a pointer to the raw data. It is valid to access up to
|
||||
// RemainingInSegment bytes of this buffer.
|
||||
char* Data() const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!Done());
|
||||
return mData;
|
||||
}
|
||||
|
||||
// Returns true if the memory in the range [Data(), Data() + aBytes) is all
|
||||
// part of one contiguous buffer.
|
||||
bool HasRoomFor(size_t aBytes) const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mData <= mDataEnd);
|
||||
return size_t(mDataEnd - mData) >= aBytes;
|
||||
}
|
||||
|
||||
// Returns the maximum value aBytes for which HasRoomFor(aBytes) will be
|
||||
// true.
|
||||
size_t RemainingInSegment() const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mData <= mDataEnd);
|
||||
return mDataEnd - mData;
|
||||
}
|
||||
|
||||
// Advances the iterator by aBytes bytes. aBytes must be less than
|
||||
// RemainingInSegment(). If advancing by aBytes takes the iterator to the
|
||||
// end of a buffer, it will be moved to the beginning of the next buffer
|
||||
// unless it is the last buffer.
|
||||
void Advance(const BufferList& aBuffers, size_t aBytes)
|
||||
{
|
||||
const Segment& segment = aBuffers.mSegments[mSegment];
|
||||
MOZ_RELEASE_ASSERT(segment.Start() <= mData);
|
||||
MOZ_RELEASE_ASSERT(mData <= mDataEnd);
|
||||
MOZ_RELEASE_ASSERT(mDataEnd == segment.End());
|
||||
|
||||
MOZ_RELEASE_ASSERT(HasRoomFor(aBytes));
|
||||
mData += aBytes;
|
||||
|
||||
if (mData == mDataEnd && mSegment + 1 < aBuffers.mSegments.length()) {
|
||||
mSegment++;
|
||||
const Segment& nextSegment = aBuffers.mSegments[mSegment];
|
||||
mData = nextSegment.Start();
|
||||
mDataEnd = nextSegment.End();
|
||||
MOZ_RELEASE_ASSERT(mData < mDataEnd);
|
||||
}
|
||||
}
|
||||
|
||||
// Advance the iterator by aBytes, possibly crossing segments. This function
|
||||
// returns false if it runs out of buffers to advance through. Otherwise it
|
||||
// returns true.
|
||||
bool AdvanceAcrossSegments(const BufferList& aBuffers, size_t aBytes)
|
||||
{
|
||||
size_t bytes = aBytes;
|
||||
while (bytes) {
|
||||
size_t toAdvance = std::min(bytes, RemainingInSegment());
|
||||
if (!toAdvance) {
|
||||
return false;
|
||||
}
|
||||
Advance(aBuffers, toAdvance);
|
||||
bytes -= toAdvance;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true when the iterator reaches the end of the BufferList.
|
||||
bool Done() const
|
||||
{
|
||||
return mData == mDataEnd;
|
||||
}
|
||||
};
|
||||
|
||||
// Special convenience method that returns Iter().Data().
|
||||
char* Start() { return mSegments[0].mData; }
|
||||
|
||||
IterImpl Iter() const { return IterImpl(*this); }
|
||||
|
||||
// Copies aSize bytes from aData into the BufferList. The storage for these
|
||||
// bytes may be split across multiple buffers. Size() is increased by aSize.
|
||||
inline bool WriteBytes(const char* aData, size_t aSize);
|
||||
|
||||
// Copies possibly non-contiguous byte range starting at aIter into
|
||||
// aData. aIter is advanced by aSize bytes. Returns false if it runs out of
|
||||
// data before aSize.
|
||||
inline bool ReadBytes(IterImpl& aIter, char* aData, size_t aSize) const;
|
||||
|
||||
// FlattenBytes reconfigures the BufferList so that data in the range
|
||||
// [aIter, aIter + aSize) is stored contiguously. A pointer to this data is
|
||||
// returned in aOutData. Returns false if not enough data is available. All
|
||||
// other iterators are invalidated by this method.
|
||||
//
|
||||
// This method requires aIter and aSize to be 8-byte aligned.
|
||||
inline bool FlattenBytes(IterImpl& aIter, const char** aOutData, size_t aSize);
|
||||
|
||||
// Return a new BufferList that shares storage with this BufferList. The new
|
||||
// BufferList is read-only. It allows iteration over aSize bytes starting at
|
||||
// aIter. Borrow can fail, in which case *aSuccess will be false upon
|
||||
// return. The borrowed BufferList can use a different AllocPolicy than the
|
||||
// original one. However, it is not responsible for freeing buffers, so the
|
||||
// AllocPolicy is only used for the buffer vector.
|
||||
template<typename BorrowingAllocPolicy>
|
||||
BufferList<BorrowingAllocPolicy> Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess,
|
||||
BorrowingAllocPolicy aAP = BorrowingAllocPolicy());
|
||||
|
||||
private:
|
||||
explicit BufferList(AllocPolicy aAP)
|
||||
: AllocPolicy(aAP),
|
||||
mOwning(false),
|
||||
mSize(0),
|
||||
mStandardCapacity(0)
|
||||
{
|
||||
}
|
||||
|
||||
void* AllocateSegment(size_t aSize, size_t aCapacity)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mOwning);
|
||||
|
||||
char* data = this->template pod_malloc<char>(aCapacity);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!mSegments.append(Segment(data, aSize, aCapacity))) {
|
||||
this->free_(data);
|
||||
return nullptr;
|
||||
}
|
||||
mSize += aSize;
|
||||
return data;
|
||||
}
|
||||
|
||||
bool mOwning;
|
||||
Vector<Segment, 1, AllocPolicy> mSegments;
|
||||
size_t mSize;
|
||||
size_t mStandardCapacity;
|
||||
};
|
||||
|
||||
template<typename AllocPolicy>
|
||||
bool
|
||||
BufferList<AllocPolicy>::WriteBytes(const char* aData, size_t aSize)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mOwning);
|
||||
MOZ_RELEASE_ASSERT(mStandardCapacity);
|
||||
|
||||
size_t copied = 0;
|
||||
size_t remaining = aSize;
|
||||
|
||||
if (!mSegments.empty()) {
|
||||
Segment& lastSegment = mSegments.back();
|
||||
|
||||
size_t toCopy = std::min(aSize, lastSegment.mCapacity - lastSegment.mSize);
|
||||
memcpy(lastSegment.mData + lastSegment.mSize, aData, toCopy);
|
||||
lastSegment.mSize += toCopy;
|
||||
mSize += toCopy;
|
||||
|
||||
copied += toCopy;
|
||||
remaining -= toCopy;
|
||||
}
|
||||
|
||||
while (remaining) {
|
||||
size_t toCopy = std::min(remaining, mStandardCapacity);
|
||||
|
||||
void* data = AllocateSegment(toCopy, mStandardCapacity);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
memcpy(data, aData + copied, toCopy);
|
||||
|
||||
copied += toCopy;
|
||||
remaining -= toCopy;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename AllocPolicy>
|
||||
bool
|
||||
BufferList<AllocPolicy>::ReadBytes(IterImpl& aIter, char* aData, size_t aSize) const
|
||||
{
|
||||
size_t copied = 0;
|
||||
size_t remaining = aSize;
|
||||
while (remaining) {
|
||||
size_t toCopy = std::min(aIter.RemainingInSegment(), remaining);
|
||||
if (!toCopy) {
|
||||
// We've run out of data in the last segment.
|
||||
return false;
|
||||
}
|
||||
memcpy(aData + copied, aIter.Data(), toCopy);
|
||||
copied += toCopy;
|
||||
remaining -= toCopy;
|
||||
|
||||
aIter.Advance(*this, toCopy);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename AllocPolicy>
|
||||
bool
|
||||
BufferList<AllocPolicy>::FlattenBytes(IterImpl& aIter, const char** aOutData, size_t aSize)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aSize);
|
||||
MOZ_RELEASE_ASSERT(mOwning);
|
||||
|
||||
if (aIter.HasRoomFor(aSize)) {
|
||||
// If the data is already contiguous, just return a pointer.
|
||||
*aOutData = aIter.Data();
|
||||
aIter.Advance(*this, aSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This buffer will become the new contiguous segment.
|
||||
char* buffer = this->template pod_malloc<char>(Size());
|
||||
if (!buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t copied = 0;
|
||||
size_t offset;
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < mSegments.length(); i++) {
|
||||
Segment& segment = mSegments[i];
|
||||
memcpy(buffer + copied, segment.Start(), segment.mSize);
|
||||
|
||||
if (i == aIter.mSegment) {
|
||||
offset = copied + (aIter.mData - segment.Start());
|
||||
|
||||
// Do we have aSize bytes after aIter?
|
||||
if (Size() - offset >= aSize) {
|
||||
found = true;
|
||||
*aOutData = buffer + offset;
|
||||
|
||||
aIter.mSegment = 0;
|
||||
aIter.mData = buffer + offset + aSize;
|
||||
aIter.mDataEnd = buffer + Size();
|
||||
}
|
||||
}
|
||||
|
||||
this->free_(segment.mData);
|
||||
|
||||
copied += segment.mSize;
|
||||
}
|
||||
|
||||
mSegments.clear();
|
||||
mSegments.infallibleAppend(Segment(buffer, Size(), Size()));
|
||||
|
||||
if (!found) {
|
||||
aIter.mSegment = 0;
|
||||
aIter.mData = Start();
|
||||
aIter.mDataEnd = Start() + Size();
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
template<typename AllocPolicy> template<typename BorrowingAllocPolicy>
|
||||
BufferList<BorrowingAllocPolicy>
|
||||
BufferList<AllocPolicy>::Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess,
|
||||
BorrowingAllocPolicy aAP)
|
||||
{
|
||||
BufferList<BorrowingAllocPolicy> result(aAP);
|
||||
|
||||
size_t size = aSize;
|
||||
while (size) {
|
||||
size_t toAdvance = std::min(size, aIter.RemainingInSegment());
|
||||
|
||||
if (!toAdvance || !result.mSegments.append(Segment(aIter.mData, toAdvance, toAdvance))) {
|
||||
*aSuccess = false;
|
||||
return result;
|
||||
}
|
||||
aIter.Advance(*this, toAdvance);
|
||||
size -= toAdvance;
|
||||
}
|
||||
|
||||
result.mSize = aSize;
|
||||
*aSuccess = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_BufferList_h */
|
||||
@@ -24,6 +24,7 @@ EXPORTS.mozilla = [
|
||||
'Attributes.h',
|
||||
'BinarySearch.h',
|
||||
'BloomFilter.h',
|
||||
'BufferList.h',
|
||||
'Casting.h',
|
||||
'ChaosMode.h',
|
||||
'Char16.h',
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
/* -*- Mode: C++; tab-width: 9; 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/. */
|
||||
|
||||
// This is included first to ensure it doesn't implicitly depend on anything
|
||||
// else.
|
||||
#include "mozilla/BufferList.h"
|
||||
|
||||
// It would be nice if we could use the InfallibleAllocPolicy from mozalloc,
|
||||
// but MFBT cannot use mozalloc.
|
||||
class InfallibleAllocPolicy
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
T* pod_malloc(size_t aNumElems)
|
||||
{
|
||||
if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
|
||||
MOZ_CRASH("TestBufferList.cpp: overflow");
|
||||
}
|
||||
T* rv = static_cast<T*>(malloc(aNumElems * sizeof(T)));
|
||||
if (!rv) {
|
||||
MOZ_CRASH("TestBufferList.cpp: out of memory");
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void free_(void* aPtr) { free(aPtr); }
|
||||
|
||||
void reportAllocOverflow() const {}
|
||||
|
||||
bool checkSimulatedOOM() const { return true; }
|
||||
};
|
||||
|
||||
typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const size_t kInitialSize = 16;
|
||||
const size_t kInitialCapacity = 24;
|
||||
const size_t kStandardCapacity = 32;
|
||||
|
||||
BufferList bl(kInitialSize, kInitialCapacity, kStandardCapacity);
|
||||
|
||||
memset(bl.Start(), 0x0c, kInitialSize);
|
||||
MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize);
|
||||
|
||||
// Simple iteration and access.
|
||||
|
||||
BufferList::IterImpl iter(bl.Iter());
|
||||
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize);
|
||||
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize));
|
||||
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize + 1));
|
||||
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(size_t(-1)));
|
||||
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
|
||||
MOZ_RELEASE_ASSERT(!iter.Done());
|
||||
|
||||
iter.Advance(bl, 4);
|
||||
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4);
|
||||
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4));
|
||||
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
|
||||
MOZ_RELEASE_ASSERT(!iter.Done());
|
||||
|
||||
iter.Advance(bl, 11);
|
||||
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4 - 11);
|
||||
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4 - 11));
|
||||
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize - 4 - 11 + 1));
|
||||
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
|
||||
MOZ_RELEASE_ASSERT(!iter.Done());
|
||||
|
||||
iter.Advance(bl, kInitialSize - 4 - 11);
|
||||
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 0);
|
||||
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(1));
|
||||
MOZ_RELEASE_ASSERT(iter.Done());
|
||||
|
||||
// Writing to the buffer.
|
||||
|
||||
const size_t kSmallWrite = 16;
|
||||
|
||||
char toWrite[kSmallWrite];
|
||||
memset(toWrite, 0x0a, kSmallWrite);
|
||||
bl.WriteBytes(toWrite, kSmallWrite);
|
||||
|
||||
MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize + kSmallWrite);
|
||||
|
||||
iter = bl.Iter();
|
||||
iter.Advance(bl, kInitialSize);
|
||||
MOZ_RELEASE_ASSERT(!iter.Done());
|
||||
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialCapacity - kInitialSize);
|
||||
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialCapacity - kInitialSize));
|
||||
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
|
||||
|
||||
// AdvanceAcrossSegments.
|
||||
|
||||
iter = bl.Iter();
|
||||
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialCapacity - 4));
|
||||
MOZ_RELEASE_ASSERT(!iter.Done());
|
||||
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
|
||||
MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
|
||||
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
|
||||
|
||||
iter = bl.Iter();
|
||||
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 4));
|
||||
MOZ_RELEASE_ASSERT(!iter.Done());
|
||||
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
|
||||
MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
|
||||
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
|
||||
|
||||
MOZ_RELEASE_ASSERT(bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 1));
|
||||
MOZ_RELEASE_ASSERT(bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
|
||||
MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite + 1));
|
||||
MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, size_t(-1)));
|
||||
|
||||
// Reading non-contiguous bytes.
|
||||
|
||||
char toRead[kSmallWrite];
|
||||
iter = bl.Iter();
|
||||
iter.Advance(bl, kInitialSize);
|
||||
bl.ReadBytes(iter, toRead, kSmallWrite);
|
||||
MOZ_RELEASE_ASSERT(memcmp(toRead, toWrite, kSmallWrite) == 0);
|
||||
MOZ_RELEASE_ASSERT(iter.Done());
|
||||
|
||||
// Make sure reading up to the end of a segment advances the iter to the next
|
||||
// segment.
|
||||
iter = bl.Iter();
|
||||
bl.ReadBytes(iter, toRead, kInitialSize);
|
||||
MOZ_RELEASE_ASSERT(!iter.Done());
|
||||
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialCapacity - kInitialSize);
|
||||
|
||||
const size_t kBigWrite = 1024;
|
||||
|
||||
char* toWriteBig = static_cast<char*>(malloc(kBigWrite));
|
||||
for (unsigned i = 0; i < kBigWrite; i++) {
|
||||
toWriteBig[i] = i % 37;
|
||||
}
|
||||
bl.WriteBytes(toWriteBig, kBigWrite);
|
||||
|
||||
char* toReadBig = static_cast<char*>(malloc(kBigWrite));
|
||||
iter = bl.Iter();
|
||||
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
|
||||
bl.ReadBytes(iter, toReadBig, kBigWrite);
|
||||
MOZ_RELEASE_ASSERT(memcmp(toReadBig, toWriteBig, kBigWrite) == 0);
|
||||
MOZ_RELEASE_ASSERT(iter.Done());
|
||||
|
||||
free(toReadBig);
|
||||
free(toWriteBig);
|
||||
|
||||
// Currently bl contains these segments:
|
||||
// #0: offset 0, [0x0c]*16 + [0x0a]*8, size 24
|
||||
// #1: offset 24, [0x0a]*8 + [i%37 for i in 0..24], size 32
|
||||
// #2: offset 56, [i%37 for i in 24..56, size 32
|
||||
// ...
|
||||
// #32: offset 1016, [i%37 for i in 984..1016], size 32
|
||||
// #33: offset 1048, [i%37 for i in 1016..1024], size 8
|
||||
|
||||
static size_t kTotalSize = kInitialSize + kSmallWrite + kBigWrite;
|
||||
|
||||
MOZ_RELEASE_ASSERT(bl.Size() == kTotalSize);
|
||||
|
||||
static size_t kLastSegmentSize = (kTotalSize - kInitialCapacity) % kStandardCapacity;
|
||||
|
||||
iter = bl.Iter();
|
||||
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kTotalSize - kLastSegmentSize - kStandardCapacity));
|
||||
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kStandardCapacity);
|
||||
iter.Advance(bl, kStandardCapacity);
|
||||
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kLastSegmentSize);
|
||||
MOZ_RELEASE_ASSERT(unsigned(*iter.Data()) == (kTotalSize - kLastSegmentSize - kInitialSize - kSmallWrite) % 37);
|
||||
|
||||
// Flattening.
|
||||
|
||||
const size_t kFlattenSize = 1000;
|
||||
|
||||
const char* flat;
|
||||
iter = bl.Iter();
|
||||
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize));
|
||||
MOZ_RELEASE_ASSERT(bl.FlattenBytes(iter, &flat, kFlattenSize));
|
||||
MOZ_RELEASE_ASSERT(flat[0] == 0x0a);
|
||||
MOZ_RELEASE_ASSERT(flat[kSmallWrite / 2] == 0x0a);
|
||||
for (size_t i = kSmallWrite; i < kFlattenSize; i++) {
|
||||
MOZ_RELEASE_ASSERT(unsigned(flat[i]) == (i - kSmallWrite) % 37);
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(unsigned(*iter.Data()) == (kFlattenSize - kSmallWrite) % 37);
|
||||
|
||||
const size_t kSecondFlattenSize = 40;
|
||||
|
||||
MOZ_RELEASE_ASSERT(bl.FlattenBytes(iter, &flat, kSecondFlattenSize));
|
||||
for (size_t i = 0; i < kSecondFlattenSize; i++) {
|
||||
MOZ_RELEASE_ASSERT(unsigned(flat[i]) == (i + kFlattenSize - kInitialSize) % 37);
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(iter.Done());
|
||||
|
||||
// Clear.
|
||||
|
||||
bl.Clear();
|
||||
MOZ_RELEASE_ASSERT(bl.Size() == 0);
|
||||
MOZ_RELEASE_ASSERT(bl.Iter().Done());
|
||||
|
||||
// Move assignment.
|
||||
|
||||
const size_t kSmallCapacity = 8;
|
||||
|
||||
BufferList bl2(0, kSmallCapacity, kSmallCapacity);
|
||||
bl2.WriteBytes(toWrite, kSmallWrite);
|
||||
bl2.WriteBytes(toWrite, kSmallWrite);
|
||||
bl2.WriteBytes(toWrite, kSmallWrite);
|
||||
|
||||
bl = mozilla::Move(bl2);
|
||||
MOZ_RELEASE_ASSERT(bl2.Size() == 0);
|
||||
MOZ_RELEASE_ASSERT(bl2.Iter().Done());
|
||||
|
||||
iter = bl.Iter();
|
||||
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3));
|
||||
MOZ_RELEASE_ASSERT(iter.Done());
|
||||
|
||||
// Borrowing.
|
||||
|
||||
const size_t kBorrowStart = 4;
|
||||
const size_t kBorrowSize = 24;
|
||||
|
||||
iter = bl.Iter();
|
||||
iter.Advance(bl, kBorrowStart);
|
||||
bool success;
|
||||
bl2 = bl.Borrow<InfallibleAllocPolicy>(iter, kBorrowSize, &success);
|
||||
MOZ_RELEASE_ASSERT(success);
|
||||
MOZ_RELEASE_ASSERT(bl2.Size() == kBorrowSize);
|
||||
|
||||
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3 - kBorrowSize - kBorrowStart));
|
||||
MOZ_RELEASE_ASSERT(iter.Done());
|
||||
|
||||
iter = bl2.Iter();
|
||||
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kBorrowSize));
|
||||
MOZ_RELEASE_ASSERT(iter.Done());
|
||||
|
||||
BufferList::IterImpl iter1(bl.Iter()), iter2(bl2.Iter());
|
||||
iter1.Advance(bl, kBorrowStart);
|
||||
MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
|
||||
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl, kBorrowSize - 5));
|
||||
MOZ_RELEASE_ASSERT(iter2.AdvanceAcrossSegments(bl2, kBorrowSize - 5));
|
||||
MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -9,6 +9,7 @@ CppUnitTests([
|
||||
'TestAtomics',
|
||||
'TestBinarySearch',
|
||||
'TestBloomFilter',
|
||||
'TestBufferList',
|
||||
'TestCasting',
|
||||
'TestCeilingFloor',
|
||||
'TestCheckedInt',
|
||||
|
||||
@@ -121,12 +121,7 @@ struct ParamTraits<mozilla::net::NetAddr>
|
||||
return false;
|
||||
|
||||
if (aResult->raw.family == AF_UNSPEC) {
|
||||
const char *tmp;
|
||||
if (aMsg->ReadBytes(aIter, &tmp, sizeof(aResult->raw.data))) {
|
||||
memcpy(&(aResult->raw.data), tmp, sizeof(aResult->raw.data));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return aMsg->ReadBytesInto(aIter, &aResult->raw.data, sizeof(aResult->raw.data));
|
||||
} else if (aResult->raw.family == AF_INET) {
|
||||
return ReadParam(aMsg, aIter, &aResult->inet.port) &&
|
||||
ReadParam(aMsg, aIter, &aResult->inet.ip);
|
||||
@@ -138,12 +133,7 @@ struct ParamTraits<mozilla::net::NetAddr>
|
||||
ReadParam(aMsg, aIter, &aResult->inet6.scope_id);
|
||||
#if defined(XP_UNIX)
|
||||
} else if (aResult->raw.family == AF_LOCAL) {
|
||||
const char *tmp;
|
||||
if (aMsg->ReadBytes(aIter, &tmp, sizeof(aResult->local.path))) {
|
||||
memcpy(&(aResult->local.path), tmp, sizeof(aResult->local.path));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return aMsg->ReadBytesInto(aIter, &aResult->local.path, sizeof(aResult->local.path));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -49,12 +49,7 @@ struct ParamTraits<mozilla::BaseEventFlags>
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
const char* outp;
|
||||
if (!aMsg->ReadBytes(aIter, &outp, sizeof(*aResult))) {
|
||||
return false;
|
||||
}
|
||||
*aResult = *reinterpret_cast<const paramType*>(outp);
|
||||
return true;
|
||||
return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user