/* * ストリームの抽象化の試み * ・特別なライブラリに頼らない * ・malloc/free などを使わない。外部のコールバック関数で対処 * ・ */ #include "myio.h" /* * ユーティリティ関数 */ /* * 文字列の長さを返す関数 */ int mystrlen (const char *str) { const char *p = str; if (!p) { return 0; } for (; *p; p++); return p-str; } /* 二つの文字列が等しいか検査する関数 * 返り値: * 1:等しい、0:等しくない */ int mystreq (const char*x, const char*y) { const char *p = x; const char *q = y; if (!x || !y) return 0; while (*p && *q) { if (*(p++) != *(q++)) return 0; } if (!*p && !*q) { return 1; } return 0; } /* * 入力ストリームの時1を返す。 */ int MYIO_is_input( MYIO *x ) { if (x) { return x->flag_in; } return 0; } /* * 出力ストリームの時1を返す。 */ int MYIO_is_output( MYIO *x ) { if (x) { return x->flag_out; } return 0; } /* * ストリームのコンテクストデータを返す */ void * MYIO_get_ptr ( MYIO *x ) { if (x) { return x->ptr; } return (void*)0; } /* * ストリームハンドラの生成 */ MYIO *MYIO_make( MYIO *myio /* ストリームのポインタ */ , MYIO_METHOD *method /* 使用するメソッド構造体のポインタ */ , int flag_in /* 入力フラグ */ , int flag_out /* 出力フラグ */ , void *ptr /* メソッドで使用するコンテクストデータ */ ) { if (!myio || !method) { return myio; } myio->method = method; myio->flag_in = flag_in; myio->flag_out = flag_out; myio->ptr = ptr; return myio; } /* * size バイト分のデータを読み出す関数。 * 返り値は buf に読み出したバイト数。 * 入力ストリームが空の時も何もしない。0が返る。 * エラー時には -1 が返る。 */ int MYIO_read( MYIO *in , char *buf , int size ) { if (!in || !buf || size<=0) { return -1; } if (! in->flag_in) { return -1; /* 入力ストリームではない */ } if (! in->method) { return -1; /* メソッドの構造体がない */ } if (! in->method->read) { return -1; /* read メソッドがない */ } return in->method->read(in, buf, size); } /* * buf に格納された size バイト分のデータを書き込む関数。 * 返り値は書き出したバイト数。 * size<=0 の時は何もしない。0が返る。 * エラー時には -1 が返る。 */ int MYIO_write( MYIO *out , const char *buf , int size ) { if (!out || !buf || size<0) { return -1; } if (size == 0) { return 0; } if (! out->flag_out) { return -1; /* 出力ストリームではない */ } if (! out->method) { return -1; } if (! out->method->write) { return -1; } return out->method->write(out, buf, size); } /* * 文字列 str (ヌル文字で終端されたデータ)を書き込む関数。 * 返り値は書き出した文字列の長さ。 * str の長さが 0 の時は何もしない。0が返る。 * エラー時には -1 が返る。 */ int MYIO_puts( MYIO *out , const char *str ) { int len = 0; if (!out || !str) { return -1; } len = mystrlen(str); if (len == 0) { return 0; } if (! out->flag_out) { return -1; /* 出力ストリームではない */ } if (! out->method) { return -1; } if (out->method->puts) { return out->method->puts(out, str); } if (out->method->write) { int o=0; while (omethod->write(out, str+o, len-o); if (write_bytes < 0) { return -1; } o += write_bytes; } return o; } return -1; } /* * size-1 バイトまでの文字列をストリームから読み出し、buf に保存する。 * EOF または LF を読み込んだところで停止する。 * LF または CRLF は '\0' に書き換えられる。 * 返り値は最後のヌル文字を含まない読み出した文字列の長さ。 * size==0 の時は何もしない。0が返る。 * エラー時には -1 が返る。 */ int MYIO_gets( MYIO *in , char *buf , int size ) { if (!in || !buf || size<0) { return -1; } if (size == 0) { return 0; } if (! in->flag_in) { return -1; /* 入力ストリームではない */ } if (! in->method) { return -1; } if (in->method->gets) { return in->method->gets(in, buf, size); } if (in->method->read) { /* バッファリングなしでは効率悪い */ int len = 0; for (len=0; len < size; len++) { char b[1]; int nbytes = in->method->read(in, b, 1); if (nbytes == 0) { break; } if (nbytes < 0 ) { return -1; } if (b[0] == MYIO_CHAR_LF) { /* ...LF */ buf[len] = '\0'; if (len>0 && buf[len-1] == MYIO_CHAR_CR) { /* ...CRLF の時 */ buf[len-1] = '\0'; len--; } break; } buf[len] = b[0]; } if (len >= size) { return -1; /* size-1 以上 */ } return len; } return -1; } /* * ストリーム間で size バイト分のデータを移す。 * 返り値は実際に移したバイト数。 * size<=0 の時は何もしない。0が返る。 * 入力ストリームが空の時も何もしない。0が返る。 * 終了後は出力ストリームの後尾に size バイト分増えている。 */ int MYIO_move ( MYIO *in /* IN: 入力ストリーム */ , int size /* IN: 読み出すバイト数 */ , MYIO *out /* OUT: 出力ストリーム */ ) { int length = 0; int remain = size; if (!in || size <=0 || !out) { return 0; } if (! in->method || ! in->method->read || ! out->method || ! out->method->write) { return -1; /* 実装がない */ } if (! in->flag_in || ! out->flag_out) { return -1; /* ストリームの入出力属性がマッチしていない */ } while (remain > 0) { char buf[MYIO_BUF_SIZE]; int nbytes = MYIO_BUF_SIZE; if (nbytes > remain) { nbytes = remain; } nbytes = in->method->read(in, buf, nbytes); if (nbytes == 0) { break; } if (nbytes < 0) { return -1; } { int o=0; while (omethod->write(out, buf+o, nbytes-o); } } remain -= nbytes; length += nbytes; } return length; } /* * バッファリングフィルタ(入力フィルタ) */ typedef struct myio_buf_ctx_st { MYIO *in; /* バッファに保持しているデータサイズ */ int size; /* バッファ中の現在の位置 */ int pos; /* バッファのサイズ */ int buf_size; /* バッファ */ char *buf; } myio_buf_ctx; int myio_buf_read ( MYIO *in , char * buf , int size ) { int read_size = 0; myio_buf_ctx *ctx = (myio_buf_ctx*)(in->ptr); #ifdef DEBUG puts("#myio_buf_read(1)"); #endif if (!ctx) { return -1; } if (!ctx->in) { return -1; } /* バッファが空か、すでに読み出し済みの時。 * 最初に呼ばれる時: ctx->pos==0 ctx->size==0 * 読み出し済みの時: ctx->pos >= ctx->size */ if (ctx->pos >= ctx->size) { #ifdef DEBUG if(0)puts("#myio_buf_read(2)"); #endif /* チャンクをバッファのサイズの範囲内で読み出し */ ctx->pos = 0; ctx->size = MYIO_read(ctx->in, ctx->buf, ctx->buf_size); if (ctx->size <= 0) { #ifdef DEBUG if(0)puts("#myio_buf_read(3)"); #endif return ctx->size; } #ifdef DEBUG if(0)puts("#myio_buf_read(4)"); #endif } while (ctx->pos < ctx->size) { if (read_size >= size) { break; } buf[read_size++] = ctx->buf[ctx->pos++]; } #ifdef DEBUG if(0)puts("#myio_buf_read(5)"); #endif return read_size; } #define MYIO_METHOD_NAME_BUF_FILTER "_BUFFERING_FILTER_" MYIO_METHOD myio_buf_method = { MYIO_METHOD_NAME_BUF_FILTER , (void*)0 , myio_buf_read , (void*)0 , (void*)0 }; /* * バッファリングフィルタ * alloc_func はメモリアロケーションの関数 * void * alloc_func(void *alloc_func_ctx, int alloc_size) */ MYIO *MYIO_buf_filter_new ( MYIO *in , int buf_size , void *alloc_func , void *alloc_func_ctx ) { MYIO *myio; myio_buf_ctx *ctx; void * (*alloc_f)(void*,int) = alloc_func; if (alloc_f == (void*)0) { return (void*)0; } if (buf_size <= 0) { return (void*)0; } ctx = (myio_buf_ctx *)alloc_f(alloc_func_ctx, sizeof(myio_buf_ctx)); ctx->in = in; ctx->size = 0; ctx->pos = 0; ctx->buf_size = buf_size; ctx->buf = (char*)alloc_f(alloc_func_ctx, ctx->buf_size); myio = (MYIO*)alloc_f(alloc_func_ctx, sizeof(MYIO)); return MYIO_make(myio, &myio_buf_method, 1, 0, ctx); } MYIO *MYIO_buf_get_MYIO ( MYIO *myio ) { if (!myio) { return (void*)0; } if (myio->ptr) { if (myio->method) { if (mystreq(myio->method->name, MYIO_METHOD_NAME_BUF_FILTER)) { myio_buf_ctx *ctx = myio->ptr; return ctx->in; } } } return (void*)0; } /* バッファリングフィルタの解放 * ・フィルタ対象ストリームの解放は行わない。 * ・free_func は領域の解放を行う関数。void free_func(free_func_ctx, data); */ void MYIO_buf_free( MYIO *myio , void *free_func , void *free_func_ctx ) { void (*free_f)(void*,void*) = free_func; if (!myio || !free_func) { return; } if (myio->ptr) { if (myio->method) { if (mystreq(myio->method->name, MYIO_METHOD_NAME_BUF_FILTER)) { myio_buf_ctx *ctx = myio->ptr; free_f(free_func_ctx, ctx->buf); free_f(free_func_ctx, myio->ptr); free_f(free_func_ctx, myio); } } } }