Allocators.hpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // ArduinoJson - https://arduinojson.org
  2. // Copyright © 2014-2024, Benoit BLANCHON
  3. // MIT License
  4. #pragma once
  5. #include <ArduinoJson/Memory/Allocator.hpp>
  6. #include <ArduinoJson/Memory/MemoryPool.hpp>
  7. #include <ArduinoJson/Memory/StringBuilder.hpp>
  8. #include <sstream>
  9. namespace {
  10. struct FailingAllocator : ArduinoJson::Allocator {
  11. static FailingAllocator* instance() {
  12. static FailingAllocator allocator;
  13. return &allocator;
  14. }
  15. private:
  16. FailingAllocator() = default;
  17. ~FailingAllocator() = default;
  18. void* allocate(size_t) override {
  19. return nullptr;
  20. }
  21. void deallocate(void*) override {}
  22. void* reallocate(void*, size_t) override {
  23. return nullptr;
  24. }
  25. };
  26. class AllocatorLogEntry {
  27. public:
  28. AllocatorLogEntry(std::string s, size_t n = 1) : str_(s), count_(n) {}
  29. const std::string& str() const {
  30. return str_;
  31. }
  32. size_t count() const {
  33. return count_;
  34. }
  35. AllocatorLogEntry operator*(size_t n) const {
  36. return AllocatorLogEntry(str_, n);
  37. }
  38. private:
  39. std::string str_;
  40. size_t count_;
  41. };
  42. inline AllocatorLogEntry Allocate(size_t s) {
  43. char buffer[32];
  44. snprintf(buffer, sizeof(buffer), "allocate(%zu)", s);
  45. return AllocatorLogEntry(buffer);
  46. }
  47. inline AllocatorLogEntry AllocateFail(size_t s) {
  48. char buffer[32];
  49. snprintf(buffer, sizeof(buffer), "allocate(%zu) -> nullptr", s);
  50. return AllocatorLogEntry(buffer);
  51. }
  52. inline AllocatorLogEntry Reallocate(size_t s1, size_t s2) {
  53. char buffer[32];
  54. snprintf(buffer, sizeof(buffer), "reallocate(%zu, %zu)", s1, s2);
  55. return AllocatorLogEntry(buffer);
  56. }
  57. inline AllocatorLogEntry ReallocateFail(size_t s1, size_t s2) {
  58. char buffer[32];
  59. snprintf(buffer, sizeof(buffer), "reallocate(%zu, %zu) -> nullptr", s1, s2);
  60. return AllocatorLogEntry(buffer);
  61. }
  62. inline AllocatorLogEntry Deallocate(size_t s) {
  63. char buffer[32];
  64. snprintf(buffer, sizeof(buffer), "deallocate(%zu)", s);
  65. return AllocatorLogEntry(buffer);
  66. }
  67. class AllocatorLog {
  68. public:
  69. AllocatorLog() = default;
  70. AllocatorLog(std::initializer_list<AllocatorLogEntry> list) {
  71. for (auto& entry : list)
  72. append(entry);
  73. }
  74. void clear() {
  75. log_.str("");
  76. }
  77. void append(const AllocatorLogEntry& entry) {
  78. for (size_t i = 0; i < entry.count(); i++)
  79. log_ << entry.str() << "\n";
  80. }
  81. std::string str() const {
  82. auto s = log_.str();
  83. if (s.empty())
  84. return "(empty)";
  85. s.pop_back(); // remove the trailing '\n'
  86. return s;
  87. }
  88. bool operator==(const AllocatorLog& other) const {
  89. return str() == other.str();
  90. }
  91. friend std::ostream& operator<<(std::ostream& os, const AllocatorLog& log) {
  92. os << log.str();
  93. return os;
  94. }
  95. private:
  96. std::ostringstream log_;
  97. };
  98. class SpyingAllocator : public ArduinoJson::Allocator {
  99. public:
  100. SpyingAllocator(
  101. Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance())
  102. : upstream_(upstream) {}
  103. virtual ~SpyingAllocator() {}
  104. size_t allocatedBytes() const {
  105. return allocatedBytes_;
  106. }
  107. void* allocate(size_t n) override {
  108. auto block = reinterpret_cast<AllocatedBlock*>(
  109. upstream_->allocate(sizeof(AllocatedBlock) + n - 1));
  110. if (block) {
  111. log_.append(Allocate(n));
  112. allocatedBytes_ += n;
  113. block->size = n;
  114. return block->payload;
  115. } else {
  116. log_.append(AllocateFail(n));
  117. return nullptr;
  118. }
  119. }
  120. void deallocate(void* p) override {
  121. auto block = AllocatedBlock::fromPayload(p);
  122. allocatedBytes_ -= block->size;
  123. log_.append(Deallocate(block ? block->size : 0));
  124. upstream_->deallocate(block);
  125. }
  126. void* reallocate(void* p, size_t n) override {
  127. auto block = AllocatedBlock::fromPayload(p);
  128. auto oldSize = block ? block->size : 0;
  129. block = reinterpret_cast<AllocatedBlock*>(
  130. upstream_->reallocate(block, sizeof(AllocatedBlock) + n - 1));
  131. if (block) {
  132. log_.append(Reallocate(oldSize, n));
  133. block->size = n;
  134. allocatedBytes_ += n - oldSize;
  135. return block->payload;
  136. } else {
  137. log_.append(ReallocateFail(oldSize, n));
  138. return nullptr;
  139. }
  140. }
  141. void clearLog() {
  142. log_.clear();
  143. }
  144. const AllocatorLog& log() const {
  145. return log_;
  146. }
  147. private:
  148. struct AllocatedBlock {
  149. size_t size;
  150. char payload[1];
  151. static AllocatedBlock* fromPayload(void* p) {
  152. if (!p)
  153. return nullptr;
  154. return reinterpret_cast<AllocatedBlock*>(
  155. // Cast to void* to silence "cast increases required alignment of
  156. // target type [-Werror=cast-align]"
  157. reinterpret_cast<void*>(reinterpret_cast<char*>(p) -
  158. offsetof(AllocatedBlock, payload)));
  159. }
  160. };
  161. AllocatorLog log_;
  162. Allocator* upstream_;
  163. size_t allocatedBytes_ = 0;
  164. };
  165. class KillswitchAllocator : public ArduinoJson::Allocator {
  166. public:
  167. KillswitchAllocator(
  168. Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance())
  169. : working_(true), upstream_(upstream) {}
  170. virtual ~KillswitchAllocator() {}
  171. void* allocate(size_t n) override {
  172. return working_ ? upstream_->allocate(n) : 0;
  173. }
  174. void deallocate(void* p) override {
  175. upstream_->deallocate(p);
  176. }
  177. void* reallocate(void* ptr, size_t n) override {
  178. return working_ ? upstream_->reallocate(ptr, n) : 0;
  179. }
  180. // Turn the killswitch on, so all allocation fail
  181. void on() {
  182. working_ = false;
  183. }
  184. private:
  185. bool working_;
  186. Allocator* upstream_;
  187. };
  188. class TimebombAllocator : public ArduinoJson::Allocator {
  189. public:
  190. TimebombAllocator(
  191. size_t initialCountdown,
  192. Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance())
  193. : countdown_(initialCountdown), upstream_(upstream) {}
  194. virtual ~TimebombAllocator() {}
  195. void* allocate(size_t n) override {
  196. if (!countdown_)
  197. return nullptr;
  198. countdown_--;
  199. return upstream_->allocate(n);
  200. }
  201. void deallocate(void* p) override {
  202. upstream_->deallocate(p);
  203. }
  204. void* reallocate(void* ptr, size_t n) override {
  205. if (!countdown_)
  206. return nullptr;
  207. countdown_--;
  208. return upstream_->reallocate(ptr, n);
  209. }
  210. void setCountdown(size_t value) {
  211. countdown_ = value;
  212. }
  213. private:
  214. size_t countdown_ = 0;
  215. Allocator* upstream_;
  216. };
  217. } // namespace
  218. inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) {
  219. using namespace ArduinoJson::detail;
  220. return sizeof(MemoryPool<VariantData>) * n;
  221. }
  222. inline size_t sizeofPool(
  223. ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) {
  224. using namespace ArduinoJson::detail;
  225. return MemoryPool<VariantData>::slotsToBytes(n);
  226. }
  227. inline size_t sizeofStringBuffer(size_t iteration = 1) {
  228. // returns 31, 63, 127, 255, etc.
  229. auto capacity = ArduinoJson::detail::StringBuilder::initialCapacity;
  230. for (size_t i = 1; i < iteration; i++)
  231. capacity = capacity * 2 + 1;
  232. return ArduinoJson::detail::sizeofString(capacity);
  233. }
  234. inline size_t sizeofString(const char* s) {
  235. return ArduinoJson::detail::sizeofString(strlen(s));
  236. }