演習8-2 K&R プログラミング言語C

演習8-2

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

#define MY_NULL 0
#define MY_EOF (-1)
#define MY_BUFSIZ 1024
#define MY_OPEN_MAX 20 /* 一時に開けるファイルの最大数 */

/* フラグのビットフィールド */
struct flags {
    unsigned int is_read    : 1;
    unsigned int is_write   : 1;
    unsigned int is_unbuf   : 1;
    unsigned int is_eof     : 1;
    unsigned int is_err     : 1;
};

typedef struct _iobuf {
    int cnt;            /* 残っている文字数 */
    char *ptr;          /* 次の文字位置 */
    char *base;         /* バッファの位置 */
    struct flags flag;  /* ファイル・アクセスのモード */
    int fd;             /* ファイル記述子 */
} MY_FILE;
extern MY_FILE _iob[MY_OPEN_MAX];

#define my_stdin   (&_iob[0])
#define my_stdout  (&_iob[1])
#define my_stderr  (&_iob[2])

int _fillbuf(MY_FILE *);
int _flushbuf(int, MY_FILE *);

#define feof(p)     ((p)->flag.is_eof == 1)
#define ferror(p)   ((p)->flag.is_err == 1)
#define fileno(p)   ((p)->fd)

#define my_getc(p)     (--(p)->cnt >= 0 \
        ? (unsigned char) *(p)->ptr++ : _fillbuf(p))
#define my_putc(x, p)  (--(p)->cnt >= 0 \
        ? *(p)->ptr++ = (x) : _flushbuf((x), p))

#define my_getchar()   my_getc(my_stdin)
#define my_putchar(x)  my_putc((x), my_stdout)

MY_FILE _iob[MY_OPEN_MAX] = { /* my_stdin, my_stdout, my_stderr: */
    { 0, (char *) 0, (char *) 0, {1, 0, 0, 0, 0}, 0 },
    { 0, (char *) 0, (char *) 0, {0, 1, 0, 0, 0}, 1 },
    { 0, (char *) 0, (char *) 0, {0, 1, 1, 0, 0}, 2 }
};

MY_FILE *my_fopen(char*, char*);

int main(int argc, char *argv[])
{
    int c;
    char mode = 'r';
    MY_FILE *fp;

    if (argc == 2) {
        if ((fp = my_fopen(argv[1], &mode)) == MY_NULL) {
            fprintf(stderr, "can't open %s\n", argv[1]);
            exit(EXIT_FAILURE);
        }
        while ((c = my_getc(fp)) != MY_EOF) {
            printf("%c", c);
        }
    }

    return 0;
}

#define PERMS 0666 /* 所有者、グループ、他人に対して RW */

/* my_fopen : ファイルを開いて、ファイル・ポインタを返す */
MY_FILE *my_fopen(char *name, char *mode)
{
    int fd;
    MY_FILE *fp;

    if (*mode != 'r' && *mode != 'w' && *mode != 'a') {
        return MY_NULL;
    }
    for (fp = _iob; fp < _iob + MY_OPEN_MAX; fp++) {
        if ((fp->flag.is_read == 0 && fp->flag.is_write == 0)) {
            break; /* found free slot */
        }
    }
    if (fp >= _iob + MY_OPEN_MAX) { /* 空きスロットなし */
        return MY_NULL;
    }

    if (*mode == 'w') {
        fd = creat(name, PERMS);
    } else if (*mode == 'a') {
        if ((fd = open(name, O_WRONLY, 0)) == -1) {
            fd = creat(name, PERMS);
        }
        lseek(fd, 0L, 2);
    } else {
        fd = open(name, O_RDONLY, 0);
    }
    if (fd == -1) { /* 名前がアクセス不能 */
        return MY_NULL;
    }
    fp->fd = fd;
    fp->cnt = 0;
    fp->base = MY_NULL;
    if (*mode == 'r') {
        fp->flag.is_read = 1;
        fp->flag.is_write = 0;
    } else {
        fp->flag.is_read = 0;
        fp->flag.is_write = 1;
    }
    return fp;
}

/* _fillbuf : 入力バッファを割り当てて、詰める */
int _fillbuf(MY_FILE *fp)
{
    int bufsize;

    if (!fp->flag.is_read && fp->flag.is_eof && fp->flag.is_err) {
        return MY_EOF;
    }
    bufsize = fp->flag.is_unbuf ? 1 : MY_BUFSIZ;
    if (fp->base == MY_NULL) { /* バッファがまだない */
        if ((fp->base = (char *) malloc(bufsize)) == MY_NULL) {
            return MY_EOF; /* バッファがとれない */
        }
    }
    fp->ptr = fp->base;
    fp->cnt = read(fp->fd, fp->ptr, bufsize);
    if (--fp->cnt < 0) {
        if (fp->cnt == -1) {
            fp->flag.is_eof = 1;
        } else {
            fp->flag.is_err = 1;
        }
        fp->cnt = 0;
        return MY_EOF;
    }
    return (unsigned char) *fp->ptr++;
}

実行結果

ビット演算を使っている版のプログラム fopen と、ビットフィールドを使った版のプログラム fopen2 とで比較してみる。

$ time ./fopen /var/log/system.log >/dev/null

real    0m0.332s
user    0m0.292s
sys     0m0.039s
$ time ./fopen2 /var/log/system.log >/dev/null

real    0m0.330s
user    0m0.290s
sys     0m0.039s
プログラミング言語C 第2版 ANSI規格準拠
B.W. カーニハン D.M. リッチー
共立出版
売り上げランキング: 9726
«
»