/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QRINGBUFFER_P_H #define QRINGBUFFER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists for the convenience // of a number of Qt sources files. This header file may change from // version to version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE class QRingBuffer { public: explicit inline QRingBuffer(int growth = 4096) : head(0), tail(0), tailBuffer(0), basicBlockSize(growth), bufferSize(0) { buffers.append(QByteArray()); } inline int nextDataBlockSize() const { return (tailBuffer == 0 ? tail : buffers.first().size()) - head; } inline const char *readPointer() const { return bufferSize == 0 ? Q_NULLPTR : (buffers.first().constData() + head); } // access the bytes at a specified position // the out-variable length will contain the amount of bytes readable // from there, e.g. the amount still the same QByteArray inline const char *readPointerAtPosition(qint64 pos, qint64 &length) const { if (pos >= 0) { pos += head; for (int i = 0; i < buffers.size(); ++i) { length = (i == tailBuffer ? tail : buffers[i].size()); if (length > pos) { length -= pos; return buffers[i].constData() + pos; } pos -= length; } } length = 0; return 0; } inline void free(int bytes) { while (bytes > 0) { int blockSize = buffers.first().size() - head; if (tailBuffer == 0 || blockSize > bytes) { // keep a single block around if it does not exceed // the basic block size, to avoid repeated allocations // between uses of the buffer if (bufferSize <= bytes) { if (buffers.first().size() <= basicBlockSize) { bufferSize = 0; head = tail = 0; } else { clear(); // try to minify/squeeze us } } else { head += bytes; bufferSize -= bytes; } return; } bufferSize -= blockSize; bytes -= blockSize; buffers.removeFirst(); --tailBuffer; head = 0; } } inline char *reserve(int bytes) { if (bytes <= 0) return 0; // if need buffer reallocation if (tail + bytes > buffers.last().size()) { if (tail + bytes > buffers.last().capacity() && tail >= basicBlockSize) { // shrink this buffer to its current size buffers.last().resize(tail); // create a new QByteArray buffers.append(QByteArray()); ++tailBuffer; tail = 0; } buffers.last().resize(qMax(basicBlockSize, tail + bytes)); } char *writePtr = buffers.last().data() + tail; bufferSize += bytes; tail += bytes; return writePtr; } inline void truncate(int pos) { if (pos < size()) chop(size() - pos); } inline void chop(int bytes) { while (bytes > 0) { if (tailBuffer == 0 || tail > bytes) { // keep a single block around if it does not exceed // the basic block size, to avoid repeated allocations // between uses of the buffer if (bufferSize <= bytes) { if (buffers.first().size() <= basicBlockSize) { bufferSize = 0; head = tail = 0; } else { clear(); // try to minify/squeeze us } } else { tail -= bytes; bufferSize -= bytes; } return; } bufferSize -= tail; bytes -= tail; buffers.removeLast(); --tailBuffer; tail = buffers.last().size(); } } inline bool isEmpty() const { return bufferSize == 0; } inline int getChar() { if (isEmpty()) return -1; char c = *readPointer(); free(1); return int(uchar(c)); } inline void putChar(char c) { char *ptr = reserve(1); *ptr = c; } inline void ungetChar(char c) { --head; if (head < 0) { if (bufferSize != 0) { buffers.prepend(QByteArray()); ++tailBuffer; } else { tail = basicBlockSize; } buffers.first().resize(basicBlockSize); head = basicBlockSize - 1; } buffers.first()[head] = c; ++bufferSize; } inline int size() const { return bufferSize; } inline void clear() { buffers.erase(buffers.begin() + 1, buffers.end()); buffers.first().clear(); head = tail = 0; tailBuffer = 0; bufferSize = 0; } inline int indexOf(char c) const { int index = 0; int j = head; for (int i = 0; i < buffers.size(); ++i) { const char *ptr = buffers[i].constData() + j; j = index + (i == tailBuffer ? tail : buffers[i].size()) - j; while (index < j) { if (*ptr++ == c) return index; ++index; } j = 0; } return -1; } inline int indexOf(char c, int maxLength) const { int index = 0; int j = head; for (int i = 0; index < maxLength && i < buffers.size(); ++i) { const char *ptr = buffers[i].constData() + j; j = qMin(index + (i == tailBuffer ? tail : buffers[i].size()) - j, maxLength); while (index < j) { if (*ptr++ == c) return index; ++index; } j = 0; } return -1; } inline int read(char *data, int maxLength) { int bytesToRead = qMin(size(), maxLength); int readSoFar = 0; while (readSoFar < bytesToRead) { int bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar, nextDataBlockSize()); if (data) memcpy(data + readSoFar, readPointer(), bytesToReadFromThisBlock); readSoFar += bytesToReadFromThisBlock; free(bytesToReadFromThisBlock); } return readSoFar; } // read an unspecified amount (will read the first buffer) inline QByteArray read() { if (bufferSize == 0) return QByteArray(); QByteArray qba(buffers.takeFirst()); qba.reserve(0); // avoid that resizing needlessly reallocates if (tailBuffer == 0) { qba.resize(tail); tail = 0; buffers.append(QByteArray()); } else { --tailBuffer; } qba.remove(0, head); // does nothing if head is 0 head = 0; bufferSize -= qba.size(); return qba; } // append a new buffer to the end inline void append(const QByteArray &qba) { if (tail == 0) { buffers.last() = qba; } else { buffers.last().resize(tail); buffers.append(qba); ++tailBuffer; } tail = qba.size(); bufferSize += tail; } inline int skip(int length) { return read(0, length); } inline int readLine(char *data, int maxLength) { if (!data || --maxLength <= 0) return -1; int i = indexOf('\n', maxLength); i = read(data, i >= 0 ? (i + 1) : maxLength); // Terminate it. data[i] = '\0'; return i; } inline bool canReadLine() const { return indexOf('\n') >= 0; } private: QList buffers; int head, tail; int tailBuffer; // always buffers.size() - 1 const int basicBlockSize; int bufferSize; }; QT_END_NAMESPACE #endif // QRINGBUFFER_P_H