// 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 #include // For |memcpy()|. #include #include #include #include #include "base/logging.h" #include "mojo/public/cpp/bindings/array.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" #include "mojo/public/cpp/bindings/map.h" namespace mojo { namespace internal { template ::value> class ArrayIterator {}; // Used as the UserTypeIterator template parameter of ArraySerializer. template class ArrayIterator { public: using IteratorType = decltype( CallGetBeginIfExists(std::declval())); explicit ArrayIterator(MaybeConstUserType& input) : input_(input), iter_(CallGetBeginIfExists(input)) {} ~ArrayIterator() {} size_t GetSize() const { return Traits::GetSize(input_); } using GetNextResult = decltype(Traits::GetValue(std::declval())); GetNextResult GetNext() { auto& value = Traits::GetValue(iter_); Traits::AdvanceIterator(iter_); return value; } using GetDataIfExistsResult = decltype( CallGetDataIfExists(std::declval())); GetDataIfExistsResult GetDataIfExists() { return CallGetDataIfExists(input_); } private: MaybeConstUserType& input_; IteratorType iter_; }; // Used as the UserTypeIterator template parameter of ArraySerializer. template class ArrayIterator { 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(), 0)); GetNextResult GetNext() { DCHECK_LT(iter_, Traits::GetSize(input_)); return Traits::GetAt(input_, iter_++); } using GetDataIfExistsResult = decltype( CallGetDataIfExists(std::declval())); GetDataIfExistsResult GetDataIfExists() { return CallGetDataIfExists(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 struct ArraySerializer; // Handles serialization and deserialization of arrays of pod types. template struct ArraySerializer< MojomType, MaybeConstUserType, UserTypeIterator, typename std::enable_if::value>::type> { using UserType = typename std::remove_const::type; using Data = typename MojomTypeTraits::Data; using DataElement = typename Data::Element; using Element = typename MojomType::Element; using Traits = ArrayTraits; static_assert(std::is_same::value, "Incorrect array serializer"); static_assert(std::is_same::value, "Incorrect array serializer"); static size_t GetSerializedSize(UserTypeIterator* input, SerializationContext* context) { return sizeof(Data) + Align(input->GetSize() * sizeof(DataElement)); } static void SerializeElements(UserTypeIterator* input, Buffer* buf, Data* output, 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(); 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 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 struct ArraySerializer< MojomType, MaybeConstUserType, UserTypeIterator, typename std::enable_if::value>::type> { using UserType = typename std::remove_const::type; using Data = typename MojomTypeTraits::Data; using DataElement = typename Data::Element; using Element = typename MojomType::Element; using Traits = ArrayTraits; static_assert(sizeof(Element) == sizeof(DataElement), "Incorrect array serializer"); static size_t GetSerializedSize(UserTypeIterator* input, SerializationContext* context) { return sizeof(Data) + Align(input->GetSize() * sizeof(DataElement)); } static void SerializeElements(UserTypeIterator* input, Buffer* buf, Data* output, 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(); for (size_t i = 0; i < size; ++i) Serialize(input->GetNext(), output->storage() + i); } static bool DeserializeElements(Data* input, UserType* output, SerializationContext* context) { if (!Traits::Resize(*output, input->size())) return false; ArrayIterator iterator(*output); for (size_t i = 0; i < input->size(); ++i) { if (!Deserialize(input->at(i), &iterator.GetNext())) return false; } return true; } }; // Serializes and deserializes arrays of bools. template struct ArraySerializer::value>::type> { using UserType = typename std::remove_const::type; using Traits = ArrayTraits; using Data = typename MojomTypeTraits::Data; static_assert(std::is_same::value, "Incorrect array serializer"); static size_t GetSerializedSize(UserTypeIterator* input, SerializationContext* context) { return sizeof(Data) + Align((input->GetSize() + 7) / 8); } static void SerializeElements(UserTypeIterator* input, Buffer* buf, Data* output, 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(); 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 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 struct ArraySerializer< MojomType, MaybeConstUserType, UserTypeIterator, typename std::enable_if< BelongsTo::value>::type> { using UserType = typename std::remove_const::type; using Data = typename MojomTypeTraits::Data; using Element = typename MojomType::Element; using Traits = ArrayTraits; static_assert(std::is_same::value, "Incorrect array serializer"); static size_t GetSerializedSize(UserTypeIterator* input, SerializationContext* context) { return sizeof(Data) + Align(input->GetSize() * sizeof(typename Data::Element)); } static void SerializeElements(UserTypeIterator* input, Buffer* buf, Data* output, const ContainerValidateParams* validate_params, SerializationContext* context) { DCHECK(!validate_params->element_validate_params) << "Handle or interface type should not have array validate params"; size_t size = input->GetSize(); for (size_t i = 0; i < size; ++i) { Serialize(input->GetNext(), &output->at(i), context); static const ValidationError kError = BelongsTo::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 iterator(*output); for (size_t i = 0; i < input->size(); ++i) { bool result = Deserialize(&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 struct ArraySerializer::value>::type> { using UserType = typename std::remove_const::type; using Data = typename MojomTypeTraits::Data; using Element = typename MojomType::Element; using DataElementPtr = typename MojomTypeTraits::Data*; using Traits = ArrayTraits; static size_t GetSerializedSize(UserTypeIterator* input, SerializationContext* context) { size_t element_count = input->GetSize(); size_t size = sizeof(Data) + element_count * sizeof(typename Data::Element); for (size_t i = 0; i < element_count; ++i) size += PrepareToSerialize(input->GetNext(), context); return size; } static void SerializeElements(UserTypeIterator* input, Buffer* buf, Data* output, const ContainerValidateParams* validate_params, SerializationContext* context) { size_t size = input->GetSize(); for (size_t i = 0; i < size; ++i) { DataElementPtr data_ptr; SerializeCaller::Run(input->GetNext(), buf, &data_ptr, validate_params->element_validate_params, context); output->at(i).Set(data_ptr); MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( !validate_params->element_is_nullable && !data_ptr, 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 iterator(*output); for (size_t i = 0; i < input->size(); ++i) { if (!Deserialize(input->at(i).Get(), &iterator.GetNext(), context)) return false; } return true; } private: template ::value> struct SerializeCaller { template static void Run(InputElementType&& input, Buffer* buf, DataElementPtr* output, const ContainerValidateParams* validate_params, SerializationContext* context) { Serialize(std::forward(input), buf, output, context); } }; template struct SerializeCaller { template static void Run(InputElementType&& input, Buffer* buf, DataElementPtr* output, const ContainerValidateParams* validate_params, SerializationContext* context) { Serialize(std::forward(input), buf, output, validate_params, context); } }; }; // Handles serialization and deserialization of arrays of unions. template struct ArraySerializer< MojomType, MaybeConstUserType, UserTypeIterator, typename std::enable_if::value>::type> { using UserType = typename std::remove_const::type; using Data = typename MojomTypeTraits::Data; using Element = typename MojomType::Element; using Traits = ArrayTraits; static_assert(std::is_same::value, "Incorrect array serializer"); static size_t GetSerializedSize(UserTypeIterator* input, SerializationContext* context) { size_t element_count = input->GetSize(); size_t size = sizeof(Data); for (size_t i = 0; i < element_count; ++i) { // Call with |inlined| set to false, so that it will account for both the // data in the union and the space in the array used to hold the union. size += PrepareToSerialize(input->GetNext(), false, context); } return size; } static void SerializeElements(UserTypeIterator* input, Buffer* buf, Data* output, const ContainerValidateParams* validate_params, SerializationContext* context) { size_t size = input->GetSize(); for (size_t i = 0; i < size; ++i) { typename Data::Element* result = output->storage() + i; Serialize(input->GetNext(), buf, &result, true, context); MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( !validate_params->element_is_nullable && output->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 iterator(*output); for (size_t i = 0; i < input->size(); ++i) { if (!Deserialize(&input->at(i), &iterator.GetNext(), context)) return false; } return true; } }; template struct Serializer, MaybeConstUserType> { using UserType = typename std::remove_const::type; using Traits = ArrayTraits; using Impl = ArraySerializer, MaybeConstUserType, ArrayIterator>; using Data = typename MojomTypeTraits>::Data; static size_t PrepareToSerialize(MaybeConstUserType& input, SerializationContext* context) { if (CallIsNullIfExists(input)) return 0; ArrayIterator iterator(input); return Impl::GetSerializedSize(&iterator, context); } static void Serialize(MaybeConstUserType& input, Buffer* buf, Data** output, const ContainerValidateParams* validate_params, SerializationContext* context) { if (!CallIsNullIfExists(input)) { MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( validate_params->expected_num_elements != 0 && Traits::GetSize(input) != validate_params->expected_num_elements, internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER, internal::MakeMessageWithExpectedArraySize( "fixed-size array has wrong number of elements", Traits::GetSize(input), validate_params->expected_num_elements)); Data* result = Data::New(Traits::GetSize(input), buf); if (result) { ArrayIterator iterator(input); Impl::SerializeElements(&iterator, buf, result, validate_params, context); } *output = result; } else { *output = nullptr; } } static bool Deserialize(Data* input, UserType* output, SerializationContext* context) { if (!input) return CallSetToNullIfExists(output); return Impl::DeserializeElements(input, output, context); } }; } // namespace internal } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_