#include "json_util.h" #include "esphome/core/log.h" namespace esphome { namespace json { static const char *const TAG = "json"; static char *global_json_build_buffer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static size_t global_json_build_buffer_size = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void reserve_global_json_build_buffer(size_t required_size) { if (global_json_build_buffer_size == 0 || global_json_build_buffer_size < required_size) { delete[] global_json_build_buffer; global_json_build_buffer_size = std::max(required_size, global_json_build_buffer_size * 2); size_t remainder = global_json_build_buffer_size % 16U; if (remainder != 0) global_json_build_buffer_size += 16 - remainder; global_json_build_buffer = new char[global_json_build_buffer_size]; } } const char *build_json(const json_build_t &f, size_t *length) { global_json_buffer.clear(); JsonObject &root = global_json_buffer.createObject(); f(root); // The Json buffer size gives us a good estimate for the required size. // Usually, it's a bit larger than the actual required string size // | JSON Buffer Size | String Size | // Discovery | 388 | 351 | // Discovery | 372 | 356 | // Discovery | 336 | 311 | // Discovery | 408 | 393 | reserve_global_json_build_buffer(global_json_buffer.size()); size_t bytes_written = root.printTo(global_json_build_buffer, global_json_build_buffer_size); if (bytes_written >= global_json_build_buffer_size - 1) { reserve_global_json_build_buffer(root.measureLength() + 1); bytes_written = root.printTo(global_json_build_buffer, global_json_build_buffer_size); } *length = bytes_written; return global_json_build_buffer; } void parse_json(const std::string &data, const json_parse_t &f) { global_json_buffer.clear(); JsonObject &root = global_json_buffer.parseObject(data); if (!root.success()) { ESP_LOGW(TAG, "Parsing JSON failed."); return; } f(root); } std::string build_json(const json_build_t &f) { size_t len; const char *c_str = build_json(f, &len); return std::string(c_str, len); } VectorJsonBuffer::String::String(VectorJsonBuffer *parent) : parent_(parent), start_(parent->size_) {} void VectorJsonBuffer::String::append(char c) const { char *last = static_cast(this->parent_->do_alloc(1)); *last = c; } const char *VectorJsonBuffer::String::c_str() const { this->append('\0'); return &this->parent_->buffer_[this->start_]; } void VectorJsonBuffer::clear() { for (char *block : this->free_blocks_) free(block); // NOLINT this->size_ = 0; this->free_blocks_.clear(); } VectorJsonBuffer::String VectorJsonBuffer::startString() { return {this}; } // NOLINT void *VectorJsonBuffer::alloc(size_t bytes) { // Make sure memory addresses are aligned uint32_t new_size = round_size_up(this->size_); this->resize(new_size); return this->do_alloc(bytes); } void *VectorJsonBuffer::do_alloc(size_t bytes) { // NOLINT const uint32_t begin = this->size_; this->resize(begin + bytes); return &this->buffer_[begin]; } void VectorJsonBuffer::resize(size_t size) { // NOLINT if (size <= this->size_) { this->size_ = size; return; } this->reserve(size); this->size_ = size; } void VectorJsonBuffer::reserve(size_t size) { // NOLINT if (size <= this->capacity_) return; uint32_t target_capacity = this->capacity_; if (this->capacity_ == 0) { // lazily initialize with a reasonable size target_capacity = JSON_OBJECT_SIZE(16); } while (target_capacity < size) target_capacity *= 2; char *old_buffer = this->buffer_; this->buffer_ = new char[target_capacity]; if (old_buffer != nullptr && this->capacity_ != 0) { this->free_blocks_.push_back(old_buffer); memcpy(this->buffer_, old_buffer, this->capacity_); } this->capacity_ = target_capacity; } size_t VectorJsonBuffer::size() const { return this->size_; } VectorJsonBuffer global_json_buffer; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace json } // namespace esphome