moved the decompressor into its own place

This commit is contained in:
Gábor Poczkodi 2024-11-10 07:02:31 +01:00
parent bfe43efcdd
commit dcf0516ffc
4 changed files with 189 additions and 117 deletions

View file

@ -0,0 +1,120 @@
#include "decompressor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace store_yaml {
static const char *const TAG = "store_yaml";
Decompressor::Decompressor(const uint8_t *ptr, size_t len) {
this->data_ptr_ = ptr;
this->data_len_ = len;
this->reset();
}
void Decompressor::reset() {
this->pos_ = 0;
this->size_ = 0;
this->buff_ = 0;
this->codes_.clear();
for (uint32_t i = 0; i < 256; i++) {
this->codes_.push_back(Entry{.p = 0, .c = i});
}
this->code_width_ = 9; // log2next + 1
}
bool Decompressor::is_eof() const { return this->pos_ >= this->data_len_; }
uint32_t Decompressor::get_bits_(size_t bits) {
if (this->is_eof()) {
return UINT32_MAX;
}
while (this->size_ < bits) {
this->buff_ = (this->buff_ << 8) | this->data_ptr_[this->pos_++];
this->size_ += 8;
}
uint32_t value = (this->buff_ >> (this->size_ - bits)) & ((1 << bits) - 1);
this->size_ -= bits;
this->buff_ &= (1 << this->size_) - 1;
return value;
}
const Entry *Decompressor::get_entry_(uint16_t &code) {
if (this->codes_.size() == ((1 << this->code_width_) - 1)) {
this->code_width_++;
}
code = (uint16_t) this->get_bits_(this->code_width_);
if (code < this->codes_.size()) {
return &this->codes_[code];
}
if (code != this->codes_.size()) {
this->pos_ = this->data_len_; // error in input, set eof
}
return nullptr;
}
std::string Decompressor::get_string_(const Entry *entry) const {
std::string s(1, (char) entry->c);
while (entry->p != 0) {
if (entry->p >= this->codes_.size()) {
ESP_LOGE(TAG, "entry->p %d not found", entry->p);
break;
}
entry = &this->codes_[entry->p];
s += (char) entry->c;
}
return std::string(s.rbegin(), s.rend());
}
std::string Decompressor::get_first() {
this->reset();
uint16_t code = 0;
const Entry *entry = this->get_entry_(code);
std::string s = this->get_string_(entry);
this->prev_.c = (uint32_t) s[0];
this->prev_.p = code;
return s;
}
std::string Decompressor::get_next() {
uint16_t code = 0;
const Entry *entry = this->get_entry_(code);
if (entry == nullptr)
entry = &this->prev_;
std::string s = this->get_string_(entry);
this->prev_.c = s[0];
this->codes_.push_back(this->prev_);
this->prev_.p = code;
return s;
}
RowDecompressor::RowDecompressor(const uint8_t *ptr, size_t len) : Decompressor(ptr, len) {
this->yaml_ = this->get_first();
}
bool RowDecompressor::get_row(std::string &row) {
while (!this->is_eof() && this->yaml_.find('\n') == std::string::npos) {
this->yaml_ += this->get_next();
}
size_t pos = this->yaml_.find('\n');
if (pos != std::string::npos) {
row = this->yaml_.substr(0, pos);
this->yaml_ = std::string(this->yaml_.begin() + pos + 1, this->yaml_.end());
return true;
}
if (this->is_eof() && this->yaml_.size() > 0) {
// no new line at the end of the file
row = this->yaml_;
this->yaml_.empty();
return true;
}
return false;
}
} // namespace store_yaml
} // namespace esphome

View file

@ -0,0 +1,47 @@
#pragma once
#include <string>
#include <vector>
namespace esphome {
namespace store_yaml {
struct Entry {
uint32_t p : 24;
uint32_t c : 8;
};
class Decompressor {
const uint8_t *data_ptr_;
size_t data_len_;
std::vector<Entry> codes_;
size_t pos_;
uint8_t size_;
uint32_t buff_;
uint8_t code_width_;
Entry prev_;
uint32_t get_bits_(size_t bits);
const Entry *get_entry_(uint16_t &code);
std::string get_string_(const Entry *entry) const;
public:
Decompressor(const uint8_t *ptr, size_t len);
void reset();
bool is_eof() const;
std::string get_first();
std::string get_next();
};
class RowDecompressor : public Decompressor {
std::string yaml_;
public:
RowDecompressor(const uint8_t *ptr, size_t len);
bool get_row(std::string &row);
};
} // namespace store_yaml
} // namespace esphome

View file

