Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(161)

Side by Side Diff: mojo/public/cpp/bindings/lib/pointer_deque.h

Issue 2900543002: Mojo C++ bindings: introduce mojo::internal::deque and use it in MultiplexRouter. (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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_POINTER_DEQUE_H_
6 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_POINTER_DEQUE_H_
7
8 #include <algorithm>
9 #include <limits>
10 #include <memory>
11 #include <type_traits>
12 #include <vector>
13
14 #include "base/logging.h"
15 #include "base/macros.h"
16
17 namespace mojo {
18 namespace internal {
19
20 template <typename T, bool owned>
21 class PointerDequeAccessor;
22
23 template <typename T, bool owned>
24 struct PointerDequeTraits;
25
26 template <typename T>
27 struct PointerDequeTraits<T, true> {
28 using ElementType = std::unique_ptr<T>;
29
30 static void destroy(std::vector<ElementType>* buffer, size_t index) {
31 (*buffer)[index].reset();
32 }
33
34 static void move(std::vector<ElementType>* buffer,
35 size_t first,
36 size_t inclusive_last,
37 size_t dest_first) {
38 auto iter_base = buffer->begin();
39 std::move(iter_base + first, iter_base + inclusive_last + 1,
40 iter_base + dest_first);
41 }
42 };
43
44 template <typename T>
45 struct PointerDequeTraits<T, false> {
46 using ElementType = T*;
47
48 static void destroy(std::vector<ElementType>* buffer, size_t index) {
49 // Do nothing.
50 }
51
52 static void move(std::vector<ElementType>* buffer,
53 size_t first,
54 size_t inclusive_last,
55 size_t dest_first) {
56 ElementType* iter_base = &(*buffer)[0];
57 memcpy(iter_base + dest_first, iter_base + first,
58 (inclusive_last - first + 1) * sizeof(ElementType));
59 }
60 };
61
62 // PointerDeque is a deque for pointers (either raw pointers or
63 // std::unique_ptr's). It uses a std::vector as ring buffer, which is
64 // expanded when necessary.
65 //
66 // The reason to introduce this class is that std::deque is memory inefficient.
67 // (Please see crbug.com/674287 for more details.)
68 template <typename T, bool owned>
69 class PointerDeque {
70 public:
71 using Traits = PointerDequeTraits<T, owned>;
72 using ElementType = typename Traits::ElementType;
73
74 static constexpr size_t kInvalidIndex = std::numeric_limits<size_t>::max();
75 static constexpr size_t kDefaultInitialCapacity = 4;
76
77 explicit PointerDeque(size_t initial_capacity = kDefaultInitialCapacity)
78 : buffer_(initial_capacity) {}
79
80 PointerDeque(PointerDeque&& other) {
81 std::swap(front_, other.front_);
82 std::swap(back_, other.back_);
83 std::swap(buffer_, other.buffer_);
84 }
85
86 PointerDeque& operator=(PointerDeque&& other) {
87 if (this != &other) {
Ken Rockot(use gerrit already) 2017/05/22 16:45:28 I think we discourage self-assignment checks in mo
yzshen1 2017/05/25 00:06:43 Done. (It seems the discussion also came to the c
88 clear();
89 std::swap(front_, other.front_);
90 std::swap(back_, other.back_);
91 std::swap(buffer_, other.buffer_);
92 }
93 return *this;
94 }
95
96 void push_front(ElementType value) {
97 expand_if_necessary();
98 if (empty()) {
99 front_ = 0;
100 back_ = 0;
101 } else {
102 front_ = get_prev(front_);
103 }
104 buffer_[front_] = std::move(value);
105 }
106
107 void pop_front() {
108 DCHECK(!empty());
109
110 Traits::destroy(&buffer_, front_);
111 if (front_ == back_) {
112 front_ = kInvalidIndex;
113 back_ = kInvalidIndex;
114 } else {
115 front_ = get_next(front_);
116 }
117 }
118
119 ElementType& front() {
120 DCHECK(!empty());
121 return buffer_[front_];
122 }
123
124 const ElementType& front() const {
125 DCHECK(!empty());
126 return buffer_[front_];
127 }
128
129 void push_back(ElementType value) {
130 expand_if_necessary();
131 if (empty()) {
132 front_ = 0;
133 back_ = 0;
134 } else {
135 back_ = get_next(back_);
136 }
137 buffer_[back_] = std::move(value);
138 }
139
140 void pop_back() {
141 DCHECK(!empty());
142
143 Traits::destroy(&buffer_, back_);
144 if (front_ == back_) {
145 front_ = kInvalidIndex;
146 back_ = kInvalidIndex;
147 } else {
148 back_ = get_prev(back_);
149 }
150 }
151
152 ElementType& back() {
153 DCHECK(!empty());
154 return buffer_[back_];
155 }
156
157 const ElementType& back() const {
158 DCHECK(!empty());
159 return buffer_[back_];
160 }
161
162 void clear() {
163 if (empty())
164 return;
165
166 if (back_ >= front_) {
167 for (size_t i = front_; i <= back_; ++i)
168 Traits::destroy(&buffer_, i);
169 } else {
170 for (size_t i = front_; i < buffer_.size(); ++i)
171 Traits::destroy(&buffer_, i);
172 for (size_t i = 0; i <= back_; ++i)
173 Traits::destroy(&buffer_, i);
174 }
175
176 front_ = kInvalidIndex;
177 back_ = kInvalidIndex;
178 }
179
180 bool empty() const { return front_ == kInvalidIndex; }
181
182 private:
183 friend class PointerDequeAccessor<T, owned>;
184
185 size_t get_next(size_t index) {
186 DCHECK(!buffer_.empty());
187 return index != buffer_.size() - 1 ? index + 1 : 0;
188 }
189
190 size_t get_prev(size_t index) {
191 DCHECK(!buffer_.empty());
192 return index != 0 ? index - 1 : buffer_.size() - 1;
193 }
194
195 void expand_if_necessary() {
196 if (buffer_.empty()) {
197 DCHECK_EQ(kInvalidIndex, front_);
198 DCHECK_EQ(kInvalidIndex, back_);
199
200 buffer_.resize(kDefaultInitialCapacity);
201 } else {
202 // Whether the buffer still has available slots.
203 if (empty() || get_prev(front_) != back_)
204 return;
205
206 size_t original_size = buffer_.size();
Ken Rockot(use gerrit already) 2017/05/22 16:45:28 nit: Good use for CheckedNumeric in base/numerics/
yzshen1 2017/05/25 00:06:44 Done.
207 CHECK_LE(original_size, std::numeric_limits<size_t>::max() / 2);
208 buffer_.resize(original_size * 2);
209
210 // Whether we need to move part of the elements to remain a ring buffer.
211 if (front_ != 0) {
212 if (back_ < original_size / 2) {
213 // Move [0, back_] to [original_size, original_size + back_].
214 Traits::move(&buffer_, 0, back_, original_size);
215 back_ += original_size;
216 } else {
217 // Move [front_, original_size - 1] to
218 // [front_ + buffer_.size() - original_size, buffer_.size() - 1].
219 size_t new_front = front_ + buffer_.size() - original_size;
220 Traits::move(&buffer_, front_, original_size - 1, new_front);
221 front_ = new_front;
222 }
223 }
224 }
225 }
226
227 size_t front_ = kInvalidIndex;
228 // NOTE: |back_| is inclusive.
229 size_t back_ = kInvalidIndex;
230 std::vector<ElementType> buffer_;
231
232 DISALLOW_COPY_AND_ASSIGN(PointerDeque);
233 };
234
235 template <typename T, bool owned>
236 constexpr size_t PointerDeque<T, owned>::kInvalidIndex;
237
238 } // namespace internal
239 } // namespace mojo
240
241 #endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_POINTER_DEQUE_H_
OLDNEW
« no previous file with comments | « mojo/public/cpp/bindings/lib/multiplex_router.h ('k') | mojo/public/cpp/bindings/tests/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698