[インデックス 16649] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/md5
パッケージに対する変更です。具体的には、MD5ハッシュ計算をより簡単に行えるようにトップレベルの Sum
関数が追加され、また、block
関数に //go:noescape
ディレクティブが追加されています。
コミット
commit 4850f5d5ea5913af57c9ae37121ad88aba2d612c
Author: Rob Pike <r@golang.org>
Date: Wed Jun 26 11:29:30 2013 -0700
crypto/md5: provide a top-level Sum function
Makes it easy to ask the simple question, what is the hash of this data?
Also mark block as non-escaping.
R=golang-dev, agl
CC=golang-dev
https://golang.org/cl/10624044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4850f5d5ea5913af57c9ae37121ad88aba2d612c
元コミット内容
crypto/md5: provide a top-level Sum function
Makes it easy to ask the simple question, what is the hash of this data?
Also mark block as non-escaping.
変更の背景
このコミットには主に二つの背景があります。
-
MD5ハッシュ計算の簡素化: 既存の
crypto/md5
パッケージでは、MD5ハッシュを計算するためにmd5.New()
でハッシュオブジェクトを作成し、Write()
メソッドでデータを書き込み、最後にSum()
メソッドを呼び出すという一連の手順が必要でした。これは柔軟性を提供しますが、単にバイト列のMD5ハッシュ値が欲しいだけのシンプルなユースケースでは冗長でした。このコミットは、このようなシンプルな要求に応えるため、トップレベルのSum
関数を提供し、より直感的なAPIを提供することを目的としています。 -
パフォーマンス最適化 (
//go:noescape
): Go言語のコンパイラは、関数の引数や戻り値がヒープに割り当てられるべきか(エスケープ解析)を自動的に判断します。ヒープ割り当てはガベージコレクションの対象となり、パフォーマンスに影響を与える可能性があります。//go:noescape
ディレクティブは、特定の関数がポインタをヒープにエスケープさせないことをコンパイラに明示的に伝えるためのものです。これにより、コンパイラはより積極的な最適化(例えば、スタック割り当ての維持)を行うことができ、特に頻繁に呼び出される低レベルの関数においてパフォーマンスの向上が期待されます。crypto/md5
パッケージのblock
関数は、MD5計算のコアとなる部分であり、頻繁に呼び出されるため、この最適化の恩恵を受けることができます。
前提知識の解説
MD5 (Message-Digest Algorithm 5)
MD5は、128ビット(16バイト)のハッシュ値を生成する広く使われている暗号学的ハッシュ関数です。任意の長さの入力データから、固定長のハッシュ値を生成します。主な用途は、データの完全性検証(データが改ざんされていないことの確認)や、パスワードのハッシュ化などです。 しかし、MD5は衝突耐性(異なる入力から同じハッシュ値が生成されること)に脆弱性が見つかっており、セキュリティが非常に重要な場面(デジタル署名など)ではSHA-256などのより強力なハッシュ関数が推奨されています。それでも、データの完全性チェックや非暗号学的な用途では依然として広く利用されています。
Go言語の crypto/md5
パッケージ
Go言語の標準ライブラリ crypto/md5
は、MD5ハッシュ関数を実装しています。
基本的な使用方法は以下のようになります。
package main
import (
"crypto/md5"
"fmt"
"io"
)
func main() {
h := md5.New() // ハッシュオブジェクトの作成
io.WriteString(h, "Hello, world!") // データの書き込み
fmt.Printf("%x\n", h.Sum(nil)) // ハッシュ値の取得
}
Go言語のエスケープ解析と //go:noescape
ディレクティブ
Go言語のコンパイラは、プログラムの実行時に変数がどこに割り当てられるべきか(スタックかヒープか)を決定する「エスケープ解析」を行います。
- スタック割り当て: 関数内で宣言された変数が、その関数が終了するまでに不要になる場合、通常はスタックに割り当てられます。スタック割り当ては高速で、ガベージコレクションのオーバーヘッドがありません。
- ヒープ割り当て: 変数が関数のスコープを超えて参照される可能性がある場合(例えば、関数の戻り値としてポインタが返される場合や、グローバル変数に代入される場合)、ヒープに割り当てられます。ヒープ割り当てはスタック割り当てよりも遅く、ガベージコレクションの対象となります。
//go:noescape
は、Goコンパイラに対する特別なディレクティブ(プラグマ)です。このディレクティブが関数宣言の直前に記述されている場合、コンパイラはその関数が引数として受け取ったポインタをヒープにエスケープさせないことを保証すると仮定します。もし実際にエスケープさせているにもかかわらず //go:noescape
を指定すると、実行時エラーや未定義の動作を引き起こす可能性があります。
このディレクティブは、主にアセンブリ言語で書かれた関数や、コンパイラがエスケープ解析を誤る可能性がある非常に低レベルの最適化が必要なGo関数で使用されます。これにより、コンパイラはより積極的な最適化(例えば、スタック割り当ての維持)を行うことができ、ガベージコレクションの負荷を軽減し、パフォーマンスを向上させることができます。
技術的詳細
このコミットは、crypto/md5
パッケージに以下の主要な変更を加えています。
-
Sum
関数の追加:md5.go
にトップレベルのSum(data []byte) [Size]byte
関数が追加されました。- この関数は、入力バイトスライス
data
を受け取り、そのMD5ハッシュ値を[Size]byte
型(Size
はMD5ハッシュのバイト長、つまり16)の配列として直接返します。 - 内部的には、
digest
型の新しいハッシュオブジェクトを作成し、Reset()
で初期化、Write()
でデータを書き込み、新しく追加されたcheckSum()
メソッドを呼び出してハッシュ値を取得しています。
-
checkSum
メソッドの導入:- 既存の
(*digest) Sum(in []byte) []byte
メソッドから、実際のハッシュ計算とパディングロジックを(*digest) checkSum() [Size]byte
という新しいプライベートメソッドに分離しました。 - これにより、ハッシュ計算のコアロジックが再利用可能になり、トップレベルの
Sum
関数と既存のSum
メソッドの両方から呼び出せるようになりました。 checkSum
メソッドは、ハッシュ値を[Size]byte
型の配列として返します。これは、Goの配列が値型であり、ヒープへのエスケープを避けるのに役立つため、パフォーマンス上の利点があります。
- 既存の
-
//go:noescape
ディレクティブの追加:md5block_decl.go
ファイルのblock
関数宣言に//go:noescape
ディレクティブが追加されました。block
関数は、MD5のコア計算ループでデータを処理する低レベルの関数です。この関数がポインタをヒープにエスケープさせないことをコンパイラに明示することで、コンパイラはより効率的なコードを生成し、ガベージコレクションのオーバーヘッドを削減できます。
-
テストケースの追加:
md5_test.go
に、新しく追加されたトップレベルのSum
関数をテストするためのコードが追加されました。- 既存のゴールデンテスト(既知の入力と期待される出力のペア)を利用して、
Sum([]byte(g.in))
の結果が期待通りであることを検証しています。
コアとなるコードの変更箇所
src/pkg/crypto/md5/md5.go
--- a/src/pkg/crypto/md5/md5.go
+++ b/src/pkg/crypto/md5/md5.go
@@ -88,7 +88,11 @@ func (d *digest) Write(p []byte) (nn int, err error) {
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := *d0
+ hash := d.checkSum()
+ return append(in, hash[:]...)
+}
+func (d *digest) checkSum() [Size]byte {
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
len := d.len
var tmp [64]byte
@@ -118,5 +122,13 @@ func (d0 *digest) Sum(in []byte) []byte {
digest[i*4+3] = byte(s >> 24)
}
- return append(in, digest[:]...)\n+ return digest
+}
+
+// Sum returns the MD5 checksum of the data.
+func Sum(data []byte) [Size]byte {
+ var d digest
+ d.Reset()
+ d.Write(data)
+ return d.checkSum()
}
src/pkg/crypto/md5/md5_test.go
--- a/src/pkg/crypto/md5/md5_test.go
+++ b/src/pkg/crypto/md5/md5_test.go
@@ -53,6 +53,10 @@ var golden = []md5Test{
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
+ s := fmt.Sprintf("%x", Sum([]byte(g.in)))
+ if s != g.out {
+ t.Fatalf("Sum function: md5(%s) = %s want %s", g.in, s, g.out)
+ }
c := New()
buf := make([]byte, len(g.in)+4)
for j := 0; j < 3+4; j++ {
src/pkg/crypto/md5/md5block_decl.go
--- a/src/pkg/crypto/md5/md5block_decl.go
+++ b/src/pkg/crypto/md5/md5block_decl.go
@@ -6,4 +6,6 @@
package md5
+//go:noescape
+
func block(dig *digest, p []byte)
コアとなるコードの解説
md5.go
の変更点
-
func (d0 *digest) Sum(in []byte) []byte
の変更:- このメソッドは、既存の
hash.Hash
インターフェースの一部として提供されるSum
メソッドです。 - 変更前は、ハッシュ計算と結果の
in
への追加を直接行っていました。 - 変更後は、
d.checkSum()
を呼び出して実際のハッシュ値を計算し、その結果をin
スライスにappend
して返しています。これにより、ハッシュ計算ロジックがcheckSum
に集約されました。
- このメソッドは、既存の
-
func (d *digest) checkSum() [Size]byte
の追加:- この新しいプライベートメソッドは、MD5ハッシュの最終計算とパディング処理を担当します。
digest
構造体の現在の状態(長さlen
とハッシュ値h
)に基づいて、MD5の最終的なハッシュ値を計算し、[Size]byte
型の配列として返します。- この分離により、ハッシュ計算のコアロジックがカプセル化され、再利用性が向上しました。
-
func Sum(data []byte) [Size]byte
の追加:- これがこのコミットの主要な機能追加です。
data
バイトスライスを直接受け取り、そのMD5ハッシュ値を計算して返します。- 内部では、一時的な
digest
オブジェクトを作成し、Reset()
で初期化、Write(data)
で入力データを処理し、最後にd.checkSum()
を呼び出してハッシュ値を取得しています。 - この関数により、MD5ハッシュを計算する際のボイラープレートコードが大幅に削減され、より簡潔に記述できるようになりました。
md5_test.go
の変更点
TestGolden
関数内に、新しく追加されたトップレベルのSum
関数をテストするコードが追加されました。fmt.Sprintf("%x", Sum([]byte(g.in)))
を使用して、Sum
関数が生成するハッシュ値を文字列に変換し、既存のゴールデンテストの期待値g.out
と比較しています。- これにより、新しい
Sum
関数が正しく機能することを確認しています。
md5block_decl.go
の変更点
func block(dig *digest, p []byte)
の宣言の直前に//go:noescape
ディレクティブが追加されました。- このディレクティブは、
block
関数が引数dig
とp
のポインタをヒープにエスケープさせないことをコンパイラに保証します。 - これにより、コンパイラは
block
関数の呼び出しにおいて、より積極的な最適化(例えば、スタック割り当ての維持)を行うことができ、ガベージコレクションのオーバーヘッドを減らし、パフォーマンスを向上させることが期待されます。
関連リンク
- Go言語の
crypto/md5
パッケージのドキュメント: https://pkg.go.dev/crypto/md5 - Go言語のエスケープ解析に関する情報: https://go.dev/doc/articles/go_mem.html (Go Memory Model)
//go:noescape
ディレクティブに関する情報 (Goのソースコードや関連するGoのIssueで詳細が見つかることが多い)
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード (特に
src/cmd/compile/internal/gc/escape.go
など、コンパイラのエスケープ解析に関連する部分) - MD5に関する一般的な情報源 (例: Wikipedia)
- Go言語のコミット履歴と関連するコードレビュー (Go CL 10624044)
[インデックス 16649] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/md5
パッケージに対する変更です。具体的には、MD5ハッシュ計算をより簡単に行えるようにトップレベルの Sum
関数が追加され、また、block
関数に //go:noescape
ディレクティブが追加されています。
コミット
commit 4850f5d5ea5913af57c9ae37121ad88aba2d612c
Author: Rob Pike <r@golang.org>
Date: Wed Jun 26 11:29:30 2013 -0700
crypto/md5: provide a top-level Sum function
Makes it easy to ask the simple question, what is the hash of this data?
Also mark block as non-escaping.
R=golang-dev, agl
CC=golang-dev
https://golang.org/cl/10624044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4850f5d5ea5913af57c9ae37121ad88aba2d612c
元コミット内容
crypto/md5: provide a top-level Sum function
Makes it easy to ask the simple question, what is the hash of this data?
Also mark block as non-escaping.
変更の背景
このコミットには主に二つの背景があります。
-
MD5ハッシュ計算の簡素化: 既存の
crypto/md5
パッケージでは、MD5ハッシュを計算するためにmd5.New()
でハッシュオブジェクトを作成し、Write()
メソッドでデータを書き込み、最後にSum()
メソッドを呼び出すという一連の手順が必要でした。これは柔軟性を提供しますが、単にバイト列のMD5ハッシュ値が欲しいだけのシンプルなユースケースでは冗長でした。このコミットは、このようなシンプルな要求に応えるため、トップレベルのSum
関数を提供し、より直感的なAPIを提供することを目的としています。 -
パフォーマンス最適化 (
//go:noescape
): Go言語のコンパイラは、関数の引数や戻り値がヒープに割り当てられるべきか(エスケープ解析)を自動的に判断します。ヒープ割り当てはガベージコレクションの対象となり、パフォーマンスに影響を与える可能性があります。//go:noescape
ディレクティブは、特定の関数がポインタをヒープにエスケープさせないことをコンパイラに明示的に伝えるためのものです。これにより、コンパイラはより積極的な最適化(例えば、スタック割り当ての維持)を行うことができ、特に頻繁に呼び出される低レベルの関数においてパフォーマンスの向上が期待されます。crypto/md5
パッケージのblock
関数は、MD5計算のコアとなる部分であり、頻繁に呼び出されるため、この最適化の恩恵を受けることができます。
前提知識の解説
MD5 (Message-Digest Algorithm 5)
MD5は、128ビット(16バイト)のハッシュ値を生成する広く使われている暗号学的ハッシュ関数です。任意の長さの入力データから、固定長のハッシュ値を生成します。主な用途は、データの完全性検証(データが改ざんされていないことの確認)や、パスワードのハッシュ化などです。 しかし、MD5は衝突耐性(異なる入力から同じハッシュ値が生成されること)に脆弱性が見つかっており、セキュリティが非常に重要な場面(デジタル署名など)ではSHA-256などのより強力なハッシュ関数が推奨されています。それでも、データの完全性チェックや非暗号学的な用途では依然として広く利用されています。
Go言語の crypto/md5
パッケージ
Go言語の標準ライブラリ crypto/md5
は、MD5ハッシュ関数を実装しています。
基本的な使用方法は以下のようになります。
package main
import (
"crypto/md5"
"fmt"
"io"
)
func main() {
h := md5.New() // ハッシュオブジェクトの作成
io.WriteString(h, "Hello, world!") // データの書き込み
fmt.Printf("%x\n", h.Sum(nil)) // ハッシュ値の取得
}
Go言語のエスケープ解析と //go:noescape
ディレクティブ
Go言語のコンパイラは、プログラムの実行時に変数がどこに割り当てられるべきか(スタックかヒープか)を決定する「エスケープ解析」を行います。
- スタック割り当て: 関数内で宣言された変数が、その関数が終了するまでに不要になる場合、通常はスタックに割り当てられます。スタック割り当ては高速で、ガベージコレクションのオーバーヘッドがありません。
- ヒープ割り当て: 変数が関数のスコープを超えて参照される可能性がある場合(例えば、関数の戻り値としてポインタが返される場合や、グローバル変数に代入される場合)、ヒープに割り当てられます。ヒープ割り当てはスタック割り当てよりも遅く、ガベージコレクションの対象となります。
//go:noescape
は、Goコンパイラに対する特別なディレクティブ(プラグマ)です。このディレクティブが関数宣言の直前に記述されている場合、コンパイラはその関数が引数として受け取ったポインタをヒープにエスケープさせないことを保証すると仮定します。もし実際にエスケープさせているにもかかわらず //go:noescape
を指定すると、実行時エラーや未定義の動作を引き起こす可能性があります。
このディレクティブは、主にアセンブリ言語で書かれた関数や、コンパイラがエスケープ解析を誤る可能性がある非常に低レベルの最適化が必要なGo関数で使用されます。これにより、コンパイラはより積極的な最適化(例えば、スタック割り当ての維持)を行うことができ、ガベージコレクションの負荷を軽減し、パフォーマンスを向上させることができます。
技術的詳細
このコミットは、crypto/md5
パッケージに以下の主要な変更を加えています。
-
Sum
関数の追加:md5.go
にトップレベルのSum(data []byte) [Size]byte
関数が追加されました。- この関数は、入力バイトスライス
data
を受け取り、そのMD5ハッシュ値を[Size]byte
型(Size
はMD5ハッシュのバイト長、つまり16)の配列として直接返します。 - 内部的には、
digest
型の新しいハッシュオブジェクトを作成し、Reset()
で初期化、Write()
でデータを書き込み、新しく追加されたcheckSum()
メソッドを呼び出してハッシュ値を取得しています。
-
checkSum
メソッドの導入:- 既存の
(*digest) Sum(in []byte) []byte
メソッドから、実際のハッシュ計算とパディングロジックを(*digest) checkSum() [Size]byte
という新しいプライベートメソッドに分離しました。 - これにより、ハッシュ計算のコアロジックが再利用可能になり、トップレベルの
Sum
関数と既存のSum
メソッドの両方から呼び出せるようになりました。 checkSum
メソッドは、ハッシュ値を[Size]byte
型の配列として返します。これは、Goの配列が値型であり、ヒープへのエスケープを避けるのに役立つため、パフォーマンス上の利点があります。
- 既存の
-
//go:noescape
ディレクティブの追加:md5block_decl.go
ファイルのblock
関数宣言に//go:noescape
ディレクティブが追加されました。block
関数は、MD5のコア計算ループでデータを処理する低レベルの関数です。この関数がポインタをヒープにエスケープさせないことをコンパイラに明示することで、コンパイラはより効率的なコードを生成し、ガベージコレクションのオーバーヘッドを削減できます。
-
テストケースの追加:
md5_test.go
に、新しく追加されたトップレベルのSum
関数をテストするためのコードが追加されました。- 既存のゴールデンテスト(既知の入力と期待される出力のペア)を利用して、
Sum([]byte(g.in))
の結果が期待通りであることを検証しています。
コアとなるコードの変更箇所
src/pkg/crypto/md5/md5.go
--- a/src/pkg/crypto/md5/md5.go
+++ b/src/pkg/crypto/md5/md5.go
@@ -88,7 +88,11 @@ func (d *digest) Write(p []byte) (nn int, err error) {
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := *d0
+ hash := d.checkSum()
+ return append(in, hash[:]...)
+}
+func (d *digest) checkSum() [Size]byte {
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
len := d.len
var tmp [64]byte
@@ -118,5 +122,13 @@ func (d0 *digest) Sum(in []byte) []byte {
digest[i*4+3] = byte(s >> 24)
}
- return append(in, digest[:]...)\n+ return digest
+}
+
+// Sum returns the MD5 checksum of the data.
+func Sum(data []byte) [Size]byte {
+ var d digest
+ d.Reset()
+ d.Write(data)
+ return d.checkSum()
}
src/pkg/crypto/md5/md5_test.go
--- a/src/pkg/crypto/md5/md5_test.go
+++ b/src/pkg/crypto/md5/md5_test.go
@@ -53,6 +53,10 @@ var golden = []md5Test{
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
+ s := fmt.Sprintf("%x", Sum([]byte(g.in)))
+ if s != g.out {
+ t.Fatalf("Sum function: md5(%s) = %s want %s", g.in, s, g.out)
+ }
c := New()
buf := make([]byte, len(g.in)+4)
for j := 0; j < 3+4; j++ {
src/pkg/crypto/md5/md5block_decl.go
--- a/src/pkg/crypto/md5/md5block_decl.go
+++ b/src/pkg/crypto/md5/md5block_decl.go
@@ -6,4 +6,6 @@
package md5
+//go:noescape
+
func block(dig *digest, p []byte)
コアとなるコードの解説
md5.go
の変更点
-
func (d0 *digest) Sum(in []byte) []byte
の変更:- このメソッドは、既存の
hash.Hash
インターフェースの一部として提供されるSum
メソッドです。 - 変更前は、ハッシュ計算と結果の
in
への追加を直接行っていました。 - 変更後は、
d.checkSum()
を呼び出して実際のハッシュ値を計算し、その結果をin
スライスにappend
して返しています。これにより、ハッシュ計算ロジックがcheckSum
に集約されました。
- このメソッドは、既存の
-
func (d *digest) checkSum() [Size]byte
の追加:- この新しいプライベートメソッドは、MD5ハッシュの最終計算とパディング処理を担当します。
digest
構造体の現在の状態(長さlen
とハッシュ値h
)に基づいて、MD5の最終的なハッシュ値を計算し、[Size]byte
型の配列として返します。- この分離により、ハッシュ計算のコアロジックがカプセル化され、再利用性が向上しました。
-
func Sum(data []byte) [Size]byte
の追加:- これがこのコミットの主要な機能追加です。
data
バイトスライスを直接受け取り、そのMD5ハッシュ値を計算して返します。- 内部では、一時的な
digest
オブジェクトを作成し、Reset()
で初期化、Write(data)
で入力データを処理し、最後にd.checkSum()
を呼び出してハッシュ値を取得しています。 - この関数により、MD5ハッシュを計算する際のボイラープレートコードが大幅に削減され、より簡潔に記述できるようになりました。
md5_test.go
の変更点
TestGolden
関数内に、新しく追加されたトップレベルのSum
関数をテストするコードが追加されました。fmt.Sprintf("%x", Sum([]byte(g.in)))
を使用して、Sum
関数が生成するハッシュ値を文字列に変換し、既存のゴールデンテストの期待値g.out
と比較しています。- これにより、新しい
Sum
関数が正しく機能することを確認しています。
md5block_decl.go
の変更点
func block(dig *digest, p []byte)
の宣言の直前に//go:noescape
ディレクティブが追加されました。- このディレクティブは、
block
関数が引数dig
とp
のポインタをヒープにエスケープさせないことをコンパイラに保証します。 - これにより、コンパイラは
block
関数の呼び出しにおいて、より積極的な最適化(例えば、スタック割り当ての維持)を行うことができ、ガベージコレクションのオーバーヘッドを減らし、パフォーマンスを向上させることが期待されます。
関連リンク
- Go言語の
crypto/md5
パッケージのドキュメント: https://pkg.go.dev/crypto/md5 - Go言語のエスケープ解析に関する情報: https://go.dev/doc/articles/go_mem.html (Go Memory Model)
//go:noescape
ディレクティブに関する情報 (Goのソースコードや関連するGoのIssueで詳細が見つかることが多い)
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード (特に
src/cmd/compile/internal/gc/escape.go
など、コンパイラのエスケープ解析に関連する部分) - MD5に関する一般的な情報源 (例: Wikipedia)
- Go言語のコミット履歴と関連するコードレビュー (Go CL 10624044)