v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
intrusive-set.h
Go to the documentation of this file.
1// Copyright 2023 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_BASE_INTRUSIVE_SET_H_
6#define V8_BASE_INTRUSIVE_SET_H_
7
8#include <iterator>
9#include <limits>
10#include <type_traits>
11
12#include "src/base/logging.h"
13
14namespace v8::base {
15
17 private:
18 template <class T, class GetIntrusiveSetIndex, class Container>
19 friend class IntrusiveSet;
20 static constexpr size_t kNotInSet = std::numeric_limits<size_t>::max();
21
22 size_t value = kNotInSet;
23};
24
25// A set of pointer-like values (`T`) that point to memory containing the
26// position inside of the set (`IntrusiveSetIndex`), to allow for O(1) insertion
27// and removal without using a hash table. This set is intrusive in the sense
28// that elements need to know their position inside of the set by storing an
29// `IntrusiveSetIndex` somewhere. In particular, all copies of a `T` value
30// should point to the same `IntrusiveSetIndex` instance. `GetIntrusiveSetIndex`
31// has to be a functor that produces `IntrusiveSetIndex&` given a `T`. The
32// reference has to remain valid and refer to the same memory location while the
33// element is in the set and until we finish iterating over the data structure
34// if the element is removed during iteration.
35//
36// Add(T): amortized O(1)
37// Contain(T): O(1)
38// Remove(T): O(1)
39template <class T, class GetIntrusiveSetIndex, class Container>
41 public:
42 // This is not needed for soundness, but rather serves as a hint that `T`
43 // should be a lightweight pointer-like value.
44 static_assert(std::is_trivially_copyable_v<T>);
45
46 explicit IntrusiveSet(Container container,
47 GetIntrusiveSetIndex index_functor = {})
48 : elements_(std::move(container)), index_functor_(index_functor) {
49 static_assert(std::is_same_v<decltype(index_functor(std::declval<T>())),
51 }
52
53 bool Contains(T x) const { return Index(x) != IntrusiveSetIndex::kNotInSet; }
54
55 // Adding elements while iterating is allowed.
56 void Add(T x) {
57 DCHECK(!Contains(x));
58 Index(x) = elements_.size();
59 elements_.push_back(x);
60 }
61
62 // Removing while iterating is allowed under very specific circumstances. See
63 // comment on `IntrusiveSet::iterator`.
64 void Remove(T x) {
66 size_t& index = Index(x);
67 DCHECK_EQ(x, elements_[index]);
68 Index(elements_.back()) = index;
69 elements_[index] = elements_.back();
71 elements_.pop_back();
72 }
73
74 // Since C++17, it is possible to have a sentinel end-iterator that is not an
75 // iterator itself.
76 class end_iterator {};
77
78 // This iterator supports insertion (newly inserted elements will be visited
79 // as part of the iteration) and removal of the current element while
80 // iterating. Removing previously visited elements is undefined behavior.
81 // ATTENTION! The memory the removed element points to needs to remain alive
82 // until the end of the iteration.
83 class iterator {
84 public:
85 explicit iterator(const IntrusiveSet& set) : set_(set) {}
87 T result = set_.elements_[index_];
89 return result;
90 }
92 // This iterator requires `operator*` being used before `operator++`.
94 if (index_ < set_.elements_.size() &&
95 last_index_location_ == &set_.Index(set_.elements_[index_])) {
96 index_++;
97 }
98 return *this;
99 }
101 return index_ < set_.elements_.size();
102 }
103
104 private:
106 size_t index_ = 0;
107 // If the current element is removed, another element is swapped in to the
108 // same position. We notice this by remembering the index memory location of
109 // the last retrieved element.
110 const size_t* last_index_location_ = nullptr;
111 };
112
113 // These iterators are only intended for range-based for loops.
114 iterator begin() const { return iterator{*this}; }
115 end_iterator end() const { return end_iterator{}; }
116
117 private:
118 Container elements_;
119 GetIntrusiveSetIndex index_functor_;
120
121 size_t& Index(T x) const { return index_functor_(x).value; }
122};
123
124} // namespace v8::base
125
126#endif // V8_BASE_INTRUSIVE_SET_H_
static constexpr size_t kNotInSet
iterator(const IntrusiveSet &set)
bool operator!=(end_iterator) const
GetIntrusiveSetIndex index_functor_
IntrusiveSet(Container container, GetIntrusiveSetIndex index_functor={})
iterator begin() const
end_iterator end() const
bool Contains(T x) const
size_t & Index(T x) const
OptionalOpIndex index
ZoneVector< RpoNumber > & result
int x
STL namespace.
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485