#pragma implementation //#define TRACE #include #include #include #include #include "bufio.h" bufio::bufio(int f,int size,int sect) { tfd = fd = f; bufcnt = -1; buf = new char[bufsize = size]; sectsize = sect; bufpos = 0; curpos = 0; cursize = 0; tmpsize = 0; } bufio::~bufio() { close(fd); if (tfd != fd) close(tfd); delete [] buf; } void *bufio::read(long pos,int size) { // resize buffer if required long newpos = pos & ~(sectsize - 1); curpos = pos - newpos; if (curpos + size > bufsize) { bufsize = (size + sectsize) & ~(sectsize - 1); // save data already read if required if (newpos >= bufpos && newpos - bufpos < bufcnt) { char *newbuf = new char[bufsize]; bufcnt -= newpos - bufpos; memcpy(newbuf,buf + newpos - bufpos,bufcnt); delete [] buf; buf = newbuf; bufpos = newpos; } else { delete [] buf; buf = new char[bufsize]; bufcnt = 0; } } // seek to new buffer if required (we already made sure size <= bufsize) if (pos < bufpos || pos > bufpos + bufcnt) { if (seek(newpos) != newpos) return 0; bufpos = newpos; bufcnt = ::read(tfd,buf,bufsize); #ifdef TRACE fprintf(stderr,"bufio::read(%ld,%d) read(%ld,%d) = %d\n", pos,size,bufpos,bufsize,bufcnt); #endif if (bufcnt < 0) { bufcnt = 0; } } curpos = pos - bufpos; cursize = size; // read additional data from input if required if (curpos + size > bufcnt || curpos == bufcnt) { int offset = curpos & ~(sectsize - 1); int remsize = bufcnt - offset; curpos -= offset; bufpos += offset; memcpy(buf,buf + offset,remsize); int rfd = (pos < tmpsize) ? tfd : fd; bufcnt = ::read(rfd, buf + remsize, bufsize - remsize); #ifdef TRACE fprintf(stderr, "bufio::read(%ld,%d) read(%d) = %d, fd=%d, tmpsize=%ld, offset=%d\n", pos,size,bufsize - remsize,bufcnt,rfd,tmpsize,offset); #endif if (bufcnt <= 0) { bufcnt = remsize; return 0; } // flush buffer to backing file bufcnt += remsize; if (tfd != fd && bufpos + bufcnt > tmpsize) { int n = tmpsize - bufpos; //assert(n >= 0); n = ::write(tfd,buf + n,bufcnt - n); if (n > 0) tmpsize += n; } } if (curpos + size > bufcnt) return 0; return buf + curpos; } void bufio::setSequential() { if (tfd != fd) return; // open tmp char buf[] = "/tmp/bufioXXXXXX"; const char *tmpnam = mktemp(buf); tfd = open(tmpnam,O_RDWR+O_CREAT+O_EXCL+O_APPEND,0600); unlink(tmpnam); } long bufio::seek(long pos) { curpos = cursize = 0; bufpos = pos; if (tfd == fd) { bufcnt = 0; if (lseek(fd,pos,0) == pos) return pos; setSequential(); if (tfd < 0) return -1; } while (tmpsize < pos) { bufcnt = ::read(fd,buf,bufsize); if (bufcnt <= 0) break; int n = ::write(tfd,buf,bufcnt); if (n <= 0) break; tmpsize += n; } return lseek(tfd,pos,0); } void *bufio::read(int size) { return read(getpos(),size); }