v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
zone-compact-set.h
Go to the documentation of this file.
1// Copyright 2016 the V8 project 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 V8_ZONE_ZONE_COMPACT_SET_H_
6#define V8_ZONE_ZONE_COMPACT_SET_H_
7
8#include <algorithm>
9#include <initializer_list>
10#include <type_traits>
11
15#include "src/handles/handles.h"
17#include "src/zone/zone.h"
18
19namespace v8 {
20namespace internal {
21
22template <typename T, typename Enable = void>
24
25template <typename T>
29
31 // Use address() instead of location() to get around handle access checks
32 // (we're not actually dereferencing the handle so it's safe to read its
33 // location)
34 return reinterpret_cast<Address*>(handle.address());
35 }
37 return handle_type(ptr);
38 }
39};
40
41// A Zone-allocated set which has a compact encoding of zero and one values.
42// Two or more values will be stored as a sorted list, which is copied on write
43// to keep the ZoneCompactSet copy constructor trivial. Note that this means
44// that insertions past the first value will trigger an allocation and copy of
45// the existing elements -- ZoneCompactSet should be preferred for cases where
46// we mostly have only zero or one values.
47//
48// T must be a Handle-like type with a specialization of ZoneCompactSetTraits.
49// In particular, it must be a trivial wrapper of a pointer to actual data --
50// ZoneCompactSet will store this pointer rather than the T type.
51template <typename T>
52class ZoneCompactSet final {
53 static_assert(std::is_trivially_copyable_v<T>);
54 static_assert(std::is_trivially_destructible_v<T>);
55
57 using handle_type = typename Traits::handle_type;
58 using data_type = typename Traits::data_type;
59
60 public:
63 : data_(Traits::HandleToPointer(handle), kSingletonTag) {}
64 explicit ZoneCompactSet(std::initializer_list<T> handles, Zone* zone)
65 : ZoneCompactSet(handles.begin(), handles.end(), zone) {}
66
67 ZoneCompactSet(const ZoneCompactSet& other) V8_NOEXCEPT = default;
71
72 template <class It,
73 typename = typename std::iterator_traits<It>::iterator_category>
74 explicit ZoneCompactSet(It first, It last, Zone* zone) {
75 auto size = last - first;
76 if (size == 0) {
77 data_ = EmptyValue();
78 } else if (size == 1) {
79 data_ =
80 PointerWithPayload(Traits::HandleToPointer(*first), kSingletonTag);
81 } else {
82 List* list = NewList(size, zone);
83 auto list_it = list->begin();
84 for (auto it = first; it != last; ++it) {
85 *list_it = Traits::HandleToPointer(*it);
86 list_it++;
87 }
88 std::sort(list->begin(), list->end());
90 }
91 }
92
94 return ZoneCompactSet<T>(begin(), end(), zone);
95 }
96
97 bool is_empty() const { return data_ == EmptyValue(); }
98
99 size_t size() const {
100 if (is_empty()) return 0;
101 if (is_singleton()) return 1;
102 return list()->size();
103 }
104
105 T at(size_t i) const {
107 if (is_singleton()) {
108 DCHECK_EQ(0u, i);
109 return Traits::PointerToHandle(singleton());
110 }
111 return Traits::PointerToHandle(list()->at(static_cast<int>(i)));
112 }
113
114 T operator[](size_t i) const { return at(i); }
115
116 void insert(T handle, Zone* zone) {
117 data_type* const value = Traits::HandleToPointer(handle);
118 if (is_empty()) {
120 } else if (is_singleton()) {
121 if (singleton() == value) return;
122 List* list = NewList(2, zone);
123 if (singleton() < value) {
124 (*list)[0] = singleton();
125 (*list)[1] = value;
126 } else {
127 (*list)[0] = value;
128 (*list)[1] = singleton();
129 }
131 } else {
132 const List* current_list = list();
133 auto it =
134 std::lower_bound(current_list->begin(), current_list->end(), value);
135 if (it != current_list->end() && *it == value) {
136 // Already in the list.
137 return;
138 }
139 // Otherwise, lower_bound returned the insertion position to keep the list
140 // sorted.
141 DCHECK(it == current_list->end() || *it > value);
142 // We need to copy the list to mutate it, so that trivial copies of the
143 // data_ pointer don't observe changes to the list.
144 // TODO(leszeks): Avoid copying on every insertion by introducing some
145 // concept of mutable/immutable/frozen/CoW sets.
146 List* new_list = NewList(current_list->size() + 1, zone);
147 auto new_it = new_list->begin();
148 new_it = std::copy(current_list->begin(), it, new_it);
149 *new_it++ = value;
150 new_it = std::copy(it, current_list->end(), new_it);
151 DCHECK_EQ(new_it, new_list->end());
152 DCHECK(std::is_sorted(new_list->begin(), new_list->end()));
153 data_ = PointerWithPayload(new_list, kListTag);
154 }
155 }
156
157 void Union(ZoneCompactSet<T> const& other, Zone* zone) {
158 for (size_t i = 0; i < other.size(); ++i) {
159 insert(other.at(i), zone);
160 }
161 }
162
163 bool contains(ZoneCompactSet<T> const& other) const {
164 if (data_ == other.data_) return true;
165 if (is_empty()) return false;
166 if (other.is_empty()) return true;
167 if (is_singleton()) {
168 DCHECK_IMPLIES(other.is_singleton(), other.singleton() != singleton());
169 return false;
170 }
171 const List* list = this->list();
172 DCHECK(std::is_sorted(list->begin(), list->end()));
173 if (other.is_singleton()) {
174 return std::binary_search(list->begin(), list->end(), other.singleton());
175 }
176 DCHECK(other.is_list());
177 DCHECK(std::is_sorted(other.list()->begin(), other.list()->end()));
178 // For each element in the `other` list, find the matching element in this
179 // list. Since both lists are sorted, each search candidate will be larger
180 // than the previous, and each found element will be the lower bound for
181 // the search of the next element.
182 auto it = list->begin();
183 for (const data_type* pointer : *other.list()) {
184 it = std::lower_bound(it, list->end(), pointer);
185 if (it == list->end() || *it != pointer) return false;
186 }
187 return true;
188 }
189
190 bool contains(T handle) const {
191 if (is_empty()) return false;
192 data_type* pointer = Traits::HandleToPointer(handle);
193 if (is_singleton()) {
194 return singleton() == pointer;
195 }
196 const List* list = this->list();
197 DCHECK(std::is_sorted(list->begin(), list->end()));
198 return std::binary_search(list->begin(), list->end(), pointer);
199 }
200
201 void remove(T handle, Zone* zone) {
202 if (is_empty()) return;
203 data_type* pointer = Traits::HandleToPointer(handle);
204 if (is_singleton()) {
205 if (singleton() == pointer) {
206 data_ = EmptyValue();
207 }
208 return;
209 }
210 const List* current_list = list();
211 auto found_it =
212 std::lower_bound(current_list->begin(), current_list->end(), pointer);
213 if (found_it == current_list->end() || *found_it != pointer) {
214 // Not in the list.
215 return;
216 }
217 // Otherwise, lower_bound returned the location of the value.
218
219 // Drop back down to singleton mode if the size will drops to 1 -- this is
220 // needed to ensure that comparisons are correct. We never have to drop down
221 // from list to zero size.
222 DCHECK_GE(current_list->size(), 2);
223 if (current_list->size() == 2) {
224 data_type* other_value;
225 if (found_it == current_list->begin()) {
226 other_value = current_list->at(1);
227 } else {
228 other_value = current_list->at(0);
229 }
230 data_ = PointerWithPayload(other_value, kSingletonTag);
231 return;
232 }
233
234 // We need to copy the list to mutate it, so that trivial copies of the
235 // data_ pointer don't observe changes to the list.
236 List* new_list = NewList(current_list->size() - 1, zone);
237 auto new_it = new_list->begin();
238 new_it = std::copy(current_list->begin(), found_it, new_it);
239 new_it = std::copy(found_it + 1, current_list->end(), new_it);
240 DCHECK_EQ(new_it, new_list->end());
241 DCHECK(std::is_sorted(new_list->begin(), new_list->end()));
242 data_ = PointerWithPayload(new_list, kListTag);
243 }
244
245 void clear() { data_ = EmptyValue(); }
246
247 friend bool operator==(ZoneCompactSet<T> const& lhs,
248 ZoneCompactSet<T> const& rhs) {
249 if (lhs.data_ == rhs.data_) return true;
250 if (lhs.is_list() && rhs.is_list()) {
251 List const* const lhs_list = lhs.list();
252 List const* const rhs_list = rhs.list();
253 return std::equal(lhs_list->begin(), lhs_list->end(), rhs_list->begin(),
254 rhs_list->end());
255 }
256 return false;
257 }
258
259 friend bool operator!=(ZoneCompactSet<T> const& lhs,
260 ZoneCompactSet<T> const& rhs) {
261 return !(lhs == rhs);
262 }
263
264 friend uintptr_t hash_value(ZoneCompactSet<T> const& set) {
265 return set.data_.raw();
266 }
267
268 class const_iterator;
269 inline const_iterator begin() const;
270 inline const_iterator end() const;
271
272 private:
273 enum Tag { kSingletonTag = 0, kEmptyTag = 1, kListTag = 2 };
274
277
278 bool is_singleton() const { return data_.GetPayload() == kSingletonTag; }
279 bool is_list() const { return data_.GetPayload() == kListTag; }
280
281 List const* list() const {
282 DCHECK(is_list());
283 return static_cast<List const*>(data_.GetPointerWithKnownPayload(kListTag));
284 }
285
287 return static_cast<data_type*>(
289 }
290
291 List* NewList(size_t size, Zone* zone) {
292 // We need to allocate both the List, and the backing store of the list, in
293 // the zone, so that we have a List pointer and not an on-stack List (which
294 // we can't use in the `data_` pointer).
295 return zone->New<List>(zone->AllocateArray<data_type*>(size), size);
296 }
297
299 return PointerWithPayload(nullptr, kEmptyTag);
300 }
301
303};
304
305template <typename T>
306std::ostream& operator<<(std::ostream& os, ZoneCompactSet<T> set) {
307 for (size_t i = 0; i < set.size(); ++i) {
308 if (i > 0) os << ", ";
309 os << set.at(i);
310 }
311 return os;
312}
313
314template <typename T>
316 public:
317 using iterator_category = std::forward_iterator_tag;
318 using difference_type = std::ptrdiff_t;
319 using value_type = T;
322
323 const_iterator(const const_iterator& other) = default;
324 const_iterator& operator=(const const_iterator& other) = default;
325
326 reference operator*() const { return (*set_)[current_]; }
327 bool operator==(const const_iterator& other) const {
328 return set_ == other.set_ && current_ == other.current_;
329 }
330 bool operator!=(const const_iterator& other) const {
331 return !(*this == other);
332 }
335 current_ += 1;
336 return *this;
337 }
339
341 DCHECK_EQ(set_, other.set_);
342 return current_ - other.current_;
343 }
344
345 private:
346 friend class ZoneCompactSet<T>;
347
348 explicit const_iterator(const ZoneCompactSet<T>* set, size_t current)
349 : set_(set), current_(current) {}
350
352 size_t current_;
353};
354
355template <typename T>
359
360template <typename T>
364
365template <typename T>
367
368} // namespace internal
369} // namespace v8
370
371#endif // V8_ZONE_ZONE_COMPACT_SET_H_
#define T
V8_INLINE PointerType * GetPointerWithKnownPayload(PayloadType payload) const
V8_INLINE PayloadType GetPayload() const
constexpr size_t size() const
Definition vector.h:70
const T & at(size_t index) const
Definition vector.h:81
constexpr T * begin() const
Definition vector.h:96
constexpr T * end() const
Definition vector.h:103
bool operator!=(const const_iterator &other) const
const_iterator(const const_iterator &other)=default
const_iterator & operator=(const const_iterator &other)=default
difference_type operator-(const const_iterator &other) const
bool operator==(const const_iterator &other) const
const_iterator(const ZoneCompactSet< T > *set, size_t current)
void Union(ZoneCompactSet< T > const &other, Zone *zone)
void remove(T handle, Zone *zone)
ZoneCompactSet(const ZoneCompactSet &other) V8_NOEXCEPT=default
friend bool operator==(ZoneCompactSet< T > const &lhs, ZoneCompactSet< T > const &rhs)
void insert(T handle, Zone *zone)
ZoneCompactSet(std::initializer_list< T > handles, Zone *zone)
static PointerWithPayload EmptyValue()
base::PointerWithPayload< void, Tag, 2 > PointerWithPayload
friend bool operator!=(ZoneCompactSet< T > const &lhs, ZoneCompactSet< T > const &rhs)
List * NewList(size_t size, Zone *zone)
ZoneCompactSet & operator=(ZoneCompactSet &&other) V8_NOEXCEPT=default
friend uintptr_t hash_value(ZoneCompactSet< T > const &set)
const_iterator begin() const
typename Traits::handle_type handle_type
ZoneCompactSet & operator=(const ZoneCompactSet &other) V8_NOEXCEPT=default
ZoneCompactSet< T > Clone(Zone *zone) const
ZoneCompactSet(ZoneCompactSet &&other) V8_NOEXCEPT=default
bool contains(ZoneCompactSet< T > const &other) const
ZoneCompactSet(It first, It last, Zone *zone)
bool contains(T handle) const
typename Traits::data_type data_type
const_iterator end() const
T * AllocateArray(size_t length)
Definition zone.h:127
T * New(Args &&... args)
Definition zone.h:114
ZoneVector< InstructionOperand > * set_
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
std::ostream & operator<<(std::ostream &os, AtomicMemoryOrder order)
return value
Definition map-inl.h:893
base::uc32 current_
#define V8_NOEXCEPT
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
static handle_type PointerToHandle(data_type *ptr)
static data_type * HandleToPointer(handle_type handle)