1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_H_
#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_H_
#include <concepts>
#include "base/numerics/safe_conversions.h"
#include "mojo/public/cpp/bindings/lib/default_construct_tag_internal.h"
#include "mojo/public/cpp/bindings/lib/template_util.h"
namespace mojo {
// This must be specialized for any type |T| to be serialized/deserialized as
// a mojom array.
//
// Usually you would like to do a partial specialization for a container (e.g.
// vector) template. Imagine you want to specialize it for Container<>, you need
// to implement:
//
// template <typename T>
// struct ArrayTraits<Container<T>> {
// using Element = T;
// // These two statements are optional. Use them if you'd like to serialize
// // a container that supports iterators but does not support O(1) random
// // access and so GetAt(...) would be expensive.
// // using Iterator = Container<T>::iterator;
// // using ConstIterator = Container<T>::const_iterator;
//
// // These two methods are optional. Please see comments in struct_traits.h
// // Note that unlike with StructTraits, IsNull() is called *twice* during
// // serialization for ArrayTraits.
// static bool IsNull(const Container<T>& input);
// static void SetToNull(Container<T>* output);
//
// static size_t GetSize(const Container<T>& input);
//
// // These two methods are optional. They are used to access the
// // underlying storage of the array to speed up copy of POD types.
// static T* GetData(Container<T>& input);
// static const T* GetData(const Container<T>& input);
//
// // The following six methods are optional if the GetAt(...) methods are
// // implemented. These methods specify how to read the elements of
// // Container in some sequential order specified by the iterator.
// //
// // Acquires an iterator positioned at the first element in the container.
// static ConstIterator GetBegin(const Container<T>& input);
// static Iterator GetBegin(Container<T>& input);
//
// // Advances |iterator| to the next position within the container.
// static void AdvanceIterator(ConstIterator& iterator);
// static void AdvanceIterator(Iterator& iterator);
//
// // Returns a reference to the value at the current position of
// // |iterator|. Optionally, the ConstIterator version of GetValue can
// // return by value instead of by reference if it makes sense for the
// // type.
// static const T& GetValue(ConstIterator& iterator);
// static T& GetValue(Iterator& iterator);
//
// // These two methods are optional if the iterator methods are
// // implemented.
// static T& GetAt(Container<T>& input, size_t index);
// static const T& GetAt(const Container<T>& input, size_t index);
//
// // Returning false results in deserialization failure and causes the
// // message pipe receiving it to be disconnected.
// // Note that mojo does not require that Resize preserve the original
// // elements in `input` it merely has to set the size of `input` to
// // `size`.
// static bool Resize(Container<T>& input, size_t size);
// };
//
template <typename T>
struct ArrayTraits {
static_assert(internal::AlwaysFalse<T>::value,
"Cannot find the mojo::ArrayTraits specialization. Did you "
"forget to include the corresponding header file?");
};
// Generic specialization for vector-like containers.
template <typename Container>
requires requires(Container& c, size_t i) {
typename Container::value_type;
{ c.size() } -> std::same_as<typename Container::size_type>;
{ c.clear() } -> std::same_as<void>;
{ c[i] } -> std::same_as<typename Container::reference>;
}
struct ArrayTraits<Container> {
using Element = Container::value_type;
// vector-like containers have no built-in null.
static bool IsNull(const Container& c) { return false; }
static void SetToNull(Container* c) {
// TODO(dcheng): Should this ever be called? It seems questionable...
c->clear();
}
static auto GetSize(const Container& c) { return c.size(); }
// Conditional since some vector implementations have specializations which do
// not provide direct access to an underlying array, e.g. `std::vector<bool>`.
static auto* GetData(Container& c)
requires requires {
{ c.data() } -> std::same_as<typename Container::pointer>;
}
{
return c.data();
}
static const auto* GetData(const Container& c)
requires requires {
{ c.data() } -> std::same_as<typename Container::const_pointer>;
}
{
return c.data();
}
// The static_casts here are safe, since out-of-range issues would be caught
// by `Resize()`.
static decltype(auto) GetAt(Container& c, size_t index) {
return c[static_cast<typename Container::size_type>(index)];
}
static decltype(auto) GetAt(const Container& c, size_t index) {
return c[static_cast<typename Container::size_type>(index)];
}
static bool Resize(Container& c, size_t size) {
if (c.size() == size) {
return true;
}
if (!base::IsValueInRangeForNumericType<typename Container::size_type>(
size)) {
return false;
}
if constexpr (std::constructible_from<Element,
::mojo::DefaultConstruct::Tag>) {
Container temp;
temp.reserve(size);
for (size_t i = 0; i < size; ++i) {
temp.emplace_back(internal::DefaultConstructTag());
}
c.swap(temp);
} else {
Container temp(static_cast<typename Container::size_type>(size));
c.swap(temp);
}
return true;
}
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_H_
|