1 // Copyright 2013 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_INTERNAL_H_ 6 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include <limits> 12 #include <new> 13 14 #include "base/component_export.h" 15 #include "base/logging.h" 16 #include "base/macros.h" 17 #include "mojo/public/c/system/macros.h" 18 #include "mojo/public/cpp/bindings/lib/bindings_internal.h" 19 #include "mojo/public/cpp/bindings/lib/buffer.h" 20 #include "mojo/public/cpp/bindings/lib/serialization_util.h" 21 #include "mojo/public/cpp/bindings/lib/template_util.h" 22 #include "mojo/public/cpp/bindings/lib/validate_params.h" 23 #include "mojo/public/cpp/bindings/lib/validation_context.h" 24 #include "mojo/public/cpp/bindings/lib/validation_errors.h" 25 #include "mojo/public/cpp/bindings/lib/validation_util.h" 26 27 namespace mojo { 28 namespace internal { 29 30 template <typename K, typename V> 31 class Map_Data; 32 33 COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) 34 std::string MakeMessageWithArrayIndex(const char* message, 35 size_t size, 36 size_t index); 37 38 COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) 39 std::string MakeMessageWithExpectedArraySize(const char* message, 40 size_t size, 41 size_t expected_size); 42 43 template <typename T> 44 struct ArrayDataTraits { 45 using StorageType = T; 46 using Ref = T&; 47 using ConstRef = const T&; 48 49 static const uint32_t kMaxNumElements = 50 (std::numeric_limits<uint32_t>::max() - sizeof(ArrayHeader)) / 51 sizeof(StorageType); 52 GetStorageSizeArrayDataTraits53 static uint32_t GetStorageSize(uint32_t num_elements) { 54 DCHECK(num_elements <= kMaxNumElements); 55 return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements; 56 } ToRefArrayDataTraits57 static Ref ToRef(StorageType* storage, size_t offset) { 58 return storage[offset]; 59 } ToConstRefArrayDataTraits60 static ConstRef ToConstRef(const StorageType* storage, size_t offset) { 61 return storage[offset]; 62 } 63 }; 64 65 // Specialization of Arrays for bools, optimized for space. It has the 66 // following differences from a generalized Array: 67 // * Each element takes up a single bit of memory. 68 // * Accessing a non-const single element uses a helper class |BitRef|, which 69 // emulates a reference to a bool. 70 template <> 71 struct ArrayDataTraits<bool> { 72 // Helper class to emulate a reference to a bool, used for direct element 73 // access. 74 class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) BitRef { 75 public: 76 ~BitRef(); 77 BitRef& operator=(bool value); 78 BitRef& operator=(const BitRef& value); 79 operator bool() const; 80 81 private: 82 friend struct ArrayDataTraits<bool>; 83 BitRef(uint8_t* storage, uint8_t mask); 84 BitRef(); 85 uint8_t* storage_; 86 uint8_t mask_; 87 }; 88 89 // Because each element consumes only 1/8 byte. 90 static const uint32_t kMaxNumElements = std::numeric_limits<uint32_t>::max(); 91 92 using StorageType = uint8_t; 93 using Ref = BitRef; 94 using ConstRef = bool; 95 96 static uint32_t GetStorageSize(uint32_t num_elements) { 97 return sizeof(ArrayHeader) + ((num_elements + 7) / 8); 98 } 99 static BitRef ToRef(StorageType* storage, size_t offset) { 100 return BitRef(&storage[offset / 8], 1 << (offset % 8)); 101 } 102 static bool ToConstRef(const StorageType* storage, size_t offset) { 103 return (storage[offset / 8] & (1 << (offset % 8))) != 0; 104 } 105 }; 106 107 // What follows is code to support the serialization/validation of 108 // Array_Data<T>. There are four interesting cases: arrays of primitives, 109 // arrays of handles/interfaces, arrays of objects and arrays of unions. 110 // Arrays of objects are represented as arrays of pointers to objects. Arrays 111 // of unions are inlined so they are not pointers, but comparing with primitives 112 // they require more work for serialization/validation. 113 // 114 // TODO(yzshen): Validation code should be organzied in a way similar to 115 // Serializer<>, or merged into it. It should be templatized with the mojo 116 // data view type instead of the data type, that way we can use MojomTypeTraits 117 // to determine the categories. 118 119 template <typename T, bool is_union, bool is_handle_or_interface> 120 struct ArraySerializationHelper; 121 122 template <typename T> 123 struct ArraySerializationHelper<T, false, false> { 124 using ElementType = typename ArrayDataTraits<T>::StorageType; 125 126 static bool ValidateElements(const ArrayHeader* header, 127 const ElementType* elements, 128 ValidationContext* validation_context, 129 const ContainerValidateParams* validate_params) { 130 DCHECK(!validate_params->element_is_nullable) 131 << "Primitive type should be non-nullable"; 132 DCHECK(!validate_params->element_validate_params) 133 << "Primitive type should not have array validate params"; 134 135 if (!validate_params->validate_enum_func) 136 return true; 137 138 // Enum validation. 139 for (uint32_t i = 0; i < header->num_elements; ++i) { 140 if (!validate_params->validate_enum_func(elements[i], validation_context)) 141 return false; 142 } 143 return true; 144 } 145 }; 146 147 template <typename T> 148 struct ArraySerializationHelper<T, false, true> { 149 using ElementType = typename ArrayDataTraits<T>::StorageType; 150 151 static bool ValidateElements(const ArrayHeader* header, 152 const ElementType* elements, 153 ValidationContext* validation_context, 154 const ContainerValidateParams* validate_params) { 155 DCHECK(!validate_params->element_validate_params) 156 << "Handle or interface type should not have array validate params"; 157 158 for (uint32_t i = 0; i < header->num_elements; ++i) { 159 if (!validate_params->element_is_nullable && 160 !IsHandleOrInterfaceValid(elements[i])) { 161 static const ValidationError kError = 162 std::is_same<T, Interface_Data>::value || 163 std::is_same<T, Handle_Data>::value 164 ? VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE 165 : VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID; 166 ReportValidationError( 167 validation_context, kError, 168 MakeMessageWithArrayIndex( 169 "invalid handle or interface ID in array expecting valid " 170 "handles or interface IDs", 171 header->num_elements, i) 172 .c_str()); 173 return false; 174 } 175 if (!ValidateHandleOrInterface(elements[i], validation_context)) 176 return false; 177 } 178 return true; 179 } 180 }; 181 182 template <typename T> 183 struct ArraySerializationHelper<Pointer<T>, false, false> { 184 using ElementType = typename ArrayDataTraits<Pointer<T>>::StorageType; 185 186 static bool ValidateElements(const ArrayHeader* header, 187 const ElementType* elements, 188 ValidationContext* validation_context, 189 const ContainerValidateParams* validate_params) { 190 for (uint32_t i = 0; i < header->num_elements; ++i) { 191 if (!validate_params->element_is_nullable && !elements[i].offset) { 192 ReportValidationError( 193 validation_context, 194 VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, 195 MakeMessageWithArrayIndex("null in array expecting valid pointers", 196 header->num_elements, 197 i).c_str()); 198 return false; 199 } 200 if (!ValidateCaller<T>::Run(elements[i], validation_context, 201 validate_params->element_validate_params)) { 202 return false; 203 } 204 } 205 return true; 206 } 207 208 private: 209 template <typename U, 210 bool is_array_or_map = IsSpecializationOf<Array_Data, U>::value || 211 IsSpecializationOf<Map_Data, U>::value> 212 struct ValidateCaller { 213 static bool Run(const Pointer<U>& data, 214 ValidationContext* validation_context, 215 const ContainerValidateParams* validate_params) { 216 DCHECK(!validate_params) 217 << "Struct type should not have array validate params"; 218 219 return ValidateStruct(data, validation_context); 220 } 221 }; 222 223 template <typename U> 224 struct ValidateCaller<U, true> { 225 static bool Run(const Pointer<U>& data, 226 ValidationContext* validation_context, 227 const ContainerValidateParams* validate_params) { 228 return ValidateContainer(data, validation_context, validate_params); 229 } 230 }; 231 }; 232 233 template <typename U> 234 struct ArraySerializationHelper<U, true, false> { 235 using ElementType = typename ArrayDataTraits<U>::StorageType; 236 237 static bool ValidateElements(const ArrayHeader* header, 238 const ElementType* elements, 239 ValidationContext* validation_context, 240 const ContainerValidateParams* validate_params) { 241 for (uint32_t i = 0; i < header->num_elements; ++i) { 242 if (!validate_params->element_is_nullable && elements[i].is_null()) { 243 ReportValidationError( 244 validation_context, 245 VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, 246 MakeMessageWithArrayIndex("null in array expecting valid unions", 247 header->num_elements, i) 248 .c_str()); 249 return false; 250 } 251 if (!ValidateInlinedUnion(elements[i], validation_context)) 252 return false; 253 } 254 return true; 255 } 256 }; 257 258 template <typename T> 259 class Array_Data { 260 public: 261 using Traits = ArrayDataTraits<T>; 262 using StorageType = typename Traits::StorageType; 263 using Ref = typename Traits::Ref; 264 using ConstRef = typename Traits::ConstRef; 265 using Helper = ArraySerializationHelper< 266 T, 267 IsUnionDataType<T>::value, 268 std::is_same<T, AssociatedInterface_Data>::value || 269 std::is_same<T, AssociatedEndpointHandle_Data>::value || 270 std::is_same<T, Interface_Data>::value || 271 std::is_same<T, Handle_Data>::value>; 272 using Element = T; 273 274 class BufferWriter { 275 public: 276 BufferWriter() = default; 277 278 void Allocate(size_t num_elements, Buffer* buffer) { 279 if (num_elements > Traits::kMaxNumElements) 280 return; 281 282 uint32_t num_bytes = 283 Traits::GetStorageSize(static_cast<uint32_t>(num_elements)); 284 buffer_ = buffer; 285 index_ = buffer_->Allocate(num_bytes); 286 new (data()) 287 Array_Data<T>(num_bytes, static_cast<uint32_t>(num_elements)); 288 } 289 290 bool is_null() const { return !buffer_; } 291 Array_Data<T>* data() { 292 DCHECK(!is_null()); 293 return buffer_->Get<Array_Data<T>>(index_); 294 } 295 Array_Data<T>* operator->() { return data(); } 296 297 private: 298 Buffer* buffer_ = nullptr; 299 size_t index_ = 0; 300 301 DISALLOW_COPY_AND_ASSIGN(BufferWriter); 302 }; 303 304 static bool Validate(const void* data, 305 ValidationContext* validation_context, 306 const ContainerValidateParams* validate_params) { 307 if (!data) 308 return true; 309 if (!IsAligned(data)) { 310 ReportValidationError(validation_context, 311 VALIDATION_ERROR_MISALIGNED_OBJECT); 312 return false; 313 } 314 if (!validation_context->IsValidRange(data, sizeof(ArrayHeader))) { 315 ReportValidationError(validation_context, 316 VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); 317 return false; 318 } 319 const ArrayHeader* header = static_cast<const ArrayHeader*>(data); 320 if (header->num_elements > Traits::kMaxNumElements || 321 header->num_bytes < Traits::GetStorageSize(header->num_elements)) { 322 ReportValidationError(validation_context, 323 VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER); 324 return false; 325 } 326 if (validate_params->expected_num_elements != 0 && 327 header->num_elements != validate_params->expected_num_elements) { 328 ReportValidationError( 329 validation_context, 330 VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER, 331 MakeMessageWithExpectedArraySize( 332 "fixed-size array has wrong number of elements", 333 header->num_elements, 334 validate_params->expected_num_elements).c_str()); 335 return false; 336 } 337 if (!validation_context->ClaimMemory(data, header->num_bytes)) { 338 ReportValidationError(validation_context, 339 VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); 340 return false; 341 } 342 343 const Array_Data<T>* object = static_cast<const Array_Data<T>*>(data); 344 return Helper::ValidateElements(&object->header_, object->storage(), 345 validation_context, validate_params); 346 } 347 348 size_t size() const { return header_.num_elements; } 349 350 Ref at(size_t offset) { 351 DCHECK(offset < static_cast<size_t>(header_.num_elements)); 352 return Traits::ToRef(storage(), offset); 353 } 354 355 ConstRef at(size_t offset) const { 356 DCHECK(offset < static_cast<size_t>(header_.num_elements)); 357 return Traits::ToConstRef(storage(), offset); 358 } 359 360 StorageType* storage() { 361 return reinterpret_cast<StorageType*>(reinterpret_cast<char*>(this) + 362 sizeof(*this)); 363 } 364 365 const StorageType* storage() const { 366 return reinterpret_cast<const StorageType*>( 367 reinterpret_cast<const char*>(this) + sizeof(*this)); 368 } 369 370 private: 371 Array_Data(uint32_t num_bytes, uint32_t num_elements) { 372 header_.num_bytes = num_bytes; 373 header_.num_elements = num_elements; 374 } 375 ~Array_Data() = delete; 376 377 internal::ArrayHeader header_; 378 379 // Elements of type internal::ArrayDataTraits<T>::StorageType follow. 380 }; 381 static_assert(sizeof(Array_Data<char>) == 8, "Bad sizeof(Array_Data)"); 382 383 // UTF-8 encoded 384 using String_Data = Array_Data<char>; 385 386 } // namespace internal 387 } // namespace mojo 388 389 #endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ 390