Previous: gauche.version
- バージョン番号の比較, Up: ライブラリモジュール - Gauche拡張モジュール [Contents][Index]
gauche.vport
- 仮想ポート仮想ポートあるいは手続き的ポートとは、その振舞いを Scheme でプログラム可能なポートです。
このモジュールは 2 種類の仮想ポートを提供します。ひとつは、 完全仮想ポートで、すべての I/O 操作でユーザが提供する手続きが 呼出されるものです。もうひとつは、仮想バッファポートで、 I/O 操作は内部バッファ上で行われ、ユーザが提供する手続きは バッファを一杯にするかフラッシュする必要がある場合にのみ 呼出されます。
このモジュールはさらに、ユニフォームベクタにより バックアップされる仮想バッファポートも提供します。 これは仮想ポートの使用例でもあります。
このタイプの仮想ポートは、<virtual-input-port>
クラス
および <virtual-output-port>
クラスで実現されています。
適切なスロットに手続きをセットすることでポートの振舞いをカスタマイズ
可能です。
{gauche.vport} このクラスのインスタンスは入力ポートとして使えます。 このポートの振舞いはインスタンスのスロットに設定された値に依存します。
まともな入力ポートとして動かすためには、少くとも、getb
スロット
あるいは getc
スロットのどちらか一方は設定しなければなりません。
さもなければ、このポートはすべての入力要求に対して EOF を返します。
設定されているのなら、その値は、引数を取らない手続きでなければなりません。 バイナリ入力の要求のたびに、この手続きが呼ばれます。
この手続きは、0 から 255 までの正確な整数を返すか、#f
あるいは
EOF オブジェクトを返さなければなりません。整数を返す場合には、
それがこのポートから読みとられる値となります。それ以外の値を返す
場合は、このポートは EOF を返します。
このポートが文字入力を要求され、かつ、getc
手続きを持たない場合、
このポートはこの手続きを呼び(複数回の可能性もある)文字全体を構築します。
設定されているのなら、その値は、引数を取らない手続きでなければなりません。 文字入力の要求のたびに、この手続きが呼ばれます。
この手続きは文字を返すか、または#f
あるいはEOFオブジェクトを返さなければ
なりません。文字を返した場合には、それがこのポートから読みとられる値と
なります。それ以外の値を返す場合は、このポートは EOF を返します。
このポートがバイナリ入力を要求され、かつ、getb
手続きを持たない場合、
このポートはこの手続きを呼び、文字をバイト列に変換し、それをこのポートから
読みだされる値として使います。
設定されているなら、その値は、正の正確な整数の引数をひとつだけとる手続き
でなければなりません。ブロックバイナリ入力、たとえば、
read-uvector
などが要求された場合に、呼出されます。
そしてそれは文字列、 不完全文字列、 #f
、 EOF オブジェクトを
返さなくてはなりません. 文字列を返す場合は上記正の正確な整数によって
指定された大きさを越えてはなりません。空文字列、 #f
、EOF を
返した場合はポートの終端にたどり着いたものとみなされます。
(注意: 文字列大きさは文字数でなく文字列が占めるバイト数で指定されます。)
この手続きは効率のためにあります。もし、この手続きが用意されて
いなければ、このポートは、データブロックを準備するのに getb
を
繰り返し呼びます。場合によっては、ブロック入力を用意するほうが
はるかに効率的です。(たとえば、メモリチャンクのブロックからの読みだし
を行なうような場合です。)
こうした利点を必要としないのなら、このスロットは未設定のままにして おいてもかまいません。
設定されているなら、その値は、真偽値をひとつだけとる手続きでなければ
なりません。この手続きは、このポートに対して、char-ready?
あるいは byte-ready?
が呼ばれたときに呼出されます。設定した手続き
が返す値が、これらの手続きの結果になります。
char-ready?
が呼ばれたのなら、真偽値引数は #t
です。
byte-ready?
が呼ばれたのなら、真偽値引数は #f
になります。
未設定なら、char-ready?
および byte-ready?
は
このポートに対して常に、#t
を返します。
設定されているのなら、その値は引数を取らない手続きでなければなりません。 その手続きはこのポートがクローズされるときに呼びだされ、返り値は 捨てられます。このポートをクローズするときになんらかのアクションが 必要でないのなら、未設定にしておけます。
この手続きはファイナライザから呼ばれることもあり得ます。したがって、 この手続きは慎重に書く必要があります。後述のファイナライズに 関する注意を参照してください。
設定されているのなら、その値は、オフセット引数とそれがどこからかを示す引数
の 2つの引数をとる手続きでなければなりません。これらの引数の意味は、
port-seek
(ポート共通の操作 参照) のそれと同じです。
この手続きは、次の読み出しが新しい位置から始められるように
内部のリードポインタを調整しなければなりません。そして
更新されたポインタ(ポート先頭からのバイトオフセット)
を返さなければなりません。
未設定なら、このポートに対する port-seek
および port-tell
の呼び出しは #f
になります。
この手続きは単に現在の位置を問合せるために、
offset
として 0 を、whence
として SEEK_CUR
を与えて
呼び出すこともあることに注意してください。リードポインタの位置は知って
いるが、動かすことができないという場合にも、この手続きを提供することが
できます。上のような問合せには、現在位置を返し、そうでない場合には、
#f
を返します。
{gauche.vport} このクラスのインスタンスは出力ポートとして使えます。 このポートの振舞いはインスタンスのスロットに設定された値に依存します。
まともな出力ポートとして動かすためには、少くとも、putb
スロット
あるいは putc
スロットのどちらか一方は設定しなければなりません。
設定されているなら、その値は、バイト値(0 から 255までの正確な整数)を ひとつだけ引数としてとる手続きでなければなりません。バイナリ出力 の要求のたびに、この手続きが呼ばれます。この手続きの返り値は無視 されます。
このスロットが未設定の場合、バイナリ出力を要求されると、このポートは
<io-unit-error>
エラーを発生させることがあります。
設定されているなら、それは文字をひとつ引数にとる手続きでなければなりません。 文字出力の要求があるたびにこの手続きがよばれます。この手続きの返り値は 無視されます。
このスロットが未設定であっても、putb
スロットが設定されていれば、
この仮想ポートは文字をバイト列に展開してから、putb
を呼びだします。
設定されていれば、その値は、文字列(不完全なものである可能性もある)を ひとつ引数としてとる手続きでなければなりません。この手続きの返り値は 無視されます。
これは、効率のためにあります。このスロットが未設定であれば、この
仮想ポートは、データのかたまりを出力するために、putb
あるいは putc
を繰り返し呼びます。もし、ひとかたまりの
出力を効率的に実行可能なコードであれば、この手続きを提供できます。
設定されていれば、その値は引数をとらない手続きでなければなりません。
ポートのフラッシュを要求されたときに呼ばれます(たとえば、flush
がこのポートに対して呼ばれたとき、あるいは、このポートがクローズ
されるとき)。
この手続きは、ポートが何らかのバッファリングをおこなうか、なんらかの 状態を持つような場合に便利です。ポートが状態をともなう操作を行わない のなら、これは未設定にしておけます。
この手続きはファイナライザから呼ばれることがあります。したがって、 特別な注意が必要です。後述のファイナライザに関する注意を 参照してください。
<virtual-input-port>
の close
スロットと同様です。
<virtual-input-port>
の seek
スロットと同様です。
このタイプの仮想ポートは <buffered-input-port>
クラス
および <buffered-output-port>
クラスで実現されています。
適切なスロットに手続きを設定するこでポートの振舞いをカスタマイズ
することができます。
これらのポートは、内部バッファを持っており、そのバッファを満たすか フラッシュするかの必要があるときにのみ、Scheme の手続きが呼ばれます。 通常、I/O毎に Scheme の手続きを呼ぶよりも遥かに効率がいいものです。 実際の内部バッファリング機構は、GaucheのファイルI/Oポートと同じです。
これらのポートはバッファとして u8vector
を使います。詳細は、
ユニフォームベクタ を参照してください。
{gauche.vport}
このクラスのインスタンスは、入力ポートとして振舞います。
これは以下のようなインスタンススロットを持ちます。
意味のある入力ポートとして使うには少くとも、fill
スロットを
設定しなければなりません。
設定されているなら、u8vector
の引数を一つとる手続きでなければ
なりません。そのベクタの最初からデータを満たさなければなりません。
要求されたものよりデータの残りが少ない場合には、
ベクタ全体を満たす必要はありません。しかしながら、
データが残っている場合には少くとも1バイトは満たさなければなりません。
もしデータがまだ利用可能になっていなければ、なにがしかのデータが利用可能に
なるまで待たなければなりません。
この手続きは実際に満されたバイト数を返さなければなりません。ポートが EOFに達したことを示すために、0 または EOFオブジェクトを返すこともできます。
設定されているなら、引数をとらない手続きでなければなりません。
この手続きは、なにがしかの読み込み用データが利用可能になっていれば
真の値を返し、そうでなければ、#f
を返します。完全仮想ポートとはちがい、
バイナリI/Oと文字I/Oを区別する必要はありません。
このスロットが未定義の場合、このポートは常にデータが利用可能になっている とみなされます。
設定されていれば、引数をとらない手続きでなければなりません。この手続きは 仮想バッファポートがクローズされるときに呼出されます。ポートがクローズ されるときに、なにがしかのクリーンアップを行うのでなければ、設定する必要 はありません。
この手続きは、ファイナライザから呼ばれる可能性がありますので、特別な 注意が必要です。後述のファイナライズに関する注意書きを参照してください。
設定されているなら、このポートの使っているファイルディスクリプタ番号(正確な非負整数)
を返す手続きでなければなりません。この手続きは port-file-number
をこのポートに対して呼出したときに呼ばれます。
そのようなファイルディスクリプタが存在していなければ、
#f
を返すことができます。
あるいは、このスロットを未設定にしておくこともできます。
設定されているなら、オフセット引数とどこからかを示す引数の2つをとる
手続きでなければなりません。これは、前述の
<virtual-input-port>
のseek 手続きと同じように動作します。
この手続きはファイナライザから呼ばれる可能性があり、特別な注意が 必要です。後述のファイナライズに関する注意を参照してください。
これらのスロットの値以外に、当該ポートの内部バッファのサイズを設定する
のにはmakeメソッドに:buffer-size
というキーワード引数で非
負の正確な整数を渡します。:buffer-size
が省略されるか、0 が渡さ
れた場合にはシステムのデフォルトのバッファサイズ(たとえば、8kとか)が使
われます。:buffer-size
はインスタンススロットではないので、バッ
ファポートのインスタンスを生成したあとで設定することはできません。以下
は 64K のバッファを使うバッファの作り方の例です。
(make <buffered-input-port> :buffer-size 65536 :fill my-filler)
{gauche.vport}
このクラスのインスタンスは出力ポートとして振舞います。これは、以下のような
インスタンススロットを持ちます。少くとも flush
は設定しなければ
なりません。
設定されているなら、u8vector
のバッファとフラグという2つの
引数をとる手続きでなければなりません。手続きはバッファ内のデータを
どこかへ出力しなければならず、実際に出力したバイト数を返します。
フラグが偽なら、この手続きはバッファ全体よりも少い(ただし、最低でも 1バイト)の出力を行ってもかまいません。もしフラグが真ならば、この手続きは、 バッファの内容をすべて出力しなければなりません。
<buffered-input-port>
の close
スロットと同様です。
<buffered-input-port>
の filenum
スロットと同様です。
<buffered-input-port>
の seek
スロットと同様です。
これらのスロットの値以外に、当該ポートの内部バッファのサイズを設定する
のにはmakeメソッドに:buffer-size
というキーワード引数で非
負の正確な整数を渡します。詳しくは前述 <buffered-input-port>
の項を見てください。
以下の 2つの手続きは、ユニフォームベクタでバックアップされた、
バッファ入/出力ポートを返します。読み込み元のベクタあるいは
書き出し相手のベクタはどのようなユニフォームベクタであってもかまいませんが、
実際の入出力時にはu8vector
にaliasされます
(ユニフォームベクタの変換 の uvector-alias
参照)。
pack
/unpack
(binary.pack
- バイナリデータのパック参照) と一緒に使うと
バイナリのデータ構造をパースしたり、構築したりするのに便利です。
また、仮想ポートの使い方の例でもあります。実装法に興味があれば、
ソースツリーの gauche/vport.scm(あるいは ext/vport/vport.scm)
を読んでください。
{gauche.vport} 与えられたユニフォームベクタ uvectorの最初からその内容を読む 入力ポートを返します。読み込み動作が、uvectorの終端に到達したら EOFが返されます。シーク操作も実装されています。
[R7RS base]
{gauche.vport}
open-input-uvector
と似ていますが、引数はu8vectorでなければなりません。
R7RS baseの手続きです。
{gauche.vport} 与えられた uvector をデータの格納に使うような出力ポートを返します。
uvectorが全て埋まった後、何が起きるかは、extendable引数によって 変わります。もし偽の値(デフォルト)では、更に送られてきたデータは黙って捨てられます。 extendableが真の値であった場合、追加のデータを保持するため 格納領域は自動的に拡張されます。
extendableに真の値を与えた場合、貯められたデータを取り出すには、
下で説明するget-output-uvector
を使ってください。渡したuvector
引数には溢れたデータは格納されないので。
特別な場合として、uvector引数を省略することができます。
この場合、データストレージとしてu8vector
が使われます。
uvector引数を省略した場合、extendable引数を
与えることはできませんが、これは真の値を与えたものとみなされます。
でないと意味がないからです。get-output-uvector
で
貯められたデータを取り出すことができます。
ポートに対してシーク操作も使うことができます。
ただし、whenceにSEEK_END
を与えた場合の解釈が、extendableかどうかで
変わることに注意してください。extendableなポートでは、
末尾は「それまでに書き込まれたデータのうちもっとも大きなオフセット」の次に
設定されます。uvectorポートを開いてすぐに1バイト書き込んだ状態では、
uvector引数にどんなに大きなベクタを渡していたとしても、SEEK_END
の基準点は2バイト目です。一方、extendableでないポートでは、
末尾はそれまでに書き込んだデータによらず、常に与えたuvectorの
末尾の次になります。その場合は、末尾やそれ以降にシークすることはできません
(port-seek
にSEEK_END
を渡す時、オフセットは必ず負数でなければ
なりません)。
[R7RS base]
{gauche.vport}
open-output-uvector
を引数なしで呼ぶのと同じです。
必要に応じて伸長するu8vectorをバッファに使います。
R7RS baseの手続きです。
{gauche.vport}
portがopen-output-uvector
で作られたポートであるなら、
これまでに蓄えられたデータをuvectorとして返します。
portがそうでない場合は#f
が返されます。
返り値のベクタは、open-output-uvector
に渡したuvector
と同じ型で、実際に書き込まれたデータの最後尾までの範囲になります。
それはopen-output-uvector
に渡されたuvectorより小さいかも
しれませんし、ポート作成時にextendableに真の値が渡されていた場合は
元のuvectorよりも大きいかもしれません。
uvectorの型がs8vector
やu8vector
以外である場合、
書き込まれたデータのうち完全な要素に足りない部分は結果に含まれません。
例えば、s32vector
でポートを作り7バイト書き込んでから
get-output-uvector
を呼ぶと、返されるs32vector
は
1要素になります。最後の3バイトは完全な32bit整数を構成しないからです。
デフォルトでは、返り値のベクタは新たにアロケートされたものです。 しかしsharedキーワード引数に真の値を渡すと、可能ならば、 portが使っている格納領域をそのまま参照するベクタが返されます。 このオプションによってコピーを減らすことができますが、 後でportをシークして前に戻って再書き込みすると、 返されたベクタの内容が書き換わってしまう可能性があることに注意してください。
[R7RS base]
{gauche.vport}
バイトベクタ出力ポートに出力されたデータをu8vectorとして取り出して返します。
portはopen-output-bytevector
か
open-output-uvector
で作られたものでなければなりません。
R7RS baseの手続きです。
以下の手続きは、文字またはオクテットのリストを入力ポートのソースとして利用するものです。
ある意味、port->list
系の手続き(入力ユーティリティ手続き参照)や
port->char-lseq
系の手続き(遅延シーケンス参照)の逆であると
言えます。
{gauche.vport} 与えられた文字もしくはオクテットのリストをデータ源とする 入力ポートを作って返します。
(read (open-input-char-list '(#\a #\b))) ⇒ ab
{gauche.vport}
portがopen-input-char-list
かopen-input-byte-list
で
作られたものであった場合、ポート中のまだ読まれていない部分のリストを返します。
既に全てのデータが読まれていたり、ポートが
open-input-char-list
かopen-input-byte-list
で作られたもので
なかった場合は、空リストが返されます。
注意: Gaucheは一つのポートからのバイナリ読み出しと文字読み出しを混ぜて使うことを
許しています。文字のリストで作られたリストポートからバイトを読み出したり、
あるいはpeek-byte
した場合、ひとつ文字がデータ源から取り出され、
バイト列に分解されてポート内部の一時バッファに置かれます。
その状態でこの手続きを呼び出した場合、バッファ内のバイト列は戻り値には含まれません。
以下の手続きは、文字あるいはオクテットのジェネレータ
入力ポートのソースとして利用するものです。
ある意味、port->char-generator
系の手続き
(ジェネレータの生成参照)の逆であると言えます。
{gauche.vport} 与えられたジェネレータをデータ源とする入力ポートを作って 返します。cgenは文字を生成するジェネレータ、 bgenはバイト(オクテット、0から255の間の正確な整数)を 生成するジェネレータでなけばなりません。 ジェネレータが想定される値以外のものを返した場合はエラーが通知されます。
(read (open-input-char-generator (string->generator "foo"))) ⇒ foo
ジェネレータは本質的に副作用に依存しているオブジェクトなので、 この手続きにcgenやbgenを渡したら、その後で それらのジェネレータに触ってはなりません。 もしそれらのジェネレータを使ってしまった場合、以降の動作は未定義となります。
{gauche.vport}
portがopen-input-char-generator
か
open-input-byte-generator
で作られたものであった場合、
残りのデータを生成するジェネレータを返します。
ポートが既にデータを全て消費していた場合は、空のジェネレータが返されます。
残りのデータを生成するジェネレータを取り出したら、 ジェネレータポートからはも読み出してはなりません。 内部状態を共有しているため、予期せぬ結果がもたらされることになるでしょう。 副作用に頼らず安全な振る舞いが必要な場合は、遅延シーケンスと リストポートを使うのが良いでしょう。
アキュムレータはジェネレータの双対です。値を一回にひとつづつ受け取る手続きで、
値の末端はEOFで示されます。
アキュムレータの操作についてはscheme.generator
- R7RSジェネレータを参照してください。
以下の手続きは、文字またはバイトを受け取るアキュムレータを出力ポートに変換します。
{gauche.vport} accは文字を受け取るアキュムレータです。 出力される文字をaccに送る出力ポートを返します。 出力ポートがクローズされると、accにEOFが渡されます。
註: 返される出力ポートにバイナリ出力を行った場合の動作は未定義です。
{gauche.vport} accはバイト(オクテット)を受け取るアキュムレータです。 出力されるバイトをaccに送る出力ポートを返します。 出力ポートがクローズされると、accにEOFが渡されます。
文字が出力された場合は、Gaucheの内部エンコーディングにしたがってオクテット列に 変換されてaccに送られます。
{gauche.vport} accはバイト(オクテット)、文字、文字列を受け取るアキュムレータです。 出力データをaccに送る出力ポートを返します。 出力ポートがクローズされると、accにEOFが渡されます。
クローズされていない仮想ポートがガベージ・コレクションされると、 クローズ手続きが呼ばれます(仮想バッファポートの場合は、 フラッシュ手続きがクローズ手続きの前に呼ばれます)。これは、 そのポートのファイナライザによって行われます。これはガベージ・コレクション 処理の一部ですから(Scheme 手続きそのものはガーベッジ・コレクタの メインパートの外側で呼ばれているのですが、それでも)特別な注意が必要です。
flush
手続きは、その出力を
Yへ送ります。もしflush
手続きがファイナライザから
呼ばれた場合、Y のファイナライザが既に
呼ばれていて、Y がクローズしてしまっていることがあるのです。
従って X の flush
手続きは、Y が既にクローズして
いるかどうかをチェックする必要があります。
close
あるいは flush
などの
手続きが、グローバルなリソースをロックしたり、アクセスしたりする
必要のある場合には、デッドロックやアクセスの衝突を回避するために
特に注意する必要があります。
単一スレッドのプログラムにおいても、ファイナライザは、Schemeの プログラムのいたるところで走る可能性があります。したがって、実質的に それは別のスレッドで走っていると考えておくべきです。
Previous: gauche.version
- バージョン番号の比較, Up: ライブラリモジュール - Gauche拡張モジュール [Contents][Index]