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