commit 9531673: [Project] Css: Rework css block structure

Vsevolod Stakhov vsevolod at highsecure.ru
Tue Mar 9 20:56:05 UTC 2021


Author: Vsevolod Stakhov
Date: 2021-03-09 20:49:16 +0000
URL: https://github.com/rspamd/rspamd/commit/9531673b2c627d94bea3e136ea956eca6a7364a3 (HEAD -> master)

[Project] Css: Rework css block structure

---
 src/libserver/css/css_property.hxx |  2 +
 src/libserver/css/css_rule.cxx     | 97 +++++++++++++++++++++++++++++++++-----
 src/libserver/css/css_rule.hxx     | 94 ++++++++++++++++++++++++++++++------
 src/libserver/css/css_value.hxx    | 17 ++++---
 4 files changed, 176 insertions(+), 34 deletions(-)

diff --git a/src/libserver/css/css_property.hxx b/src/libserver/css/css_property.hxx
index 597fc20c5..b72b655f9 100644
--- a/src/libserver/css/css_property.hxx
+++ b/src/libserver/css/css_property.hxx
@@ -118,6 +118,8 @@ struct alignas(int) css_property {
 	constexpr auto is_normal_number(void) const -> bool {
 		return type == css_property_type::PROPERTY_OPACITY;
 	}
+
+	auto operator==(const css_property &other) const { return type == other.type; }
 };
 
 
diff --git a/src/libserver/css/css_rule.cxx b/src/libserver/css/css_rule.cxx
index a29577fe9..a706c759c 100644
--- a/src/libserver/css/css_rule.cxx
+++ b/src/libserver/css/css_rule.cxx
@@ -15,9 +15,87 @@
  */
 
 #include "css_rule.hxx"
