508 lines
20 KiB
C++
508 lines
20 KiB
C++
// Copyright 2014 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.
|
|
|
|
#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 <limits>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "base/logging.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/serialization_forward.h"
|
|
#include "mojo/public/cpp/bindings/lib/template_util.h"
|
|
#include "mojo/public/cpp/bindings/lib/validation_errors.h"
|
|
|
|
namespace mojo {
|
|
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(
|
|
CallGetBeginIfExists<Traits>(std::declval<MaybeConstUserType&>()));
|
|
|
|
explicit ArrayIterator(MaybeConstUserType& input)
|
|
: input_(input), iter_(CallGetBeginIfExists<Traits>(input)) {}
|
|
~ArrayIterator() {}
|
|
|
|
size_t GetSize() const { return Traits::GetSize(input_); }
|
|
|
|
using GetNextResult =
|
|
decltype(Traits::GetValue(std::declval<IteratorType&>()));
|
|
GetNextResult GetNext() {
|
|
GetNextResult value = Traits::GetValue(iter_);
|
|
Traits::AdvanceIterator(iter_);
|
|
return value;
|
|
}
|
|
|
|
using GetDataIfExistsResult = decltype(
|
|
CallGetDataIfExists<Traits>(std::declval<MaybeConstUserType&>()));
|
|
GetDataIfExistsResult GetDataIfExists() {
|
|
return CallGetDataIfExists<Traits>(input_);
|
|
}
|
|
|
|
private:
|
|
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_); }
|
|
|
|
using GetNextResult =
|
|
decltype(Traits::GetAt(std::declval<MaybeConstUserType&>(), 0));
|
|
GetNextResult GetNext() {
|
|
DCHECK_LT(iter_, Traits::GetSize(input_));
|
|
return Traits::GetAt(input_, iter_++);
|
|
}
|
|
|
|
using GetDataIfExistsResult = decltype(
|
|
CallGetDataIfExists<Traits>(std::declval<MaybeConstUserType&>()));
|
|
GetDataIfExistsResult GetDataIfExists() {
|
|
return CallGetDataIfExists<Traits>(input_);
|
|
}
|
|
|
|
private:
|
|
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,
|
|
typename std::enable_if<BelongsTo<typename MojomType::Element,
|
|
MojomTypeCategory::POD>::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>;
|
|
using BufferWriter = typename Data::BufferWriter;
|
|
|
|
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,
|
|
Buffer* buf,
|
|
BufferWriter* writer,
|
|
const ContainerValidateParams* validate_params,
|
|
SerializationContext* context) {
|
|
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";
|
|
|
|
size_t size = input->GetSize();
|
|
if (size == 0)
|
|
return;
|
|
|
|
auto data = input->GetDataIfExists();
|
|
Data* output = writer->data();
|
|
if (data) {
|
|
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,
|
|
SerializationContext* context) {
|
|
if (!Traits::Resize(*output, input->size()))
|
|
return false;
|
|
ArrayIterator<Traits, UserType> iterator(*output);
|
|
if (input->size()) {
|
|
auto data = iterator.GetDataIfExists();
|
|
if (data) {
|
|
memcpy(data, input->storage(), input->size() * sizeof(DataElement));
|
|
} else {
|
|
for (size_t i = 0; i < input->size(); ++i)
|
|
iterator.GetNext() = input->at(i);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// Handles serialization and deserialization of arrays of enum types.
|
|
template <typename MojomType,
|
|
typename MaybeConstUserType,
|
|
typename UserTypeIterator>
|
|
struct ArraySerializer<
|
|
MojomType,
|
|
MaybeConstUserType,
|
|
UserTypeIterator,
|
|
typename std::enable_if<BelongsTo<typename MojomType::Element,
|
|
MojomTypeCategory::ENUM>::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>;
|
|
using BufferWriter = typename Data::BufferWriter;
|
|
|
|
static_assert(sizeof(Element) == sizeof(DataElement),
|
|
"Incorrect array serializer");
|
|
|
|
static void SerializeElements(UserTypeIterator* input,
|
|
Buffer* buf,
|
|
BufferWriter* writer,
|
|
const ContainerValidateParams* validate_params,
|
|
SerializationContext* context) {
|
|
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 = writer->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,
|
|
SerializationContext* context) {
|
|
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;
|
|
}
|
|
};
|
|
|
|
// 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::BOOLEAN>::value>::type> {
|
|
using UserType = typename std::remove_const<MaybeConstUserType>::type;
|
|
using Traits = ArrayTraits<UserType>;
|
|
using Data = typename MojomTypeTraits<MojomType>::Data;
|
|
using BufferWriter = typename Data::BufferWriter;
|
|
|
|
static_assert(std::is_same<bool, typename Traits::Element>::value,
|
|
"Incorrect array serializer");
|
|
|
|
static void SerializeElements(UserTypeIterator* input,
|
|
Buffer* buf,
|
|
BufferWriter* writer,
|
|
const ContainerValidateParams* validate_params,
|
|
SerializationContext* context) {
|
|
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 = writer->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,
|
|
SerializationContext* context) {
|
|
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::ASSOCIATED_INTERFACE |
|
|
MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST |
|
|
MojomTypeCategory::HANDLE | MojomTypeCategory::INTERFACE |
|
|
MojomTypeCategory::INTERFACE_REQUEST>::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>;
|
|
using BufferWriter = typename Data::BufferWriter;
|
|
|
|
static void SerializeElements(UserTypeIterator* input,
|
|
Buffer* buf,
|
|
BufferWriter* writer,
|
|
const ContainerValidateParams* validate_params,
|
|
SerializationContext* context) {
|
|
DCHECK(!validate_params->element_validate_params)
|
|
<< "Handle or interface type should not have array validate params";
|
|
|
|
Data* output = writer->data();
|
|
size_t size = input->GetSize();
|
|
for (size_t i = 0; i < size; ++i) {
|
|
typename UserTypeIterator::GetNextResult next = input->GetNext();
|
|
Serialize<Element>(next, &output->at(i), context);
|
|
|
|
static const ValidationError kError =
|
|
BelongsTo<Element,
|
|
MojomTypeCategory::ASSOCIATED_INTERFACE |
|
|
MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST>::value
|
|
? VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID
|
|
: VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE;
|
|
MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
|
|
!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,
|
|
SerializationContext* context) {
|
|
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(), context);
|
|
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::ARRAY | MojomTypeCategory::MAP |
|
|
MojomTypeCategory::STRING |
|
|
MojomTypeCategory::STRUCT>::value>::type> {
|
|
using UserType = typename std::remove_const<MaybeConstUserType>::type;
|
|
using Data = typename MojomTypeTraits<MojomType>::Data;
|
|
using Element = typename MojomType::Element;
|
|
using DataElementWriter =
|
|
typename MojomTypeTraits<Element>::Data::BufferWriter;
|
|
using Traits = ArrayTraits<UserType>;
|
|
using BufferWriter = typename Data::BufferWriter;
|
|
|
|
static void SerializeElements(UserTypeIterator* input,
|
|
Buffer* buf,
|
|
BufferWriter* writer,
|
|
const ContainerValidateParams* validate_params,
|
|
SerializationContext* context) {
|
|
size_t size = input->GetSize();
|
|
for (size_t i = 0; i < size; ++i) {
|
|
DataElementWriter data_writer;
|
|
typename UserTypeIterator::GetNextResult next = input->GetNext();
|
|
SerializeCaller<Element>::Run(next, buf, &data_writer,
|
|
validate_params->element_validate_params,
|
|
context);
|
|
writer->data()->at(i).Set(data_writer.is_null() ? nullptr
|
|
: data_writer.data());
|
|
MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
|
|
!validate_params->element_is_nullable && data_writer.is_null(),
|
|
VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
|
|
MakeMessageWithArrayIndex("null in array expecting valid pointers",
|
|
size, i));
|
|
}
|
|
}
|
|
static bool DeserializeElements(Data* input,
|
|
UserType* output,
|
|
SerializationContext* context) {
|
|
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(),
|
|
context))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
template <typename T,
|
|
bool is_array_or_map = BelongsTo<T,
|
|
MojomTypeCategory::ARRAY |
|
|
MojomTypeCategory::MAP>::value>
|
|
struct SerializeCaller {
|
|
template <typename InputElementType>
|
|
static void Run(InputElementType&& input,
|
|
Buffer* buf,
|
|
DataElementWriter* writer,
|
|
const ContainerValidateParams* validate_params,
|
|
SerializationContext* context) {
|
|
Serialize<T>(std::forward<InputElementType>(input), buf, writer, context);
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct SerializeCaller<T, true> {
|
|
template <typename InputElementType>
|
|
static void Run(InputElementType&& input,
|
|
Buffer* buf,
|
|
DataElementWriter* writer,
|
|
const ContainerValidateParams* validate_params,
|
|
SerializationContext* context) {
|
|
Serialize<T>(std::forward<InputElementType>(input), buf, writer,
|
|
validate_params, context);
|
|
}
|
|
};
|
|
};
|
|
|
|
// 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::UNION>::value>::type> {
|
|
using UserType = typename std::remove_const<MaybeConstUserType>::type;
|
|
using Data = typename MojomTypeTraits<MojomType>::Data;
|
|
using Element = typename MojomType::Element;
|
|
using ElementWriter = typename Data::Element::BufferWriter;
|
|
using Traits = ArrayTraits<UserType>;
|
|
using BufferWriter = typename Data::BufferWriter;
|
|
|
|
static void SerializeElements(UserTypeIterator* input,
|
|
Buffer* buf,
|
|
BufferWriter* writer,
|
|
const ContainerValidateParams* validate_params,
|
|
SerializationContext* context) {
|
|
size_t size = input->GetSize();
|
|
for (size_t i = 0; i < size; ++i) {
|
|
ElementWriter result;
|
|
result.AllocateInline(buf, writer->data()->storage() + i);
|
|
typename UserTypeIterator::GetNextResult next = input->GetNext();
|
|
Serialize<Element>(next, buf, &result, true, context);
|
|
MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
|
|
!validate_params->element_is_nullable &&
|
|
writer->data()->at(i).is_null(),
|
|
VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
|
|
MakeMessageWithArrayIndex("null in array expecting valid unions",
|
|
size, i));
|
|
}
|
|
}
|
|
|
|
static bool DeserializeElements(Data* input,
|
|
UserType* output,
|
|
SerializationContext* context) {
|
|
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(), context))
|
|
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;
|
|
using BufferWriter = typename Data::BufferWriter;
|
|
|
|
static void Serialize(MaybeConstUserType& input,
|
|
Buffer* buf,
|
|
BufferWriter* writer,
|
|
const ContainerValidateParams* validate_params,
|
|
SerializationContext* context) {
|
|
if (CallIsNullIfExists<Traits>(input))
|
|
return;
|
|
|
|
const size_t size = Traits::GetSize(input);
|
|
MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
|
|
validate_params->expected_num_elements != 0 &&
|
|
size != validate_params->expected_num_elements,
|
|
internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
|
|
internal::MakeMessageWithExpectedArraySize(
|
|
"fixed-size array has wrong number of elements", size,
|
|
validate_params->expected_num_elements));
|
|
writer->Allocate(size, buf);
|
|
ArrayIterator<Traits, MaybeConstUserType> iterator(input);
|
|
Impl::SerializeElements(&iterator, buf, writer, validate_params, context);
|
|
}
|
|
|
|
static bool Deserialize(Data* input,
|
|
UserType* output,
|
|
SerializationContext* context) {
|
|
if (!input)
|
|
return CallSetToNullIfExists<Traits>(output);
|
|
return Impl::DeserializeElements(input, output, context);
|
|
}
|
|
};
|
|
|
|
} // namespace internal
|
|
} // namespace mojo
|
|
|
|
#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
|