OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ |
| 10 |
| 11 #ifndef WEBRTC_BASE_NUMERICS_CYCLIC_BUFFER_H_ |
| 12 #define WEBRTC_BASE_NUMERICS_CYCLIC_BUFFER_H_ |
| 13 |
| 14 #include <utility> // for move |
| 15 |
| 16 #include "webrtc/base/checks.h" |
| 17 |
| 18 namespace webrtc { |
| 19 |
| 20 enum GrowthPolicy { ASSERT = 0, DISCARD_OLDEST, GROW_BUFFER }; |
| 21 |
| 22 template <typename T, GrowthPolicy policy> |
| 23 class CyclicBuffer { |
| 24 public: |
| 25 explicit CyclicBuffer(size_t size); |
| 26 ~CyclicBuffer(); |
| 27 |
| 28 // TODO(terelius): Check what happens if size_ == capacity_ == 1 |
| 29 // TODO(terelius): Overload for rvalue references. Refactor to avoid code |
| 30 // duplication. |
| 31 void push_back(const T& elem) { |
| 32 T* new_position = extend_back(); |
| 33 *new_position = elem; |
| 34 } |
| 35 |
| 36 void push_back(T&& elem) { |
| 37 T* new_position = extend_back(); |
| 38 *new_position = std::move(elem); |
| 39 } |
| 40 |
| 41 void push_front(const T& elem) { |
| 42 T* new_position = extend_front(); |
| 43 *new_position = elem; |
| 44 } |
| 45 |
| 46 void push_front(T&& elem) { |
| 47 T* new_position = extend_front(); |
| 48 *new_position = std::move(elem); |
| 49 } |
| 50 |
| 51 void pop_front() { |
| 52 RTC_DCHECK_GT(size_, 0); |
| 53 if (size_ == 1) { |
| 54 head_ = tail_ = nullptr; |
| 55 size_ = 0; |
| 56 return; |
| 57 } |
| 58 |
| 59 head_++; |
| 60 if (head_ == buffer_end_) |
| 61 head_ = buffer_begin_; |
| 62 size_--; |
| 63 } |
| 64 |
| 65 void pop_back() { |
| 66 RTC_DCHECK_GT(size_, 0); |
| 67 if (size_ == 1) { |
| 68 head_ = tail_ = nullptr; |
| 69 size_ = 0; |
| 70 return; |
| 71 } |
| 72 |
| 73 if (tail_ == buffer_begin_) |
| 74 tail_ = buffer_end_; |
| 75 tail_--; |
| 76 size_--; |
| 77 } |
| 78 |
| 79 T& front() { |
| 80 RTC_DCHECK_GT(size_, 0); |
| 81 return *head_; |
| 82 } |
| 83 |
| 84 const T& front() const { |
| 85 RTC_DCHECK_GT(size_, 0); |
| 86 return *head_; |
| 87 } |
| 88 |
| 89 T& back() { |
| 90 RTC_DCHECK_GT(size_, 0); |
| 91 return *tail_; |
| 92 } |
| 93 |
| 94 const T& back() const { |
| 95 RTC_DCHECK_GT(size_, 0); |
| 96 return *tail_; |
| 97 } |
| 98 |
| 99 size_t size() const { return size_; } |
| 100 |
| 101 size_t capacity() const { return capacity_; } |
| 102 |
| 103 // TODO(terelius): Implement resize() instead? Make it private? |
| 104 void Grow(size_t new_capacity) { |
| 105 RTC_DCHECK_GT(new_capacity, capacity_); |
| 106 T* new_buffer = new T[new_capacity]; |
| 107 T* read_it = head_; |
| 108 T* write_it = new_buffer; |
| 109 if (size_ > 0) { |
| 110 // Copy the contents to the new buffer |
| 111 do { |
| 112 *write_it = *read_it; |
| 113 write_it++; |
| 114 read_it++; |
| 115 if (read_it == buffer_end_) |
| 116 read_it = buffer_begin_; |
| 117 } while (read_it != tail_); |
| 118 } |
| 119 |
| 120 delete[] buffer_begin_; |
| 121 buffer_begin_ = new_buffer; |
| 122 buffer_end_ = buffer_begin_ + new_capacity; |
| 123 capacity_ = new_capacity; |
| 124 head_ = buffer_begin_; |
| 125 tail_ = buffer_begin_ + size_ - 1; |
| 126 } |
| 127 |
| 128 private: |
| 129 T* extend_back(); |
| 130 T* extend_front(); |
| 131 |
| 132 T* buffer_begin_; |
| 133 T* buffer_end_; |
| 134 T* head_; |
| 135 T* tail_; |
| 136 size_t capacity_; |
| 137 size_t size_; |
| 138 }; |
| 139 |
| 140 template <typename T, GrowthPolicy policy> |
| 141 CyclicBuffer<T, policy>::CyclicBuffer(size_t capacity) |
| 142 : buffer_begin_(nullptr), |
| 143 buffer_end_(nullptr), |
| 144 head_(nullptr), |
| 145 tail_(nullptr), |
| 146 capacity_(capacity), |
| 147 size_(0) { |
| 148 RTC_CHECK(capacity > 0); |
| 149 buffer_begin_ = new T[capacity]; |
| 150 buffer_end_ = buffer_begin_ + capacity; |
| 151 } |
| 152 |
| 153 template <typename T, GrowthPolicy policy> |
| 154 CyclicBuffer<T, policy>::~CyclicBuffer() { |
| 155 delete[] buffer_begin_; |
| 156 } |
| 157 |
| 158 // TODO(terelius): Handle capacity 0? |
| 159 // TODO(terelius): Verify correctness for capacity 1. |
| 160 template <typename T, GrowthPolicy policy> |
| 161 T* CyclicBuffer<T, policy>::extend_back() { |
| 162 if (size_ == 0) { |
| 163 head_ = tail_ = buffer_begin_; |
| 164 size_++; |
| 165 return tail_; |
| 166 } |
| 167 |
| 168 // Ensure there is enough space to insert the element. |
| 169 if (policy == ASSERT) { |
| 170 RTC_DCHECK_LT(size_, capacity_); |
| 171 } else if (policy == DISCARD_OLDEST && size_ == capacity_) { |
| 172 pop_front(); // Redundant if we discard oldest element below. |
| 173 } else if (policy == GROW_BUFFER && size_ == capacity_) { |
| 174 Grow(2 * capacity_ + 1); |
| 175 } |
| 176 // Find next place to insert. |
| 177 tail_++; |
| 178 if (tail_ == buffer_end_) |
| 179 tail_ = buffer_begin_; |
| 180 |
| 181 // Discard oldest element if buffer is full. |
| 182 if (tail_ == head_) { |
| 183 head_++; |
| 184 if (head_ == buffer_end_) |
| 185 head_ = buffer_begin_; |
| 186 size_--; |
| 187 } |
| 188 |
| 189 // Insert element. |
| 190 size_++; |
| 191 return tail_; |
| 192 } |
| 193 |
| 194 // TODO(terelius): Handle capacity 0? |
| 195 // TODO(terelius): Verify correctness for capacity 1. |
| 196 template <typename T, GrowthPolicy policy> |
| 197 T* CyclicBuffer<T, policy>::extend_front() { |
| 198 if (size_ == 0) { |
| 199 head_ = tail_ = buffer_begin_; |
| 200 size_++; |
| 201 return tail_; |
| 202 } |
| 203 |
| 204 // Ensure there is enough space to insert the element. |
| 205 if (policy == ASSERT) { |
| 206 RTC_DCHECK_LT(size_, capacity_); |
| 207 } else if (policy == DISCARD_OLDEST && size_ == capacity_) { |
| 208 pop_back(); // Redundant if we discard oldest element below. |
| 209 } else if (policy == GROW_BUFFER && size_ == capacity_) { |
| 210 Grow(2 * capacity_ + 1); |
| 211 } |
| 212 // Find next place to insert. |
| 213 if (head_ == buffer_begin_) |
| 214 head_ = buffer_end_; |
| 215 head_--; |
| 216 |
| 217 // Discard oldest element if buffer is full. |
| 218 if (head_ == tail_) { |
| 219 if (tail_ == buffer_begin_) |
| 220 tail_ = buffer_end_; |
| 221 tail_--; |
| 222 size_--; |
| 223 } |
| 224 |
| 225 // Insert element. |
| 226 size_++; |
| 227 return head_; |
| 228 } |
| 229 |
| 230 } // namespace webrtc |
| 231 |
| 232 #endif // WEBRTC_BASE_NUMERICS_CYCLIC_BUFFER_H_ |
OLD | NEW |