/**************************************************************************** ** ** Copyright (C) 2011-2012 Denis Shienkov ** Copyright (C) 2011 Sergey Belyashov ** Copyright (C) 2012 Laszlo Papp ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtSerialPort 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 QSERIALPORT_P_H #define QSERIALPORT_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "qserialport.h" QT_BEGIN_NAMESPACE class QGCRingBuffer { public: explicit inline QGCRingBuffer(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 buffers.isEmpty() ? 0 : (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) { bufferSize -= bytes; if (bufferSize <= 0) clear(); // try to minify/squeeze us else head += 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 >= 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) { bufferSize -= bytes; if (bufferSize <= 0) clear(); // try to minify/squeeze us else tail -= bytes; return; } bufferSize -= tail; bytes -= tail; buffers.removeLast(); --tailBuffer; tail = buffers.last().size(); } } inline bool isEmpty() const { return tailBuffer == 0 && tail == 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) { buffers.prepend(QByteArray()); buffers.first().resize(basicBlockSize); head = basicBlockSize - 1; ++tailBuffer; } 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; }; class QSerialPortPrivateData { public: enum IoConstants { ReadChunkSize = 512 }; QSerialPortPrivateData(QSerialPort *q); int timeoutValue(int msecs, int elapsed); qint64 readBufferMaxSize; QGCRingBuffer readBuffer; QGCRingBuffer writeBuffer; QSerialPort::SerialPortError error; QString systemLocation; qint32 inputBaudRate; qint32 outputBaudRate; QSerialPort::DataBits dataBits; QSerialPort::Parity parity; QSerialPort::StopBits stopBits; QSerialPort::FlowControl flowControl; QSerialPort::DataErrorPolicy policy; bool settingsRestoredOnClose; QSerialPort * const q_ptr; }; QT_END_NAMESPACE #endif // QSERIALPORT_P_H