Bug 1814683 - Part 5: Support serializing sequences of non-default-constructable types, r=ipc-reviewers,jld

This change allows callers of ReadSequenceParam to return a
Maybe<output_iterator> rather than a T* which will be used to fill the
resulting sequence. Both STL collections and std::array have iterators like
this, which can be returned from these methods without dramatically changing
the signature to support types without default constructors.

For types which support output iterators, they will be used for any type
without a trivial default constructor to avoid unnecessary constructor calls
and initialization when deserializing.

Differential Revision: https://phabricator.services.mozilla.com/D168882
This commit is contained in:
Nika Layzell 2023-03-20 15:40:34 +00:00
Родитель fc898b85f5
Коммит 0e179b3feb
2 изменённых файлов: 106 добавлений и 33 удалений

Просмотреть файл

@ -8,6 +8,7 @@
#define CHROME_COMMON_IPC_MESSAGE_UTILS_H_
#include <cstdint>
#include <iterator>
#include <map>
#include <unordered_map>
#include <string>
@ -454,29 +455,8 @@ void WriteSequenceParam(MessageWriter* writer, std::remove_reference_t<P>* data,
}
}
/**
* 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 a pointer to the memory region which the sequence
* should be read into.
*/
template <typename F,
typename P = std::remove_reference_t<
decltype(*std::declval<F>()(std::declval<uint32_t>()))>>
auto WARN_UNUSED_RESULT ReadSequenceParam(MessageReader* reader, F&& allocator)
-> std::enable_if_t<
std::is_same_v<P*, std::remove_reference_t<
decltype(allocator(std::declval<uint32_t>()))>>,
bool> {
uint32_t length = 0;
if (!reader->ReadUInt32(&length)) {
reader->FatalError("failed to read byte length in ReadSequenceParam");
return false;
}
P* data = allocator(length);
template <typename P>
bool ReadSequenceParamImpl(MessageReader* reader, P* data, uint32_t length) {
if (length == 0) {
return true;
}
@ -505,6 +485,57 @@ auto WARN_UNUSED_RESULT ReadSequenceParam(MessageReader* reader, F&& allocator)
}
}
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) {
return true;
}
if (!data) {
reader->FatalError("allocation failed in ReadSequenceParam");
return false;
}
for (uint32_t i = 0; i < length; ++i) {
auto elt = ReadParam<P>(reader);
if (!elt) {
return false;
}
*data.ref() = elt.extract();
++data.ref();
}
return true;
}
/**
* 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>
bool WARN_UNUSED_RESULT ReadSequenceParam(MessageReader* reader,
F&& allocator) {
uint32_t length = 0;
if (!reader->ReadUInt32(&length)) {
reader->FatalError("failed to read byte length in ReadSequenceParam");
return false;
}
return ReadSequenceParamImpl<P>(reader, allocator(length), length);
}
// 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)
@ -683,7 +714,7 @@ struct ParamTraitsStd<std::basic_string<T>> {
WriteSequenceParam<const T&>(writer, p.data(), p.size());
}
static bool Read(MessageReader* reader, param_type* r) {
return ReadSequenceParam(reader, [&](uint32_t length) -> T* {
return ReadSequenceParam<T>(reader, [&](uint32_t length) -> T* {
r->resize(length);
return r->data();
});

Просмотреть файл

@ -103,7 +103,7 @@ struct ParamTraits<nsTSubstring<T>> {
return true;
}
return ReadSequenceParam(aReader, [&](uint32_t aLength) -> T* {
return ReadSequenceParam<T>(aReader, [&](uint32_t aLength) -> T* {
T* data = nullptr;
aResult->GetMutableData(&data, aLength);
return data;
@ -169,8 +169,13 @@ struct ParamTraits<nsTArray<E>> {
}
static bool Read(MessageReader* aReader, paramType* aResult) {
return ReadSequenceParam(aReader, [&](uint32_t aLength) -> E* {
return ReadSequenceParam<E>(aReader, [&](uint32_t aLength) {
if constexpr (std::is_trivially_default_constructible_v<E>) {
return aResult->AppendElements(aLength);
} else {
aResult->SetCapacity(aLength);
return mozilla::Some(MakeBackInserter(*aResult));
}
});
}
};
@ -191,10 +196,42 @@ struct ParamTraits<FallibleTArray<E>> {
}
static bool Read(MessageReader* aReader, paramType* aResult) {
return ReadSequenceParam(aReader, [&](uint32_t aLength) -> E* {
return ReadSequenceParam<E>(aReader, [&](uint32_t aLength) {
if constexpr (std::is_trivially_default_constructible_v<E>) {
return aResult->AppendElements(aLength, mozilla::fallible);
} else {
if (!aResult->SetCapacity(aLength, mozilla::fallible)) {
return mozilla::Maybe<BackInserter>{};
}
return mozilla::Some(BackInserter{.mArray = aResult});
}
});
}
private:
struct BackInserter {
using iterator_category = std::output_iterator_tag;
using value_type = void;
using difference_type = void;
using pointer = void;
using reference = void;
struct Proxy {
paramType& mArray;
template <typename U>
void operator=(U&& aValue) {
// This won't fail because we've reserved capacity earlier.
MOZ_ALWAYS_TRUE(mArray.AppendElement(aValue, mozilla::fallible));
}
};
Proxy operator*() { return Proxy{.mArray = *mArray}; }
BackInserter& operator++() { return *this; }
BackInserter& operator++(int) { return *this; }
paramType* mArray = nullptr;
};
};
template <typename E, size_t N>
@ -222,7 +259,7 @@ struct ParamTraits<mozilla::Vector<E, N, AP>> {
}
static bool Read(MessageReader* aReader, paramType* aResult) {
return ReadSequenceParam(aReader, [&](uint32_t aLength) -> E* {
return ReadSequenceParam<E>(aReader, [&](uint32_t aLength) -> E* {
if (!aResult->resize(aLength)) {
// So that OOM failure shows up as OOM crash instead of IPC FatalError.
NS_ABORT_OOM(aLength * sizeof(E));
@ -244,9 +281,14 @@ struct ParamTraits<std::vector<E>> {
}
static bool Read(MessageReader* aReader, paramType* aResult) {
return ReadSequenceParam(aReader, [&](uint32_t aLength) -> E* {
return ReadSequenceParam<E>(aReader, [&](uint32_t aLength) {
if constexpr (std::is_trivially_default_constructible_v<E>) {
aResult->resize(aLength);
return aResult->data();
} else {
aResult->reserve(aLength);
return mozilla::Some(std::back_inserter(*aResult));
}
});
}
};