/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file.
/** * This constant determines the threshold size (in bytes) for deciding whether * shared memory should be used during serialization/deserialization handled * by the MessageBufferWriter class. * * NOTE: Even above this threshold, if MessageBufferWriter fails to allocate a * shared memory region, it may still fall-back to sending the message inline.
*/
constexpr uint32_t kMessageBufferShmemThreshold = 64 * 1024; // 64 KB
/** * Context used to serialize into an IPC::Message. Provides relevant context * used when serializing.
*/ class MOZ_STACK_CLASS MessageWriter final { public: explicit MessageWriter(Message& message,
mozilla::ipc::IProtocol* actor = nullptr)
: message_(message), actor_(actor) {}
/** * Context used to read data from an IPC::Message. Provides relevant context * used when deserializing and tracks iteration.
*/ class MOZ_STACK_CLASS MessageReader final { public: explicit MessageReader(const Message& message,
mozilla::ipc::IProtocol* actor = nullptr)
: message_(message), iter_(message), actor_(actor) {}
// Helper for checking `T::kHasDeprecatedReadParamPrivateConstructor` using a // fallback when the member isn't defined. template <typename T> inline constexpr auto HasDeprecatedReadParamPrivateConstructor(int)
-> decltype(T::kHasDeprecatedReadParamPrivateConstructor) { return T::kHasDeprecatedReadParamPrivateConstructor;
}
/** * Result type returned from some `ParamTraits<T>::Read` implementations, and * from `IPC::ReadParam<T>(MessageReader*)`. Either contains the value or * indicates a failure to deserialize. * * This type can be thought of as a variant on `Maybe<T>`, except that it * unconditionally constructs the underlying value if it is default * constructible. This helps keep code size down, especially when calling * outparameter-based ReadParam implementations (bug 1815177).
*/ template <typename T, bool = std::is_default_constructible_v<T> ||
detail::HasDeprecatedReadParamPrivateConstructor<T>(0)> class ReadResult { public:
ReadResult() = default;
// Try to extract a `Maybe<T>` from this ReadResult.
mozilla::Maybe<T> TakeMaybe() { if (mIsOk) {
mIsOk = false; return mozilla::Some(std::move(mData));
} return mozilla::Nothing();
}
// Get the underlying data from this ReadResult, even if not OK. // // This is only available for types which are default constructible, and is // used to optimize old-style `ReadParam` calls.
T& GetStorage() { return mData; }
// Compliment to `GetStorage` used to set the ReadResult into an OK state // without constructing the underlying value. void SetOk(bool aIsOk) { mIsOk = aIsOk; }
// Try to extract a `Maybe<T>` from this ReadResult.
mozilla::Maybe<T> TakeMaybe() { return std::move(mData); }
// These methods are only available if the type is default constructible.
T& GetStorage() = delete; void SetOk(bool aIsOk) = delete;
private:
mozilla::Maybe<T> mData;
};
//----------------------------------------------------------------------------- // An iterator class for reading the fields contained within a Message.
class MessageIterator { public: explicit MessageIterator(const Message& m) : msg_(m), iter_(m) {} int NextInt() const { int val; if (!msg_.ReadInt(&iter_, &val)) NOTREACHED(); return val;
}
intptr_t NextIntPtr() const {
intptr_t val; if (!msg_.ReadIntPtr(&iter_, &val)) NOTREACHED(); return val;
} const std::string NextString() const {
std::string val; if (!msg_.ReadString(&iter_, &val)) NOTREACHED(); return val;
} const std::wstring NextWString() const {
std::wstring val; if (!msg_.ReadWString(&iter_, &val)) NOTREACHED(); return val;
}
//----------------------------------------------------------------------------- // ParamTraits specializations, etc. // // The full set of types ParamTraits is specialized upon contains *possibly* // repeated types: unsigned long may be uint32_t or size_t, unsigned long long // may be uint64_t or size_t, nsresult may be uint32_t, and so on. You can't // have ParamTraits<unsigned int> *and* ParamTraits<uint32_t> if unsigned int // is uint32_t -- that's multiple definitions, and you can only have one. // // You could use #ifs and macro conditions to avoid duplicates, but they'd be // hairy: heavily dependent upon OS and compiler author choices, forced to // address all conflicts by hand. Happily there's a better way. The basic // idea looks like this, where T -> U represents T inheriting from U: // // class ParamTraits<P> // | // --> class ParamTraits1<P> // | // --> class ParamTraits2<P> // | // --> class ParamTraitsN<P> // or however many levels // // The default specialization of ParamTraits{M}<P> is an empty class that // inherits from ParamTraits{M + 1}<P> (or nothing in the base case). // // Now partition the set of parameter types into sets without duplicates. // Assign each set of types to a level M. Then specialize ParamTraitsM for // each of those types. A reference to ParamTraits<P> will consist of some // number of empty classes inheriting in sequence, ending in a non-empty // ParamTraits{N}<P>. It's okay for the parameter types to be duplicative: // either name of a type will resolve to the same ParamTraits{N}<P>. // // The nice thing is that because templates are instantiated lazily, if we // indeed have uint32_t == unsigned int, say, with the former in level N and // the latter in M > N, ParamTraitsM<unsigned int> won't be created (as long as // nobody uses ParamTraitsM<unsigned int>, but why would you), and no duplicate // code will be compiled or extra symbols generated. It's as efficient at // runtime as manually figuring out and avoiding conflicts by #ifs. // // The scheme we follow below names the various classes according to the types // in them, and the number of ParamTraits levels is larger, but otherwise it's // exactly the above idea. //
class MOZ_STACK_CLASS MessageBufferWriter { public: // Create a MessageBufferWriter to write `full_len` bytes into `writer`. // If the length exceeds a threshold, a shared memory region may be used // instead of including the data inline. // // NOTE: This does _NOT_ write out the length of the buffer. // NOTE: Data written this way _MUST_ be read using `MessageBufferReader`.
MessageBufferWriter(MessageWriter* writer, uint32_t full_len);
~MessageBufferWriter();
// Write `len` bytes from `data` into the message. // // Exactly `full_len` bytes should be written across multiple calls before the // `MessageBufferWriter` is destroyed. // // WARNING: all writes (other than the last write) must be multiples of 4 // bytes in length. Not doing this will lead to padding being introduced into // the payload and break things. This can probably be improved in the future // with deeper integration between `MessageBufferWriter` and `Pickle`. bool WriteBytes(constvoid* data, uint32_t len);
class MOZ_STACK_CLASS MessageBufferReader { public: // Create a MessageBufferReader to read `full_len` bytes from `reader` which // were written using `MessageBufferWriter`. // // NOTE: This may consume a shared memory region from the message, meaning // that the same data cannot be read multiple times. // NOTE: Data read this way _MUST_ be written using `MessageBufferWriter`.
MessageBufferReader(MessageReader* reader, uint32_t full_len);
~MessageBufferReader();
// Read `count` bytes from the message into `data`. // // Exactly `full_len` bytes should be read across multiple calls before the // `MessageBufferReader` is destroyed. // // WARNING: all reads (other than the last read) must be multiples of 4 bytes // in length. Not doing this will lead to bytes being skipped in the payload // and break things. This can probably be improved in the future with deeper // integration between `MessageBufferReader` and `Pickle`.
[[nodiscard]] bool ReadBytesInto(void* data, uint32_t len);
// Whether or not it is safe to serialize the given type using // `WriteBytesOrShmem`. template <typename P>
constexpr bool kUseWriteBytes =
!std::is_same_v<std::remove_const_t<std::remove_reference_t<P>>, bool> &&
(std::is_integral_v<std::remove_const_t<std::remove_reference_t<P>>> ||
std::is_floating_point_v<std::remove_const_t<std::remove_reference_t<P>>>);
/** * Helper for writing a contiguous sequence (such as for a string or array) into * a message, with optimizations for basic integral and floating point types. * * Integral types will be copied into shared memory if the sequence exceeds 64k * bytes in size. * * Values written with this method must be read with `ReadSequenceParam`. * * The type parameter specifies the semantics to use, and should generally * either be `P&&` or `const P&`. The constness of the `data` argument should * match this parameter.
*/ template <typename P> void WriteSequenceParam(MessageWriter* writer, std::remove_reference_t<P>* data,
size_t length) {
mozilla::CheckedUint32 ipc_length(length); if (!ipc_length.isValid()) {
writer->FatalError("invalid length passed to WriteSequenceParam"); return;
}
writer->WriteUInt32(ipc_length.value());
if constexpr (kUseWriteBytes<P>) {
mozilla::CheckedUint32 byte_length =
ipc_length * sizeof(std::remove_reference_t<P>); if (!byte_length.isValid()) {
writer->FatalError("invalid byte length in WriteSequenceParam"); return;
}
MessageBufferWriter buf_writer(writer, byte_length.value());
buf_writer.WriteBytes(data, byte_length.value());
} else { auto* end = data + length; for (auto* it = data; it != end; ++it) {
WriteParam(writer, std::forward<P>(*it));
}
}
}
template <typename P> bool ReadSequenceParamImpl(MessageReader* reader, P* data, uint32_t length) { if (length == 0) { returntrue;
} if (!data) {
reader->FatalError("allocation failed in ReadSequenceParam"); returnfalse;
}
if constexpr (kUseWriteBytes<P>) {
mozilla::CheckedUint32 byte_length(length);
byte_length *= sizeof(P); if (!byte_length.isValid()) {
reader->FatalError("invalid byte length in ReadSequenceParam"); returnfalse;
}
MessageBufferReader buf_reader(reader, byte_length.value()); return buf_reader.ReadBytesInto(data, byte_length.value());
} else {
P* end = data + length; for (auto* it = data; it != end; ++it) { if (!ReadParam(reader, it)) { returnfalse;
}
} returntrue;
}
}
template <typename P, typename I> bool ReadSequenceParamImpl(MessageReader* reader, mozilla::Maybe<I>&& data,
uint32_t length) {
static_assert(!kUseWriteBytes<P>, "Cannot return an output iterator if !kUseWriteBytes<P>");
static_assert(
std::is_base_of_v<std::output_iterator_tag, typename std::iterator_traits<I>::iterator_category>, "must be Maybe<output iterator>"); if (length == 0) { returntrue;
} if (!data) {
reader->FatalError("allocation failed in ReadSequenceParam"); returnfalse;
}
for (uint32_t i = 0; i < length; ++i) { auto elt = ReadParam<P>(reader); if (!elt) { returnfalse;
}
*data.ref() = std::move(*elt);
++data.ref();
} returntrue;
}
/** * Helper for reading a contiguous sequence (such as a string or array) into a * message which was previously written using `WriteSequenceParam`. * * The function argument `allocator` will be called with the length of the * sequence, and must return either a pointer to the memory region which the * sequence should be read into, or a Maybe of a C++ output iterator which will * infallibly accept length elements, and append them to the output sequence. * * If the type satisfies kUseWriteBytes, output iterators are not supported.
*/ template <typename P, typename F>
[[nodiscard]] bool ReadSequenceParam(MessageReader* reader, F&& allocator) {
uint32_t length = 0; if (!reader->ReadUInt32(&length)) {
reader->FatalError("failed to read byte length in ReadSequenceParam"); returnfalse;
}
// Temporary fallback class to allow types to declare serialization using the // IPDLParamTraits type class. Will be removed once all remaining // IPDLParamTraits implementations are gone. (bug 1754009)
// `UniqueFileHandle` may be serialized over IPC channels. On the receiving // side, the UniqueFileHandle is a valid duplicate of the handle which was // transmitted. // // When sending a UniqueFileHandle, the handle must be valid at the time of // transmission. As transmission is asynchronous, this requires passing // ownership of the handle to IPC. // // A UniqueFileHandle may only be read once. After it has been read once, it // will be consumed, and future reads will return an invalid handle. template <> struct ParamTraitsIPC<mozilla::UniqueFileHandle> { typedef mozilla::UniqueFileHandle param_type; staticvoid Write(MessageWriter* writer, param_type&& p) { constbool valid = p != nullptr;
WriteParam(writer, valid); if (valid) { if (!writer->WriteFileHandle(std::move(p))) {
writer->FatalError("Too many file handles for one message!");
NOTREACHED() << "Too many file handles for one message!";
}
}
} staticbool Read(MessageReader* reader, param_type* r) { bool valid; if (!ReadParam(reader, &valid)) {
reader->FatalError("Error reading file handle validity"); returnfalse;
}
if (!valid) {
*r = nullptr; returntrue;
}
if (!reader->ConsumeFileHandle(r)) {
reader->FatalError("File handle not found in message!"); returnfalse;
} returntrue;
}
};
#ifdefined(XP_DARWIN) // `UniqueMachSendRight` may be serialized over IPC channels. On the receiving // side, the UniqueMachSendRight is the local name of the right which was // transmitted. // // When sending a UniqueMachSendRight, the right must be valid at the time of // transmission. As transmission is asynchronous, this requires passing // ownership of the handle to IPC. // // A UniqueMachSendRight may only be read once. After it has been read once, it // will be consumed, and future reads will return an invalid right. template <> struct ParamTraitsIPC<mozilla::UniqueMachSendRight> { typedef mozilla::UniqueMachSendRight param_type; staticvoid Write(MessageWriter* writer, param_type&& p) { constbool valid = p != nullptr;
WriteParam(writer, valid); if (valid) { if (!writer->WriteMachSendRight(std::move(p))) {
writer->FatalError("Too many mach send rights for one message!");
NOTREACHED() << "Too many mach send rights for one message!";
}
}
} staticbool Read(MessageReader* reader, param_type* r) { bool valid; if (!ReadParam(reader, &valid)) {
reader->FatalError("Error reading mach send right validity"); returnfalse;
}
if (!valid) {
*r = nullptr; returntrue;
}
if (!reader->ConsumeMachSendRight(r)) {
reader->FatalError("Mach send right not found in message!"); returnfalse;
} returntrue;
}
}; #endif
// See comments for the IPDLParamTraits specializations for RefPtr<T> and // nsCOMPtr<T> for more details. template <class T> struct ParamTraitsMozilla<RefPtr<T>> { staticvoid Write(MessageWriter* writer, const RefPtr<T>& p) {
ParamTraits<T*>::Write(writer, p.get());
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.