TcpSocketBuffer.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2005-2007 by Peter Dimov.
00004 
00005 This file is part of Calitko (http://www.calitko.org).
00006 
00007 Calitko is free software you can redistribute it and/or modify
00008 it under the terms of the GNU General Public License as published by
00009 the Free Software Foundation either version 2 of the License, or
00010 (at your option) any later version.
00011 
00012 Calitko is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Calitko if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021 */
00022 
00023 #include "Qt.h"
00024 #include "TcpSocketBuffer.h"
00025 #include "Socket.h"
00026 #include "Imports.cpp"
00027 
00029 
00035 TcpSocketBuffer::TcpSocketBuffer (int readBufferSize, int writeBufferSize)
00036     : readBuffer (0), readSize (0), readStart (0), readEnd (0),
00037       writeBuffer (0),  writeSize (0), writeStart (0), writeEnd (0),
00038       writeFlushEnd (0)
00039 {
00040     Q_ASSERT (readBufferSize >= 0);
00041     Q_ASSERT (writeBufferSize >= 0);
00042 
00043     setReadBufferSize (readBufferSize);
00044     setWriteBufferSize (writeBufferSize);
00045 }
00046 
00048 TcpSocketBuffer::~TcpSocketBuffer()
00049 {
00050     delete[] readBuffer;
00051     delete[] writeBuffer;
00052 }
00053 
00055 
00066 QByteArray TcpSocketBuffer::peek (int count) const
00067 {
00068     if (!canRead (count))
00069         return QByteArray();
00070     else {
00071         QByteArray res;
00072         res.resize (count);
00073         peek (res.data(), res.size());
00074         return res;
00075     }
00076 }
00077 
00079 
00090 QByteArray TcpSocketBuffer::peekAtMost (int count) const
00091 {
00092     return peek (std::min (count, readEnd - readStart));
00093 }
00094 
00096 
00110 QByteArray TcpSocketBuffer::peekTo (const QByteArray &delimiter) const
00111 {
00112     QByteArray allBytes = peekAll();
00113     int pos = allBytes.indexOf (delimiter);
00114     if (pos == -1)
00115         return QByteArray();
00116     else
00117         return allBytes.left (pos + delimiter.size());
00118 }
00119 
00121 
00127 QByteArray TcpSocketBuffer::peekAll() const
00128 {
00129     return peek (bytesToRead());
00130 }
00131 
00133 
00141 QByteArray TcpSocketBuffer::read (int count)
00142 {
00143     QByteArray res = peek (count);
00144     purgeReadBuffer (res.size());
00145     return res;
00146 }
00147 
00149 
00157 QByteArray TcpSocketBuffer::readAtMost (int count)
00158 {
00159     QByteArray res = peekAtMost (count);
00160     purgeReadBuffer (res.size());
00161     return res;
00162 }
00163 
00165 
00176 QByteArray TcpSocketBuffer::readTo (const QByteArray &delimiter)
00177 {
00178     QByteArray res = peekTo (delimiter);
00179     purgeReadBuffer (res.size());
00180     return res;
00181 }
00182 
00184 
00187 QByteArray TcpSocketBuffer::readAll()
00188 {
00189     QByteArray res = peekAll();
00190     purgeReadBuffer (res.size());
00191     return res;
00192 }
00193 
00195 
00198 void TcpSocketBuffer::tryReadFrom (Socket *socket)
00199 {
00200     int readSize = 0;
00201     while ((readSize = nextReadChunkSize()) > 0) {
00202         char *readBuffer = nextReadChunk();
00203         qint64 read = socket->read (readBuffer, readSize);
00204         if (read > 0)
00205             hasRead (read);
00206         // If the socket could not read all bytes we requested, try again later.
00207         if (read < readSize)
00208             break;
00209     }
00210 }
00211 
00213 
00223 bool TcpSocketBuffer::canRead (int count) const
00224 {
00225     Q_ASSERT (count >= 0);
00226     return readStart + count <= readEnd;
00227 }
00228 
00230 
00245 void TcpSocketBuffer::peek (char *destination, int size) const
00246 {
00247     Q_ASSERT (canRead (size));
00248     // It's a ring buffer so be careful not to read beyound the array end:
00249     if (readStart + size <= readSize) {
00250         memcpy (destination, readBuffer + readStart, size);
00251     } else {
00252         // Read the part at the end of the ring buffer array:
00253         int count = readSize - readStart;
00254         memcpy (destination, readBuffer + readStart, count);
00255         // Read the rest from the start of the ring buffer:
00256         Q_ASSERT (size - count > 0);
00257         memcpy (destination + count, readBuffer, size - count);
00258     }
00259 }
00260 
00262 
00269 void TcpSocketBuffer::purgeReadBuffer (int count)
00270 {
00271     Q_ASSERT (canRead (count));
00272     readStart += count;
00273     // We keep readStart in the range [0, readSize)
00274     // and readEnd in the range [0, readSize * 2)
00275     if (readStart >= readSize) {
00276         readStart -= readSize;
00277         readEnd -= readSize;
00278     }
00279 }
00280 
00282 
00292 void TcpSocketBuffer::setReadBufferSize (int size)
00293 {
00294     Q_ASSERT (size >= 0);
00295     char *newBuffer = 0;
00296     if (size > 0)
00297         newBuffer = new char [size];
00298     delete[] readBuffer;
00299     readBuffer = newBuffer;
00300     readSize = size;
00301     readStart = readEnd = 0;
00302 }
00303 
00305 
00308 int TcpSocketBuffer::readBufferSize() const
00309 {
00310     return readSize;
00311 }
00312 
00314 
00317 int TcpSocketBuffer::bytesToRead() const
00318 {
00319     return readEnd - readStart;
00320 }
00321 
00323 
00329 char* TcpSocketBuffer::nextReadChunk() const
00330 {
00331     int readPos = (readEnd >= readSize) ? (readEnd - readSize) : readEnd;
00332     return readBuffer + readPos;
00333 }
00334 
00336 
00339 int TcpSocketBuffer::nextReadChunkSize() const
00340 {
00341     if (readEnd >= readSize)
00342         return readStart - (readEnd - readSize);
00343     else
00344         return readSize - readEnd;
00345 }
00346 
00348 
00358 void TcpSocketBuffer::hasRead (int count)
00359 {
00360     Q_ASSERT (count >= 0);
00361     Q_ASSERT (readEnd + count <= readStart + readSize);
00362     readEnd += count;
00363 }
00364 
00366 
00376 bool TcpSocketBuffer::write (const QByteArray &bytes, bool flush)
00377 {
00378     int size = bytes.size();
00379     if (!canWrite (size))
00380         return false;
00381     write (bytes.data(), size, flush);
00382     return true;
00383 }
00384 
00386 
00389 void TcpSocketBuffer::tryWriteTo (Socket *socket)
00390 {
00391     while (bytesToWrite() > 0) {
00392         // buffer is responsible to provide only bytes which have been flushed
00393         char *writeBuffer = nextWriteChunk();
00394         int writeSize = nextWriteChunkSize();
00395         qint64 written = socket->write (writeBuffer, writeSize);
00396         Q_ASSERT (written <= writeSize);
00397         if (written > 0)
00398             hasWritten (written);
00399         // If the buffer cannot take all we gave it, then try again later.
00400         if (written < writeSize)
00401             break;
00402     }
00403 }
00404 
00406 
00416 bool TcpSocketBuffer::canWrite (int count) const
00417 {
00418     Q_ASSERT (count >= 0);
00419     return writeEnd + count <= writeStart + writeSize;
00420 }
00421 
00423 
00443 void TcpSocketBuffer::write (const char *source, int size, bool flush)
00444 {
00445     Q_ASSERT (canWrite (size));
00446     int writePos = (writeEnd >= writeSize) ? (writeEnd - writeSize) : writeEnd;
00447     if (writePos + size <= writeSize) {
00448         memcpy (writeBuffer + writeEnd, source, size);
00449     } else {
00450         int count = writeSize - writeEnd;
00451         memcpy (writeBuffer + writePos, source, count);
00452         Q_ASSERT (size - count > 0);
00453         memcpy (writeBuffer, source + count, size - count);
00454     }
00455     writeEnd += size;
00456     if (flush)
00457         writeFlushEnd = writeEnd;
00458 }
00459 
00461 
00471 void TcpSocketBuffer::setWriteBufferSize (int size)
00472 {
00473     Q_ASSERT (size >= 0);
00474     char *newBuffer = 0;
00475     if (size > 0)
00476         newBuffer = new char [size];
00477     delete[] writeBuffer;
00478     writeBuffer = newBuffer;
00479     writeSize = size;
00480     writeStart = writeEnd = writeFlushEnd = 0;
00481 }
00482 
00484 
00487 int TcpSocketBuffer::writeBufferSize() const
00488 {
00489     return writeSize;
00490 }
00491 
00493 
00496 int TcpSocketBuffer::bytesToWrite() const
00497 {
00498     return writeEnd - writeStart;
00499 }
00500 
00502 
00508 char * TcpSocketBuffer::nextWriteChunk() const
00509 {
00510     return writeBuffer + writeStart;
00511 }
00512 
00514 
00517 int TcpSocketBuffer::nextWriteChunkSize() const
00518 {
00519     return std::min (writeSize - writeStart, writeFlushEnd - writeStart);
00520 }
00521 
00523 
00533 void TcpSocketBuffer::hasWritten (int count)
00534 {
00535     Q_ASSERT (count >= 0);
00536     Q_ASSERT (writeStart + count <= writeFlushEnd);
00537     Q_ASSERT (writeFlushEnd <= writeEnd);
00538     writeStart += count;
00539     if (writeStart >= writeSize) {
00540         writeStart -= writeSize;
00541         writeFlushEnd -= writeSize;
00542         writeEnd -= writeSize;
00543     }
00544 }