Home | History | Annotate | Download | only in lib
      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 
     53   static uint32_t GetStorageSize(uint32_t num_elements) {
     54     DCHECK(num_elements <= kMaxNumElements);
     55     return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements;
     56   }
     57   static Ref ToRef(StorageType* storage, size_t offset) {
     58     return storage[offset];
     59   }
     60   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