Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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関数は、以下のロジックで動作します。

  1. 事前チェック: 入力文字列inが既にすべて小文字であるかをチェックします。
    • 文字列を1文字ずつ(runeとして)走査し、'A'から'Z'の範囲の大文字が見つかった場合、isAlreadyLowerCaseフラグをfalseに設定し、ループを中断します。
    • もしループが完了してもisAlreadyLowerCasetrueのままであれば、文字列は既にすべて小文字なので、そのままinを返します。これにより、不要なメモリ割り当てと処理を避けます。
  2. 小文字変換: 入力文字列に大文字が含まれている場合、新しいバイトスライスoutを作成し、入力文字列のバイト表現をコピーします。
    • outスライスを走査し、各バイトcがASCIIの大文字('A'から'Z')の範囲にあるかをチェックします。
    • もし大文字であれば、そのバイトに'a' - 'A'(これは32に相当します)を加算することで、対応する小文字に変換します。これはASCII文字コードの特性を利用したもので、大文字と小文字の間に32の差があることを利用しています。
    • 変換後、outバイトスライスをstring型に変換して返します。

このカスタム実装は、stringsパッケージやunicodeパッケージへの依存を完全に排除し、ASCII文字の小文字変換に特化することで、より軽量で効率的なコードを実現しています。特に、Plan 9のようなリソースが限られた環境や、バイナリサイズが重要なアプリケーションにおいて、このような最適化は有益です。

コアとなるコードの変更箇所

src/pkg/net/lookup_plan9.goファイルにおいて、以下の変更が行われました。

  1. 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"
     )
    
  2. カスタム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)
    }
    
  3. 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文字の範囲に限定されている点です。

  1. isAlreadyLowerCaseチェック: 最初に、入力文字列に大文字が含まれているかを効率的にチェックします。もし大文字が一つもなければ、変換の必要がないため、元の文字列をそのまま返します。これは、既に小文字である文字列に対して無駄な処理やメモリ割り当てを避けるための最適化です。
  2. バイトスライスへの変換と直接操作: 大文字が含まれている場合、文字列をバイトスライス[]byteに変換します。Goの文字列はイミュータブル(不変)であるため、変更を加えるには一度バイトスライスに変換する必要があります。
  3. ASCII変換ロジック: 各バイトcがASCIIの大文字('A'から'Z')の範囲にある場合、'a' - 'A'(これは数値の32に相当します)を加算することで、対応する小文字に変換します。これは、ASCII文字コードにおいて大文字と小文字が連続しており、その差が一定であるという特性を利用した、非常に効率的な方法です。
  4. 文字列への再変換: 最後に、変更されたバイトスライスを再び文字列に変換して返します。

このカスタムtoLower関数は、lookupProtocol関数内でstrings.ToLower(name)の代わりに呼び出されるようになり、netパッケージのlookup_plan9.goファイルからstringsパッケージへの直接的な依存がなくなりました。これにより、この特定のコンテキストにおけるGoバイナリのフットプリントがわずかに削減され、より効率的な実行が可能になります。

関連リンク

参考にした情報源リンク