Mirror of espurna firmware for wireless switches and more
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

353 lines
8.4 KiB

  1. /*
  2. Part of the SYSTEM MODULE
  3. Copyright (C) 2019-2021 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
  4. */
  5. #pragma once
  6. #include <Arduino.h>
  7. #include <sys/pgmspace.h>
  8. #include <memory>
  9. #include "compat.h"
  10. // missing in our original header
  11. extern "C" int memcmp_P(const void*, const void*, size_t);
  12. namespace espurna {
  13. // base class for loop / oneshot / generic callbacks that do not need arguments
  14. // *not expected* to be used instead of std function at all times.
  15. // main purpose of this special class is to circumvent the need for rtti in
  16. // our gcc stl implementation and retrieve the 'target function' pointer
  17. // (*should* be different in gcc 11 / 12 though, target() became constexpr)
  18. struct Callback {
  19. using Type = void (*)();
  20. using WrapperType = std::function<void()>;
  21. Callback() = default;
  22. Callback(const Callback& other) :
  23. _storage(nullptr),
  24. _type(other._type)
  25. {
  26. copy(other);
  27. }
  28. Callback& operator=(const Callback& other) {
  29. reset();
  30. copy(other);
  31. return *this;
  32. }
  33. Callback(const Callback&&) = delete;
  34. Callback(Callback&& other) noexcept :
  35. _storage(nullptr),
  36. _type(other._type)
  37. {
  38. move(other);
  39. }
  40. Callback& operator=(Callback&& other) noexcept;
  41. template <typename T>
  42. using is_callback = std::is_same<std::remove_cvref<T>, Callback>;
  43. template <typename T>
  44. using is_type = std::is_same<T, Type>;
  45. template <typename T>
  46. using type_convertible = std::is_convertible<T, Type>;
  47. template <typename T>
  48. using wrapper_convertible = std::is_convertible<T, WrapperType>;
  49. // when T *can* be converted into Callback::Type
  50. // usually, function pointer *or* lambda without capture list
  51. template <typename T,
  52. typename = typename std::enable_if<
  53. is_type<T>::value
  54. || type_convertible<T>::value>::type>
  55. constexpr Callback(T callback) noexcept :
  56. _storage(Type(callback)),
  57. _type(StorageType::Simple)
  58. {}
  59. // anything else convertible into std function
  60. template <typename T,
  61. typename = typename std::enable_if<
  62. !is_callback<T>::value>::type,
  63. typename = typename std::enable_if<
  64. wrapper_convertible<T>::value>::type,
  65. typename = typename std::enable_if<
  66. !type_convertible<T>::value>::type>
  67. Callback(T callback) :
  68. _storage(WrapperType(std::move(callback))),
  69. _type(StorageType::Wrapper)
  70. {
  71. static_assert(!is_callback<T>::value, "");
  72. }
  73. ~Callback() {
  74. reset();
  75. }
  76. bool isEmpty() const {
  77. return (_type == StorageType::Empty);
  78. }
  79. bool isSimple() const {
  80. return (_type == StorageType::Simple);
  81. }
  82. bool isWrapped() const {
  83. return (_type == StorageType::Wrapper);
  84. }
  85. bool operator==(Type callback) const {
  86. return isSimple() && (_storage.simple == callback);
  87. }
  88. void swap(Callback&) noexcept;
  89. void operator()() const;
  90. private:
  91. union Storage {
  92. WrapperType wrapper;
  93. Type simple;
  94. ~Storage() {
  95. }
  96. explicit Storage(WrapperType callback) :
  97. wrapper(std::move(callback))
  98. {}
  99. constexpr explicit Storage(Type callback) :
  100. simple(callback)
  101. {}
  102. constexpr explicit Storage(std::nullptr_t) :
  103. simple(nullptr)
  104. {}
  105. };
  106. enum class StorageType {
  107. Empty,
  108. Simple,
  109. Wrapper,
  110. };
  111. void copy(const Callback&);
  112. void move(Callback&) noexcept;
  113. void reset();
  114. Storage _storage { nullptr };
  115. StorageType _type { StorageType::Empty };
  116. };
  117. // aka `std::source_location`
  118. struct SourceLocation {
  119. int line;
  120. const char* file;
  121. const char* func;
  122. };
  123. inline constexpr SourceLocation make_source_location(
  124. int line = __builtin_LINE(),
  125. const char* file = __builtin_FILE(),
  126. const char* func = __builtin_FUNCTION())
  127. {
  128. return SourceLocation{
  129. .line = line,
  130. .file = file,
  131. .func = func
  132. };
  133. }
  134. // disallow re-locking, tracking external `bool`
  135. struct ReentryLock {
  136. ReentryLock() = delete;
  137. ReentryLock(const ReentryLock&) = delete;
  138. ReentryLock& operator=(const ReentryLock&) = delete;
  139. ReentryLock(ReentryLock&&) = default;
  140. ReentryLock& operator=(ReentryLock&&) = delete;
  141. ReentryLock(bool& handle) :
  142. _initialized(!handle),
  143. _handle(handle)
  144. {}
  145. ~ReentryLock() {
  146. unlock();
  147. }
  148. bool initialized() const {
  149. return _initialized;
  150. }
  151. void lock() {
  152. if (initialized()) {
  153. _handle = true;
  154. }
  155. }
  156. void unlock() {
  157. if (initialized()) {
  158. _handle = false;
  159. }
  160. }
  161. private:
  162. bool _initialized;
  163. bool& _handle;
  164. };
  165. struct StringView {
  166. constexpr StringView() noexcept :
  167. _ptr(nullptr),
  168. _len(0)
  169. {}
  170. ~StringView() = default;
  171. StringView(std::nullptr_t) = delete;
  172. constexpr StringView(const StringView&) noexcept = default;
  173. constexpr StringView(StringView&&) noexcept = default;
  174. #if __cplusplus > 201103L
  175. constexpr StringView& operator=(const StringView&) noexcept = default;
  176. constexpr StringView& operator=(StringView&&) noexcept = default;
  177. #else
  178. StringView& operator=(const StringView&) noexcept = default;
  179. StringView& operator=(StringView&&) noexcept = default;
  180. #endif
  181. constexpr StringView(const char* ptr, size_t len) noexcept :
  182. _ptr(ptr),
  183. _len(len)
  184. {}
  185. constexpr StringView(const char* ptr) noexcept :
  186. StringView(ptr, __builtin_strlen(ptr))
  187. {}
  188. template <size_t Size>
  189. constexpr StringView(const char (&string)[Size]) noexcept :
  190. StringView(&string[0], Size - 1)
  191. {}
  192. constexpr StringView(const char* begin, const char* end) noexcept :
  193. StringView(begin, end - begin)
  194. {}
  195. explicit StringView(const __FlashStringHelper* ptr) noexcept :
  196. _ptr(reinterpret_cast<const char*>(ptr)),
  197. _len(strlen_P(_ptr))
  198. {}
  199. StringView(const String& string) noexcept :
  200. StringView(string.c_str(), string.length())
  201. {}
  202. StringView& operator=(const String& string) noexcept {
  203. _ptr = string.c_str();
  204. _len = string.length();
  205. return *this;
  206. }
  207. template <size_t Size>
  208. constexpr StringView& operator=(const char (&string)[Size]) noexcept {
  209. _ptr = &string[0];
  210. _len = Size - 1;
  211. return *this;
  212. }
  213. constexpr const char* begin() const noexcept {
  214. return _ptr;
  215. }
  216. constexpr const char* end() const noexcept {
  217. return _ptr + _len;
  218. }
  219. constexpr const char* c_str() const {
  220. return _ptr;
  221. }
  222. constexpr const char& operator[](size_t offset) const {
  223. return *(_ptr + offset);
  224. }
  225. constexpr size_t length() const {
  226. return _len;
  227. }
  228. String toString() const {
  229. String out;
  230. out.concat(_ptr, _len);
  231. return out;
  232. }
  233. explicit operator String() const {
  234. return toString();
  235. }
  236. bool equals(StringView) const;
  237. bool equalsIgnoreCase(StringView) const;
  238. bool startsWith(StringView) const;
  239. bool endsWith(StringView) const;
  240. private:
  241. #if defined(HOST_MOCK)
  242. constexpr static bool inFlash(const char*) {
  243. return false;
  244. }
  245. #else
  246. static bool inFlash(const char* ptr) {
  247. // common comparison would use >=0x40000000
  248. // instead, slightly reduce the footprint by
  249. // checking *only* for numbers below it
  250. static constexpr uintptr_t Mask { 1 << 30 };
  251. return (reinterpret_cast<uintptr_t>(ptr) & Mask) > 0;
  252. }
  253. #endif
  254. const char* _ptr;
  255. size_t _len;
  256. };
  257. inline bool operator==(StringView lhs, StringView rhs) {
  258. return lhs.equals(rhs);
  259. }
  260. inline bool operator!=(StringView lhs, StringView rhs) {
  261. return !lhs.equals(rhs);
  262. }
  263. inline String operator+(String&& lhs, StringView rhs) {
  264. lhs.concat(rhs.c_str(), rhs.length());
  265. return lhs;
  266. }
  267. inline String operator+=(String& lhs, StringView rhs) {
  268. lhs.concat(rhs.c_str(), rhs.length());
  269. return lhs;
  270. }
  271. #define STRING_VIEW(X) ({\
  272. alignas(4) static constexpr char __pstr__[] PROGMEM = (X);\
  273. ::espurna::StringView{__pstr__};\
  274. })
  275. #define STRING_VIEW_INLINE(NAME, X)\
  276. alignas(4) static constexpr char __pstr__ ## NAME ## __ [] PROGMEM = (X);\
  277. constexpr auto NAME = ::espurna::StringView(__pstr__ ## NAME ## __)
  278. } // namespace espurna