Skip to content

Commit

Permalink
Add math utility abd circular buffer
Browse files Browse the repository at this point in the history
  • Loading branch information
GOB52 committed May 27, 2024
1 parent bebde0e commit 20f5db1
Show file tree
Hide file tree
Showing 6 changed files with 675 additions and 14 deletions.
3 changes: 3 additions & 0 deletions lib/M5Utility/src/M5Utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@

#include "m5_utility/log/library_log.hpp"

#include "m5_utility/container/circular_buffer.hpp"

#include "m5_utility/bit_segment.hpp"
#include "m5_utility/compatibility_feature.hpp"
#include "m5_utility/murmurhash3.hpp"
#include "m5_utility/types.hpp"
#include "m5_utility/crc.hpp"
#include "m5_utility/string.hpp"
#include "m5_utility/conversion.hpp"
#include "m5_utility/math.hpp"

/*!
@namespace m5
Expand Down
285 changes: 285 additions & 0 deletions lib/M5Utility/src/m5_utility/container/circular_buffer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
/*!
@file circular_buffer.hpp
@brief Circular buffer (Ring buffer)
SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
SPDX-License-Identifier: MIT
*/
#ifndef M5_UTILITY_CONTAINER_CIRCULAR_BUFFER_HPP
#define M5_UTILITY_CONTAINER_CIRCULAR_BUFFER_HPP

#include <cstddef>
#include <array>
#include <cassert>
#include "../stl//optional.hpp"

