| // 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_ |