first commit

This commit is contained in:
stuce-bot 2025-06-30 20:47:33 +02:00
commit 5893b00dd2
1669 changed files with 1982740 additions and 0 deletions

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 M5Stack Technology CO LTD
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,54 @@
# M5Utility
## Overview
This library is a collection of common functionality for other M5Libraries and products.
## UnitTest (For internal maintainers)
UnitTest is included, which can run on PlatformIO.
## Doxygen document
[GitHub Pages](https://m5stack.github.io/M5Utility/)
If you want to generate documents on your local machine, execute the following command
```
bash docs/doxy.sh
```
It will output it under docs/html
If you want to output Git commit hashes to html, do it for the git cloned folder.
### Required
- [Doxyegn](https://www.doxygen.nl/)
- [pcregrep](https://formulae.brew.sh/formula/pcre2)
- [Git](https://git-scm.com/) (Output commit hash to html)
---
## 概要
このライブラリは M5Stack 関連ライブラリのための共通ライブラリです。
## ユニットテスト(メンテナンス用)
googleTest によるユニットテストを内包しています。 PlatformIO で実行できます。
## Doxygen ドキュメント
[GitHub Pages](https://m5stack.github.io/M5Utility/)
あなたのローカルマシンでドキュメントを生成したい場合は、以下のコマンドを実行してください。
```
bash docs/doxy.sh
```
docs/html の下に出力されます。
Git コミットのハッシュを html に出力したい場合は、 git クローンしたフォルダに対して実行してください。
### 必要な物
- [Doxyegn](https://www.doxygen.nl/)
- [pcregrep](https://formulae.brew.sh/formula/pcre2)
- [Git](https://git-scm.com/)

View file

@ -0,0 +1,41 @@
{
"build": {
"arduino": {
"memory_type": "qio_opi",
"ldscript": "esp32s3_out.ld",
"partitions": "default_8MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DARDUINO_M5STACK_ATOMS3R",
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "dio",
"mcu": "esp32s3",
"variant": "m5stack_atoms3"
},
"connectivity": [
"bluetooth",
"wifi"
],
"frameworks": [
"arduino",
"espidf"
],
"name": "M5Stack AtomS3R",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"require_upload_port": true,
"speed": 460800
},
"url": "https://docs.m5stack.com/en/core/AtomS3R",
"vendor": "M5Stack"
}

View file

@ -0,0 +1,33 @@
{
"build": {
"core": "esp32",
"extra_flags": [
"-DARDUINO_M5STACK_NANOC6"
],
"f_cpu": "160000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"mcu": "esp32c6",
"variant": "esp32c6"
},
"connectivity": [
"wifi"
],
"debug": {
"openocd_target": "esp32c6.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "M5Stack NanoC6",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194384,
"require_upload_port": true,
"speed": 460800
},
"url": "https://docs.m5stack.com/en/core/M5NanoC6",
"vendor": "M5Stack"
}

View file

@ -0,0 +1,40 @@
{
"build": {
"arduino":{
"ldscript": "esp32_out.ld",
"partitions": "default_8MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DM5STACK_M5STICK_CPLUS2",
"-DBOARD_HAS_PSRAM",
"-mfix-esp32-psram-cache-issue",
"-mfix-esp32-psram-cache-strategy=memw",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "m5stick_c"
},
"connectivity": [
"wifi",
"bluetooth"
],
"frameworks": [
"arduino",
"espidf"
],
"name": "M5Stick-CPlus2",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"require_upload_port": true,
"speed": 1500000
},
"url": "https://docs.m5stack.com/en/core/M5StickC%20PLUS2",
"vendor": "M5Stack"
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
#!/bin/bash
# Please execute on repositry root
## Get version from library.properties
## Get git rev of HEAD
LIB_VERSION="$(pcregrep -o1 "^\s*version\s*=\s*(\*|\d+(\.\d+){0,3}(\.\*)?)" library.properties)"
#echo ${DOXYGEN_PROJECT_NUMBER}
DOXYGEN_PROJECT_NUMBER="${LIB_VERSION} git rev:$(git rev-parse --short HEAD)" doxygen docs/Doxyfile

View file

@ -0,0 +1,28 @@
{
"name": "M5Utility",
"description": "Library for other M5 libraries and products",
"keywords": "M5Utility",
"authors": {
"name": "M5Stack",
"url": "http://www.m5stack.com"
},
"repository": {
"type": "git",
"url": "https://github.com/m5stack/M5Utility.git"
},
"dependencies": [],
"version": "0.0.2",
"frameworks": [
"arduino"
],
"platforms": [
"espressif32"
],
"headers": "M5Utility.h",
"license": "MIT",
"export": {
"exclude": [
"docs/html"
]
}
}

View file

@ -0,0 +1,11 @@
name=M5Utility
version=0.0.2
author=M5Stack
maintainer=M5Stack
sentence=Library for other M5 libraries and products
paragraph=M5Stack, M5Stack Core2, M5Stack CoreInk, M5StickC, M5StickC-Plus, M5Paper, M5Tough, M5ATOM, M5STAMP, M5Station, See more on http://M5Stack.com
category=Other
url=https://github.com/m5stack/M5Utility.git
architectures=esp32
includes=M5Utility.h
depends=

View file

@ -0,0 +1,216 @@
;-----------------------------------------------------------------------
; M5Utility
; For UnitTest
;-----------------------------------------------------------------------
[platformio]
;default_envs = test_Core, test_Core2, test_CoreS3, test_Fire, test_StampS3, test_Dial, test_AtomS3, test_NanoC6, test_StickCPlus, test_Paper
[env]
build_flags =-Wall -Wextra -Wreturn-local-addr -Werror=format -Werror=return-local-addr
lib_ldf_mode = deep
test_framework = googletest
test_build_src = true
lib_deps=m5stack/M5Unified
google/googletest@1.12.1
; --------------------------------
;Choose device
[m5base]
monitor_speed = 115200
monitor_filters = esp32_exception_decoder, time
upload_speed = 1500000
test_speed = 115200
test_filter= embedded/*
test_ignore= native/*
platform = espressif32 @6.8.1
framework = arduino
[Core]
extends = m5base
board = m5stack-grey
;m5stack-core-esp32-16M ;;6.8.0 or later
;m5stack-core-esp32
lib_deps = ${env.lib_deps}
[Core2]
extends = m5base
board = m5stack-core2
lib_deps = ${env.lib_deps}
[CoreS3]
extends = m5base
board = m5stack-cores3
lib_deps = ${env.lib_deps}
[Fire]
extends = m5base
board = m5stack-fire
lib_deps = ${env.lib_deps}
[StampS3]
;include M5Capsule, DinMeter
extends = m5base
board = m5stack-stamps3
lib_deps = ${env.lib_deps}
[Dial]
extends = m5base
board = m5stack-stamps3
lib_deps = ${env.lib_deps}
m5stack/M5Dial
[AtomMatrix]
extends = m5base
board = m5stack-atom
lib_deps = ${env.lib_deps}
[AtomS3]
extends = m5base
board = m5stack-atoms3
lib_deps = ${env.lib_deps}
; Using ./boards/m5stack-atoms3r.json
[AtomS3R]
extends = m5base
board = m5stack-atoms3r
lib_deps = ${env.lib_deps}
; Using ./boards/m5stack-nanoc6.json
[NanoC6]
extends = m5base
board = m5stack-nanoc6
platform = https://github.com/platformio/platform-espressif32.git
platform_packages =
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git
platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1
board_build.partitions = default.csv
lib_deps = ${env.lib_deps}
[StickCPlus]
extends = m5base
board = m5stick-c
lib_deps = ${env.lib_deps}
; Using ./boards/m5stick-cplus2.json
[StickCPlus2]
extends = m5base
board = m5stick-cplus2
lib_deps = ${env.lib_deps}
[Paper]
extends = m5base
board = m5stack-fire
lib_deps = ${env.lib_deps}
[CoreInk]
extends = m5base
board = m5stack-coreink
lib_deps = ${env.lib_deps}
[sdl]
build_flags = -O3 -xc++ -std=c++14 -lSDL2
-arch arm64 ; for arm mac
-I"/usr/local/include/SDL2" ; for intel mac homebrew SDL2
-L"/usr/local/lib" ; for intel mac homebrew SDL2
-I"${sysenv.HOMEBREW_PREFIX}/include/SDL2" ; for arm mac homebrew SDL2
-L"${sysenv.HOMEBREW_PREFIX}/lib" ; for arm mac homebrew SDL2
platform = native
test_filter= native/*
test_ignore= embedded/*
lib_deps = ${env.lib_deps}
; --------------------------------
;Choose build options
[option_release]
build_type=release
build_flags = ${env.build_flags}
-DCORE_DEBUG_LEVEL=3
-DLOG_LOCAL_LEVEL=3
-DAPP_LOG_LEVEL=3
-DM5_LOG_LEVEL=3
[option_log]
build_type=release
build_flags = ${env.build_flags}
-DCORE_DEBUG_LEVEL=5
-DLOG_LOCAL_LEVEL=5
-DAPP_LOG_LEVEL=5
[option_debug]
build_type=debug
build_flags = ${env.build_flags}
-DCORE_DEBUG_LEVEL=5
-DLOG_LOCAL_LEVEL=5
-DAPP_LOG_LEVEL=5
-DDEBUG
[option_map]
build_type=release
build_flags = ${env.build_flags}
-DCORE_DEBUG_LEVEL=3
-DLOG_LOCAL_LEVEL=3
-DAPP_LOG_LEVEL=3
-DM5_LOG_LEVEL=0
-Wl,-Map,output.map
; --------------------------------
; UnitTest
[env:test_native]
extends = sdl, option_release
build_flags = ${sdl.build_flags} ${option_release.build_flags}
[env:test_Core]
extends=Core, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_Core2]
extends=Core2, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_CoreS3]
extends=CoreS3, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_Fire]
extends=Fire, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_StampS3]
extends=StampS3, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_Dial]
extends=Dial, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_AtomMatrix]
extends=AtomMatrix, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_AtomS3]
extends=AtomS3, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_AtomS3R]
extends=AtomS3R, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_NanoC6]
extends=NanoC6, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_StickCPlus]
extends=StickCPlus, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_StickCPlus2]
extends=StickCPlus2, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_Paper]
extends=Paper, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}
[env:test_CoreInk]
extends=CoreInk, option_release
build_flags = ${env.build_flags} ${option_release.build_flags}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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

View 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

View 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

View file

@ -0,0 +1,452 @@
/*
MurmurHash3 by
https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
Modified by M5Stack
*/
//-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
// Note - The x86 and x64 versions do _not_ produce the same results, as the
// algorithms are optimized for their respective platforms. You can still
// compile and run any of them on any platform, but your performance with the
// non-native version will be less than optimal.
#include "MurmurHash3.h"
//-----------------------------------------------------------------------------
// Platform-specific functions and macros
// Microsoft Visual Studio
#if defined(_MSC_VER)
#include <cstdlib>
#define ROTL32(x, y) _rotl(x, y)
#define ROTL64(x, y) _rotl64(x, y)
#define BIG_CONSTANT(x) (x)
// Other compilers
#else // defined(_MSC_VER)
inline uint32_t rotl32(uint32_t x, int8_t r)
{
return (x << r) | (x >> (32 - r));
}
inline uint64_t rotl64(uint64_t x, int8_t r)
{
return (x << r) | (x >> (64 - r));
}
#define ROTL32(x, y) rotl32(x, y)
#define ROTL64(x, y) rotl64(x, y)
#define BIG_CONSTANT(x) (x##LLU)
#endif // !defined(_MSC_VER)
//-----------------------------------------------------------------------------
// Block read - if your platform needs to do endian-swapping or can only
// handle aligned reads, do the conversion here
uint32_t getblock32(const uint32_t *p, int i)
{
return p[i];
}
uint64_t getblock64(const uint64_t *p, int i)
{
return p[i];
}
//-----------------------------------------------------------------------------
// Finalization mix - force all bits of a hash block to avalanche
uint32_t fmix32(uint32_t h)
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
//----------
uint64_t fmix64(uint64_t k)
{
k ^= k >> 33;
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
k ^= k >> 33;
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
k ^= k >> 33;
return k;
}
//-----------------------------------------------------------------------------
// objsize: 0x0-0x15f: 351
void MurmurHash3_x86_32(const void *key, int len, uint32_t seed, void *out)
{
const uint8_t *data = (const uint8_t *)key;
const int nblocks = len / 4;
uint32_t h1 = seed;
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
//----------
// body
const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4);
for (int i = -nblocks; i; i++) {
uint32_t k1 = getblock32(blocks, i);
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1, 13);
h1 = h1 * 5 + 0xe6546b64;
}
//----------
// tail
const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
uint32_t k1 = 0;
switch (len & 3) {
case 3:
k1 ^= tail[2] << 16;
/* Falls through. */
case 2:
k1 ^= tail[1] << 8;
/* Falls through. */
case 1:
k1 ^= tail[0];
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
};
//----------
// finalization
h1 ^= len;
h1 = fmix32(h1);
*(uint32_t *)out = h1;
}
//-----------------------------------------------------------------------------
// objsize: 0x160-0x4bb: 859
void MurmurHash3_x86_128(const void *key, const int len, uint32_t seed, void *out)
{
const uint8_t *data = (const uint8_t *)key;
const int nblocks = len / 16;
uint32_t h1 = seed;
uint32_t h2 = seed;
uint32_t h3 = seed;
uint32_t h4 = seed;
const uint32_t c1 = 0x239b961b;
const uint32_t c2 = 0xab0e9789;
const uint32_t c3 = 0x38b34ae5;
const uint32_t c4 = 0xa1e38b93;
//----------
// body
const uint32_t *blocks = (const uint32_t *)(data + nblocks * 16);
for (int i = -nblocks; i; i++) {
uint32_t k1 = getblock32(blocks, i * 4 + 0);
uint32_t k2 = getblock32(blocks, i * 4 + 1);
uint32_t k3 = getblock32(blocks, i * 4 + 2);
uint32_t k4 = getblock32(blocks, i * 4 + 3);
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1, 19);
h1 += h2;
h1 = h1 * 5 + 0x561ccd1b;
k2 *= c2;
k2 = ROTL32(k2, 16);
k2 *= c3;
h2 ^= k2;
h2 = ROTL32(h2, 17);
h2 += h3;
h2 = h2 * 5 + 0x0bcaa747;
k3 *= c3;
k3 = ROTL32(k3, 17);
k3 *= c4;
h3 ^= k3;
h3 = ROTL32(h3, 15);
h3 += h4;
h3 = h3 * 5 + 0x96cd1c35;
k4 *= c4;
k4 = ROTL32(k4, 18);
k4 *= c1;
h4 ^= k4;
h4 = ROTL32(h4, 13);
h4 += h1;
h4 = h4 * 5 + 0x32ac3b17;
}
//----------
// tail
const uint8_t *tail = (const uint8_t *)(data + nblocks * 16);
uint32_t k1 = 0;
uint32_t k2 = 0;
uint32_t k3 = 0;
uint32_t k4 = 0;
switch (len & 15) {
case 15:
k4 ^= tail[14] << 16;
/* Falls through. */
case 14:
k4 ^= tail[13] << 8;
/* Falls through. */
case 13:
k4 ^= tail[12] << 0;
k4 *= c4;
k4 = ROTL32(k4, 18);
k4 *= c1;
h4 ^= k4;
/* Falls through. */
case 12:
k3 ^= tail[11] << 24;
/* Falls through. */
case 11:
k3 ^= tail[10] << 16;
/* Falls through. */
case 10:
k3 ^= tail[9] << 8;
/* Falls through. */
case 9:
k3 ^= tail[8] << 0;
k3 *= c3;
k3 = ROTL32(k3, 17);
k3 *= c4;
h3 ^= k3;
/* Falls through. */
case 8:
k2 ^= tail[7] << 24;
/* Falls through. */
case 7:
k2 ^= tail[6] << 16;
/* Falls through. */
case 6:
k2 ^= tail[5] << 8;
/* Falls through. */
case 5:
k2 ^= tail[4] << 0;
k2 *= c2;
k2 = ROTL32(k2, 16);
k2 *= c3;
h2 ^= k2;
/* Falls through. */
case 4:
k1 ^= tail[3] << 24;
/* Falls through. */
case 3:
k1 ^= tail[2] << 16;
/* Falls through. */
case 2:
k1 ^= tail[1] << 8;
/* Falls through. */
case 1:
k1 ^= tail[0] << 0;
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
};
//----------
// finalization
h1 ^= len;
h2 ^= len;
h3 ^= len;
h4 ^= len;
h1 += h2;
h1 += h3;
h1 += h4;
h2 += h1;
h3 += h1;
h4 += h1;
h1 = fmix32(h1);
h2 = fmix32(h2);
h3 = fmix32(h3);
h4 = fmix32(h4);
h1 += h2;
h1 += h3;
h1 += h4;
h2 += h1;
h3 += h1;
h4 += h1;
((uint32_t *)out)[0] = h1;
((uint32_t *)out)[1] = h2;
((uint32_t *)out)[2] = h3;
((uint32_t *)out)[3] = h4;
}
//-----------------------------------------------------------------------------
// objsize: 0x500-0x7bb: 699
void MurmurHash3_x64_128(const void *key, const int len, const uint32_t seed, void *out)
{
const uint8_t *data = (const uint8_t *)key;
const int nblocks = len / 16;
uint64_t h1 = seed;
uint64_t h2 = seed;
const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);
//----------
// body
const uint64_t *blocks = (const uint64_t *)(data);
for (int i = 0; i < nblocks; i++) {
uint64_t k1 = getblock64(blocks, i * 2 + 0);
uint64_t k2 = getblock64(blocks, i * 2 + 1);
k1 *= c1;
k1 = ROTL64(k1, 31);
k1 *= c2;
h1 ^= k1;
h1 = ROTL64(h1, 27);
h1 += h2;
h1 = h1 * 5 + 0x52dce729;
k2 *= c2;
k2 = ROTL64(k2, 33);
k2 *= c1;
h2 ^= k2;
h2 = ROTL64(h2, 31);
h2 += h1;
h2 = h2 * 5 + 0x38495ab5;
}
//----------
// tail
const uint8_t *tail = (const uint8_t *)(data + nblocks * 16);
uint64_t k1 = 0;
uint64_t k2 = 0;
switch (len & 15) {
case 15:
k2 ^= ((uint64_t)tail[14]) << 48;
/* Falls through. */
case 14:
k2 ^= ((uint64_t)tail[13]) << 40;
/* Falls through. */
case 13:
k2 ^= ((uint64_t)tail[12]) << 32;
/* Falls through. */
case 12:
k2 ^= ((uint64_t)tail[11]) << 24;
/* Falls through. */
case 11:
k2 ^= ((uint64_t)tail[10]) << 16;
/* Falls through. */
case 10:
k2 ^= ((uint64_t)tail[9]) << 8;
/* Falls through. */
case 9:
k2 ^= ((uint64_t)tail[8]) << 0;
k2 *= c2;
k2 = ROTL64(k2, 33);
k2 *= c1;
h2 ^= k2;
/* Falls through. */
case 8:
k1 ^= ((uint64_t)tail[7]) << 56;
/* Falls through. */
case 7:
k1 ^= ((uint64_t)tail[6]) << 48;
/* Falls through. */
case 6:
k1 ^= ((uint64_t)tail[5]) << 40;
/* Falls through. */
case 5:
k1 ^= ((uint64_t)tail[4]) << 32;
/* Falls through. */
case 4:
k1 ^= ((uint64_t)tail[3]) << 24;
/* Falls through. */
case 3:
k1 ^= ((uint64_t)tail[2]) << 16;
/* Falls through. */
case 2:
k1 ^= ((uint64_t)tail[1]) << 8;
/* Falls through. */
case 1:
k1 ^= ((uint64_t)tail[0]) << 0;
k1 *= c1;
k1 = ROTL64(k1, 31);
k1 *= c2;
h1 ^= k1;
};
//----------
// finalization
h1 ^= len;
h2 ^= len;
h1 += h2;
h2 += h1;
h1 = fmix64(h1);
h2 = fmix64(h2);
h1 += h2;
h2 += h1;
((uint64_t *)out)[0] = h1;
((uint64_t *)out)[1] = h2;
}
//-----------------------------------------------------------------------------

View file

@ -0,0 +1,26 @@
/*
MurmurHash3 by
https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.hpp
Modified by M5Stack
*/
//-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
#ifndef _MURMURHASH3_H_
#define _MURMURHASH3_H_
#include <cstdint>
//-----------------------------------------------------------------------------
void MurmurHash3_x86_32(const void* key, int len, uint32_t seed, void* out);
void MurmurHash3_x86_128(const void* key, int len, uint32_t seed, void* out);
void MurmurHash3_x64_128(const void* key, int len, uint32_t seed, void* out);
//-----------------------------------------------------------------------------
#endif // _MURMURHASH3_H_

View file

@ -0,0 +1,58 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
main for UnitTest on native
*/
#include <gtest/gtest.h>
// C++ version
#if __cplusplus >= 202002L
#pragma message "C++20 or later"
#elif __cplusplus >= 201703L
#pragma message "C++17 or later"
#elif __cplusplus >= 201402L
#pragma message "C++14 or later"
#elif __cplusplus >= 201103L
#pragma message "C++11 or later"
#else
#error "Need C++11 or later"
#endif
// Compiler
#if defined(__clang__)
#pragma message "Clang"
#elif defined(_MSC_VER)
#pragma message "MSVC"
#elif defined(__BORLANDC__)
#pragma message "BORLANDC"
#elif defined(__MINGW32__) || defined(__MINGW64__)
#pragma message "MINGW"
#elif defined(__INTEL_COMPILER)
#pragma message "ICC"
#elif defined(__GNUG__)
#pragma message "GCC"
#else
#pragma message "Unknown compiler"
#endif
/*
For native test, this main() is used.
For embedded test, overwrite by main() in library.
*/
__attribute__((weak)) int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
#ifdef GTEST_FILTER
::testing::GTEST_FLAG(filter) = GTEST_FILTER;
#endif
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
RUN_ALL_TESTS();
#pragma GCC diagnostic pop
// Always return zero-code and allow PlatformIO to parse results
return 0;
}

View file

@ -0,0 +1,473 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
*/
#include <gtest/gtest.h>
#include <M5Utility.hpp>
#include <M5Unified.hpp>
namespace {
using namespace m5::container;
void cb_basic_test()
{
SCOPED_TRACE("Basic");
FixedCircularBuffer<int, 4> rbuf;
EXPECT_TRUE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.capacity(), 4U);
EXPECT_EQ(rbuf.size(), 0U);
EXPECT_FALSE(rbuf.front());
EXPECT_FALSE(rbuf.back());
EXPECT_FALSE(rbuf.at(0));
EXPECT_FALSE(rbuf.at(1));
EXPECT_FALSE(rbuf.at(2));
EXPECT_FALSE(rbuf.at(3));
// H
// | | | | 1 |
// T
rbuf.push_front(1);
EXPECT_FALSE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.size(), 1U);
EXPECT_EQ(rbuf[0], 1);
EXPECT_EQ(rbuf.front(), 1);
EXPECT_EQ(rbuf.back(), 1);
// M5_LOGW("1> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
// H
// | | | | |
// T
auto r = rbuf.front();
EXPECT_TRUE(r);
int v = *r;
rbuf.pop_front();
EXPECT_EQ(v, 1);
EXPECT_TRUE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.size(), 0U);
EXPECT_FALSE(rbuf.front());
EXPECT_FALSE(rbuf.back());
// M5_LOGW("2> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
// H
// | 2 | | | |
// T
rbuf.push_back(2);
EXPECT_FALSE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.size(), 1U);
EXPECT_EQ(rbuf[0], 2);
EXPECT_EQ(rbuf.front(), 2);
EXPECT_EQ(rbuf.back(), 2);
// M5_LOGW("3> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
// H
// | | | | |
// T
r = rbuf.back();
EXPECT_TRUE(r);
v = *r;
rbuf.pop_back();
EXPECT_EQ(v, 2);
EXPECT_TRUE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.size(), 0U);
EXPECT_FALSE(rbuf.front());
EXPECT_FALSE(rbuf.back());
// M5_LOGW("3> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
// H
// | 4 | 5 | 6 | 3 |
// T
rbuf.push_front(3); // to front
EXPECT_FALSE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.size(), 1U);
EXPECT_EQ(rbuf.front(), 3);
EXPECT_EQ(rbuf.back(), 3);
rbuf.push_back(4); // to back
EXPECT_FALSE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.size(), 2U);
EXPECT_EQ(rbuf.front(), 3);
EXPECT_EQ(rbuf.back(), 4);
rbuf.push_back(5); // to back
EXPECT_FALSE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.size(), 3U);
EXPECT_EQ(rbuf.front(), 3);
EXPECT_EQ(rbuf.back(), 5);
rbuf.push_front(6); // to front
EXPECT_FALSE(rbuf.empty());
EXPECT_TRUE(rbuf.full());
EXPECT_EQ(rbuf.size(), 4U);
EXPECT_EQ(rbuf[0], 6);
EXPECT_EQ(rbuf[1], 3);
EXPECT_EQ(rbuf[2], 4);
EXPECT_EQ(rbuf[3], 5);
EXPECT_EQ(rbuf.front(), 6);
EXPECT_EQ(rbuf.back(), 5);
// M5_LOGW("4> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
// H
// | 4 | 7 | 6 | 3 |
// T
rbuf.push_front(7);
EXPECT_FALSE(rbuf.empty());
EXPECT_TRUE(rbuf.full());
EXPECT_EQ(rbuf.size(), 4U);
EXPECT_EQ(rbuf[0], 7);
EXPECT_EQ(rbuf[1], 6);
EXPECT_EQ(rbuf[2], 3);
EXPECT_EQ(rbuf[3], 4);
EXPECT_EQ(rbuf.front(), 7);
EXPECT_EQ(rbuf.back(), 4);
// M5_LOGW("5> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
// H
// | 4 | 8 | 6 | 3 |
// T
rbuf.push_back(8);
EXPECT_FALSE(rbuf.empty());
EXPECT_TRUE(rbuf.full());
EXPECT_EQ(rbuf.size(), 4U);
EXPECT_EQ(rbuf[0], 6);
EXPECT_EQ(rbuf[1], 3);
EXPECT_EQ(rbuf[2], 4);
EXPECT_EQ(rbuf[3], 8);
EXPECT_EQ(rbuf.front(), 6);
EXPECT_EQ(rbuf.back(), 8);
// M5_LOGW("5> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
// H
// | | | | |
// T
rbuf.pop_back();
EXPECT_EQ(rbuf.back(), 4);
rbuf.pop_back();
EXPECT_EQ(rbuf.back(), 3);
rbuf.pop_back();
EXPECT_EQ(rbuf.back(), 6);
rbuf.pop_back();
EXPECT_TRUE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.size(), 0U);
EXPECT_FALSE(rbuf.front());
EXPECT_FALSE(rbuf.back());
// M5_LOGW("6> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
// H
// | 11| 12| 13| 10 |
// T
rbuf.push_back(9);
rbuf.push_back(10);
rbuf.push_back(11);
rbuf.push_back(12);
rbuf.push_back(13);
EXPECT_FALSE(rbuf.empty());
EXPECT_TRUE(rbuf.full());
EXPECT_EQ(rbuf.size(), 4U);
EXPECT_EQ(rbuf[0], 10);
EXPECT_EQ(rbuf[1], 11);
EXPECT_EQ(rbuf[2], 12);
EXPECT_EQ(rbuf[3], 13);
EXPECT_EQ(rbuf.front(), 10);
EXPECT_EQ(rbuf.back(), 13);
// M5_LOGW("7> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
// H
// | | | 13| |
// T
rbuf.pop_front();
EXPECT_EQ(rbuf.front(), 11);
EXPECT_EQ(rbuf.back(), 13);
rbuf.pop_front();
EXPECT_EQ(rbuf.front(), 12);
EXPECT_EQ(rbuf.back(), 13);
rbuf.pop_front();
EXPECT_FALSE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.size(), 1U);
EXPECT_EQ(rbuf.front(), 13);
EXPECT_EQ(rbuf.back(), 13);
// M5_LOGW("8> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
// H
// | | | | |
// T
rbuf.clear();
EXPECT_TRUE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.size(), 0U);
EXPECT_FALSE(rbuf.front());
EXPECT_FALSE(rbuf.back());
// M5_LOGW("9> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
// H
// |111|111|111|111|
// T
rbuf.fill(111);
EXPECT_TRUE(rbuf.full());
EXPECT_EQ(rbuf.size(), 4U);
EXPECT_EQ(rbuf[0], 111);
EXPECT_EQ(rbuf[1], 111);
EXPECT_EQ(rbuf[2], 111);
EXPECT_EQ(rbuf[3], 111);
EXPECT_EQ(rbuf.front(), 111);
EXPECT_EQ(rbuf.back(), 111);
// M5_LOGW("A> %zu/%zu/%u", rbuf._head, rbuf._tail, rbuf._full);
}
void cb_constructor_test()
{
SCOPED_TRACE("Constructor");
std::vector<int> table(100);
for (int i = 0; i < 100; ++i) {
table[i] = i;
}
{
FixedCircularBuffer<int, 8> rbuf(2U, 52);
EXPECT_FALSE(rbuf.empty());
EXPECT_FALSE(rbuf.full());
EXPECT_EQ(rbuf.capacity(), 8U);
EXPECT_EQ(rbuf.size(), 2U);
EXPECT_EQ(rbuf[0], 52);
EXPECT_EQ(rbuf[1], 52);
}
{
FixedCircularBuffer<int, 8> rbuf(100U, 52);
EXPECT_FALSE(rbuf.empty());
EXPECT_TRUE(rbuf.full());
EXPECT_EQ(rbuf.capacity(), 8U);
EXPECT_EQ(rbuf.size(), 8U);
EXPECT_EQ(rbuf.front(), 52);
EXPECT_EQ(rbuf.back(), 52);
}
{
FixedCircularBuffer<int, 10> rbuf2({9, 8, 7, 6, 5});
EXPECT_FALSE(rbuf2.empty());
EXPECT_FALSE(rbuf2.full());
EXPECT_EQ(rbuf2.capacity(), 10U);
EXPECT_EQ(rbuf2.size(), 5U);
EXPECT_EQ(rbuf2[0], 9);
EXPECT_EQ(rbuf2[1], 8);
EXPECT_EQ(rbuf2[2], 7);
EXPECT_EQ(rbuf2[3], 6);
EXPECT_EQ(rbuf2[4], 5);
}
{
FixedCircularBuffer<int, 8> rbuf2(table.begin(), table.end());
EXPECT_FALSE(rbuf2.empty());
EXPECT_TRUE(rbuf2.full());
EXPECT_EQ(rbuf2.capacity(), 8U);
EXPECT_EQ(rbuf2.size(), 8U);
EXPECT_EQ(rbuf2.front(), 100 - 8);
EXPECT_EQ(rbuf2.back(), 99);
}
FixedCircularBuffer<float, 3> rbuf3 = {1.1f, 2.2f, 3.3f};
EXPECT_FALSE(rbuf3.empty());
EXPECT_TRUE(rbuf3.full());
EXPECT_EQ(rbuf3.capacity(), 3U);
EXPECT_EQ(rbuf3.size(), 3U);
EXPECT_FLOAT_EQ(rbuf3[0], 1.1f);
EXPECT_FLOAT_EQ(rbuf3[1], 2.2f);
EXPECT_FLOAT_EQ(rbuf3[2], 3.3f);
}
void cb_read()
{
SCOPED_TRACE("Read");
FixedCircularBuffer<int, 128> rb;
int buf[128] = {};
size_t rcnt = 0;
// empty
rcnt = rb.read(buf, 64);
EXPECT_EQ(rcnt, 0U);
// fill
for (size_t i = 0; i < rb.capacity(); ++i) {
rb.push_back(i);
}
EXPECT_EQ(rb.size(), 128U);
// read half
rcnt = rb.read(buf, 64);
EXPECT_EQ(rcnt, 64U);
for (int i = 0; i < 64; ++i) {
EXPECT_EQ(buf[i], i) << i;
}
EXPECT_EQ(rb.size(), 128U);
EXPECT_EQ(rb.front(), 0);
EXPECT_EQ(rb.back(), 127);
// pop half
for (int i = 0; i < 64; ++i) {
rb.pop_front();
}
// read
rcnt = rb.read(buf, 64);
EXPECT_EQ(rcnt, 64U);
for (int i = 0; i < 64; ++i) {
EXPECT_EQ(buf[i], i + 64) << (i + 64);
}
EXPECT_EQ(rb.size(), 64U);
EXPECT_EQ(rb.front(), 64);
EXPECT_EQ(rb.back(), 127);
// pop half
for (int i = 0; i < 32; ++i) {
rb.pop_front();
}
// read
rcnt = rb.read(buf, 64);
EXPECT_EQ(rcnt, 32U);
for (int i = 0; i < 32; ++i) {
EXPECT_EQ(buf[i], i + 96) << (i + 96);
}
EXPECT_EQ(rb.size(), 32U);
EXPECT_EQ(rb.front(), 96);
EXPECT_EQ(rb.back(), 127);
// push
for (int i = 0; i < 100; ++i) {
rb.push_back(i + 128);
}
// read
rcnt = rb.read(buf, 64);
EXPECT_EQ(rcnt, 64U);
for (int i = 0; i < 64; ++i) {
EXPECT_EQ(buf[i], i + 100) << (i + 100);
}
EXPECT_EQ(rb.size(), 128U);
EXPECT_EQ(*rb.front(), 227 - 127);
EXPECT_EQ(*rb.back(), 227);
}
void cb_iterator_test()
{
SCOPED_TRACE("Iterators");
FixedCircularBuffer<int, 4> rb = {0, 1, 2};
FixedCircularBuffer<int, 6> rb2;
// empty
EXPECT_EQ(rb2.begin(), rb2.end());
EXPECT_EQ(rb2.rbegin(), rb2.rend());
EXPECT_EQ(rb2.cbegin(), rb2.cend());
EXPECT_EQ(rb2.crbegin(), rb2.crend());
//
int c = 0;
for (auto it = rb.begin(); it != rb.end(); ++it) {
EXPECT_EQ(*it, c++);
}
c = 2;
for (auto it = rb.rbegin(); it != rb.rend(); ++it) {
EXPECT_EQ(*it, c--);
}
c = 0;
for (auto it = rb.cbegin(); it != rb.cend(); ++it) {
EXPECT_EQ(*it, c++);
}
c = 2;
for (auto it = rb.crbegin(); it != rb.crend(); ++it) {
EXPECT_EQ(*it, c--);
}
//
rb.clear();
rb.push_back(9);
rb.push_back(8);
rb.push_back(7);
rb.push_back(6);
EXPECT_TRUE(rb.full());
c = 9;
for (auto&& e : rb) {
EXPECT_EQ(e, c--);
}
c = 6;
for (auto it = rb.crbegin(); it != rb.crend(); ++it) {
EXPECT_EQ(*it, c++);
}
// make the rounds
rb.push_back(5);
rb.push_back(4);
c = 7;
for (auto&& e : rb) {
EXPECT_EQ(e, c--);
}
c = 4;
for (auto it = rb.crbegin(); it != rb.crend(); ++it) {
EXPECT_EQ(*it, c++);
}
c = 7;
std::for_each(std::begin(rb), std::end(rb), [&c](const int& e) { EXPECT_EQ(e, c--); });
#if __cplusplus >= 201402L
// std::rbegin, rend require C++14 or later
c = 4;
std::for_each(std::rbegin(rb), std::rend(rb), [&c](const int& e) { EXPECT_EQ(e, c++); });
#endif
c = 4;
for (auto it = rb.cend(); it != rb.cbegin(); /**/) {
--it;
EXPECT_EQ(*it, c++);
}
c = 7;
for (auto it = rb.rend(); it != rb.rbegin(); /**/) {
--it;
EXPECT_EQ(*it, c--);
}
{
auto it = rb.begin();
EXPECT_EQ(*it++, 7);
EXPECT_EQ(*it--, 6);
EXPECT_EQ(*++it, 6);
EXPECT_EQ(*--it, 7);
auto itr = rb.rbegin();
EXPECT_EQ(*itr++, 4);
EXPECT_EQ(*itr--, 5);
EXPECT_EQ(*++itr, 5);
EXPECT_EQ(*--itr, 4);
}
}
} // namespace
TEST(Utility, CircularBuffer)
{
cb_basic_test();
cb_constructor_test();
cb_read();
cb_iterator_test();
}

