[インデックス 13307] ファイルの概要
このコミットは、Go言語の実験的なHTMLパーサーであるexp/html
パッケージにおける重要な最適化と堅牢性向上を目的としています。具体的には、HTML要素のタグ名を文字列として比較する代わりに、内部的に整数値("アトム")として比較するように変更しています。これにより、パーサーのパフォーマンスが向上し、より効率的な処理が可能になります。
コミット
commit c8fac7b9676a84778280b44684e76f930e7f0bd0
Author: Nigel Tao <nigeltao@golang.org>
Date: Thu Jun 7 13:46:57 2012 +1000
exp/html: when parsing, compare atoms (ints) instead of strings.
This is the mechanical part of the 2-part change that started with
https://golang.org/cl/6305053/
R=rsc
CC=andybalholm, golang-dev, r
https://golang.org/cl/6295055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c8fac7b9676a84778280b44684e76f930e7f0bd0
元コミット内容
exp/html: when parsing, compare atoms (ints) instead of strings.
このコミットは、HTMLパーサーがタグ名を比較する際に、文字列ではなくアトム(整数)を使用するように変更します。これは、https://golang.org/cl/6305053/ で始まった2段階の変更の機械的な部分です。
変更の背景
HTMLパーシングにおいて、要素のタグ名(例: <div>
, <span>
, <p>
)は頻繁に比較されます。従来のパーサーでは、これらのタグ名を文字列として直接比較していました。しかし、文字列比較は、特に大量の要素を処理する場合に、パフォーマンスのボトルネックとなる可能性があります。文字列比較は文字ごとに比較を行うため、計算コストが高く、メモリ割り当ても発生しやすいです。
この問題を解決するため、Go言語のexp/html
パッケージでは、HTMLタグ名を一意の整数値("アトム")にマッピングするアプローチが導入されました。これにより、文字列比較を整数比較に置き換えることが可能になり、パーシング処理の効率と速度が大幅に向上します。
このコミットは、その変更の「機械的な部分」であり、アトムの導入によって可能になった、既存のコードベースにおける文字列比較から整数比較への置き換えを大規模に行っています。参照されているhttps://golang.org/cl/6305053/
は、このアトム化の概念と、a.Atom
型および関連する定数の導入に関する最初の変更セットです。このコミットは、その先行する変更によって提供された新しいAPIとデータ構造を、パーサーのロジック全体に適用するものです。
前提知識の解説
HTMLパーシングの基本
HTMLパーシングとは、HTMLドキュメントを読み込み、その構造を解析して、プログラムが扱えるデータ構造(通常はDOMツリー)に変換するプロセスです。このプロセスでは、タグの開始・終了、属性の解析、テキストコンテンツの抽出など、様々な処理が行われます。パーサーは、HTML仕様に厳密に従ってドキュメントを解釈し、エラーを適切に処理する必要があります。
アトム化 (Atomization)
アトム化とは、頻繁に現れる文字列(この場合はHTMLタグ名)を、より効率的に比較できる一意の整数値にマッピングする技術です。例えば、HTMLのタグ名である"div"
、"span"
、"p"
といった文字列は、それぞれa.Div
、a.Span
、a.P
といった整数定数に対応付けられます。
アトム化の主な利点は以下の通りです。
- 高速な比較: 文字列比較は文字数に比例して時間がかかりますが、整数比較は常に一定時間で完了します。これは、特にパーサーのように大量の文字列比較を行うシステムにおいて、顕著なパフォーマンス向上をもたらします。
- メモリ効率: 同じ文字列が複数回出現する場合でも、メモリ上にはその文字列のコピーが1つだけ存在し、各参照は対応する整数値を持つだけで済みます。これにより、メモリ使用量を削減できます。
- タイプセーフティ: 特定の文字列が特定の意味を持つことをコード上で明示的に表現できるため、誤った文字列が使用されるリスクを減らし、コードの可読性と保守性を向上させます。
Go言語におけるexp/html
パッケージ
exp/html
パッケージは、Go言語でHTML5の仕様に準拠したパーサーを提供する実験的なパッケージです。このパッケージは、WebブラウザがHTMLを解析するのと同じアルゴリズム(HTML5パーシングアルゴリズム)を実装することを目指しています。このパッケージは、Go標準ライブラリのhtml
パッケージの基盤となりました。
技術的詳細
このコミットの核心は、src/pkg/exp/html/parse.go
ファイルにおけるHTMLタグ名の比較方法の変更です。
-
a.Atom
型の導入と使用:- 以前は、HTMLタグ名は
string
型として扱われ、Node
構造体のData
フィールドに格納されていました。 - この変更により、
Node
構造体にDataAtom a.Atom
という新しいフィールドが追加されました。a.Atom
は、HTMLタグ名に対応する整数定数を定義した型です。 - 例えば、
a.Html
,a.Table
,a.Td
などは、それぞれHTMLの<html>
,<table>
,<td>
タグに対応する一意の整数値です。
- 以前は、HTMLタグ名は
-
文字列比較から整数比較への置き換え:
- パーサー内の多くの場所で、タグ名を比較するために
switch n.Data
やt == tag
のような文字列比較が使用されていました。 - これらの比較は、
switch n.DataAtom
やt == tagAtom
のように、a.Atom
型の値を用いた整数比較に置き換えられました。 - これにより、パーシングの各段階で発生するタグ名の比較処理が劇的に高速化されます。
- パーサー内の多くの場所で、タグ名を比較するために
-
関数のシグネチャ変更:
- タグ名を受け取る関数のシグネチャも変更されました。例えば、
popUntil(s scope, matchTags ...string)
はpopUntil(s scope, matchTags ...a.Atom)
に変更され、文字列の可変長引数ではなく、アトムの可変長引数を受け取るようになりました。 addElement
関数も、タグ名を文字列とアトムの両方で受け取るように変更され、内部でDataAtom
フィールドを設定するようになりました。
- タグ名を受け取る関数のシグネチャも変更されました。例えば、
-
defaultScopeStopTags
の型変更:- HTMLパーシングのスコープ管理で使用される
defaultScopeStopTags
マップのキーと値の型が、map[string][]string
からmap[string][]a.Atom
に変更されました。これにより、このマップもアトムベースの比較を利用できるようになりました。
- HTMLパーシングのスコープ管理で使用される
これらの変更は、HTMLパーサーの内部ロジック全体にわたって適用されており、タグ名の処理方法の根本的な変更を反映しています。
コアとなるコードの変更箇所
変更は主にsrc/pkg/exp/html/parse.go
ファイルに集中しています。
-
defaultScopeStopTags
の定義:--- a/src/pkg/exp/html/parse.go +++ b/src/pkg/exp/html/parse.go @@ -54,10 +54,10 @@ func (p *parser) top() *Node { // Stop tags for use in popUntil. These come from section 12.2.3.2. var ( - defaultScopeStopTags = map[string][]string{ - "": {"applet", "caption", "html", "table", "td", "th", "marquee", "object"}, - "math": {"annotation-xml", "mi", "mn", "mo", "ms", "mtext"}, - "svg": {"desc", "foreignObject", "title"}, + defaultScopeStopTags = map[string][]a.Atom{ + "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object}, + "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, + "svg": {a.Desc, a.ForeignObject, a.Title}, } )
-
popUntil
関数のシグネチャ変更と内部比較:--- a/src/pkg/exp/html/parse.go +++ b/src/pkg/exp/html/parse.go @@ -90,7 +90,7 @@ const ( // no higher element in the stack that was also in the stop tags). For example, // popUntil(tableScope, "table") returns true and leaves: // ["html", "body", "font"] -func (p *parser) popUntil(s scope, matchTags ...string) bool { +func (p *parser) popUntil(s scope, matchTags ...a.Atom) bool { if i := p.indexOfElementInScope(s, matchTags...); i != -1 { p.oe = p.oe[:i] return true @@ -101,12 +101,12 @@ func (p *parser) popUntil(s scope, matchTags ...string) bool { // indexOfElementInScope returns the index in p.oe of the highest element whose // tag is in matchTags that is in scope. If no matching element is in scope, it // returns -1. -func (p *parser) indexOfElementInScope(s scope, matchTags ...string) int { +func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { for i := len(p.oe) - 1; i >= 0; i-- { - tag := p.oe[i].Data + tagAtom := p.oe[i].DataAtom if p.oe[i].Namespace == "" { for _, t := range matchTags { - if t == tag { + if t == tagAtom { return i } }
-
addElement
関数のシグネチャ変更とDataAtom
の設定:--- a/src/pkg/exp/html/parse.go +++ b/src/pkg/exp/html/parse.go @@ -278,17 +278,20 @@ func (p *parser) addText(text string) { } // addElement calls addChild with an element node. -func (p *parser) addElement(tag string, attr []Attribute) { +// TODO: tagAtom, tag and attr are almost always p.tok.DataAtom, p.tok.Data, p.tok.Attr. +// The common case should be a no-arg addElement method. +func (p *parser) addElement(tagAtom a.Atom, tag string, attr []Attribute) { p.addChild(&Node{ - Type: ElementNode, - Data: tag, // TODO: also set DataAtom. - Attr: attr, + Type: ElementNode, + DataAtom: tagAtom, + Data: tag, + Attr: attr, }) } // Section 12.2.3.3. -func (p *parser) addFormattingElement(tag string, attr []Attribute) { - p.addElement(tag, attr) +func (p *parser) addFormattingElement(tagAtom a.Atom, tag string, attr []Attribute) { + p.addElement(tagAtom, tag, attr) // Implement the Noah's Ark clause, but with three per family instead of two. identicalElements := 0
-
パーシングロジック内の
switch
文でのDataAtom
の使用:--- a/src/pkg/exp/html/parse.go +++ b/src/pkg/exp/html/parse.go @@ -410,28 +413,28 @@ func (p *parser) resetInsertionMode() { tn = p.context } - switch n.Data { - case "select": + switch n.DataAtom { + case a.Select: p.im = inSelectIM - case "td", "th": + case a.Td, a.Th: p.im = inCellIM - case "tr": + case a.Tr: p.im = inRowIM - case "tbody", "thead", "tfoot": + case a.Tbody, a.Thead, a.Tfoot: p.im = inTableBodyIM - case "caption": + case a.Caption: p.im = inCaptionIM - case "colgroup": + case a.Colgroup: p.im = inColumnGroupIM - case "table": + case a.Table: p.im = inTableIM - case "head": + case a.Head: p.im = inBodyIM - case "body": + case a.Body: p.im = inBodyIM - case "frameset": + case a.Frameset: p.im = inFramesetIM - case "html": + case a.Html: p.im = beforeHeadIM default: continue
これらの変更は、parse.go
ファイル全体にわたって数百箇所に及び、文字列ベースのタグ名処理からアトムベースの処理への完全な移行を示しています。
コアとなるコードの解説
このコミットの主要な変更は、HTMLパーサーがタグ名を扱う方法を根本的に変えることです。
-
Node.DataAtom
フィールド:- HTMLドキュメントの各要素は
Node
構造体で表現されます。以前は、タグ名がNode.Data
(string
型)に格納されていました。 - この変更により、
Node
構造体にDataAtom a.Atom
という新しいフィールドが追加されました。a.Atom
は、exp/html/atom
パッケージで定義された整数定数であり、各HTMLタグ名に対応します。 - これにより、タグ名を文字列として保持するだけでなく、そのアトム表現も保持するようになります。
- HTMLドキュメントの各要素は
-
a.Atom
定数による比較:- パーサーのロジックでは、現在のトークンやスタック上の要素のタグ名に基づいて、様々な条件分岐や処理が行われます。
- 例えば、
switch p.tok.Data
(トークンの文字列データで分岐)やif tag == "table"
(文字列比較)のようなコードが多数存在しました。 - これらの文字列比較は、
switch p.tok.DataAtom
やif tagAtom == a.Table
のように、対応するa.Atom
定数を用いた整数比較に置き換えられました。 - 整数比較はCPUサイクルが少なく、文字列比較に比べてはるかに高速です。これは、特に大規模なHTMLドキュメントを解析する際に、パーサー全体のパフォーマンスに大きな影響を与えます。
-
関数の引数としての
a.Atom
:popUntil
,indexOfElementInScope
,elementInScope
などの関数は、以前はタグ名をstring
型の可変長引数として受け取っていました。- これらの関数のシグネチャが変更され、
a.Atom
型の可変長引数を受け取るようになりました。これにより、関数内部でのタグ名比較もアトムベースで行われるようになります。
-
addElement
とaddFormattingElement
の変更:- これらの関数は、新しい要素ノードを作成し、それをパーサーのスタックに追加する役割を担います。
- 変更後、これらの関数はタグの文字列表現(
tag string
)とアトム表現(tagAtom a.Atom
)の両方を受け取るようになりました。 - これにより、作成される
Node
にはData
とDataAtom
の両方のフィールドが適切に設定され、後続の処理でアトムベースの比較が利用できるようになります。
このコミットは、HTMLパーシングのパフォーマンスを向上させるための重要なステップであり、Go言語のexp/html
パッケージがより効率的で堅牢なパーサーへと進化する上で不可欠な変更でした。
関連リンク
- 先行する変更セット (CL 6305053): https://golang.org/cl/6305053/ - このコミットで導入された
a.Atom
型と関連定数の定義が含まれています。 - Go言語の
html
パッケージ: https://pkg.go.dev/golang.org/x/net/html -exp/html
パッケージは、最終的にこの標準パッケージに統合されました。
参考にした情報源リンク
- HTML5 Parsing Algorithm: https://html.spec.whatwg.org/multipage/parsing.html - HTMLパーシングの標準仕様。
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/ - Goプロジェクトの変更履歴とコードレビューの詳細を確認できる場所。
- Go言語のドキュメント: https://go.dev/doc/
- アトム化 (コンピュータサイエンス): 一般的なアトム化の概念に関する情報源。# [インデックス 13307] ファイルの概要
このコミットは、Go言語の実験的なHTMLパーサーであるexp/html
パッケージにおける重要な最適化と堅牢性向上を目的としています。具体的には、HTML要素のタグ名を文字列として比較する代わりに、内部的に整数値("アトム")として比較するように変更しています。これにより、パーサーのパフォーマンスが向上し、より効率的な処理が可能になります。
コミット
commit c8fac7b9676a84778280b44684e76f930e7f0bd0
Author: Nigel Tao <nigeltao@golang.org>
Date: Thu Jun 7 13:46:57 2012 +1000
exp/html: when parsing, compare atoms (ints) instead of strings.
This is the mechanical part of the 2-part change that started with
https://golang.org/cl/6305053/
R=rsc
CC=andybalholm, golang-dev, r
https://golang.org/cl/6295055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c8fac7b9676a84778280b44684e76f930e7f0bd0
元コミット内容
exp/html: when parsing, compare atoms (ints) instead of strings.
このコミットは、HTMLパーサーがタグ名を比較する際に、文字列ではなくアトム(整数)を使用するように変更します。これは、https://golang.org/cl/6305053/ で始まった2段階の変更の機械的な部分です。
変更の背景
HTMLパーシングにおいて、要素のタグ名(例: <div>
, <span>
, <p>
)は頻繁に比較されます。従来のパーサーでは、これらのタグ名を文字列として直接比較していました。しかし、文字列比較は、特に大量の要素を処理する場合に、パフォーマンスのボトルネックとなる可能性があります。文字列比較は文字ごとに比較を行うため、計算コストが高く、メモリ割り当ても発生しやすいです。
この問題を解決するため、Go言語のexp/html
パッケージでは、HTMLタグ名を一意の整数値("アトム")にマッピングするアプローチが導入されました。これにより、文字列比較を整数比較に置き換えることが可能になり、パーシング処理の効率と速度が大幅に向上します。
このコミットは、その変更の「機械的な部分」であり、アトムの導入によって可能になった、既存のコードベースにおける文字列比較から整数比較への置き換えを大規模に行っています。参照されているhttps://golang.org/cl/6305053/
は、このアトム化の概念と、a.Atom
型および関連する定数の導入に関する最初の変更セットです。このコミットは、その先行する変更によって提供された新しいAPIとデータ構造を、パーサーのロジック全体に適用するものです。
前提知識の解説
HTMLパーシングの基本
HTMLパーシングとは、HTMLドキュメントを読み込み、その構造を解析して、プログラムが扱えるデータ構造(通常はDOMツリー)に変換するプロセスです。このプロセスでは、タグの開始・終了、属性の解析、テキストコンテンツの抽出など、様々な処理が行われます。パーサーは、HTML仕様に厳密に従ってドキュメントを解釈し、エラーを適切に処理する必要があります。
アトム化 (Atomization)
アトム化とは、頻繁に現れる文字列(この場合はHTMLタグ名)を、より効率的に比較できる一意の整数値にマッピングする技術です。例えば、HTMLのタグ名である"div"
、"span"
、"p"
といった文字列は、それぞれa.Div
、a.Span
、a.P
といった整数定数に対応付けられます。
アトム化の主な利点は以下の通りです。
- 高速な比較: 文字列比較は文字数に比例して時間がかかりますが、整数比較は常に一定時間で完了します。これは、特にパーサーのように大量の文字列比較を行うシステムにおいて、顕著なパフォーマンス向上をもたらします。
- メモリ効率: 同じ文字列が複数回出現する場合でも、メモリ上にはその文字列のコピーが1つだけ存在し、各参照は対応する整数値を持つだけで済みます。これにより、メモリ使用量を削減できます。
- タイプセーフティ: 特定の文字列が特定の意味を持つことをコード上で明示的に表現できるため、誤った文字列が使用されるリスクを減らし、コードの可読性と保守性を向上させます。
Go言語におけるexp/html
パッケージ
exp/html
パッケージは、Go言語でHTML5の仕様に準拠したパーサーを提供する実験的なパッケージです。このパッケージは、WebブラウザがHTMLを解析するのと同じアルゴリズム(HTML5パーシングアルゴリズム)を実装することを目指しています。このパッケージは、Go標準ライブラリのhtml
パッケージの基盤となりました。
技術的詳細
このコミットの核心は、src/pkg/exp/html/parse.go
ファイルにおけるHTMLタグ名の比較方法の変更です。
-
a.Atom
型の導入と使用:- 以前は、HTMLタグ名は
string
型として扱われ、Node
構造体のData
フィールドに格納されていました。 - この変更により、
Node
構造体にDataAtom a.Atom
という新しいフィールドが追加されました。a.Atom
は、HTMLタグ名に対応する整数定数を定義した型です。 - 例えば、
a.Html
,a.Table
,a.Td
などは、それぞれHTMLの<html>
,<table>
,<td>
タグに対応する一意の整数値です。
- 以前は、HTMLタグ名は
-
文字列比較から整数比較への置き換え:
- パーサー内の多くの場所で、タグ名を比較するために
switch n.Data
やt == tag
のような文字列比較が使用されていました。 - これらの比較は、
switch n.DataAtom
やt == tagAtom
のように、a.Atom
型の値を用いた整数比較に置き換えられました。 - これにより、パーシングの各段階で発生するタグ名の比較処理が劇的に高速化されます。
- パーサー内の多くの場所で、タグ名を比較するために
-
関数のシグネチャ変更:
- タグ名を受け取る関数のシグネチャも変更されました。例えば、
popUntil(s scope, matchTags ...string)
はpopUntil(s scope, matchTags ...a.Atom)
に変更され、文字列の可変長引数ではなく、アトムの可変長引数を受け取るようになりました。 addElement
関数も、タグ名を文字列とアトムの両方で受け取るように変更され、内部でDataAtom
フィールドを設定するようになりました。
- タグ名を受け取る関数のシグネチャも変更されました。例えば、
-
defaultScopeStopTags
の型変更:- HTMLパーシングのスコープ管理で使用される
defaultScopeStopTags
マップのキーと値の型が、map[string][]string
からmap[string][]a.Atom
に変更されました。これにより、このマップもアトムベースの比較を利用できるようになりました。
- HTMLパーシングのスコープ管理で使用される
これらの変更は、HTMLパーサーの内部ロジック全体にわたって適用されており、タグ名の処理方法の根本的な変更を反映しています。
コアとなるコードの変更箇所
変更は主にsrc/pkg/exp/html/parse.go
ファイルに集中しています。
-
defaultScopeStopTags
の定義:--- a/src/pkg/exp/html/parse.go +++ b/src/pkg/exp/html/parse.go @@ -54,10 +54,10 @@ func (p *parser) top() *Node { // Stop tags for use in popUntil. These come from section 12.2.3.2. var ( - defaultScopeStopTags = map[string][]string{ - "": {"applet", "caption", "html", "table", "td", "th", "marquee", "object"}, - "math": {"annotation-xml", "mi", "mn", "mo", "ms", "mtext"}, - "svg": {"desc", "foreignObject", "title"}, + defaultScopeStopTags = map[string][]a.Atom{ + "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object}, + "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, + "svg": {a.Desc, a.ForeignObject, a.Title}, } )
-
popUntil
関数のシグネチャ変更と内部比較:--- a/src/pkg/exp/html/parse.go +++ b/src/pkg/exp/html/parse.go @@ -90,7 +90,7 @@ const ( // no higher element in the stack that was also in the stop tags). For example, // popUntil(tableScope, "table") returns true and leaves: // ["html", "body", "font"] -func (p *parser) popUntil(s scope, matchTags ...string) bool { +func (p *parser) popUntil(s scope, matchTags ...a.Atom) bool { if i := p.indexOfElementInScope(s, matchTags...); i != -1 { p.oe = p.oe[:i] return true @@ -101,12 +101,12 @@ func (p *parser) popUntil(s scope, matchTags ...string) bool { // indexOfElementInScope returns the index in p.oe of the highest element whose // tag is in matchTags that is in scope. If no matching element is in scope, it // returns -1. -func (p *parser) indexOfElementInScope(s scope, matchTags ...string) int { +func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { for i := len(p.oe) - 1; i >= 0; i-- { - tag := p.oe[i].Data + tagAtom := p.oe[i].DataAtom if p.oe[i].Namespace == "" { for _, t := range matchTags { - if t == tag { + if t == tagAtom { return i } }
-
addElement
関数のシグネチャ変更とDataAtom
の設定:--- a/src/pkg/exp/html/parse.go +++ b/src/pkg/exp/html/parse.go @@ -278,17 +278,20 @@ func (p *parser) addText(text string) { } // addElement calls addChild with an element node. -func (p *parser) addElement(tag string, attr []Attribute) { +// TODO: tagAtom, tag and attr are almost always p.tok.DataAtom, p.tok.Data, p.tok.Attr. +// The common case should be a no-arg addElement method. +func (p *parser) addElement(tagAtom a.Atom, tag string, attr []Attribute) { p.addChild(&Node{ - Type: ElementNode, - Data: tag, // TODO: also set DataAtom. - Attr: attr, + Type: ElementNode, + DataAtom: tagAtom, + Data: tag, + Attr: attr, }) } // Section 12.2.3.3. -func (p *parser) addFormattingElement(tag string, attr []Attribute) { - p.addElement(tag, attr) +func (p *parser) addFormattingElement(tagAtom a.Atom, tag string, attr []Attribute) { + p.addElement(tagAtom, tag, attr) // Implement the Noah's Ark clause, but with three per family instead of two. identicalElements := 0
-
パーシングロジック内の
switch
文でのDataAtom
の使用:--- a/src/pkg/exp/html/parse.go +++ b/src/pkg/exp/html/parse.go @@ -410,28 +413,28 @@ func (p *parser) resetInsertionMode() { tn = p.context } - switch n.Data { - case "select": + switch n.DataAtom { + case a.Select: p.im = inSelectIM - case "td", "th": + case a.Td, a.Th: p.im = inCellIM - case "tr": + case a.Tr: p.im = inRowIM - case "tbody", "thead", "tfoot": + case a.Tbody, a.Thead, a.Tfoot: p.im = inTableBodyIM - case "caption": + case a.Caption: p.im = inCaptionIM - case "colgroup": + case a.Colgroup: p.im = inColumnGroupIM - case "table": + case a.Table: p.im = inTableIM - case "head": + case a.Head: p.im = inBodyIM - case "body": + case a.Body: p.im = inBodyIM - case "frameset": + case a.Frameset: p.im = inFramesetIM - case "html": + case a.Html: p.im = beforeHeadIM default: continue
これらの変更は、parse.go
ファイル全体にわたって数百箇所に及び、文字列ベースのタグ名処理からアトムベースの処理への完全な移行を示しています。
コアとなるコードの解説
このコミットの主要な変更は、HTMLパーサーがタグ名を扱う方法を根本的に変えることです。
-
Node.DataAtom
フィールド:- HTMLドキュメントの各要素は
Node
構造体で表現されます。以前は、タグ名がNode.Data
(string
型)に格納されていました。 - この変更により、
Node
構造体にDataAtom a.Atom
という新しいフィールドが追加されました。a.Atom
は、exp/html/atom
パッケージで定義された整数定数であり、各HTMLタグ名に対応します。 - これにより、タグ名を文字列として保持するだけでなく、そのアトム表現も保持するようになります。
- HTMLドキュメントの各要素は
-
a.Atom
定数による比較:- パーサーのロジックでは、現在のトークンやスタック上の要素のタグ名に基づいて、様々な条件分岐や処理が行われます。
- 例えば、
switch p.tok.Data
(トークンの文字列データで分岐)やif tag == "table"
(文字列比較)のようなコードが多数存在しました。 - これらの文字列比較は、
switch p.tok.DataAtom
やif tagAtom == a.Table
のように、対応するa.Atom
定数を用いた整数比較に置き換えられました。 - 整数比較はCPUサイクルが少なく、文字列比較に比べてはるかに高速です。これは、特に大規模なHTMLドキュメントを解析する際に、パーサー全体のパフォーマンスに大きな影響を与えます。
-
関数の引数としての
a.Atom
:popUntil
,indexOfElementInScope
,elementInScope
などの関数は、以前はタグ名をstring
型の可変長引数として受け取っていました。- これらの関数のシグネチャが変更され、
a.Atom
型の可変長引数を受け取るようになりました。これにより、関数内部でのタグ名比較もアトムベースで行われるようになります。
-
addElement
とaddFormattingElement
の変更:- これらの関数は、新しい要素ノードを作成し、それをパーサーのスタックに追加する役割を担います。
- 変更後、これらの関数はタグの文字列表現(
tag string
)とアトム表現(tagAtom a.Atom
)の両方を受け取るようになりました。 - これにより、作成される
Node
にはData
とDataAtom
の両方のフィールドが適切に設定され、後続の処理でアトムベースの比較が利用できるようになります。
このコミットは、HTMLパーシングのパフォーマンスを向上させるための重要なステップであり、Go言語のexp/html
パッケージがより効率的で堅牢なパーサーへと進化する上で不可欠な変更でした。
関連リンク
- 先行する変更セット (CL 6305053): https://golang.org/cl/6305053/ - このコミットで導入された
a.Atom
型と関連定数の定義が含まれています。 - Go言語の
html
パッケージ: https://pkg.go.dev/golang.org/x/net/html -exp/html
パッケージは、最終的にこの標準パッケージに統合されました。
参考にした情報源リンク
- HTML5 Parsing Algorithm: https://html.spec.whatwg.org/multipage/parsing.html - HTMLパーシングの標準仕様。
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/ - Goプロジェクトの変更履歴とコードレビューの詳細を確認できる場所。
- Go言語のドキュメント: https://go.dev/doc/
- アトム化 (コンピュータサイエンス): 一般的なアトム化の概念に関する情報源。