Skip to content

Commit 23298f8

Browse files
committed
Rewrite OSD string template processor
Split it to 2 steps: tokenization (cached) and rendering
1 parent 8fe4c66 commit 23298f8

File tree

1 file changed

+102
-63
lines changed

1 file changed

+102
-63
lines changed

src/osd.cpp

Lines changed: 102 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ extern "C" {
3636
#include <cstdlib> //KILLME
3737
#include <string>
3838
#include <optional>
39+
#include <regex>
3940
#include <utility>
4041
#include <filesystem>
4142
#include <cairo.h>
@@ -341,7 +342,7 @@ class Fact {
341342
Fact(FactMeta meta, double val): meta(meta), value(val), type(T_DOUBLE) {};
342343
Fact(FactMeta meta, std::string val): meta(meta), value(val), type(T_STRING) {};
343344

344-
bool isDefined() {
345+
bool isDefined() const {
345346
return type != T_UNDEF;
346347
}
347348

@@ -827,7 +828,9 @@ class IconTextWidget: public Widget {
827828
class TplTextWidget: public Widget {
828829
public:
829830
TplTextWidget(int pos_x, int pos_y, std::string tpl, uint num_args):
830-
Widget(pos_x, pos_y, num_args), tpl(tpl), num_args(num_args) {};
831+
Widget(pos_x, pos_y, num_args), tpl(tpl), num_args(num_args) {
832+
_tokens = tokenize(tpl);
833+
};
831834

832835
virtual void draw(cairo_t *cr) {
833836
auto [x, y] = xy(cr);
@@ -840,77 +843,113 @@ class TplTextWidget: public Widget {
840843
uint default_precision = 2;
841844

842845
protected:
846+
enum class TokenType {
847+
Literal,
848+
Bool,
849+
Int,
850+
Uint,
851+
Float,
852+
String
853+
};
854+
855+
struct Token {
856+
TokenType type;
857+
std::optional<std::string> value; // Used to hold literal
858+
uint precision; // Precision for float placeholders if applicable
859+
860+
Token(TokenType t, std::string v) // literal
861+
: type(t), value(std::move(v)), precision(0) {}
862+
Token(TokenType t, uint p) // float
863+
: type(t), value(std::nullopt), precision(p) {}
864+
Token(TokenType t) // other
865+
: type(t), value(std::nullopt), precision(0) {}
866+
};
867+
843868
std::unique_ptr<std::string> render_tpl() {
844-
return render_tpl(tpl, args);
869+
return render_tokens(_tokens, args);
870+
}
871+
872+
std::unique_ptr<std::string> render_tpl(const std::string& tpl, const std::vector<Fact>& facts) {
873+
auto tokens = tokenize(tpl);
874+
return render_tokens(tokens, facts);
845875
}
846-
std::unique_ptr<std::string> render_tpl(std::string tpl, const std::vector<Fact>& args) {
847-
bool at_placeholder = false;
848-
bool at_precision = false;
849-
uint precision = default_precision;
850-
int fact_i = 0;
876+
877+
std::unique_ptr<std::string> render_tokens(const std::vector<Token>& tokens,
878+
const std::vector<Fact>& facts) {
851879
std::ostringstream msg;
852-
for(char& c : tpl) {
853-
if (c == '%') {
854-
at_placeholder = true;
855-
} else if (!at_placeholder) {
856-
msg << c;
857-
} else if (at_placeholder && c == '%') {
858-
msg << '%';
859-
at_placeholder = false;
860-
} else if (at_placeholder) {
861-
if (at_precision && std::isdigit(c)) {
862-
precision = precision * 10 + (c - '0');
863-
continue; // exit early
864-
}
865-
at_precision = false;
866-
if (fact_i >= args.size()) {
867-
msg << '?'; // Handle out-of-bounds fact access
868-
break;
869-
}
870-
const Fact& fact = args.at(fact_i);
871-
if (!fact.isDefined()) {
872-
msg << '?';
873-
fact_i++;
874-
continue;
875-
}
876-
switch (c) {
877-
case 'b':
878-
msg << (fact.getBoolValue() ? 't' : 'f');
879-
fact_i++;
880-
break;
881-
case 'd':
882-
case 'i':
883-
msg << fact.getIntValue();
884-
fact_i++;
885-
break;
886-
case 'u':
887-
msg << fact.getUintValue();
888-
fact_i++;
889-
break;
890-
case 'f':
891-
msg << std::fixed << std::setprecision(precision) << fact.getDoubleValue();
892-
fact_i++;
893-
break;
894-
case 's':
895-
msg << fact.getStrValue();
896-
fact_i++;
897-
break;
898-
case '.':
899-
// beginning of float precision specifier `%.3f` / `%.123f` / `%.0f`
900-
at_precision = true;
901-
precision = 0;
902-
continue; // exit earlier to not reset `at_placeholder`
903-
default:
904-
msg << '?';
880+
size_t fact_i = 0; // To track the current index in the facts vector
881+
882+
for (const Token& token : tokens) {
883+
if (token.type == TokenType::Literal) {
884+
msg << *token.value; // Append literal directly, dereference std::optional
885+
} else {
886+
// Check if we have enough facts and if the current fact is defined
887+
if (fact_i >= facts.size() || !facts[fact_i].isDefined()) {
888+
msg << '?'; // Append '?' for undefined facts
889+
} else {
890+
switch (token.type) {
891+
case TokenType::Bool:
892+
msg << (facts[fact_i].getBoolValue() ? 't' : 'f');
893+
break;
894+
case TokenType::Int:
895+
msg << facts[fact_i].getIntValue();
896+
break;
897+
case TokenType::Uint:
898+
msg << facts[fact_i].getUintValue();
899+
break;
900+
case TokenType::Float:
901+
msg << std::fixed << std::setprecision(token.precision) << facts[fact_i].getDoubleValue();
902+
break;
903+
case TokenType::String:
904+
msg << facts[fact_i].getStrValue();
905+
break;
906+
}
905907
}
906-
at_placeholder = false;
908+
fact_i++; // Move to the next fact for the next placeholder
907909
}
908910
}
909911
return std::make_unique<std::string>(msg.str());
910912
}
911913

912-
protected:
914+
std::vector<Token> tokenize(const std::string& tpl) {
915+
std::vector<Token> tokens;
916+
std::regex token_regex(R"(%%|%[bisu]|%(\.\d+)?f|[^%]+)"); // Match placeholders and literals
917+
std::sregex_iterator iter(tpl.begin(), tpl.end(), token_regex);
918+
std::sregex_iterator end;
919+
920+
while (iter != end) {
921+
std::string match = iter->str();
922+
if (match == "%%") {
923+
tokens.emplace_back(TokenType::Literal, "%");
924+
} else if (match[0] == '%') {
925+
if (match.size() == 2) { // Simple placeholder like %b, %i, %u, %s
926+
if (match[1] == 'b') {
927+
tokens.emplace_back(TokenType::Bool);
928+
} else if (match[1] == 'i' || match[1] == 'd') {
929+
tokens.emplace_back(TokenType::Int);
930+
} else if (match[1] == 'u') {
931+
tokens.emplace_back(TokenType::Uint);
932+
} else if (match[1] == 's') {
933+
tokens.emplace_back(TokenType::String);
934+
}
935+
} else if (match.back() == 'f') { // Float placeholder
936+
uint precision = 0;
937+
if (match.size() > 2 && match[1] == '.') {
938+
precision = std::stoi(match.substr(2, match.size() - 3)); // Extract precision
939+
}
940+
tokens.emplace_back(TokenType::Float, precision); // Add float token
941+
}
942+
} else {
943+
tokens.emplace_back(TokenType::Literal, match); // Accumulate literal
944+
}
945+
++iter;
946+
}
947+
948+
return tokens;
949+
}
950+
913951
std::string tpl;
952+
std::vector<Token> _tokens;
914953
uint num_args;
915954
};
916955

0 commit comments

Comments
 (0)