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.

399 lines
10 KiB

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