[インデックス 13921] ファイルの概要
このコミットは、Go言語のencoding/asn1
パッケージにおける整数型の扱いに関する重要な変更を導入しています。具体的には、コードがint
型を32ビットと仮定していた問題を修正し、64ビット整数への対応を準備するものです。これにより、異なるアーキテクチャや将来のGoのバージョンにおけるint
型のサイズ変更に対して、より堅牢なコードベースが構築されます。
コミット
commit 2f0661558883e60e148d319d89401d56870a9756
Author: Russ Cox <rsc@golang.org>
Date: Mon Sep 24 10:30:37 2012 -0400
encoding/asn1: prepare for 64-bit ints
The code was assuming that int = 32 bits. Don't.
Update #2188.
R=agl
CC=golang-dev
https://golang.org/cl/6543063
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2f0661558883e60e148d319d89401d56870a9756
元コミット内容
encoding/asn1: prepare for 64-bit ints
The code was assuming that int = 32 bits. Don't.
Update #2188.
変更の背景
この変更の背景には、Go言語におけるint
型のサイズに関する設計上の考慮事項があります。Go言語の仕様では、int
型は少なくとも32ビットの符号付き整数であることが保証されていますが、その正確なサイズはコンパイラと実行環境に依存します。多くのシステムでは32ビットですが、64ビットシステムでは64ビットになる可能性があります。
以前のencoding/asn1
パッケージのコードは、int
型が常に32ビットであるという暗黙の仮定に基づいていました。この仮定は、特にparseInt
関数がint64
からint
へのキャストを行う際に、値が32ビットの範囲に収まることを期待している箇所で問題を引き起こす可能性がありました。もしint
が64ビットシステムで64ビットとして扱われるようになると、この仮定は破綻し、予期せぬ挙動やバグにつながる恐れがありました。
コミットメッセージにあるUpdate #2188
は、GoのIssue 2188に関連しています。このIssueは、GoのAPIにおける負のインデックスの使用やint
型のサイズに関する議論を含んでおり、int
が少なくとも32ビットであり、x86_64システムではint64
になる可能性についても触れられています。このコミットは、このような将来的なint
型のサイズ変更に備え、コードの堅牢性を高めることを目的としています。
前提知識の解説
ASN.1 (Abstract Syntax Notation One)
ASN.1は、データ構造を記述するための標準的な記法であり、通信プロトコルやデータストレージにおいて、異なるシステム間でデータを交換する際に使用されます。ASN.1は、データの型(整数、文字列、シーケンスなど)と構造を定義し、その定義に基づいてデータをエンコード(符号化)およびデコード(復号化)します。
Go言語のint
型
Go言語のint
型は、プラットフォームに依存する符号付き整数型です。そのサイズは、実行されているシステムのワードサイズ(通常は32ビットまたは64ビット)に合わせられます。これにより、Goプログラムは異なるアーキテクチャで効率的に動作できますが、開発者はint
の正確なサイズに依存しないようにコードを記述する必要があります。
reflect
パッケージ
Go言語のreflect
パッケージは、実行時にプログラムの構造を検査および変更するための機能を提供します。これにより、型情報、フィールド、メソッドなどを動的に操作できます。このコミットでは、reflect.Int
、reflect.Int32
、reflect.Int64
といった定数を使用して、リフレクションを通じて取得した値の型を識別しています。
技術的詳細
このコミットの主要な技術的変更点は、encoding/asn1
パッケージ内の整数解析ロジックの修正です。
-
parseInt
からparseInt32
へのリネームと型変更:- 元の
parseInt
関数は、int
を返していました。これは、int
が32ビットであるという仮定に基づいていました。 - 変更後、この関数は
parseInt32
とリネームされ、戻り値の型が明示的にint32
に変更されました。これにより、この関数が常に32ビットの整数を扱うことが明確になります。 - 内部では
parseInt64
の結果をint32
にキャストし、元の値とキャスト後の値が異なる場合にエラーを返すことで、オーバーフローを検出しています。
- 元の
-
parseField
関数における整数型のハンドリングの改善:parseField
関数は、ASN.1データをGoの構造体のフィールドにマッピングする役割を担っています。- 以前は
reflect.Int
とreflect.Int32
を個別に処理し、parseInt
を使用していました。reflect.Int64
はparseInt64
を使用していました。 - 変更後、
reflect.Int
,reflect.Int32
,reflect.Int64
の3つの型をまとめて処理するようになりました。 val.Type().Size() == 4
という条件を追加し、Goのint
型が32ビット(4バイト)である場合にparseInt32
を使用し、それ以外の場合(つまり64ビットの場合)はparseInt64
を使用するように分岐しています。これにより、int
型の実際のサイズに応じて適切な解析関数が選択されるようになります。
これらの変更により、encoding/asn1
パッケージは、int
型が32ビットであるか64ビットであるかにかかわらず、正確に整数値を解析できるようになり、将来的なGoのバージョンや異なるアーキテクチャへの対応が強化されました。
コアとなるコードの変更箇所
src/pkg/encoding/asn1/asn1.go
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -77,15 +77,15 @@ func parseInt64(bytes []byte) (ret int64, err error) {
// parseInt treats the given bytes as a big-endian, signed integer and returns
// the result.
-func parseInt(bytes []byte) (int, error) {
+func parseInt32(bytes []byte) (int32, error) {
ret64, err := parseInt64(bytes)
if err != nil {
return 0, err
}
-\tif ret64 != int64(int(ret64)) {\n+\tif ret64 != int64(int32(ret64)) {\n \t\treturn 0, StructuralError{\"integer too large\"}\n \t}\n-\treturn int(ret64), nil
+\treturn int32(ret64), nil
}
var bigOne = big.NewInt(1)
@@ -670,7 +670,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
err = err1
return
case enumeratedType:
-\t\tparsedInt, err1 := parseInt(innerBytes)
+\t\tparsedInt, err1 := parseInt32(innerBytes)
if err1 == nil {
v.SetInt(int64(parsedInt))
}
@@ -692,19 +692,20 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
err = err1
return
-\tcase reflect.Int, reflect.Int32:
-\t\tparsedInt, err1 := parseInt(innerBytes)
-\t\tif err1 == nil {\n-\t\t\tval.SetInt(int64(parsedInt))\n-\t\t}\n-\t\terr = err1
-\t\treturn
-\tcase reflect.Int64:
-\t\tparsedInt, err1 := parseInt64(innerBytes)
-\t\tif err1 == nil {\n-\t\t\tval.SetInt(parsedInt)\n+\tcase reflect.Int, reflect.Int32, reflect.Int64:
+\t\tif val.Type().Size() == 4 {\n+\t\t\tparsedInt, err1 := parseInt32(innerBytes)\n+\t\t\tif err1 == nil {\n+\t\t\t\tval.SetInt(int64(parsedInt))\n+\t\t\t}\n+\t\t\terr = err1
+\t\t} else {\n+\t\t\tparsedInt, err1 := parseInt64(innerBytes)\n+\t\t\tif err1 == nil {\n+\t\t\t\tval.SetInt(parsedInt)\n+\t\t\t}\n+\t\t\terr = err1
}
-\t\terr = err1
return
// TODO(dfc) Add support for the remaining integer types
case reflect.Struct:
src/pkg/encoding/asn1/asn1_test.go
--- a/src/pkg/encoding/asn1/asn1_test.go
+++ b/src/pkg/encoding/asn1/asn1_test.go
@@ -64,7 +64,7 @@ var int32TestData = []int32Test{\n
func TestParseInt32(t *testing.T) {
for i, test := range int32TestData {
-\t\tret, err := parseInt(test.in)
+\t\tret, err := parseInt32(test.in)
if (err == nil) != test.ok {
t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
}
コアとなるコードの解説
parseInt
からparseInt32
への変更
-
旧:
func parseInt(bytes []byte) (int, error)
- この関数は、バイトスライスを解析して
int
型の値を返していました。Goのint
型はプラットフォーム依存であるため、32ビットシステムでは32ビット、64ビットシステムでは64ビットになります。しかし、この関数は暗黙的に32ビットを想定している可能性がありました。 ret64 != int64(int(ret64))
というチェックは、int64
の値をint
にキャストした際に情報が失われないか(つまり、int
の範囲に収まるか)を確認していました。もしint
が32ビットで、ret64
が32ビットの範囲を超える値だった場合、このチェックは正しく機能しますが、int
が64ビットだった場合は常にtrue
となり、チェックの意味がなくなります。
- この関数は、バイトスライスを解析して
-
新:
func parseInt32(bytes []byte) (int32, error)
- 関数名が
parseInt32
に変更され、戻り値の型が明示的にint32
になりました。これにより、この関数が常に32ビットの整数を扱うことが明確になります。 ret64 != int64(int32(ret64))
というチェックに変わりました。これにより、int64
の値を明示的にint32
にキャストし、その際に情報が失われないかを確認しています。これは、int
の実際のサイズに関わらず、常に32ビットの範囲チェックを行うことを保証します。
- 関数名が
parseField
関数における整数型の処理
-
旧:
case reflect.Int, reflect.Int32: parsedInt, err1 := parseInt(innerBytes) // ... case reflect.Int64: parsedInt, err1 := parseInt64(innerBytes) // ...
reflect.Int
とreflect.Int32
はparseInt
で処理され、reflect.Int64
はparseInt64
で処理されていました。ここでもreflect.Int
が32ビットであるという仮定が見られます。
-
新:
case reflect.Int, reflect.Int32, reflect.Int64: if val.Type().Size() == 4 { // 32-bit int parsedInt, err1 := parseInt32(innerBytes) // ... } else { // 64-bit int parsedInt, err1 := parseInt64(innerBytes) // ... }
reflect.Int
,reflect.Int32
,reflect.Int64
の3つの型をまとめて処理するようになりました。val.Type().Size() == 4
という条件が追加されました。これは、リフレクションで取得した値の型が占めるバイトサイズが4バイト(32ビット)であるかどうかをチェックしています。- もし4バイトであれば、それは32ビットの整数型(
int
が32ビットの場合、またはint32
)であると判断し、新しく定義されたparseInt32
関数を使用して解析します。 - それ以外の場合(つまり、
int
が64ビットの場合、またはint64
)は、parseInt64
関数を使用して解析します。
- もし4バイトであれば、それは32ビットの整数型(
- この変更により、
encoding/asn1
パッケージは、Goのint
型が実行環境で32ビットとして扱われるか64ビットとして扱われるかに応じて、適切な整数解析ロジックを動的に選択できるようになりました。これにより、コードの移植性と堅牢性が大幅に向上しています。
asn1_test.go
の変更
TestParseInt32
関数内で、parseInt
の呼び出しがparseInt32
に変更されました。これは、関数名変更に伴うテストコードの更新です。
これらの変更は、Go言語のint
型のプラットフォーム依存性に対応し、encoding/asn1
パッケージがより広範な環境で正しく機能するようにするための重要なステップです。
関連リンク
- Go言語の
int
型に関する議論: https://golang.org/issue/2188 (Web検索結果に基づく) - Go言語の
encoding/asn1
パッケージのドキュメント: https://pkg.go.dev/encoding/asn1 (一般的な情報源として) - Go言語の
reflect
パッケージのドキュメント: https://pkg.go.dev/reflect (一般的な情報源として)
参考にした情報源リンク
- https://github.com/golang/go/commit/2f0661558883e60e148d319d89401d56870a9756
- Web search results for "Go issue 2188" (Google Search)
- 特に、
int
が少なくとも32ビットであり、x86_64システムではint64
になる可能性についての言及。
- 特に、