namespace m5 {
namespace container {

/*!
@class CircularBuffer
@brief Container called ring buffer or circular buffer
@tparam T Type of the element
@tparam N Capacity
*/
template <typename T, size_t N>
class CircularBuffer {
public:
using value_type = T;
using size_type = std::size_t;
using reference = T&;
using const_reference = const T&;
using return_type = m5::stl::optional<value_type>;

///@name Constructor
///@{
CircularBuffer() = default;
CircularBuffer(size_type n, const_reference value) : CircularBuffer() {
assign(n, value);
}
template <class InputIter>
CircularBuffer(InputIter first, InputIter last) : CircularBuffer() {
assign(first, last);
}
CircularBuffer(std::initializer_list<T> il)
: CircularBuffer(il.begin(), il.end()) {
}

CircularBuffer(const CircularBuffer&) = default;
CircularBuffer(CircularBuffer&&) = default;
///@}

/// @name Assignment
/// @{
/*! @brief Copy */
CircularBuffer& operator=(const CircularBuffer&) = default;
//! @brief Move
CircularBuffer& operator=(CircularBuffer&&) = default;
/*!
@brief Replaces the contents with copies of those in the range [first,
last)
@param first,last The Range to copy the elements from
*/
template <class InputIterator>
void assign(InputIterator first, InputIterator last) {
clear();
size_type sz = last - first;
if (sz > N) {
first += (sz - N);
}
auto n = std::min(N, sz);
while (n--) {
push_back(*first++);
}
}
/*!
@brief assigns values to the container
@param n Number of elements
@param v Value to assign to the elements
@note Fill with the value as many times as n or the capacity
*/
void assign(size_type n, const_reference v) {
clear();
n = std::min(N, n);
while (n--) {
push_back(v);
}
}
/*!
@brief assigns values to the container
@param il Initializer list to copy the values from
*/
inline void assign(std::initializer_list<T> il) {
assign(il.begin(), il.end());
}
/// @}

///@name Element access
///@{
/*!
@brief Access the first element
@return m5::stl::optional<value_type>
*/
inline return_type front() const {
return !empty() ? m5::stl::make_optional(_buf[_tail])
: m5::stl::nullopt;
}
/*!
@brief Access the last element
@return m5::stl::optional<value_type>
*/
inline return_type back() const {
return !empty() ? m5::stl::make_optional(_buf[(_head - 1 + N) % N])
: m5::stl::nullopt;
}
/*!
@brief Access specified element
@return Reference to the requested element
*/
inline const_reference operator[](size_type i) const& {
assert(size() > 0 && "container empty");
assert(i < size() && "index overflow");
return _buf[(_tail + i) % N];
}
/*!
@brief Access specified element with bounds checking
@return m5::stl::optional<value_type>
*/
inline return_type at(size_type i) const {
return (!empty() && i < size())
? m5::stl::make_optional(_buf[(_tail + i) % N])
: m5::stl::nullopt;
}
/*!
@brief Read from buffer
@param[out] outbuf Output buffer
@param num Max elements of output buffer
@return Number of elements read
*/
std::size_t read(value_type* outbuf, const std::size_t num) {
std::size_t sz = std::min(num, size());
if (sz == 0) {
return sz;
}
auto tail = _tail;
auto src = &_buf[tail];
std::size_t elms = std::min(N - tail, sz);

std::copy(src, src + elms, outbuf);
tail = (tail + elms) % N;
std::size_t ret = elms;

if (elms < sz) {
outbuf += elms;
src = &_buf[tail];
elms = sz - elms;

std::copy(src, src + elms, outbuf);
ret += elms;
}
return ret;
}
/// @}

///@name Capacity
///@{
/*!
@brief checks whether the container is empty
@return True if empty
*/
inline bool empty() const {
return !full() && (_head == _tail);
}
/*!
@brief checks whether the container is full
@return True if full
*/
inline bool full() const {
return _full;
}
/*!
@brief returns the number of elements
*/
inline size_type size() const {
return full() ? N
: (_head >= _tail ? _head - _tail : N + _head - _tail);
}
/*!
@brief Returns the number of elements that can be held in currently
storage
*/
inline size_type capacity() const {
return N;
}
///@}

///@name Modifiers
///@{
/*! @brief Clears the contents */
void clear() {
_full = false;
_head = _tail;
}
//! @brief Adds an element to the top
void push_front(const value_type& v) {
_tail = (_tail - 1 + N) % N;
_buf[_tail] = v;
if (_full) {
_head = (_head - 1 + N) % N;
}
_full = (_head == _tail);
}
//! @brief Adds an element to the end
void push_back(const value_type& v) {
_buf[_head] = v;
_head = (_head + 1) % N;
if (_full) {
_tail = (_tail + 1) % N;
}
_full = (_head == _tail);
}
//! @brief removes the top element
inline void pop_front() {
if (!empty()) {
_tail = (_tail + 1) % N;
_full = false;
}
}
//! @brief removes the end element
inline void pop_back() {
if (!empty()) {
_head = (_head - 1 + N) % N;
_full = false;
}
}
///@}

///@name Operations
///@{
/*!
@brief Assigns the value to all elements in the container
@param v Value to assign to the elements
*/
void fill(const value_type& v) {
clear();
_buf.fill(v);
_full = true;
}
/*!
@brief Swaps the contents
@param o Ccontainer to exchange the contents with
*/
void swap(CircularBuffer& o) {
if (this != &o) {
std::swap(_buf, o._buf);
std::swap(_head, o._head);
std::swap(_tail, o._tail);
std::swap(_full, o._full);
}
}
///@}

private:
std::array<T, N> _buf{};
std::size_t _head{}, _tail{};
bool _full{};
};

} // namespace container
} // namespace m5

namespace std {
/*!
@brief Specializes the std::swap algorithm
@related m5::container::CircularBuffer
@param a,b Containers whose contents to swap
*/
template <typename T, size_t N>
inline void swap(m5::container::CircularBuffer<T, N>& a,
m5::container::CircularBuffer<T, N>& b) {
a.swap(b);
}
} // namespace std

#endif
33 changes: 33 additions & 0 deletions lib/M5Utility/src/m5_utility/math.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*!
@file math.hpp
@brief Maths-related
SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
SPDX-License-Identifier: MIT
*/
#ifndef M5_UTILITY_MATH_HPP
#define M5_UTILITY_MATH_HPP

#include <cstdint>
#include <type_traits>

namespace m5 {
namespace math {
template <typename T>

/*!
@brief Is value power of 2?
@tparam Type of the value
@param v Value
@return True if value is power of 2
*/
inline constexpr bool is_powerof2(const T v) {
static_assert(std::is_integral<T>::value,
"The argument v is only an integer value.");
return v > 0 && ((v & (v - 1)) == 0);
}

} // namespace math
} // namespace m5
#endif
Loading

0 comments on commit 20f5db1

Please sign in to comment.