[インデックス 13213] ファイルの概要
このコミットは、Go言語の実験的なロケールパッケージ exp/locale/collate
内の maketables.go
および builder.go
に関連する変更です。exp/locale/collate
パッケージは、Unicode Collation Algorithm (UCA) に基づいて文字列の照合(ソート順序の決定)を行うためのデータテーブルを生成する機能を提供します。このコミットの主な目的は、テーブル構築プロセスにおける二重構築(double building)の回避と、データ整合性チェックの追加です。
コミット
このコミットは、exp/locale/collate
パッケージにおけるテーブル生成プロセスを改善し、効率性と堅牢性を向上させるものです。具体的には、maketables.go
での二重構築を避け、builder.go
にチェックを追加することで、テーブル構築のロジックをより安全にしています。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c633f85f65ece7bb063bd5dd7b06aff167ba5f59
元コミット内容
commit c633f85f65ece7bb063bd5dd7b06aff167ba5f59
Author: Marcel van Lohuizen <mpvl@golang.org>
Date: Wed May 30 17:47:56 2012 +0200
exp/locale/collate: avoid double building in maketables.go. Also added check.
R=r
CC=golang-dev
https://golang.org/cl/6202063
変更の背景
exp/locale/collate
パッケージは、Unicode Collation Algorithm (UCA) に基づく照合テーブルを生成するためのビルドツールを含んでいます。これらのテーブルは、異なる言語や文化圏における文字列の正しいソート順序を決定するために不可欠です。
このコミットが行われた背景には、テーブル構築プロセスにおける潜在的な非効率性とバグの可能性がありました。具体的には、maketables.go
というファイルがテーブルを生成する際に、builder.go
内の build()
メソッドが複数回呼び出される可能性があり、これが「二重構築(double building)」の問題を引き起こしていました。二重構築は、不必要な計算リソースの消費や、場合によっては不正確なテーブル生成につながる可能性があります。
また、照合要素のプライマリウェイト(主要なソートキー)が予期せぬ大きな値になる可能性があり、これに対するチェックが不足していました。UCAでは、プライマリウェイトは通常、特定の範囲内に収まることが期待されます。この範囲を超える値は、データ処理の誤りや、生成されるテーブルの破損を示す可能性があります。
これらの問題を解決するため、開発者は build()
メソッドが一度だけ実行されるように制御するメカニズムと、プライマリウェイトの範囲を検証するチェックを追加する必要がありました。
前提知識の解説
Unicode Collation Algorithm (UCA)
Unicode Collation Algorithm (UCA) は、Unicode文字列を言語的・文化的に正しい順序でソートするための国際標準です。単純なバイナリ比較では、異なる言語の文字や記号が期待通りにソートされないため、UCAが導入されました。UCAは、以下の主要な概念に基づいています。
- 照合要素 (Collation Elements): 各文字や文字の組み合わせは、一つまたは複数の照合要素にマッピングされます。これらの要素は、ソートの際に比較される数値のシーケンスです。
- 多段階ソート (Multi-level Sorting): UCAは通常、3つまたは4つのレベルでソートを行います。
- プライマリウェイト (Primary Weight): 文字の基本的な形状や識別子に基づきます。例えば、'a'と'b'は異なるプライマリウェイトを持ちます。これは大文字・小文字やアクセント記号を無視した比較に相当します。
- セカンダリウェイト (Secondary Weight): アクセント記号やダイアクリティカルマーク(例: 'a'と'á')の違いを区別します。
- ターシャリウェイト (Tertiary Weight): 大文字・小文字の違い(例: 'a'と'A')を区別します。
- クォータナリウェイト (Quaternary Weight): 特定のケース(例: 圧縮された空白文字)で追加の区別を提供します。
- 照合キー (Collation Key): 文字列から生成されるバイト列で、このキーをバイナリ比較することで、UCAに準拠したソート順序が得られます。
exp/locale/collate
パッケージ
Go言語の golang.org/x/text/collate
(以前は exp/locale/collate
として実験的に開発されていた) パッケージは、UCAをGoプログラムで利用するための機能を提供します。このパッケージは、特定のロケール(言語タグ)に基づいて Collator
インスタンスを作成し、文字列の比較やソートを行うことができます。
このパッケージの内部では、UCAのルールに従って文字列を比較するためのデータテーブルが必要です。これらのテーブルは、Unicodeのデータファイルから生成され、builder.go
のようなツールによって構築されます。
builder.go
の役割
builder.go
ファイルは、exp/locale/collate
パッケージが使用する照合テーブルを構築するためのロジックを含んでいます。Builder
型は、Unicodeの照合データを取り込み、それをGoプログラムで効率的に利用できる形式のテーブルに変換する役割を担っています。この変換プロセスには、CJK(中国語、日本語、韓国語)文字の処理、照合要素の簡素化、拡張や収縮の処理、そして最終的なトライ(trie)構造の構築などが含まれます。
技術的詳細
このコミットは、src/pkg/exp/locale/collate/build/builder.go
ファイルに焦点を当てています。
Builder
構造体への built
フィールドの追加
Builder
構造体は、照合テーブルを構築するプロセス全体を管理します。このコミットでは、Builder
構造体に新たに built bool
フィールドが追加されました。
type Builder struct {
entry []*entry
t *table
err error
built bool // 新しく追加されたフィールド
}
この built
フィールドは、Builder
インスタンスが既にテーブル構築プロセスを完了したかどうかを示すフラグとして機能します。
build()
メソッドの変更
Builder
構造体の build()
メソッドは、実際に照合テーブルを構築する一連の処理を実行します。このコミットでは、build()
メソッドの冒頭に if !b.built
という条件が追加されました。
func (b *Builder) build() (*table, error) {
if !b.built { // 新しく追加された条件
b.built = true
b.t = &table{}
b.contractCJK()
b.simplify() // requires contractCJK
b.processExpansions() // requires simplify
b.processContractions() // requires simplify
b.buildTrie() // requires process*
}
if b.err != nil {
return nil, b.err
}
// ... 後続の処理 ...
}
この変更により、build()
メソッドが複数回呼び出された場合でも、テーブル構築のコアロジック(contractCJK()
から buildTrie()
までの一連の処理)は一度しか実行されなくなります。b.built
が true
に設定されると、それ以降の呼び出しではこのブロックがスキップされます。
convertLargeWeights
関数へのチェック追加
convertLargeWeights
関数は、照合要素のプライマリウェイトを処理し、必要に応じて大きなウェイトを変換する役割を担っています。この関数に、プライマリウェイト p
が 0xFFFF
(65535) を超えていないかをチェックする新しい条件が追加されました。
func convertLargeWeights(elems [][]int) (res [][]int, err error) {
// ... 既存のコード ...
for _, ce := range elems {
p := ce[0] // プライマリウェイト
if p < firstLargePrimary {
continue
}
if p > 0xFFFF { // 新しく追加されたチェック
return elems, fmt.Errorf("found primary weight %X; should be <= 0xFFFF", p)
}
if p >= illegalPrimary {
ce[0] = illegalOffset + p - illegalPrimary
} else {
// ... 既存のコード ...
}
}
// ... 既存のコード ...
}
このチェックは、プライマリウェイトが予期される範囲内にあることを保証するためのものです。0xFFFF
を超えるプライマリウェイトは、通常、データのエラーや不正な照合要素の生成を示唆するため、ここでエラーを発生させることで早期に問題を検出できます。
コアとなるコードの変更箇所
--- a/src/pkg/exp/locale/collate/build/builder.go
+++ b/src/pkg/exp/locale/collate/build/builder.go
@@ -22,6 +22,7 @@ import (
// - trie valueBlocks are currently 100K. There are a lot of sparse blocks
// and many consecutive values with the same stride. This can be further
// compacted.
+// - compress secondary weights into 8 bits.
// entry is used to keep track of a single entry in the collation element table
// during building. Examples of entries can be found in the Default Unicode
@@ -69,6 +70,7 @@ type Builder struct {
entry []*entry
t *table
err error
+ built bool
}
// NewBuilder returns a new Builder.
@@ -178,14 +180,16 @@ func (b *Builder) error(e error) {
}\n \n func (b *Builder) build() (*table, error) {\n-\tb.t = &table{}\n-\n-\tb.contractCJK()\n-\tb.simplify() // requires contractCJK
-\tb.processExpansions() // requires simplify
-\tb.processContractions() // requires simplify
-\tb.buildTrie() // requires process*
-\n+\tif !b.built {\n+\t\tb.built = true\n+\t\tb.t = &table{}\n+\n+\t\tb.contractCJK()\n+\t\tb.simplify() // requires contractCJK\n+\t\tb.processExpansions() // requires simplify\n+\t\tb.processContractions() // requires simplify\n+\t\tb.buildTrie() // requires process*\n+\t}\n \tif b.err != nil {\n \t\treturn nil, b.err\n \t}\n@@ -334,6 +338,9 @@ func convertLargeWeights(elems [][]int) (res [][]int, err error) {\n \t\tif p < firstLargePrimary {\n \t\t\tcontinue\n \t\t}\n+\t\tif p > 0xFFFF {\n+\t\t\treturn elems, fmt.Errorf(\"found primary weight %X; should be <= 0xFFFF\", p)\n+\t\t}\n \t\tif p >= illegalPrimary {\n \t\t\tce[0] = illegalOffset + p - illegalPrimary\n \t\t} else {\n```
## コアとなるコードの解説
### `Builder` 構造体への `built` フィールドの追加
`Builder` 構造体に `built bool` フィールドが追加されたことで、`Builder` インスタンスがテーブル構築処理を一度実行したかどうかを追跡できるようになりました。これは、`build()` メソッドが複数回呼び出されるシナリオにおいて、不必要な再構築を防ぐための状態管理フラグとして機能します。
### `build()` メソッドの変更
`build()` メソッド内の変更は、テーブル構築の効率性と堅牢性を大幅に向上させます。
```go
if !b.built {
b.built = true
b.t = &table{}
b.contractCJK()
b.simplify()
b.processExpansions()
b.processContractions()
b.buildTrie()
}
この if !b.built
ブロックは、以下の目的を果たします。
- 二重構築の回避:
build()
メソッドが初めて呼び出されたときにのみ、b.built
がfalse
であるため、テーブル構築の主要なステップ(contractCJK()
からbuildTrie()
まで)が実行されます。一度実行されるとb.built
はtrue
に設定され、それ以降のbuild()
の呼び出しではこのブロックがスキップされます。これにより、テーブルが不必要に複数回構築されることを防ぎ、パフォーマンスが向上します。 - 初期化の保証:
b.t = &table{}
の行は、テーブル構築が開始される前に新しいテーブルインスタンスが確実に初期化されることを保証します。
convertLargeWeights
関数へのチェック追加
convertLargeWeights
関数に追加された以下のチェックは、データ整合性の観点から非常に重要です。
if p > 0xFFFF {
return elems, fmt.Errorf("found primary weight %X; should be <= 0xFFFF", p)
}
- プライマリウェイトの範囲検証: Unicode Collation Algorithm (UCA) におけるプライマリウェイトは、通常、特定の範囲内に収まることが期待されます。
0xFFFF
(65535) は、多くのシステムでプライマリウェイトを表現するために使用される16ビットの最大値に対応します。このチェックは、生成されたプライマリウェイトがこの上限を超えていないことを確認します。 - 早期エラー検出: もしプライマリウェイトが
0xFFFF
を超える場合、それは通常、入力データの問題、アルゴリズムの実装ミス、または予期せぬデータ状態を示します。このチェックにより、問題がさらに深刻になる前に早期にエラーを検出し、デバッグを容易にします。fmt.Errorf
を使用して具体的なエラーメッセージを返すことで、問題の原因特定に役立つ情報を提供しています。
これらの変更は、exp/locale/collate
パッケージが生成する照合テーブルの信頼性と効率性を高める上で重要な役割を果たします。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/c633f85f65ece7bb063bd5dd7b06aff167ba5f59
- Go言語
x/text/collate
パッケージ (現在の場所): https://pkg.go.dev/golang.org/x/text/collate
参考にした情報源リンク
- go.dev:
golang.org/x/text/collate
package documentation - reintech.io: Understanding Go's
collate
package - unicode.org: Unicode Collation Algorithm (UCA)
- stackoverflow.com: Go
collate
package usage examples - golangbridge.org: Discussion on Go's
collate
package - github.com: Unicode Collation Algorithm (UCA) specifications and implementations