名前
fopencookie - 独自のストリームをオープンする書式
#define _GNU_SOURCE /* feature_test_macros(7) 参照 */ #include <stdio.h>
FILE *fopencookie(void *cookie, const char *mode, cookie_io_functions_t io_funcs);
説明
fopencookie() を使うと、 プログラマーは標準 I/O ストリームの独自の実装を作成することができる。 この実装はストリームのデータを自分が選んだ場所に格納することができる。 例えば、 fopencookie() は fmemopen(3) を実装するのに使用されている。 fmemopen(3) はメモリー上のバッファーに格納されたデータに対するストリームインターフェースを提供している。 独自のストリームを作成するためには、 プログラマーは以下を行う必要がある。- *
- ストリームに対する I/O を実行する際に標準 I/O ライブラリが内部で使用する 4 つの "フック" 関数を実装する。
- *
- "cookie" データ型を定義する。 "cookie" データ型は、上記のフック関数が使用する管理情報 (例えば、データを格納する場所など) を提供する構造体である。 標準の I/O パッケージにはこの cookie の内容に関する情報を持たないが (したがって fopencookie() に渡される際の型は void * である)、 フック関数が呼び出される際に第一引数として cookie が渡される。
- *
- fopencookie() を呼び出して、新しいストリームをオープンし、 そのストリームに cookie とフック関数を関連付ける。
typedef struct { cookie_read_function_t *read; cookie_write_function_t *write; cookie_seek_function_t *seek; cookie_close_function_t *close; } cookie_io_functions_t;
4 つのフィールドの詳細は以下のとおりである。
- cookie_read_function_t *read
- この関数はストリームに対する read 操作を実装する。 呼び出される際、 3 つの引数を受け取る。
-
ssize_t read(void *cookie, char *buf, size_t size);
- 引数 buf と size は、 それぞれ、 入力データを配置できるバッファーとそのバッファーのサイズである。 関数の結果として、 read 関数は buf にコピーされたバイト数を、 ファイル末尾の場合は 0 を、 エラーの場合は -1 を返す。 read 関数はストリームのオフセットを適切に更新すべきである。
- *read がヌルポインターの場合、 独自のストリームからの読み出しは常にファイル末尾 (end of file) を返す。
- cookie_write_function_t *write
- この関数はストリームに対する write 操作を実装する。 呼び出される際、 3 つの引数を受け取る。
-
ssize_t write(void *cookie, const char *buf, size_t size);
- 引数 buf と size は、 それぞれ、 ストリームへの出力するデータが入ったバッファーとそのバッファーのサイズである。 関数の結果として、 write 関数は buf からコピーされたバイト数を返し、 エラーの場合は -1 を返す。 (この関数は負の値を返してはならない。) write 関数はストリームのオフセットを適切に更新すべきである。
- *write がヌルポインターの場合、 このストリームへの出力は破棄される。
- cookie_seek_function_t *seek
- この関数はストリームに対する seek 操作を実装する。 呼び出される際、 3 つの引数を受け取る。
-
int seek(void *cookie, off64_t *offset, int whence);
- *offset 引数は新しいファイルオフセットを指定する。 新しいオフセットは whence に以下の値のどれが指定されたかに応じて決まる。
- SEEK_SET
- ストリームオフセットを、ストリームの先頭から *offset バイトの位置に設定する。
- SEEK_CUR
- ストリームの現在のオフセットに *offset を加算する。
- SEEK_END
- ストリームのオフセットを、ストリームのサイズに *offset を足した場所に設定する。
- 関数が返る前に、 seek 関数はストリームの新しいオフセットを示すように *offset を更新すべきである。
- 関数の結果として、 seek 関数は成功すると 0 を、 エラーの場合 -1 を返す。
- *seek がヌルポインターの場合、 このストリームに対して seek 操作を行うことができない。
- cookie_close_function_t *close
- この関数はストリームをクローズする。 このフック関数では、 このストリームに割り当てられたバッファーを解放するといったことができる。 呼び出される際、 1 つの引数を受け取る。
-
int close(void *cookie);
- cookie 引数は fopencookie() の呼び出し時にプログラマーが渡した cookie である。
- 関数の結果として、 close 関数は成功すると 0 を、 エラーの場合 EOF を返す。
- *close が NULL の場合、 ストリームがクローズされる際に特別な操作は何も行われない。
返り値
成功すると fopencookie() は新しいストリームへのポインターを返す。 エラーの場合、 NULL が返される。属性
この節で使用されている用語の説明については、 attributes(7) を参照。インターフェース | 属性 | 値 |
fopencookie() | Thread safety | MT-Safe |
準拠
この関数は非標準の GNU 拡張である。例
以下のプログラムは、 fmemopen(3) で利用できるのと似た (同じではない) 機能を持つ独自のストリームを実装している。 データがメモリーバッファーに格納されるストリームを実装している。 このプログラムは、 コマンドライン引数をストリームに書き込み、 それからストリームをたどって 5 文字ごとに 2 文字を読み出して、 それを標準出力に書き込む。 以下のシェルセッションはこのプログラムの使用例である。$ ./a.out 'hello world' /he/ / w/ /d/ Reached end of file
このプログラムを改良して様々なエラー状況に強くすることもできる (例えば、 オープン済みのストリームに対応する cookie でストリームをオープンしようとした、 すでにクローズされたストリームをクローズしようとした、など)。
プログラムのソース
#define _GNU_SOURCE #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define INIT_BUF_SIZE 4 struct memfile_cookie { char *buf; /* Dynamically sized buffer for data */ size_t allocated; /* Size of buf */ size_t endpos; /* Number of characters in buf */ off_t offset; /* Current file offset in buf */ }; ssize_t memfile_write(void *c, const char *buf, size_t size) { char *new_buff; struct memfile_cookie *cookie = c; /* Buffer too small? Keep doubling size until big enough */ while (size + cookie->offset > cookie->allocated) { new_buff = realloc(cookie->buf, cookie->allocated * 2); if (new_buff == NULL) { return -1; } else { cookie->allocated *= 2; cookie->buf = new_buff; } } memcpy(cookie->buf + cookie->offset, buf, size); cookie->offset += size; if (cookie->offset > cookie->endpos) cookie->endpos = cookie->offset; return size; } ssize_t memfile_read(void *c, char *buf, size_t size) { ssize_t xbytes; struct memfile_cookie *cookie = c; /* Fetch minimum of bytes requested and bytes available */ xbytes = size; if (cookie->offset + size > cookie->endpos) xbytes = cookie->endpos - cookie->offset; if (xbytes < 0) /* offset may be past endpos */ xbytes = 0; memcpy(buf, cookie->buf + cookie->offset, xbytes); cookie->offset += xbytes; return xbytes; } int memfile_seek(void *c, off64_t *offset, int whence) { off64_t new_offset; struct memfile_cookie *cookie = c; if (whence == SEEK_SET) new_offset = *offset; else if (whence == SEEK_END) new_offset = cookie->endpos + *offset; else if (whence == SEEK_CUR) new_offset = cookie->offset + *offset; else return -1; if (new_offset < 0) return -1; cookie->offset = new_offset; *offset = new_offset; return 0; } int memfile_close(void *c) { struct memfile_cookie *cookie = c; free(cookie->buf); cookie->allocated = 0; cookie->buf = NULL; return 0; } int main(int argc, char *argv[]) { cookie_io_functions_t memfile_func = { .read = memfile_read, .write = memfile_write, .seek = memfile_seek, .close = memfile_close }; FILE *stream; struct memfile_cookie mycookie; size_t nread; char buf[1000]; /* Set up the cookie before calling fopencookie() */ mycookie.buf = malloc(INIT_BUF_SIZE); if (mycookie.buf == NULL) { perror("malloc"); exit(EXIT_FAILURE); } mycookie.allocated = INIT_BUF_SIZE; mycookie.offset = 0; mycookie.endpos = 0; stream = fopencookie(&mycookie,"w+", memfile_func); if (stream == NULL) { perror("fopencookie"); exit(EXIT_FAILURE); } /* Write command-line arguments to our file */ for (int j = 1; j < argc; j++) if (fputs(argv[j], stream) == EOF) { perror("fputs"); exit(EXIT_FAILURE); } /* Read two bytes out of every five, until EOF */ for (long p = 0; ; p += 5) { if (fseek(stream, p, SEEK_SET) == -1) { perror("fseek"); exit(EXIT_FAILURE); } nread = fread(buf, 1, 2, stream); if (nread == 0) { if (ferror(stream) != 0) { fprintf(stderr, "fread failed\n"); exit(EXIT_FAILURE); } printf("Reached end of file\n"); break; } printf("/%.*s/\n", (int) nread, buf); } exit(EXIT_SUCCESS); }
関連項目
fclose(3), fmemopen(3), fopen(3), fseek(3)この文書について
この man ページは Linux man-pages プロジェクトのリリース 5.10 の一部である。プロジェクトの説明とバグ報告に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。2020-11-01 | Linux |