first commit
This commit is contained in:
commit
5893b00dd2
1669 changed files with 1982740 additions and 0 deletions
17
libraries/M5Utility/src/M5Utility.h
Normal file
17
libraries/M5Utility/src/M5Utility.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file M5Utility.h
|
||||
*/
|
||||
#ifndef M5_UTILITY_H
|
||||
#define M5_UTILITY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "M5Utility.hpp"
|
||||
#else
|
||||
#error M5Utility requires a C++ compiler, please change file extension to .cc or .cpp
|
||||
#endif
|
||||
#endif
|
||||
63
libraries/M5Utility/src/M5Utility.hpp
Normal file
63
libraries/M5Utility/src/M5Utility.hpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file M5Utility.hpp
|
||||
@brief Main header of M5Utility
|
||||
|
||||
@mainpage M5Utility
|
||||
This library is a collection of common functionality for other M5Libraries and products.
|
||||
*/
|
||||
#ifndef M5_UTILITY_HPP
|
||||
#define M5_UTILITY_HPP
|
||||
|
||||
#include "m5_utility/stl/expected.hpp"
|
||||
#include "m5_utility/stl/extension.hpp"
|
||||
#include "m5_utility/stl/optional.hpp"
|
||||
#include "m5_utility/stl/endianness.hpp"
|
||||
|
||||
#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"
|
||||
#include "m5_utility/misc.hpp"
|
||||
|
||||
/*!
|
||||
@namespace m5
|
||||
@brief Top level namespace of M5
|
||||
*/
|
||||
namespace m5 {
|
||||
/*!
|
||||
@namespace utility
|
||||
@brief For utilities
|
||||
*/
|
||||
namespace utility {
|
||||
}
|
||||
|
||||
/*!
|
||||
@namespace stl
|
||||
@brief STL compatibility functions and classes
|
||||
*/
|
||||
namespace stl {
|
||||
}
|
||||
|
||||
/*!
|
||||
@namespace container
|
||||
@brief Container classes
|
||||
*/
|
||||
namespace container {
|
||||
}
|
||||
|
||||
} // namespace m5
|
||||
|
||||
#endif
|
||||
237
libraries/M5Utility/src/m5_utility/bit_segment.hpp
Normal file
237
libraries/M5Utility/src/m5_utility/bit_segment.hpp
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file bit_segment.hpp
|
||||
@brief A class for separating the bits of an integer variable and giving
|
||||
meaning to each
|
||||
*/
|
||||
#ifndef M5_UTILITY_BIT_SEGMENT_HPP
|
||||
#define M5_UTILITY_BIT_SEGMENT_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
|
||||
/*!
|
||||
@class BitSegment
|
||||
@tparam LowerBits How many lower bits are used as a lower segment.
|
||||
@tparam T Integer type used as base class
|
||||
@note If the base class is signed, the upper bits excluding the sign bit are
|
||||
used
|
||||
*/
|
||||
template <size_t LowerBits, typename T>
|
||||
class BitSegment {
|
||||
public:
|
||||
///@cond
|
||||
using base_type = typename std::remove_const<typename std::remove_reference<T>::type>::type;
|
||||
constexpr static bool SIGNED = std::is_signed<base_type>::value;
|
||||
static_assert(std::is_integral<base_type>::value, "Base type must be integral");
|
||||
static_assert(LowerBits > 0, "LowerBits must be not zero");
|
||||
static_assert(LowerBits <= (sizeof(base_type) * 8 - (SIGNED ? 1 : 0)), "LowerBits too large");
|
||||
using unsigned_type = typename std::make_unsigned<base_type>::type;
|
||||
constexpr static unsigned_type UPPER_BITS = sizeof(unsigned_type) * 8U - LowerBits - (SIGNED ? 1 : 0);
|
||||
constexpr static unsigned_type LOWER_BITS = static_cast<unsigned_type>(LowerBits);
|
||||
constexpr static unsigned_type UPPER_SHIFT = LOWER_BITS;
|
||||
constexpr static unsigned_type UPPER_MASK = ((unsigned_type)1 << UPPER_BITS) - 1;
|
||||
constexpr static unsigned_type LOWER_MASK = ((unsigned_type)1 << LOWER_BITS) - 1;
|
||||
///@endcond
|
||||
|
||||
///@name Constructor
|
||||
///@{
|
||||
inline constexpr BitSegment() = default; //!< @brief default
|
||||
//! @brief Copy
|
||||
inline constexpr BitSegment(const BitSegment& o) : _v(o._v)
|
||||
{
|
||||
}
|
||||
//! @brief Implicit conversion
|
||||
inline constexpr BitSegment(const base_type v) : _v(v)
|
||||
{
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Assignment
|
||||
///@{
|
||||
BitSegment& operator=(const BitSegment& o)
|
||||
{
|
||||
if (this != &o) {
|
||||
_v = o._v;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BitSegment& operator=(const base_type v)
|
||||
{
|
||||
_v = v;
|
||||
return *this;
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Cast
|
||||
///@{
|
||||
/*! @brief Cast to boolean */
|
||||
inline constexpr explicit operator bool() const
|
||||
{
|
||||
return _v;
|
||||
}
|
||||
//! @brief Cast to base_type (Implicit conversion)
|
||||
inline constexpr operator base_type() const
|
||||
{
|
||||
return _v;
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Getter
|
||||
///@{
|
||||
/*! @brief Gets the value of upper segment */
|
||||
inline constexpr unsigned_type upper() const
|
||||
{
|
||||
return (_v >> UPPER_SHIFT) & UPPER_MASK;
|
||||
}
|
||||
//! @brief Gets the value of lower segment
|
||||
inline constexpr unsigned_type lower() const
|
||||
{
|
||||
return _v & LOWER_MASK;
|
||||
}
|
||||
//! @brief Gets the raw value
|
||||
inline constexpr base_type raw() const
|
||||
{
|
||||
return _v;
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Setter
|
||||
///@{
|
||||
/*! @brief Set the value of upper segment */
|
||||
inline void upper(const unsigned_type v)
|
||||
{
|
||||
_v = (_v & ~(UPPER_MASK << UPPER_SHIFT)) | ((v & UPPER_MASK) << UPPER_SHIFT);
|
||||
}
|
||||
//! @brief Set the value of lower segment
|
||||
inline void lower(const unsigned_type v)
|
||||
{
|
||||
_v = (_v & ~LOWER_MASK) | (v & LOWER_MASK);
|
||||
}
|
||||
//! @brief Set the raw value
|
||||
inline void raw(const base_type v)
|
||||
{
|
||||
_v = v;
|
||||
}
|
||||
///@}
|
||||
|
||||
private:
|
||||
base_type _v{};
|
||||
};
|
||||
|
||||
///@name Compare between same types.
|
||||
/// @related m5::utility::BitSegment
|
||||
///@{
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator==(const BitSegment<LowerBits, T>& a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return a.raw() == b.raw();
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator!=(const BitSegment<LowerBits, T>& a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator<(const BitSegment<LowerBits, T>& a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return a.raw() < b.raw();
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator>(const BitSegment<LowerBits, T>& a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator<=(const BitSegment<LowerBits, T>& a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return !(a > b);
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator>=(const BitSegment<LowerBits, T>& a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Compare between BitSegment and integer
|
||||
/// @related m5::utility::BitSegment
|
||||
///@{
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator==(const BitSegment<LowerBits, T>& a, const int b)
|
||||
{
|
||||
return a.raw() == b;
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator!=(const BitSegment<LowerBits, T>& a, const int b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator<(const BitSegment<LowerBits, T>& a, const int b)
|
||||
{
|
||||
return a.raw() < b;
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator>(const BitSegment<LowerBits, T>& a, const int b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator<=(const BitSegment<LowerBits, T>& a, const int b)
|
||||
{
|
||||
return !(a > b);
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator>=(const BitSegment<LowerBits, T>& a, const int b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Compare between integer and BitSegment
|
||||
/// @related m5::utility::BitSegment
|
||||
///@{
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator==(const int a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return a == b.raw();
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator!=(const int a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator<(const int a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return a < b.raw();
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator>(const int a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator<=(const int a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return !(a > b);
|
||||
}
|
||||
template <size_t LowerBits, typename T>
|
||||
bool operator>=(const int a, const BitSegment<LowerBits, T>& b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
///@}
|
||||
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
#endif
|
||||
53
libraries/M5Utility/src/m5_utility/compatibility_feature.cpp
Normal file
53
libraries/M5Utility/src/m5_utility/compatibility_feature.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file compatibility_feature.cpp
|
||||
@brief Maintain compatibility with Arduino API, etc.
|
||||
*/
|
||||
#include "compatibility_feature.hpp"
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace {
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
const clock::time_point start_at = clock::now();
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
|
||||
unsigned long millis()
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - ::start_at).count();
|
||||
}
|
||||
unsigned long micros()
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - ::start_at).count();
|
||||
}
|
||||
void delay(const unsigned long ms)
|
||||
{
|
||||
#if 0
|
||||
auto abst = clock::now() + std::chrono::milliseconds(ms);
|
||||
std::this_thread::sleep_until(abst);
|
||||
#else
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||
#endif
|
||||
}
|
||||
|
||||
void delayMicroseconds(const unsigned int us)
|
||||
{
|
||||
#if 0
|
||||
auto abst = clock::now() + std::chrono::microseconds(us);
|
||||
std::this_thread::sleep_until(abst);
|
||||
#else
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(us));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
44
libraries/M5Utility/src/m5_utility/compatibility_feature.hpp
Normal file
44
libraries/M5Utility/src/m5_utility/compatibility_feature.hpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file compatibility_feature.hpp
|
||||
@brief Maintain compatibility with Arduino API, etc.
|
||||
*/
|
||||
#ifndef M5_UTILITY_COMPATIBILITY_FEATURE_HPP
|
||||
#define M5_UTILITY_COMPATIBILITY_FEATURE_HPP
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
|
||||
///@name Arduino API
|
||||
///@{
|
||||
/*!
|
||||
@brief Returns the number of milliseconds passed since the Arduino board began
|
||||
running the current program
|
||||
*/
|
||||
unsigned long millis();
|
||||
/*!
|
||||
@brief Returns the number of microseconds since the Arduino board began
|
||||
running the current program
|
||||
*/
|
||||
unsigned long micros();
|
||||
/*!
|
||||
@brief Pauses the program for the amount of time (in milliseconds) specified
|
||||
as parameter.
|
||||
@warning Accuracy varies depending on the environment.
|
||||
*/
|
||||
void delay(const unsigned long ms);
|
||||
/*!
|
||||
@brief Pauses the program for the amount of time (in microseconds) specified
|
||||
by the parameter.
|
||||
@warning Accuracy varies depending on the environment.
|
||||
*/
|
||||
void delayMicroseconds(const unsigned int us);
|
||||
///@}
|
||||
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
#endif
|
||||
537
libraries/M5Utility/src/m5_utility/container/circular_buffer.hpp
Normal file
537
libraries/M5Utility/src/m5_utility/container/circular_buffer.hpp
Normal file
|
|
@ -0,0 +1,537 @@
|
|||
/*
|
||||
* Spdx-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file circular_buffer.hpp
|
||||
@brief Circular buffer with STL-like interface
|
||||
*/
|
||||
#ifndef M5_UTILITY_CONTAINER_CIRCULAR_BUFFER_HPP
|
||||
#define M5_UTILITY_CONTAINER_CIRCULAR_BUFFER_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
#include <cassert>
|
||||
#if __cplusplus >= 201703L
|
||||
#pragma message "Using std::optional"
|
||||
#include <optional>
|
||||
#else
|
||||
#pragma message "Using m5::stl::optional"
|
||||
#include "../stl/optional.hpp"
|
||||
#endif
|
||||
|
||||
namespace m5 {
|
||||
namespace container {
|
||||
|
||||
/*!
|
||||
@class CircularBuffer
|
||||
@brief Type CircularBuffer giving size in constructor
|
||||
@tparam T Type of the element
|
||||
*/
|
||||
template <typename T>
|
||||
class CircularBuffer {
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
#if __cplusplus >= 201703L
|
||||
using return_type = std::optional<value_type>;
|
||||
#else
|
||||
using return_type = m5::stl::optional<value_type>;
|
||||
#endif
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
///@name Constructor
|
||||
///@{
|
||||
CircularBuffer() = delete;
|
||||
explicit CircularBuffer(const size_t n)
|
||||
{
|
||||
assert(n != 0 && "Illegal size");
|
||||
_cap = n;
|
||||
_buf.resize(n);
|
||||
}
|
||||
CircularBuffer(const size_type n, const_reference value) : CircularBuffer(n)
|
||||
{
|
||||
assign(n, value);
|
||||
}
|
||||
template <class InputIter>
|
||||
CircularBuffer(const size_type n, InputIter first, InputIter last) : CircularBuffer(n)
|
||||
{
|
||||
assign(first, last);
|
||||
}
|
||||
CircularBuffer(const size_type n, std::initializer_list<T> il) : CircularBuffer(n, il.begin(), il.end())
|
||||
{
|
||||
}
|
||||
|
||||
CircularBuffer(const CircularBuffer&) = default;
|
||||
|
||||
CircularBuffer(CircularBuffer&&) noexcept = 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 > _cap) {
|
||||
first += (sz - _cap);
|
||||
}
|
||||
auto n = std::min(_cap, 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(_cap, n);
|
||||
while (n--) {
|
||||
push_back(v);
|
||||
}
|
||||
}
|
||||
/*!
|
||||
@brief assigns values to the container
|
||||
@param il Initializer list from which the copy is made
|
||||
*/
|
||||
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
|
||||
{
|
||||
#if __cplusplus >= 201703L
|
||||
return !empty() ? std::make_optional(_buf[_tail]) : std::nullopt;
|
||||
#else
|
||||
return !empty() ? m5::stl::make_optional(_buf[_tail]) : m5::stl::nullopt;
|
||||
#endif
|
||||
}
|
||||
/*!
|
||||
@brief Access the last element
|
||||
@return m5::stl::optional<value_type>
|
||||
*/
|
||||
inline return_type back() const
|
||||
{
|
||||
#if __cplusplus >= 201703L
|
||||
return !empty() ? std::make_optional(_buf[(_head - 1 + _cap) % _cap]) : std::nullopt;
|
||||
#else
|
||||
return !empty() ? m5::stl::make_optional(_buf[(_head - 1 + _cap) % _cap]) : m5::stl::nullopt;
|
||||
#endif
|
||||
}
|
||||
/*!
|
||||
@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) % _cap];
|
||||
}
|
||||
/*!
|
||||
@brief Access specified element with bounds checking
|
||||
@return m5::stl::optional<value_type>
|
||||
*/
|
||||
inline return_type at(size_type i) const
|
||||
{
|
||||
#if __cplusplus >= 201703L
|
||||
return (!empty() && i < size()) ? std::make_optional(_buf[(_tail + i) % _cap]) : std::nullopt;
|
||||
#else
|
||||
return (!empty() && i < size()) ? m5::stl::make_optional(_buf[(_tail + i) % _cap]) : m5::stl::nullopt;
|
||||
#endif
|
||||
}
|
||||
/*!
|
||||
@brief Read from buffer
|
||||
@param[out] outbuf Output buffer
|
||||
@param num Max elements of output buffer
|
||||
@return Number of elements read
|
||||
*/
|
||||
size_t read(value_type* outbuf, const size_t num)
|
||||
{
|
||||
size_t sz = std::min(num, size());
|
||||
if (sz == 0) {
|
||||
return sz;
|
||||
}
|
||||
auto tail = _tail;
|
||||
auto src = &_buf[tail];
|
||||
size_t elms = std::min(_cap - tail, sz);
|
||||
|
||||
std::copy(src, src + elms, outbuf);
|
||||
tail = (tail + elms) % _cap;
|
||||
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() ? _cap : (_head >= _tail ? _head - _tail : _cap + _head - _tail);
|
||||
}
|
||||
/*!
|
||||
@brief Returns the number of elements that can be held in currently
|
||||
storage
|
||||
*/
|
||||
inline size_type capacity() const
|
||||
{
|
||||
return _cap;
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Modifiers
|
||||
///@{
|
||||
/*! @brief Clears the contents */
|
||||
void clear()
|
||||
{
|
||||
_full = false;
|
||||
_head = _tail = 0U;
|
||||
}
|
||||
//! @brief Adds an element to the top
|
||||
void push_front(const value_type& v)
|
||||
{
|
||||
_tail = (_tail - 1 + _cap) % _cap;
|
||||
_buf[_tail] = v;
|
||||
if (_full) {
|
||||
_head = (_head - 1 + _cap) % _cap;
|
||||
}
|
||||
_full = (_head == _tail);
|
||||
}
|
||||
//! @brief Adds an element to the end
|
||||
void push_back(const value_type& v)
|
||||
{
|
||||
_buf[_head] = v;
|
||||
_head = (_head + 1) % _cap;
|
||||
if (_full) {
|
||||
_tail = (_tail + 1) % _cap;
|
||||
}
|
||||
_full = (_head == _tail);
|
||||
}
|
||||
//! @brief removes the top element
|
||||
inline void pop_front()
|
||||
{
|
||||
if (!empty()) {
|
||||
_tail = (_tail + 1) % _cap;
|
||||
_full = false;
|
||||
}
|
||||
}
|
||||
//! @brief removes the end element
|
||||
inline void pop_back()
|
||||
{
|
||||
if (!empty()) {
|
||||
_head = (_head - 1 + _cap) % _cap;
|
||||
_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();
|
||||
std::fill(_buf.begin(), _buf.end(), 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(_cap, o._cap);
|
||||
std::swap(_head, o._head);
|
||||
std::swap(_tail, o._tail);
|
||||
std::swap(_full, o._full);
|
||||
}
|
||||
}
|
||||
///@}
|
||||
|
||||
///@cond
|
||||
class iterator {
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = CircularBuffer::value_type;
|
||||
using pointer = CircularBuffer::value_type*;
|
||||
using reference = CircularBuffer::reference;
|
||||
|
||||
iterator() : _buffer(nullptr), _pos(0)
|
||||
{
|
||||
}
|
||||
iterator(CircularBuffer* buf, size_t pos) : _buffer(buf), _pos(pos)
|
||||
{
|
||||
}
|
||||
|
||||
inline reference operator*() const
|
||||
{
|
||||
return _buffer->_buf[_pos % _buffer->capacity()];
|
||||
}
|
||||
inline pointer operator->() const
|
||||
{
|
||||
return &(_buffer->_buf[_pos % _buffer->capacity()]);
|
||||
}
|
||||
inline iterator& operator++()
|
||||
{
|
||||
++_pos;
|
||||
return *this;
|
||||
}
|
||||
inline iterator& operator--()
|
||||
{
|
||||
--_pos;
|
||||
return *this;
|
||||
}
|
||||
inline iterator operator++(int)
|
||||
{
|
||||
iterator tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
inline iterator operator--(int)
|
||||
{
|
||||
iterator tmp = *this;
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend inline bool operator==(const iterator& a, const iterator& b)
|
||||
{
|
||||
return a._buffer == b._buffer && a._pos == b._pos;
|
||||
}
|
||||
friend inline bool operator!=(const iterator& a, const iterator& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
private:
|
||||
CircularBuffer* _buffer;
|
||||
size_t _pos;
|
||||
};
|
||||
|
||||
class const_iterator {
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = CircularBuffer::value_type;
|
||||
using pointer = const CircularBuffer::value_type*;
|
||||
using reference = CircularBuffer::const_reference;
|
||||
|
||||
const_iterator() : _buffer(nullptr), _pos(0)
|
||||
{
|
||||
}
|
||||
const_iterator(const CircularBuffer* buf, size_t pos) : _buffer(buf), _pos(pos)
|
||||
{
|
||||
}
|
||||
|
||||
inline reference operator*() const
|
||||
{
|
||||
return _buffer->_buf[_pos % _buffer->capacity()];
|
||||
}
|
||||
inline pointer operator->() const
|
||||
{
|
||||
return &(_buffer->_buf[_pos % _buffer->capacity()]);
|
||||
}
|
||||
inline const_iterator& operator++()
|
||||
{
|
||||
++_pos;
|
||||
return *this;
|
||||
}
|
||||
inline const_iterator& operator--()
|
||||
{
|
||||
--_pos;
|
||||
return *this;
|
||||
}
|
||||
inline const_iterator operator++(int)
|
||||
{
|
||||
const_iterator tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
inline const_iterator operator--(int)
|
||||
{
|
||||
const_iterator tmp = *this;
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend inline bool operator==(const const_iterator& a, const const_iterator& b)
|
||||
{
|
||||
return a._buffer == b._buffer && a._pos == b._pos;
|
||||
}
|
||||
friend inline bool operator!=(const const_iterator& a, const const_iterator& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
private:
|
||||
const CircularBuffer* _buffer;
|
||||
size_t _pos;
|
||||
};
|
||||
///@endcond
|
||||
|
||||
///@note Iterator is bidirectional
|
||||
/// @name Iterator
|
||||
/// @{
|
||||
inline iterator begin() noexcept
|
||||
{
|
||||
return iterator(this, _tail);
|
||||
}
|
||||
inline iterator end() noexcept
|
||||
{
|
||||
return iterator(this, _tail + size());
|
||||
}
|
||||
inline const_iterator cbegin() const noexcept
|
||||
{
|
||||
return const_iterator(this, _tail);
|
||||
}
|
||||
inline const_iterator cend() const noexcept
|
||||
{
|
||||
return const_iterator(this, _tail + size());
|
||||
}
|
||||
inline reverse_iterator rbegin() noexcept
|
||||
{
|
||||
return std::reverse_iterator<iterator>(end());
|
||||
}
|
||||
inline reverse_iterator rend() noexcept
|
||||
{
|
||||
return std::reverse_iterator<iterator>(begin());
|
||||
}
|
||||
inline const_reverse_iterator crbegin() const noexcept
|
||||
{
|
||||
return std::reverse_iterator<const_iterator>(cend());
|
||||
}
|
||||
inline const_reverse_iterator crend() const noexcept
|
||||
{
|
||||
return std::reverse_iterator<const_iterator>(cbegin());
|
||||
}
|
||||
/// @}
|
||||
|
||||
private:
|
||||
std::vector<T> _buf{};
|
||||
size_t _cap{}, _head{}, _tail{};
|
||||
bool _full{};
|
||||
};
|
||||
|
||||
/*!
|
||||
@class FixedCircularBuffer
|
||||
@brief Type CircularBuffer giving size in template parameter
|
||||
@tparam T Type of the element
|
||||
@tpatam N Capacity of the buffer
|
||||
*/
|
||||
template <typename T, size_t N>
|
||||
class FixedCircularBuffer : public CircularBuffer<T> {
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
#if __cplusplus >= 201703L
|
||||
using return_type = std::optional<value_type>;
|
||||
#else
|
||||
using return_type = m5::stl::optional<value_type>;
|
||||
#endif
|
||||
|
||||
FixedCircularBuffer() : CircularBuffer<T>(N)
|
||||
{
|
||||
}
|
||||
FixedCircularBuffer(const size_type n, const_reference value) : CircularBuffer<T>(N)
|
||||
{
|
||||
CircularBuffer<T>::assign(n, value);
|
||||
}
|
||||
template <class InputIter>
|
||||
FixedCircularBuffer(InputIter first, InputIter last) : CircularBuffer<T>(N, first, last)
|
||||
{
|
||||
}
|
||||
FixedCircularBuffer(std::initializer_list<T> il) : CircularBuffer<T>(N, il)
|
||||
{
|
||||
}
|
||||
|
||||
FixedCircularBuffer(const FixedCircularBuffer&) = default;
|
||||
FixedCircularBuffer(FixedCircularBuffer&&) = default;
|
||||
|
||||
FixedCircularBuffer& operator=(const FixedCircularBuffer&) = default;
|
||||
|
||||
FixedCircularBuffer& operator=(FixedCircularBuffer&&) noexcept = default;
|
||||
};
|
||||
|
||||
} // 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>
|
||||
inline void swap(m5::container::CircularBuffer<T>& a, m5::container::CircularBuffer<T>& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
44
libraries/M5Utility/src/m5_utility/conversion.hpp
Normal file
44
libraries/M5Utility/src/m5_utility/conversion.hpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file conversion.hpp
|
||||
@brief Numeric conversion
|
||||
*/
|
||||
#ifndef M5_UTILITY_CONVERSION_HPP
|
||||
#define M5_UTILITY_CONVERSION_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
|
||||
/*!
|
||||
@brief Convert an unsigned integer of any maximum number of bits to a signed
|
||||
integer
|
||||
@tparam Bits Number of bits assumed by value
|
||||
@code {.cpp}
|
||||
uint32_t u24{0x00FFFFFF};
|
||||
// 24 bit unsigned int to int32_t
|
||||
uint32_t s32 = unsigned_to_signed<24>(u24);
|
||||
// s32 is -1 (Not 16777215)
|
||||
@endcode
|
||||
*/
|
||||
template <size_t Bits, typename T>
|
||||
constexpr auto unsigned_to_signed(const T v) -> typename std::make_signed<T>::type
|
||||
{
|
||||
static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value, "T must be an unsigned integer");
|
||||
static_assert(Bits <= sizeof(T) * 8, "Bits must be less than or equal to the number of bits in T");
|
||||
|
||||
using S = typename std::make_signed<T>::type;
|
||||
return static_cast<S>((v & (1ULL << (Bits - 1))) ? (v & ((1ULL << Bits) - 1)) - (1ULL << Bits)
|
||||
: (v & ((1ULL << Bits) - 1)));
|
||||
}
|
||||
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
#endif
|
||||
220
libraries/M5Utility/src/m5_utility/crc.hpp
Normal file
220
libraries/M5Utility/src/m5_utility/crc.hpp
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file crc.hpp
|
||||
@brief Calculate CRC
|
||||
*/
|
||||
#ifndef M5_UTILITY_CRC_HPP
|
||||
#define M5_UTILITY_CRC_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "misc.hpp"
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
|
||||
/*!
|
||||
@class CRC8
|
||||
@brief Base class of the CRC8 calculator
|
||||
*/
|
||||
class CRC8 {
|
||||
public:
|
||||
CRC8() = delete;
|
||||
/*!
|
||||
@param init Initial value
|
||||
@param polynormal Generated polynomial
|
||||
@param refIn Inverted input?
|
||||
@param refOut Inverted output?
|
||||
@param xorout Exclusive OR output
|
||||
*/
|
||||
CRC8(const uint8_t init, const uint8_t polynomial, const bool refIn, const bool refOut, const uint8_t xorout)
|
||||
: _crc{init}, _init(init), _polynomial{polynomial}, _xorout{xorout}, _refIn{refIn}, _refOut{refOut}
|
||||
{
|
||||
}
|
||||
/*!
|
||||
@brief Calculate the CRC of the specified range
|
||||
@param data Pointer of the array
|
||||
@param len Length of the array
|
||||
@return CRC value
|
||||
*/
|
||||
inline uint8_t range(const uint8_t* data, size_t len)
|
||||
{
|
||||
auto crc = calculate(data, len, _init, _polynomial, _refIn, _refOut, _xorout, false);
|
||||
return finalize(crc, _refOut, _xorout);
|
||||
}
|
||||
/*!
|
||||
@brief Stores the CRC of the specified array using the current internal
|
||||
information
|
||||
@param data Pointer of the array
|
||||
@param len Length of the array
|
||||
@return CRC value
|
||||
@note Used when you want to calculate the value of the entire divided
|
||||
continuous data, such as streaming data
|
||||
*/
|
||||
inline uint8_t update(const uint8_t* data, size_t len)
|
||||
{
|
||||
_crc = calculate(data, len, _crc, _polynomial, _refIn, _refOut, _xorout, false);
|
||||
return finalize(_crc, _refOut, _xorout);
|
||||
}
|
||||
/*!
|
||||
@brief CRC value at the time of the call
|
||||
@return CRC value
|
||||
*/
|
||||
inline uint8_t value() const
|
||||
{
|
||||
return finalize(_crc, _refOut, _xorout);
|
||||
}
|
||||
/*!
|
||||
@brief Calculate CRC8
|
||||
@param data Pointer of the array
|
||||
@param len Length of the array
|
||||
@param polynormal Generated polynomial
|
||||
@param refIn Inverted input?
|
||||
@param refOut Inverted output?
|
||||
@param xorout Exclusive OR output
|
||||
@param do_finalize Apply processing to output values?(true as defaut)
|
||||
@return CRC value
|
||||
*/
|
||||
static uint8_t calculate(const uint8_t* data, size_t len, const uint8_t init, const uint8_t polynomial,
|
||||
const bool refIn, const bool refOut, const uint8_t xorout, bool do_finalize = true)
|
||||
{
|
||||
uint8_t crc{init};
|
||||
while (len--) {
|
||||
uint8_t e = refIn ? reverseBitOrder(*data) : *data;
|
||||
++data;
|
||||
crc ^= e;
|
||||
uint_fast8_t cnt{8};
|
||||
while (cnt--) {
|
||||
if (crc & 0x80) {
|
||||
crc = (crc << 1) ^ polynomial;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return do_finalize ? finalize(crc, refOut, xorout) : crc;
|
||||
}
|
||||
|
||||
protected:
|
||||
static inline uint8_t finalize(const uint8_t value, const bool refOut, const uint8_t xorout)
|
||||
{
|
||||
return (refOut ? reverseBitOrder(value) : value) ^ xorout;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t _crc{}, _init{}, _polynomial{}, _xorout{};
|
||||
bool _refIn{}, _refOut{};
|
||||
};
|
||||
|
||||
/*!
|
||||
@class CRC16
|
||||
@brief Base class of the CRC16 calculator
|
||||
*/
|
||||
class CRC16 {
|
||||
public:
|
||||
CRC16() = delete;
|
||||
/*!
|
||||
@param init Initial value
|
||||
@param polynormal Generated polynomial
|
||||
@param refIn Inverted input?
|
||||
@param refOut Inverted output?
|
||||
@param xorout Exclusive OR output
|
||||
*/
|
||||
CRC16(const uint16_t init, const uint16_t polynomial, const bool refIn, const bool refOut, const uint16_t xorout)
|
||||
: _crc{init}, _init{init}, _polynomial{polynomial}, _xorout{xorout}, _refIn{refIn}, _refOut{refOut}
|
||||
{
|
||||
}
|
||||
/*!
|
||||
@brief Calculate the CRC of the specified range
|
||||
@param data Pointer of the array
|
||||
@param len Length of the array
|
||||
@return CRC value
|
||||
*/
|
||||
inline uint16_t range(const uint8_t* data, size_t len)
|
||||
{
|
||||
auto crc = calculate(data, len, _init, _polynomial, _refIn, _refOut, _xorout, false);
|
||||
return finalize(crc, _refOut, _xorout);
|
||||
}
|
||||
/*!
|
||||
@brief Stores the CRC of the specified array using the current internal
|
||||
information
|
||||
@param data Pointer of the array
|
||||
@param len Length of the array
|
||||
@return CRC value
|
||||
@note Used when you want to calculate the value of the entire divided
|
||||
continuous data, such as streaming data
|
||||
*/
|
||||
inline uint16_t update(const uint8_t* data, size_t len)
|
||||
{
|
||||
_crc = calculate(data, len, _crc, _polynomial, _refIn, _refOut, _xorout, false);
|
||||
return finalize(_crc, _refOut, _xorout);
|
||||
}
|
||||
/*!
|
||||
@brief CRC value at the time of the call
|
||||
@return CRC value
|
||||
*/
|
||||
inline uint16_t value() const
|
||||
{
|
||||
return finalize(_crc, _refOut, _xorout);
|
||||
}
|
||||
/*!
|
||||
@brief Calculate CRC16
|
||||
@param data Pointer of the array
|
||||
@param len Length of the array
|
||||
@param polynormal Generated polynomial
|
||||
@param refIn Inverted input?
|
||||
@param refOut Inverted output?
|
||||
@param xorout Exclusive OR output
|
||||
@param do_finalize Apply processing to output values?(true as defaut)
|
||||
@return CRC value
|
||||
*/
|
||||
static uint16_t calculate(const uint8_t* data, size_t len, const uint16_t init, const uint16_t polynomial,
|
||||
const bool refIn, const bool refOut, const uint16_t xorout, bool do_finalize = true)
|
||||
{
|
||||
uint16_t crc{init};
|
||||
while (len--) {
|
||||
uint8_t e{refIn ? reverseBitOrder(*data) : *data};
|
||||
++data;
|
||||
crc ^= (e << 8);
|
||||
uint_fast8_t cnt{8};
|
||||
while (cnt--) {
|
||||
if (crc & 0x8000) {
|
||||
crc = (crc << 1) ^ polynomial;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return do_finalize ? finalize(crc, refOut, xorout) : crc;
|
||||
}
|
||||
|
||||
protected:
|
||||
static inline uint16_t finalize(const uint16_t value, const bool refOut, const uint16_t xorout)
|
||||
{
|
||||
return (refOut ? reverseBitOrder(value) : value) ^ xorout;
|
||||
}
|
||||
|
||||
private:
|
||||
uint16_t _crc{}, _init{}, _polynomial{}, _xorout{};
|
||||
bool _refIn{}, _refOut{};
|
||||
};
|
||||
|
||||
/*!
|
||||
@class CRC8_CheckSum
|
||||
@brief Typical CRC8 calculator used for read and write data with the chip
|
||||
*/
|
||||
class CRC8_Checksum : public CRC8 {
|
||||
public:
|
||||
CRC8_Checksum() : CRC8(0xFF, 0x31, false, false, 0x00)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
|
||||
#endif
|
||||
110
libraries/M5Utility/src/m5_utility/log/library_log.cpp
Normal file
110
libraries/M5Utility/src/m5_utility/log/library_log.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file library_log.cpp
|
||||
@brief Logging for libraries
|
||||
*/
|
||||
#include "library_log.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <inttypes.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
namespace {
|
||||
using clock = std::chrono::steady_clock;
|
||||
// using clock = std::chrono::high_resolution_clock;
|
||||
const clock::time_point start_at = clock::now();
|
||||
} // namespace
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
namespace log {
|
||||
|
||||
void logPrintf(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vprintf(format, ap);
|
||||
va_end(ap);
|
||||
#if !defined(ARDUINO)
|
||||
fflush(stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dump(const void* iaddr, const size_t len, const bool align)
|
||||
{
|
||||
constexpr static char hc[] = "0123456789ABCDEF";
|
||||
uint8_t* addr = (uint8_t*)iaddr;
|
||||
uint8_t abyte = align ? 0x0F : 0x00;
|
||||
|
||||
uint_fast8_t skip_left{(uint_fast8_t)((uintptr_t)addr & abyte)};
|
||||
|
||||
char hex[128]{};
|
||||
|
||||
printf("DUMP:0x%08" PRIxPTR " %zu bytes\n", (uintptr_t)addr, len);
|
||||
|
||||
// First line
|
||||
size_t i{}, ia{};
|
||||
uint_fast8_t cols = std::min(len - i, (size_t)16 - skip_left);
|
||||
uint8_t left = snprintf(hex, sizeof(hex), "0x%08" PRIxPTR "| ", (uintptr_t)addr & ~abyte);
|
||||
|
||||
for (uint_fast8_t s = 0; s < skip_left; ++s) {
|
||||
hex[left++] = ' ';
|
||||
hex[left++] = ' ';
|
||||
hex[left++] = ' ';
|
||||
}
|
||||
for (uint_fast8_t c = 0; c < cols; ++c) {
|
||||
left += snprintf(hex + left, 4, "%c%c ", hc[(addr[i] >> 4) & 0x0F], hc[addr[i] & 0x0F]);
|
||||
++i;
|
||||
}
|
||||
for (uint_fast8_t s = skip_left; s < 16U - cols; ++s) {
|
||||
hex[left++] = ' ';
|
||||
hex[left++] = ' ';
|
||||
hex[left++] = ' ';
|
||||
}
|
||||
|
||||
hex[left++] = '|';
|
||||
for (uint_fast8_t s = 0; s < skip_left; ++s) {
|
||||
hex[left++] = ' ';
|
||||
}
|
||||
for (uint_fast8_t c = 0; c < cols; ++c) {
|
||||
left += snprintf(hex + left, 2, "%c", std::isprint(addr[ia]) ? (char)addr[ia] : '.');
|
||||
++ia;
|
||||
}
|
||||
puts(hex);
|
||||
|
||||
// Second line~
|
||||
while (i < len) {
|
||||
cols = std::min(len - i, (size_t)16U);
|
||||
left = snprintf(hex, sizeof(hex), "0x%08" PRIxPTR "| ", (uintptr_t)(addr + i) & ~abyte);
|
||||
for (uint_fast8_t c = 0; c < cols; ++c) {
|
||||
left += snprintf(hex + left, 4, "%c%c ", hc[(addr[i] >> 4) & 0x0F], hc[addr[i] & 0x0F]);
|
||||
++i;
|
||||
}
|
||||
for (uint_fast8_t s = 0; s < 16U - cols; ++s) {
|
||||
hex[left++] = ' ';
|
||||
hex[left++] = ' ';
|
||||
hex[left++] = ' ';
|
||||
}
|
||||
|
||||
hex[left++] = '|';
|
||||
for (uint_fast8_t c = 0; c < cols; ++c) {
|
||||
left += snprintf(hex + left, 2, "%c", std::isprint(addr[ia]) ? (char)addr[ia] : '.');
|
||||
++ia;
|
||||
}
|
||||
puts(hex);
|
||||
}
|
||||
}
|
||||
|
||||
elapsed_time_t elapsedTime()
|
||||
{
|
||||
return std::chrono::duration_cast<elapsed_time_t>(clock::now() - start_at);
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
206
libraries/M5Utility/src/m5_utility/log/library_log.hpp
Normal file
206
libraries/M5Utility/src/m5_utility/log/library_log.hpp
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file library_log.hpp
|
||||
@brief Logging for libraries
|
||||
*/
|
||||
#ifndef M5_UTILITY_LOG_LIBRARY_LOG_HPP
|
||||
#define M5_UTILITY_LOG_LIBRARY_LOG_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
namespace log {
|
||||
|
||||
/*!
|
||||
@enum log_level_t
|
||||
@brief Log output control level
|
||||
@details
|
||||
*/
|
||||
enum class LogLevel : uint8_t {
|
||||
None, //!< No output
|
||||
Error, //!< Error
|
||||
Warn, //!< Warning
|
||||
Info, //!< Information
|
||||
Debug, //!< Debug
|
||||
Verbose, //!< Verbose
|
||||
};
|
||||
using log_level_t = LogLevel;
|
||||
|
||||
#if defined(NDEBUG)
|
||||
constexpr log_level_t logOutputLevel = log_level_t::None;
|
||||
#elif defined(M5_LOG_LEVEL)
|
||||
constexpr log_level_t logOutputLevel = static_cast<log_level_t>(M5_LOG_LEVEL);
|
||||
#elif defined(CORE_DEBUG_LEVEL)
|
||||
constexpr log_level_t logOutputLevel = static_cast<log_level_t>(CORE_DEBUG_LEVEL);
|
||||
#else
|
||||
/*!
|
||||
@var logOutputLevel
|
||||
@brief Base value of log level to be output
|
||||
@details The value can be specified in the compile options.
|
||||
-DM5_LOG_LEVEL=[0..5] or -DCORE_LOG_LEVEL=[0...5]
|
||||
default as NONE
|
||||
@warning No output if NDEBUG defined
|
||||
*/
|
||||
constexpr log_level_t logOutputLevel = log_level_t::None;
|
||||
#endif
|
||||
|
||||
/// @cond
|
||||
// Does the string contain slash?
|
||||
constexpr bool containss_slash(const char* s)
|
||||
{
|
||||
return *s ? (*s == '/' ? true : containss_slash(s + 1)) : false;
|
||||
}
|
||||
// Returns the next position after the right-most slash
|
||||
constexpr const char* after_right_slash(const char* s)
|
||||
{
|
||||
return (*s == '/') ? (s + 1) : after_right_slash(s - 1);
|
||||
}
|
||||
// Gets the tail of string
|
||||
constexpr const char* tail(const char* s)
|
||||
{
|
||||
return *s ? tail(s + 1) : s;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/*!
|
||||
@brief Gets the filename from full pathname
|
||||
@warning If the string is too long, the recursion depth may be too deep to
|
||||
fail. (If compile time calculation)
|
||||
*/
|
||||
constexpr const char* pathToFilename(const char* path)
|
||||
{
|
||||
return (path && path[0]) ? (containss_slash(path) ? after_right_slash(tail(path)) : path) : "";
|
||||
}
|
||||
|
||||
//! @brief Output formatted strings
|
||||
void logPrintf(const char* format, ...);
|
||||
//! @brief Output dump
|
||||
void dump(const void* addr, const size_t len, const bool align = true);
|
||||
|
||||
using elapsed_time_t = std::chrono::milliseconds;
|
||||
// using elapsed_time_t = std::chrono::microseconds;
|
||||
|
||||
//! @brief Gets the elapsed time for log
|
||||
elapsed_time_t elapsedTime();
|
||||
|
||||
///@cond
|
||||
#ifndef M5_UTILITY_LOG_FORMAT
|
||||
#define M5_UTILITY_LOG_FORMAT(letter, format) \
|
||||
"[%6lld][" #letter "][%s:%u] %s(): " format "\n", (int64_t)m5::utility::log::elapsedTime().count(), \
|
||||
m5::utility::log::pathToFilename(__FILE__), __LINE__, __func__
|
||||
#endif
|
||||
///@endcond
|
||||
|
||||
/*!
|
||||
@def M5_LIB_LOGE
|
||||
@brief Output log (level ERROR)
|
||||
*/
|
||||
#define M5_LIB_LOGE(format, ...) \
|
||||
do { \
|
||||
if (m5::utility::log::logOutputLevel >= m5::utility::log::log_level_t::Error) { \
|
||||
m5::utility::log::logPrintf(M5_UTILITY_LOG_FORMAT(E, format), ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
/*!
|
||||
@def M5_LIB_LOGW
|
||||
@brief Output log (level WARN)
|
||||
*/
|
||||
#define M5_LIB_LOGW(format, ...) \
|
||||
do { \
|
||||
if (m5::utility::log::logOutputLevel >= m5::utility::log::log_level_t::Warn) { \
|
||||
m5::utility::log::logPrintf(M5_UTILITY_LOG_FORMAT(W, format), ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
/*!
|
||||
@def M5_LIB_LOGI
|
||||
@brief Output log (level INFO)
|
||||
*/
|
||||
#define M5_LIB_LOGI(format, ...) \
|
||||
do { \
|
||||
if (m5::utility::log::logOutputLevel >= m5::utility::log::log_level_t::Info) { \
|
||||
m5::utility::log::logPrintf(M5_UTILITY_LOG_FORMAT(I, format), ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
/*!
|
||||
@def M5_LIB_LOGD
|
||||
@brief Output log (level DEBUG)
|
||||
*/
|
||||
#define M5_LIB_LOGD(format, ...) \
|
||||
do { \
|
||||
if (m5::utility::log::logOutputLevel >= m5::utility::log::log_level_t::Debug) { \
|
||||
m5::utility::log::logPrintf(M5_UTILITY_LOG_FORMAT(D, format), ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
/*!
|
||||
@def M5_LIB_LOGV
|
||||
@brief Output log (level VERBOSE)
|
||||
*/
|
||||
#define M5_LIB_LOGV(format, ...) \
|
||||
do { \
|
||||
if (m5::utility::log::logOutputLevel >= m5::utility::log::log_level_t::Verbose) { \
|
||||
m5::utility::log::logPrintf(M5_UTILITY_LOG_FORMAT(V, format), ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*!
|
||||
@def M5_DUMPE
|
||||
@brief Output log (level ERROR)
|
||||
*/
|
||||
#define M5_DUMPE(addr, len) \
|
||||
do { \
|
||||
if (m5::utility::log::logOutputLevel >= m5::utility::log::log_level_t::Error) { \
|
||||
m5::utility::log::dump((addr), (len)); \
|
||||
} \
|
||||
} while (0)
|
||||
/*!
|
||||
@def M5_DUMPW
|
||||
@brief Output log (level WARN)
|
||||
*/
|
||||
#define M5_DUMPW(addr, len) \
|
||||
do { \
|
||||
if (m5::utility::log::logOutputLevel >= m5::utility::log::log_level_t::Warn) { \
|
||||
m5::utility::log::dump((addr), (len)); \
|
||||
} \
|
||||
} while (0)
|
||||
/*!
|
||||
@def M5_DUMPI
|
||||
@brief Output log (level INFO)
|
||||
*/
|
||||
#define M5_DUMPI(addr, len) \
|
||||
do { \
|
||||
if (m5::utility::log::logOutputLevel >= m5::utility::log::log_level_t::Info) { \
|
||||
m5::utility::log::dump((addr), (len)); \
|
||||
} \
|
||||
} while (0)
|
||||
/*!
|
||||
@def M5_DUMPD
|
||||
@brief Output log (level DEBUG)
|
||||
*/
|
||||
#define M5_DUMPD(addr, len) \
|
||||
do { \
|
||||
if (m5::utility::log::logOutputLevel >= m5::utility::log::log_level_t::Debug) { \
|
||||
m5::utility::log::dump((addr), (len)); \
|
||||
} \
|
||||
} while (0)
|
||||
/*!
|
||||
@def M5_DUMPV
|
||||
@brief Output log (level VERBOSE)
|
||||
*/
|
||||
#define M5_DUMPV(addr, len) \
|
||||
do { \
|
||||
if (m5::utility::log::logOutputLevel >= m5::utility::log::log_level_t::Verbose) { \
|
||||
m5::utility::log::dump((addr), (len)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
} // namespace log
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
|
||||
#endif
|
||||
34
libraries/M5Utility/src/m5_utility/math.hpp
Normal file
34
libraries/M5Utility/src/m5_utility/math.hpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file math.hpp
|
||||
@brief Maths-related
|
||||
*/
|
||||
#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
|
||||
60
libraries/M5Utility/src/m5_utility/misc.hpp
Normal file
60
libraries/M5Utility/src/m5_utility/misc.hpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file misc.hpp
|
||||
@brief Miscellaneous features
|
||||
*/
|
||||
#ifndef M5_UTILITY_MISC_HPP
|
||||
#define M5_UTILITY_MISC_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
|
||||
//! @brief Valid I2C address?
|
||||
inline bool isValidI2CAddress(const uint16_t addr)
|
||||
{
|
||||
if (addr <= 0x7F) { // 7 bit
|
||||
return (addr >= 0x08 && addr <= 0x77);
|
||||
}
|
||||
return addr <= 0x3FF; // 10 bit
|
||||
}
|
||||
|
||||
//! @brief Reversing the bit order
|
||||
inline uint8_t reverseBitOrder(const uint8_t u8)
|
||||
{
|
||||
#if defined(__clang__) && 0
|
||||
#pragma message "Using clang builtin"
|
||||
return __builtin_bitreverse8(u8);
|
||||
#else
|
||||
uint8_t v{u8};
|
||||
v = ((v & 0xF0) >> 4) | ((v & 0x0F) << 4);
|
||||
v = ((v & 0xCC) >> 2) | ((v & 0x33) << 2);
|
||||
v = ((v & 0xAA) >> 1) | ((v & 0x55) << 1);
|
||||
return v;
|
||||
#endif
|
||||
}
|
||||
|
||||
//! @brief Reversing the bit order
|
||||
inline uint16_t reverseBitOrder(const uint16_t u16)
|
||||
{
|
||||
#if defined(__clang__) && 0
|
||||
#pragma message "Using clang builtin"
|
||||
return __builtin_bitreverse16(u16);
|
||||
#else
|
||||
uint16_t v{u16};
|
||||
v = ((v & 0xFF00) >> 8) | ((v & 0x00FF) << 8);
|
||||
v = ((v & 0xF0F0) >> 4) | ((v & 0x0F0F) << 4);
|
||||
v = ((v & 0xCCCC) >> 2) | ((v & 0x3333) << 2);
|
||||
v = ((v & 0xAAAA) >> 1) | ((v & 0x5555) << 1);
|
||||
return v;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
#endif
|
||||
24
libraries/M5Utility/src/m5_utility/murmurhash3.cpp
Normal file
24
libraries/M5Utility/src/m5_utility/murmurhash3.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file murmurhash3.cpp
|
||||
@brief MurmurHash3
|
||||
*/
|
||||
#include "murmurhash3.hpp"
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
namespace mmh3 {
|
||||
|
||||
uint32_t calculate(const char* str)
|
||||
{
|
||||
auto len = strlen(str);
|
||||
return finalize(rest(str + ((len >> 2) * sizeof(uint32_t)), (len & 3), group_of_4(str, len >> 2)), len);
|
||||
}
|
||||
|
||||
} // namespace mmh3
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
155
libraries/M5Utility/src/m5_utility/murmurhash3.hpp
Normal file
155
libraries/M5Utility/src/m5_utility/murmurhash3.hpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file murmurhash3.hpp
|
||||
@brief MurmurHash3
|
||||
|
||||
MurmurHash (public domain) by Austin Appleby in 2008
|
||||
@sa https://en.wikipedia.org/wiki/MurmurHash
|
||||
*/
|
||||
#ifndef M5_UTILITY_MURMURHASH3_HPP
|
||||
#define M5_UTILITY_MURMURHASH3_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include "./stl/endianness.hpp"
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
/*!
|
||||
@namespace mmh3
|
||||
@brief For Murmurhash3
|
||||
*/
|
||||
namespace mmh3 {
|
||||
|
||||
/// @cond
|
||||
constexpr uint32_t mul(const uint32_t v, const uint32_t vv)
|
||||
{
|
||||
return v * vv;
|
||||
}
|
||||
|
||||
constexpr uint32_t xor_value(const uint32_t v, const uint32_t x)
|
||||
{
|
||||
return v ^ x;
|
||||
}
|
||||
|
||||
constexpr uint32_t shift_right(const uint32_t v, const uint8_t s)
|
||||
{
|
||||
return v >> s;
|
||||
}
|
||||
|
||||
constexpr uint32_t xor_by_shift_right(const uint32_t v, const uint32_t s)
|
||||
{
|
||||
return xor_value(v, shift_right(v, s));
|
||||
}
|
||||
|
||||
constexpr uint32_t str2uint32_little(const char* str, const size_t getc = sizeof(uint32_t), uint32_t v = 0)
|
||||
{
|
||||
return getc ? str2uint32_little(str, getc - 1, (v << 8) | str[getc - 1]) : v;
|
||||
}
|
||||
|
||||
constexpr uint32_t str2uint32_big(const char* str, const size_t getc = sizeof(uint32_t), uint32_t v = 0)
|
||||
{
|
||||
return getc ? str2uint32_big(str + 1, getc - 1, (v << 8) | *str) : v;
|
||||
}
|
||||
|
||||
// Switch between little and big enfian.
|
||||
template <uint32_t Endian>
|
||||
constexpr typename std::enable_if<Endian, uint32_t>::type str2uint32(const char* str,
|
||||
const size_t getc = sizeof(uint32_t),
|
||||
uint32_t v = 0)
|
||||
{
|
||||
return str2uint32_little(str, getc, v);
|
||||
}
|
||||
|
||||
template <uint32_t Endian>
|
||||
constexpr typename std::enable_if<!Endian, uint32_t>::type str2uint32(const char* str,
|
||||
const size_t getc = sizeof(uint32_t),
|
||||
uint32_t v = 0)
|
||||
{
|
||||
return str2uint32_big(str, getc, v);
|
||||
}
|
||||
|
||||
constexpr uint32_t scramble_sub(const uint32_t k)
|
||||
{
|
||||
return (k << 15) | (k >> 17);
|
||||
}
|
||||
|
||||
constexpr uint32_t scramble(const uint32_t k, const uint32_t h)
|
||||
{
|
||||
return h ^ mul(scramble_sub(mul(k, 0xcc9e2d51)), 0x1b873593);
|
||||
}
|
||||
|
||||
constexpr uint32_t group_of_4_sub_3(const uint32_t h)
|
||||
{
|
||||
return h * 5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
constexpr uint32_t group_of_4_sub_2(const uint32_t h)
|
||||
{
|
||||
return (h << 13) | (h >> 19);
|
||||
}
|
||||
|
||||
constexpr uint32_t group_of_4_sub_1(const uint32_t k, const uint32_t h)
|
||||
{
|
||||
return group_of_4_sub_3(group_of_4_sub_2(scramble(k, h)));
|
||||
}
|
||||
|
||||
constexpr uint32_t group_of_4(const char* str, const size_t len, const uint32_t h = 0)
|
||||
{
|
||||
return len ? group_of_4(str + sizeof(uint32_t), len - 1, group_of_4_sub_1(str2uint32<m5::endian::little>(str), h))
|
||||
: h;
|
||||
}
|
||||
|
||||
constexpr uint32_t rest(const char* str, const size_t len, const uint32_t h = 0)
|
||||
{
|
||||
return len ? scramble(str2uint32<m5::endian::little>(str, len), h) : h;
|
||||
}
|
||||
|
||||
constexpr uint32_t finalize(uint32_t h, size_t len)
|
||||
{
|
||||
return xor_by_shift_right(
|
||||
mul(xor_by_shift_right(mul(xor_by_shift_right(xor_value(h, len), 16), 0x85ebca6b), 13), 0xc2b2ae35), 16);
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/*!
|
||||
@brief MurmurHash3 by compile-time calculation
|
||||
@param str String
|
||||
@param len Length of string
|
||||
@return 32bit MurmurHash3 from input string
|
||||
*/
|
||||
constexpr uint32_t calculate(const char* str, const size_t len)
|
||||
{
|
||||
return finalize(rest(str + ((len >> 2) * sizeof(uint32_t)), (len & 3), group_of_4(str, len >> 2)), len);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief User-defined literals "_mmh3"
|
||||
@return 32bit MurmurHash3 from input string
|
||||
@code
|
||||
using namespace m5::unit::mmh3;
|
||||
uint32_t h = "M5 Stack"_mmh3;
|
||||
@endcode
|
||||
*/
|
||||
constexpr uint32_t operator"" _mmh3(const char* str, const size_t len)
|
||||
{
|
||||
return calculate(str, len);
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief Calculate MurmurHash3 from string
|
||||
@param str String
|
||||
@return 32bit MurmurHash3 from input string
|
||||
*/
|
||||
uint32_t calculate(const char* str);
|
||||
|
||||
} // namespace mmh3
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
#endif
|
||||
85
libraries/M5Utility/src/m5_utility/stl/endianness.hpp
Normal file
85
libraries/M5Utility/src/m5_utility/stl/endianness.hpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file endianness.hpp
|
||||
@brief Compile-time endian identification
|
||||
*/
|
||||
#ifndef M5_UTILITY_STL_ENDIANESS_HPP
|
||||
#define M5_UTILITY_STL_ENDIANESS_HPP
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
// #pragma message "Using std::endian"
|
||||
#include <bit>
|
||||
#elif __has_include(<endian.h>)
|
||||
// #pragma message "Using endian.h"
|
||||
#include <endian.h>
|
||||
#elif __has_include(<machine/endian.h>)
|
||||
// #pragma message "Using machine/endian.h"
|
||||
#include <machine/endian.h>
|
||||
#else
|
||||
// #pragma message "Using hacked"
|
||||
#include <cstdint>
|
||||
#endif
|
||||
|
||||
namespace m5 {
|
||||
namespace stl {
|
||||
|
||||
// C++20 or later
|
||||
#if __cplusplus >= 202002L || DOXYGEN_PROCESS
|
||||
|
||||
using endian = std::endian;
|
||||
|
||||
// endian header
|
||||
#elif __has_include(<endian.h>) || __has_include(<machine/endian.h>)
|
||||
|
||||
enum class endian {
|
||||
#if defined(__BYTE_ORDER)
|
||||
little = __LITTLE_ENDIAN,
|
||||
big = __BIG_ENDIAN,
|
||||
native = __BYTE_ORDER
|
||||
#elif defined(_BYTE_ORDER)
|
||||
little = _LITTLE_ENDIAN,
|
||||
big = _BIG_ENDIAN,
|
||||
native = _BYTE_ORDER
|
||||
#elif defined(BYTE_ORDER)
|
||||
little = LITTLE_ENDIAN,
|
||||
big = BIG_ENDIAN,
|
||||
native = BYTE_ORDER
|
||||
#else
|
||||
little = 0,
|
||||
big = 0,
|
||||
native = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
/// @cond
|
||||
constexpr uint32_t val32 = 0x11223344;
|
||||
constexpr uint8_t ref8 = static_cast<const uint8_t&>(val32);
|
||||
/// @endcond
|
||||
enum class endian { little = 0x44, big = 0x11, native = ref8 };
|
||||
#endif
|
||||
} // namespace stl
|
||||
|
||||
/*!
|
||||
@namespace enidan
|
||||
@brief endianness detection
|
||||
*/
|
||||
namespace endian {
|
||||
///@name endian type
|
||||
///@{
|
||||
constexpr bool little = m5::stl::endian::native == m5::stl::endian::little; //!< @brief true if little endian.
|
||||
constexpr bool big = m5::stl::endian::native == m5::stl::endian::big; //!< @brief true if big endian.
|
||||
constexpr bool other = !little && !big; //!< @brief true if other endian.
|
||||
///@}
|
||||
|
||||
static_assert(little || big || other, "Unable to determine endianness");
|
||||
static_assert(((int)little + (int)big + (int)other) == 1, "Endian matches more than one");
|
||||
} // namespace endian
|
||||
} // namespace m5
|
||||
|
||||
#endif
|
||||
2523
libraries/M5Utility/src/m5_utility/stl/expected.hpp
Normal file
2523
libraries/M5Utility/src/m5_utility/stl/expected.hpp
Normal file
File diff suppressed because it is too large
Load diff
50
libraries/M5Utility/src/m5_utility/stl/extension.hpp
Normal file
50
libraries/M5Utility/src/m5_utility/stl/extension.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*!
|
||||
@file stl_extension.hpp
|
||||
@brief STL extensions
|
||||
|
||||
Add features that cannot be used depending on the C++ version, etc.
|
||||
|
||||
SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef M5_UTILITY_STL_EXTENSION_HPP
|
||||
#define M5_UTILITY_STL_EXTENSION_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace m5 {
|
||||
namespace stl {
|
||||
|
||||
/*! @brief Like std::size C++17 or later.(for container) */
|
||||
template <class C>
|
||||
constexpr auto size(const C& c) -> decltype(c.size())
|
||||
{
|
||||
return c.size();
|
||||
}
|
||||
|
||||
/*! @brief Like std::size C++17 or later.(for raw array) */
|
||||
template <typename T, size_t N>
|
||||
constexpr auto size(const T (&)[N]) noexcept -> size_t
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Converts an enumeration to its underlying type.(Like std::to_underlying
|
||||
C++23 or later)
|
||||
@tparam E Type of enum
|
||||
@param e Enumeration value to convert
|
||||
@return The integer value of the underlying type of Enum, converted from e.
|
||||
*/
|
||||
template <typename E>
|
||||
constexpr inline typename std::underlying_type<E>::type to_underlying(const E e) noexcept
|
||||
{
|
||||
return static_cast<typename std::underlying_type<E>::type>(e);
|
||||
}
|
||||
|
||||
} // namespace stl
|
||||
} // namespace m5
|
||||
|
||||
#endif
|
||||
2185
libraries/M5Utility/src/m5_utility/stl/optional.hpp
Normal file
2185
libraries/M5Utility/src/m5_utility/stl/optional.hpp
Normal file
File diff suppressed because it is too large
Load diff
53
libraries/M5Utility/src/m5_utility/string.cpp
Normal file
53
libraries/M5Utility/src/m5_utility/string.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file string.cpp
|
||||
@brief Utilities for string
|
||||
*/
|
||||
#include "string.hpp"
|
||||
#include <cstdarg>
|
||||
#include <algorithm>
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
|
||||
std::string formatString(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
size_t sz = vsnprintf(nullptr, 0U, fmt, args); // calculate length
|
||||
va_end(args);
|
||||
|
||||
char buf[sz + 1];
|
||||
va_start(args, fmt); // Reinitiaize args (args cannot reuse because
|
||||
// indefinite value after vsnprintf)
|
||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
va_end(args);
|
||||
// String don't has constructor(const char*, const size_t);
|
||||
buf[sz] = '\0';
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
std::string& trimRight(std::string& s)
|
||||
{
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](std::string::value_type& ch) { return !std::isspace(ch); }));
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string& trimLeft(std::string& s)
|
||||
{
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](std::string::value_type& ch) { return !std::isspace(ch); }).base(),
|
||||
s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string& trim(std::string& s)
|
||||
{
|
||||
return trimRight(trimLeft(s));
|
||||
}
|
||||
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
65
libraries/M5Utility/src/m5_utility/string.hpp
Normal file
65
libraries/M5Utility/src/m5_utility/string.hpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file string.hpp
|
||||
@brief Utilities for string
|
||||
*/
|
||||
#ifndef M5_UTILITY_STRING_HPP
|
||||
#define M5_UTILITY_STRING_HPP
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace m5 {
|
||||
namespace utility {
|
||||
|
||||
//! @ brief Create a string in a format similar to printf
|
||||
std::string formatString(const char* fmt, ...);
|
||||
|
||||
///@name Trim
|
||||
///@warning The string entered will be changed
|
||||
///@{
|
||||
/*! @brief Trim right */
|
||||
std::string& trimRight(std::string& s);
|
||||
//! @brief Trim left
|
||||
std::string& trimLeft(std::string& s);
|
||||
//! @brief Trim both ends
|
||||
std::string& trim(std::string& s);
|
||||
///@}
|
||||
|
||||
///@name Convert
|
||||
///@{
|
||||
/*!
|
||||
@brief Convert from 0~15 to hexadecimal character
|
||||
@tparam Case Capitalise if true
|
||||
*/
|
||||
template <bool Case = true>
|
||||
constexpr char uintToHexChar(const uint8_t v)
|
||||
{
|
||||
return (v & 0x0F) < 10 ? '0' + (v & 0x0F) : (Case ? 'A' : 'a') + ((v & 0x0F) - 10);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Convert any one unsigned integer to a hexadecimal string
|
||||
@tparam T Value type (Must be unsigned integer)
|
||||
*/
|
||||
template <typename T, bool Case = true>
|
||||
std::string unsignedToHexString(const T& v)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value, "T must be unsigned integer");
|
||||
std::string s;
|
||||
for (size_t i = sizeof(T); i > 0; --i) {
|
||||
uint8_t u8 = (v >> ((i - 1) * 8)) & 0xFF;
|
||||
s += uintToHexChar<Case>((u8 >> 4) & 0x0F);
|
||||
s += uintToHexChar<Case>(u8 & 0x0F);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
///@}
|
||||
|
||||
} // namespace utility
|
||||
} // namespace m5
|
||||
#endif
|
||||
276
libraries/M5Utility/src/m5_utility/types.hpp
Normal file
276
libraries/M5Utility/src/m5_utility/types.hpp
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Spdx-Filecopyrighttext: 2024 M5Stack Technology CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
/*!
|
||||
@file types.hpp
|
||||
@brief Type and enumerator definitions
|
||||
*/
|
||||
#ifndef M5_UTILITY_TYPES_HPP
|
||||
#define M5_UTILITY_TYPES_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
#include "stl/endianness.hpp"
|
||||
|
||||
namespace m5 {
|
||||
namespace types {
|
||||
|
||||
/*!
|
||||
@struct U16
|
||||
@tparam DELittle Endian type specification<br> true: Little false: Big
|
||||
@brief Endian-compliant uint16
|
||||
*/
|
||||
template <bool DELittle>
|
||||
union U16 {
|
||||
/// @name Constrcutor
|
||||
///@{
|
||||
/*! @brief default constructor */
|
||||
constexpr U16() : u16{0}
|
||||
{
|
||||
}
|
||||
//!@brief from uint16_t
|
||||
#if 0
|
||||
template <bool PELittle = m5::endian::little>
|
||||
constexpr explicit U16(const uint16_t v) : u8{
|
||||
static_cast<uint8_t>(
|
||||
DELittle == PELittle ? (v & 0XFF) : (v >> 8)),
|
||||
u8[1] = static_cast<uint8_t>(
|
||||
DELittle == PELittle ? (v >> 8) : (v & 0xFF)) } {}
|
||||
#else
|
||||
template <bool PELittle = m5::endian::little>
|
||||
explicit U16(const uint16_t v)
|
||||
{
|
||||
set<PELittle>(v);
|
||||
}
|
||||
|
||||
#endif
|
||||
//! @brief Stored in order of high and low
|
||||
constexpr U16(const uint8_t high, const uint8_t low) : u8{high, low}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr U16(const U16&) = default;
|
||||
|
||||
constexpr U16(U16&& o) noexcept = default;
|
||||
///@}
|
||||
|
||||
///@name Assignment
|
||||
///@{
|
||||
U16& operator=(const U16&) = default;
|
||||
|
||||
U16& operator=(U16&&) noexcept = default;
|
||||
|
||||
template <bool PELittle = m5::endian::little>
|
||||
U16& operator=(const uint16_t v)
|
||||
{
|
||||
set<PELittle>(v);
|
||||
return *this;
|
||||
}
|
||||
template <typename H, typename L>
|
||||
U16& operator=(const std::pair<H, L>& o)
|
||||
{
|
||||
static_assert(std::is_integral<H>::value && std::is_integral<L>::value, "HIGH & LOW Must be integral");
|
||||
u8[0] = static_cast<uint8_t>(o.first);
|
||||
u8[1] = static_cast<uint8_t>(o.second);
|
||||
return *this;
|
||||
}
|
||||
///@}
|
||||
|
||||
///@name Cast
|
||||
///@{
|
||||
/*! @brief To boolean */
|
||||
inline explicit operator bool() const
|
||||
{
|
||||
return u16;
|
||||
}
|
||||
/*! @brief To const uint8_t* */
|
||||
inline explicit operator const uint8_t*() const
|
||||
{
|
||||
return data();
|
||||
}
|
||||
/*! @brief To uint8_t* */
|
||||
inline explicit operator uint8_t*() const
|
||||
{
|
||||
return data();
|
||||
}
|
||||
//! @brief To uint16_t on processor endianness
|
||||
inline explicit operator uint16_t() const
|
||||
{
|
||||
return get();
|
||||
}
|
||||
///@}
|
||||
|
||||
/*!
|
||||
@brief Set value with specified endianness
|
||||
@tparam PELittle Endianness (default as processor endianness)
|
||||
*/
|
||||
template <bool PELittle = m5::endian::little>
|
||||
inline void set(const uint16_t v)
|
||||
{
|
||||
if (DELittle == PELittle) {
|
||||
u16 = v;
|
||||
} else {
|
||||
u8[0] = static_cast<uint8_t>(v >> 8);
|
||||
u8[1] = static_cast<uint8_t>(v & 0xFF);
|
||||
}
|
||||
}
|
||||
/*!
|
||||
@brief Gets value with specified endianness
|
||||
@tparam PELittle Endianness (default as processor endianness)
|
||||
*/
|
||||
template <bool PELittle = m5::endian::little>
|
||||
inline uint16_t get() const
|
||||
{
|
||||
uint16_t r{u16};
|
||||
if (DELittle != PELittle) {
|
||||
r = U16<DELittle>{u8[1], u8[0]}.u16;
|
||||
}
|
||||
return r;
|
||||
};
|
||||
//! @brief Gets the high byte
|
||||
inline uint8_t high() const
|
||||
{
|
||||
return u8[0];
|
||||
}
|
||||
//! @brief Gets the low byte
|
||||
inline uint8_t low() const
|
||||
{
|
||||
return u8[1];
|
||||
}
|
||||
//! @brief Gets the const pointer
|
||||
inline const uint8_t* data() const
|
||||
{
|
||||
return u8;
|
||||
}
|
||||
//! @brief Gets the pointer
|
||||
inline uint8_t* data()
|
||||
{
|
||||
return u8;
|
||||
}
|
||||
//! @brief Gets size in uint8_t units.
|
||||
inline size_t size() const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
uint16_t u16{}; //!< @brief Raw value
|
||||
uint8_t u8[2]; //!< @brief Raw value according to uint8_t
|
||||
};
|
||||
|
||||
using big_uint16_t = U16<false>;
|
||||
using little_uint16_t = U16<true>;
|
||||
|
||||
///@name Compare
|
||||
/// @related m5::types::U16
|
||||
///@{
|
||||
// ==
|
||||
inline bool operator==(const big_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return a.u16 == b.u16;
|
||||
}
|
||||
inline bool operator==(const big_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return std::tie(a.u8[0], a.u8[1]) == std::tie(b.u8[1], b.u8[0]);
|
||||
}
|
||||
inline bool operator==(const little_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return std::tie(a.u8[1], a.u8[0]) == std::tie(b.u8[0], b.u8[1]);
|
||||
}
|
||||
inline bool operator==(const little_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return a.u16 == b.u16;
|
||||
}
|
||||
// !=
|
||||
inline bool operator!=(const big_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
inline bool operator!=(const big_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
inline bool operator!=(const little_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
inline bool operator!=(const little_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
// <
|
||||
inline bool operator<(const big_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return a.u16 < b.u16;
|
||||
}
|
||||
inline bool operator<(const big_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return std::tie(a.u8[0], a.u8[1]) < std::tie(b.u8[1], b.u8[0]);
|
||||
}
|
||||
inline bool operator<(const little_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return std::tie(a.u8[1], a.u8[0]) < std::tie(b.u8[0], b.u8[1]);
|
||||
}
|
||||
inline bool operator<(const little_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return a.u16 < b.u16;
|
||||
}
|
||||
// >
|
||||
inline bool operator>(const big_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
inline bool operator>(const big_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
inline bool operator>(const little_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
inline bool operator>(const little_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
// <=
|
||||
inline bool operator<=(const big_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return !(a > b);
|
||||
}
|
||||
inline bool operator<=(const big_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return !(a > b);
|
||||
}
|
||||
inline bool operator<=(const little_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return !(a > b);
|
||||
}
|
||||
inline bool operator<=(const little_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return !(a > b);
|
||||
}
|
||||
// >=
|
||||
inline bool operator>=(const big_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
inline bool operator>=(const big_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
inline bool operator>=(const little_uint16_t& a, const big_uint16_t& b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
inline bool operator>=(const little_uint16_t& a, const little_uint16_t& b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
///@}
|
||||
|
||||
} // namespace types
|
||||
} // namespace m5
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue