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

[インデックス 15634] ファイルの概要

このコミットは、Go言語のlib9ライブラリにおけるWindowsビルドの問題を修正するものです。具体的には、tempdir_windows.cファイル内で使用されていたrunesmprint関数の利用を停止し、代わりにutflenchartorune関数を組み合わせて文字列変換を行うように変更しています。これにより、Windows環境でのビルドエラーが解消されます。

コミット

commit 5641a09171ce220b6eb28631dce6a1243dc78e12
Author: Russ Cox <rsc@golang.org>
Date:   Thu Mar 7 14:38:49 2013 -0500

    lib9: fix windows build (don't use runesmprint)
    
    TBR=golang-dev
    CC=golang-dev
    https://golang.org/cl/7575046

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/5641a09171ce220b6eb28631dce6a1243dc78e12

元コミット内容

lib9: fix windows build (don't use runesmprint)

TBR=golang-dev
CC=golang-dev
https://golang.org/cl/7575046

変更の背景

このコミットの主な背景は、Go言語のlib9ライブラリがWindows環境で正しくビルドできない問題があったことです。コミットメッセージに「fix windows build (don't use runesmprint)」と明記されている通り、runesmprint関数がWindowsビルドにおいて互換性の問題や未定義の動作を引き起こしていた可能性が高いです。

Go言語はクロスプラットフォーム対応を重視しており、様々なオペレーティングシステムで動作するように設計されています。そのため、特定のプラットフォームでビルドが失敗する問題は、開発者にとって重要な修正点となります。この修正は、Go言語のWindowsサポートを強化し、より安定した開発環境を提供することを目的としています。

前提知識の解説

lib9

lib9は、Go言語のランタイムや標準ライブラリの一部として使用される、Plan 9オペレーティングシステムに由来するユーティリティ関数の集合体です。Plan 9はベル研究所で開発された分散オペレーティングシステムであり、その設計思想や一部のAPIはGo言語に大きな影響を与えています。lib9には、文字列操作、ファイルシステム操作、プロセス管理など、低レベルのシステムプログラミングに役立つ関数が含まれています。

Rune と UTF-8

Go言語では、Unicodeコードポイントを表現するためにrune型(int32のエイリアス)を使用します。文字列はUTF-8エンコーディングされたバイト列として扱われます。UTF-8は可変長エンコーディングであり、1つのUnicodeコードポイントが1バイトから4バイトで表現されます。

runesmprint (非推奨/問題のある関数)

runesmprintは、このコミットで削除された関数であり、おそらくC言語のsprintfに似た機能を持つが、Rune文字列を扱うための関数であったと推測されます。コミットの背景から、この関数がWindows環境でのビルドや実行時に問題を引き起こしていたと考えられます。具体的な問題としては、Windowsの文字エンコーディング(通常はUTF-16ベース)とPlan 9由来のRuneやUTF-8の扱いのミスマッチ、あるいはWindows SDKの特定のバージョンとの非互換性などが考えられます。

utflen

utflen関数は、UTF-8エンコードされた文字列のUnicodeコードポイント(Rune)の数を返します。これは、バイト数ではなく、論理的な文字数を数えるために使用されます。例えば、日本語の文字列のようにマルチバイト文字を含む場合でも、正しく文字数をカウントできます。

chartorune

chartorune関数は、UTF-8エンコードされたバイト列の先頭から1つのUnicodeコードポイント(Rune)を読み取り、そのRuneと、読み取ったバイト数を返します。この関数は、UTF-8文字列をRuneのシーケンスにデコードする際に使用されます。

WinRune

WinRuneは、Windows APIが期待するワイド文字(通常はUTF-16エンコードされたwchar_t)を表現するための型であると推測されます。WindowsのファイルパスやAPI呼び出しでは、しばしばワイド文字が使用されるため、Go言語の内部表現(UTF-8/Rune)からWindowsのネイティブ表現への変換が必要になります。

技術的詳細

このコミットの技術的詳細は、src/lib9/tempdir_windows.cファイルにおける文字列変換ロジックの変更に集約されます。

変更前は、torune関数内でrunesmprintを使用して入力文字列pRune配列r1に変換していました。その後、r1の各要素をWinRune配列rにコピーしていました。このアプローチは、runesmprintがWindows環境で問題を引き起こすため、ビルドエラーや実行時エラーの原因となっていました。

変更後は、runesmprintの使用を完全に廃止し、より低レベルでプラットフォームに依存しないUTF-8処理関数であるutflenchartoruneを組み合わせて文字列変換を行っています。

  1. utflen(p): まず、入力文字列pのRune数をutflenで取得します。これにより、変換後のWinRune配列に必要なメモリサイズを正確に計算できます。
  2. malloc((n+1)*sizeof r[0]): 取得したRune数nに基づいて、WinRune配列に必要なメモリを動的に確保します。+1はヌル終端文字のためです。
  3. ループとchartorune: forループを使用して、入力文字列pを先頭から1Runeずつ処理します。
    • p += chartorune(&rr, p);: chartorune関数は、pが指すUTF-8バイト列から1つのRuneをrrにデコードし、読み取ったバイト数を返します。この返り値をpに加算することで、ポインタを次のRuneの開始位置に進めます。
    • r[i] = rr;: デコードされたRune rrWinRune配列rの現在のインデックスiに格納します。
  4. ヌル終端: ループの最後にr[n] = '\0';で配列をヌル終端します。これはC言語の文字列処理における一般的な慣習であり、文字列の終わりを示します。

この変更により、runesmprintが引き起こしていたWindows固有の問題が回避され、Go言語のクロスプラットフォーム互換性が向上しました。utflenchartoruneは、UTF-8の仕様に基づいて動作するため、プラットフォームに依存しない堅牢な文字列処理を実現します。

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

--- a/src/lib9/tempdir_windows.c
+++ b/src/lib9/tempdir_windows.c
@@ -30,18 +30,16 @@ WinRune*
 torune(char *p)
 {
 	int i, n;
-	Rune *r1;
+	Rune rr;
 	WinRune *r;
 	
-	r1 = runesmprint("%s", p);
-	n = 0;
-	while(r1[n] != '\0')
-		n++;
-	n++;
-	r = malloc(n*sizeof r[0]);
-	for(i=0; i<n; i++)
-		r[i] = r1[i];
-	free(r1);
+	n = utflen(p);
+	r = malloc((n+1)*sizeof r[0]);
+	for(i=0; i<n; i++) {
+		p += chartorune(&rr, p);
+		r[i] = rr;
+	}
+	r[n] = '\0';
 	return r;
 }

コアとなるコードの解説

このコードスニペットは、src/lib9/tempdir_windows.cファイル内のtorune関数の変更を示しています。この関数は、Cスタイルのchar*文字列(UTF-8エンコードされていると仮定される)を、Windows APIが期待する可能性のあるWinRune*(ワイド文字配列)に変換する役割を担っています。

変更前のコードの課題:

変更前のコードでは、runesmprintという関数を使用していました。この関数は、おそらくGo言語の内部的なRune文字列操作に関連するもので、C言語のsprintfのようにフォーマットされた文字列をRune配列として生成する機能を持っていたと考えられます。しかし、この関数がWindows環境でのビルドや実行時に問題を引き起こしていたため、削除されました。具体的には、runesmprintが返すRune*配列r1WinRune*配列rにコピーしていましたが、このrunesmprintの動作自体がWindowsのコンテキストで不安定だったり、利用できなかったりしたことが問題の根源です。

変更後のコードの改善点:

変更後のコードは、runesmprintへの依存を排除し、より基本的なUTF-8処理関数であるutflenchartoruneを直接使用することで、Windowsビルドの安定性を確保しています。

  1. Rune rr;: 一時的に単一のRuneを格納するための変数rrが導入されました。
  2. n = utflen(p);: 入力文字列pが含むRuneの総数をutflen関数で計算し、変数nに格納します。これにより、変換後のWinRune配列に必要な正確なサイズがわかります。
  3. r = malloc((n+1)*sizeof r[0]);: n個のRuneとヌル終端文字(\0)のために必要なメモリをWinRune型のサイズで確保します。mallocは動的にメモリを割り当てるC言語の標準関数です。
  4. for(i=0; i<n; i++) { ... }: n回繰り返すループが開始されます。このループは、入力文字列の各Runeを順番に処理します。
  5. p += chartorune(&rr, p);:
    • chartorune(&rr, p): この関数は、ポインタpが指すUTF-8バイト列の先頭から1つのRuneをデコードし、そのRuneをrrに格納します。また、デコードしたRuneが占めるバイト数を返します。
    • p += ...: chartoruneが返したバイト数をpに加算することで、ポインタpは次のRuneの開始位置に移動します。これにより、ループの次のイテレーションで次のRuneを処理できます。
  6. r[i] = rr;: デコードされたRune rrを、新しく確保されたWinRune配列rの現在のインデックスiに格納します。
  7. r[n] = '\0';: ループが終了した後、WinRune配列の最後にヌル終端文字を追加します。これは、C言語の文字列として正しく機能するために不可欠です。

この変更により、torune関数はrunesmprintという問題のある関数に依存することなく、UTF-8文字列をRuneのシーケンスとして正確に処理し、Windows環境で利用可能な形式に変換できるようになりました。これは、Go言語のクロスプラットフォーム互換性と堅牢性を高める上で重要な修正です。

関連リンク

参考にした情報源リンク

  • Go言語のドキュメント(rune型、UTF-8文字列の扱いなど)
  • Plan 9のドキュメント(lib9の背景理解のため)
  • C言語のmalloc、ポインタ操作に関する一般的な知識
  • Windows APIにおけるワイド文字(wchar_t)の概念
  • GitHubのコミットページ: https://github.com/golang/go/commit/5641a09171ce220b6eb28631dce6a1243dc78e12
  • Go言語のsrc/lib9ディレクトリ内の他のファイル(文脈理解のため)
  • Go言語のsrc/runtimeディレクトリ内の関連ファイル(runeやUTF-8処理の低レベル実装理解のため)
  • utflenchartorune関数の一般的な定義と使用法に関する情報(Go言語のソースコードや関連ドキュメントから)
  • Go言語のIssueトラッカーやメーリングリスト(過去のWindowsビルド問題に関する議論を検索)
  • Go言語のtempdirに関する議論や実装(一時ディレクトリの作成に関する文脈理解のため)
  • Go言語のgo.modgo.sumファイル(依存関係の確認のため、今回は直接使用せず)
  • golang-devメーリングリストのアーカイブ(TBR/CCの文脈理解のため)
  • Go言語のクロスコンパイルに関するドキュメント