OLD | NEW |
---|---|
(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_ | |
OLD | NEW |