+#include <limits>
 
 namespace rspamd::css {
 
+/* Class methods */
+void css_rule::override_values(const css_rule &other)
+{
+	values.resize(0);
+	values.reserve(other.values.size());
+
+	std::copy(other.values.begin(), other.values.end(),
+			std::back_inserter(values));
+}
+
+void css_rule::merge_values(const css_rule &other)
+{
+	int bits = 0;
+	/* Ensure that our bitset is large enough */
+	static_assert(static_cast<std::size_t>(css_value::css_value_type::CSS_VALUE_NYI) << 1 <
+		std::numeric_limits<int>::max());
+
+	for (const auto &v : values) {
+		bits |= static_cast<int>(v.type);
+	}
+
+	/* Copy only not set values */
+	std::copy_if(other.values.begin(), other.values.end(), std::back_inserter(values),
+			[&bits](const auto &elt) -> bool {
+		return !isset(&bits, static_cast<int>(elt.type));
+	});
+}
+
+auto css_declarations_block::add_rule(rule_shared_ptr &&rule) -> void
+{
+	auto it = rules.find(rule);
+	auto &&remote_prop = rule->get_prop();
+
+	if (it != rules.end()) {
+		auto &&local_rule = *it;
+		auto &&local_prop = local_rule->get_prop();
+
+		if (local_prop.flag == css_property_flag::FLAG_IMPORTANT) {
+			if (remote_prop.flag == css_property_flag::FLAG_IMPORTANT) {
+				local_rule->override_values(*rule);
+			}
+			else {
+				/* Ignore remote not important over local important */
+				local_rule->merge_values(*rule);
+			}
+		}
+		else if (local_prop.flag == css_property_flag::FLAG_NOT_IMPORTANT) {
+			if (remote_prop.flag == css_property_flag::FLAG_NOT_IMPORTANT) {
+				local_rule->override_values(*rule);
+			}
+			else {
+				/* Ignore local not important over important */
+				local_rule->merge_values(*rule);
+			}
+		}
+		else {
+			if (remote_prop.flag == css_property_flag::FLAG_IMPORTANT) {
+				/* Override with remote */
+				local_rule->override_values(*rule);
+			}
+			else if (remote_prop.flag == css_property_flag::FLAG_NOT_IMPORTANT) {
+				/* Ignore remote not important over local normal */
+			}
+			else {
+				/* Merge both */
+				local_rule->merge_values(*rule);
+			}
+		}
+	}
+}
+
+}
+
+namespace rspamd::css {
+
+/* Static functions */
+
 static auto
 allowed_property_value(const css_property &prop, const css_consumed_block &parser_block)
 	-> std::optional<css_value>
@@ -70,15 +148,15 @@ allowed_property_value(const css_property &prop, const css_consumed_block &parse
 
 auto process_declaration_tokens(rspamd_mempool_t *pool,
 								const blocks_gen_functor &next_block_functor)
-	-> declarations_vec
+	-> css_declarations_block
 {
-	declarations_vec ret;
+	css_declarations_block ret;
 	bool can_continue = true;
 	css_property cur_property{css_property_type::PROPERTY_NYI,
 							  css_property_flag::FLAG_NORMAL};
 	static const css_property bad_property{css_property_type::PROPERTY_NYI,
 										   css_property_flag::FLAG_NORMAL};
-	std::unique_ptr<css_rule> cur_rule;
+	std::shared_ptr<css_rule> cur_rule;
 
 	enum {
 		parse_property,
@@ -121,7 +199,7 @@ auto process_declaration_tokens(rspamd_mempool_t *pool,
 					}
 					else {
 						state = parse_value;
-						cur_rule = std::make_unique<css_rule>(cur_property);
+						cur_rule = std::make_shared<css_rule>(cur_property);
 					}
 				}
 			}
@@ -131,7 +209,7 @@ auto process_declaration_tokens(rspamd_mempool_t *pool,
 					const auto &parser_tok = next_tok.get_token_or_empty();
 
 					if (parser_tok.type == css_parser_token::token_type::semicolon_token) {
-						ret.push_back(std::move(cur_rule));
+						ret.add_rule(std::move(cur_rule));
 						state = parse_property;
 						seen_not = false;
 						continue;
@@ -199,7 +277,7 @@ auto process_declaration_tokens(rspamd_mempool_t *pool,
 			break;
 		case css_consumed_block::parser_tag_type::css_eof_block:
 			if (state == parse_value) {
-				ret.push_back(std::move(cur_rule));
+				ret.add_rule(std::move(cur_rule));
 			}
 			can_continue = false;
 			break;
@@ -212,14 +290,9 @@ auto process_declaration_tokens(rspamd_mempool_t *pool,
 	return ret; /* copy elision */
 }
 
-void css_rule::add_value(std::unique_ptr<css_value> &&value)
-{
-	values.emplace_back(std::forward<std::unique_ptr<css_value>>(value));
-}
-
 void css_rule::add_value(const css_value &value)
 {
-	values.emplace_back(std::make_unique<css_value>(css_value{value}));
+	values.push_back(value);
 }
 
 }
\ No newline at end of file
diff --git a/src/libserver/css/css_rule.hxx b/src/libserver/css/css_rule.hxx
index 41a6ef3be..dd6158538 100644
--- a/src/libserver/css/css_rule.hxx
+++ b/src/libserver/css/css_rule.hxx
@@ -21,6 +21,7 @@
 #include "css_value.hxx"
 #include "css_property.hxx"
 #include "css_parser.hxx"
+#include "contrib/robin-hood/robin_hood.h"
 #include <vector>
 #include <memory>
 
@@ -28,29 +29,35 @@ namespace rspamd::css {
 
 class css_rule {
 	css_property prop;
-	using css_values_vec = std::vector<std::unique_ptr<css_value> >;
+	using css_values_vec = std::vector<css_value>;
 	css_values_vec values;
+
 public:
 	/* We must create css rule explicitly from a property and values */
 	css_rule() = delete;
+
 	css_rule(const css_rule &other) = delete;
+
 	/* Constructors */
-	css_rule(css_rule &&other) = default;
-	explicit css_rule(css_property &&prop, css_values_vec &&values) :
-		prop(prop), values(std::forward<css_values_vec>(values)) {}
-	explicit css_rule(const css_property &prop) : prop(prop), values{} {}
+	css_rule(css_rule &&other) noexcept = default;
+
+	explicit css_rule(css_property &&prop, css_values_vec &&values) noexcept :
+			prop(prop), values(std::forward<css_values_vec>(values)) {}
+
+	explicit css_rule(const css_property &prop) noexcept : prop(prop), values{} {}
+
 	/* Methods */
-	void add_value(std::unique_ptr<css_value> &&value);
-	void add_value(const css_value &value);
-	constexpr const css_values_vec& get_values(void) const { return values; }
-	constexpr const css_property& get_prop(void) const { return prop; }
-};
+	/* Comparison is special, as we care merely about property, not the values */
+	auto operator==(const css_rule &other) const { return prop == other.prop; }
 
-using declarations_vec = std::vector<std::unique_ptr<css_rule>>;
+	constexpr const css_values_vec &get_values(void) const { return values; }
+	constexpr const css_property &get_prop(void) const { return prop; }
 
-auto process_declaration_tokens(rspamd_mempool_t *pool,
-							 const blocks_gen_functor &next_token_functor)
-	-> declarations_vec;
+	/* Import values from another rules according to the importance */
+	void override_values(const css_rule &other);
+	void merge_values(const css_rule &other);
+	void add_value(const css_value &value);
+};
 
 }
 
@@ -59,10 +66,67 @@ namespace std {
 template<>
 class hash<rspamd::css::css_rule> {
 public:
-	constexpr size_t operator() (const rspamd::css::css_rule &rule) const {
+	constexpr auto operator()(const rspamd::css::css_rule &rule) const -> auto {
 		return hash<rspamd::css::css_property>()(rule.get_prop());
 	}
 };
+
+}
+
+namespace rspamd::css {
+
+/*
+ * We have to define transparent hash and compare methods
+ *
+ * TODO: move to some utility library
+ */
+template<typename T>
+struct shared_ptr_equal {
+	using is_transparent = void; /* We want to find values in a set of shared_ptr by reference */
+	auto operator()(const std::shared_ptr<T> &a, const std::shared_ptr<T> &b) const {
+		return (*a) == (*b);
+	}
+	auto operator()(const std::shared_ptr<T> &a, const T &b) const {
+		return (*a) == b;
+	}
+	auto operator()(const T &a, const std::shared_ptr<T> &b) const {
+		return a == (*b);
+	}
+};
+
+template<typename T>
+struct shared_ptr_hash {
+	using is_transparent = void; /* We want to find values in a set of shared_ptr by reference */
+	auto operator()(const std::shared_ptr<T> &a) const {
+		return std::hash<T>()(*a);
+	}
+	auto operator()(const T &a) const {
+		return std::hash<T>()(a);
+	}
+};
+
+class css_declarations_block {
+public:
+	using rule_shared_ptr = std::shared_ptr<css_rule>;
+	using rule_shared_hash = shared_ptr_hash<css_rule>;
+	using rule_shared_eq = shared_ptr_equal<css_rule>;
+	css_declarations_block() = default;
+	auto add_rule(rule_shared_ptr &&rule) -> void;
+	auto get_rules(void) const -> const auto & {
+		return rules;
+	}
+
+	auto has_rule(const css_rule &rule) const -> bool {
+		return (rules.find(rule) != rules.end());
+	}
+private:
+	robin_hood::unordered_flat_set<rule_shared_ptr, rule_shared_hash, rule_shared_eq> rules;
+};
+
+auto process_declaration_tokens(rspamd_mempool_t *pool,
+							 const blocks_gen_functor &next_token_functor)
+	-> css_declarations_block;
+
 }
 
 #endif //RSPAMD_CSS_RULE_HXX
\ No newline at end of file
diff --git a/src/libserver/css/css_value.hxx b/src/libserver/css/css_value.hxx
index 64109c5f0..d7c8f5c45 100644
--- a/src/libserver/css/css_value.hxx
+++ b/src/libserver/css/css_value.hxx
@@ -59,20 +59,23 @@ enum class css_display_value {
  * for simplicity
  */
 struct css_value {
+	/* Bitset of known types */
 	enum class css_value_type {
-		CSS_VALUE_COLOR,
-		CSS_VALUE_NUMBER,
-		CSS_VALUE_DISPLAY,
-		CSS_VALUE_DIMENSION,
-		CSS_VALUE_NYI,
-	} type;
-
+		CSS_VALUE_COLOR = 1 << 0,
+		CSS_VALUE_NUMBER = 1 << 1,
+		CSS_VALUE_DISPLAY = 1 << 2,
+		CSS_VALUE_DIMENSION = 1 << 3,
+		CSS_VALUE_NYI = 1 << 4,
+	};
+
+	css_value_type type;
 	std::variant<css_color,
 			double,
 			css_display_value,
 			css_dimension,
 			std::monostate> value;
 
+	css_value() : type(css_value_type::CSS_VALUE_NYI) {}
 	css_value(const css_color &color) :
 			type(css_value_type::CSS_VALUE_COLOR), value(color) {}
 	css_value(double num) :


More information about the Commits mailing list