diff options
Diffstat (limited to 'kdecore/kbufferedio.cpp')
-rw-r--r-- | kdecore/kbufferedio.cpp | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/kdecore/kbufferedio.cpp b/kdecore/kbufferedio.cpp new file mode 100644 index 000000000..1c46e4f05 --- /dev/null +++ b/kdecore/kbufferedio.cpp @@ -0,0 +1,299 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 2001 Thiago Macieira <thiago.macieira@kdemail.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <string.h> + +#include <qptrlist.h> +#include <qcstring.h> +#include "kbufferedio.h" + +/** + * @section impldetails Implementation Details + * + * The KBufferedIO class has two purposes: first, it defines an API on how + * that classes providing buffered I/O should provide. Next, it implements on + * top of that API a generic buffering, that should suffice for most cases. + * + * The buffering implemented consists of two separate buffer areas, one for + * the input (or read) buffer, and one for the output (or write) buffer. Each + * of those buffers is implemented through a QList of QByteArrays instead of + * simply QByteArrays. The idea is that, instead of having one large, contiguous + * buffer area, we have several small ones. Even though this could be seen as + * a waste of memory, it makes our life easier, because we can just append a new + * QByteArray to the list and not have to worry with copying the rest of the + * buffer, should we need to expand. + * + * This way, we have the capability of unlimited buffering, which can grow to + * the extent of available memory. + * + * For each buffer, we provide three kinds of functions, available as protected + * members: consume, feed and size. The size functions calculate the current + * size of the buffer, by adding each individual QByteArray size. The feed + * functions are used by the I/O functions that receive data from somewhere, + * i.e., from the system, in the case of the input buffer, and from the user, + * in the case of the output buffer. These two functions are used to give + * the buffers more data. And the consume functions are used by the functions + * that send out data (to the system, for the write buffer, and to the user, + * for the read buffer). + * + * Note that for your own implementation, you can have your readBlock function + * merely call consumeReadBuffer, similarly to peekBlock. As for + * the writeBlock function, you'd call feedWriteBuffer. + * + * Now, the function receiving data from the system will need to simply call + * feedReadBuffer, much in the same way of unreadBlock. The tricky part is + * for the output function. We do not provide a member function that copies + * data from the output buffer into another buffer for sending. We believe that + * would be a waste of resources and CPU time, since you'd have to allocate + * that buffer, copy data into it and then call the OS, which will likely just + * copy data out of it. + * + * Instead, we found it better to leave it to you to access outBuf member + * variable directly and use the buffers there. Should you want to copy that + * into a larger buffer before sending, that's up to you. + * + * Both buffers work in the same way: they're an "array" of buffers, each + * concatenated to the other. All data in all buffers is valid data, except + * for the first QByteArray, whose valid data starts at inBufIndex/outBufIndex + * bytes from the start. That is, the data starts in the first QByteArray buffer + * that many bytes from the start and goes on contiguously until the last + * QByteArray. This has been decided like that because we didn't want to + * create a new QByteArray of the remaining bytes in the first buffer, after + * a consume operation, because that could take some time. It is faster + * this way, although not really easy. + * + * If you want to take a look at an implementation of a buffered I/O class, + * refer to KExtendedSocket's source code. + */ + +// constructor +KBufferedIO::KBufferedIO() : + inBufIndex(0), outBufIndex(0) +{ + inBuf.setAutoDelete(true); + outBuf.setAutoDelete(true); +} + +// destructor +KBufferedIO::~KBufferedIO() +{ +} + +// sets the buffer sizes +// this implementation doesn't support setting the buffer sizes +// if any parameter is different than -1 or -2, fail +bool KBufferedIO::setBufferSize(int rsize, int wsize /* = -2 */) +{ + if (wsize != -2 && wsize != -1) + return false; + if (rsize != -2 && rsize != -1) + return false; + + return true; +} + +int KBufferedIO::bytesAvailable() const +{ + return readBufferSize(); +} + +int KBufferedIO::bytesToWrite() const +{ + return writeBufferSize(); +} + +// This function will scan the read buffer for a '\n' +bool KBufferedIO::canReadLine() const +{ + if (bytesAvailable() == 0) + return false; // no new line in here + + QByteArray* buf; + + // scan each QByteArray for the occurrence of '\n' + QPtrList<QByteArray> &buflist = ((KBufferedIO*)this)->inBuf; + buf = buflist.first(); + char *p = buf->data() + inBufIndex; + int n = buf->size() - inBufIndex; + while (buf != NULL) + { + while (n--) + if (*p++ == '\n') + return true; + buf = buflist.next(); + if (buf != NULL) + { + p = buf->data(); + n = buf->size(); + } + } + + return false; // no new line found +} + +// unreads the current data +// that is, writes into the read buffer, at the beginning +int KBufferedIO::unreadBlock(const char *data, uint len) +{ + return feedReadBuffer(len, data, true); +} + +// +// protected member functions +// + +unsigned KBufferedIO::consumeReadBuffer(unsigned nbytes, char *destbuffer, bool discard) +{ + { + register unsigned u = readBufferSize(); + if (nbytes > u) + nbytes = u; // we can't consume more than there is + } + + QByteArray *buf; + unsigned copied = 0; + unsigned index = inBufIndex; + + buf = inBuf.first(); + while (nbytes && buf) + { + // should we copy it all? + unsigned to_copy = buf->size() - index; + if (to_copy > nbytes) + to_copy = nbytes; + + if (destbuffer) + memcpy(destbuffer + copied, buf->data() + index, to_copy); + nbytes -= to_copy; + copied += to_copy; + + if (buf->size() - index > to_copy) + { + index += to_copy; + break; // we aren't copying everything, that means that's + // all the user wants + } + else + { + index = 0; + if (discard) + { + inBuf.remove(); + buf = inBuf.first(); + } + else + buf = inBuf.next(); + } + } + + if (discard) + inBufIndex = index; + + return copied; +} + +void KBufferedIO::consumeWriteBuffer(unsigned nbytes) +{ + QByteArray *buf = outBuf.first(); + if (buf == NULL) + return; // nothing to consume + + if (nbytes < buf->size() - outBufIndex) + // we want to consume less than there is in the first buffer + outBufIndex += nbytes; + else + { + nbytes -= buf->size() - outBufIndex; + outBufIndex = 0; + outBuf.remove(); + + while ((buf = outBuf.current()) != NULL) + if (buf->size() <= nbytes) + { + nbytes -= buf->size(); + outBuf.remove(); + } + else + { + outBufIndex = nbytes; + break; + } + } +} + +unsigned KBufferedIO::feedReadBuffer(unsigned nbytes, const char *buffer, bool atBeginning) +{ + if (nbytes == 0) + return 0; + + QByteArray *a = new QByteArray(nbytes); + a->duplicate(buffer, nbytes); + + if (atBeginning) + inBuf.prepend(a); + else + inBuf.append(a); + + return nbytes; +} + +unsigned KBufferedIO::feedWriteBuffer(unsigned nbytes, const char *buffer) +{ + if (nbytes == 0) + return 0; + + QByteArray *a = new QByteArray(nbytes); + a->duplicate(buffer, nbytes); + outBuf.append(a); + return nbytes; +} + +unsigned KBufferedIO::readBufferSize() const +{ + unsigned count = 0; + QByteArray *buf = ((KBufferedIO*)this)->inBuf.first(); + while (buf != NULL) + { + count += buf->size(); + buf = ((KBufferedIO*)this)->inBuf.next(); + } + + return count - inBufIndex; +} + +unsigned KBufferedIO::writeBufferSize() const +{ + unsigned count = 0; + QByteArray *buf = ((KBufferedIO*)this)->outBuf.first(); + while (buf != NULL) + { + count += buf->size(); + buf = (const_cast<KBufferedIO*>(this))->outBuf.next(); + } + + return count - outBufIndex; +} + +void KBufferedIO::virtual_hook( int id, void* data ) +{ KAsyncIO::virtual_hook( id, data ); } + +#include "kbufferedio.moc" |