@ -1,7 +1,6 @@
#include "store_yaml.h"
#include "esphome/core/log.h"
#include <string>
#include <map>
#include "esphome/core/helpers.h"
namespace esphome {
namespace store_yaml {
@ -11,126 +10,28 @@ static const char *const TAG = "store_yaml";
void StoreYamlComponent::dump_config() {
if (this->show_in_dump_config_) {
ESP_LOGCONFIG(TAG, "YAML:");
this->log(true);
RowDecompressor dec(ESPHOME_YAML, ESPHOME_YAML_SIZE);
std::string row;
while (dec.get_row(row)) {
ESP_LOGCONFIG(TAG, "%s", row.c_str());
}
}
}
struct Entry {
uint32_t p : 24;
uint32_t c : 8;
};
class Decompressor {
std::map<uint16_t, Entry> codes_;
size_t pos_;
uint8_t size_;
uint32_t buff_;
size_t code_index_;
uint8_t code_width_;
Entry prev_;
public:
Decompressor() { this->reset(); }
void reset() {
this->pos_ = 0;
this->size_ = 0;
this->buff_ = 0;
this->codes_.clear();
for (uint32_t i = 0; i < 256; i++) {
this->codes_[i] = Entry{.p = 0, .c = i};
}
this->code_index_ = this->codes_.size();
this->code_width_ = 9; // log2next + 1
}
uint32_t get_bits(size_t bits) {
if (this->is_eof())
return UINT32_MAX;
while (this->size_ < bits) {
this->buff_ = (this->buff_ << 8) | ESPHOME_YAML[this->pos_++];
this->size_ += 8;
}
uint32_t value = (this->buff_ >> (this->size_ - bits)) & ((1 << bits) - 1);
this->size_ -= bits;
this->buff_ &= (1 << this->size_) - 1;
return value;
}
const Entry *get_entry(uint16_t &code) {
if (this->code_index_ == ((1 << this->code_width_) - 1)) {
this->code_width_++;
}
code = (uint16_t) get_bits(this->code_width_);
auto i = this->codes_.find(code);
if (i != this->codes_.end()) {
return &i->second;
}
if (code != this->codes_.size()) {
this->pos_ = ESPHOME_YAML_SIZE; // error in input, set eof
}
return nullptr;
}
bool is_eof() const { return this->pos_ >= ESPHOME_YAML_SIZE; }
std::string get_string(const Entry *entry) const {
std::string s(1, (char) entry->c);
while (entry->p != 0) {
entry = &this->codes_.find(entry->p)->second;
s = std::string(1, (char) entry->c) + s;
}
return s;
}
std::string get_first() {
this->reset();
uint16_t code = 0;
const Entry *entry = this->get_entry(code);
std::string s = this->get_string(entry);
this->prev_.c = (uint32_t) s[0];
this->prev_.p = code;
return s;
}
std::string get_next() {
uint16_t code = 0;
const Entry *entry = this->get_entry(code);
if (entry == nullptr)
entry = &this->prev_;
std::string s = this->get_string(entry);
this->prev_.c = s[0];
this->codes_[this->code_index_++] = this->prev_;
this->prev_.p = code;
return s;
}
};
void StoreYamlComponent::log(bool dump_config) const {
Decompressor *dec = new Decompressor();
std::string yaml = dec->get_first();
while (!dec->is_eof()) {
yaml += dec->get_next();
// print line by line because the logger cannot handle too much data
// it also uses less memory
size_t newline = 0;
while (newline != std::string::npos) {
newline = yaml.find('\n');
if (newline != std::string::npos) {
std::string row = yaml.substr(0, newline);
yaml = std::string(yaml.begin() + newline + 1, yaml.end());
if (dump_config) {
ESP_LOGCONFIG(TAG, "%s", row.c_str());
} else {
ESP_LOGI(TAG, "%s", row.c_str());
}
}
void StoreYamlComponent::loop() {
if (this->dec_) {
std::string row;
if (this->dec_->get_row(row)) {
ESP_LOGI(TAG, "%s", row.c_str());
} else {
this->dec_ = nullptr;
}
}
}
delete dec;
void StoreYamlComponent::log() {
ESP_LOGI(TAG, "YAML:");
this->dec_ = make_unique<RowDecompressor>(ESPHOME_YAML, ESPHOME_YAML_SIZE);
}
} // namespace store_yaml

View file

@ -3,6 +3,8 @@
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/core/hal.h"
#include <memory>
#include "decompressor.h"
extern const uint8_t ESPHOME_YAML[] PROGMEM;
extern const size_t ESPHOME_YAML_SIZE;
@ -11,12 +13,14 @@ namespace esphome {
namespace store_yaml {
class StoreYamlComponent : public Component {
std::unique_ptr<RowDecompressor> dec_;
bool show_in_dump_config_{false};
public:
void dump_config() override;
void loop() override;
void log();
void set_show_in_dump_config(bool show) { this->show_in_dump_config_ = show; }
void log(bool dump_config = false) const;
};
template<typename... Ts> class LogAction : public Action<Ts...>, public Parented<StoreYamlComponent> {