Files
pip/libs/main/core/pibase.h
2025-01-06 12:55:44 +03:00

756 lines
22 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*! \file pibase.h
* \ingroup Core
* \~\brief
* \~english Base types and functions
* \~russian Базовые типы и методы
*
* \~\details
* \~english
* This file implements first layer above the system and
* declares some basic useful functions
* \~russian
* Этот файл реализует первый слой после системы и объявляет
* несколько базовых полезных методов
*/
/*
PIP - Platform Independent Primitives
Base types and functions
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PIBASE_H
#define PIBASE_H
#include "pibase_macros.h"
#include "pimemoryblock.h"
#include "pip_export.h"
#include <string.h>
#ifdef CC_AVR_GCC
# include <ArduinoSTL.h>
#endif
#include <atomic>
#include <cassert>
#include <cstddef>
#include <cstdio>
#include <functional>
#include <initializer_list>
#include <limits>
//! \~\brief
//! \~english Global variable enabling output to piCout, default is true
//! \~russian Глобальная переменная, включающая вывод в piCout, при старте true
extern PIP_EXPORT bool piDebug;
//! \~\brief
//! \~english Global variable that set minimum real update interval
//! for function PIInit::mountInfo(), default is 10000 ms
//! \~russian Глобальная переменная минимального ожидания между реальным обновлением
//! в методе PIInit::mountInfo(), по умолчанию 10000 мс
extern PIP_EXPORT double piMountInfoRefreshIntervalMs;
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned long long ullong;
typedef long long llong;
typedef long double ldouble;
//! \~\brief
//! \~english Templated function for swap two values
//! \~russian Шаблонный метод для перестановки двух значений
//! \~\details
//! \~english Example:\n \snippet piincludes.cpp swap
//! \~russian Пример:\n \snippet piincludes.cpp swap
template<typename T>
inline void piSwap(T & f, T & s) {
T t(std::move(f));
f = std::move(s);
s = std::move(t);
}
//! \~\brief
//! \~english Templated function for swap two values without "="
//! \~russian Шаблонный метод для перестановки двух значений без использования "="
//! \~\details
//! \~english Example:\n \snippet piincludes.cpp swapBinary
//! \~russian Пример:\n \snippet piincludes.cpp swapBinary
template<typename T>
inline void piSwapBinary(T & f, T & s) {
if ((size_t *)&f == (size_t *)&s) return;
size_t j = (sizeof(T) / sizeof(size_t)), bs = j * sizeof(size_t), bf = sizeof(T);
size_t i = 0;
for (i = 0; i < j; ++i) {
((size_t *)(&f))[i] ^= ((size_t *)(&s))[i];
((size_t *)(&s))[i] ^= ((size_t *)(&f))[i];
((size_t *)(&f))[i] ^= ((size_t *)(&s))[i];
}
for (i = bs; i < bf; ++i) {
((uchar *)(&f))[i] ^= ((uchar *)(&s))[i];
((uchar *)(&s))[i] ^= ((uchar *)(&f))[i];
((uchar *)(&f))[i] ^= ((uchar *)(&s))[i];
}
}
template<>
inline void piSwapBinary(const void *& f, const void *& s) {
if ((size_t *)f == (size_t *)s) return;
size_t j = (sizeof(void *) / sizeof(size_t)), bs = j * sizeof(size_t), bf = sizeof(void *);
size_t i = 0;
void *pf = const_cast<void *>(f), *ps = const_cast<void *>(s);
for (i = 0; i < j; ++i) {
((size_t *)(&pf))[i] ^= ((size_t *)(&ps))[i];
((size_t *)(&ps))[i] ^= ((size_t *)(&pf))[i];
((size_t *)(&pf))[i] ^= ((size_t *)(&ps))[i];
}
for (i = bs; i < bf; ++i) {
((uchar *)(&pf))[i] ^= ((uchar *)(&ps))[i];
((uchar *)(&ps))[i] ^= ((uchar *)(&pf))[i];
((uchar *)(&pf))[i] ^= ((uchar *)(&ps))[i];
}
}
//! \~\brief
//! \~english Function for compare two values without "==" by raw content
//! \~russian Метод для сравнения двух значений без использования "==" (по сырому содержимому)
//! \~\details
//! \~english Example:\n \snippet piincludes.cpp compareBinary
//! \~russian Пример:\n \snippet piincludes.cpp compareBinary
inline bool piCompareBinary(const void * f, const void * s, size_t size) {
return 0 == memcmp(f, s, size);
}
//! \~\brief
//! \~english Templated function return round of float falue
//! \~russian Шаблонный метод, возвращающий округленное значение
//! \~\details
//! \~english
//! Round is the nearest integer value \n
//! There are some macros:
//! - \c piRoundf for "float"
//! - \c piRoundd for "double"
//!
//! Example:
//! \snippet piincludes.cpp round
//! \~russian
//! Округленное значение - это ближайшее целое число\n
//! Есть несколько макросов:
//! - \c piRoundf для "float"
//! - \c piRoundd для "double"
//!
//! Пример:
//! \snippet piincludes.cpp round
template<typename T>
inline constexpr int piRound(const T & v) {
return int(v >= T(0.) ? v + T(0.5) : v - T(0.5));
}
//! \~\brief
//! \~english Templated function return floor of float falue
//! \~russian Шаблонный метод, возвращающий floor значение
//! \~\details
//! \~english
//! Floor is the largest integer that is not greater than "v" \n
//! There are some macros:
//! - \c piFloorf for "float"
//! - \c piFloord for "double"
//!
//! Example:
//! \snippet piincludes.cpp floor
//! \~russian
//! Floor значение - это наибольшее целое, не большее чем "v"\n
//! Есть несколько макросов:
//! - \c piFloorf для "float"
//! - \c piFloord для "double"
//!
//! Пример:
//! \snippet piincludes.cpp floor
template<typename T>
inline constexpr int piFloor(const T & v) {
return v < T(0) ? int(v) - 1 : int(v);
}
//! \~\brief
//! \~english Templated function return ceil of float falue
//! \~russian Шаблонный метод, возвращающий ceil значение
//! \~\details
//! \~english
//! Ceil is the smallest integer that is not less than "v" \n
//! There are some macros:
//! - \c piCeilf for "float"
//! - \c piCeild for "double"
//!
//! Example:
//! \snippet piincludes.cpp ceil
//! \~russian
//! Ceil значение - это наименьшее целое, не меньшее чем "v" \n
//! Есть несколько макросов:
//! - \c piCeilf для "float"
//! - \c piCeild для "double"
//!
//! Пример:
//! \snippet piincludes.cpp ceil
template<typename T>
inline constexpr int piCeil(const T & v) {
return v < T(0) ? int(v) : int(v) + 1;
}
//! \~\brief
//! \~english Templated function return absolute of numeric falue
//! \~russian Шаблонный метод, возвращающий модуль числового значения
//! \~\details
//! \~english
//! Absolute is the positive or equal 0 value \n
//! There are some macros:
//! - \c piAbss for "short"
//! - \c piAbsi for "int"
//! - \c piAbsl for "long"
//! - \c piAbsll for "llong"
//! - \c piAbsf for "float"
//! - \c piAbsd for "double"
//!
//! Example:
//! \snippet piincludes.cpp abs
//! \~russian
//! Модуль числового значения всегда >= 0 \n
//! Есть несколько макросов:
//! - \c piAbss для "short"
//! - \c piAbsi для "int"
//! - \c piAbsl для "long"
//! - \c piAbsll для "llong"
//! - \c piAbsf для "float"
//! - \c piAbsd для "double"
//!
//! Пример:
//! \snippet piincludes.cpp abs
template<typename T>
inline constexpr T piAbs(const T & v) {
return (v >= T(0) ? v : -v);
}
template<typename T>
constexpr T piMin(const T & f, const T & s) {
return ((f > s) ? s : f);
}
//! \~\brief
//! \~english Templated function return minimum of several values
//! \~russian Шаблонный метод, возвращающий минимум из нескольких значений
//! \~\details
//! \~english
//! There are some macros:
//! - \c piMins for "short"
//! - \c piMini for "int"
//! - \c piMinl for "long"
//! - \c piMinll for "llong"
//! - \c piMinf for "float"
//! - \c piMind for "double"
//!
//! Example:
//! \snippet piincludes.cpp min2
//! \~russian
//! Есть несколько макросов:
//! - \c piMins для "short"
//! - \c piMini для "int"
//! - \c piMinl для "long"
//! - \c piMinll для "llong"
//! - \c piMinf для "float"
//! - \c piMind для "double"
//!
//! Пример:
//! \snippet piincludes.cpp min2
template<typename T, typename... Args>
constexpr T piMin(const T & f, const T & s, const Args &... args) {
return piMin<T>(piMin<T>(f, s), args...);
}
template<typename T>
constexpr T piMax(const T & f, const T & s) {
return ((f < s) ? s : f);
}
//! \~\brief
//! \~english Templated function return maximum of several values
//! \~russian Шаблонный метод, возвращающий максимум из нескольких значений
//! \~\details
//! \~english
//! There are some macros:
//! - \c piMaxs for "short"
//! - \c piMaxi for "int"
//! - \c piMaxl for "long"
//! - \c piMaxll for "llong"
//! - \c piMaxf for "float"
//! - \c piMaxd for "double"
//!
//! Example:
//! \snippet piincludes.cpp max2
//! \~russian
//! Есть несколько макросов:
//! - \c piMaxs для "short"
//! - \c piMaxi для "int"
//! - \c piMaxl для "long"
//! - \c piMaxll для "llong"
//! - \c piMaxf для "float"
//! - \c piMaxd для "double"
//!
//! Пример:
//! \snippet piincludes.cpp max2
template<typename T, typename... Args>
constexpr T piMax(const T & f, const T & s, const Args &... args) {
return piMax<T>(piMax<T>(f, s), args...);
}
//! \~\brief
//! \~english Templated function return clamped value
//! \~russian Шаблонный метод, возвращающий ограниченное значение
//! \~\details
//! \~english
//! Clamped is the not greater than "max" and not lesser than "min" value \n
//! There are some macros:
//! - \c piClamps for "short"
//! - \c piClampi for "int"
//! - \c piClampl for "long"
//! - \c piClampll for "llong"
//! - \c piClampf for "float"
//! - \c piClampd for "double"
//!
//! Example:
//! \snippet piincludes.cpp clamp
//! \~russian
//! Ограниченное значение - не больше чем "max" и не меньше чем "min"
//! Есть несколько макросов:
//! - \c piClamps для "short"
//! - \c piClampi для "int"
//! - \c piClampl для "long"
//! - \c piClampll для "llong"
//! - \c piClampf для "float"
//! - \c piClampd для "double"
//!
//! Пример:
//! \snippet piincludes.cpp clamp
template<typename T>
inline constexpr T piClamp(const T & v, const T & min, const T & max) {
return (v > max ? max : (v < min ? min : v));
}
//! \~\brief
//! \~english Function for compare two numeric values with epsilon
//! \~russian Метод для сравнения двух чисел с порогом
//! \~\details
//! \~english
//! There are some macros:
//! - \c piComparef for "float"
//! - \c piCompared for "double"
//!
//! Example:
//! \snippet piincludes.cpp compare
//! \~russian
//! Есть несколько макросов:
//! - \c piComparef для "float"
//! - \c piCompared для "double"
//!
//! Пример:
//! \snippet piincludes.cpp compare
template<typename T>
inline bool piCompare(const T & a, const T & b, const T & epsilon = std::numeric_limits<T>::epsilon()) {
return piAbs(a - b) <= epsilon;
}
// Endians
//! \~\brief
//! \~english Function inverse byte order in memory block ([1..N] -> [N..1])
//! \~russian Метод для смены порядка байт в блоке памяти ([1..N] -> [N..1])
inline void piChangeEndianBinary(void * data, size_t size) {
auto hs = size / 2;
for (size_t i = 0; i < hs; i++)
piSwap<uchar>(((uchar *)data)[size - i - 1], ((uchar *)data)[i]);
}
//! \~\brief
//! \~english Function inverse byte order in memory block ([1..N] -> [N..1])
//! \~russian Метод для смены порядка байт в блоке памяти ([1..N] -> [N..1])
inline void piChangeEndianBinary(PIMemoryBlock mem_blk) {
if (mem_blk.isNull()) return;
piChangeEndianBinary(mem_blk.data(), mem_blk.size());
}
//! \~\brief
//! \~english Templated function that inverse byte order of value "v"
//! \~russian Шаблонный метод, меняющий порядок байт в переменной "v"
template<typename T>
inline void piChangeEndian(T & v);
//! \~\brief
//! \~english Templated function that returns value "v" with inversed byte order
//! \~russian Шаблонный метод, возвращающий значение "v" с измененным порядком байт
template<typename T>
inline T piChangedEndian(const T & v);
template<typename T>
inline void piChangeEndian(T & v) {
piChangeEndianBinary(&v, sizeof(T));
}
template<typename T>
inline T piChangedEndian(const T & v) {
T ret = v;
piChangeEndianBinary(&ret, sizeof(T));
return ret;
}
template<>
inline uint16_t piChangedEndian(const uint16_t & v) {
return (v << 8) | (v >> 8);
}
template<>
inline uint32_t piChangedEndian(const uint32_t & v) {
return (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | ((v << 24) & 0xFF000000);
}
template<>
inline float piChangedEndian(const float & v) {
union {
float f;
uint32_t i;
} u;
u.f = v;
u.i = piChangedEndian(u.i);
return u.f;
}
template<>
inline void piChangeEndian(uint16_t & v) {
v = piChangedEndian(v);
}
template<>
inline void piChangeEndian(uint32_t & v) {
v = piChangedEndian(v);
}
template<>
inline void piChangeEndian(float & v) {
v = piChangedEndian(v);
}
DEPRECATEDM("use piChangeEndianBinary()")
inline void piLetobe(void * data, int size) {
piChangeEndianBinary(data, size);
}
template<typename T>
DEPRECATEDM("use piChangedEndian()")
inline T piLetobe(const T & v) {
return piChangedEndian(v);
}
//! \~\brief
//! \~english Generic hash function, implements murmur3/32 algorithm
//! \~russian Хэш-функция общего назначения, по алгоритму murmur3/32
inline uint piHashData(const uchar * data, uint len, uint seed = 0) {
if (!data || len <= 0) return 0u;
uint h = seed;
if (len > 3) {
uint i = len >> 2;
do {
uint k;
memcpy(&k, data, sizeof(uint));
data += sizeof(uint);
k *= 0xcc9e2d51;
k = (k << 15) | (k >> 17);
k *= 0x1b873593;
h ^= k;
h = (h << 13) | (h >> 19);
h = h * 5 + 0xe6546b64;
} while (--i);
}
if (len & 3) {
uint i = len & 3;
uint k = 0;
do {
k <<= 8;
k |= data[i - 1];
} while (--i);
k *= 0xcc9e2d51;
k = (k << 15) | (k >> 17);
k *= 0x1b873593;
h ^= k;
}
h ^= len;
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
template<typename T>
inline uint piHash(const T & v) {
return 0;
}
template<>
inline uint piHash(const char & v) {
return (uint)v;
}
template<>
inline uint piHash(const uchar & v) {
return (uint)v;
}
template<>
inline uint piHash(const short & v) {
return (uint)v;
}
template<>
inline uint piHash(const ushort & v) {
return (uint)v;
}
template<>
inline uint piHash(const int & v) {
return (uint)v;
}
template<>
inline uint piHash(const uint & v) {
return (uint)v;
}
template<>
inline uint piHash(const llong & v) {
return piHashData((const uchar *)&v, sizeof(v));
}
template<>
inline uint piHash(const ullong & v) {
return piHashData((const uchar *)&v, sizeof(v));
}
template<>
inline uint piHash(const float & v) {
return (uint)v;
}
template<>
inline uint piHash(const double & v) {
return piHashData((const uchar *)&v, sizeof(v));
}
template<>
inline uint piHash(const ldouble & v) {
return piHashData((const uchar *)&v, sizeof(v));
}
//! \~\brief
//! \~english Zero "size" bytes by address "ptr".
//! \~russian Зануляет "size" байт по адресу "ptr".
inline void piZeroMemory(void * ptr, size_t size) {
memset(ptr, 0, size);
}
//! \~\brief
//! \~english Zero variable "v" memory.
//! \~russian Зануляет память переменной "v".
template<typename T>
inline void piZeroMemory(T & v) {
piZeroMemory(&v, sizeof(v));
}
//! \~\brief
//! \~english Call \b delete on each "container" element.
//! \~russian Вызывает \b delete на каждый элемент "container".
template<typename T>
inline void piDeleteAll(const T & container) {
for (auto i: container) {
delete i;
}
}
//! \~\brief
//! \~english Call \b delete on each element of
//! [C++11 initializer list](https://en.cppreference.com/w/cpp/utility/initializer_list).
//! \~russian Вызывает \b delete на каждый элемент
//! [списка инициализации C++11](https://ru.cppreference.com/w/cpp/utility/initializer_list).
template<typename T>
inline void piDeleteAll(std::initializer_list<T> container) {
for (auto i: container) {
delete i;
}
}
//! \~\brief
//! \~english Call \b delete on each "container" element and clear container.
//! \~russian Вызывает \b delete на каждый элемент "container" и очищает контейнер.
template<typename T>
inline void piDeleteAllAndClear(T & container) {
piDeleteAll(container);
container.clear();
}
//! \~\brief
//! \~english Call \b delete if "pointer" is not null and set it to null. Returns if deleted.
//! \~russian Вызывает \b delete на "pointer" если он не нулевой и устанавливает его в ноль. Возвращает было ли удаление.
template<typename T>
inline bool piDeleteSafety(T *& pointer) {
if (!pointer) return false;
delete pointer;
pointer = nullptr;
return true;
}
#define piRoundf piRound<float>
#define piRoundd piRound<double>
#define piComparef piCompare<float>
#define piCompared piCompare<double>
#define piFloorf piFloor<float>
#define piFloord piFloor<double>
#define piCeilf piCeil<float>
#define piCeild piCeil<double>
#define piAbss piAbs<short>
#define piAbsi piAbs<int>
#define piAbsl piAbs<long>
#define piAbsll piAbs<llong>
#define piAbsf piAbs<float>
#define piAbsd piAbs<double>
#define piMins piMin<short>
#define piMini piMin<int>
#define piMinl piMin<long>
#define piMinll piMin<llong>
#define piMinf piMin<float>
#define piMind piMin<double>
#define piMaxs piMax<short>
#define piMaxi piMax<int>
#define piMaxl piMax<long>
#define piMaxll piMax<llong>
#define piMaxf piMax<float>
#define piMaxd piMax<double>
#define piClamps piClamp<short>
#define piClampi piClamp<int>
#define piClampl piClamp<long>
#define piClampll piClamp<llong>
#define piClampf piClamp<float>
#define piClampd piClamp<double>
#define piLetobes piLetobe<ushort>
#define piLetobei piLetobe<uint>
#define piLetobel piLetobe<ulong>
#define piLetobell piLetobe<ullong>
#define piLetobef piLetobe<float>
//! \~\brief
//! \~english Class for executing a function upon scope exit
//! \~russian Класс для выполнения функции при выходе из области видимости
//! \~\details
//! \~english Example
//! \~russian Пример
//! \~\code
//! bool yourFunc() {
//! PIScopeExitCall error_call([]() { piCout << "Error!"; });
//! ...
//! if (!good0) {
//! ...
//! return false;
//! }
//! if (!good1) {
//! ...
//! return false;
//! }
//! ...
//! error_call.cancel();
//! return true;
//! }
//! \endcode
//! \~english In this example "Error!" will be printed on every \b false function return.
//! \~russian В данном примере будет выведен "Error!" при каждом \b false возврате из функции.
class PIP_EXPORT PIScopeExitCall {
public:
//! \~\brief
//! \~english Constructor that takes a function to execute
//! \~russian Конструктор, который принимает функцию для выполнения
explicit PIScopeExitCall(std::function<void()> f): func(f) {}
//! \~\brief
//! \~english Destructor that executes the function if it exists
//! \~russian Деструктор, который выполняет функцию, если она существует
~PIScopeExitCall() { call(); }
//! \~\brief
//! \~english Method for canceling the function
//! \~russian Метод для отмены функции
void cancel() { func = nullptr; }
//! \~\brief
//! \~english Method for call the function
//! \~russian Метод для вызова функции
void call() {
if (func) func();
}
//! \~\brief
//! \~english Method for call and canceling the function
//! \~russian Метод для вызова и отмены функции
void callAndCancel() {
call();
cancel();
}
private:
NO_COPY_CLASS(PIScopeExitCall)
std::function<void()> func;
};
//! \~\brief
//! \~english Inherit from this class to make your class non-trivially copyable.
//! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым.
struct PIP_EXPORT PINonTriviallyCopyable {
PINonTriviallyCopyable() noexcept = default;
PINonTriviallyCopyable(const PINonTriviallyCopyable &) noexcept = default;
PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept;
PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) noexcept = default;
PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) noexcept = default;
~PINonTriviallyCopyable() = default;
};
inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept = default;
template<typename T>
struct FunctionType {
using Type = void;
};
template<typename Ret, typename Class, typename... Args>
struct FunctionType<Ret (Class::*)(Args...) const> {
using Type = std::function<Ret(Args...)>;
};
template<typename L>
typename FunctionType<decltype(&L::operator())>::Type toStdFunction(L const & func) {
return func;
}
#endif // PIBASE_H