/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include #include #include namespace facebook::react { /** * A container for storing entries of type T, with the following properties: * - It can only grow up to a specified max size * - It's a circular buffer (the oldest elements are dropped if reached max * size and adding a new element) * * Note that the space for maxSize elements is reserved on construction. This * ensures that pointers to elements remain stable across add() operations. */ template class CircularBuffer { public: explicit CircularBuffer(size_t maxSize) : maxSize_(maxSize) { entries_.reserve(maxSize_); } /** * Adds (pushes) element into the buffer. * * Returns the result of the operation, which will depend on whether the * buffer reached the max allowed size, in which case `true` is returned. If * no items were overridden `false` is returned. */ bool add(const T& el) { if (entries_.size() < maxSize_) { // Haven't reached max buffer size yet, just add and grow the buffer entries_.emplace_back(el); return false; } else { // Overwrite the oldest (but already consumed) element in the buffer entries_[position_] = el; position_ = (position_ + 1) % entries_.size(); return true; } } T& operator[](size_t idx) { return entries_[(position_ + idx) % entries_.size()]; } size_t size() const { return entries_.size(); } void clear() { entries_.clear(); position_ = 0; } /** * Clears buffer entries by predicate */ void clear(std::function predicate) { std::vector entries; entries.reserve(maxSize_); for (size_t i = 0; i < entries_.size(); i++) { T& el = entries_[(i + position_) % entries_.size()]; if (predicate(el)) { continue; } entries.push_back(std::move(el)); } position_ = 0; entries.swap(entries_); } /** * Retrieves buffer entries, whether consumed or not */ std::vector getEntries() const { std::vector res; getEntries(res); return res; } /** * Retrieves buffer entries, whether consumed or not, with predicate */ std::vector getEntries(std::function predicate) const { std::vector res; getEntries(res, predicate); return res; } void getEntries(std::vector& res) const { const size_t oldSize = res.size(); res.resize(oldSize + entries_.size()); std::copy( entries_.begin() + position_, entries_.end(), res.begin() + oldSize); std::copy( entries_.begin(), entries_.begin() + position_, res.begin() + oldSize + entries_.size() - position_); } void getEntries(std::vector& res, std::function predicate) const { for (size_t i = 0; i < entries_.size(); i++) { const T& el = entries_[(i + position_) % entries_.size()]; if (predicate(el)) { res.push_back(el); } } } private: std::vector entries_; const size_t maxSize_; // Current starting position in the circular buffer: size_t position_{0}; }; } // namespace facebook::react