ladybird/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.cpp
Max Wipfli 519a1cdc22 LibWeb: Change HTMLToken storage architecture
This completely changes how HTMLTokens store their data. Previously,
space was allocated for all token types separately. Now, the HTMLToken's
data is stored in just a String, two booleans and a Variant.

This change reduces sizeof(HTMLToken) from 68 to 32. Also, this reduces
raw tokenization time by around 20 to 50 percent, depending on the page.
Full document parsing time (with HTMLDocumentParser, on a local HTML
page without any dependency files) is reduced by between 4 and 20
percent, depending on the page.

Since tokenizing HTML pages can easily generated 50'000 tokens and more,
the storage has been designed in a way that avoids heap allocations
where possible, while trying to reduce the size of the tokens. The only
tokens which need to allocate on the heap are thus DOCTYPE tokens (max.
1 per document), and tag tokens (but only if they have attributes). This
way, only around 5 percent of all tokens generated need to allocate on
the heap (except for StringImpl allocations).
2021-07-17 16:24:57 +04:30

76 lines
2 KiB
C++

/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/HTML/Parser/HTMLToken.h>
namespace Web::HTML {
String HTMLToken::to_string() const
{
StringBuilder builder;
switch (type()) {
case HTMLToken::Type::DOCTYPE:
builder.append("DOCTYPE");
builder.append(" { name: '");
builder.append(doctype_data().name);
builder.append("' }");
break;
case HTMLToken::Type::StartTag:
builder.append("StartTag");
break;
case HTMLToken::Type::EndTag:
builder.append("EndTag");
break;
case HTMLToken::Type::Comment:
builder.append("Comment");
break;
case HTMLToken::Type::Character:
builder.append("Character");
break;
case HTMLToken::Type::EndOfFile:
builder.append("EndOfFile");
break;
case HTMLToken::Type::Invalid:
VERIFY_NOT_REACHED();
}
if (type() == HTMLToken::Type::StartTag || type() == HTMLToken::Type::EndTag) {
builder.append(" { name: '");
builder.append(tag_name());
builder.append("', { ");
for_each_attribute([&](auto& attribute) {
builder.append(attribute.local_name);
builder.append("=\"");
builder.append(attribute.value);
builder.append("\" ");
return IterationDecision::Continue;
});
builder.append("} }");
}
if (is_comment()) {
builder.append(" { data: '");
builder.append(comment());
builder.append("' }");
}
if (is_character()) {
builder.append(" { data: '");
builder.append_code_point(code_point());
builder.append("' }");
}
if (type() == HTMLToken::Type::Character) {
builder.appendff("@{}:{}", m_start_position.line, m_start_position.column);
} else {
builder.appendff("@{}:{}-{}:{}", m_start_position.line, m_start_position.column, m_end_position.line, m_end_position.column);
}
return builder.to_string();
}
}