[インデックス 15135] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおけるPlan 9オペレーティングシステム向けのstat
メッセージのマーシャリング(直列化)およびアンマーシャリング(非直列化)に関するバグ修正を扱っています。具体的には、Qid
構造体のフィールドの順序が誤っていたために発生していた問題を解決します。
コミット
commit c56bb1d238672f658c4f5f5e1efc0fa88c3b3101
Author: Anthony Martin <ality@pbrane.org>
Date: Mon Feb 4 19:47:23 2013 -0800
syscall: fix marshaling of stat messages on Plan 9
The order of the Qid fields was reversed. Mea culpa.
R=seed
CC=golang-dev
https://golang.org/cl/7231062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c56bb1d238672f658c4f5f5e1efc0fa88c3b3101
元コミット内容
syscall
: Plan 9におけるstat
メッセージのマーシャリングを修正。
Qid
フィールドの順序が逆になっていた。私の過失である。
変更の背景
このコミットは、Go言語のsyscall
パッケージがPlan 9オペレーティングシステムと連携する際に発生していた重要なバグを修正するために行われました。stat
メッセージは、ファイルやディレクトリのメタデータ(サイズ、パーミッション、タイムスタンプなど)を表現するために使用されるデータ構造です。Plan 9のファイルシステムプロトコルでは、これらのメッセージが特定のバイト順序でネットワーク経由で送受信されます。
問題は、stat
メッセージの一部であるQid
構造体のフィールドが、Goのsyscall
パッケージ内でバイト列に変換(マーシャリング)される際、およびバイト列からGoの構造体に戻される(アンマーシャリング)際に、そのフィールドの順序がPlan 9の期待する順序と逆になっていたことにありました。この誤った順序は、stat
メッセージのデータが正しく解釈されない原因となり、結果としてファイル操作やシステムコールが期待通りに機能しないという深刻な問題を引き起こしていました。コミットメッセージにある「Mea culpa.」は、この誤りが開発者自身のミスであることを認める表現です。
前提知識の解説
Plan 9 from Bell Labs
Plan 9は、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルとして表現し、ファイルシステムプロトコル(9P)を通じてアクセスするという特徴を持っています。Go言語は、その設計思想や開発者の背景(Rob Pike、Ken Thompsonなど、Plan 9の開発者と共通する)から、Plan 9との親和性が高いことで知られています。
Go言語の syscall
パッケージ
Go言語の標準ライブラリであるsyscall
パッケージは、オペレーティングシステムが提供する低レベルのシステムコールにアクセスするための機能を提供します。これにより、Goプログラムはファイルシステム操作、プロセス管理、ネットワーク通信など、OSカーネルが提供する基本的なサービスを直接利用できます。OSごとにシステムコールのインターフェースやデータ構造が異なるため、syscall
パッケージ内には各OSに特化した実装(例: syscall/dir_plan9.go
)が含まれています。
stat
メッセージと Qid
構造体
Plan 9のファイルシステムプロトコル(9P)において、stat
メッセージはファイルやディレクトリの属性(メタデータ)を記述するために使用されます。このメッセージには、ファイルの種類、パーミッション、サイズ、タイムスタンプなどの情報が含まれます。
Qid
("quad ID"の略)は、Plan 9においてファイルシステム内のオブジェクトを一意に識別するための重要な構造体です。これは、ファイルやディレクトリのパス、バージョン、タイプなどの情報を含みます。Qid
は通常、以下の3つのフィールドで構成されます。
Type
: ファイルの種類(ディレクトリ、通常ファイルなど)を示す1バイトのフィールド。Vers
: ファイルのバージョンを示す4バイト(uint32
)のフィールド。ファイルが変更されるたびにインクリメントされます。Path
: ファイルシステム内のオブジェクトを一意に識別する8バイト(uint64
)のフィールド。
これらのフィールドは、ネットワーク上で送受信される際に特定のバイト順序で直列化(マーシャリング)され、受信側で逆のプロセス(アンマーシャリング)によって元の構造体に復元されます。この順序が誤っていると、データが破損したり、誤ったファイルが参照されたりする原因となります。
マーシャリングとアンマーシャリング
- マーシャリング (Marshaling): プログラム内のデータ構造(例: Goの
struct
)を、ネットワーク経由で送信したり、ファイルに保存したりするために、バイト列や特定のデータ形式(JSON, XMLなど)に変換するプロセスです。この際、フィールドの順序、バイトオーダー(エンディアン)、パディングなどが厳密に定義されている必要があります。 - アンマーシャリング (Unmarshaling): マーシャリングされたバイト列やデータ形式を、プログラム内のデータ構造に復元するプロセスです。マーシャリングとアンマーシャリングは、データの整合性を保つために、同じ規則に従って行われる必要があります。
このコミットでは、syscall/dir_plan9.go
ファイル内のDir
構造体のMarshal
メソッドとUnmarshalDir
関数において、Qid
のPath
とVers
フィールドのマーシャリング/アンマーシャリング順序がPlan 9のプロトコル仕様と異なっていたことが問題でした。
技術的詳細
このバグは、src/pkg/syscall/dir_plan9.go
ファイル内のDir
構造体のMarshal
メソッドとUnmarshalDir
関数に存在していました。これらの関数は、Plan 9のstat
メッセージをGoのDir
構造体との間で変換する役割を担っています。
具体的には、Qid
構造体のフィールドであるPath
(uint64
)とVers
(uint32
)のマーシャリングおよびアンマーシャリングの順序が、Plan 9のプロトコル仕様と逆になっていました。
元のコードでは、Qid.Path
がQid.Vers
の前にマーシャリング/アンマーシャリングされていました。
// 元のMarshal (抜粋)
b = pbit64(b, d.Qid.Path) // Path (uint64)
b = pbit32(b, d.Qid.Vers) // Vers (uint32)
b = pbit8(b, d.Qid.Type) // Type (uint8)
// 元のUnmarshalDir (抜粋)
d.Qid.Path, b = gbit64(b) // Path (uint64)
d.Qid.Vers, b = gbit32(b) // Vers (uint32)
d.Qid.Type, b = gbit8(b) // Type (uint8)
しかし、Plan 9のプロトコルでは、Qid
のフィールドはType
、Vers
、Path
の順でバイト列に配置されるべきでした。
このコミットでは、Qid.Vers
とQid.Path
の順序を入れ替えることで、この問題を修正しています。Qid.Type
は元々正しい位置にありました。
修正後の順序は以下のようになります。
// 修正後のMarshal (抜粋)
b = pbit8(b, d.Qid.Type) // Type (uint8)
b = pbit32(b, d.Qid.Vers) // Vers (uint32)
b = pbit64(b, d.Qid.Path) // Path (uint64)
// 修正後のUnmarshalDir (抜粋)
d.Qid.Type, b = gbit8(b) // Type (uint8)
d.Qid.Vers, b = gbit32(b) // Vers (uint32)
d.Qid.Path, b = gbit64(b) // Path (uint64)
pbitX
関数(例: pbit16
, pbit32
, pbit64
, pbit8
)は、指定されたバイト数(16ビット、32ビット、64ビット、8ビット)の値をバイトスライスb
に書き込み、更新されたバイトスライスを返します。同様に、gbitX
関数はバイトスライスb
から指定されたバイト数の値を読み取り、読み取った値と残りのバイトスライスを返します。これらの関数は、Goのsyscall
パッケージ内で低レベルのバイト操作を行うためのユーティリティ関数です。
この修正により、GoプログラムがPlan 9システムとstat
メッセージを交換する際に、Qid
情報が正しく解釈されるようになり、ファイルシステム操作の信頼性が向上しました。
コアとなるコードの変更箇所
diff --git a/src/pkg/syscall/dir_plan9.go b/src/pkg/syscall/dir_plan9.go
index eee8be44a3..b7ab4cd108 100644
--- a/src/pkg/syscall/dir_plan9.go
+++ b/src/pkg/syscall/dir_plan9.go
@@ -68,9 +68,9 @@ func (d *Dir) Marshal(b []byte) (n int, err error) {
b = pbit16(b, uint16(n)-2)
b = pbit16(b, d.Type)
b = pbit32(b, d.Dev)
- b = pbit64(b, d.Qid.Path)
- b = pbit32(b, d.Qid.Vers)
b = pbit8(b, d.Qid.Type)
+ b = pbit32(b, d.Qid.Vers)
+ b = pbit64(b, d.Qid.Path)
b = pbit32(b, d.Mode)
b = pbit32(b, d.Atime)
b = pbit32(b, d.Mtime)
@@ -101,9 +101,9 @@ func UnmarshalDir(b []byte) (*Dir, error) {
var d Dir
d.Type, b = gbit16(b)
d.Dev, b = gbit32(b)
- d.Qid.Path, b = gbit64(b)
- d.Qid.Vers, b = gbit32(b)
d.Qid.Type, b = gbit8(b)
+ d.Qid.Vers, b = gbit32(b)
+ d.Qid.Path, b = gbit64(b)
d.Mode, b = gbit32(b)
d.Atime, b = gbit32(b)
d.Mtime, b = gbit32(b)
コアとなるコードの解説
上記の差分は、src/pkg/syscall/dir_plan9.go
ファイル内の2つの主要な関数、(*Dir) Marshal
とUnmarshalDir
における変更を示しています。
func (d *Dir) Marshal(b []byte) (n int, err error)
このメソッドは、Dir
構造体の内容をバイトスライスb
にマーシャリング(直列化)します。変更前は、d.Qid.Path
がd.Qid.Vers
の前にバイトスライスに書き込まれていました。
- b = pbit64(b, d.Qid.Path)
: 変更前、Qid.Path
(64ビット)が最初に書き込まれていました。- b = pbit32(b, d.Qid.Vers)
: 変更前、次にQid.Vers
(32ビット)が書き込まれていました。b = pbit8(b, d.Qid.Type)
:Qid.Type
(8ビット)は変更前も後も同じ位置にあり、これはPlan 9のプロトコル仕様と一致しています。+ b = pbit32(b, d.Qid.Vers)
: 変更後、Qid.Type
の直後にQid.Vers
が書き込まれるようになりました。+ b = pbit64(b, d.Qid.Path)
: 変更後、その次にQid.Path
が書き込まれるようになりました。
この変更により、Qid
のフィールドがType
、Vers
、Path
の正しい順序でバイト列に直列化されるようになりました。
func UnmarshalDir(b []byte) (*Dir, error)
この関数は、バイトスライスb
からDir
構造体の内容をアンマーシャリング(非直列化)します。マーシャリングの逆の操作であり、同様にQid.Path
とQid.Vers
の読み取り順序が修正されています。
d.Type, b = gbit16(b)
:Dir
のType
フィールド(16ビット)の読み取り。これはQid.Type
とは別のフィールドです。d.Dev, b = gbit32(b)
:Dir
のDev
フィールド(32ビット)の読み取り。- d.Qid.Path, b = gbit64(b)
: 変更前、Qid.Path
が最初に読み取られていました。- d.Qid.Vers, b = gbit32(b)
: 変更前、次にQid.Vers
が読み取られていました。d.Qid.Type, b = gbit8(b)
:Qid.Type
は変更前も後も同じ位置にあり、これはPlan 9のプロトコル仕様と一致しています。+ d.Qid.Vers, b = gbit32(b)
: 変更後、Qid.Type
の直後にQid.Vers
が読み取られるようになりました。+ d.Qid.Path, b = gbit64(b)
: 変更後、その次にQid.Path
が読み取られるようになりました。
これらの変更により、Plan 9のstat
メッセージからQid
情報が正しく抽出され、GoのDir
構造体にマッピングされることが保証されます。この修正は、GoとPlan 9間の相互運用性において、ファイルシステム操作の正確性を確保するために不可欠でした。
関連リンク
- Go CL 7231062: https://golang.org/cl/7231062
参考にした情報源リンク
- Plan 9 from Bell Labs: https://9p.io/plan9/
- Go言語
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall - Plan 9 File Protocol (9P): https://9p.io/magic/man2html/5/9p
- Qid structure in Plan 9: https://9p.io/magic/man2html/5/stat (stat(5) man page)
- Go言語の
pbit
およびgbit
関数の一般的な概念(具体的な実装はGoのソースコードを参照)# [インデックス 15135] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおけるPlan 9オペレーティングシステム向けのstat
メッセージのマーシャリング(直列化)およびアンマーシャリング(非直列化)に関するバグ修正を扱っています。具体的には、Qid
構造体のフィールドの順序が誤っていたために発生していた問題を解決します。
コミット
commit c56bb1d238672f658c4f5f5e1efc0fa88c3b3101
Author: Anthony Martin <ality@pbrane.org>
Date: Mon Feb 4 19:47:23 2013 -0800
syscall: fix marshaling of stat messages on Plan 9
The order of the Qid fields was reversed. Mea culpa.
R=seed
CC=golang-dev
https://golang.org/cl/7231062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c56bb1d238672f658c4f5f5e1efc0fa88c3b3101
元コミット内容
syscall
: Plan 9におけるstat
メッセージのマーシャリングを修正。
Qid
フィールドの順序が逆になっていた。私の過失である。
変更の背景
このコミットは、Go言語のsyscall
パッケージがPlan 9オペレーティングシステムと連携する際に発生していた重要なバグを修正するために行われました。stat
メッセージは、ファイルやディレクトリのメタデータ(サイズ、パーミッション、タイムスタンプなど)を表現するために使用されるデータ構造です。Plan 9のファイルシステムプロトコルでは、これらのメッセージが特定のバイト順序でネットワーク経由で送受信されます。
問題は、stat
メッセージの一部であるQid
構造体のフィールドが、Goのsyscall
パッケージ内でバイト列に変換(マーシャリング)される際、およびバイト列からGoの構造体に戻される(アンマーシャリング)際に、そのフィールドの順序がPlan 9の期待する順序と逆になっていたことにありました。この誤った順序は、stat
メッセージのデータが正しく解釈されない原因となり、結果としてファイル操作やシステムコールが期待通りに機能しないという深刻な問題を引き起こしていました。コミットメッセージにある「Mea culpa.」は、この誤りが開発者自身のミスであることを認める表現です。
前提知識の解説
Plan 9 from Bell Labs
Plan 9は、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルとして表現し、ファイルシステムプロトコル(9P)を通じてアクセスするという特徴を持っています。Go言語は、その設計思想や開発者の背景(Rob Pike、Ken Thompsonなど、Plan 9の開発者と共通する)から、Plan 9との親和性が高いことで知られています。
Go言語の syscall
パッケージ
Go言語の標準ライブラリであるsyscall
パッケージは、オペレーティングシステムが提供する低レベルのシステムコールにアクセスするための機能を提供します。これにより、Goプログラムはファイルシステム操作、プロセス管理、ネットワーク通信など、OSカーネルが提供する基本的なサービスを直接利用できます。OSごとにシステムコールのインターフェースやデータ構造が異なるため、syscall
パッケージ内には各OSに特化した実装(例: syscall/dir_plan9.go
)が含まれています。
stat
メッセージと Qid
構造体
Plan 9のファイルシステムプロトコル(9P)において、stat
メッセージはファイルやディレクトリの属性(メタデータ)を記述するために使用されます。このメッセージには、ファイルの種類、パーミッション、サイズ、タイムスタンプなどの情報が含まれます。
Qid
("quad ID"の略)は、Plan 9においてファイルシステム内のオブジェクトを一意に識別するための重要な構造体です。これは、ファイルやディレクトリのパス、バージョン、タイプなどの情報を含みます。Qid
は通常、以下の3つのフィールドで構成されます。
Type
: ファイルの種類(ディレクトリ、通常ファイルなど)を示す1バイトのフィールド。- **``Vers
**: ファイルのバージョンを示す4バイト(
uint32`)のフィールド。ファイルが変更されるたびにインクリメントされます。 Path
: ファイルシステム内のオブジェクトを一意に識別する8バイト(uint64
)のフィールド。
これらのフィールドは、ネットワーク上で送受信される際に特定のバイト順序で直列化(マーシャリング)され、受信側で逆のプロセス(アンマーシャリング)によって元の構造体に復元されます。この順序が誤っていると、データが破損したり、誤ったファイルが参照されたりする原因となります。
マーシャリングとアンマーシャリング
- マーシャリング (Marshaling): プログラム内のデータ構造(例: Goの
struct
)を、ネットワーク経由で送信したり、ファイルに保存したりするために、バイト列や特定のデータ形式(JSON, XMLなど)に変換するプロセスです。この際、フィールドの順序、バイトオーダー(エンディアン)、パディングなどが厳密に定義されている必要があります。 - アンマーシャリング (Unmarshaling): マーシャリングされたバイト列やデータ形式を、プログラム内のデータ構造に復元するプロセスです。マーシャリングとアンマーシャリングは、データの整合性を保つために、同じ規則に従って行われる必要があります。
このコミットでは、syscall/dir_plan9.go
ファイル内のDir
構造体のMarshal
メソッドとUnmarshalDir
関数において、Qid
のPath
とVers
フィールドのマーシャリング/アンマーシャリング順序がPlan 9のプロトコル仕様と異なっていたことが問題でした。
技術的詳細
このバグは、src/pkg/syscall/dir_plan9.go
ファイル内のDir
構造体のMarshal
メソッドとUnmarshalDir
関数に存在していました。これらの関数は、Plan 9のstat
メッセージをGoのDir
構造体との間で変換する役割を担っています。
具体的には、Qid
構造体のフィールドであるPath
(uint64
)とVers
(uint32
)のマーシャリングおよびアンマーシャリングの順序が、Plan 9のプロトコル仕様と逆になっていました。
元のコードでは、Qid.Path
がQid.Vers
の前にマーシャリング/アンマーシャリングされていました。
// 元のMarshal (抜粋)
b = pbit64(b, d.Qid.Path) // Path (uint64)
b = pbit32(b, d.Qid.Vers) // Vers (uint32)
b = pbit8(b, d.Qid.Type) // Type (uint8)
// 元のUnmarshalDir (抜粋)
d.Qid.Path, b = gbit64(b) // Path (uint64)
d.Qid.Vers, b = gbit32(b) // Vers (uint32)
d.Qid.Type, b = gbit8(b) // Type (uint8)
しかし、Plan 9のプロトコルでは、Qid
のフィールドはType
、Vers
、Path
の順でバイト列に配置されるべきでした。
このコミットでは、Qid.Vers
とQid.Path
の順序を入れ替えることで、この問題を修正しています。Qid.Type
は元々正しい位置にありました。
修正後の順序は以下のようになります。
// 修正後のMarshal (抜粋)
b = pbit8(b, d.Qid.Type) // Type (uint8)
b = pbit32(b, d.Qid.Vers) // Vers (uint32)
b = pbit64(b, d.Qid.Path) // Path (uint64)
// 修正後のUnmarshalDir (抜粋)
d.Qid.Type, b = gbit8(b) // Type (uint8)
d.Qid.Vers, b = gbit32(b) // Vers (uint32)
d.Qid.Path, b = gbit64(b) // Path (uint64)
pbitX
関数(例: pbit16
, pbit32
, pbit64
, pbit8
)は、指定されたバイト数(16ビット、32ビット、64ビット、8ビット)の値をバイトスライスb
に書き込み、更新されたバイトスライスを返します。同様に、gbitX
関数はバイトスライスb
から指定されたバイト数の値を読み取り、読み取った値と残りのバイトスライスを返します。これらの関数は、Goのsyscall
パッケージ内で低レベルのバイト操作を行うためのユーティリティ関数です。
この修正により、GoプログラムがPlan 9システムとstat
メッセージを交換する際に、Qid
情報が正しく解釈されるようになり、ファイルシステム操作の信頼性が向上しました。
コアとなるコードの変更箇所
diff --git a/src/pkg/syscall/dir_plan9.go b/src/pkg/syscall/dir_plan9.go
index eee8be44a3..b7ab4cd108 100644
--- a/src/pkg/syscall/dir_plan9.go
+++ b/src/pkg/syscall/dir_plan9.go
@@ -68,9 +68,9 @@ func (d *Dir) Marshal(b []byte) (n int, err error) {
b = pbit16(b, uint16(n)-2)
b = pbit16(b, d.Type)
b = pbit32(b, d.Dev)
- b = pbit64(b, d.Qid.Path)
- b = pbit32(b, d.Qid.Vers)
b = pbit8(b, d.Qid.Type)
+ b = pbit32(b, d.Qid.Vers)
+ b = pbit64(b, d.Qid.Path)
b = pbit32(b, d.Mode)
b = pbit32(b, d.Atime)
b = pbit32(b, d.Mtime)
@@ -101,9 +101,9 @@ func UnmarshalDir(b []byte) (*Dir, error) {
var d Dir
d.Type, b = gbit16(b)
d.Dev, b = gbit32(b)
- d.Qid.Path, b = gbit64(b)
- d.Qid.Vers, b = gbit32(b)
d.Qid.Type, b = gbit8(b)
+ d.Qid.Vers, b = gbit32(b)
+ d.Qid.Path, b = gbit64(b)
d.Mode, b = gbit32(b)
d.Atime, b = gbit32(b)
d.Mtime, b = gbit32(b)
コアとなるコードの解説
上記の差分は、src/pkg/syscall/dir_plan9.go
ファイル内の2つの主要な関数、(*Dir) Marshal
とUnmarshalDir
における変更を示しています。
func (d *Dir) Marshal(b []byte) (n int, err error)
このメソッドは、Dir
構造体の内容をバイトスライスb
にマーシャリング(直列化)します。変更前は、d.Qid.Path
がd.Qid.Vers
の前にバイトスライスに書き込まれていました。
- b = pbit64(b, d.Qid.Path)
: 変更前、Qid.Path
(64ビット)が最初に書き込まれていました。- b = pbit32(b, d.Qid.Vers)
: 変更前、次にQid.Vers
(32ビット)が書き込まれていました。b = pbit8(b, d.Qid.Type)
:Qid.Type
(8ビット)は変更前も後も同じ位置にあり、これはPlan 9のプロトコル仕様と一致しています。+ b = pbit32(b, d.Qid.Vers)
: 変更後、Qid.Type
の直後にQid.Vers
が書き込まれるようになりました。+ b = pbit64(b, d.Qid.Path)
: 変更後、その次にQid.Path
が書き込まれるようになりました。
この変更により、Qid
のフィールドがType
、Vers
、Path
の正しい順序でバイト列に直列化されるようになりました。
func UnmarshalDir(b [][]byte) (*Dir, error)
この関数は、バイトスライスb
からDir
構造体の内容をアンマーシャリング(非直列化)します。マーシャリングの逆の操作であり、同様にQid.Path
とQid.Vers
の読み取り順序が修正されています。
d.Type, b = gbit16(b)
:Dir
のType
フィールド(16ビット)の読み取り。これはQid.Type
とは別のフィールドです。d.Dev, b = gbit32(b)
:Dir
のDev
フィールド(32ビット)の読み取り。- d.Qid.Path, b = gbit64(b)
: 変更前、Qid.Path
が最初に読み取られていました。- d.Qid.Vers, b = gbit32(b)
: 変更前、次にQid.Vers
が読み取られていました。d.Qid.Type, b = gbit8(b)
:Qid.Type
は変更前も後も同じ位置にあり、これはPlan 9のプロトコル仕様と一致しています。+ d.Qid.Vers, b = gbit32(b)
: 変更後、Qid.Type
の直後にQid.Vers
が読み取られるようになりました。+ d.Qid.Path, b = gbit64(b)
: 変更後、その次にQid.Path
が読み取られるようになりました。
これらの変更により、Plan 9のstat
メッセージからQid
情報が正しく抽出され、GoのDir
構造体にマッピングされることが保証されます。この修正は、GoとPlan 9間の相互運用性において、ファイルシステム操作の正確性を確保するために不可欠でした。
関連リンク
- Go CL 7231062: https://golang.org/cl/7231062
参考にした情報源リンク
- Plan 9 from Bell Labs: https://9p.io/plan9/
- Go言語
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall - Plan 9 File Protocol (9P): https://9p.io/magic/man2html/5/9p
- Qid structure in Plan 9: https://9p.io/magic/man2html/5/stat (stat(5) man page)
- Go言語の
pbit
およびgbit
関数の一般的な概念(具体的な実装はGoのソースコードを参照)