A major lift.

Quite a few uses of raw memory management had to be
struck down, in a batch.  It could have been a bit
more precise to do some of the lower dependencies
first, but I didn't start there with my analysis.
This commit is contained in:
2025-04-09 03:47:30 -04:00
parent 885a5ba629
commit 228da913c4
6 changed files with 106 additions and 112 deletions

View File

@ -15,6 +15,8 @@
#include "html_common.hh" #include "html_common.hh"
#include "css.hh" #include "css.hh"
#include <iostream>
using namespace dw::core::style; using namespace dw::core::style;
void CssProperty::print () { void CssProperty::print () {
@ -235,8 +237,6 @@ CssSimpleSelector::CssSimpleSelector () {
} }
CssSimpleSelector::~CssSimpleSelector () { CssSimpleSelector::~CssSimpleSelector () {
for (int i = 0; i < klass.size (); i++)
dFree (klass.get (i));
dFree (id); dFree (id);
dFree (pseudo); dFree (pseudo);
} }
@ -244,8 +244,7 @@ CssSimpleSelector::~CssSimpleSelector () {
void CssSimpleSelector::setSelect (SelectType t, const char *v) { void CssSimpleSelector::setSelect (SelectType t, const char *v) {
switch (t) { switch (t) {
case SELECT_CLASS: case SELECT_CLASS:
klass.increase (); klass.push_back(v);
klass.set (klass.size () - 1, dStrdup (v));
break; break;
case SELECT_PSEUDO_CLASS: case SELECT_PSEUDO_CLASS:
if (pseudo == NULL) if (pseudo == NULL)
@ -275,9 +274,9 @@ bool CssSimpleSelector::match (const DoctreeNode *n) {
return false; return false;
for (int i = 0; i < klass.size (); i++) { for (int i = 0; i < klass.size (); i++) {
bool found = false; bool found = false;
if (n->klass != NULL) { if (n->klass.has_value()) {
for (int j = 0; j < n->klass->size (); j++) { for (int j = 0; j < n->klass.value().size (); j++) {
if (dStrAsciiCasecmp (klass.get(i), n->klass->get(j)) == 0) { if (dStrAsciiCasecmp (klass.at(i).c_str(), n->klass.value().at(j).c_str()) == 0) {
found = true; found = true;
break; break;
} }
@ -314,14 +313,13 @@ void CssSimpleSelector::print () {
element, pseudo, id); element, pseudo, id);
fprintf (stderr, "class "); fprintf (stderr, "class ");
for (int i = 0; i < klass.size (); i++) for (int i = 0; i < klass.size (); i++)
fprintf (stderr, ".%s", klass.get (i)); fprintf (stderr, ".%s", klass.at (i).c_str());
} }
CssRule::CssRule (CssSelector *selector, CssPropertyList *props, int pos) { CssRule::CssRule (std::shared_ptr< CssSelector > selector, CssPropertyList *props, int pos) {
assert (selector->size () > 0); assert (selector->size () > 0);
this->selector = selector; this->selector= selector;
this->selector->ref ();
this->props = props; this->props = props;
this->props->ref (); this->props->ref ();
this->pos = pos; this->pos = pos;
@ -329,7 +327,6 @@ CssRule::CssRule (CssSelector *selector, CssPropertyList *props, int pos) {
} }
CssRule::~CssRule () { CssRule::~CssRule () {
selector->unref ();
props->unref (); props->unref ();
} }
@ -351,16 +348,13 @@ void CssRule::print () {
* will be added behind the others. * will be added behind the others.
* This gives later added rules more weight. * This gives later added rules more weight.
*/ */
void CssStyleSheet::RuleList::insert (CssRule *rule) { void
increase (); CssStyleSheet::RuleList::insert( std::shared_ptr< CssRule > rule )
int i = size () - 1; {
auto where= std::upper_bound( begin( rules ), end( rules ), rule,
[&]( const auto &l, const auto &r ) { return l->specificity() < r->specificity(); } );
while (i > 0 && rule->specificity () < get (i - 1)->specificity ()) { rules.insert( where, rule );
*getRef (i) = get (i - 1);
i--;
}
*getRef (i) = rule;
} }
/** /**
@ -369,42 +363,43 @@ void CssStyleSheet::RuleList::insert (CssRule *rule) {
* To improve matching performance the rules are organized into * To improve matching performance the rules are organized into
* rule lists based on the topmost simple selector of their selector. * rule lists based on the topmost simple selector of their selector.
*/ */
void CssStyleSheet::addRule (CssRule *rule) { void CssStyleSheet::addRule (std::shared_ptr< CssRule > rule) {
CssSimpleSelector *top = rule->selector->top (); CssSimpleSelector *top = rule->selector->top ();
RuleList *ruleList = NULL; std::shared_ptr< RuleList > ruleList;
lout::object::ConstString *string; std::string string;
if (top->getId ()) { if (top->getId ()) {
string = new lout::object::ConstString (top->getId ()); string = top->getId ();
ruleList = idTable.get (string); if (not idTable.contains(string)) {
if (ruleList == NULL) { ruleList= std::make_shared< RuleList > ();
ruleList = new RuleList (); idTable.emplace (string, ruleList);
idTable.put (string, ruleList);
} else { } else {
delete string; ruleList= idTable.at(string);
} }
} else if (top->getClass () && top->getClass ()->size () > 0) { assert( ruleList );
string = new lout::object::ConstString (top->getClass ()->get (0)); } else if (top->getClass ().size () > 0) {
ruleList = classTable.get (string); string = top->getClass ().at (0);
if (ruleList == NULL) { if (not classTable.contains(string)) {
ruleList = new RuleList; ruleList = std::make_shared< RuleList >();
classTable.put (string, ruleList); classTable.emplace (string, ruleList);
} else { } else {
delete string; ruleList= classTable.at(string);
} }
assert( ruleList );
} else if (top->getElement () >= 0 && top->getElement () < ntags) { } else if (top->getElement () >= 0 && top->getElement () < ntags) {
ruleList = &elementTable[top->getElement ()]; ruleList = elementTable[top->getElement ()];
assert( ruleList );
} else if (top->getElement () == CssSimpleSelector::ELEMENT_ANY) { } else if (top->getElement () == CssSimpleSelector::ELEMENT_ANY) {
ruleList = &anyTable; ruleList = anyTable;
assert( ruleList );
} }
if (ruleList) { if (ruleList) {
ruleList->insert (rule); ruleList->insert( rule );
if (rule->selector->getRequiredMatchCache () > requiredMatchCache) if (rule->selector->getRequiredMatchCache () > requiredMatchCache)
requiredMatchCache = rule->selector->getRequiredMatchCache (); requiredMatchCache = rule->selector->getRequiredMatchCache ();
} else { } else {
assert (top->getElement () == CssSimpleSelector::ELEMENT_NONE); assert (top->getElement () == CssSimpleSelector::ELEMENT_NONE);
delete rule;
} }
} }
@ -417,13 +412,11 @@ void CssStyleSheet::addRule (CssRule *rule) {
void CssStyleSheet::apply (CssPropertyList *props, Doctree *docTree, void CssStyleSheet::apply (CssPropertyList *props, Doctree *docTree,
const DoctreeNode *node, MatchCache *matchCache) const { const DoctreeNode *node, MatchCache *matchCache) const {
static const int maxLists = 32; static const int maxLists = 32;
const RuleList *ruleList[maxLists]; std::array< std::shared_ptr< const RuleList >, maxLists > ruleList;
int numLists = 0, index[maxLists] = {0}; int numLists = 0, index[maxLists] = {0};
if (node->id) { if (node->id) {
lout::object::ConstString idString (node->id); ruleList[numLists] = idTable.contains( node->id ) ? idTable.at (node->id) : nullptr;
ruleList[numLists] = idTable.get (&idString);
if (ruleList[numLists]) if (ruleList[numLists])
numLists++; numLists++;
} }
@ -435,19 +428,19 @@ void CssStyleSheet::apply (CssPropertyList *props, Doctree *docTree,
break; break;
} }
lout::object::ConstString classString (node->klass->get (i)); const std::string classString= node->klass.value().at( i );
ruleList[numLists] = classTable.get (&classString); ruleList[numLists] = classTable.contains( classString ) ? classTable.at (classString) : nullptr;
if (ruleList[numLists]) if (ruleList[numLists])
numLists++; numLists++;
} }
} }
ruleList[numLists] = &elementTable[node->element]; ruleList[numLists] = elementTable[node->element];
if (ruleList[numLists]) if (ruleList[numLists])
numLists++; numLists++;
ruleList[numLists] = &anyTable; ruleList[numLists] = anyTable;
if (ruleList[numLists]) if (ruleList[numLists])
numLists++; numLists++;
@ -461,21 +454,21 @@ void CssStyleSheet::apply (CssPropertyList *props, Doctree *docTree,
int minSpecIndex = -1; int minSpecIndex = -1;
for (int i = 0; i < numLists; i++) { for (int i = 0; i < numLists; i++) {
const RuleList *rl = ruleList[i]; std::shared_ptr< const RuleList > rl = ruleList[i];
if (rl && rl->size () > index[i] && if (rl && rl->size () > index[i] &&
(rl->get(index[i])->specificity () < minSpec || (rl->at(index[i])->specificity () < minSpec ||
(rl->get(index[i])->specificity () == minSpec && (rl->at(index[i])->specificity () == minSpec &&
rl->get(index[i])->position () < minPos))) { rl->at(index[i])->position () < minPos))) {
minSpec = rl->get(index[i])->specificity (); minSpec = rl->at(index[i])->specificity ();
minPos = rl->get(index[i])->position (); minPos = rl->at(index[i])->position ();
minSpecIndex = i; minSpecIndex = i;
} }
} }
if (minSpecIndex >= 0) { if (minSpecIndex >= 0) {
CssRule *rule = ruleList[minSpecIndex]->get (index[minSpecIndex]); auto &rule = ruleList[minSpecIndex]->at (index[minSpecIndex]);
rule->apply(props, docTree, node, matchCache); rule->apply(props, docTree, node, matchCache);
index[minSpecIndex]++; index[minSpecIndex]++;
} else { } else {
@ -526,26 +519,25 @@ void CssContext::apply (CssPropertyList *props, Doctree *docTree,
sheet[CSS_PRIMARY_USER_IMPORTANT].apply (props, docTree, node, &matchCache); sheet[CSS_PRIMARY_USER_IMPORTANT].apply (props, docTree, node, &matchCache);
} }
void CssContext::addRule (CssSelector *sel, CssPropertyList *props, void CssContext::addRule (std::shared_ptr< CssSelector > sel, CssPropertyList *props,
CssPrimaryOrder order) { CssPrimaryOrder order) {
if (props->size () > 0) { if (props->size () > 0) {
CssRule *rule = new CssRule (sel, props, pos++); auto rule = std::make_unique< CssRule >(sel, props, pos++);
if ((order == CSS_PRIMARY_AUTHOR || if ((order == CSS_PRIMARY_AUTHOR ||
order == CSS_PRIMARY_AUTHOR_IMPORTANT) && order == CSS_PRIMARY_AUTHOR_IMPORTANT) &&
!rule->isSafe ()) { !rule->isSafe ()) {
_MSG_WARN ("Ignoring unsafe author style that might reveal browsing history\n"); _MSG_WARN ("Ignoring unsafe author style that might reveal browsing history\n");
delete rule;
} else { } else {
rule->selector->setMatchCacheOffset(matchCache.size ()); rule->selector->setMatchCacheOffset(matchCache.size ());
if (rule->selector->getRequiredMatchCache () > matchCache.size ()) if (rule->selector->getRequiredMatchCache () > matchCache.size ())
matchCache.setSize (rule->selector->getRequiredMatchCache (), -1); matchCache.setSize (rule->selector->getRequiredMatchCache (), -1);
if (order == CSS_PRIMARY_USER_AGENT) { if (order == CSS_PRIMARY_USER_AGENT) {
userAgentSheet.addRule (rule); userAgentSheet.addRule (std::move(rule));
} else { } else {
sheet[order].addRule (rule); sheet[order].addRule (std::move(rule));
} }
} }
} }

View File

@ -13,10 +13,18 @@
#ifndef __CSS_HH__ #ifndef __CSS_HH__
#define __CSS_HH__ #define __CSS_HH__
#include <string>
#include <memory>
#include <vector>
#include <array>
#include <map>
#include "dw/core.hh" #include "dw/core.hh"
#include "doctree.hh" #include "doctree.hh"
#include "html.hh" #include "html.hh"
#include <Alepha/Utility/evaluate.h>
/* Origin and weight. Used only internally.*/ /* Origin and weight. Used only internally.*/
typedef enum { typedef enum {
CSS_PRIMARY_USER_AGENT, CSS_PRIMARY_USER_AGENT,
@ -356,7 +364,7 @@ class CssSimpleSelector {
private: private:
int element; int element;
char *pseudo, *id; char *pseudo, *id;
lout::misc::SimpleVector <char *> klass; std::vector< std::string > klass;
public: public:
enum { enum {
@ -375,7 +383,7 @@ class CssSimpleSelector {
~CssSimpleSelector (); ~CssSimpleSelector ();
inline void setElement (int e) { element = e; }; inline void setElement (int e) { element = e; };
void setSelect (SelectType t, const char *v); void setSelect (SelectType t, const char *v);
inline lout::misc::SimpleVector <char *> *getClass () { return &klass; }; inline std::vector< std::string > &getClass () { return klass; };
inline const char *getPseudoClass () { return pseudo; }; inline const char *getPseudoClass () { return pseudo; };
inline const char *getId () { return id; }; inline const char *getId () { return id; };
inline int getElement () { return element; }; inline int getElement () { return element; };
@ -438,8 +446,6 @@ class CssSelector {
int specificity (); int specificity ();
bool checksPseudoClass (); bool checksPseudoClass ();
void print (); void print ();
inline void ref () { refCount++; }
inline void unref () { if (--refCount == 0) delete this; }
}; };
/** /**
@ -453,9 +459,9 @@ class CssRule {
int spec, pos; int spec, pos;
public: public:
CssSelector *selector; std::shared_ptr< CssSelector > selector;
CssRule (CssSelector *selector, CssPropertyList *props, int pos); CssRule (std::shared_ptr< CssSelector > selector, CssPropertyList *props, int pos);
~CssRule (); ~CssRule ();
void apply (CssPropertyList *props, Doctree *docTree, void apply (CssPropertyList *props, Doctree *docTree,
@ -475,20 +481,19 @@ class CssRule {
*/ */
class CssStyleSheet { class CssStyleSheet {
private: private:
class RuleList : public lout::misc::SimpleVector <CssRule*>, class RuleList
public lout::object::Object { {
public: private:
RuleList () : lout::misc::SimpleVector <CssRule*> (1) {}; std::vector< std::shared_ptr< CssRule > > rules;
~RuleList () {
for (int i = 0; i < size (); i++)
delete get (i);
};
void insert (CssRule *rule); public:
inline bool equals (lout::object::Object *other) { void insert( std::shared_ptr< CssRule > rule );
return this == other;
}; constexpr std::size_t size() const noexcept { return rules.size(); }
inline int hashValue () { return (intptr_t) this; }; constexpr bool empty() const noexcept { return rules.empty(); }
template< typename Self >
auto &&at( this Self &&self, const std::size_t index ) { return self.rules.at( index ); }
}; };
class RuleMap : public lout::container::typed::HashTable class RuleMap : public lout::container::typed::HashTable
@ -500,13 +505,21 @@ class CssStyleSheet {
static const int ntags = HTML_NTAGS; static const int ntags = HTML_NTAGS;
RuleList elementTable[ntags], anyTable; using ElementTable= std::array< std::shared_ptr< RuleList >, ntags >;
RuleMap idTable, classTable; ElementTable elementTable= Alepha::Utility::evaluate <=[]
{
ElementTable rv;
std::generate( begin( rv ), end( rv ), &std::make_unique< RuleList > );
return rv;
};
std::shared_ptr< RuleList > anyTable= std::make_unique< RuleList >();
std::map< std::string, std::shared_ptr< RuleList > > idTable;
std::map< std::string, std::shared_ptr< RuleList > > classTable;
int requiredMatchCache; int requiredMatchCache;
public: public:
CssStyleSheet () { requiredMatchCache = 0; } CssStyleSheet () { requiredMatchCache = 0; }
void addRule (CssRule *rule); void addRule (std::shared_ptr< CssRule > rule);
void apply (CssPropertyList *props, Doctree *docTree, void apply (CssPropertyList *props, Doctree *docTree,
const DoctreeNode *node, MatchCache *matchCache) const; const DoctreeNode *node, MatchCache *matchCache) const;
int getRequiredMatchCache () { return requiredMatchCache; } int getRequiredMatchCache () { return requiredMatchCache; }
@ -525,7 +538,7 @@ class CssContext {
public: public:
CssContext (); CssContext ();
void addRule (CssSelector *sel, CssPropertyList *props, void addRule (std::shared_ptr< CssSelector > sel, CssPropertyList *props,
CssPrimaryOrder order); CssPrimaryOrder order);
void apply (CssPropertyList *props, void apply (CssPropertyList *props,
Doctree *docTree, DoctreeNode *node, Doctree *docTree, DoctreeNode *node,

View File

@ -1476,14 +1476,13 @@ bool CssParser::parseSimpleSelector(CssSimpleSelector *selector)
return true; return true;
} }
CssSelector *CssParser::parseSelector() std::unique_ptr< CssSelector > CssParser::parseSelector()
{ {
CssSelector *selector = new CssSelector (); auto selector = std::make_unique< CssSelector >();
while (true) { while (true) {
if (! parseSimpleSelector (selector->top ())) { if (! parseSimpleSelector (selector->top ())) {
delete selector; selector = nullptr;
selector = NULL;
break; break;
} }
@ -1499,8 +1498,7 @@ CssSelector *CssParser::parseSelector()
} else if (ttype != CSS_TK_END && spaceSeparated) { } else if (ttype != CSS_TK_END && spaceSeparated) {
selector->addSimpleSelector (CssSelector::COMB_DESCENDANT); selector->addSimpleSelector (CssSelector::COMB_DESCENDANT);
} else { } else {
delete selector; selector = nullptr;
selector = NULL;
break; break;
} }
} }
@ -1515,17 +1513,15 @@ CssSelector *CssParser::parseSelector()
void CssParser::parseRuleset() void CssParser::parseRuleset()
{ {
std::vector < CssSelector * > list; std::vector < std::shared_ptr< CssSelector > > list;
CssPropertyList *props, *importantProps; CssPropertyList *props, *importantProps;
CssSelector *selector;
while (true) { while (true) {
selector = parseSelector(); auto selector= parseSelector();
if (selector) { if (selector) {
selector->ref(); list.push_back(std::move( selector ));
list.push_back(selector);
} }
// \todo dump whole ruleset in case of parse error as required by CSS 2.1 // \todo dump whole ruleset in case of parse error as required by CSS 2.1
@ -1559,7 +1555,7 @@ void CssParser::parseRuleset()
} }
for (std::size_t i = 0; i < list.size(); i++) { for (std::size_t i = 0; i < list.size(); i++) {
CssSelector *s = list.at(i); const auto &s = list.at(i);
if (origin == CSS_ORIGIN_USER_AGENT) { if (origin == CSS_ORIGIN_USER_AGENT) {
context->addRule(s, props, CSS_PRIMARY_USER_AGENT); context->addRule(s, props, CSS_PRIMARY_USER_AGENT);
@ -1570,8 +1566,6 @@ void CssParser::parseRuleset()
context->addRule(s, props, CSS_PRIMARY_AUTHOR); context->addRule(s, props, CSS_PRIMARY_AUTHOR);
context->addRule(s, importantProps, CSS_PRIMARY_AUTHOR_IMPORTANT); context->addRule(s, importantProps, CSS_PRIMARY_AUTHOR_IMPORTANT);
} }
s->unref();
} }
props->unref(); props->unref();

View File

@ -43,7 +43,7 @@ class CssParser {
char *parseUrl(); char *parseUrl();
void parseImport(DilloHtml *html); void parseImport(DilloHtml *html);
void parseMedia(); void parseMedia();
CssSelector *parseSelector(); std::unique_ptr< CssSelector > parseSelector();
void parseRuleset(); void parseRuleset();
void ignoreBlock(); void ignoreBlock();
void ignoreStatement(); void ignoreStatement();

View File

@ -3,6 +3,9 @@
#include "lout/misc.hh" #include "lout/misc.hh"
#include <string>
#include <vector>
class DoctreeNode { class DoctreeNode {
public: public:
DoctreeNode *parent; DoctreeNode *parent;
@ -10,7 +13,7 @@ class DoctreeNode {
DoctreeNode *lastChild; DoctreeNode *lastChild;
int num; // unique ascending id int num; // unique ascending id
int element; int element;
lout::misc::SimpleVector<char*> *klass; std::optional< std::vector< std::string > > klass;
const char *pseudo; const char *pseudo;
const char *id; const char *id;
@ -18,7 +21,6 @@ class DoctreeNode {
parent = NULL; parent = NULL;
sibling = NULL; sibling = NULL;
lastChild = NULL; lastChild = NULL;
klass = NULL;
pseudo = NULL; pseudo = NULL;
id = NULL; id = NULL;
element = 0; element = 0;
@ -31,11 +33,6 @@ class DoctreeNode {
lastChild = lastChild->sibling; lastChild = lastChild->sibling;
delete n; delete n;
} }
if (klass) {
for (int i = 0; i < klass->size (); i++)
dFree (klass->get(i));
delete klass;
}
} }
}; };

View File

@ -165,18 +165,16 @@ void StyleEngine::setId (const char *id) {
/** /**
* \brief split a string at sep chars and return a SimpleVector of strings * \brief split a string at sep chars and return a SimpleVector of strings
*/ */
static lout::misc::SimpleVector<char *> *splitStr (const char *str, char sep) { static std::vector< std::string > splitStr (const char *str, char sep) {
const char *p1 = NULL; const char *p1 = NULL;
lout::misc::SimpleVector<char *> *list = std::vector< std::string > list;
new lout::misc::SimpleVector<char *> (1);
for (;; str++) { for (;; str++) {
if (*str != '\0' && *str != sep) { if (*str != '\0' && *str != sep) {
if (!p1) if (!p1)
p1 = str; p1 = str;
} else if (p1) { } else if (p1) {
list->increase (); list.emplace_back( p1, str - p1 );
list->set (list->size () - 1, dStrndup (p1, str - p1));
p1 = NULL; p1 = NULL;
} }
@ -189,7 +187,7 @@ static lout::misc::SimpleVector<char *> *splitStr (const char *str, char sep) {
void StyleEngine::setClass (const char *klass) { void StyleEngine::setClass (const char *klass) {
DoctreeNode *dn = doctree->top (); DoctreeNode *dn = doctree->top ();
assert (dn->klass == NULL); assert (not dn->klass.has_value());
dn->klass = splitStr (klass, ' '); dn->klass = splitStr (klass, ' ');
} }