xref: /aosp_15_r20/external/libchrome/mojo/public/cpp/bindings/lib/array_internal.h (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
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