Index: webrtc/base/numerics/cyclic_buffer.h |
diff --git a/webrtc/base/numerics/cyclic_buffer.h b/webrtc/base/numerics/cyclic_buffer.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0f923ed4572424d5b434c917d274048b275d6bc9 |
--- /dev/null |
+++ b/webrtc/base/numerics/cyclic_buffer.h |
@@ -0,0 +1,232 @@ |
+/* |
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
+ * |
+ * Use of this source code is governed by a BSD-style license |
+ * that can be found in the LICENSE file in the root of the source |
+ * tree. An additional intellectual property rights grant can be found |
+ * in the file PATENTS. All contributing project authors may |
+ * be found in the AUTHORS file in the root of the source tree. |
+ */ |
+ |
+#ifndef WEBRTC_BASE_NUMERICS_CYCLIC_BUFFER_H_ |
+#define WEBRTC_BASE_NUMERICS_CYCLIC_BUFFER_H_ |
+ |
+#include <utility> // for move |
+ |
+#include "webrtc/base/checks.h" |
+ |
+namespace webrtc { |
+ |
+enum GrowthPolicy { ASSERT = 0, DISCARD_OLDEST, GROW_BUFFER }; |
+ |
+template <typename T, GrowthPolicy policy> |
+class CyclicBuffer { |
+ public: |
+ explicit CyclicBuffer(size_t size); |
+ ~CyclicBuffer(); |
+ |
+ // TODO(terelius): Check what happens if size_ == capacity_ == 1 |
+ // TODO(terelius): Overload for rvalue references. Refactor to avoid code |
+ // duplication. |
+ void push_back(const T& elem) { |
+ T* new_position = extend_back(); |
+ *new_position = elem; |
+ } |
+ |
+ void push_back(T&& elem) { |
+ T* new_position = extend_back(); |
+ *new_position = std::move(elem); |
+ } |
+ |
+ void push_front(const T& elem) { |
+ T* new_position = extend_front(); |
+ *new_position = elem; |
+ } |
+ |
+ void push_front(T&& elem) { |
+ T* new_position = extend_front(); |
+ *new_position = std::move(elem); |
+ } |
+ |
+ void pop_front() { |
+ RTC_DCHECK_GT(size_, 0); |
+ if (size_ == 1) { |
+ head_ = tail_ = nullptr; |
+ size_ = 0; |
+ return; |
+ } |
+ |
+ head_++; |
+ if (head_ == buffer_end_) |
+ head_ = buffer_begin_; |
+ size_--; |
+ } |
+ |
+ void pop_back() { |
+ RTC_DCHECK_GT(size_, 0); |
+ if (size_ == 1) { |
+ head_ = tail_ = nullptr; |
+ size_ = 0; |
+ return; |
+ } |
+ |
+ if (tail_ == buffer_begin_) |
+ tail_ = buffer_end_; |
+ tail_--; |
+ size_--; |
+ } |
+ |
+ T& front() { |
+ RTC_DCHECK_GT(size_, 0); |
+ return *head_; |
+ } |
+ |
+ const T& front() const { |
+ RTC_DCHECK_GT(size_, 0); |
+ return *head_; |
+ } |
+ |
+ T& back() { |
+ RTC_DCHECK_GT(size_, 0); |
+ return *tail_; |
+ } |
+ |
+ const T& back() const { |
+ RTC_DCHECK_GT(size_, 0); |
+ return *tail_; |
+ } |
+ |
+ size_t size() const { return size_; } |
+ |
+ size_t capacity() const { return capacity_; } |
+ |
+ // TODO(terelius): Implement resize() instead? Make it private? |
+ void Grow(size_t new_capacity) { |
+ RTC_DCHECK_GT(new_capacity, capacity_); |
+ T* new_buffer = new T[new_capacity]; |
+ T* read_it = head_; |
+ T* write_it = new_buffer; |
+ if (size_ > 0) { |
+ // Copy the contents to the new buffer |
+ do { |
+ *write_it = *read_it; |
+ write_it++; |
+ read_it++; |
+ if (read_it == buffer_end_) |
+ read_it = buffer_begin_; |
+ } while (read_it != tail_); |
+ } |
+ |
+ delete[] buffer_begin_; |
+ buffer_begin_ = new_buffer; |
+ buffer_end_ = buffer_begin_ + new_capacity; |
+ capacity_ = new_capacity; |
+ head_ = buffer_begin_; |
+ tail_ = buffer_begin_ + size_ - 1; |
+ } |
+ |
+ private: |
+ T* extend_back(); |
+ T* extend_front(); |
+ |
+ T* buffer_begin_; |
+ T* buffer_end_; |
+ T* head_; |
+ T* tail_; |
+ size_t capacity_; |
+ size_t size_; |
+}; |
+ |
+template <typename T, GrowthPolicy policy> |
+CyclicBuffer<T, policy>::CyclicBuffer(size_t capacity) |
+ : buffer_begin_(nullptr), |
+ buffer_end_(nullptr), |
+ head_(nullptr), |
+ tail_(nullptr), |
+ capacity_(capacity), |
+ size_(0) { |
+ RTC_CHECK(capacity > 0); |
+ buffer_begin_ = new T[capacity]; |
+ buffer_end_ = buffer_begin_ + capacity; |
+} |
+ |
+template <typename T, GrowthPolicy policy> |
+CyclicBuffer<T, policy>::~CyclicBuffer() { |
+ delete[] buffer_begin_; |
+} |
+ |
+// TODO(terelius): Handle capacity 0? |
+// TODO(terelius): Verify correctness for capacity 1. |
+template <typename T, GrowthPolicy policy> |
+T* CyclicBuffer<T, policy>::extend_back() { |
+ if (size_ == 0) { |
+ head_ = tail_ = buffer_begin_; |
+ size_++; |
+ return tail_; |
+ } |
+ |
+ // Ensure there is enough space to insert the element. |
+ if (policy == ASSERT) { |
+ RTC_DCHECK_LT(size_, capacity_); |
+ } else if (policy == DISCARD_OLDEST && size_ == capacity_) { |
+ pop_front(); // Redundant if we discard oldest element below. |
+ } else if (policy == GROW_BUFFER && size_ == capacity_) { |
+ Grow(2 * capacity_ + 1); |
+ } |
+ // Find next place to insert. |
+ tail_++; |
+ if (tail_ == buffer_end_) |
+ tail_ = buffer_begin_; |
+ |
+ // Discard oldest element if buffer is full. |
+ if (tail_ == head_) { |
+ head_++; |
+ if (head_ == buffer_end_) |
+ head_ = buffer_begin_; |
+ size_--; |
+ } |
+ |
+ // Insert element. |
+ size_++; |
+ return tail_; |
+} |
+ |
+// TODO(terelius): Handle capacity 0? |
+// TODO(terelius): Verify correctness for capacity 1. |
+template <typename T, GrowthPolicy policy> |
+T* CyclicBuffer<T, policy>::extend_front() { |
+ if (size_ == 0) { |
+ head_ = tail_ = buffer_begin_; |
+ size_++; |
+ return tail_; |
+ } |
+ |
+ // Ensure there is enough space to insert the element. |
+ if (policy == ASSERT) { |
+ RTC_DCHECK_LT(size_, capacity_); |
+ } else if (policy == DISCARD_OLDEST && size_ == capacity_) { |
+ pop_back(); // Redundant if we discard oldest element below. |
+ } else if (policy == GROW_BUFFER && size_ == capacity_) { |
+ Grow(2 * capacity_ + 1); |
+ } |
+ // Find next place to insert. |
+ if (head_ == buffer_begin_) |
+ head_ = buffer_end_; |
+ head_--; |
+ |
+ // Discard oldest element if buffer is full. |
+ if (head_ == tail_) { |
+ if (tail_ == buffer_begin_) |
+ tail_ = buffer_end_; |
+ tail_--; |
+ size_--; |
+ } |
+ |
+ // Insert element. |
+ size_++; |
+ return head_; |
+} |
+ |
+} // namespace webrtc |
+ |
+#endif // WEBRTC_BASE_NUMERICS_CYCLIC_BUFFER_H_ |