View file

@ -0,0 +1,104 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
*/
#include <gtest/gtest.h>
#include <M5Utility.hpp>
TEST(Utility, Conversion)
{
{
SCOPED_TRACE("8bit:4");
std::pair<uint8_t, int8_t> table[] = {
// zero
{0x00, 0},
// only sign bit
{0x08, -8},
// all bits
{0x0F, -1},
// all signed bits
{0x07, 7},
};
for (auto&& e : table) {
EXPECT_EQ(m5::utility::unsigned_to_signed<4>(e.first), e.second) << e.first;
}
}
{
SCOPED_TRACE("8bit:8");
std::pair<uint8_t, int8_t> table[] = {
// zero
{0, 0},
// only sign bit
{0x80, -128},
// all bits
{0xFF, -1},
// all signed bits
{0x7F, 127},
};
for (auto&& e : table) {
EXPECT_EQ(m5::utility::unsigned_to_signed<8>(e.first), e.second) << e.first;
}
}
{
SCOPED_TRACE("16bit:10");
std::pair<uint16_t, int16_t> table[] = {
// zero
{0x0000, 0},
// only sign bit
{0x0200, -512},
// all bits
{0x03FF, -1},
// all signed bits
{0x01FF, 511},
//
{0x0100, 256},
};
for (auto&& e : table) {
EXPECT_EQ(m5::utility::unsigned_to_signed<10>(e.first), e.second) << e.first;
}
}
{
SCOPED_TRACE("32bit:20");
std::pair<uint32_t, int32_t> table[] = {
// zero
{0x00000000, 0},
// only sign bit
{0x00080000, -524288},
// all bits
{0x000FFFFF, -1},
// all signed bits
{0x0007FFFF, 524287},
//
{0x00040000, 262144},
};
for (auto&& e : table) {
EXPECT_EQ(m5::utility::unsigned_to_signed<20>(e.first), e.second) << e.first;
}
}
{
SCOPED_TRACE("64bit:47");
std::pair<uint64_t, int64_t> table[] = {
// zero
{0x0000000000000000ULL, 0},
// only sign bit
{0x0000400000000000ULL, -70368744177664LL},
// all bits
{0x00007FFFFFFFFFFFULL, -1LL},
// all signed bits
{0x00003FFFFFFFFFFFULL, 70368744177663LL},
//
{0x0000200000000000ULL, 35184372088832},
};
for (auto&& e : table) {
EXPECT_EQ(m5::utility::unsigned_to_signed<47>(e.first), e.second) << e.second;
}
}
}

View file

