1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_ 6 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_ 7 8 #include <stddef.h> 9 #include <string.h> // For |memcpy()|. 10 11 #include <limits> 12 #include <type_traits> 13 #include <utility> 14 #include <vector> 15 16 #include "base/logging.h" 17 #include "mojo/public/cpp/bindings/array.h" 18 #include "mojo/public/cpp/bindings/lib/array_internal.h" 19 #include "mojo/public/cpp/bindings/lib/serialization_forward.h" 20 #include "mojo/public/cpp/bindings/lib/template_util.h" 21 #include "mojo/public/cpp/bindings/lib/validation_errors.h" 22 #include "mojo/public/cpp/bindings/map.h" 23 24 namespace mojo { 25 namespace internal { 26 27 template <typename Traits, 28 typename MaybeConstUserType, 29 bool HasGetBegin = 30 HasGetBeginMethod<Traits, MaybeConstUserType>::value> 31 class ArrayIterator {}; 32 33 // Used as the UserTypeIterator template parameter of ArraySerializer. 34 template <typename Traits, typename MaybeConstUserType> 35 class ArrayIterator<Traits, MaybeConstUserType, true> { 36 public: 37 using IteratorType = decltype( 38 CallGetBeginIfExists<Traits>(std::declval<MaybeConstUserType&>())); 39 ArrayIterator(MaybeConstUserType & input)40 explicit ArrayIterator(MaybeConstUserType& input) 41 : input_(input), iter_(CallGetBeginIfExists<Traits>(input)) {} ~ArrayIterator()42 ~ArrayIterator() {} 43 GetSize()44 size_t GetSize() const { return Traits::GetSize(input_); } 45 46 using GetNextResult = 47 decltype(Traits::GetValue(std::declval<IteratorType&>())); GetNext()48 GetNextResult GetNext() { 49 auto& value = Traits::GetValue(iter_); 50 Traits::AdvanceIterator(iter_); 51 return value; 52 } 53 54 using GetDataIfExistsResult = decltype( 55 CallGetDataIfExists<Traits>(std::declval<MaybeConstUserType&>())); GetDataIfExists()56 GetDataIfExistsResult GetDataIfExists() { 57 return CallGetDataIfExists<Traits>(input_); 58 } 59 60 private: 61 MaybeConstUserType& input_; 62 IteratorType iter_; 63 }; 64 65 // Used as the UserTypeIterator template parameter of ArraySerializer. 66 template <typename Traits, typename MaybeConstUserType> 67 class ArrayIterator<Traits, MaybeConstUserType, false> { 68 public: ArrayIterator(MaybeConstUserType & input)69 explicit ArrayIterator(MaybeConstUserType& input) : input_(input), iter_(0) {} ~ArrayIterator()70 ~ArrayIterator() {} 71 GetSize()72 size_t GetSize() const { return Traits::GetSize(input_); } 73 74 using GetNextResult = 75 decltype(Traits::GetAt(std::declval<MaybeConstUserType&>(), 0)); GetNext()76 GetNextResult GetNext() { 77 DCHECK_LT(iter_, Traits::GetSize(input_)); 78 return Traits::GetAt(input_, iter_++); 79 } 80 81 using GetDataIfExistsResult = decltype( 82 CallGetDataIfExists<Traits>(std::declval<MaybeConstUserType&>())); GetDataIfExists()83 GetDataIfExistsResult GetDataIfExists() { 84 return CallGetDataIfExists<Traits>(input_); 85 } 86 87 private: 88 MaybeConstUserType& input_; 89 size_t iter_; 90 }; 91 92 // ArraySerializer is also used to serialize map keys and values. Therefore, it 93 // has a UserTypeIterator parameter which is an adaptor for reading to hide the 94 // difference between ArrayTraits and MapTraits. 95 template <typename MojomType, 96 typename MaybeConstUserType, 97 typename UserTypeIterator, 98 typename EnableType = void> 99 struct ArraySerializer; 100 101 // Handles serialization and deserialization of arrays of pod types. 102 template <typename MojomType, 103 typename MaybeConstUserType, 104 typename UserTypeIterator> 105 struct ArraySerializer< 106 MojomType, 107 MaybeConstUserType, 108 UserTypeIterator, 109 typename std::enable_if<BelongsTo<typename MojomType::Element, 110 MojomTypeCategory::POD>::value>::type> { 111 using UserType = typename std::remove_const<MaybeConstUserType>::type; 112 using Data = typename MojomTypeTraits<MojomType>::Data; 113 using DataElement = typename Data::Element; 114 using Element = typename MojomType::Element; 115 using Traits = ArrayTraits<UserType>; 116 117 static_assert(std::is_same<Element, DataElement>::value, 118 "Incorrect array serializer"); 119 static_assert(std::is_same<Element, typename Traits::Element>::value, 120 "Incorrect array serializer"); 121 122 static size_t GetSerializedSize(UserTypeIterator* input, 123 SerializationContext* context) { 124 return sizeof(Data) + Align(input->GetSize() * sizeof(DataElement)); 125 } 126 127 static void SerializeElements(UserTypeIterator* input, 128 Buffer* buf, 129 Data* output, 130 const ContainerValidateParams* validate_params, 131 SerializationContext* context) { 132 DCHECK(!validate_params->element_is_nullable) 133 << "Primitive type should be non-nullable"; 134 DCHECK(!validate_params->element_validate_params) 135 << "Primitive type should not have array validate params"; 136 137 size_t size = input->GetSize(); 138 if (size == 0) 139 return; 140 141 auto data = input->GetDataIfExists(); 142 if (data) { 143 memcpy(output->storage(), data, size * sizeof(DataElement)); 144 } else { 145 for (size_t i = 0; i < size; ++i) 146 output->at(i) = input->GetNext(); 147 } 148 } 149 150 static bool DeserializeElements(Data* input, 151 UserType* output, 152 SerializationContext* context) { 153 if (!Traits::Resize(*output, input->size())) 154 return false; 155 ArrayIterator<Traits, UserType> iterator(*output); 156 if (input->size()) { 157 auto data = iterator.GetDataIfExists(); 158 if (data) { 159 memcpy(data, input->storage(), input->size() * sizeof(DataElement)); 160 } else { 161 for (size_t i = 0; i < input->size(); ++i) 162 iterator.GetNext() = input->at(i); 163 } 164 } 165 return true; 166 } 167 }; 168 169 // Handles serialization and deserialization of arrays of enum types. 170 template <typename MojomType, 171 typename MaybeConstUserType, 172 typename UserTypeIterator> 173 struct ArraySerializer< 174 MojomType, 175 MaybeConstUserType, 176 UserTypeIterator, 177 typename std::enable_if<BelongsTo<typename MojomType::Element, 178 MojomTypeCategory::ENUM>::value>::type> { 179 using UserType = typename std::remove_const<MaybeConstUserType>::type; 180 using Data = typename MojomTypeTraits<MojomType>::Data; 181 using DataElement = typename Data::Element; 182 using Element = typename MojomType::Element; 183 using Traits = ArrayTraits<UserType>; 184 185 static_assert(sizeof(Element) == sizeof(DataElement), 186 "Incorrect array serializer"); 187 188 static size_t GetSerializedSize(UserTypeIterator* input, 189 SerializationContext* context) { 190 return sizeof(Data) + Align(input->GetSize() * sizeof(DataElement)); 191 } 192 193 static void SerializeElements(UserTypeIterator* input, 194 Buffer* buf, 195 Data* output, 196 const ContainerValidateParams* validate_params, 197 SerializationContext* context) { 198 DCHECK(!validate_params->element_is_nullable) 199 << "Primitive type should be non-nullable"; 200 DCHECK(!validate_params->element_validate_params) 201 << "Primitive type should not have array validate params"; 202 203 size_t size = input->GetSize(); 204 for (size_t i = 0; i < size; ++i) 205 Serialize<Element>(input->GetNext(), output->storage() + i); 206 } 207 208 static bool DeserializeElements(Data* input, 209 UserType* output, 210 SerializationContext* context) { 211 if (!Traits::Resize(*output, input->size())) 212 return false; 213 ArrayIterator<Traits, UserType> iterator(*output); 214 for (size_t i = 0; i < input->size(); ++i) { 215 if (!Deserialize<Element>(input->at(i), &iterator.GetNext())) 216 return false; 217 } 218 return true; 219 } 220 }; 221 222 // Serializes and deserializes arrays of bools. 223 template <typename MojomType, 224 typename MaybeConstUserType, 225 typename UserTypeIterator> 226 struct ArraySerializer<MojomType, 227 MaybeConstUserType, 228 UserTypeIterator, 229 typename std::enable_if<BelongsTo< 230 typename MojomType::Element, 231 MojomTypeCategory::BOOLEAN>::value>::type> { 232 using UserType = typename std::remove_const<MaybeConstUserType>::type; 233 using Traits = ArrayTraits<UserType>; 234 using Data = typename MojomTypeTraits<MojomType>::Data; 235 236 static_assert(std::is_same<bool, typename Traits::Element>::value, 237 "Incorrect array serializer"); 238 239 static size_t GetSerializedSize(UserTypeIterator* input, 240 SerializationContext* context) { 241 return sizeof(Data) + Align((input->GetSize() + 7) / 8); 242 } 243 244 static void SerializeElements(UserTypeIterator* input, 245 Buffer* buf, 246 Data* output, 247 const ContainerValidateParams* validate_params, 248 SerializationContext* context) { 249 DCHECK(!validate_params->element_is_nullable) 250 << "Primitive type should be non-nullable"; 251 DCHECK(!validate_params->element_validate_params) 252 << "Primitive type should not have array validate params"; 253 254 size_t size = input->GetSize(); 255 for (size_t i = 0; i < size; ++i) 256 output->at(i) = input->GetNext(); 257 } 258 static bool DeserializeElements(Data* input, 259 UserType* output, 260 SerializationContext* context) { 261 if (!Traits::Resize(*output, input->size())) 262 return false; 263 ArrayIterator<Traits, UserType> iterator(*output); 264 for (size_t i = 0; i < input->size(); ++i) 265 iterator.GetNext() = input->at(i); 266 return true; 267 } 268 }; 269 270 // Serializes and deserializes arrays of handles or interfaces. 271 template <typename MojomType, 272 typename MaybeConstUserType, 273 typename UserTypeIterator> 274 struct ArraySerializer< 275 MojomType, 276 MaybeConstUserType, 277 UserTypeIterator, 278 typename std::enable_if< 279 BelongsTo<typename MojomType::Element, 280 MojomTypeCategory::ASSOCIATED_INTERFACE | 281 MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST | 282 MojomTypeCategory::HANDLE | 283 MojomTypeCategory::INTERFACE | 284 MojomTypeCategory::INTERFACE_REQUEST>::value>::type> { 285 using UserType = typename std::remove_const<MaybeConstUserType>::type; 286 using Data = typename MojomTypeTraits<MojomType>::Data; 287 using Element = typename MojomType::Element; 288 using Traits = ArrayTraits<UserType>; 289 290 static_assert(std::is_same<Element, typename Traits::Element>::value, 291 "Incorrect array serializer"); 292 293 static size_t GetSerializedSize(UserTypeIterator* input, 294 SerializationContext* context) { 295 return sizeof(Data) + 296 Align(input->GetSize() * sizeof(typename Data::Element)); 297 } 298 299 static void SerializeElements(UserTypeIterator* input, 300 Buffer* buf, 301 Data* output, 302 const ContainerValidateParams* validate_params, 303 SerializationContext* context) { 304 DCHECK(!validate_params->element_validate_params) 305 << "Handle or interface type should not have array validate params"; 306 307 size_t size = input->GetSize(); 308 for (size_t i = 0; i < size; ++i) { 309 Serialize<Element>(input->GetNext(), &output->at(i), context); 310 311 static const ValidationError kError = 312 BelongsTo<Element, 313 MojomTypeCategory::ASSOCIATED_INTERFACE | 314 MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST>::value 315 ? VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID 316 : VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE; 317 MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( 318 !validate_params->element_is_nullable && 319 !IsHandleOrInterfaceValid(output->at(i)), 320 kError, 321 MakeMessageWithArrayIndex("invalid handle or interface ID in array " 322 "expecting valid handles or interface IDs", 323 size, i)); 324 } 325 } 326 static bool DeserializeElements(Data* input, 327 UserType* output, 328 SerializationContext* context) { 329 if (!Traits::Resize(*output, input->size())) 330 return false; 331 ArrayIterator<Traits, UserType> iterator(*output); 332 for (size_t i = 0; i < input->size(); ++i) { 333 bool result = 334 Deserialize<Element>(&input->at(i), &iterator.GetNext(), context); 335 DCHECK(result); 336 } 337 return true; 338 } 339 }; 340 341 // This template must only apply to pointer mojo entity (strings, structs, 342 // arrays and maps). 343 template <typename MojomType, 344 typename MaybeConstUserType, 345 typename UserTypeIterator> 346 struct ArraySerializer<MojomType, 347 MaybeConstUserType, 348 UserTypeIterator, 349 typename std::enable_if<BelongsTo< 350 typename MojomType::Element, 351 MojomTypeCategory::ARRAY | MojomTypeCategory::MAP | 352 MojomTypeCategory::STRING | 353 MojomTypeCategory::STRUCT>::value>::type> { 354 using UserType = typename std::remove_const<MaybeConstUserType>::type; 355 using Data = typename MojomTypeTraits<MojomType>::Data; 356 using Element = typename MojomType::Element; 357 using DataElementPtr = typename MojomTypeTraits<Element>::Data*; 358 using Traits = ArrayTraits<UserType>; 359 360 static size_t GetSerializedSize(UserTypeIterator* input, 361 SerializationContext* context) { 362 size_t element_count = input->GetSize(); 363 size_t size = sizeof(Data) + element_count * sizeof(typename Data::Element); 364 for (size_t i = 0; i < element_count; ++i) 365 size += PrepareToSerialize<Element>(input->GetNext(), context); 366 return size; 367 } 368 369 static void SerializeElements(UserTypeIterator* input, 370 Buffer* buf, 371 Data* output, 372 const ContainerValidateParams* validate_params, 373 SerializationContext* context) { 374 size_t size = input->GetSize(); 375 for (size_t i = 0; i < size; ++i) { 376 DataElementPtr data_ptr; 377 SerializeCaller<Element>::Run(input->GetNext(), buf, &data_ptr, 378 validate_params->element_validate_params, 379 context); 380 output->at(i).Set(data_ptr); 381 MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( 382 !validate_params->element_is_nullable && !data_ptr, 383 VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, 384 MakeMessageWithArrayIndex("null in array expecting valid pointers", 385 size, i)); 386 } 387 } 388 static bool DeserializeElements(Data* input, 389 UserType* output, 390 SerializationContext* context) { 391 if (!Traits::Resize(*output, input->size())) 392 return false; 393 ArrayIterator<Traits, UserType> iterator(*output); 394 for (size_t i = 0; i < input->size(); ++i) { 395 if (!Deserialize<Element>(input->at(i).Get(), &iterator.GetNext(), 396 context)) 397 return false; 398 } 399 return true; 400 } 401 402 private: 403 template <typename T, 404 bool is_array_or_map = BelongsTo<T, 405 MojomTypeCategory::ARRAY | 406 MojomTypeCategory::MAP>::value> 407 struct SerializeCaller { 408 template <typename InputElementType> 409 static void Run(InputElementType&& input, 410 Buffer* buf, 411 DataElementPtr* output, 412 const ContainerValidateParams* validate_params, 413 SerializationContext* context) { 414 Serialize<T>(std::forward<InputElementType>(input), buf, output, context); 415 } 416 }; 417 418 template <typename T> 419 struct SerializeCaller<T, true> { 420 template <typename InputElementType> 421 static void Run(InputElementType&& input, 422 Buffer* buf, 423 DataElementPtr* output, 424 const ContainerValidateParams* validate_params, 425 SerializationContext* context) { 426 Serialize<T>(std::forward<InputElementType>(input), buf, output, 427 validate_params, context); 428 } 429 }; 430 }; 431 432 // Handles serialization and deserialization of arrays of unions. 433 template <typename MojomType, 434 typename MaybeConstUserType, 435 typename UserTypeIterator> 436 struct ArraySerializer< 437 MojomType, 438 MaybeConstUserType, 439 UserTypeIterator, 440 typename std::enable_if<BelongsTo<typename MojomType::Element, 441 MojomTypeCategory::UNION>::value>::type> { 442 using UserType = typename std::remove_const<MaybeConstUserType>::type; 443 using Data = typename MojomTypeTraits<MojomType>::Data; 444 using Element = typename MojomType::Element; 445 using Traits = ArrayTraits<UserType>; 446 447 static_assert(std::is_same<typename MojomType::Element, 448 typename Traits::Element>::value, 449 "Incorrect array serializer"); 450 451 static size_t GetSerializedSize(UserTypeIterator* input, 452 SerializationContext* context) { 453 size_t element_count = input->GetSize(); 454 size_t size = sizeof(Data); 455 for (size_t i = 0; i < element_count; ++i) { 456 // Call with |inlined| set to false, so that it will account for both the 457 // data in the union and the space in the array used to hold the union. 458 size += PrepareToSerialize<Element>(input->GetNext(), false, context); 459 } 460 return size; 461 } 462 463 static void SerializeElements(UserTypeIterator* input, 464 Buffer* buf, 465 Data* output, 466 const ContainerValidateParams* validate_params, 467 SerializationContext* context) { 468 size_t size = input->GetSize(); 469 for (size_t i = 0; i < size; ++i) { 470 typename Data::Element* result = output->storage() + i; 471 Serialize<Element>(input->GetNext(), buf, &result, true, context); 472 MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( 473 !validate_params->element_is_nullable && output->at(i).is_null(), 474 VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, 475 MakeMessageWithArrayIndex("null in array expecting valid unions", 476 size, i)); 477 } 478 } 479 480 static bool DeserializeElements(Data* input, 481 UserType* output, 482 SerializationContext* context) { 483 if (!Traits::Resize(*output, input->size())) 484 return false; 485 ArrayIterator<Traits, UserType> iterator(*output); 486 for (size_t i = 0; i < input->size(); ++i) { 487 if (!Deserialize<Element>(&input->at(i), &iterator.GetNext(), context)) 488 return false; 489 } 490 return true; 491 } 492 }; 493 494 template <typename Element, typename MaybeConstUserType> 495 struct Serializer<Array<Element>, MaybeConstUserType> { 496 using UserType = typename std::remove_const<MaybeConstUserType>::type; 497 using Traits = ArrayTraits<UserType>; 498 using Impl = ArraySerializer<Array<Element>, 499 MaybeConstUserType, 500 ArrayIterator<Traits, MaybeConstUserType>>; 501 using Data = typename MojomTypeTraits<Array<Element>>::Data; 502 503 static size_t PrepareToSerialize(MaybeConstUserType& input, 504 SerializationContext* context) { 505 if (CallIsNullIfExists<Traits>(input)) 506 return 0; 507 ArrayIterator<Traits, MaybeConstUserType> iterator(input); 508 return Impl::GetSerializedSize(&iterator, context); 509 } 510 511 static void Serialize(MaybeConstUserType& input, 512 Buffer* buf, 513 Data** output, 514 const ContainerValidateParams* validate_params, 515 SerializationContext* context) { 516 if (!CallIsNullIfExists<Traits>(input)) { 517 MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( 518 validate_params->expected_num_elements != 0 && 519 Traits::GetSize(input) != validate_params->expected_num_elements, 520 internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER, 521 internal::MakeMessageWithExpectedArraySize( 522 "fixed-size array has wrong number of elements", 523 Traits::GetSize(input), validate_params->expected_num_elements)); 524 Data* result = Data::New(Traits::GetSize(input), buf); 525 if (result) { 526 ArrayIterator<Traits, MaybeConstUserType> iterator(input); 527 Impl::SerializeElements(&iterator, buf, result, validate_params, 528 context); 529 } 530 *output = result; 531 } else { 532 *output = nullptr; 533 } 534 } 535 536 static bool Deserialize(Data* input, 537 UserType* output, 538 SerializationContext* context) { 539 if (!input) 540 return CallSetToNullIfExists<Traits>(output); 541 return Impl::DeserializeElements(input, output, context); 542 } 543 }; 544 545 } // namespace internal 546 } // namespace mojo 547 548 #endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_ 549