diff options
Diffstat (limited to 'lib/fdbuf/fdibuf.cc')
-rw-r--r-- | lib/fdbuf/fdibuf.cc | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/lib/fdbuf/fdibuf.cc b/lib/fdbuf/fdibuf.cc new file mode 100644 index 0000000..205d63c --- /dev/null +++ b/lib/fdbuf/fdibuf.cc @@ -0,0 +1,197 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "fdbuf.h" +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +/////////////////////////////////////////////////////////////////////////////// +// Class fdibuf +/////////////////////////////////////////////////////////////////////////////// +fdibuf::fdibuf(int fdesc, bool dc, unsigned bufsz) + : fdbuf(fdesc, dc, bufsz) +{ +} + +fdibuf::fdibuf(const char* filename, unsigned bufsz) + : fdbuf(open(filename, O_RDONLY), true, bufsz) +{ + if(fd == -1) { + flags = flag_error; + errnum = errno; + } +} + +fdibuf::~fdibuf() +{ +} + +bool fdibuf::eof() const +{ + return (flags & flag_eof) && (bufstart >= buflength); +} + +bool fdibuf::operator!() const +{ + return eof() || error() || closed(); +} + +// refill is protected -- no locking +bool fdibuf::refill() +{ + if(flags) + return false; + if(bufstart != 0) { + if(bufstart < buflength) { + buflength -= bufstart; + memcpy(buf, buf+bufstart, buflength); + } else + buflength = 0; + bufstart = 0; + } + unsigned oldbuflength = buflength; + if(buflength < bufsize) { + ssize_t red = _read(buf+buflength, bufsize-buflength); + if(red < 0) { + errnum = errno; + flags |= flag_error; + } + else if(red == 0) + flags |= flag_eof; + else { + buflength += red; + offset += red; + } + } + return buflength > oldbuflength; +} + +bool fdibuf::get(char& ch) +{ + lock(); + count = 0; + if(bufstart >= buflength) + refill(); + bool r = true; + if(eof() || error()) + r = false; + else { + ch = buf[bufstart++]; + count = 1; + } + unlock(); + return r; +} + +bool fdibuf::read_large(char* data, unsigned datalen) +{ + lock(); + count = 0; + + // If there's any content in the buffer, memcpy it out first. + unsigned len = buflength - bufstart; + if(len > datalen) + len = datalen; + memcpy(data, buf+bufstart, len); + data += len; + datalen -= len; + bufstart += len; + count += len; + + // After the buffer is empty and there's still data to read, + // read it straight from the fd instead of copying it through the buffer. + while(datalen > 0) { + ssize_t red = _read(data, datalen); + if(red < 0) { + errnum = errno; + flags |= flag_error; + break; + } + else if(red == 0) { + flags |= flag_eof; + break; + } + data += red; + datalen -= red; + offset += red; + count += red; + } + unlock(); + return datalen == 0; +} + +bool fdibuf::read(char* data, unsigned datalen) +{ + if(datalen >= bufsize) + return read_large(data, datalen); + lock(); + count = 0; + char* ptr = data; + while(datalen && !eof()) { + if(bufstart >= buflength) + refill(); + unsigned len = buflength-bufstart; + if(len > datalen) + len = datalen; + memcpy(ptr, buf+bufstart, len); + bufstart += len; + datalen -= len; + ptr += len; + count += len; + } + unlock(); + return !datalen; +} + +bool fdibuf::seek(unsigned o) +{ + lock(); + unsigned buf_start = offset - buflength; + if(o >= buf_start && o < offset) { + bufstart = o - buf_start; + } + else { + if(lseek(fd, o, SEEK_SET) != (off_t)o) { + errnum = errno; + flags |= flag_error; + unlock(); + return false; + } + offset = o; + buflength = bufstart = 0; + } + count = 0; + flags &= ~flag_eof; + unlock(); + return true; +} + +bool fdibuf::seekfwd(unsigned o) +{ + return seek(tell() + o); +} + +ssize_t fdibuf::_read(char* buf, ssize_t len) +{ + return ::read(fd, buf, len); +} + +/////////////////////////////////////////////////////////////////////////////// +// Globals +/////////////////////////////////////////////////////////////////////////////// +fdibuf fin(0); |