@ -0,0 +1,147 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
*/
#include <gtest/gtest.h>
#include <M5Utility.hpp>
#include <M5Unified.hpp>
namespace {
struct CRCType {
const char* name;
const uint32_t result;
const uint32_t poly;
const uint32_t init;
const bool refIn;
const bool refOut;
const uint32_t xorout;
};
// See also
// https://crccalc.com/?crc=0123456789abcdef&method=crc8&datatype=hex&outtype=0
CRCType crc8_table[] = {
// name, result, poly, init, refIn, refOut, xorout
{"CRC-8", 0x1E, 0x07, 0x00, false, false, 0x00}, {"CRC-8/CDMA2000", 0xF0, 0x9B, 0xFF, false, false, 0x00},
{"CRC-8/DARC", 0x4E, 0x39, 0x00, true, true, 0x00}, {"CRC-8/DVB-S2", 0x7D, 0xD5, 0x00, false, false, 0x00},
{"CRC-8/EBU", 0x17, 0x1D, 0xFF, true, true, 0x00}, {"CRC-8/I-CODE", 0x6C, 0x1D, 0xFD, false, false, 0x00},
{"CRC-8/ITU", 0x4B, 0x07, 0x00, false, false, 0x55}, {"CRC-8/MAXIM", 0xDD, 0x31, 0x00, true, true, 0x00},
{"CRC-8/ROHC", 0xFC, 0x07, 0xFF, true, true, 0x00}, {"CRC-8/WCDMA", 0x22, 0x9B, 0x00, true, true, 0x00},
};
CRCType crc16_table[] = {
// name, result, poly, init, refIn, refOut, xorout
{"CRC-16/ARC", 0xF3A6, 0x8005, 0x0000, true, true, 0x0000},
{"CRC-16/AUG-CCITT", 0x486C, 0x1021, 0x1D0F, false, false, 0x0000},
{"CRC-16/BUYPASS", 0x2951, 0x8005, 0x0000, false, false, 0x0000},
{"CRC-16/CCITT-FALSE", 0x986B, 0x1021, 0xFFFF, false, false, 0x0000},
{"CRC-16/CDMA2000", 0x593C, 0xC867, 0xFFFF, false, false, 0x0000},
{"CRC-16/DDS-110", 0x27B1, 0x8005, 0x800D, false, false, 0x0000},
{"CRC-16/DECT-R", 0xE83B, 0x0589, 0x0000, false, false, 0x0001},
{"CRC-16/DECT-X", 0xE83A, 0x0589, 0x0000, false, false, 0x0000},
{"CRC-16/DNP", 0xDFCE, 0x3D65, 0x0000, true, true, 0xFFFF},
{"CRC-16/EN-13757", 0x984F, 0x3D65, 0x0000, false, false, 0xFFFF},
{"CRC-16/GENIBUS", 0x6794, 0x1021, 0xFFFF, false, false, 0xFFFF},
{"CRC-16/KERMIT", 0x5A3C, 0x1021, 0x0000, true, true, 0x0000},
{"CRC-16/MAXIM", 0x0C59, 0x8005, 0x0000, true, true, 0xFFFF},
{"CRC-16/MCRF4XX", 0x26B0, 0x1021, 0xFFFF, true, true, 0x0000},
{"CRC-16/MODBUS", 0xF8E6, 0x8005, 0xFFFF, true, true, 0x0000},
{"CRC-16/RIELLO", 0x077C, 0x1021, 0xB2AA, true, true, 0x0000}, // 0x554D
{"CRC-16/T10-DIF", 0x4FF0, 0x8BB7, 0x0000, false, false, 0x0000},
{"CRC-16/TELEDISK", 0x3267, 0xA097, 0x0000, false, false, 0x0000},
{"CRC-16/TMS37157", 0x5A4B, 0x1021, 0x89EC, true, true, 0x0000}, // 0x3791
{"CRC-16/USB", 0x0719, 0x8005, 0xFFFF, true, true, 0xFFFF},
{"CRC-16/X-25", 0xD94F, 0x1021, 0xFFFF, true, true, 0xFFFF},
{"CRC-16/XMODEM", 0xA955, 0x1021, 0x0000, false, false, 0x0000},
{"CRC-A", 0x0F06, 0x1021, 0xC6C6, true, true, 0x0000}, // 0x6363
};
const std::vector<uint8_t> tdata = {
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
};
}; // namespace
using namespace m5::utility;
TEST(Utility, CRC8)
{
for (auto&& e : crc8_table) {
SCOPED_TRACE(e.name);
CRC8 crc(e.init, e.poly, e.refIn, e.refOut, e.xorout);
auto v = crc.range(tdata.data(), tdata.size());
auto u = crc.update(tdata.data(), tdata.size());
EXPECT_EQ(v, e.result);
EXPECT_EQ(crc.value(), e.result);
EXPECT_EQ(u, crc.value());
v = crc.range(tdata.data(), tdata.size());
u = crc.update(tdata.data(), tdata.size());
EXPECT_EQ(v, e.result);
EXPECT_NE(v, u);
}
}
TEST(Utility, CRC16)
{
for (auto&& e : crc16_table) {
SCOPED_TRACE(e.name);
CRC16 crc(e.init, e.poly, e.refIn, e.refOut, e.xorout);
auto v = crc.update(tdata.data(), tdata.size());
EXPECT_EQ(crc.value(), e.result);
EXPECT_EQ(v, crc.value());
}
}
// Test whether calculation from the whole and calculation from split chunks are
// equivalent
TEST(Utility, Chunk)
{
constexpr uint8_t d8[32] = {0x04, 0x67, 0xfc, 0x4d, 0xf4, 0xe7, 0x9c, 0x3b, 0x05, 0xb8, 0xad,
0x31, 0x97, 0xb1, 0x21, 0x72, 0x59, 0x5d, 0x80, 0x26, 0x66, 0x0c,
0x12, 0xa9, 0x53, 0xa6, 0x70, 0x87, 0x91, 0x5d, 0xa4, 0x9a};
;
// CRC8
for (auto&& e : crc8_table) {
SCOPED_TRACE(e.name);
uint8_t crc_all = CRC8::calculate(d8, m5::stl::size(d8), e.init, e.poly, e.refIn, e.refOut, e.xorout);
CRC8 crc(e.init, e.poly, e.refIn, e.refOut, e.xorout);
uint8_t crc_chunk{};
for (int i = 0; i < 4; ++i) {
crc_chunk = crc.update(d8 + (i * 8), 8);
// M5_LOGW("%s:i:%d:[%x/%x]", e.name, i, crc_chunk,
// crc.range(d8 + (i * 8), 8));
if (i == 0) {
EXPECT_EQ(crc_chunk, crc.range(d8 + (i * 8), 8));
} else {
EXPECT_NE(crc_chunk, crc.range(d8 + (i * 8), 8));
}
}
EXPECT_EQ(crc_all, crc_chunk);
EXPECT_EQ(crc_all, crc.value());
}
// CRC16
for (auto&& e : crc16_table) {
SCOPED_TRACE(e.name);
uint16_t crc_all = CRC16::calculate(d8, m5::stl::size(d8), e.init, e.poly, e.refIn, e.refOut, e.xorout);
CRC16 crc(e.init, e.poly, e.refIn, e.refOut, e.xorout);
uint16_t crc_chunk{};
for (int i = 0; i < 4; ++i) {
crc_chunk = crc.update(d8 + (i * 8), 8);
if (i == 0) {
EXPECT_EQ(crc_chunk, crc.range(d8 + (i * 8), 8));
} else {
EXPECT_NE(crc_chunk, crc.range(d8 + (i * 8), 8));
}
}
EXPECT_EQ(crc_all, crc_chunk);
EXPECT_EQ(crc_all, crc_chunk);
}
}

View file

@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
main for UnitTest on embedded
*/
#include <gtest/gtest.h>
#include <M5Unified.h>
#pragma message "Embedded setup/loop"
#if __has_include(<esp_idf_version.h>)
#include <esp_idf_version.h>
#else // esp_idf_version.h has been introduced in Arduino 1.0.5 (ESP-IDF3.3)
#define ESP_IDF_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
#define ESP_IDF_VERSION ESP_IDF_VERSION_VAL(3, 2, 0)
#endif
namespace {
auto& lcd = M5.Display;
}
void test()
{
lcd.fillRect(0, 0, lcd.width() >> 1, lcd.height(), RUN_ALL_TESTS() ? TFT_RED : TFT_GREEN);
}
void setup()
{
M5.begin();
M5_LOGI("CPP %ld", __cplusplus);
M5_LOGI("ESP-IDF Version %d.%d.%d", (ESP_IDF_VERSION >> 16) & 0xFF, (ESP_IDF_VERSION >> 8) & 0xFF,
ESP_IDF_VERSION & 0xFF);
M5_LOGI("Heap: %u", esp_get_free_heap_size());
lcd.clear(TFT_DARKGRAY);
::testing::InitGoogleTest();
#ifdef GTEST_FILTER
::testing::GTEST_FLAG(filter) = GTEST_FILTER;
#endif
}
void loop()
{
test();
while (true) {
delay(10000);
}
}

View file

@ -0,0 +1,19 @@
#include <gtest/gtest.h>
// #include <stdexcept>
// #define TL_ASSERT(cond) if (!(cond)) { throw std::runtime_error(std::string("assertion failure")); }
#include <m5_utility/stl/expected.hpp>
#if 0
TEST(Expected, Assertions) {
m5::stl::expected<int,int> o1 = 42;
EXPECT_ANY_THROW(o1.error());
m5::stl::expected<int,int> o2 {m5::stl::unexpect, 0};
EXPECT_ANY_THROW(*o2);
struct foo { int bar; };
m5::stl::expected<struct foo,int> o3 {m5::stl::unexpect, 0};
EXPECT_ANY_THROW(o3->bar);
}
#endif

View file

@ -0,0 +1,86 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/expected.hpp>
TEST(Expected, Assignment)
{
m5::stl::expected<int, int> e1 = 42;
m5::stl::expected<int, int> e2 = 17;
m5::stl::expected<int, int> e3 = 21;
m5::stl::expected<int, int> e4 = m5::stl::make_unexpected(42);
m5::stl::expected<int, int> e5 = m5::stl::make_unexpected(17);
m5::stl::expected<int, int> e6 = m5::stl::make_unexpected(21);
e1 = e2;
EXPECT_TRUE(e1);
EXPECT_TRUE(*e1 == 17);
EXPECT_TRUE(e2);
EXPECT_TRUE(*e2 == 17);
e1 = std::move(e2);
EXPECT_TRUE(e1);
EXPECT_TRUE(*e1 == 17);
EXPECT_TRUE(e2);
EXPECT_TRUE(*e2 == 17);
e1 = 42;
EXPECT_TRUE(e1);
EXPECT_TRUE(*e1 == 42);
auto unex = m5::stl::make_unexpected(12);
e1 = unex;
EXPECT_TRUE(!e1);
EXPECT_TRUE(e1.error() == 12);
e1 = m5::stl::make_unexpected(42);
EXPECT_TRUE(!e1);
EXPECT_TRUE(e1.error() == 42);
e1 = e3;
EXPECT_TRUE(e1);
EXPECT_TRUE(*e1 == 21);
e4 = e5;
EXPECT_TRUE(!e4);
EXPECT_TRUE(e4.error() == 17);
e4 = std::move(e6);
EXPECT_TRUE(!e4);
EXPECT_TRUE(e4.error() == 21);
e4 = e1;
EXPECT_TRUE(e4);
EXPECT_TRUE(*e4 == 21);
}
TEST(Expected, AssignmentDeletion)
{
struct has_all {
has_all() = default;
has_all(const has_all &) = default;
has_all(has_all &&) noexcept = default;
has_all &operator=(const has_all &) = default;
};
m5::stl::expected<has_all, has_all> e1 = {};
m5::stl::expected<has_all, has_all> e2 = {};
e1 = e2;
struct except_move {
except_move() = default;
except_move(const except_move &) = default;
except_move(except_move &&) noexcept(false)
{
}
except_move &operator=(const except_move &) = default;
};
// m5::stl::expected<except_move, except_move> e3 = {};
// m5::stl::expected<except_move, except_move> e4 = {};
// e3 = e4; //should not compile
}

View file

