[インデックス 18025] ファイルの概要
このコミットは、Go言語の標準ライブラリnet
パッケージ内のlookup_plan9.go
ファイルに対する変更です。具体的には、文字列を小文字に変換するtoLower
関数の実装を、strings
パッケージへの依存なしに行うように修正しています。これにより、net
パッケージがstrings
およびunicode
パッケージに依存しない、より軽量な実装が実現されています。
コミット
commit e87b1710138e512019cc2bb22420a15cc5336bf3
Author: David du Colombier <0intro@gmail.com>
Date: Tue Dec 17 14:19:11 2013 -0800
net: reimplement toLower to not depend on strings
R=golang-dev, r, bradfitz
CC=golang-dev, jas
https://golang.org/cl/43610043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e87b1710138e512019cc2bb22420a15cc5336bf3
元コミット内容
net: reimplement toLower to not depend on strings
このコミットは、net
パッケージ内のtoLower
関数を再実装し、strings
パッケージへの依存を取り除くことを目的としています。
変更の背景
Go言語の標準ライブラリは、その設計において効率性と最小限の依存関係を重視しています。特に、低レベルのネットワーク操作を扱うnet
パッケージのようなコアコンポーネントでは、不必要な依存関係を排除することで、バイナリサイズを削減し、コンパイル時間を短縮し、実行時のオーバーヘッドを最小限に抑えることができます。
このコミットの背景には、strings.ToLower
関数が内部的にunicode
パッケージに依存している可能性があり、これがnet
パッケージにとって過剰な依存関係と見なされたことが考えられます。lookup_plan9.go
というファイル名が示すように、このコードはPlan 9オペレーティングシステム向けのネットワークルックアップ処理に関連しています。Plan 9は、そのミニマリストな設計思想で知られており、GoのPlan 9ポートにおいても、可能な限り軽量な実装が求められる傾向にあります。
lookupProtocol
関数が扱うプロトコル名(例: "tcp", "udp"など)は通常ASCII文字のみで構成されており、完全なUnicode対応の小文字変換は不要です。そのため、strings.ToLower
が提供する広範なUnicode対応は、この特定のコンテキストではオーバースペックであり、その依存関係を排除することがパフォーマンスやリソース使用の観点から望ましいと判断されたものと推測されます。
前提知識の解説
- Go言語の
strings
パッケージ: Go言語の標準ライブラリの一部で、文字列操作のためのユーティリティ関数を提供します。strings.ToLower
は、与えられた文字列のすべての文字を小文字に変換する関数です。この関数は、Unicodeのケースマッピングルールに従って変換を行います。 - Go言語の
unicode
パッケージ: Unicode文字のプロパティ(カテゴリ、スクリプト、ケースマッピングなど)に関する情報を提供するパッケージです。strings.ToLower
のような関数は、多言語対応のためにこのパッケージを利用することがあります。 - Plan 9オペレーティングシステム: ベル研究所で開発された分散オペレーティングシステムです。その設計哲学は、すべてをファイルとして扱うというもので、シンプルさとモジュール性を重視しています。Go言語はPlan 9の影響を強く受けており、Goの初期のバージョンではPlan 9への移植が積極的に行われていました。
net
パッケージ: Go言語の標準ライブラリの一部で、ネットワークI/O機能を提供します。TCP/IP、UDP、DNSルックアップなど、様々なネットワークプロトコルを扱うための機能が含まれています。- ASCII (American Standard Code for Information Interchange): 英語圏で広く使われる文字コードで、基本的な英数字、記号、制御文字を定義しています。0から127までの値で表現されます。
- Unicode: 世界中の文字を統一的に扱うための文字コード標準です。ASCIIを含む多くの文字体系をサポートしています。
技術的詳細
このコミットの主要な技術的変更点は、strings.ToLower
の代わりに、ASCII文字のみを対象としたカスタムの小文字変換関数toLower
を導入したことです。
元のstrings.ToLower
は、内部的にUnicodeのケースマッピングテーブルを参照し、多言語の文字(例: ドイツ語のẞ
がss
になるなど)も適切に小文字に変換します。しかし、lookup_plan9.go
のコンテキストでは、IPプロトコル名(例: "TCP", "UDP")のようなASCII文字のみを扱うため、この広範な機能は不要です。
新しいtoLower
関数は、以下のロジックで動作します。
- 事前チェック: 入力文字列
in
が既にすべて小文字であるかをチェックします。- 文字列を1文字ずつ(runeとして)走査し、
'A'
から'Z'
の範囲の大文字が見つかった場合、isAlreadyLowerCase
フラグをfalse
に設定し、ループを中断します。 - もしループが完了しても
isAlreadyLowerCase
がtrue
のままであれば、文字列は既にすべて小文字なので、そのままin
を返します。これにより、不要なメモリ割り当てと処理を避けます。
- 文字列を1文字ずつ(runeとして)走査し、
- 小文字変換: 入力文字列に大文字が含まれている場合、新しいバイトスライス
out
を作成し、入力文字列のバイト表現をコピーします。out
スライスを走査し、各バイトc
がASCIIの大文字('A'
から'Z'
)の範囲にあるかをチェックします。- もし大文字であれば、そのバイトに
'a' - 'A'
(これは32に相当します)を加算することで、対応する小文字に変換します。これはASCII文字コードの特性を利用したもので、大文字と小文字の間に32の差があることを利用しています。 - 変換後、
out
バイトスライスをstring
型に変換して返します。
このカスタム実装は、strings
パッケージやunicode
パッケージへの依存を完全に排除し、ASCII文字の小文字変換に特化することで、より軽量で効率的なコードを実現しています。特に、Plan 9のようなリソースが限られた環境や、バイナリサイズが重要なアプリケーションにおいて、このような最適化は有益です。
コアとなるコードの変更箇所
src/pkg/net/lookup_plan9.go
ファイルにおいて、以下の変更が行われました。
-
strings
パッケージのインポート削除:--- a/src/pkg/net/lookup_plan9.go +++ b/src/pkg/net/lookup_plan9.go @@ -7,7 +7,6 @@ package net import ( "errors" "os" - "strings" )
-
カスタム
toLower
関数の追加:// toLower returns a lower-case version of in. Restricting us to // ASCII is sufficient to handle the IP protocol names and allow // us to not depend on the strings and unicode packages. func toLower(in string) string { isAlreadyLowerCase := true for _, c := range in { if 'A' <= c && c <= 'Z' { isAlreadyLowerCase = false break } } if isAlreadyLowerCase { return in } out := []byte(in) for i, c := range out { if 'A' <= c && c <= 'Z' { out[i] += 'a' - 'A' } } return string(out) }
-
lookupProtocol
関数内でのtoLower
の使用:--- a/src/pkg/net/lookup_plan9.go +++ b/src/pkg/net/lookup_plan9.go @@ -70,10 +69,33 @@ func queryDNS(addr string, typ string) (res []string, err error) { return query("/net/dns", addr+" "+typ, 1024) } +// toLower returns a lower-case version of in. Restricting us to +// ASCII is sufficient to handle the IP protocol names and allow +// us to not depend on the strings and unicode packages. +func toLower(in string) string { + isAlreadyLowerCase := true + for _, c := range in { + if 'A' <= c && c <= 'Z' { + isAlreadyLowerCase = false + break + } + } + if isAlreadyLowerCase { + return in + } + out := []byte(in) + for i, c := range out { + if 'A' <= c && c <= 'Z' { + out[i] += 'a' - 'A' + } + } + return string(out) +} + // lookupProtocol looks up IP protocol name and returns // the corresponding protocol number. func lookupProtocol(name string) (proto int, err error) { - lines, err := query("/net/cs", "!protocol="+strings.ToLower(name), 128) + lines, err := query("/net/cs", "!protocol="+toLower(name), 128) if err != nil { return 0, err }
コアとなるコードの解説
このコミットの核心は、toLower
関数の新しい実装です。
func toLower(in string) string {
// 1. 事前チェック: 既に小文字であるかを確認
isAlreadyLowerCase := true
for _, c := range in { // 文字列をrune(Unicodeコードポイント)として走査
if 'A' <= c && c <= 'Z' { // 大文字のASCII文字が見つかった場合
isAlreadyLowerCase = false // フラグをfalseに設定
break // ループを中断
}
}
if isAlreadyLowerCase {
return in // 既に小文字であれば、元の文字列をそのまま返す
}
// 2. 小文字変換: 大文字が含まれている場合のみ実行
out := []byte(in) // 入力文字列をバイトスライスに変換(ASCII文字の場合、1バイト1文字)
for i, c := range out { // バイトスライスを走査
if 'A' <= c && c <= 'Z' { // 大文字のASCIIバイトが見つかった場合
out[i] += 'a' - 'A' // ASCIIコードの差を利用して小文字に変換 (例: 'A'(65) + 32 = 'a'(97))
}
}
return string(out) // 変換されたバイトスライスを文字列に変換して返す
}
この関数は、入力文字列in
を受け取り、その小文字バージョンを返します。重要なのは、この関数がASCII文字の範囲に限定されている点です。
isAlreadyLowerCase
チェック: 最初に、入力文字列に大文字が含まれているかを効率的にチェックします。もし大文字が一つもなければ、変換の必要がないため、元の文字列をそのまま返します。これは、既に小文字である文字列に対して無駄な処理やメモリ割り当てを避けるための最適化です。- バイトスライスへの変換と直接操作: 大文字が含まれている場合、文字列をバイトスライス
[]byte
に変換します。Goの文字列はイミュータブル(不変)であるため、変更を加えるには一度バイトスライスに変換する必要があります。 - ASCII変換ロジック: 各バイト
c
がASCIIの大文字('A'
から'Z'
)の範囲にある場合、'a' - 'A'
(これは数値の32に相当します)を加算することで、対応する小文字に変換します。これは、ASCII文字コードにおいて大文字と小文字が連続しており、その差が一定であるという特性を利用した、非常に効率的な方法です。 - 文字列への再変換: 最後に、変更されたバイトスライスを再び文字列に変換して返します。
このカスタムtoLower
関数は、lookupProtocol
関数内でstrings.ToLower(name)
の代わりに呼び出されるようになり、net
パッケージのlookup_plan9.go
ファイルからstrings
パッケージへの直接的な依存がなくなりました。これにより、この特定のコンテキストにおけるGoバイナリのフットプリントがわずかに削減され、より効率的な実行が可能になります。
関連リンク
- Go言語の
strings
パッケージのドキュメント: https://pkg.go.dev/strings - Go言語の
unicode
パッケージのドキュメント: https://pkg.go.dev/unicode - Plan 9 from Bell Labs: https://9p.io/plan9/
- Go言語の
net
パッケージのドキュメント: https://pkg.go.dev/net
参考にした情報源リンク
- GitHubのコミットページ: https://github.com/golang/go/commit/e87b1710138e512019cc2bb22420a15cc5336bf3
- Gerrit Code Review (Go project): https://golang.org/cl/43610043 (現在はGitHubにリダイレクトされる可能性があります)
- ASCIIコード表 (大文字と小文字の差について): 一般的なプログラミングリソースやオンラインのASCIIテーブルで確認できます。
- Go言語の文字列とバイトスライスに関する公式ドキュメントやチュートリアル。