// ArduinoJson - https://arduinojson.org // Copyright © 2014-2023, Benoit BLANCHON // MIT License #pragma once #include #include #include struct FailingAllocator : ArduinoJson::Allocator { static FailingAllocator* instance() { static FailingAllocator allocator; return &allocator; } private: FailingAllocator() = default; ~FailingAllocator() = default; void* allocate(size_t) override { return nullptr; } void deallocate(void*) override {} void* reallocate(void*, size_t) override { return nullptr; } }; class AllocatorLog { public: class Entry { public: Entry(std::string s, size_t n = 1) : str_(s), count_(n) {} const std::string& str() const { return str_; } size_t count() const { return count_; } Entry operator*(size_t n) const { return Entry(str_, n); } private: std::string str_; size_t count_; }; static Entry Allocate(size_t s) { char buffer[32]; sprintf(buffer, "allocate(%zu)", s); return Entry(buffer); } static Entry AllocateFail(size_t s) { char buffer[32]; sprintf(buffer, "allocate(%zu) -> nullptr", s); return Entry(buffer); } static Entry Reallocate(size_t s1, size_t s2) { char buffer[32]; sprintf(buffer, "reallocate(%zu, %zu)", s1, s2); return Entry(buffer); }; static Entry ReallocateFail(size_t s1, size_t s2) { char buffer[32]; sprintf(buffer, "reallocate(%zu, %zu) -> nullptr", s1, s2); return Entry(buffer); }; static Entry Deallocate(size_t s) { char buffer[32]; sprintf(buffer, "deallocate(%zu)", s); return Entry(buffer); }; AllocatorLog& operator<<(const Entry& entry) { for (size_t i = 0; i < entry.count(); i++) log_ << entry.str() << "\n"; return *this; } std::string str() const { auto s = log_.str(); if (s.empty()) return "(empty)"; s.pop_back(); // remove the trailing '\n' return s; } bool operator==(const AllocatorLog& other) const { return str() == other.str(); } friend std::ostream& operator<<(std::ostream& os, const AllocatorLog& log) { os << log.str(); return os; } private: std::ostringstream log_; }; class SpyingAllocator : public ArduinoJson::Allocator { public: SpyingAllocator( Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance()) : upstream_(upstream) {} virtual ~SpyingAllocator() {} void* allocate(size_t n) override { auto block = reinterpret_cast( upstream_->allocate(sizeof(AllocatedBlock) + n - 1)); if (block) { log_ << AllocatorLog::Allocate(n); block->size = n; return block->payload; } else { log_ << AllocatorLog::AllocateFail(n); return nullptr; } } void deallocate(void* p) override { auto block = AllocatedBlock::fromPayload(p); log_ << AllocatorLog::Deallocate(block ? block->size : 0); upstream_->deallocate(block); } void* reallocate(void* p, size_t n) override { auto block = AllocatedBlock::fromPayload(p); auto oldSize = block ? block->size : 0; block = reinterpret_cast( upstream_->reallocate(block, sizeof(AllocatedBlock) + n - 1)); if (block) { log_ << AllocatorLog::Reallocate(oldSize, n); block->size = n; return block->payload; } else { log_ << AllocatorLog::ReallocateFail(oldSize, n); return nullptr; } } void clearLog() { log_ = AllocatorLog(); } const AllocatorLog& log() const { return log_; } private: struct AllocatedBlock { size_t size; char payload[1]; static AllocatedBlock* fromPayload(void* p) { if (!p) return nullptr; return reinterpret_cast( // Cast to void* to silence "cast increases required alignment of // target type [-Werror=cast-align]" reinterpret_cast(reinterpret_cast(p) - offsetof(AllocatedBlock, payload))); } }; AllocatorLog log_; Allocator* upstream_; }; class ControllableAllocator : public ArduinoJson::Allocator { public: ControllableAllocator( Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance()) : enabled_(true), upstream_(upstream) {} virtual ~ControllableAllocator() {} void* allocate(size_t n) override { return enabled_ ? upstream_->allocate(n) : 0; } void deallocate(void* p) override { upstream_->deallocate(p); } void* reallocate(void* ptr, size_t n) override { return enabled_ ? upstream_->reallocate(ptr, n) : 0; } void disable() { enabled_ = false; } private: bool enabled_; Allocator* upstream_; }; class TimebombAllocator : public ArduinoJson::Allocator { public: TimebombAllocator( size_t initialCountdown, Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance()) : countdown_(initialCountdown), upstream_(upstream) {} virtual ~TimebombAllocator() {} void* allocate(size_t n) override { if (!countdown_) return nullptr; countdown_--; return upstream_->allocate(n); } void deallocate(void* p) override { upstream_->deallocate(p); } void* reallocate(void* ptr, size_t n) override { if (!countdown_) return nullptr; countdown_--; return upstream_->reallocate(ptr, n); } void setCountdown(size_t value) { countdown_ = value; } private: size_t countdown_ = 0; Allocator* upstream_; }; inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) { return sizeof(ArduinoJson::detail::VariantPool) * n; } inline size_t sizeofPool( ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) { return ArduinoJson::detail::VariantPool::slotsToBytes(n); }