blob: 0f0be5dee5e874cfde9680161be80c5bdb134a43 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
#include <stddef.h>
#include <string.h> // For |memcpy()|.
#include <type_traits>
#include <utility>
#include "base/check.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "mojo/public/cpp/bindings/array_data_view.h"
#include "mojo/public/cpp/bindings/lib/array_internal.h"
#include "mojo/public/cpp/bindings/lib/message_fragment.h"
#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
#include "mojo/public/cpp/bindings/lib/template_util.h"
#include "mojo/public/cpp/bindings/lib/validation_errors.h"
namespace mojo {
class Message;
namespace internal {
template <typename Traits,
typename MaybeConstUserType,
bool HasGetBegin =
HasGetBeginMethod<Traits, MaybeConstUserType>::value>
class ArrayIterator {};
// Used as the UserTypeIterator template parameter of ArraySerializer.
template <typename Traits, typename MaybeConstUserType>
class ArrayIterator<Traits, MaybeConstUserType, true> {
public:
using IteratorType =
decltype(Traits::GetBegin(std::declval<MaybeConstUserType&>()));
explicit ArrayIterator(MaybeConstUserType& input)
: input_(input), iter_(Traits::GetBegin(input)) {}
~ArrayIterator() {}
size_t GetSize() const { return Traits::GetSize(input_); }
decltype(auto) GetNext() {
decltype(auto) value = Traits::GetValue(iter_);
Traits::AdvanceIterator(iter_);
return value;
}
const MaybeConstUserType& input() const { return input_; }
private:
// RAW_PTR_EXCLUSION: Binary size increase.
RAW_PTR_EXCLUSION MaybeConstUserType& input_;
IteratorType iter_;
};
// Used as the UserTypeIterator template parameter of ArraySerializer.
template <typename Traits, typename MaybeConstUserType>
class ArrayIterator<Traits, MaybeConstUserType, false> {
public:
explicit ArrayIterator(MaybeConstUserType& input) : input_(input), iter_(0) {}
~ArrayIterator() {}
size_t GetSize() const { return Traits::GetSize(input_); }
decltype(auto) GetNext() {
DCHECK_LT(iter_, Traits::GetSize(input_));
return Traits::GetAt(input_, iter_++);
}
const MaybeConstUserType& input() const { return input_; }
private:
// RAW_PTR_EXCLUSION: Binary size increase.
RAW_PTR_EXCLUSION MaybeConstUserType& input_;
size_t iter_;
};
// ArraySerializer is also used to serialize map keys and values. Therefore, it
// has a UserTypeIterator parameter which is an adaptor for reading to hide the
// difference between ArrayTraits and MapTraits.
template <typename MojomType,
typename MaybeConstUserType,
typename UserTypeIterator,
typename EnableType = void>
struct ArraySerializer;
// Handles serialization and deserialization of arrays of pod types.
template <typename MojomType,
typename MaybeConstUserType,
typename UserTypeIterator>
struct ArraySerializer<
MojomType,
MaybeConstUserType,
UserTypeIterator,
std::enable_if_t<BelongsTo<typename MojomType::Element,
MojomTypeCategory::kPOD>::value>> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
using Data = typename MojomTypeTraits<MojomType>::Data;
using DataElement = typename Data::Element;
using Element = typename MojomType::Element;
using Traits = ArrayTraits<UserType>;
static_assert(std::is_same<Element, DataElement>::value,
"Incorrect array serializer");
static_assert(
std::is_same<
Element,
typename std::remove_const<typename Traits::Element>::type>::value,
"Incorrect array serializer");
static void SerializeElements(
UserTypeIterator* input,
MessageFragment<Data>& fragment,
const ContainerValidateParams* validate_params) {
DCHECK(!validate_params->element_validate_params)
<< "Primitive type should not have array validate params";
size_t size = input->GetSize();
if (size == 0)
return;
Data* output = fragment.data();
if constexpr (HasGetDataMethod<Traits, MaybeConstUserType>::value) {
auto data = Traits::GetData(input->input());
memcpy(output->storage(), data, size * sizeof(DataElement));
} else {
for (size_t i = 0; i < size; ++i)
output->at(i) = input->GetNext();
}
}
static bool DeserializeElements(Data* input,
UserType* output,
Message* message) {
if (!Traits::Resize(*output, input->size()))
return false;
if (input->size()) {
if constexpr (HasGetDataMethod<Traits, UserType>::value) {
auto data = Traits::GetData(*output);
memcpy(data, input->storage(), input->size() * sizeof(DataElement));
} else {
ArrayIterator<Traits, UserType> iterator(*output);
for (size_t i = 0; i < input->size(); ++i)
iterator.GetNext() = static_cast<DataElement>(input->at(i));
}
}
return true;
}
};
// Handles serialization and deserialization of arrays of enum types.
template <typename MojomType,
typename MaybeConstUserType,
typename UserTypeIterator>
requires(!base::is_instantiation<typename MojomType::Element, std::optional>)
struct ArraySerializer<
MojomType,
MaybeConstUserType,
UserTypeIterator,
typename std::enable_if<BelongsTo<typename MojomType::Element,
MojomTypeCategory::kEnum>::value>::type> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
using Data = typename MojomTypeTraits<MojomType>::Data;
using DataElement = typename Data::Element;
using Element = typename MojomType::Element;
using Traits = ArrayTraits<UserType>;
static_assert(sizeof(Element) == sizeof(DataElement),
"Incorrect array serializer");
static void SerializeElements(
UserTypeIterator* input,
MessageFragment<Data>& fragment,
const ContainerValidateParams* validate_params) {
DCHECK(!validate_params->element_is_nullable)
<< "Primitive type should be non-nullable";
DCHECK(!validate_params->element_validate_params)
<< "Primitive type should not have array validate params";
Data* output = fragment.data();
size_t size = input->GetSize();
for (size_t i = 0; i < size; ++i)
Serialize<Element>(input->GetNext(), output->storage() + i);
}
static bool DeserializeElements(Data* input,
UserType* output,
Message* message) {
if (!Traits::Resize(*output, input->size()))
return false;
ArrayIterator<Traits, UserType> iterator(*output);
for (size_t i = 0; i < input->size(); ++i) {
if (!Deserialize<Element>(input->at(i), &iterator.GetNext()))
return false;
}
return true;
}
};
// Handles serialization and deserialization of arrays of optional enum types.
template <typename MojomType,
typename MaybeConstUserType,
typename UserTypeIterator>
requires(base::is_instantiation<typename MojomType::Element, std::optional>)
struct ArraySerializer<
MojomType,
MaybeConstUserType,
UserTypeIterator,
std::enable_if_t<BelongsTo<typename MojomType::Element,
MojomTypeCategory::kEnum>::value>> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
using Data = typename MojomTypeTraits<MojomType>::Data;
using DataElement = typename Data::Element;
using Element = typename MojomType::Element;
using Traits = ArrayTraits<UserType>;
static_assert(IsAbslOptional<typename Traits::Element>::value,
"Output type should be optional");
static_assert(sizeof(Element) == sizeof(DataElement),
"Incorrect array serializer");
static void SerializeElements(
UserTypeIterator* input,
MessageFragment<Data>& fragment,
const ContainerValidateParams* validate_params) {
DCHECK(!validate_params->element_validate_params)
<< "Primitive type should not have array validate params";
Data* output = fragment.data();
size_t size = input->GetSize();
for (size_t i = 0; i < size; ++i) {
auto next = input->GetNext();
if (next) {
int32_t serialized;
Serialize<typename Element::value_type>(*next, &serialized);
output->at(i) = serialized;
} else {
output->at(i) = std::nullopt;
}
}
}
static bool DeserializeElements(Data* input,
UserType* output,
Message* message) {
if (!Traits::Resize(*output, input->size())) {
return false;
}
ArrayIterator<Traits, UserType> iterator(*output);
for (size_t i = 0; i < input->size(); ++i) {
std::optional<int32_t> element = input->at(i).ToOptional();
if (element) {
typename Element::value_type deserialized;
if (!Deserialize<typename Element::value_type>(*element,
&deserialized)) {
return false;
}
iterator.GetNext() = deserialized;
} else {
iterator.GetNext() = std::nullopt;
}
}
return true;
}
};
// Serializes and deserializes arrays of bools.
template <typename MojomType,
typename MaybeConstUserType,
typename UserTypeIterator>
struct ArraySerializer<MojomType,
MaybeConstUserType,
UserTypeIterator,
typename std::enable_if<BelongsTo<
typename MojomType::Element,
MojomTypeCategory::kBoolean>::value>::type> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
using Traits = ArrayTraits<UserType>;
using Data = typename MojomTypeTraits<MojomType>::Data;
static_assert(std::is_same<bool, typename Traits::Element>::value,
"Incorrect array serializer");
static void SerializeElements(
UserTypeIterator* input,
MessageFragment<Data>& fragment,
const ContainerValidateParams* validate_params) {
DCHECK(!validate_params->element_is_nullable)
<< "Primitive type should be non-nullable";
DCHECK(!validate_params->element_validate_params)
<< "Primitive type should not have array validate params";
Data* output = fragment.data();
size_t size = input->GetSize();
for (size_t i = 0; i < size; ++i)
output->at(i) = input->GetNext();
}
static bool DeserializeElements(Data* input,
UserType* output,
Message* message) {
if (!Traits::Resize(*output, input->size()))
return false;
ArrayIterator<Traits, UserType> iterator(*output);
for (size_t i = 0; i < input->size(); ++i)
iterator.GetNext() = input->at(i);
return true;
}
};
// Serializes and deserializes arrays of handles or interfaces.
template <typename MojomType,
typename MaybeConstUserType,
typename UserTypeIterator>
struct ArraySerializer<
MojomType,
MaybeConstUserType,
UserTypeIterator,
typename std::enable_if<BelongsTo<
typename MojomType::Element,
MojomTypeCategory::kAssociatedInterface |
MojomTypeCategory::kAssociatedInterfaceRequest |
MojomTypeCategory::kHandle | MojomTypeCategory::kInterface |
MojomTypeCategory::kInterfaceRequest>::value>::type> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
using Data = typename MojomTypeTraits<MojomType>::Data;
using Element = typename MojomType::Element;
using Traits = ArrayTraits<UserType>;
static void SerializeElements(
UserTypeIterator* input,
MessageFragment<Data>& fragment,
const ContainerValidateParams* validate_params) {
DCHECK(!validate_params->element_validate_params)
<< "Handle or interface type should not have array validate params";
Data* output = fragment.data();
size_t size = input->GetSize();
for (size_t i = 0; i < size; ++i) {
decltype(auto) next = input->GetNext();
Serialize<Element>(next, &output->at(i), &fragment.message());
static const ValidationError kError =
BelongsTo<Element,
MojomTypeCategory::kAssociatedInterface |
MojomTypeCategory::kAssociatedInterfaceRequest>::value
? VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID
: VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE;
MOJO_INTERNAL_CHECK_SERIALIZATION(
SendValidation::kDefault,
!(!validate_params->element_is_nullable &&
!IsHandleOrInterfaceValid(output->at(i))),
kError,
MakeMessageWithArrayIndex("invalid handle or interface ID in array "
"expecting valid handles or interface IDs",
size, i));
}
}
static bool DeserializeElements(Data* input,
UserType* output,
Message* message) {
if (!Traits::Resize(*output, input->size()))
return false;
ArrayIterator<Traits, UserType> iterator(*output);
for (size_t i = 0; i < input->size(); ++i) {
bool result =
Deserialize<Element>(&input->at(i), &iterator.GetNext(), message);
DCHECK(result);
}
return true;
}
};
// This template must only apply to pointer mojo entity (strings, structs,
// arrays and maps).
template <typename MojomType,
typename MaybeConstUserType,
typename UserTypeIterator>
struct ArraySerializer<MojomType,
MaybeConstUserType,
UserTypeIterator,
typename std::enable_if<BelongsTo<
typename MojomType::Element,
MojomTypeCategory::kArray | MojomTypeCategory::kMap |
MojomTypeCategory::kString |
MojomTypeCategory::kStruct>::value>::type> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
using Data = typename MojomTypeTraits<MojomType>::Data;
using Element = typename MojomType::Element;
using ElementData = typename MojomTypeTraits<Element>::Data;
using Traits = ArrayTraits<UserType>;
static void SerializeElements(
UserTypeIterator* input,
MessageFragment<Data>& fragment,
const ContainerValidateParams* validate_params) {
size_t size = input->GetSize();
for (size_t i = 0; i < size; ++i) {
MessageFragment<ElementData> data_fragment(fragment.message());
decltype(auto) next = input->GetNext();
SerializeCaller<Element>::Run(next, data_fragment,
validate_params->element_validate_params);
fragment->at(i).Set(data_fragment.is_null() ? nullptr
: data_fragment.data());
MOJO_INTERNAL_CHECK_SERIALIZATION(
SendValidation::kDefault,
!(!validate_params->element_is_nullable && data_fragment.is_null()),
VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
MakeMessageWithArrayIndex("null in array expecting valid pointers",
size, i));
}
}
static bool DeserializeElements(Data* input,
UserType* output,
Message* message) {
if (!Traits::Resize(*output, input->size()))
return false;
ArrayIterator<Traits, UserType> iterator(*output);
for (size_t i = 0; i < input->size(); ++i) {
if (!Deserialize<Element>(input->at(i).Get(), &iterator.GetNext(),
message))
return false;
}
return true;
}
private:
template <typename T,
bool is_array_or_map = BelongsTo<
T,
MojomTypeCategory::kArray | MojomTypeCategory::kMap>::value>
struct SerializeCaller {
template <typename InputElementType>
static void Run(InputElementType&& input,
MessageFragment<ElementData>& fragment,
const ContainerValidateParams* validate_params) {
Serialize<T>(std::forward<InputElementType>(input), fragment);
}
};
template <typename T>
struct SerializeCaller<T, true> {
template <typename InputElementType>
static void Run(InputElementType&& input,
MessageFragment<ElementData>& fragment,
const ContainerValidateParams* validate_params) {
Serialize<T>(std::forward<InputElementType>(input), fragment,
validate_params);
}
};
};
// Handles serialization and deserialization of arrays of unions.
template <typename MojomType,
typename MaybeConstUserType,
typename UserTypeIterator>
struct ArraySerializer<MojomType,
MaybeConstUserType,
UserTypeIterator,
typename std::enable_if<
BelongsTo<typename MojomType::Element,
MojomTypeCategory::kUnion>::value>::type> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
using Data = typename MojomTypeTraits<MojomType>::Data;
using Element = typename MojomType::Element;
using DataElement = typename Data::Element;
using Traits = ArrayTraits<UserType>;
static void SerializeElements(
UserTypeIterator* input,
MessageFragment<Data>& fragment,
const ContainerValidateParams* validate_params) {
size_t size = input->GetSize();
for (size_t i = 0; i < size; ++i) {
MessageFragment<DataElement> inlined_union_element(fragment.message());
inlined_union_element.Claim(fragment->storage() + i);
decltype(auto) next = input->GetNext();
Serialize<Element>(next, inlined_union_element, true);
MOJO_INTERNAL_CHECK_SERIALIZATION(
SendValidation::kDefault,
!(!validate_params->element_is_nullable &&
inlined_union_element.is_null()),
VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
MakeMessageWithArrayIndex("null in array expecting valid unions",
size, i));
}
}
static bool DeserializeElements(Data* input,
UserType* output,
Message* message) {
if (!Traits::Resize(*output, input->size()))
return false;
ArrayIterator<Traits, UserType> iterator(*output);
for (size_t i = 0; i < input->size(); ++i) {
if (!Deserialize<Element>(&input->at(i), &iterator.GetNext(), message))
return false;
}
return true;
}
};
template <typename Element, typename MaybeConstUserType>
struct Serializer<ArrayDataView<Element>, MaybeConstUserType> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
using Traits = ArrayTraits<UserType>;
using Impl = ArraySerializer<ArrayDataView<Element>,
MaybeConstUserType,
ArrayIterator<Traits, MaybeConstUserType>>;
using Data = typename MojomTypeTraits<ArrayDataView<Element>>::Data;
static void Serialize(MaybeConstUserType& input,
MessageFragment<Data>& fragment,
const ContainerValidateParams* validate_params) {
if (CallIsNullIfExists<Traits>(input))
return;
const size_t size = Traits::GetSize(input);
MOJO_INTERNAL_CHECK_SERIALIZATION(
SendValidation::kDefault,
!(validate_params->expected_num_elements != 0 &&
size != validate_params->expected_num_elements),
VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
MakeMessageWithExpectedArraySize(
"fixed-size array has wrong number of elements", size,
validate_params->expected_num_elements));
fragment.AllocateArrayData(size);
ArrayIterator<Traits, MaybeConstUserType> iterator(input);
Impl::SerializeElements(&iterator, fragment, validate_params);
}
static bool Deserialize(Data* input, UserType* output, Message* message) {
if (!input)
return CallSetToNullIfExists<Traits>(output);
return Impl::DeserializeElements(input, output, message);
}
};
} // namespace internal
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_