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