RIO包是CSAPP中一个基于read和write函数实现的能够处理终端并提供了带缓冲区读取的IO包。代码很简单,但很巧妙。
缩减了原程序的初始化等函数之后,主要有以下四个对外函数:
1.ssize_t readn(int fd,void *usrbuf,size_t n);
fd为通过open得到的文件描述符,通常为一个小整数。该函数从指定文件读取n个byte到usrbuf中。如果成功执行,返回传输的字节数,如果读到EOF,返回0,如果出错,返回-1。
2.ssize_t writen(int fd,void *usrbuf,size_t n);
该函数将usebuf中最多n个字节写到文件中。如果成功,返回写入的字节数,如果出错返回-1.
3.ssize_t readlineb(rio_t rp,void usrbuf,size_t maxlen);
rio_t是一个定义乐缓冲区的结构体,具体实现见下面代码。该函数一次从缓冲区/文件读取一行数据(最长不超过maxlen)
4.ssize_t readnb(rio_t rp,void usrbuf,size_t n);
该函数是带有缓冲区版本的read,类似于readn。
myrio.h
#pragma once
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <cstring>
//a buffer named rio_t
#define RIO_BUFSIZE 8192
typedef struct rio_t{
int rio_fd;
int rio_cnt; //unread bytes in rio_buf
char *rio_bufptr; //next unread byte in rio_buf
char rio_buf[RIO_BUFSIZE]; //internal buffer
rio_t(int fd)
:rio_fd(fd),rio_cnt(0),rio_bufptr(rio_buf){ }
} rio_t;
class Rio{
public:
//io function without internal buffer
//Succeed: return the number of byte; EOF: return 0; Fail: return -1
ssize_t readn(int fd,void *usrbuf,size_t n);
ssize_t writen(int fd,void *usrbuf,size_t n);
//io function with internal buffer
//Succeed: return the number of byte; EOF: return 0; Fail: return -1
ssize_t readlineb(rio_t *rp,void *usrbuf,size_t maxlen);
ssize_t readnb(rio_t *rp,void *usrbuf,size_t n);
private:
//read with buffer
ssize_t rio_read(rio_t *rp,char *usrbuf,size_t n);
};
myrio.cpp
#include "myrio.h"
//the difference between these function and system's is that:
//this function can deal with the interrupt.
ssize_t Rio::readn(int fd,void *usrbuf,size_t n){
size_t nleft = n;
ssize_t nread;
char *buf = static_cast<char*>(usrbuf);
while(nleft > 0){
if((nread = read(fd,buf,nleft))<0){
if(errno ==EINTR) nread = 0;
else return -1;
}else if(nleft == 0){
break;
}else{
nleft -= nread;
buf += nread;
}
}
return (n - nleft);
}
ssize_t Rio::writen(int fd,void *usrbuf,size_t n){
size_t nleft = n;
ssize_t nwriten;
char *buf = static_cast<char*>(usrbuf);
while(nleft > 0){
if((nwriten = write(fd,buf,nleft)) <= 0){
if(errno == EINTR) nwriten = 0;
else return -1;
}else{
nleft -= nwriten;
buf += nwriten;
}
return n;
}
}
ssize_t Rio::rio_read(rio_t *rp,char *usrbuf,size_t n){
int cnt;
while(rp->rio_cnt <= 0){ //refill rio_buf if buf is empty
rp->rio_cnt = read(rp->rio_fd,rp->rio_buf,sizeof(rp->rio_buf));
if(rp->rio_cnt < 0){
if(errno != EINTR) return -1;
}else if(rp->rio_cnt == 0){
return 0;
}else{
rp->rio_bufptr = rp->rio_buf;
}
}
cnt = n;
if(rp->rio_cnt < n)
cnt = rp->rio_cnt;
memcpy(usrbuf,rp->rio_bufptr,cnt);
rp->rio_bufptr += cnt;
rp->rio_cnt -= cnt;
return cnt;
}
ssize_t Rio::readlineb(rio_t *rp,void *usrbuf,size_t maxlen){
char c;
char *buf = static_cast<char*>(usrbuf);
int n,rc;
for(n=1;n<maxlen;n++){
if((rc = rio_read(rp,&c,1)) == 1){
*buf = c;
buf++;
if(c == '\n')
break;
}else if(rc == 0){
if(n == 1) return 0;
else break;
}else{
return -1;
}
}
*buf = '\0';
return n;
}
ssize_t Rio::readnb(rio_t *rp,void *usrbuf,size_t n){
size_t nleft = n;
ssize_t nread;
char *buf = static_cast<char*>(usrbuf);
while(nleft > 0){
if((nread = rio_read(rp,buf,nleft)) < 0){
if(errno == EINTR) nread = 0;
else return -1;
}else if(nread == 0){
break;
}else{
nleft -= nread;
buf += nread;
}
}
return (n-nleft);
}