@ -0,0 +1,218 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/expected.hpp>
#include <string>
// Old versions of GCC don't have the correct trait names. Could fix them up if needs be.
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__))
// nothing for now
#else
TEST(Expected, Triviality)
{
EXPECT_TRUE((std::is_trivially_copy_constructible<m5::stl::expected<int, int> >::value));
EXPECT_TRUE((std::is_trivially_copy_assignable<m5::stl::expected<int, int> >::value));
EXPECT_TRUE((std::is_trivially_move_constructible<m5::stl::expected<int, int> >::value));
EXPECT_TRUE((std::is_trivially_move_assignable<m5::stl::expected<int, int> >::value));
EXPECT_TRUE((std::is_trivially_destructible<m5::stl::expected<int, int> >::value));
EXPECT_TRUE((std::is_trivially_copy_constructible<m5::stl::expected<void, int> >::value));
EXPECT_TRUE((std::is_trivially_move_constructible<m5::stl::expected<void, int> >::value));
EXPECT_TRUE((std::is_trivially_destructible<m5::stl::expected<void, int> >::value));
{
struct T {
T(const T&) = default;
T(T&&) = default;
T& operator=(const T&) = default;
T& operator=(T&&) = default;
~T() = default;
};
EXPECT_TRUE((std::is_trivially_copy_constructible<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((std::is_trivially_copy_assignable<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((std::is_trivially_move_constructible<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((std::is_trivially_move_assignable<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((std::is_trivially_destructible<m5::stl::expected<T, int> >::value));
}
{
struct T {
T(const T&)
{
}
T(T&&)
{
}
T& operator=(const T&)
{
return *this;
}
T& operator=(T&&)
{
return *this;
}
~T()
{
}
};
EXPECT_TRUE(!(std::is_trivially_copy_constructible<m5::stl::expected<T, int> >::value));
EXPECT_TRUE(!(std::is_trivially_copy_assignable<m5::stl::expected<T, int> >::value));
EXPECT_TRUE(!(std::is_trivially_move_constructible<m5::stl::expected<T, int> >::value));
EXPECT_TRUE(!(std::is_trivially_move_assignable<m5::stl::expected<T, int> >::value));
EXPECT_TRUE(!(std::is_trivially_destructible<m5::stl::expected<T, int> >::value));
}
}
TEST(Expected, Deletion)
{
EXPECT_TRUE((std::is_copy_constructible<m5::stl::expected<int, int> >::value));
EXPECT_TRUE((std::is_copy_assignable<m5::stl::expected<int, int> >::value));
EXPECT_TRUE((std::is_move_constructible<m5::stl::expected<int, int> >::value));
EXPECT_TRUE((std::is_move_assignable<m5::stl::expected<int, int> >::value));
EXPECT_TRUE((std::is_destructible<m5::stl::expected<int, int> >::value));
{
struct T {
T() = default;
};
EXPECT_TRUE((std::is_default_constructible<m5::stl::expected<T, int> >::value));
}
{
struct T {
T(int)
{
}
};
EXPECT_TRUE(!(std::is_default_constructible<m5::stl::expected<T, int> >::value));
}
{
struct T {
T(const T&) = default;
T(T&&) = default;
T& operator=(const T&) = default;
T& operator=(T&&) = default;
~T() = default;
};
EXPECT_TRUE((std::is_copy_constructible<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((std::is_copy_assignable<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((std::is_move_constructible<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((std::is_move_assignable<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((std::is_destructible<m5::stl::expected<T, int> >::value));
}
{
struct T {
T(const T&) = delete;
T(T&&) = delete;
T& operator=(const T&) = delete;
T& operator=(T&&) = delete;
};
EXPECT_TRUE(!(std::is_copy_constructible<m5::stl::expected<T, int> >::value));
EXPECT_TRUE(!(std::is_copy_assignable<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((!std::is_move_constructible<m5::stl::expected<T, int> >::value));
EXPECT_TRUE(!(std::is_move_assignable<m5::stl::expected<T, int> >::value));
}
{
struct T {
T(const T&) = delete;
T(T&&) = default;
T& operator=(const T&) = delete;
T& operator=(T&&) = default;
};
EXPECT_TRUE(!(std::is_copy_constructible<m5::stl::expected<T, int> >::value));
EXPECT_TRUE(!(std::is_copy_assignable<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((std::is_move_constructible<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((std::is_move_assignable<m5::stl::expected<T, int> >::value));
}
{
struct T {
T(const T&) = default;
T(T&&) = delete;
T& operator=(const T&) = default;
T& operator=(T&&) = delete;
};
EXPECT_TRUE((std::is_copy_constructible<m5::stl::expected<T, int> >::value));
EXPECT_TRUE((std::is_copy_assignable<m5::stl::expected<T, int> >::value));
}
{
m5::stl::expected<int, int> e;
EXPECT_TRUE(std::is_default_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_move_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_assignable<decltype(e)>::value);
EXPECT_TRUE(std::is_move_assignable<decltype(e)>::value);
EXPECT_TRUE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
EXPECT_TRUE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
#if !defined(TL_EXPECTED_GCC49)
EXPECT_TRUE(std::is_trivially_move_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_trivially_move_assignable<decltype(e)>::value);
#endif
}
{
m5::stl::expected<int, std::string> e;
EXPECT_TRUE(std::is_default_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_move_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_assignable<decltype(e)>::value);
EXPECT_TRUE(std::is_move_assignable<decltype(e)>::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
#if !defined(TL_EXPECTED_GCC49)
EXPECT_TRUE(!std::is_trivially_move_constructible<decltype(e)>::value);
EXPECT_TRUE(!std::is_trivially_move_assignable<decltype(e)>::value);
#endif
}
{
m5::stl::expected<std::string, int> e;
EXPECT_TRUE(std::is_default_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_move_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_assignable<decltype(e)>::value);
EXPECT_TRUE(std::is_move_assignable<decltype(e)>::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
#if !defined(TL_EXPECTED_GCC49)
EXPECT_TRUE(!std::is_trivially_move_constructible<decltype(e)>::value);
EXPECT_TRUE(!std::is_trivially_move_assignable<decltype(e)>::value);
#endif
}
{
m5::stl::expected<std::string, std::string> e;
EXPECT_TRUE(std::is_default_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_move_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_assignable<decltype(e)>::value);
EXPECT_TRUE(std::is_move_assignable<decltype(e)>::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
#if !defined(TL_EXPECTED_GCC49)
EXPECT_TRUE(!std::is_trivially_move_constructible<decltype(e)>::value);
EXPECT_TRUE(!std::is_trivially_move_assignable<decltype(e)>::value);
#endif
}
}
#endif

View file

@ -0,0 +1,7 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/expected.hpp>
TEST(Expected, Constexpr)
{
// TODO
}

View file

@ -0,0 +1,136 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/expected.hpp>
#include <type_traits>
#include <vector>
#include <string>
struct takes_init_and_variadic {
std::vector<int> v;
std::tuple<int, int> t;
template <class... Args>
takes_init_and_variadic(std::initializer_list<int> l, Args &&...args) : v(l), t(std::forward<Args>(args)...)
{
}
};
TEST(Expected, Constructors)
{
{
m5::stl::expected<int, int> e;
EXPECT_TRUE(e);
EXPECT_TRUE(e == 0);
}
{
m5::stl::expected<int, int> e = m5::stl::make_unexpected(0);
EXPECT_TRUE(!e);
EXPECT_TRUE(e.error() == 0);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 0);
EXPECT_TRUE(!e);
EXPECT_TRUE(e.error() == 0);
}
{
m5::stl::expected<int, int> e(m5::stl::in_place, 42);
EXPECT_TRUE(e);
EXPECT_TRUE(e == 42);
}
{
m5::stl::expected<std::vector<int>, int> e(m5::stl::in_place, {0, 1});
EXPECT_TRUE(e);
EXPECT_TRUE((*e)[0] == 0);
EXPECT_TRUE((*e)[1] == 1);
}
{
m5::stl::expected<std::tuple<int, int>, int> e(m5::stl::in_place, 0, 1);
EXPECT_TRUE(e);
EXPECT_TRUE(std::get<0>(*e) == 0);
EXPECT_TRUE(std::get<1>(*e) == 1);
}
{
m5::stl::expected<takes_init_and_variadic, int> e(m5::stl::in_place, {0, 1}, 2, 3);
EXPECT_TRUE(e);
EXPECT_TRUE(e->v[0] == 0);
EXPECT_TRUE(e->v[1] == 1);
EXPECT_TRUE(std::get<0>(e->t) == 2);
EXPECT_TRUE(std::get<1>(e->t) == 3);
}
{
m5::stl::expected<int, int> e;
EXPECT_TRUE(std::is_default_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_move_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_assignable<decltype(e)>::value);
EXPECT_TRUE(std::is_move_assignable<decltype(e)>::value);
EXPECT_TRUE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
EXPECT_TRUE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
#if !defined(TL_EXPECTED_GCC49)
EXPECT_TRUE(std::is_trivially_move_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_trivially_move_assignable<decltype(e)>::value);
#endif
}
{
m5::stl::expected<int, std::string> e;
EXPECT_TRUE(std::is_default_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_move_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_assignable<decltype(e)>::value);
EXPECT_TRUE(std::is_move_assignable<decltype(e)>::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
#if !defined(TL_EXPECTED_GCC49)
EXPECT_TRUE(!std::is_trivially_move_constructible<decltype(e)>::value);
EXPECT_TRUE(!std::is_trivially_move_assignable<decltype(e)>::value);
#endif
}
{
m5::stl::expected<std::string, int> e;
EXPECT_TRUE(std::is_default_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_move_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_assignable<decltype(e)>::value);
EXPECT_TRUE(std::is_move_assignable<decltype(e)>::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
#if !defined(TL_EXPECTED_GCC49)
EXPECT_TRUE(!std::is_trivially_move_constructible<decltype(e)>::value);
EXPECT_TRUE(!std::is_trivially_move_assignable<decltype(e)>::value);
#endif
}
{
m5::stl::expected<std::string, std::string> e;
EXPECT_TRUE(std::is_default_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_move_constructible<decltype(e)>::value);
EXPECT_TRUE(std::is_copy_assignable<decltype(e)>::value);
EXPECT_TRUE(std::is_move_assignable<decltype(e)>::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
EXPECT_TRUE(!TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
#if !defined(TL_EXPECTED_GCC49)
EXPECT_TRUE(!std::is_trivially_move_constructible<decltype(e)>::value);
EXPECT_TRUE(!std::is_trivially_move_assignable<decltype(e)>::value);
#endif
}
{
m5::stl::expected<void, int> e;
EXPECT_TRUE(e);
}
{
m5::stl::expected<void, int> e(m5::stl::unexpect, 42);
EXPECT_TRUE(!e);
EXPECT_TRUE(e.error() == 42);
}
}

View file

@ -0,0 +1,51 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/expected.hpp>
#include <memory>
#include <vector>
#include <tuple>
namespace {
struct takes_init_and_variadic {
std::vector<int> v;
std::tuple<int, int> t;
template <class... Args>
takes_init_and_variadic(std::initializer_list<int> l, Args &&...args) : v(l), t(std::forward<Args>(args)...)
{
}
};
} // namespace
TEST(Expected, Emplace)
{
{
m5::stl::expected<std::unique_ptr<int>, int> e;
e.emplace(new int{42});
EXPECT_TRUE(e);
EXPECT_TRUE(**e == 42);
}
{
m5::stl::expected<std::vector<int>, int> e;
e.emplace({0, 1});
EXPECT_TRUE(e);
EXPECT_TRUE((*e)[0] == 0);
EXPECT_TRUE((*e)[1] == 1);
}
{
m5::stl::expected<std::tuple<int, int>, int> e;
e.emplace(2, 3);
EXPECT_TRUE(e);
EXPECT_TRUE(std::get<0>(*e) == 2);
EXPECT_TRUE(std::get<1>(*e) == 3);
}
{
m5::stl::expected<::takes_init_and_variadic, int> e = m5::stl::make_unexpected(0);
e.emplace({0, 1}, 2, 3);
EXPECT_TRUE(e);
EXPECT_TRUE(e->v[0] == 0);
EXPECT_TRUE(e->v[1] == 1);
EXPECT_TRUE(std::get<0>(e->t) == 2);
EXPECT_TRUE(std::get<1>(e->t) == 3);
}
}

View file

@ -0,0 +1,832 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/expected.hpp>
#define TOKENPASTE(x, y) x##y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#undef STATIC_REQUIRE
#define STATIC_REQUIRE(e) \
constexpr bool TOKENPASTE2(rqure, __LINE__) = e; \
(void)TOKENPASTE2(rqure, __LINE__); \
EXPECT_TRUE(e);
TEST(Expected, MapExtensions)
{
auto mul2 = [](int a) { return a * 2; };
auto ret_void = [](int a) { (void)a; };
{
m5::stl::expected<int, int> e = 21;
auto ret = e.map(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.map(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).map(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).map(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.map(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.map(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).map(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).map(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = e.map(ret_void);
EXPECT_TRUE(ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.map(ret_void);
EXPECT_TRUE(ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).map(ret_void);
EXPECT_TRUE(ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).map(ret_void);
EXPECT_TRUE(ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.map(ret_void);
EXPECT_TRUE(!ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.map(ret_void);
EXPECT_TRUE(!ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).map(ret_void);
EXPECT_TRUE(!ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).map(ret_void);
EXPECT_TRUE(!ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
// mapping functions which return references
{
m5::stl::expected<int, int> e(42);
auto ret = e.map([](int& i) -> int& { return i; });
EXPECT_TRUE(ret);
EXPECT_TRUE(ret == 42);
}
}
TEST(Expected, MapErrorExtensions)
{
auto mul2 = [](int a) { return a * 2; };
auto ret_void = [](int a) { (void)a; };
{
m5::stl::expected<int, int> e = 21;
auto ret = e.map_error(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.map_error(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).map_error(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).map_error(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.map_error(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 42);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.map_error(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 42);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).map_error(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 42);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).map_error(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 42);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = e.map_error(ret_void);
EXPECT_TRUE(ret);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.map_error(ret_void);
EXPECT_TRUE(ret);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).map_error(ret_void);
EXPECT_TRUE(ret);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).map_error(ret_void);
EXPECT_TRUE(ret);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.map_error(ret_void);
EXPECT_TRUE(!ret);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.map_error(ret_void);
EXPECT_TRUE(!ret);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).map_error(ret_void);
EXPECT_TRUE(!ret);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).map_error(ret_void);
EXPECT_TRUE(!ret);
}
}
TEST(Expected, AndThenExtensions)
{
auto succeed = [](int a) {
(void)a;
return m5::stl::expected<int, int>(21 * 2);
};
auto fail = [](int a) {
(void)a;
return m5::stl::expected<int, int>(m5::stl::unexpect, 17);
};
{
m5::stl::expected<int, int> e = 21;
auto ret = e.and_then(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.and_then(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).and_then(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).and_then(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = e.and_then(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 17);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.and_then(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 17);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).and_then(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 17);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).and_then(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 17);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.and_then(succeed);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.and_then(succeed);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).and_then(succeed);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).and_then(succeed);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.and_then(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.and_then(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).and_then(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).and_then(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
}
TEST(Expected, or_else)
{
using eptr = std::unique_ptr<int>;
auto succeed = [](int a) {
(void)a;
return m5::stl::expected<int, int>(21 * 2);
};
auto succeedptr = [](eptr e) {
(void)e;
return m5::stl::expected<int, eptr>(21 * 2);
};
auto fail = [](int a) {
(void)a;
return m5::stl::expected<int, int>(m5::stl::unexpect, 17);
};
auto failptr = [](eptr e) {
*e = 17;
return m5::stl::expected<int, eptr>(m5::stl::unexpect, std::move(e));
};
auto failvoid = [](int) {};
auto failvoidptr = [](const eptr&) { /* don't consume */ };
auto consumeptr = [](eptr) {};
auto make_u_int = [](int n) { return std::unique_ptr<int>(new int(n)); };
{
m5::stl::expected<int, int> e = 21;
auto ret = e.or_else(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.or_else(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).or_else(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
m5::stl::expected<int, eptr> e = 21;
auto ret = std::move(e).or_else(succeedptr);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).or_else(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = e.or_else(fail);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.or_else(fail);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).or_else(fail);
EXPECT_TRUE(ret);
EXPECT_TRUE(ret == 21);
}
{
m5::stl::expected<int, eptr> e = 21;
auto ret = std::move(e).or_else(failptr);
EXPECT_TRUE(ret);
EXPECT_TRUE(ret == 21);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).or_else(fail);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.or_else(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.or_else(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).or_else(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
m5::stl::expected<int, eptr> e(m5::stl::unexpect, make_u_int(21));
auto ret = std::move(e).or_else(succeedptr);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).or_else(succeed);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.or_else(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 17);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.or_else(failvoid);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.or_else(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 17);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.or_else(failvoid);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).or_else(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 17);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).or_else(failvoid);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
m5::stl::expected<int, eptr> e(m5::stl::unexpect, make_u_int(21));
auto ret = std::move(e).or_else(failvoidptr);
EXPECT_TRUE(!ret);
EXPECT_TRUE(*ret.error() == 21);
}
{
m5::stl::expected<int, eptr> e(m5::stl::unexpect, make_u_int(21));
auto ret = std::move(e).or_else(consumeptr);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == nullptr);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).or_else(fail);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 17);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).or_else(failvoid);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
}
TEST(Expected, TransformExtensions)
{
auto mul2 = [](int a) { return a * 2; };
auto ret_void = [](int a) { (void)a; };
{
m5::stl::expected<int, int> e = 21;
auto ret = e.transform(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.transform(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).transform(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).transform(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 42);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.transform(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.transform(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).transform(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).transform(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 21);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = e.transform(ret_void);
EXPECT_TRUE(ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.transform(ret_void);
EXPECT_TRUE(ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).transform(ret_void);
EXPECT_TRUE(ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).transform(ret_void);
EXPECT_TRUE(ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.transform(ret_void);
EXPECT_TRUE(!ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.transform(ret_void);
EXPECT_TRUE(!ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).transform(ret_void);
EXPECT_TRUE(!ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).transform(ret_void);
EXPECT_TRUE(!ret);
STATIC_REQUIRE((std::is_same<decltype(ret), m5::stl::expected<void, int>>::value));
}
// mapping functions which return references
{
m5::stl::expected<int, int> e(42);
auto ret = e.transform([](int& i) -> int& { return i; });
EXPECT_TRUE(ret);
EXPECT_TRUE(ret == 42);
}
}
TEST(Expected, TransformErrorExtensions)
{
auto mul2 = [](int a) { return a * 2; };
auto ret_void = [](int a) { (void)a; };
{
m5::stl::expected<int, int> e = 21;
auto ret = e.transform_error(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.transform_error(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).transform_error(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).transform_error(mul2);
EXPECT_TRUE(ret);
EXPECT_TRUE(*ret == 21);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.transform_error(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 42);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.transform_error(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 42);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).transform_error(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 42);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).transform_error(mul2);
EXPECT_TRUE(!ret);
EXPECT_TRUE(ret.error() == 42);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = e.transform_error(ret_void);
EXPECT_TRUE(ret);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = e.transform_error(ret_void);
EXPECT_TRUE(ret);
}
{
m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).transform_error(ret_void);
EXPECT_TRUE(ret);
}
{
const m5::stl::expected<int, int> e = 21;
auto ret = std::move(e).transform_error(ret_void);
EXPECT_TRUE(ret);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.transform_error(ret_void);
EXPECT_TRUE(!ret);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = e.transform_error(ret_void);
EXPECT_TRUE(!ret);
}
{
m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).transform_error(ret_void);
EXPECT_TRUE(!ret);
}
{
const m5::stl::expected<int, int> e(m5::stl::unexpect, 21);
auto ret = std::move(e).transform_error(ret_void);
EXPECT_TRUE(!ret);
}
}
struct S {
int x;
};
struct F {
int x;
};
TEST(Expected, issue14)
{
auto res = m5::stl::expected<S, F>{m5::stl::unexpect, F{}};
res.map_error([](F f) { (void)f; });
}
TEST(Expected, issue32)
{
int i = 0;
m5::stl::expected<void, int> a;
a.map([&i] { i = 42; });
EXPECT_TRUE(i == 42);
auto x = a.map([] { return 42; });
EXPECT_TRUE(*x == 42);
}

View file

@ -0,0 +1,256 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/expected.hpp>
#include <string>
#include <memory>
using std::string;
m5::stl::expected<int, string> getInt3(int val)
{
return val;
}
m5::stl::expected<int, string> getInt2(int val)
{
return val;
}
m5::stl::expected<int, string> getInt1()
{
return getInt2(5).and_then(getInt3);
}
TEST(Expected, Issue1)
{
getInt1();
}
m5::stl::expected<int, int> operation1()
{
return 42;
}
m5::stl::expected<std::string, int> operation2(int const val)
{
(void)val;
return "Bananas";
}
TEST(Expected, Issue17)
{
auto const intermediate_result = operation1();
intermediate_result.and_then(operation2);
}
struct a { /* empty */
};
struct b : a { /* empty */
};
auto doit() -> m5::stl::expected<std::unique_ptr<b>, int>
{
return m5::stl::make_unexpected(0);
}
TEST(Expected, Issue23)
{
m5::stl::expected<std::unique_ptr<a>, int> msg = doit();
EXPECT_TRUE(!msg.has_value());
}
TEST(Expected, Issue26)
{
m5::stl::expected<a, int> exp = m5::stl::expected<b, int>(m5::stl::unexpect, 0);
EXPECT_TRUE(!exp.has_value());
}
struct foo {
foo() = default;
foo(const foo &) = delete;
foo(foo &&) noexcept {};
};
TEST(Expected, Issue29)
{
std::vector<foo> v;
v.emplace_back();
m5::stl::expected<std::vector<foo>, int> ov = std::move(v);
EXPECT_TRUE(ov->size() == 1);
}
m5::stl::expected<int, std::string> error()
{
return m5::stl::make_unexpected(std::string("error1 "));
}
std::string maperror(std::string s)
{
return s + "maperror ";
}
TEST(Expected, Issue30)
{
error().map_error(maperror);
}
struct i31 {
int i;
};
TEST(Expected, Issue31)
{
const m5::stl::expected<i31, int> a = i31{42};
(void)a->i;
m5::stl::expected<void, std::string> result;
m5::stl::expected<void, std::string> result2 = result;
result2 = result;
}
TEST(Expected, Issue33)
{
m5::stl::expected<void, int> res{m5::stl::unexpect, 0};
EXPECT_TRUE(!res);
res = res.map_error([](int i) {
(void)i;
return 42;
});
EXPECT_TRUE(res.error() == 42);
}
m5::stl::expected<void, std::string> voidWork()
{
return {};
}
m5::stl::expected<int, std::string> work2()
{
return 42;
}
void errorhandling(std::string)
{
}
TEST(Expected, Issue34)
{
m5::stl::expected<int, std::string> result = voidWork().and_then(work2);
result.map_error([&](std::string result) { errorhandling(result); });
}
struct non_copyable {
non_copyable(non_copyable &&) = default;
non_copyable(non_copyable const &) = delete;
non_copyable() = default;
};
TEST(Expected, Issue42)
{
m5::stl::expected<non_copyable, int>{}.map([](non_copyable) {});
}
TEST(Expected, Issue43)
{
auto result = m5::stl::expected<void, std::string>{};
result = m5::stl::make_unexpected(std::string{"foo"});
}
#if !(__GNUC__ <= 5)
#include <memory>
using MaybeDataPtr = m5::stl::expected<int, std::unique_ptr<int>>;
MaybeDataPtr test(int i) noexcept
{
return std::move(i);
}
MaybeDataPtr test2(int i) noexcept
{
return std::move(i);
}
TEST(Expected, Issue49)
{
auto m = test(10).and_then(test2);
}
#endif
m5::stl::expected<int, std::unique_ptr<std::string>> func()
{
return 1;
}
TEST(Expected, Issue61)
{
EXPECT_TRUE(func().value() == 1);
}
struct move_tracker {
int moved = 0;
move_tracker() = default;
move_tracker(move_tracker const &other) noexcept {};
move_tracker(move_tracker &&orig) noexcept : moved(orig.moved + 1)
{
}
move_tracker &operator=(move_tracker const &other) noexcept
{
return *this;
}
move_tracker &operator=(move_tracker &&orig) noexcept
{
moved = orig.moved + 1;
return *this;
}
};
TEST(Expected, Issue122)
{
m5::stl::expected<move_tracker, int> res;
res.emplace();
EXPECT_TRUE(res.value().moved == 0);
}
#ifdef __cpp_deduction_guides
TEST(Expected, Issue89)
{
auto s = m5::stl::unexpected("Some string");
EXPECT_TRUE(s.value() == std::string("Some string"));
}
#endif
namespace {
struct SS {
int i = 0;
int j = 0;
SS(int i) : i(i)
{
}
SS(int i, int j) : i(i), j(j)
{
}
};
} // namespace
TEST(Expected, Issue107)
{
m5::stl::expected<int, SS> ex1(m5::stl::unexpect, 2);
m5::stl::expected<int, SS> ex2(m5::stl::unexpect, 2, 2);
EXPECT_TRUE(ex1.error().i == 2);
EXPECT_TRUE(ex1.error().j == 0);
EXPECT_TRUE(ex2.error().i == 2);
EXPECT_TRUE(ex2.error().j == 2);
}
TEST(Expected, Issue129)
{
m5::stl::expected<std::unique_ptr<int>, int> x1{std::unique_ptr<int>(new int(4))};
m5::stl::expected<std::unique_ptr<int>, int> y1{std::unique_ptr<int>(new int(2))};
x1 = std::move(y1);
EXPECT_TRUE(**x1 == 2);
}

View file

@ -0,0 +1,7 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/expected.hpp>
TEST(Expected, Noexcept)
{
// TODO
}

View file

@ -0,0 +1,44 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/expected.hpp>
struct move_detector {
move_detector() = default;
move_detector(move_detector &&rhs)
{
rhs.been_moved = true;
}
bool been_moved = false;
};
TEST(Expected, Observers)
{
m5::stl::expected<int, int> o1 = 42;
m5::stl::expected<int, int> o2{m5::stl::unexpect, 0};
const m5::stl::expected<int, int> o3 = 42;
EXPECT_TRUE(*o1 == 42);
EXPECT_TRUE(*o1 == o1.value());
EXPECT_TRUE(o2.value_or(42) == 42);
EXPECT_TRUE(o2.error() == 0);
EXPECT_TRUE(o3.value() == 42);
auto success = std::is_same<decltype(o1.value()), int &>::value;
EXPECT_TRUE(success);
success = std::is_same<decltype(o3.value()), const int &>::value;
EXPECT_TRUE(success);
success = std::is_same<decltype(std::move(o1).value()), int &&>::value;
EXPECT_TRUE(success);
#ifndef TL_EXPECTED_NO_CONSTRR
success = std::is_same<decltype(std::move(o3).value()), const int &&>::value;
EXPECT_TRUE(success);
#endif
m5::stl::expected<move_detector, int> o4{m5::stl::in_place};
move_detector o5 = std::move(o4).value();
EXPECT_TRUE(o4->been_moved);
EXPECT_TRUE(!o5.been_moved);
// Add by GOB
EXPECT_TRUE(o1.error_or(52) == 52);
EXPECT_TRUE(o2.error_or(52) == 0);
}

View file

@ -0,0 +1,18 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/expected.hpp>
TEST(Expected, RelationalOperators)
{
m5::stl::expected<int, int> o1 = 42;
m5::stl::expected<int, int> o2{m5::stl::unexpect, 0};
const m5::stl::expected<int, int> o3 = 42;
EXPECT_TRUE(o1 == o1);
EXPECT_TRUE(o1 != o2);
EXPECT_TRUE(o1 == o3);
EXPECT_TRUE(o3 == o3);
m5::stl::expected<void, int> o6;
EXPECT_TRUE(o6 == o6);
}

View file

@ -0,0 +1,126 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/expected.hpp>
struct no_throw {
no_throw(std::string i) : i(i)
{
}
std::string i;
};
struct canthrow_move {
canthrow_move(std::string i) : i(i)
{
}
canthrow_move(canthrow_move const &) = default;
canthrow_move(canthrow_move &&other) noexcept(false) : i(other.i)
{
}
canthrow_move &operator=(canthrow_move &&) = default;
std::string i;
};
bool should_throw = false;
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
struct willthrow_move {
willthrow_move(std::string i) : i(i)
{
}
willthrow_move(willthrow_move const &) = default;
willthrow_move(willthrow_move &&other) : i(other.i)
{
if (should_throw) throw 0;
}
willthrow_move &operator=(willthrow_move &&) = default;
std::string i;
};
#endif // TL_EXPECTED_EXCEPTIONS_ENABLED
static_assert(m5::stl::detail::is_swappable<no_throw>::value, "");
template <class T1, class T2>
void swap_test()
{
std::string s1 = "abcdefghijklmnopqrstuvwxyz";
std::string s2 = "zyxwvutsrqponmlkjihgfedcba";
m5::stl::expected<T1, T2> a{s1};
m5::stl::expected<T1, T2> b{s2};
swap(a, b);
EXPECT_TRUE(a->i == s2);
EXPECT_TRUE(b->i == s1);
a = s1;
b = m5::stl::unexpected<T2>(s2);
swap(a, b);
EXPECT_TRUE(a.error().i == s2);
EXPECT_TRUE(b->i == s1);
a = m5::stl::unexpected<T2>(s1);
b = s2;
swap(a, b);
EXPECT_TRUE(a->i == s2);
EXPECT_TRUE(b.error().i == s1);
a = m5::stl::unexpected<T2>(s1);
b = m5::stl::unexpected<T2>(s2);
swap(a, b);
EXPECT_TRUE(a.error().i == s2);
EXPECT_TRUE(b.error().i == s1);
a = s1;
b = s2;
a.swap(b);
EXPECT_TRUE(a->i == s2);
EXPECT_TRUE(b->i == s1);
a = s1;
b = m5::stl::unexpected<T2>(s2);
a.swap(b);
EXPECT_TRUE(a.error().i == s2);
EXPECT_TRUE(b->i == s1);
a = m5::stl::unexpected<T2>(s1);
b = s2;
a.swap(b);
EXPECT_TRUE(a->i == s2);
EXPECT_TRUE(b.error().i == s1);
a = m5::stl::unexpected<T2>(s1);
b = m5::stl::unexpected<T2>(s2);
a.swap(b);
EXPECT_TRUE(a.error().i == s2);
EXPECT_TRUE(b.error().i == s1);
}
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
TEST(Expected, swap)
{
{
SCOPED_TRACE("no_throw no-throw");
swap_test<no_throw, no_throw>();
}
{
SCOPED_TRACE("no_throw canthrow");
swap_test<no_throw, canthrow_move>();
}
{
SCOPED_TRACE("canthrow no_throw");
swap_test<canthrow_move, no_throw>();
}
std::string s1 = "abcdefghijklmnopqrstuvwxyz";
std::string s2 = "zyxwvutsrqponmlkjihgfedcbaxxx";
m5::stl::expected<no_throw, willthrow_move> a{s1};
m5::stl::expected<no_throw, willthrow_move> b{m5::stl::unexpect, s2};
should_throw = 1;
#ifdef _MSC_VER
// this seems to break catch on GCC and Clang
EXPECT_TRUE_THROWS(swap(a, b));
#endif
EXPECT_TRUE(a->i == s1);
EXPECT_TRUE(b.error().i == s2);
}
#endif // TL_EXPECTED_EXCEPTIONS_ENABLED

View file

@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
Each split by test_filter creates a separate binary. So they are combined by including cpp.
Note that the /optional files are not compiled directly if not set to test_filter
*/
#include "expected/assertions.cpp"
#include "expected/assignment.cpp"
#include "expected/bases.cpp"
#include "expected/constexpr.cpp"
#include "expected/constructors.cpp"
#include "expected/emplace.cpp"
#include "expected/extensions.cpp"
#include "expected/issues.cpp"
#include "expected/noexcept.cpp"
#include "expected/observers.cpp"
#include "expected/relops.cpp"
#include "expected/swap.cpp"

View file

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
*/
#ifndef TEST_HEKPER_HPP
#define TEST_HEKPER_HPP
// catch2 STATIC_REQUIRE
#define STATIC_EXPECT_TRUE(constexpr_cond) \
do { \
static_assert((constexpr_cond), "Occurrence of compile-time assertion"); \
} while (0)
#endif

View file

@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
*/
#include <gtest/gtest.h>
#include <M5Utility.hpp>
TEST(Utility, is_powerof2)
{
EXPECT_FALSE(m5::math::is_powerof2(0));
EXPECT_TRUE(m5::math::is_powerof2(1));
EXPECT_TRUE(m5::math::is_powerof2(2));
EXPECT_FALSE(m5::math::is_powerof2(3));
constexpr auto b = m5::math::is_powerof2(-1);
EXPECT_FALSE(b);
EXPECT_FALSE(m5::math::is_powerof2(-2));
}

View file

@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
*/
#include <gtest/gtest.h>
#include <M5Utility.hpp>
using namespace m5::utility;
TEST(Utility, reverseBitOrder)
{
EXPECT_EQ(reverseBitOrder((uint8_t)0), 0);
EXPECT_EQ(reverseBitOrder((uint16_t)0), 0);
EXPECT_EQ(reverseBitOrder((uint8_t)0xFF), 0xFF);
EXPECT_EQ(reverseBitOrder((uint16_t)0xFFFF), 0xFFFF);
EXPECT_EQ(reverseBitOrder((uint8_t)0x0F), 0xF0);
EXPECT_EQ(reverseBitOrder((uint16_t)0xFF00), 0x00FF);
EXPECT_EQ(reverseBitOrder((uint8_t)0x4C), 0x32);
EXPECT_EQ(reverseBitOrder((uint16_t)0x4C4C), 0x3232);
EXPECT_EQ(reverseBitOrder((uint8_t)0x8E), 0x71);
EXPECT_EQ(reverseBitOrder((uint8_t)0x65), 0xA6);
EXPECT_EQ(reverseBitOrder((uint16_t)0x8E65), 0xA671);
}

View file

@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
*/
#include <gtest/gtest.h>
#include <random>
#include <M5Utility.hpp>
#include "MurmurHash3.h" // from https://github.com/rurban/smhasher/tree/master (public domain)
#if defined(ARDUINO)
#include <WString.h>
using string_t = String;
#else
#include <cstring>
using string_t = std::string;
#endif
namespace {
auto rng = std::default_random_engine{};
string_t make_random_str()
{
constexpr char dic[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789"
"!#$%&()=-~^|@[{]}:;+*,.<>/?_";
string_t s;
size_t len = 1 + (rng() & 31);
while (len--) {
s += dic[rng() % (sizeof(dic) / sizeof(dic[0]) - 1)];
}
return s;
}
using test_pair_t = std::pair<const char*, uint32_t>;
test_pair_t test_pairs[] = {
{"", 0},
{"M5Stack", 0x8c97d1e0U},
{"M5Stack is a leading provider of IoT solutions.", 0x1a1eca6dU},
};
} // namespace
TEST(MurmurHash3, endianness)
{
using namespace m5::utility::mmh3;
constexpr char tst[] = "M5ST";
auto big = str2uint32<false>(tst);
auto little = str2uint32<true>(tst);
EXPECT_EQ(big, 0x4d355354U);
EXPECT_EQ(little, 0x5453354dU);
}
// User-defined literals "_mmh3"
TEST(MurmurHash3, user_defined_literals)
{
using namespace m5::utility::mmh3;
constexpr auto h0 = ""_mmh3;
constexpr auto h1 = "M5Stack"_mmh3;
constexpr auto h2 = "M5Stack is a leading provider of IoT solutions."_mmh3;
EXPECT_EQ(h0, 0U);
EXPECT_EQ(h1, 0x8c97d1e0U);
EXPECT_EQ(h2, 0x1a1eca6dU);
}
// Verification of value correctness
TEST(MurmurHash3, verify)
{
for (auto&& e : test_pairs) {
auto& s = e.first;
auto h = m5::utility::mmh3::calculate(s);
uint32_t h2{};
MurmurHash3_x86_32(s, strlen(s), 0, &h2);
EXPECT_EQ(e.second, h) << '[' << s << "] len:" << strlen(s);
EXPECT_EQ(h, h2) << '[' << s << "] len:" << strlen(s);
}
constexpr int count = 10000;
for (int i = 0; i < count; ++i) {
string_t s = make_random_str();
size_t len = strlen(s.c_str());
uint32_t h1{};
MurmurHash3_x86_32(s.c_str(), len, 0, &h1);
uint32_t h2 = m5::utility::mmh3::calculate(s.c_str());
EXPECT_EQ(h1, h2) << '[' << s.c_str() << "] len:" << strlen(s.c_str());
}
}

View file

@ -0,0 +1,72 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
TEST(Optional, Assignment)
{
m5::stl::optional<int> o1 = 42;
m5::stl::optional<int> o2 = 12;
m5::stl::optional<int> o3;
o1 = o1;
EXPECT_TRUE(*o1 == 42);
o1 = o2;
EXPECT_TRUE(*o1 == 12);
o1 = o3;
EXPECT_TRUE(!o1);
o1 = 42;
EXPECT_TRUE(*o1 == 42);
o1 = m5::stl::nullopt;
EXPECT_TRUE(!o1);
o1 = std::move(o2);
EXPECT_TRUE(*o1 == 12);
m5::stl::optional<short> o4 = 42;
o1 = o4;
EXPECT_TRUE(*o1 == 42);
o1 = std::move(o4);
EXPECT_TRUE(*o1 == 42);
}
TEST(Optional, AssignmentReference)
{
auto i = 42;
auto j = 12;
m5::stl::optional<int&> o1 = i;
m5::stl::optional<int&> o2 = j;
m5::stl::optional<int&> o3;
o1 = o1;
EXPECT_TRUE(*o1 == 42);
EXPECT_TRUE(&*o1 == &i);
o1 = o2;
EXPECT_TRUE(*o1 == 12);
o1 = o3;
EXPECT_TRUE(!o1);
auto k = 42;
o1 = k;
EXPECT_TRUE(*o1 == 42);
EXPECT_TRUE(*o1 == i);
EXPECT_TRUE(*o1 == k);
EXPECT_TRUE(&*o1 != &i);
EXPECT_TRUE(&*o1 == &k);
k = 12;
EXPECT_TRUE(*o1 == 12);
o1 = m5::stl::nullopt;
EXPECT_TRUE(!o1);
o1 = std::move(o2);
EXPECT_TRUE(*o1 == 12);
}

View file

@ -0,0 +1,136 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
// Old versions of GCC don't have the correct trait names. Could fix them up if needs be.
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__))
// nothing for now
#else
TEST(Optional, Triviality)
{
EXPECT_TRUE(std::is_trivially_copy_constructible<m5::stl::optional<int> >::value);
EXPECT_TRUE(std::is_trivially_copy_assignable<m5::stl::optional<int> >::value);
EXPECT_TRUE(std::is_trivially_move_constructible<m5::stl::optional<int> >::value);
EXPECT_TRUE(std::is_trivially_move_assignable<m5::stl::optional<int> >::value);
EXPECT_TRUE(std::is_trivially_destructible<m5::stl::optional<int> >::value);
{
struct T {
T(const T&) = default;
T(T&&) = default;
T& operator=(const T&) = default;
T& operator=(T&&) = default;
~T() = default;
};
EXPECT_TRUE(std::is_trivially_copy_constructible<m5::stl::optional<T> >::value);
EXPECT_TRUE(std::is_trivially_copy_assignable<m5::stl::optional<T> >::value);
EXPECT_TRUE(std::is_trivially_move_constructible<m5::stl::optional<T> >::value);
EXPECT_TRUE(std::is_trivially_move_assignable<m5::stl::optional<T> >::value);
EXPECT_TRUE(std::is_trivially_destructible<m5::stl::optional<T> >::value);
}
{
struct T {
T(const T&)
{
}
T(T&&)
{
}
T& operator=(const T&)
{
return *this;
}
T& operator=(T&&)
{
return *this;
};
~T()
{
}
};
EXPECT_TRUE(!std::is_trivially_copy_constructible<m5::stl::optional<T> >::value);
EXPECT_TRUE(!std::is_trivially_copy_assignable<m5::stl::optional<T> >::value);
EXPECT_TRUE(!std::is_trivially_move_constructible<m5::stl::optional<T> >::value);
EXPECT_TRUE(!std::is_trivially_move_assignable<m5::stl::optional<T> >::value);
EXPECT_TRUE(!std::is_trivially_destructible<m5::stl::optional<T> >::value);
}
}
TEST(Optional, Deletion)
{
EXPECT_TRUE(std::is_copy_constructible<m5::stl::optional<int> >::value);
EXPECT_TRUE(std::is_copy_assignable<m5::stl::optional<int> >::value);
EXPECT_TRUE(std::is_move_constructible<m5::stl::optional<int> >::value);
EXPECT_TRUE(std::is_move_assignable<m5::stl::optional<int> >::value);
EXPECT_TRUE(std::is_destructible<m5::stl::optional<int> >::value);
{
struct T {
T(const T&) = default;
T(T&&) = default;
T& operator=(const T&) = default;
T& operator=(T&&) = default;
~T() = default;
};
EXPECT_TRUE(std::is_copy_constructible<m5::stl::optional<T> >::value);
EXPECT_TRUE(std::is_copy_assignable<m5::stl::optional<T> >::value);
EXPECT_TRUE(std::is_move_constructible<m5::stl::optional<T> >::value);
EXPECT_TRUE(std::is_move_assignable<m5::stl::optional<T> >::value);
EXPECT_TRUE(std::is_destructible<m5::stl::optional<T> >::value);
}
{
struct T {
T(const T&) = delete;
T(T&&) = delete;
T& operator=(const T&) = delete;
T& operator=(T&&) = delete;
};
EXPECT_TRUE(!std::is_copy_constructible<m5::stl::optional<T> >::value);
EXPECT_TRUE(!std::is_copy_assignable<m5::stl::optional<T> >::value);
EXPECT_TRUE(!std::is_move_constructible<m5::stl::optional<T> >::value);
EXPECT_TRUE(!std::is_move_assignable<m5::stl::optional<T> >::value);
}
{
struct T {
T(const T&) = delete;
T(T&&) = default;
T& operator=(const T&) = delete;
T& operator=(T&&) = default;
};
EXPECT_TRUE(!std::is_copy_constructible<m5::stl::optional<T> >::value);
EXPECT_TRUE(!std::is_copy_assignable<m5::stl::optional<T> >::value);
EXPECT_TRUE(std::is_move_constructible<m5::stl::optional<T> >::value);
EXPECT_TRUE(std::is_move_assignable<m5::stl::optional<T> >::value);
}
{
struct T {
T(const T&) = default;
T(T&&) = delete;
T& operator=(const T&) = default;
T& operator=(T&&) = delete;
};
EXPECT_TRUE(std::is_copy_constructible<m5::stl::optional<T> >::value);
EXPECT_TRUE(std::is_copy_assignable<m5::stl::optional<T> >::value);
}
}
#endif

View file

@ -0,0 +1,44 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
#include "helper.hpp"
TEST(Optional, Constexpr)
{
#if !defined(TL_OPTIONAL_MSVC2015) && defined(TL_OPTIONAL_CXX14)
{
SCOPED_TRACE("empty construct");
constexpr m5::stl::optional<int> o2{};
constexpr m5::stl::optional<int> o3 = {};
constexpr m5::stl::optional<int> o4 = m5::stl::nullopt;
constexpr m5::stl::optional<int> o5 = {m5::stl::nullopt};
constexpr m5::stl::optional<int> o6(m5::stl::nullopt);
STATIC_EXPECT_TRUE(!o2);
STATIC_EXPECT_TRUE(!o3);
STATIC_EXPECT_TRUE(!o4);
STATIC_EXPECT_TRUE(!o5);
STATIC_EXPECT_TRUE(!o6);
}
{
SCOPED_TRACE("value construct");
constexpr m5::stl::optional<int> o1 = 42;
constexpr m5::stl::optional<int> o2{42};
constexpr m5::stl::optional<int> o3(42);
constexpr m5::stl::optional<int> o4 = {42};
constexpr int i = 42;
constexpr m5::stl::optional<int> o5 = std::move(i);
constexpr m5::stl::optional<int> o6{std::move(i)};
constexpr m5::stl::optional<int> o7(std::move(i));
constexpr m5::stl::optional<int> o8 = {std::move(i)};
STATIC_EXPECT_TRUE(*o1 == 42);
STATIC_EXPECT_TRUE(*o2 == 42);
STATIC_EXPECT_TRUE(*o3 == 42);
STATIC_EXPECT_TRUE(*o4 == 42);
STATIC_EXPECT_TRUE(*o5 == 42);
STATIC_EXPECT_TRUE(*o6 == 42);
STATIC_EXPECT_TRUE(*o7 == 42);
STATIC_EXPECT_TRUE(*o8 == 42);
}
#endif
}

View file

@ -0,0 +1,69 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
#include <vector>
namespace constructor {
struct foo {
foo() = default;
foo(foo &) = delete;
foo(foo &&) noexcept
{
}
};
} // namespace constructor
TEST(Optional, Constructors)
{
m5::stl::optional<int> o1;
EXPECT_TRUE(!o1);
m5::stl::optional<int> o2 = m5::stl::nullopt;
EXPECT_TRUE(!o2);
m5::stl::optional<int> o3 = 42;
EXPECT_TRUE(*o3 == 42);
m5::stl::optional<int> o4 = o3;
EXPECT_TRUE(*o4 == 42);
m5::stl::optional<int> o5 = o1;
EXPECT_TRUE(!o5);
m5::stl::optional<int> o6 = std::move(o3);
EXPECT_TRUE(*o6 == 42);
m5::stl::optional<short> o7 = 42;
EXPECT_TRUE(*o7 == 42);
m5::stl::optional<int> o8 = o7;
EXPECT_TRUE(*o8 == 42);
m5::stl::optional<int> o9 = std::move(o7);
EXPECT_TRUE(*o9 == 42);
{
m5::stl::optional<int &> o;
EXPECT_TRUE(!o);
m5::stl::optional<int &> oo = o;
EXPECT_TRUE(!oo);
}
{
auto i = 42;
m5::stl::optional<int &> o = i;
EXPECT_TRUE(o);
EXPECT_TRUE(*o == 42);
m5::stl::optional<int &> oo = o;
EXPECT_TRUE(oo);
EXPECT_TRUE(*oo == 42);
}
std::vector<constructor::foo> v;
v.emplace_back();
m5::stl::optional<std::vector<constructor::foo>> ov = std::move(v);
EXPECT_TRUE(ov->size() == 1);
}

View file

@ -0,0 +1,27 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
#include <utility>
#include <tuple>
TEST(Optional, Emplace)
{
m5::stl::optional<std::pair<std::pair<int, int>, std::pair<double, double>>> i;
i.emplace(std::piecewise_construct, std::make_tuple(0, 2), std::make_tuple(3, 4));
EXPECT_TRUE(i->first.first == 0);
EXPECT_TRUE(i->first.second == 2);
EXPECT_TRUE(i->second.first == 3);
EXPECT_TRUE(i->second.second == 4);
}
struct A {
A()
{
throw std::exception();
}
};
TEST(Optional, EmplaceWithException)
{
m5::stl::optional<A> a;
EXPECT_ANY_THROW(a.emplace());
}

View file

@ -0,0 +1,501 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
#include <string>
#include "helper.hpp"
constexpr int get_int(int)
{
return 42;
}
TL_OPTIONAL_11_CONSTEXPR m5::stl::optional<int> get_opt_int(int)
{
return 42;
}
// What is Clang Format up to?!
TEST(Optional, Monadic)
{
{ // lhs is empty
SCOPED_TRACE("map");
m5::stl::optional<int> o1;
auto o1r = o1.map([](int i) { return i + 2; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o1r), m5::stl::optional<int>>::value));
EXPECT_TRUE(!o1r);
// lhs has value
m5::stl::optional<int> o2 = 40;
auto o2r = o2.map([](int i) { return i + 2; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o2r), m5::stl::optional<int>>::value));
EXPECT_TRUE(o2r.value() == 42);
struct rval_call_map {
double operator()(int) &&
{
return 42.0;
};
};
// ensure that function object is forwarded
m5::stl::optional<int> o3 = 42;
auto o3r = o3.map(rval_call_map{});
STATIC_EXPECT_TRUE((std::is_same<decltype(o3r), m5::stl::optional<double>>::value));
EXPECT_TRUE(o3r.value() == 42);
// ensure that lhs is forwarded
m5::stl::optional<int> o4 = 40;
auto o4r = std::move(o4).map([](int &&i) { return i + 2; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o4r), m5::stl::optional<int>>::value));
EXPECT_TRUE(o4r.value() == 42);
// ensure that lhs is const-propagated
const m5::stl::optional<int> o5 = 40;
auto o5r = o5.map([](const int &i) { return i + 2; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o5r), m5::stl::optional<int>>::value));
EXPECT_TRUE(o5r.value() == 42);
// test void return
m5::stl::optional<int> o7 = 40;
auto f7 = [](const int &) { return; };
auto o7r = o7.map(f7);
STATIC_EXPECT_TRUE((std::is_same<decltype(o7r), m5::stl::optional<m5::stl::monostate>>::value));
EXPECT_TRUE(o7r.has_value());
// test each overload in turn
m5::stl::optional<int> o8 = 42;
auto o8r = o8.map([](int) { return 42; });
EXPECT_TRUE(*o8r == 42);
m5::stl::optional<int> o9 = 42;
auto o9r = o9.map([](int) { return; });
EXPECT_TRUE(o9r);
m5::stl::optional<int> o12 = 42;
auto o12r = std::move(o12).map([](int) { return 42; });
EXPECT_TRUE(*o12r == 42);
m5::stl::optional<int> o13 = 42;
auto o13r = std::move(o13).map([](int) { return; });
EXPECT_TRUE(o13r);
const m5::stl::optional<int> o16 = 42;
auto o16r = o16.map([](int) { return 42; });
EXPECT_TRUE(*o16r == 42);
const m5::stl::optional<int> o17 = 42;
auto o17r = o17.map([](int) { return; });
EXPECT_TRUE(o17r);
const m5::stl::optional<int> o20 = 42;
auto o20r = std::move(o20).map([](int) { return 42; });
EXPECT_TRUE(*o20r == 42);
const m5::stl::optional<int> o21 = 42;
auto o21r = std::move(o21).map([](int) { return; });
EXPECT_TRUE(o21r);
m5::stl::optional<int> o24 = m5::stl::nullopt;
auto o24r = o24.map([](int) { return 42; });
EXPECT_TRUE(!o24r);
m5::stl::optional<int> o25 = m5::stl::nullopt;
auto o25r = o25.map([](int) { return; });
EXPECT_TRUE(!o25r);
m5::stl::optional<int> o28 = m5::stl::nullopt;
auto o28r = std::move(o28).map([](int) { return 42; });
EXPECT_TRUE(!o28r);
m5::stl::optional<int> o29 = m5::stl::nullopt;
auto o29r = std::move(o29).map([](int) { return; });
EXPECT_TRUE(!o29r);
const m5::stl::optional<int> o32 = m5::stl::nullopt;
auto o32r = o32.map([](int) { return 42; });
EXPECT_TRUE(!o32r);
const m5::stl::optional<int> o33 = m5::stl::nullopt;
auto o33r = o33.map([](int) { return; });
EXPECT_TRUE(!o33r);
const m5::stl::optional<int> o36 = m5::stl::nullopt;
auto o36r = std::move(o36).map([](int) { return 42; });
EXPECT_TRUE(!o36r);
const m5::stl::optional<int> o37 = m5::stl::nullopt;
auto o37r = std::move(o37).map([](int) { return; });
EXPECT_TRUE(!o37r);
// callable which returns a reference
m5::stl::optional<int> o38 = 42;
auto o38r = o38.map([](int &i) -> const int &{ return i; });
EXPECT_TRUE(o38r);
EXPECT_TRUE(*o38r == 42);
int i = 42;
m5::stl::optional<int &> o39 = i;
o39.map([](int &x) { x = 12; });
EXPECT_TRUE(i == 12);
}
{
SCOPED_TRACE("map constexpr");
#if !defined(_MSC_VER) && defined(TL_OPTIONAL_CXX14)
// test each overload in turn
constexpr m5::stl::optional<int> o16 = 42;
constexpr auto o16r = o16.map(get_int);
STATIC_EXPECT_TRUE(*o16r == 42);
constexpr m5::stl::optional<int> o20 = 42;
constexpr auto o20r = std::move(o20).map(get_int);
STATIC_EXPECT_TRUE(*o20r == 42);
constexpr m5::stl::optional<int> o32 = m5::stl::nullopt;
constexpr auto o32r = o32.map(get_int);
STATIC_EXPECT_TRUE(!o32r);
constexpr m5::stl::optional<int> o36 = m5::stl::nullopt;
constexpr auto o36r = std::move(o36).map(get_int);
STATIC_EXPECT_TRUE(!o36r);
#endif
}
{ // lhs is empty
SCOPED_TRACE("transform");
m5::stl::optional<int> o1;
auto o1r = o1.transform([](int i) { return i + 2; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o1r), m5::stl::optional<int>>::value));
EXPECT_TRUE(!o1r);
// lhs has value
m5::stl::optional<int> o2 = 40;
auto o2r = o2.transform([](int i) { return i + 2; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o2r), m5::stl::optional<int>>::value));
EXPECT_TRUE(o2r.value() == 42);
struct rval_call_transform {
double operator()(int) &&
{
return 42.0;
};
};
// ensure that function object is forwarded
m5::stl::optional<int> o3 = 42;
auto o3r = o3.transform(rval_call_transform{});
STATIC_EXPECT_TRUE((std::is_same<decltype(o3r), m5::stl::optional<double>>::value));
EXPECT_TRUE(o3r.value() == 42);
// ensure that lhs is forwarded
m5::stl::optional<int> o4 = 40;
auto o4r = std::move(o4).transform([](int &&i) { return i + 2; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o4r), m5::stl::optional<int>>::value));
EXPECT_TRUE(o4r.value() == 42);
// ensure that lhs is const-propagated
const m5::stl::optional<int> o5 = 40;
auto o5r = o5.transform([](const int &i) { return i + 2; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o5r), m5::stl::optional<int>>::value));
EXPECT_TRUE(o5r.value() == 42);
// test void return
m5::stl::optional<int> o7 = 40;
auto f7 = [](const int &) { return; };
auto o7r = o7.transform(f7);
STATIC_EXPECT_TRUE((std::is_same<decltype(o7r), m5::stl::optional<m5::stl::monostate>>::value));
EXPECT_TRUE(o7r.has_value());
// test each overload in turn
m5::stl::optional<int> o8 = 42;
auto o8r = o8.transform([](int) { return 42; });
EXPECT_TRUE(*o8r == 42);
m5::stl::optional<int> o9 = 42;
auto o9r = o9.transform([](int) { return; });
EXPECT_TRUE(o9r);
m5::stl::optional<int> o12 = 42;
auto o12r = std::move(o12).transform([](int) { return 42; });
EXPECT_TRUE(*o12r == 42);
m5::stl::optional<int> o13 = 42;
auto o13r = std::move(o13).transform([](int) { return; });
EXPECT_TRUE(o13r);
const m5::stl::optional<int> o16 = 42;
auto o16r = o16.transform([](int) { return 42; });
EXPECT_TRUE(*o16r == 42);
const m5::stl::optional<int> o17 = 42;
auto o17r = o17.transform([](int) { return; });
EXPECT_TRUE(o17r);
const m5::stl::optional<int> o20 = 42;
auto o20r = std::move(o20).transform([](int) { return 42; });
EXPECT_TRUE(*o20r == 42);
const m5::stl::optional<int> o21 = 42;
auto o21r = std::move(o21).transform([](int) { return; });
EXPECT_TRUE(o21r);
m5::stl::optional<int> o24 = m5::stl::nullopt;
auto o24r = o24.transform([](int) { return 42; });
EXPECT_TRUE(!o24r);
m5::stl::optional<int> o25 = m5::stl::nullopt;
auto o25r = o25.transform([](int) { return; });
EXPECT_TRUE(!o25r);
m5::stl::optional<int> o28 = m5::stl::nullopt;
auto o28r = std::move(o28).transform([](int) { return 42; });
EXPECT_TRUE(!o28r);
m5::stl::optional<int> o29 = m5::stl::nullopt;
auto o29r = std::move(o29).transform([](int) { return; });
EXPECT_TRUE(!o29r);
const m5::stl::optional<int> o32 = m5::stl::nullopt;
auto o32r = o32.transform([](int) { return 42; });
EXPECT_TRUE(!o32r);
const m5::stl::optional<int> o33 = m5::stl::nullopt;
auto o33r = o33.transform([](int) { return; });
EXPECT_TRUE(!o33r);
const m5::stl::optional<int> o36 = m5::stl::nullopt;
auto o36r = std::move(o36).transform([](int) { return 42; });
EXPECT_TRUE(!o36r);
const m5::stl::optional<int> o37 = m5::stl::nullopt;
auto o37r = std::move(o37).transform([](int) { return; });
EXPECT_TRUE(!o37r);
// callable which returns a reference
m5::stl::optional<int> o38 = 42;
auto o38r = o38.transform([](int &i) -> const int &{ return i; });
EXPECT_TRUE(o38r);
EXPECT_TRUE(*o38r == 42);
int i = 42;
m5::stl::optional<int &> o39 = i;
o39.transform([](int &x) { x = 12; });
EXPECT_TRUE(i == 12);
}
{
SCOPED_TRACE("transform constexpr");
#if !defined(_MSC_VER) && defined(TL_OPTIONAL_CXX14)
// test each overload in turn
constexpr m5::stl::optional<int> o16 = 42;
constexpr auto o16r = o16.transform(get_int);
STATIC_EXPECT_TRUE(*o16r == 42);
constexpr m5::stl::optional<int> o20 = 42;
constexpr auto o20r = std::move(o20).transform(get_int);
STATIC_EXPECT_TRUE(*o20r == 42);
constexpr m5::stl::optional<int> o32 = m5::stl::nullopt;
constexpr auto o32r = o32.transform(get_int);
STATIC_EXPECT_TRUE(!o32r);
constexpr m5::stl::optional<int> o36 = m5::stl::nullopt;
constexpr auto o36r = std::move(o36).transform(get_int);
STATIC_EXPECT_TRUE(!o36r);
#endif
}
{
SCOPED_TRACE("and_then");
// lhs is empty
m5::stl::optional<int> o1;
auto o1r = o1.and_then([](int) { return m5::stl::optional<float>{42}; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o1r), m5::stl::optional<float>>::value));
EXPECT_TRUE(!o1r);
// lhs has value
m5::stl::optional<int> o2 = 12;
auto o2r = o2.and_then([](int) { return m5::stl::optional<float>{42}; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o2r), m5::stl::optional<float>>::value));
EXPECT_TRUE(o2r.value() == 42.f);
// lhs is empty, rhs returns empty
m5::stl::optional<int> o3;
auto o3r = o3.and_then([](int) { return m5::stl::optional<float>{}; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o3r), m5::stl::optional<float>>::value));
EXPECT_TRUE(!o3r);
// rhs returns empty
m5::stl::optional<int> o4 = 12;
auto o4r = o4.and_then([](int) { return m5::stl::optional<float>{}; });
STATIC_EXPECT_TRUE((std::is_same<decltype(o4r), m5::stl::optional<float>>::value));
EXPECT_TRUE(!o4r);
struct rval_call_and_then {
m5::stl::optional<double> operator()(int) &&
{
return m5::stl::optional<double>(42.0);
};
};
// ensure that function object is forwarded
m5::stl::optional<int> o5 = 42;
auto o5r = o5.and_then(rval_call_and_then{});
STATIC_EXPECT_TRUE((std::is_same<decltype(o5r), m5::stl::optional<double>>::value));
EXPECT_TRUE(o5r.value() == 42);
// ensure that lhs is forwarded
m5::stl::optional<int> o6 = 42;
auto o6r = std::move(o6).and_then([](int &&i) { return m5::stl::optional<double>(i); });
STATIC_EXPECT_TRUE((std::is_same<decltype(o6r), m5::stl::optional<double>>::value));
EXPECT_TRUE(o6r.value() == 42);
// ensure that function object is const-propagated
const m5::stl::optional<int> o7 = 42;
auto o7r = o7.and_then([](const int &i) { return m5::stl::optional<double>(i); });
STATIC_EXPECT_TRUE((std::is_same<decltype(o7r), m5::stl::optional<double>>::value));
EXPECT_TRUE(o7r.value() == 42);
// test each overload in turn
m5::stl::optional<int> o8 = 42;
auto o8r = o8.and_then([](int) { return m5::stl::make_optional(42); });
EXPECT_TRUE(*o8r == 42);
m5::stl::optional<int> o9 = 42;
auto o9r = std::move(o9).and_then([](int) { return m5::stl::make_optional(42); });
EXPECT_TRUE(*o9r == 42);
const m5::stl::optional<int> o10 = 42;
auto o10r = o10.and_then([](int) { return m5::stl::make_optional(42); });
EXPECT_TRUE(*o10r == 42);
const m5::stl::optional<int> o11 = 42;
auto o11r = std::move(o11).and_then([](int) { return m5::stl::make_optional(42); });
EXPECT_TRUE(*o11r == 42);
m5::stl::optional<int> o16 = m5::stl::nullopt;
auto o16r = o16.and_then([](int) { return m5::stl::make_optional(42); });
EXPECT_TRUE(!o16r);
m5::stl::optional<int> o17 = m5::stl::nullopt;
auto o17r = std::move(o17).and_then([](int) { return m5::stl::make_optional(42); });
EXPECT_TRUE(!o17r);
const m5::stl::optional<int> o18 = m5::stl::nullopt;
auto o18r = o18.and_then([](int) { return m5::stl::make_optional(42); });
EXPECT_TRUE(!o18r);
const m5::stl::optional<int> o19 = m5::stl::nullopt;
auto o19r = std::move(o19).and_then([](int) { return m5::stl::make_optional(42); });
EXPECT_TRUE(!o19r);
int i = 3;
m5::stl::optional<int &> o20{i};
std::move(o20).and_then([](int &r) { return m5::stl::optional<int &>{++r}; });
EXPECT_TRUE(o20);
EXPECT_TRUE(i == 4);
}
{
SCOPED_TRACE("constexpr and_then");
#if !defined(_MSC_VER) && defined(TL_OPTIONAL_CXX14)
constexpr m5::stl::optional<int> o10 = 42;
constexpr auto o10r = o10.and_then(get_opt_int);
EXPECT_TRUE(*o10r == 42);
constexpr m5::stl::optional<int> o11 = 42;
constexpr auto o11r = std::move(o11).and_then(get_opt_int);
EXPECT_TRUE(*o11r == 42);
constexpr m5::stl::optional<int> o18 = m5::stl::nullopt;
constexpr auto o18r = o18.and_then(get_opt_int);
EXPECT_TRUE(!o18r);
constexpr m5::stl::optional<int> o19 = m5::stl::nullopt;
constexpr auto o19r = std::move(o19).and_then(get_opt_int);
EXPECT_TRUE(!o19r);
#endif
}
{
SCOPED_TRACE("or else");
m5::stl::optional<int> o1 = 42;
EXPECT_TRUE(*(o1.or_else([] { return m5::stl::make_optional(13); })) == 42);
m5::stl::optional<int> o2;
EXPECT_TRUE(*(o2.or_else([] { return m5::stl::make_optional(13); })) == 13);
}
{
SCOPED_TRACE("disjunction");
m5::stl::optional<int> o1 = 42;
m5::stl::optional<int> o2 = 12;
m5::stl::optional<int> o3;
EXPECT_TRUE(*o1.disjunction(o2) == 42);
EXPECT_TRUE(*o1.disjunction(o3) == 42);
EXPECT_TRUE(*o2.disjunction(o1) == 12);
EXPECT_TRUE(*o2.disjunction(o3) == 12);
EXPECT_TRUE(*o3.disjunction(o1) == 42);
EXPECT_TRUE(*o3.disjunction(o2) == 12);
}
{
SCOPED_TRACE("conjunction");
m5::stl::optional<int> o1 = 42;
EXPECT_TRUE(*o1.conjunction(42.0) == 42.0);
EXPECT_TRUE(*o1.conjunction(std::string{"hello"}) == std::string{"hello"});
m5::stl::optional<int> o2;
EXPECT_TRUE(!o2.conjunction(42.0));
EXPECT_TRUE(!o2.conjunction(std::string{"hello"}));
}
{
SCOPED_TRACE("map_or");
m5::stl::optional<int> o1 = 21;
EXPECT_TRUE((o1.map_or([](int x) { return x * 2; }, 13)) == 42);
m5::stl::optional<int> o2;
EXPECT_TRUE((o2.map_or([](int x) { return x * 2; }, 13)) == 13);
}
{
SCOPED_TRACE("map_or_else");
m5::stl::optional<int> o1 = 21;
EXPECT_TRUE((o1.map_or_else([](int x) { return x * 2; }, [] { return 13; })) == 42);
m5::stl::optional<int> o2;
EXPECT_TRUE((o2.map_or_else([](int x) { return x * 2; }, [] { return 13; })) == 13);
}
{
SCOPED_TRACE("take");
m5::stl::optional<int> o1 = 42;
EXPECT_TRUE(*o1.take() == 42);
EXPECT_TRUE(!o1);
m5::stl::optional<int> o2;
EXPECT_TRUE(!o2.take());
EXPECT_TRUE(!o2);
}
struct foo {
void non_const()
{
}
};
#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && !defined(TL_OPTIONAL_GCC54) && \
!defined(TL_OPTIONAL_GCC55)
{
SCOPED_TRACE("Issue #1");
m5::stl::optional<foo> f = foo{};
auto l = [](auto &&x) { x.non_const(); };
f.map(l);
}
#endif
struct overloaded {
m5::stl::optional<int> operator()(foo &)
{
return 0;
}
m5::stl::optional<std::string> operator()(const foo &)
{
return "";
}
};
{
SCOPED_TRACE("Issue #2");
m5::stl::optional<foo> f = foo{};
auto x = f.and_then(overloaded{});
}
};

View file

@ -0,0 +1,6 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
TEST(Optional, Hashing)
{
}

View file

@ -0,0 +1,45 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
#include <tuple>
#include <vector>
namespace in_place {
struct takes_init_and_variadic {
std::vector<int> v;
std::tuple<int, int> t;
template <class... Args>
takes_init_and_variadic(std::initializer_list<int> l, Args &&...args) : v(l), t(std::forward<Args>(args)...)
{
}
};
} // namespace in_place
TEST(Optional, Inplace)
{
m5::stl::optional<int> o1{m5::stl::in_place};
m5::stl::optional<int> o2(m5::stl::in_place);
EXPECT_TRUE(o1);
EXPECT_TRUE(o1 == 0);
EXPECT_TRUE(o2);
EXPECT_TRUE(o2 == 0);
m5::stl::optional<int> o3(m5::stl::in_place, 42);
EXPECT_TRUE(o3 == 42);
m5::stl::optional<std::tuple<int, int>> o4(m5::stl::in_place, 0, 1);
EXPECT_TRUE(o4);
EXPECT_TRUE(std::get<0>(*o4) == 0);
EXPECT_TRUE(std::get<1>(*o4) == 1);
m5::stl::optional<std::vector<int>> o5(m5::stl::in_place, {0, 1});
EXPECT_TRUE(o5);
EXPECT_TRUE((*o5)[0] == 0);
EXPECT_TRUE((*o5)[1] == 1);
m5::stl::optional<in_place::takes_init_and_variadic> o6(m5::stl::in_place, {0, 1}, 2, 3);
EXPECT_TRUE(o6->v[0] == 0);
EXPECT_TRUE(o6->v[1] == 1);
EXPECT_TRUE(std::get<0>(o6->t) == 2);
EXPECT_TRUE(std::get<1>(o6->t) == 3);
}

View file

@ -0,0 +1,59 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
#include <type_traits>
namespace issues {
struct foo {
int& v()
{
return i;
}
int i = 0;
};
} // namespace issues
int& x(int& i)
{
i = 42;
return i;
}
TEST(Optional, issue14)
{
m5::stl::optional<issues::foo> f = issues::foo{};
auto v = f.map(&issues::foo::v).map(x);
static_assert(std::is_same<decltype(v), m5::stl::optional<int&>>::value, "Must return a reference");
EXPECT_TRUE(f->i == 42);
EXPECT_TRUE(*v == 42);
EXPECT_TRUE((&f->i) == (&*v));
}
struct fail_on_copy_self {
int value;
fail_on_copy_self(int v) : value(v)
{
}
fail_on_copy_self(const fail_on_copy_self& other) : value(other.value)
{
EXPECT_TRUE(&other != this);
}
};
TEST(Optional, issue15)
{
m5::stl::optional<fail_on_copy_self> o = fail_on_copy_self(42);
o = o;
EXPECT_TRUE(o->value == 42);
}
TEST(Optional, issue33)
{
int i = 0;
int j = 0;
m5::stl::optional<int&> a = i;
a.emplace(j);
*a = 42;
EXPECT_TRUE(j == 42);
EXPECT_TRUE(*a == 42);
EXPECT_TRUE(a.has_value());
}

View file

@ -0,0 +1,49 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
#include <tuple>
#include <vector>
namespace make_opt {
struct takes_init_and_variadic {
std::vector<int> v;
std::tuple<int, int> t;
template <class... Args>
takes_init_and_variadic(std::initializer_list<int> l, Args &&...args) : v(l), t(std::forward<Args>(args)...)
{
}
};
} // namespace make_opt
TEST(Optional, MakeOptional)
{
auto o1 = m5::stl::make_optional(42);
auto o2 = m5::stl::optional<int>(42);
constexpr bool is_same = std::is_same<decltype(o1), m5::stl::optional<int>>::value;
EXPECT_TRUE(is_same);
EXPECT_TRUE(o1 == o2);
auto o3 = m5::stl::make_optional<std::tuple<int, int, int, int>>(0, 1, 2, 3);
EXPECT_TRUE(std::get<0>(*o3) == 0);
EXPECT_TRUE(std::get<1>(*o3) == 1);
EXPECT_TRUE(std::get<2>(*o3) == 2);
EXPECT_TRUE(std::get<3>(*o3) == 3);
auto o4 = m5::stl::make_optional<std::vector<int>>({0, 1, 2, 3});
EXPECT_TRUE(o4.value()[0] == 0);
EXPECT_TRUE(o4.value()[1] == 1);
EXPECT_TRUE(o4.value()[2] == 2);
EXPECT_TRUE(o4.value()[3] == 3);
auto o5 = m5::stl::make_optional<make_opt::takes_init_and_variadic>({0, 1}, 2, 3);
EXPECT_TRUE(o5->v[0] == 0);
EXPECT_TRUE(o5->v[1] == 1);
EXPECT_TRUE(std::get<0>(o5->t) == 2);
EXPECT_TRUE(std::get<1>(o5->t) == 3);
auto i = 42;
auto o6 = m5::stl::make_optional<int &>(i);
EXPECT_TRUE((std::is_same<decltype(o6), m5::stl::optional<int &>>::value));
EXPECT_TRUE(o6);
EXPECT_TRUE(*o6 == 42);
}

View file

@ -0,0 +1,119 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
TEST(Optional, Noexcept)
{
m5::stl::optional<int> o1{4};
m5::stl::optional<int> o2{42};
{
SCOPED_TRACE("comparison with nullopt");
EXPECT_TRUE(noexcept(o1 == m5::stl::nullopt));
EXPECT_TRUE(noexcept(m5::stl::nullopt == o1));
EXPECT_TRUE(noexcept(o1 != m5::stl::nullopt));
EXPECT_TRUE(noexcept(m5::stl::nullopt != o1));
EXPECT_TRUE(noexcept(o1 < m5::stl::nullopt));
EXPECT_TRUE(noexcept(m5::stl::nullopt < o1));
EXPECT_TRUE(noexcept(o1 <= m5::stl::nullopt));
EXPECT_TRUE(noexcept(m5::stl::nullopt <= o1));
EXPECT_TRUE(noexcept(o1 > m5::stl::nullopt));
EXPECT_TRUE(noexcept(m5::stl::nullopt > o1));
EXPECT_TRUE(noexcept(o1 >= m5::stl::nullopt));
EXPECT_TRUE(noexcept(m5::stl::nullopt >= o1));
}
{
SCOPED_TRACE("swap");
// TODO see why this fails
#if !defined(_MSC_VER) || _MSC_VER > 1900
EXPECT_TRUE(noexcept(swap(o1, o2)) == noexcept(o1.swap(o2)));
struct nothrow_swappable {
nothrow_swappable &swap(const nothrow_swappable &) noexcept
{
return *this;
}
};
struct throw_swappable {
throw_swappable() = default;
throw_swappable(const throw_swappable &)
{
}
throw_swappable(throw_swappable &&)
{
}
throw_swappable &swap(const throw_swappable &)
{
return *this;
}
};
m5::stl::optional<nothrow_swappable> ont;
m5::stl::optional<throw_swappable> ot;
EXPECT_TRUE(noexcept(ont.swap(ont)));
EXPECT_TRUE(!noexcept(ot.swap(ot)));
#endif
}
{
SCOPED_TRACE("constructors");
// TODO see why this fails
#if !defined(_MSC_VER) || _MSC_VER > 1900
EXPECT_TRUE(noexcept(m5::stl::optional<int>{}));
EXPECT_TRUE(noexcept(m5::stl::optional<int>{m5::stl::nullopt}));
struct nothrow_move {
nothrow_move(nothrow_move &&) noexcept = default;
};
struct throw_move {
throw_move(throw_move &&)
{
}
};
using nothrow_opt = m5::stl::optional<nothrow_move>;
using throw_opt = m5::stl::optional<throw_move>;
EXPECT_TRUE(std::is_nothrow_move_constructible<nothrow_opt>::value);
EXPECT_TRUE(!std::is_nothrow_move_constructible<throw_opt>::value);
#endif
}
{
SCOPED_TRACE("assignment");
EXPECT_TRUE(noexcept(o1 = m5::stl::nullopt));
struct nothrow_move_assign {
nothrow_move_assign() = default;
nothrow_move_assign(nothrow_move_assign &&) noexcept = default;
nothrow_move_assign &operator=(const nothrow_move_assign &) = default;
};
struct throw_move_assign {
throw_move_assign() = default;
throw_move_assign(throw_move_assign &&)
{
}
throw_move_assign &operator=(const throw_move_assign &)
{
return *this;
}
};
using nothrow_opt = m5::stl::optional<nothrow_move_assign>;
using throw_opt = m5::stl::optional<throw_move_assign>;
EXPECT_TRUE(noexcept(std::declval<nothrow_opt>() = std::declval<nothrow_opt>()));
EXPECT_TRUE(!noexcept(std::declval<throw_opt>() = std::declval<throw_opt>()));
}
{
SCOPED_TRACE("observers");
EXPECT_TRUE(noexcept(static_cast<bool>(o1)));
EXPECT_TRUE(noexcept(o1.has_value()));
}
{
SCOPED_TRACE("modifiers");
EXPECT_TRUE(noexcept(o1.reset()));
}
}

View file

@ -0,0 +1,17 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
TEST(Optional, Nullopt)
{
m5::stl::optional<int> o1 = m5::stl::nullopt;
m5::stl::optional<int> o2{m5::stl::nullopt};
m5::stl::optional<int> o3(m5::stl::nullopt);
m5::stl::optional<int> o4 = {m5::stl::nullopt};
EXPECT_TRUE(!o1);
EXPECT_TRUE(!o2);
EXPECT_TRUE(!o3);
EXPECT_TRUE(!o4);
EXPECT_TRUE(!std::is_default_constructible<m5::stl::nullopt_t>::value);
}

View file

@ -0,0 +1,39 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
struct move_detector {
move_detector() = default;
move_detector(move_detector &&rhs)
{
rhs.been_moved = true;
}
bool been_moved = false;
};
TEST(Optional, Observers)
{
m5::stl::optional<int> o1 = 42;
m5::stl::optional<int> o2;
const m5::stl::optional<int> o3 = 42;
EXPECT_TRUE(*o1 == 42);
EXPECT_TRUE(*o1 == o1.value());
EXPECT_TRUE(o2.value_or(42) == 42);
EXPECT_TRUE(o3.value() == 42);
auto success = std::is_same<decltype(o1.value()), int &>::value;
EXPECT_TRUE(success);
success = std::is_same<decltype(o3.value()), const int &>::value;
EXPECT_TRUE(success);
success = std::is_same<decltype(std::move(o1).value()), int &&>::value;
EXPECT_TRUE(success);
#ifndef TL_OPTIONAL_NO_CONSTRR
success = std::is_same<decltype(std::move(o3).value()), const int &&>::value;
EXPECT_TRUE(success);
#endif
m5::stl::optional<move_detector> o4{m5::stl::in_place};
move_detector o5 = std::move(o4).value();
EXPECT_TRUE(o4->been_moved);
EXPECT_TRUE(!o5.been_moved);
}

View file

@ -0,0 +1,154 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
TEST(Optional, RelationalOps)
{
m5::stl::optional<int> o1{4};
m5::stl::optional<int> o2{42};
m5::stl::optional<int> o3{};
{
SCOPED_TRACE("self simple");
EXPECT_TRUE(!(o1 == o2));
EXPECT_TRUE(o1 == o1);
EXPECT_TRUE(o1 != o2);
EXPECT_TRUE(!(o1 != o1));
EXPECT_TRUE(o1 < o2);
EXPECT_TRUE(!(o1 < o1));
EXPECT_TRUE(!(o1 > o2));
EXPECT_TRUE(!(o1 > o1));
EXPECT_TRUE(o1 <= o2);
EXPECT_TRUE(o1 <= o1);
EXPECT_TRUE(!(o1 >= o2));
EXPECT_TRUE(o1 >= o1);
}
{
SCOPED_TRACE("nullopt simple");
EXPECT_TRUE(!(o1 == m5::stl::nullopt));
EXPECT_TRUE(!(m5::stl::nullopt == o1));
EXPECT_TRUE(o1 != m5::stl::nullopt);
EXPECT_TRUE(m5::stl::nullopt != o1);
EXPECT_TRUE(!(o1 < m5::stl::nullopt));
EXPECT_TRUE(m5::stl::nullopt < o1);
EXPECT_TRUE(o1 > m5::stl::nullopt);
EXPECT_TRUE(!(m5::stl::nullopt > o1));
EXPECT_TRUE(!(o1 <= m5::stl::nullopt));
EXPECT_TRUE(m5::stl::nullopt <= o1);
EXPECT_TRUE(o1 >= m5::stl::nullopt);
EXPECT_TRUE(!(m5::stl::nullopt >= o1));
EXPECT_TRUE(o3 == m5::stl::nullopt);
EXPECT_TRUE(m5::stl::nullopt == o3);
EXPECT_TRUE(!(o3 != m5::stl::nullopt));
EXPECT_TRUE(!(m5::stl::nullopt != o3));
EXPECT_TRUE(!(o3 < m5::stl::nullopt));
EXPECT_TRUE(!(m5::stl::nullopt < o3));
EXPECT_TRUE(!(o3 > m5::stl::nullopt));
EXPECT_TRUE(!(m5::stl::nullopt > o3));
EXPECT_TRUE(o3 <= m5::stl::nullopt);
EXPECT_TRUE(m5::stl::nullopt <= o3);
EXPECT_TRUE(o3 >= m5::stl::nullopt);
EXPECT_TRUE(m5::stl::nullopt >= o3);
}
{
SCOPED_TRACE("with T simple");
EXPECT_TRUE(!(o1 == 1));
EXPECT_TRUE(!(1 == o1));
EXPECT_TRUE(o1 != 1);
EXPECT_TRUE(1 != o1);
EXPECT_TRUE(!(o1 < 1));
EXPECT_TRUE(1 < o1);
EXPECT_TRUE(o1 > 1);
EXPECT_TRUE(!(1 > o1));
EXPECT_TRUE(!(o1 <= 1));
EXPECT_TRUE(1 <= o1);
EXPECT_TRUE(o1 >= 1);
EXPECT_TRUE(!(1 >= o1));
EXPECT_TRUE(o1 == 4);
EXPECT_TRUE(4 == o1);
EXPECT_TRUE(!(o1 != 4));
EXPECT_TRUE(!(4 != o1));
EXPECT_TRUE(!(o1 < 4));
EXPECT_TRUE(!(4 < o1));
EXPECT_TRUE(!(o1 > 4));
EXPECT_TRUE(!(4 > o1));
EXPECT_TRUE(o1 <= 4);
EXPECT_TRUE(4 <= o1);
EXPECT_TRUE(o1 >= 4);
EXPECT_TRUE(4 >= o1);
}
m5::stl::optional<std::string> o4{"hello"};
m5::stl::optional<std::string> o5{"xyz"};
{
SCOPED_TRACE("self complex");
EXPECT_TRUE(!(o4 == o5));
EXPECT_TRUE(o4 == o4);
EXPECT_TRUE(o4 != o5);
EXPECT_TRUE(!(o4 != o4));
EXPECT_TRUE(o4 < o5);
EXPECT_TRUE(!(o4 < o4));
EXPECT_TRUE(!(o4 > o5));
EXPECT_TRUE(!(o4 > o4));
EXPECT_TRUE(o4 <= o5);
EXPECT_TRUE(o4 <= o4);
EXPECT_TRUE(!(o4 >= o5));
EXPECT_TRUE(o4 >= o4);
}
{
SCOPED_TRACE("nullopt complex");
EXPECT_TRUE(!(o4 == m5::stl::nullopt));
EXPECT_TRUE(!(m5::stl::nullopt == o4));
EXPECT_TRUE(o4 != m5::stl::nullopt);
EXPECT_TRUE(m5::stl::nullopt != o4);
EXPECT_TRUE(!(o4 < m5::stl::nullopt));
EXPECT_TRUE(m5::stl::nullopt < o4);
EXPECT_TRUE(o4 > m5::stl::nullopt);
EXPECT_TRUE(!(m5::stl::nullopt > o4));
EXPECT_TRUE(!(o4 <= m5::stl::nullopt));
EXPECT_TRUE(m5::stl::nullopt <= o4);
EXPECT_TRUE(o4 >= m5::stl::nullopt);
EXPECT_TRUE(!(m5::stl::nullopt >= o4));
EXPECT_TRUE(o3 == m5::stl::nullopt);
EXPECT_TRUE(m5::stl::nullopt == o3);
EXPECT_TRUE(!(o3 != m5::stl::nullopt));
EXPECT_TRUE(!(m5::stl::nullopt != o3));
EXPECT_TRUE(!(o3 < m5::stl::nullopt));
EXPECT_TRUE(!(m5::stl::nullopt < o3));
EXPECT_TRUE(!(o3 > m5::stl::nullopt));
EXPECT_TRUE(!(m5::stl::nullopt > o3));
EXPECT_TRUE(o3 <= m5::stl::nullopt);
EXPECT_TRUE(m5::stl::nullopt <= o3);
EXPECT_TRUE(o3 >= m5::stl::nullopt);
EXPECT_TRUE(m5::stl::nullopt >= o3);
}
{
SCOPED_TRACE("with T complex");
EXPECT_TRUE(!(o4 == "a"));
EXPECT_TRUE(!("a" == o4));
EXPECT_TRUE(o4 != "a");
EXPECT_TRUE("a" != o4);
EXPECT_TRUE(!(o4 < "a"));
EXPECT_TRUE("a" < o4);
EXPECT_TRUE(o4 > "a");
EXPECT_TRUE(!("a" > o4));
EXPECT_TRUE(!(o4 <= "a"));
EXPECT_TRUE("a" <= o4);
EXPECT_TRUE(o4 >= "a");
EXPECT_TRUE(!("a" >= o4));
EXPECT_TRUE(o4 == "hello");
EXPECT_TRUE("hello" == o4);
EXPECT_TRUE(!(o4 != "hello"));
EXPECT_TRUE(!("hello" != o4));
EXPECT_TRUE(!(o4 < "hello"));
EXPECT_TRUE(!("hello" < o4));
EXPECT_TRUE(!(o4 > "hello"));
EXPECT_TRUE(!("hello" > o4));
EXPECT_TRUE(o4 <= "hello");
EXPECT_TRUE("hello" <= o4);
EXPECT_TRUE(o4 >= "hello");
EXPECT_TRUE("hello" >= o4);
}
}

View file

@ -0,0 +1,29 @@
#include <gtest/gtest.h>
#include <m5_utility/stl/optional.hpp>
TEST(Optional, SwapValue)
{
m5::stl::optional<int> o1 = 42;
m5::stl::optional<int> o2 = 12;
o1.swap(o2);
EXPECT_TRUE(o1.value() == 12);
EXPECT_TRUE(o2.value() == 42);
}
TEST(Optional, SwapValueWithNullIntialized)
{
m5::stl::optional<int> o1 = 42;
m5::stl::optional<int> o2 = m5::stl::nullopt;
o1.swap(o2);
EXPECT_TRUE(!o1.has_value());
EXPECT_TRUE(o2.value() == 42);
}
TEST(Optional, SwapNullIntializedWithValue)
{
m5::stl::optional<int> o1 = m5::stl::nullopt;
m5::stl::optional<int> o2 = 42;
o1.swap(o2);
EXPECT_TRUE(o1.value() == 42);
EXPECT_TRUE(!o2.has_value());
}

View file

@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
Each split by test_filter creates a separate binary. So they are combined by
including cpp. Note that the /optional files are not compiled directly if not
set to test_filter
*/
#include "optional/assignment.cpp"
#include "optional/bases.cpp"
#include "optional/constexpr.cpp"
#include "optional/constructors.cpp"
#include "optional/emplace.cpp"
#include "optional/extensions.cpp"
#include "optional/hash.cpp"
#include "optional/in_place.cpp"
#include "optional/issues.cpp"
#include "optional/make_optional.cpp"
#include "optional/noexcept.cpp"
#include "optional/nullopt.cpp"
#include "optional/observers.cpp"
#include "optional/relops.cpp"
#include "optional/swap.cpp"

View file

@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
*/
#include <gtest/gtest.h>
#include <M5Utility.hpp>
TEST(Utility, String)
{
std::string org = "\t\r\n\v STRING \v\n\r\t";
std::string s = org;
s = m5::utility::trim(s); // Call trimRight/Left in it
EXPECT_STREQ(s.c_str(), "STRING");
}
TEST(Utility, HexString)
{
{
std::pair<uint8_t, char> table_upper[] = {
{0, '0'}, {1, '1'}, {2, '2'}, {3, '3'}, {4, '4'}, {5, '5'}, {6, '6'}, {7, '7'}, {8, '8'},
{9, '9'}, {10, 'A'}, {11, 'B'}, {12, 'C'}, {13, 'D'}, {14, 'E'}, {15, 'F'}, {100, '4'},
};
std::pair<uint8_t, char> table_lower[] = {
{0, '0'}, {1, '1'}, {2, '2'}, {3, '3'}, {4, '4'}, {5, '5'}, {6, '6'}, {7, '7'}, {8, '8'},
{9, '9'}, {10, 'a'}, {11, 'b'}, {12, 'c'}, {13, 'd'}, {14, 'e'}, {15, 'f'}, {100, '4'},
};
for (auto&& e : table_upper) {
EXPECT_EQ(m5::utility::uintToHexChar<true>(e.first), e.second);
}
for (auto&& e : table_lower) {
EXPECT_EQ(m5::utility::uintToHexChar<false>(e.first), e.second);
}
}
std::string s;
{
uint8_t zero = 0;
uint8_t v = 0xA2;
s = m5::utility::unsignedToHexString(zero);
EXPECT_STREQ(s.c_str(), "00");
s = m5::utility::unsignedToHexString(v);
EXPECT_STREQ(s.c_str(), "A2");
}
{
uint16_t zero = 0;
uint16_t v = 0x0D51;
s = m5::utility::unsignedToHexString(zero);
EXPECT_STREQ(s.c_str(), "0000");
s = m5::utility::unsignedToHexString(v);
EXPECT_STREQ(s.c_str(), "0D51");
}
{
uint32_t zero = 0;
uint32_t v = 0xBEAF1234;
s = m5::utility::unsignedToHexString(zero);
EXPECT_STREQ(s.c_str(), "00000000");
s = m5::utility::unsignedToHexString(v);
EXPECT_STREQ(s.c_str(), "BEAF1234");
}
{
uint64_t zero = 0;
uint64_t v = 0x5252DEADBEAF0303;
s = m5::utility::unsignedToHexString(zero);
EXPECT_STREQ(s.c_str(), "0000000000000000");
s = m5::utility::unsignedToHexString(v);
EXPECT_STREQ(s.c_str(), "5252DEADBEAF0303");
}
}

View file

@ -0,0 +1,181 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
*/
#include <gtest/gtest.h>
#include <M5Utility.hpp>
using namespace m5::types;
TEST(Utility, Types_U16)
{
// Constructor
constexpr big_uint16_t bg0;
constexpr little_uint16_t lt0;
EXPECT_EQ(bg0.u16, 0U);
EXPECT_EQ(lt0.u16, 0U);
big_uint16_t bg1{0x1234};
little_uint16_t lt1{0x1234};
EXPECT_EQ(bg1.u8[0], 0x12);
EXPECT_EQ(bg1.u8[1], 0x34);
EXPECT_EQ(lt1.u8[0], 0x34);
EXPECT_EQ(lt1.u8[1], 0x12);
constexpr big_uint16_t bg2{0x12, 0x34};
constexpr little_uint16_t lt2{0x12, 0x34};
EXPECT_EQ(bg2.u8[0], 0x12);
EXPECT_EQ(bg2.u8[1], 0x34);
EXPECT_EQ(lt2.u8[0], 0x12);
EXPECT_EQ(lt2.u8[1], 0x34);
big_uint16_t bg3 = bg1;
little_uint16_t lt3 = lt1;
EXPECT_EQ(bg3.u8[0], 0x12);
EXPECT_EQ(bg3.u8[1], 0x34);
EXPECT_EQ(lt3.u8[0], 0x34);
EXPECT_EQ(lt3.u8[1], 0x12);
big_uint16_t bg4 = std::move(bg3);
little_uint16_t lt4 = std::move(lt3);
EXPECT_EQ(bg4.u8[0], 0x12);
EXPECT_EQ(bg4.u8[1], 0x34);
EXPECT_EQ(lt4.u8[0], 0x34);
EXPECT_EQ(lt4.u8[1], 0x12);
// Assignment
big_uint16_t bg5;
little_uint16_t lt5;
bg5 = bg4;
lt5 = lt4;
EXPECT_EQ(bg5.u8[0], 0x12);
EXPECT_EQ(bg5.u8[1], 0x34);
EXPECT_EQ(lt5.u8[0], 0x34);
EXPECT_EQ(lt5.u8[1], 0x12);
big_uint16_t bg60;
big_uint16_t bg61;
little_uint16_t lt60;
little_uint16_t lt61;
if (m5::endian::little) {
bg60.operator=<false>(0x3412); // big to big
bg61.operator=<true>(0x1234); // little to big
lt60.operator=<false>(0x3412); // big to little
lt61.operator=<true>(0x1234); // little to little
} else {
bg60.operator=<false>(0x1234); // big to big
bg61.operator=<true>(0x3412); // little to big
lt60.operator=<false>(0x1234); // big to little
lt61.operator=<true>(0x3412); // little to little
}
EXPECT_EQ(bg60.u8[0], 0x12);
EXPECT_EQ(bg60.u8[1], 0x34);
EXPECT_EQ(bg61.u8[0], 0x12);
EXPECT_EQ(bg61.u8[1], 0x34);
EXPECT_EQ(lt60.u8[0], 0x34);
EXPECT_EQ(lt60.u8[1], 0x12);
EXPECT_EQ(lt61.u8[0], 0x34);
EXPECT_EQ(lt61.u8[1], 0x12);
big_uint16_t bg7;
little_uint16_t lt7;
bg7 = std::make_pair<int, int>(0x12, 0x34);
lt7 = std::make_pair<int, int>(0x12, 0x34);
EXPECT_EQ(bg7.u8[0], 0x12);
EXPECT_EQ(bg7.u8[1], 0x34);
EXPECT_EQ(lt7.u8[0], 0x12);
EXPECT_EQ(lt7.u8[1], 0x34);
// Compile error (static_assert)
// bg7 = std::make_pair<float, double>( 1.2f, 3.4 );
// Cast
EXPECT_FALSE((bool)bg0);
EXPECT_FALSE((bool)lt0);
EXPECT_TRUE((bool)bg1);
EXPECT_TRUE((bool)lt1);
EXPECT_EQ((uint16_t)bg0, 00U);
EXPECT_EQ((uint16_t)lt0, 00U);
EXPECT_EQ((uint16_t)bg1, 0x1234);
EXPECT_EQ((uint16_t)lt1, 0x1234);
EXPECT_EQ(*(const uint8_t*)bg0, 0U);
EXPECT_EQ(*(const uint8_t*)lt0, 0U);
EXPECT_EQ(*(const uint8_t*)bg1, 0x12U);
EXPECT_EQ(*(const uint8_t*)lt1, 0x34U);
// set() is using in operator=
// get() is using in cast to uint16
// data() is using in cast to const uint8_t*
}
TEST(Utility, Types_U16_Compare)
{
big_uint16_t bg0{0x1234};
little_uint16_t lt0{0x1234};
EXPECT_EQ(bg0, lt0);
EXPECT_GE(bg0, lt0);
EXPECT_LE(bg0, lt0);
EXPECT_FALSE(bg0 < lt0);
EXPECT_FALSE(bg0 > lt0);
{
big_uint16_t bg1{0x1235};
little_uint16_t lt1{0x1235};
EXPECT_NE(bg0, bg1);
EXPECT_NE(bg0, lt1);
EXPECT_LT(bg0, bg1);
EXPECT_LT(bg0, lt1);
EXPECT_LE(bg0, bg1);
EXPECT_LE(bg0, lt1);
EXPECT_GE(bg1, bg0);
EXPECT_GE(lt1, bg0);
EXPECT_GT(bg1, bg0);
EXPECT_GT(lt1, bg0);
EXPECT_NE(lt0, bg1);
EXPECT_NE(lt0, lt1);
EXPECT_LT(lt0, bg1);
EXPECT_LT(lt0, lt1);
EXPECT_LE(lt0, bg1);
EXPECT_LE(lt0, lt1);
EXPECT_GE(bg1, lt0);
EXPECT_GE(lt1, lt0);
EXPECT_GT(bg1, lt0);
EXPECT_GT(lt1, lt0);
}
{
big_uint16_t bg1{0x1334};
little_uint16_t lt1{0x1334};
EXPECT_NE(bg0, bg1);
EXPECT_NE(bg0, lt1);
EXPECT_LT(bg0, bg1);
EXPECT_LT(bg0, lt1);
EXPECT_LE(bg0, bg1);
EXPECT_LE(bg0, lt1);
EXPECT_GE(bg1, bg0);
EXPECT_GE(lt1, bg0);
EXPECT_GT(bg1, bg0);
EXPECT_GT(lt1, bg0);
EXPECT_NE(lt0, bg1);
EXPECT_NE(lt0, lt1);
EXPECT_LT(lt0, bg1);
EXPECT_LT(lt0, lt1);
EXPECT_LE(lt0, bg1);
EXPECT_LE(lt0, lt1);
EXPECT_GE(bg1, lt0);
EXPECT_GE(lt1, lt0);
EXPECT_GT(bg1, lt0);
EXPECT_GT(lt1, lt0);
}
}

View file

@ -0,0 +1,333 @@
/*
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
UnitTest for M5Utility
*/
#include <gtest/gtest.h>
#include <M5Utility.hpp>
#include <random>
#include <chrono>
#include <thread>
using namespace m5::utility::log;
namespace {
using pf_t = std::pair<const char* /* full path */, const char* /* filename */>;
pf_t table[] = {
{"", ""}, {nullptr, ""},
{"aaa", "aaa"}, {"ソソソソ", "ソソソソ"},
{"a/b.c", "b.c"}, {"c:/aaa/bbb/ccc/ddd.eee", "ddd.eee"},
};
} // namespace
TEST(Utility, pathToFilename)
{
{
constexpr auto fn0 = pathToFilename("");
EXPECT_STREQ(fn0, "");
constexpr auto fn1 = pathToFilename("ABC");
EXPECT_STREQ(fn1, "ABC");
constexpr auto fn2 = pathToFilename("a:/bb/ccc/dddd/eee.f");
EXPECT_STREQ(fn2, "eee.f");
}
for (auto&& e : table) {
EXPECT_STREQ(pathToFilename(e.first), e.second) << e.first;
}
}
#if 0
TEST(Utility, log) {
M5_LIB_LOGE("Error");
M5_LIB_LOGW("Warn");
M5_LIB_LOGI("Info");
M5_LIB_LOGD("Debug");
M5_LIB_LOGV("Verbose");
constexpr uint8_t test[] = {
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23,
0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67,
0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB,
0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
};
M5_DUMPE(test + 0, sizeof(test) - 0);
M5_DUMPE(test + 1, sizeof(test) - 1);
M5_DUMPE(test + 7, sizeof(test) - 07;
M5_DUMPE(test + 11, sizeof(test) - 11);
M5_DUMPE(test + 19, sizeof(test) - 19);
}
#endif
namespace {
auto rng = std::default_random_engine{};
}
TEST(Utility, BitSegment)
{
// static_assert
#if 0
{
m5::utility::BitSegment<11, int8_t> compile_error1; // 11 > 8
m5::utility::BitSegment<0, int8_t> copile_error2; // 0
}
#endif
// Constructor/Assignemt
{
using bs_t = m5::utility::BitSegment<6, uint8_t>;
bs_t v0{0x84}; // base_type constructor
bs_t v1 = v0; // same type constructor
EXPECT_EQ(v0.raw(), 0x84);
EXPECT_EQ(v0.upper(), v1.upper());
EXPECT_EQ(v0.lower(), v1.lower());
EXPECT_EQ(v0.raw(), v1.raw());
bs_t v2, v3;
EXPECT_EQ(v2.raw(), 0);
EXPECT_EQ(v2.raw(), v3.raw());
v2 = -1; // base_type assignment
EXPECT_NE(v2.raw(), v3.raw());
v3 = v2; // same type assignment
EXPECT_EQ(v2.raw(), v3.raw());
bs_t v4 = 0xC0; // base_type constructor
EXPECT_EQ(v4.raw(), 0xC0);
}
// Getter/Setter using signed
{
using bs1_t = m5::utility::BitSegment<12, int16_t>;
auto t = std::is_same<int16_t, bs1_t::base_type>::value;
EXPECT_TRUE(t);
t = std::is_same<uint16_t, bs1_t::unsigned_type>::value;
EXPECT_TRUE(t);
EXPECT_TRUE(bs1_t::SIGNED);
EXPECT_EQ(+bs1_t::UPPER_BITS, 3);
EXPECT_EQ(+bs1_t::LOWER_BITS, 12);
EXPECT_EQ(+bs1_t::UPPER_SHIFT, 12);
EXPECT_EQ(+bs1_t::UPPER_MASK, 0x07);
EXPECT_EQ(+bs1_t::LOWER_MASK, 0xFFF);
bs1_t value0;
EXPECT_EQ(value0.upper(), 0U);
EXPECT_EQ(value0.lower(), 0U);
EXPECT_EQ(value0.raw(), 0);
value0.lower(123);
EXPECT_EQ(value0.upper(), 0U);
EXPECT_EQ(value0.lower(), 123U);
EXPECT_EQ(value0.raw(), 123);
value0.lower(0x7FFF);
EXPECT_EQ(value0.upper(), 0U);
EXPECT_EQ(value0.lower(), 0x0FFFU);
EXPECT_EQ(value0.raw(), 0x0FFF);
value0.upper(5);
EXPECT_EQ(value0.upper(), 5U);
EXPECT_EQ(value0.lower(), 0xFFFU);
EXPECT_EQ(value0.raw(), 0x5FFF);
value0.upper(0xFF);
EXPECT_EQ(value0.upper(), 0x0007U);
EXPECT_EQ(value0.lower(), 0x0FFFU);
EXPECT_EQ(value0.raw(), 0x7FFF);
value0.raw(-1);
EXPECT_EQ(value0.upper(), 0x0007U);
EXPECT_EQ(value0.lower(), 0x0FFFU);
EXPECT_EQ(value0.raw(), -1);
// Keep signed bit
value0.lower(234);
EXPECT_EQ(value0.upper(), 0x0007U);
EXPECT_EQ(value0.lower(), 234U);
EXPECT_EQ(value0.raw(), -3862); // 0xF0EA
}
// Getter/Setter using unsigned
{
using bs2_t = m5::utility::BitSegment<12, const uint32_t&>;
auto t = std::is_same<uint32_t, bs2_t::base_type>::value;
EXPECT_TRUE(t);
t = std::is_same<uint32_t, bs2_t::unsigned_type>::value;
EXPECT_TRUE(t);
EXPECT_FALSE(bs2_t::SIGNED);
EXPECT_EQ(+bs2_t::UPPER_BITS, 20U);
EXPECT_EQ(+bs2_t::LOWER_BITS, 12U);
EXPECT_EQ(+bs2_t::UPPER_SHIFT, 12U);
EXPECT_EQ(+bs2_t::UPPER_MASK, 0xFFFFFU);
EXPECT_EQ(+bs2_t::LOWER_MASK, 0xFFFU);
bs2_t value0;
EXPECT_EQ(value0.upper(), 0U);
EXPECT_EQ(value0.lower(), 0U);
EXPECT_EQ(value0.raw(), 0U);
value0.lower(123);
EXPECT_EQ(value0.upper(), 0U);
EXPECT_EQ(value0.lower(), 123U);
EXPECT_EQ(value0.raw(), 123U);
value0.lower(0x00003FFF);
EXPECT_EQ(value0.upper(), 0U);
EXPECT_EQ(value0.lower(), 0x00000FFFU);
EXPECT_EQ(value0.raw(), 0x00000FFFU);
value0.upper(5);
EXPECT_EQ(value0.upper(), 5U);
EXPECT_EQ(value0.lower(), 0x00000FFFU);
EXPECT_EQ(value0.raw(), 0x00005FFFU);
value0.upper(0x84218421);
EXPECT_EQ(value0.upper(), 0x00018421U);
EXPECT_EQ(value0.lower(), 0x00000FFFU);
EXPECT_EQ(value0.raw(), 0x18421FFFU);
value0.raw(-1);
EXPECT_EQ(value0.upper(), 0x000FFFFFU);
EXPECT_EQ(value0.lower(), 0x00000FFFU);
EXPECT_EQ(value0.raw(), 0xFFFFFFFFU);
// Not include signed bit
value0.lower(234);
EXPECT_EQ(value0.upper(), 0x000FFFFFU);
EXPECT_EQ(value0.lower(), 234U);
EXPECT_EQ(value0.raw(), 4294963434U); // 0xFFFFF0EA
}
// Compare
{
using bs_t = m5::utility::BitSegment<14, int64_t>;
bs_t v0, v1;
EXPECT_TRUE(v0 == v1);
EXPECT_FALSE(v0 != v1);
EXPECT_FALSE(v0 < v1);
EXPECT_FALSE(v0 > v1);
EXPECT_TRUE(v0 <= v1);
EXPECT_TRUE(v0 >= v1);
EXPECT_TRUE(v0 == 0);
EXPECT_FALSE(v0 != 0);
EXPECT_FALSE(v0 < 0);
EXPECT_FALSE(v0 > 0);
EXPECT_TRUE(v0 <= 0);
EXPECT_TRUE(v0 >= 0);
EXPECT_TRUE(0 == v0);
EXPECT_FALSE(0 != v0);
EXPECT_FALSE(0 < v0);
EXPECT_FALSE(0 > v0);
EXPECT_TRUE(0 <= v0);
v1.lower(123);
EXPECT_FALSE(v0 == v1);
EXPECT_TRUE(v0 != v1);
EXPECT_TRUE(v0 < v1);
EXPECT_FALSE(v0 > v1);
EXPECT_TRUE(v0 <= v1);
EXPECT_FALSE(v0 >= v1);
v0.lower(987);
EXPECT_FALSE(v0 == v1);
EXPECT_TRUE(v0 != v1);
EXPECT_FALSE(v0 < v1);
EXPECT_TRUE(v0 > v1);
EXPECT_FALSE(v0 <= v1);
EXPECT_TRUE(v0 >= v1);
v0 = v1;
v1.upper(654321);
EXPECT_FALSE(v0 == v1);
EXPECT_TRUE(v0 != v1);
EXPECT_TRUE(v0 < v1);
EXPECT_FALSE(v0 > v1);
EXPECT_TRUE(v0 <= v1);
EXPECT_FALSE(v0 >= v1);
v0.upper(99999999);
EXPECT_FALSE(v0 == v1);
EXPECT_TRUE(v0 != v1);
EXPECT_FALSE(v0 < v1);
EXPECT_TRUE(v0 > v1);
EXPECT_FALSE(v0 <= v1);
EXPECT_TRUE(v0 >= v1);
v0 = v1 = 0;
v0 = -123;
v1 = -12;
EXPECT_FALSE(v0 == v1);
EXPECT_TRUE(v0 != v1);
EXPECT_TRUE(v0 < v1);
EXPECT_FALSE(v0 > v1);
EXPECT_TRUE(v0 <= v1);
EXPECT_FALSE(v0 >= v1);
v1 = -876543;
EXPECT_FALSE(v0 == v1);
EXPECT_TRUE(v0 != v1);
EXPECT_FALSE(v0 < v1);
EXPECT_TRUE(v0 > v1);
EXPECT_FALSE(v0 <= v1);
EXPECT_TRUE(v0 >= v1);
}
}
namespace {
using clock = std::chrono::high_resolution_clock;
}
TEST(Utility, comatibility)
{
// millis
{
auto ms = m5::utility::millis();
auto ams = clock::now() + std::chrono::milliseconds(1);
std::this_thread::sleep_until(ams);
EXPECT_GT(m5::utility::millis(), ms);
}
// micros
{
auto us = m5::utility::micros();
auto aus = clock::now() + std::chrono::microseconds(1);
std::this_thread::sleep_until(aus);
EXPECT_GT(m5::utility::micros(), us);
}
// delay
{
constexpr unsigned long wait{20};
auto start = clock::now();
m5::utility::delay(wait);
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - start).count();
auto elapsed2 = std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - start).count();
// printf("-----> %lld %lld\n", elapsed, elapsed2);
#if defined(ARDUINO)
EXPECT_GE(elapsed,
wait - 1); // Arduino delay may return before the specified time.
#else
EXPECT_GE(elapsed, wait);
#endif
}
// delayMicroseconds
{
auto start = clock::now();
m5::utility::delayMicroseconds(1);
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - start).count();
EXPECT_GE(elapsed, 1);
}
}