• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 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 TOOLS_GN_IMMUTABLE_VECTOR_H_
6 #define TOOLS_GN_IMMUTABLE_VECTOR_H_
7 
8 #include <cstdlib>
9 #include <type_traits>
10 #include <utility>
11 
12 #include "util/aligned_alloc.h"
13 
14 // An ImmutableVector<T> represents a fixed-size vector of constant items of
15 // type T. The in-memory representation is more efficient, only using one
16 // pointer to a single heap-allocated memory block that also contains the size.
17 //
18 // An ImmutableVectorView<T> acts as a copyable and movable reference to another
19 // ImmutableVector<T> instance. They both point to the same memory in the heap,
20 // but the view is not owning and is invalidated when the instance it points to
21 // is destroyed.
22 //
23 // Apart from that, they can be used with the same methods as a  const
24 // std::vector<T>.
25 //
26 template <typename T>
27 class ImmutableVector;
28 
29 template <typename T>
30 class ImmutableVectorView {
31  public:
32   // Default constructor
33   ImmutableVectorView() = default;
34 
35   // Constructor from an ImmutableVector.
ImmutableVectorView(const ImmutableVector<T> & vector)36   ImmutableVectorView(const ImmutableVector<T>& vector)
37       : header_(vector.header_) {}
38 
39   // Usual methods to access items.
data()40   const T* data() const { return begin(); }
size()41   size_t size() const { return header_ ? header_->size : 0u; }
empty()42   bool empty() const { return size() == 0; }
43   const T& operator[](size_t offset) const { return begin()[offset]; }
44 
begin()45   const T* begin() const { return header_ ? &header_->item0 : nullptr; }
end()46   const T* end() const {
47     return header_ ? &header_->item0 + header_->size : nullptr;
48   }
49 
front()50   const T& front() const { return begin()[0]; }
back()51   const T& back() const { return end()[-1]; }
52 
53   // For use with standard algorithms and templates.
54   using element_type = T;
55   using value_type = std::remove_cv_t<T>;
56   using size_type = std::size_t;
57   using difference_type = std::ptrdiff_t;
58   using pointer = T*;
59   using const_pointer = const T*;
60   using reference = T&;
61   using const_reference = const T&;
62   using iterator = const T*;
63   using const_iterator = const T*;
64 
find(const T & item)65   const_iterator find(const T& item) const {
66     auto it = begin();
67     auto it_end = end();
68     for (; it != it_end; ++it) {
69       if (*it == item)
70         break;
71     }
72     return it;
73   }
74 
contains(const T & item)75   bool contains(const T& item) const {
76     for (const auto& cur_item : *this)
77       if (cur_item == item)
78         return true;
79 
80     return false;
81   }
82 
83  protected:
84   struct Header {
85     size_t size;
86     T item0;
87   };
88 
ImmutableVectorView(Header * header)89   ImmutableVectorView(Header* header) : header_(header) {}
90 
91   Header* header_ = nullptr;
92 };
93 
94 template <typename T>
95 class ImmutableVector : public ImmutableVectorView<T> {
96  public:
97   // Default constructor.
98   ImmutableVector() = default;
99 
100   // Range constructors.
ImmutableVector(const T * begin,size_t size)101   ImmutableVector(const T* begin, size_t size)
102       : ImmutableVectorView<T>(AllocateAndCopyFrom(begin, size)) {}
103 
ImmutableVector(const T * begin,const T * end)104   ImmutableVector(const T* begin, const T* end)
105       : ImmutableVector(begin, end - begin) {}
106 
107   // In-place constructor
108   // |producer| must be a callable that generates a T instance that will
109   // be used to construct items in place in the allocated vector.
110   template <typename P,
111             typename = std::void_t<
112                 decltype(static_cast<const T&>(std::declval<P>()()))>>
ImmutableVector(P producer,size_t size)113   ImmutableVector(P producer, size_t size) {
114     if (size) {
115       this->header_ = AllocateFor(size);
116       auto* dst = &(this->header_->item0);
117       auto* dst_limit = dst + size;
118       for (; dst != dst_limit; ++dst)
119         new (dst) T(producer());
120     }
121   }
122 
123   // Container constructor
124   //
125   // This uses std::void_t<> to select container types whose values
126   // are convertible to |const T&|, and which have |begin()| and |size()|
127   // methods.
128   //
129   // This constructor copies items from the constructor into the
130   // ImmutableVector.
131   template <typename C,
132             typename = std::void_t<
133                 decltype(static_cast<const T>(*std::declval<C>().begin())),
134                 decltype(std::declval<C>().size())>>
ImmutableVector(const C & container)135   ImmutableVector(const C& container) {
136     size_t size = container.size();
137     if (size) {
138       this->header_ = AllocateFor(size);
139       auto src = container.begin();
140       auto* dst = &(this->header_->item0);
141       auto* dst_limit = dst + size;
142       for (; dst != dst_limit; ++dst, ++src) {
143         new (dst) T(*src);
144       }
145     }
146   }
147 
148   // Another container constructor where the items can be moved into
149   // the resulting ImmutableVector.
150   template <typename C,
151             typename = std::void_t<
152                 decltype(static_cast<const T>(*std::declval<C>().begin())),
153                 decltype(std::declval<C>().size())>>
ImmutableVector(C && container)154   ImmutableVector(C&& container) {
155     size_t size = container.size();
156     if (size) {
157       this->header_ = AllocateFor(size);
158       auto src = container.begin();
159       auto* dst = &(this->header_->item0);
160       auto* dst_limit = dst + size;
161       for (; dst != dst_limit; ++dst, ++src)
162         new (dst) T(std::move(*src));
163     }
164   }
165 
166   // Initializer-list container.
ImmutableVector(std::initializer_list<T> list)167   ImmutableVector(std::initializer_list<T> list)
168       : ImmutableVector(list.begin(), list.size()) {}
169 
170   // Copy operations
ImmutableVector(const ImmutableVector & other)171   ImmutableVector(const ImmutableVector& other)
172       : ImmutableVectorView<T>(
173             AllocateAndCopyFrom(other.begin(), other.size())) {}
174 
175   ImmutableVector& operator=(const ImmutableVector& other) {
176     if (this != &other) {
177       this->~ImmutableVector();
178       new (this) ImmutableVector(other);
179     }
180     return *this;
181   }
182 
183   // Move operations
ImmutableVector(ImmutableVector && other)184   ImmutableVector(ImmutableVector&& other) noexcept {
185     this->header_ = other.header_;
186     other.header_ = nullptr;
187   }
188 
189   ImmutableVector& operator=(ImmutableVector&& other) noexcept {
190     if (this != &other) {
191       this->~ImmutableVector();
192       new (this) ImmutableVector(std::move(other));
193     }
194     return *this;
195   }
196 
197   // Destructor
~ImmutableVector()198   ~ImmutableVector() {
199     if (this->header_) {
200       auto* cur = &(this->header_->item0);
201       auto* limit = cur + this->size();
202       while (cur != limit) {
203         (*cur).~T();
204         cur++;
205       }
206       Allocator::Free(this->header_);
207     }
208   }
209 
210  protected:
211   friend class ImmutableVectorView<T>;
212 
213   using Header = typename ImmutableVectorView<T>::Header;
214 
215   // Don't use std::max() here to avoid including <algorithm> which is massive.
216   static constexpr size_t kAlignment = alignof(T) > alignof(Header)
217                                            ? alignof(T)
218                                            : alignof(Header);
219 
220   using Allocator = AlignedAlloc<kAlignment>;
221 
AllocateFor(size_t count)222   static Header* AllocateFor(size_t count) {
223     if (!count)
224       return nullptr;
225 
226     auto* header = reinterpret_cast<Header*>(
227         Allocator::Alloc(sizeof(Header) + (count - 1u) * sizeof(T)));
228     header->size = count;
229     return header;
230   }
231 
AllocateAndCopyFrom(const T * begin,size_t size)232   static Header* AllocateAndCopyFrom(const T* begin, size_t size) {
233     Header* header = AllocateFor(size);
234     if (size) {
235       T* dst = &(header->item0);
236       T* limit = dst + size;
237       for (; dst != limit; ++dst, ++begin)
238         new (dst) T(*begin);
239     }
240     return header;
241   }
242 };
243 
244 #endif  // TOOLS_GN_IMMUTABLE_VECTOR_H_
245