commit 87310b0f73d58b567d19e4d62159a78176649871
parent 575bc6cd845f4aa234e6d23afedd8820b228576e
Author: Matsuda Kenji <info@mtkn.jp>
Date: Thu, 14 Nov 2024 20:57:57 +0900
update
Diffstat:
3 files changed, 180 insertions(+), 3 deletions(-)
diff --git a/man/draft/9p.html b/man/draft/9p.html
@@ -527,6 +527,69 @@ type Msg interface {
メッセージの構造体をバイト列にエンコードするための関数である。\
<code>String() string</code>はログ出力用。\
</p>
+<p>
+メッセージはバイト列として受け取った後、\
+メッセージのタイプによって処理を分ける。\
+その際、扱いやすいようにバイト列を各メッセージの構造体に\
+変換する。\
+変換前のバイト列から、各メッセージに共通のフィールドを\
+取得できると便利なので、\
+バイト列のまま<code>Msg</code>インターフェースを実装する\
+<code>bufMsg</code>を定義した。\
+名前はいまいち。\
+</p>
+<pre><code>\
+// A bufMsg is Msg with just an array of bytes.
+// TODO: rename.
+type bufMsg []byte
+
+func (msg bufMsg) Size() uint32 { return gbit32(msg[0:4]) }
+func (msg bufMsg) Type() MsgType { return MsgType(msg[4]) }
+func (msg bufMsg) GetTag() uint16 { return gbit16(msg[5:7]) }
+func (msg bufMsg) SetTag(t uint16) { pbit16(msg[5:7], t) }
+func (msg bufMsg) marshal() []byte { return []byte(msg)[:msg.Size()] }
+func (msg bufMsg) String() string {
+ switch msg.Type() {
+ case Tversion:
+ return newTVersion(msg).String()
+ /* 省略 */
+ }
+}
+</code></pre>
+<p>
+<code>gbit32</code>は4バイトのリトルエンディアンの配列を\
+整数に変換するもので、\
+<code>pbit32</code>は逆に整数をリトルエンディアンのスライスに\
+格納するものである。\
+</p>
+<p>
+各メッセージはそれぞれのフィールドを構造体のフィールドとして\
+定義したものである。\
+例えば<code>tversion</code>に対応する構造体は以下の通り。\
+</p>
+<pre><code>\
+type TVersion struct {
+ Tag uint16
+ Msize uint32
+ Version string
+}
+</code></pre>
+<p>
+バイト列から各メッセージの構造体に変換するために\
+<code>new<i>メッセージタイプ</i></code>\
+を定義した。
+</p>
+<pre><code>\
+func newTVersion(buf []byte) *TVersion {
+ msg := new(TVersion)
+ msg.Tag = gbit16(buf[5:7])
+ msg.Msize = gbit32(buf[7:11])
+ vs := gbit16(buf[11:13])
+ msg.Version = string(buf[13 : 13+vs])
+ return msg
+}
+</code></pre>
+
<h3>メッセージのやりとり</h3>
<p>
まずは9Pメッセージを読む関数を実装する。\
@@ -568,3 +631,48 @@ func readMsg(r io.Reader) ([]byte, error) {
return buf, nil
}
</code></pre>
+<p>
+読み込んだバイト列は<code>ReadMsg</code>関数で\
+メッセージの構造体に変換する。\
+</p>
+<pre><code>\
+RecvMsg(r io.Reader) (Msg, error) {
+ b, err := readMsg(r)
+ if err == io.EOF {
+ return nil, err
+ } else if err != nil {
+ return nil, fmt.Errorf("readMsg: %v", err)
+ }
+ return unmarshal(b)
+}
+</code></pre>
+<p>
+返信のメッセージはメッセージの構造体を<code>marshal</code>関数\
+でバイト列に変換して<code>io.Writer</code>に書き込む。\
+</p>
+<pre><code>\
+// SendMsg send a 9P message to w
+func SendMsg(msg Msg, w io.Writer) error {
+ if _, err := w.Write(msg.marshal()); err != nil {
+ return fmt.Errorf("write: %v", err)
+ }
+ return nil
+}
+</code></pre>
+
+<h3>メインループ</h3>
+<p>
+ライブラリはメッセージを読み込むためのリスナーgoroutinと\
+返信を書き込むためのレスポンダーgoroutine、\
+そして各メッセージを処理するためのgoroutineを立ち上げて、\
+チャネルでそれぞれを繋ぐ。\
+リスナーがメッセージを読み込むと、メッセージのタイプに応じて\
+各goroutineにメッセージの構造体を渡し、\
+渡されたgoroutineはメッセージに応じた処理をし、\
+返信のメッセージをリスポンダーgoroutineに渡す。\
+リスポンダーgoroutineはメッセージをバイト列に変換して返信する。
+</p>
+<p>
+この設計がいいかどうかよく知らんけど、Go言語を触るなら\
+goroutineとチャネルによるパイプラインを作ってみたいやん。\
+</p>
diff --git a/pub/draft/9p.html b/pub/draft/9p.html
@@ -339,7 +339,49 @@ type Msg interface {
}
</code></pre>
<p>
-<code>marshal()</code>はサーバーから返信を送る際にメッセージの構造体をバイト列にエンコードするための関数である。</p>
+<code>marshal()</code>はサーバーから返信を送る際にメッセージの構造体をバイト列にエンコードするための関数である。<code>String() string</code>はログ出力用。</p>
+<p>
+メッセージはバイト列として受け取った後、メッセージのタイプによって処理を分ける。その際、扱いやすいようにバイト列を各メッセージの構造体に変換する。変換前のバイト列から、各メッセージに共通のフィールドを取得できると便利なので、バイト列のまま<code>Msg</code>インターフェースを実装する<code>bufMsg</code>を定義した。名前はいまいち。</p>
+<pre><code>// A bufMsg is Msg with just an array of bytes.
+// TODO: rename.
+type bufMsg []byte
+
+func (msg bufMsg) Size() uint32 { return gbit32(msg[0:4]) }
+func (msg bufMsg) Type() MsgType { return MsgType(msg[4]) }
+func (msg bufMsg) GetTag() uint16 { return gbit16(msg[5:7]) }
+func (msg bufMsg) SetTag(t uint16) { pbit16(msg[5:7], t) }
+func (msg bufMsg) marshal() []byte { return []byte(msg)[:msg.Size()] }
+func (msg bufMsg) String() string {
+ switch msg.Type() {
+ case Tversion:
+ return newTVersion(msg).String()
+ /* 省略 */
+ }
+}
+</code></pre>
+<p>
+<code>gbit32</code>は4バイトのリトルエンディアンの配列を整数に変換するもので、<code>pbit32</code>は逆に整数をリトルエンディアンのスライスに格納するものである。</p>
+<p>
+各メッセージはそれぞれのフィールドを構造体のフィールドとして定義したものである。例えば<code>tversion</code>に対応する構造体は以下の通り。</p>
+<pre><code>type TVersion struct {
+ Tag uint16
+ Msize uint32
+ Version string
+}
+</code></pre>
+<p>
+バイト列から各メッセージの構造体に変換するために<code>new<i>メッセージタイプ</i></code>を定義した。
+</p>
+<pre><code>func newTVersion(buf []byte) *TVersion {
+ msg := new(TVersion)
+ msg.Tag = gbit16(buf[5:7])
+ msg.Msize = gbit32(buf[7:11])
+ vs := gbit16(buf[11:13])
+ msg.Version = string(buf[13 : 13+vs])
+ return msg
+}
+</code></pre>
+
<h3>メッセージのやりとり</h3>
<p>
まずは9Pメッセージを読む関数を実装する。バイト列が読めればいいので引数は<code>io.Reader</code>にした:
@@ -379,6 +421,33 @@ type Msg interface {
return buf, nil
}
</code></pre>
+<p>
+読み込んだバイト列は<code>ReadMsg</code>関数でメッセージの構造体に変換する。</p>
+<pre><code>RecvMsg(r io.Reader) (Msg, error) {
+ b, err := readMsg(r)
+ if err == io.EOF {
+ return nil, err
+ } else if err != nil {
+ return nil, fmt.Errorf("readMsg: %v", err)
+ }
+ return unmarshal(b)
+}
+</code></pre>
+<p>
+返信のメッセージはメッセージの構造体を<code>marshal</code>関数でバイト列に変換して<code>io.Writer</code>に書き込む。</p>
+<pre><code>// SendMsg send a 9P message to w
+func SendMsg(msg Msg, w io.Writer) error {
+ if _, err := w.Write(msg.marshal()); err != nil {
+ return fmt.Errorf("write: %v", err)
+ }
+ return nil
+}
+</code></pre>
+
+<h3>メインループ</h3>
+<p>
+ライブラリはメッセージを読み込むためのリスナーgoroutinと返信を書き込むためのレスポンダーgoroutine、そして各メッセージを処理するためのgoroutineを立ち上げる。リスナーがメッセージを読み込むと、メッセージのタイプに応じて各goroutineにメッセージの構造体を渡し、渡されたgoroutineはメッセージに応じた処理をし、返信のメッセージをリスポンダーgoroutineに渡す。リスポンダーgoroutineはメッセージをバイト列に変換して返信する。
+</p>
</article>
</main>
diff --git a/pub/rss.xml b/pub/rss.xml
@@ -5,8 +5,8 @@
<description>ウェブページの更新履歴</description>
<language>ja-jp</language>
<link>https://www.mtkn.jp</link>
-<lastBuildDate>Sun, 10 Nov 2024 20:02:27 +0900</lastBuildDate>
-<pubDate>Sun, 10 Nov 2024 20:02:27 +0900</pubDate>
+<lastBuildDate>Thu, 14 Nov 2024 20:48:32 +0900</lastBuildDate>
+<pubDate>Thu, 14 Nov 2024 20:48:32 +0900</pubDate>
<docs>https://www.rssboard.org/rss-specification</docs>
<item>
<title>麻婆豆腐</title>