[Gauche-devel-jp] Re: soft-port

Back to archive index

HIRAUCHI Hideyuki hira****@verys*****
2004年 2月 24日 (火) 23:22:59 JST


> 今ならまだ仕様が固まっていないので、希望を出しておけば好みの方向にねじ
> まげることも可能かもしれません。ということで、出しましょう。:)

この手の話はガーフィってことで、どうでしょう:-)
GRFIの番号は適当です。勝手に1番を名乗って良い物かどうか、分からなかったので。
細部まで煮詰めていませんが、こんな風にねじまげることが出来ればいいなーと
思ってます。
-----

* GRFI 1192   パイプとフィルタによるポートの拡張

** 動機

http://lists.sourceforge.jp/mailman/archives/gauche-devel-jp/2004-February/000650.html

** 目的

portを拡張、連結するための仕組みを提供する。

** 方法

portの作成時にFilter列を与えることによってportを拡張する。
portのread/write時に、read/writeされたobjがFilter列を通って、read/writeの値となる。
read-charを呼ぶとcharが先頭フィルタの引数となり、read-byteを呼ぶとbyteが先頭フィルタの引数となる。
writeの場合も同様。

上流------------------->下流
    src->[F1->F2->F3]->usr
    usr->[F4->F5->F6]->dst

    ※F1,F4を先頭フィルタと呼び、F3,F6を末尾フィルタと呼ぶ。

** Filterの説明

Filterは2つのプロシージャから構成されている。

    1. フィルタリングプロシージャ
    2. フロー制御要求ハンドラ

フィルタリングプロシージャはobjを引数として受け取る。
フィルタリングプロシージャの戻り値がobjなら下流のフィルタへ流れ、
フロー制御要求なら上流のフィルタへ投げられる。

フロー制御要求には次のものがある

    MORE:上流に次の入出力を要求する。
    SWITCH:フィルタリングプロシージャが自分のフロー制御要求ハンドラをMOREで呼ぶ。
    FLUSH:上流にフラッシュするよう要求する。    ;;要らないかも

MOREによって自前バッファリングや、型変換なとが可能になる
int8のストリームをint32にするためにはMOREを3回要求すればよい。
フィルタリングプロシージャが投げたフロー制御要求は上流のフィルタへと遡っていく。
フロー制御要求ハンドラは、下流のフィルタが投げたフロー制御要求をキャッチすることができる。
そのときobjを返せばそれは下流に流れ、フロー制御要求を返せば、また上流へと遡っていく。
フロー制御要求ハンドラにより、MOREの逆が出来るようになる。
int32のストリームをint8にしたいなら、MOREを3回キャッチし、4回目のMOREはスルーすればよい。
末尾フィルタの制御要求ハンドラが呼ばれることは無い。

Filterは2つのプロシージャのうちどちらか一つ、または両方を実装すること。
制御要求ハンドラのみを提供するフィルタを間に挟むこともできる。
portの終端はEOFで判断する。FilterはEOFを受け取ったらMOREを返してはならない。
ため込んでいるデータがあるなら、この機にフラッシュすること。


** 手続き的ポート(null-portに対するフィルタリング)

null-portにFilter列を渡す場合、srcの次にあるフィルタのフィルタリングプロシージャは
常に空リストを受け取ることになる。
それ以外は、既存のポートに適用した場合と同じである。

;; 解説

Filterをループの制御から解放するのが、この設計のキモです。
base64-decodeなら、objを3つ隠し持ったプロシージャとして定義すれば良いと言うこと。
4つ読んだら3つ流すという風に定義する訳です。
なので、Filterかread/writeすることはありません。
制御はusrかpipeに任せます↓。

;usr任せ

(with-input-from-file "tmp1.attach.gz"
                       :filters (gunzip-filter base64-decode-filter)
  (with-output-to-file "tmp1.bz2
                       :filters (bz2-filter)
    (let loop (b (read-byte))
      (if (eof-objct? b)
          #t
          (begin (write-byte b)
                 (loop (read-byte)))))))

;or pipe任せ1

(with-input-from-file "tmp1.attach.gz"
                       :filters (gunzip-filter base64-decode-filter)
  (with-output-to-file "tmp1.bz2
                       :filters (bz2-filter)
    (execute-pipe-line read-byte  (current-input-port)
                       write-byte (current-output-port))))

;or pipe任せ2

(execute-pipe-line
  (pipe-line
    (file-source "tmp1.attach.gz")
    (gunzip-filter)
    (base64-decode-filter)
    (bz2-filter)
    (file-sink "tmp1.bz2")))

;さらに pipe任せ3teeとかjunction-pointとかも設定できちゃう。

(execute-pipe-line
  (pipe-line
    (file-source "tmp1.attach.gz")
    (gunzip-filter)
    (base64-decode-filter)
    (tee (file-sink "tmp1.attach"))
    (bz2-filter)
    (tee (other-pipe-line))
    (file-sink "tmp1.bz2")))

source/sinkはpull/pushを実装しています。
最後の2例ではexecute-pipe-lineはそれを使用して読み書きしています。

#これをScheme(or C)で、どう実装すればいいかは、正直分かっておりません。
#JavaでEDIシステムを作ったときに、この発想でフレームワークを組んだことはあるんですが。
#あと、input-filter,output-filterの区別はありませんでした。
#source/sink(dest)/filter/pipeの4つでした。source/sinkをportとすれば3つか。

** 参考

POSA本(邦訳の名前を忘れちゃいまいた。これのPipesAndFiltersパターンの受動フィルタ、だったような)
http://www.amazon.co.jp/exec/obidos/ASIN/0471606952/qid%3D1008600740/sr%3D1-3/ref%3Dsr%5F1%5F0%5F3/249-0743650-8440348

--hira




Gauche-devel-jp メーリングリストの案内
Back to archive index