mirror of
https://github.com/esphome/esphome.git
synced 2026-02-08 00:31:58 +00:00
[text_sensor] Use in-place mutation for filters to reduce heap allocations (#13475)
This commit is contained in:
@@ -9,19 +9,18 @@ namespace text_sensor {
|
||||
static const char *const TAG = "text_sensor.filter";
|
||||
|
||||
// Filter
|
||||
void Filter::input(const std::string &value) {
|
||||
void Filter::input(std::string value) {
|
||||
ESP_LOGVV(TAG, "Filter(%p)::input(%s)", this, value.c_str());
|
||||
optional<std::string> out = this->new_value(value);
|
||||
if (out.has_value())
|
||||
this->output(*out);
|
||||
if (this->new_value(value))
|
||||
this->output(value);
|
||||
}
|
||||
void Filter::output(const std::string &value) {
|
||||
void Filter::output(std::string &value) {
|
||||
if (this->next_ == nullptr) {
|
||||
ESP_LOGVV(TAG, "Filter(%p)::output(%s) -> SENSOR", this, value.c_str());
|
||||
this->parent_->internal_send_state_to_frontend(value);
|
||||
} else {
|
||||
ESP_LOGVV(TAG, "Filter(%p)::output(%s) -> %p", this, value.c_str(), this->next_);
|
||||
this->next_->input(value);
|
||||
this->next_->input(std::move(value));
|
||||
}
|
||||
}
|
||||
void Filter::initialize(TextSensor *parent, Filter *next) {
|
||||
@@ -35,43 +34,48 @@ LambdaFilter::LambdaFilter(lambda_filter_t lambda_filter) : lambda_filter_(std::
|
||||
const lambda_filter_t &LambdaFilter::get_lambda_filter() const { return this->lambda_filter_; }
|
||||
void LambdaFilter::set_lambda_filter(const lambda_filter_t &lambda_filter) { this->lambda_filter_ = lambda_filter; }
|
||||
|
||||
optional<std::string> LambdaFilter::new_value(std::string value) {
|
||||
auto it = this->lambda_filter_(value);
|
||||
ESP_LOGVV(TAG, "LambdaFilter(%p)::new_value(%s) -> %s", this, value.c_str(), it.value_or("").c_str());
|
||||
return it;
|
||||
bool LambdaFilter::new_value(std::string &value) {
|
||||
auto result = this->lambda_filter_(value);
|
||||
if (result.has_value()) {
|
||||
ESP_LOGVV(TAG, "LambdaFilter(%p)::new_value(%s) -> %s (continue)", this, value.c_str(), result->c_str());
|
||||
value = std::move(*result);
|
||||
return true;
|
||||
}
|
||||
ESP_LOGVV(TAG, "LambdaFilter(%p)::new_value(%s) -> (stop)", this, value.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// ToUpperFilter
|
||||
optional<std::string> ToUpperFilter::new_value(std::string value) {
|
||||
bool ToUpperFilter::new_value(std::string &value) {
|
||||
for (char &c : value)
|
||||
c = ::toupper(c);
|
||||
return value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ToLowerFilter
|
||||
optional<std::string> ToLowerFilter::new_value(std::string value) {
|
||||
bool ToLowerFilter::new_value(std::string &value) {
|
||||
for (char &c : value)
|
||||
c = ::tolower(c);
|
||||
return value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Append
|
||||
optional<std::string> AppendFilter::new_value(std::string value) {
|
||||
bool AppendFilter::new_value(std::string &value) {
|
||||
value.append(this->suffix_);
|
||||
return value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prepend
|
||||
optional<std::string> PrependFilter::new_value(std::string value) {
|
||||
bool PrependFilter::new_value(std::string &value) {
|
||||
value.insert(0, this->prefix_);
|
||||
return value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Substitute
|
||||
SubstituteFilter::SubstituteFilter(const std::initializer_list<Substitution> &substitutions)
|
||||
: substitutions_(substitutions) {}
|
||||
|
||||
optional<std::string> SubstituteFilter::new_value(std::string value) {
|
||||
bool SubstituteFilter::new_value(std::string &value) {
|
||||
for (const auto &sub : this->substitutions_) {
|
||||
// Compute lengths once per substitution (strlen is fast, called infrequently)
|
||||
const size_t from_len = strlen(sub.from);
|
||||
@@ -84,20 +88,20 @@ optional<std::string> SubstituteFilter::new_value(std::string value) {
|
||||
pos += to_len;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Map
|
||||
MapFilter::MapFilter(const std::initializer_list<Substitution> &mappings) : mappings_(mappings) {}
|
||||
|
||||
optional<std::string> MapFilter::new_value(std::string value) {
|
||||
bool MapFilter::new_value(std::string &value) {
|
||||
for (const auto &mapping : this->mappings_) {
|
||||
if (value == mapping.from) {
|
||||
value.assign(mapping.to);
|
||||
return value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return value; // Pass through if no match
|
||||
return true; // Pass through if no match
|
||||
}
|
||||
|
||||
} // namespace text_sensor
|
||||
|
||||
@@ -17,21 +17,20 @@ class Filter {
|
||||
public:
|
||||
/** This will be called every time the filter receives a new value.
|
||||
*
|
||||
* It can return an empty optional to indicate that the filter chain
|
||||
* should stop, otherwise the value in the filter will be passed down
|
||||
* the chain.
|
||||
* Modify the value in place. Return false to stop the filter chain
|
||||
* (value will not be published), or true to continue.
|
||||
*
|
||||
* @param value The new value.
|
||||
* @return An optional string, the new value that should be pushed out.
|
||||
* @param value The value to filter (modified in place).
|
||||
* @return True to continue the filter chain, false to stop.
|
||||
*/
|
||||
virtual optional<std::string> new_value(std::string value) = 0;
|
||||
virtual bool new_value(std::string &value) = 0;
|
||||
|
||||
/// Initialize this filter, please note this can be called more than once.
|
||||
virtual void initialize(TextSensor *parent, Filter *next);
|
||||
|
||||
void input(const std::string &value);
|
||||
void input(std::string value);
|
||||
|
||||
void output(const std::string &value);
|
||||
void output(std::string &value);
|
||||
|
||||
protected:
|
||||
friend TextSensor;
|
||||
@@ -45,15 +44,14 @@ using lambda_filter_t = std::function<optional<std::string>(std::string)>;
|
||||
/** This class allows for creation of simple template filters.
|
||||
*
|
||||
* The constructor accepts a lambda of the form std::string -> optional<std::string>.
|
||||
* It will be called with each new value in the filter chain and returns the modified
|
||||
* value that shall be passed down the filter chain. Returning an empty Optional
|
||||
* means that the value shall be discarded.
|
||||
* Return a modified string to continue the chain, or return {} to stop
|
||||
* (value will not be published).
|
||||
*/
|
||||
class LambdaFilter : public Filter {
|
||||
public:
|
||||
explicit LambdaFilter(lambda_filter_t lambda_filter);
|
||||
|
||||
optional<std::string> new_value(std::string value) override;
|
||||
bool new_value(std::string &value) override;
|
||||
|
||||
const lambda_filter_t &get_lambda_filter() const;
|
||||
void set_lambda_filter(const lambda_filter_t &lambda_filter);
|
||||
@@ -71,7 +69,14 @@ class StatelessLambdaFilter : public Filter {
|
||||
public:
|
||||
explicit StatelessLambdaFilter(optional<std::string> (*lambda_filter)(std::string)) : lambda_filter_(lambda_filter) {}
|
||||
|
||||
optional<std::string> new_value(std::string value) override { return this->lambda_filter_(value); }
|
||||
bool new_value(std::string &value) override {
|
||||
auto result = this->lambda_filter_(value);
|
||||
if (result.has_value()) {
|
||||
value = std::move(*result);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
optional<std::string> (*lambda_filter_)(std::string);
|
||||
@@ -80,20 +85,20 @@ class StatelessLambdaFilter : public Filter {
|
||||
/// A simple filter that converts all text to uppercase
|
||||
class ToUpperFilter : public Filter {
|
||||
public:
|
||||
optional<std::string> new_value(std::string value) override;
|
||||
bool new_value(std::string &value) override;
|
||||
};
|
||||
|
||||
/// A simple filter that converts all text to lowercase
|
||||
class ToLowerFilter : public Filter {
|
||||
public:
|
||||
optional<std::string> new_value(std::string value) override;
|
||||
bool new_value(std::string &value) override;
|
||||
};
|
||||
|
||||
/// A simple filter that adds a string to the end of another string
|
||||
class AppendFilter : public Filter {
|
||||
public:
|
||||
explicit AppendFilter(const char *suffix) : suffix_(suffix) {}
|
||||
optional<std::string> new_value(std::string value) override;
|
||||
bool new_value(std::string &value) override;
|
||||
|
||||
protected:
|
||||
const char *suffix_;
|
||||
@@ -103,7 +108,7 @@ class AppendFilter : public Filter {
|
||||
class PrependFilter : public Filter {
|
||||
public:
|
||||
explicit PrependFilter(const char *prefix) : prefix_(prefix) {}
|
||||
optional<std::string> new_value(std::string value) override;
|
||||
bool new_value(std::string &value) override;
|
||||
|
||||
protected:
|
||||
const char *prefix_;
|
||||
@@ -118,7 +123,7 @@ struct Substitution {
|
||||
class SubstituteFilter : public Filter {
|
||||
public:
|
||||
explicit SubstituteFilter(const std::initializer_list<Substitution> &substitutions);
|
||||
optional<std::string> new_value(std::string value) override;
|
||||
bool new_value(std::string &value) override;
|
||||
|
||||
protected:
|
||||
FixedVector<Substitution> substitutions_;
|
||||
@@ -151,7 +156,7 @@ class SubstituteFilter : public Filter {
|
||||
class MapFilter : public Filter {
|
||||
public:
|
||||
explicit MapFilter(const std::initializer_list<Substitution> &mappings);
|
||||
optional<std::string> new_value(std::string value) override;
|
||||
bool new_value(std::string &value) override;
|
||||
|
||||
protected:
|
||||
FixedVector<Substitution> mappings_;
|
||||
|
||||
Reference in New Issue
Block a user