What the purpose of 'bufp += nread;' in the Robust reading rio_read() function?

112 Views Asked by At

I'm reading the code below in Chapter 10 in the book called Computer System: A Programmer's Perspective. I understand all the code below except the line: bufp += nread; I found this line is pointless, because I don't see the point to increment the bufp pointer by nread?

Anyone can explain to me why bufp+= nread; exits there? Thank you very much!

ssize_t rio_readn(int fd, void *usrbuf, size_t n) 
{
    size_t nleft = n;
    ssize_t nread;
    char *bufp = usrbuf;

    while(nleft > 0) {
        if((nread = read(fd, bufp, nleft)) < 0) {
            if (errno == EINTR)
                nread = 0;
            else
                return -1;
        }
        else if (nread == 0)
            break;
        nleft -= nread;
        bufp += nread;
    }
    return (n - nleft);
}

I have tried the code without bufp += nread;, it worked without it. And I've searched online everywhere if anyone had the same concern as mine, but I didn't find it.

2

There are 2 best solutions below

1
Mark Adler On

If bufp was not incremented, then the next read() would write over the previously read data. The way it works is that read() delivers nread bytes, which may be less than nleft. On the first call the read data occupies the buffer locations usrbuf[0..nread-1]. On the next call of read() after bufp += nread;, bufp will point to usrbuf[nread], which is the next available byte in the buffer after what was previously read.

The only reason that code would work without the increment would be if the loop only ran once, or ran twice with the second read() reading zero bytes. Those two would in fact be the normal case for reading regular files, since then read() will get all of the available data. The reason that this function needs a loop, is that when reading from a pipe, read() will not wait for nleft bytes from the source, and instead return what is available at the time. (read() will wait for at least one byte or for EOF, so that if it returns zero, you know it got to EOF.) The loop assures that n bytes are read if they eventually become available from the pipe.

1
Luis Colorado On

I have tried the code without bufp += nread;, it worked without it. And I've searched online everywhere if anyone had the same concern as mine, but I didn't find it.

When you do a partial read (something you have not observed) and fill partially the buffer, and more data is to come, you will make a new read() to try to fill the rest of the buffer. I'll try to explain first with comments the meaning of each statement in the while loop.

    while(nleft > 0) {  /* while there's still room in the buffer for more data */
        if((nread = read(fd, bufp, nleft)) < 0) {
            /* if nread  < 0, error checking must be done. */
            if (errno == EINTR)
                nread = 0; /* in case something interrupted the read, return
                            * 0, I have no clear idea if this is what is
                            * required, as some data can have been
                            * accumulated already, I should return n - nleft,
                            * as is done at the bottom, but can be fine,
                            * depending on the requirements.
                            */
            else
                return -1;  /* considered error, some data can be lost */
        }
        else if (nread == 0) /* nread == 0 is a special case (end of input) */
            break;
        /* now update the pointers to fill more space with another read if there's
         * still room in the buffer */
        nleft -= nread;  /* subtract the read from nleft to reflect the new
                          * available size in the buffer */
        bufp += nread;   /* advance the pointer nread positions, so next read
                          * doesn't overwrite the just read data */
    }
    return (n - nleft);  /* n - nleft is how much data has been read */