Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qbuffer.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "qbuffer.h"
6#include <QtCore/qmetaobject.h>
7#include "private/qiodevice_p.h"
8
9#include <limits>
10
11QT_BEGIN_NAMESPACE
12
13/** QBufferPrivate **/
14class QBufferPrivate : public QIODevicePrivate
15{
16 Q_DECLARE_PUBLIC(QBuffer)
17
18public:
19 QBufferPrivate() = default;
20
21 QByteArray *buf = nullptr;
22 QByteArray defaultBuf;
23
24 qint64 peek(char *data, qint64 maxSize) override;
25 QByteArray peek(qint64 maxSize) override;
26
27#ifndef QT_NO_QOBJECT
28 qint64 writtenSinceLastEmit = 0;
29 int signalConnectionCount = 0;
30 bool signalsEmitted = false;
31#endif
32};
33
34qint64 QBufferPrivate::peek(char *data, qint64 maxSize)
35{
36 qint64 readBytes = qMin(maxSize, static_cast<qint64>(buf->size()) - pos);
37 memcpy(data, buf->constData() + pos, readBytes);
38 return readBytes;
39}
40
41QByteArray QBufferPrivate::peek(qint64 maxSize)
42{
43 qint64 readBytes = qMin(maxSize, static_cast<qint64>(buf->size()) - pos);
44 if (pos == 0 && maxSize >= buf->size())
45 return *buf;
46 return QByteArray(buf->constData() + pos, readBytes);
47}
48
49/*!
50 \class QBuffer
51 \inmodule QtCore
52 \reentrant
53 \brief The QBuffer class provides a QIODevice interface for a QByteArray.
54
55 \ingroup io
56
57 QBuffer allows you to access a QByteArray using the QIODevice
58 interface. The QByteArray is treated just as a standard random-accessed
59 file. Example:
60
61 \snippet buffer/buffer.cpp 0
62
63 By default, an internal QByteArray buffer is created for you when
64 you create a QBuffer. You can access this buffer directly by
65 calling buffer(). You can also use QBuffer with an existing
66 QByteArray by calling setBuffer(), or by passing your array to
67 QBuffer's constructor.
68
69 Call open() to open the buffer. Then call write() or
70 putChar() to write to the buffer, and read(), readLine(),
71 readAll(), or getChar() to read from it. size() returns the
72 current size of the buffer, and you can seek to arbitrary
73 positions in the buffer by calling seek(). When you are done with
74 accessing the buffer, call close().
75
76 The following code snippet shows how to write data to a
77 QByteArray using QDataStream and QBuffer:
78
79 \snippet buffer/buffer.cpp 1
80
81 Effectively, we convert the application's QPalette into a byte
82 array. Here's how to read the data from the QByteArray:
83
84 \snippet buffer/buffer.cpp 2
85
86 QTextStream and QDataStream also provide convenience constructors
87 that take a QByteArray and that create a QBuffer behind the
88 scenes.
89
90 QBuffer emits readyRead() when new data has arrived in the
91 buffer. By connecting to this signal, you can use QBuffer to
92 store temporary data before processing it. QBuffer also emits
93 bytesWritten() every time new data has been written to the buffer.
94
95 \sa QFile, QDataStream, QTextStream, QByteArray
96*/
97
98#ifdef QT_NO_QOBJECT
99QBuffer::QBuffer()
100 : QIODevice(*new QBufferPrivate)
101{
102 Q_D(QBuffer);
103 d->buf = &d->defaultBuf;
104}
105QBuffer::QBuffer(QByteArray *buf)
106 : QIODevice(*new QBufferPrivate)
107{
108 Q_D(QBuffer);
109 d->buf = buf ? buf : &d->defaultBuf;
110 d->defaultBuf.clear();
111}
112#else
113/*!
114 Constructs an empty buffer with the given \a parent. You can call
115 setData() to fill the buffer with data, or you can open it in
116 write mode and use write().
117
118 \sa open()
119*/
120QBuffer::QBuffer(QObject *parent)
121 : QIODevice(*new QBufferPrivate, parent)
122{
123 Q_D(QBuffer);
124 d->buf = &d->defaultBuf;
125}
126
127/*!
128 Constructs a QBuffer that uses the QByteArray pointed to by \a
129 byteArray as its internal buffer, and with the given \a parent.
130 The caller is responsible for ensuring that \a byteArray remains
131 valid until the QBuffer is destroyed, or until setBuffer() is
132 called to change the buffer. QBuffer doesn't take ownership of
133 the QByteArray.
134
135 If you open the buffer in write-only mode or read-write mode and
136 write something into the QBuffer, \a byteArray will be modified.
137
138 Example:
139
140 \snippet buffer/buffer.cpp 3
141
142 \sa open(), setBuffer(), setData()
143*/
144QBuffer::QBuffer(QByteArray *byteArray, QObject *parent)
145 : QIODevice(*new QBufferPrivate, parent)
146{
147 Q_D(QBuffer);
148 d->buf = byteArray ? byteArray : &d->defaultBuf;
149 d->defaultBuf.clear();
150}
151#endif
152
153/*!
154 Destroys the buffer.
155*/
156
157QBuffer::~QBuffer()
158{
159}
160
161/*!
162 Makes QBuffer use the QByteArray pointed to by \a
163 byteArray as its internal buffer. The caller is responsible for
164 ensuring that \a byteArray remains valid until the QBuffer is
165 destroyed, or until setBuffer() is called to change the buffer.
166 QBuffer doesn't take ownership of the QByteArray.
167
168 Does nothing if isOpen() is true.
169
170 If you open the buffer in write-only mode or read-write mode and
171 write something into the QBuffer, \a byteArray will be modified.
172
173 Example:
174
175 \snippet buffer/buffer.cpp 4
176
177 If \a byteArray is \nullptr, the buffer creates its own internal
178 QByteArray to work on. This byte array is initially empty.
179
180 \sa buffer(), setData(), open()
181*/
182
183void QBuffer::setBuffer(QByteArray *byteArray)
184{
185 Q_D(QBuffer);
186 if (isOpen()) {
187 qWarning("QBuffer::setBuffer: Buffer is open");
188 return;
189 }
190 if (byteArray) {
191 d->buf = byteArray;
192 } else {
193 d->buf = &d->defaultBuf;
194 }
195 d->defaultBuf.clear();
196}
197
198/*!
199 Returns a reference to the QBuffer's internal buffer. You can use
200 it to modify the QByteArray behind the QBuffer's back.
201
202 \sa setBuffer(), data()
203*/
204
205QByteArray &QBuffer::buffer()
206{
207 Q_D(QBuffer);
208 return *d->buf;
209}
210
211/*!
212 \overload
213
214 This is the same as data().
215*/
216
217const QByteArray &QBuffer::buffer() const
218{
219 Q_D(const QBuffer);
220 return *d->buf;
221}
222
223
224/*!
225 Returns the data contained in the buffer.
226
227 This is the same as buffer().
228
229 \sa setData(), setBuffer()
230*/
231
232const QByteArray &QBuffer::data() const
233{
234 Q_D(const QBuffer);
235 return *d->buf;
236}
237
238/*!
239 Sets the contents of the internal buffer to be \a data. This is
240 the same as assigning \a data to buffer().
241
242 Does nothing if isOpen() is true.
243
244 \sa setBuffer()
245*/
246void QBuffer::setData(const QByteArray &data)
247{
248 Q_D(QBuffer);
249 if (isOpen()) {
250 qWarning("QBuffer::setData: Buffer is open");
251 return;
252 }
253 *d->buf = data;
254}
255
256/*!
257 \overload
258
259 Sets the contents of the internal buffer to be the first \a size
260 bytes of \a data.
261
262 \note In Qt versions prior to 6.5, this function took the length as
263 an \c{int} parameter, potentially truncating sizes.
264*/
265void QBuffer::setData(const char *data, qsizetype size)
266{
267 Q_D(QBuffer);
268 if (isOpen()) {
269 qWarning("QBuffer::setData: Buffer is open");
270 return;
271 }
272 d->buf->assign(data, data + size);
273}
274
275/*!
276 Opens the buffer using \a mode flags, returning \c true if successful;
277 otherwise returns \c false.
278
279 The flags for \a mode must include \l QIODeviceBase::ReadOnly,
280 \l WriteOnly, or \l ReadWrite. If not, an error is printed and
281 the method fails. In any other case, it succeeds.
282
283 Unlike \l QFile::open(), opening a QBuffer with \l WriteOnly
284 does not truncate it. However, \l pos() is set to \c{0}.
285 Use \l Append or \l Truncate to change either behavior.
286*/
287bool QBuffer::open(OpenMode mode)
288{
289 Q_D(QBuffer);
290
291 if ((mode & (Append | Truncate)) != 0)
292 mode |= WriteOnly;
293 if ((mode & (ReadOnly | WriteOnly)) == 0) {
294 qWarning("QBuffer::open: Buffer access not specified");
295 return false;
296 }
297
298 if ((mode & Truncate) == Truncate)
299 d->buf->resize(0);
300
301 return QIODevice::open(mode | QIODevice::Unbuffered);
302}
303
304/*!
305 \reimp
306*/
307void QBuffer::close()
308{
309 QIODevice::close();
310}
311
312/*!
313 \reimp
314*/
315qint64 QBuffer::pos() const
316{
317 return QIODevice::pos();
318}
319
320/*!
321 \reimp
322*/
323qint64 QBuffer::size() const
324{
325 Q_D(const QBuffer);
326 return qint64(d->buf->size());
327}
328
329/*!
330 \reimp
331*/
332bool QBuffer::seek(qint64 pos)
333{
334 Q_D(QBuffer);
335 const auto oldBufSize = d->buf->size();
336 constexpr qint64 MaxSeekPos = (std::numeric_limits<decltype(oldBufSize)>::max)();
337 if (pos <= MaxSeekPos && pos > oldBufSize && isWritable()) {
338 QT_TRY {
339 d->buf->resize(qsizetype(pos), '\0');
340 } QT_CATCH(const std::bad_alloc &) {} // swallow, failure case is handled below
341 if (d->buf->size() != pos) {
342 qWarning("QBuffer::seek: Unable to fill gap");
343 return false;
344 }
345 }
346 if (pos > d->buf->size() || pos < 0) {
347 qWarning("QBuffer::seek: Invalid pos: %lld", pos);
348 return false;
349 }
350 return QIODevice::seek(pos);
351}
352
353/*!
354 \reimp
355*/
356bool QBuffer::atEnd() const
357{
358 return QIODevice::atEnd();
359}
360
361/*!
362 \reimp
363*/
364bool QBuffer::canReadLine() const
365{
366 Q_D(const QBuffer);
367 if (!isOpen())
368 return false;
369
370 return d->buf->indexOf('\n', int(pos())) != -1 || QIODevice::canReadLine();
371}
372
373/*!
374 \reimp
375*/
376qint64 QBuffer::readData(char *data, qint64 len)
377{
378 Q_D(QBuffer);
379 if ((len = qMin(len, qint64(d->buf->size()) - pos())) <= 0)
380 return qint64(0);
381 memcpy(data, d->buf->constData() + pos(), len);
382 return len;
383}
384
385/*!
386 \reimp
387*/
388qint64 QBuffer::writeData(const char *data, qint64 len)
389{
390 Q_D(QBuffer);
391 const quint64 required = quint64(pos()) + quint64(len); // cannot overflow (pos() ≥ 0, len ≥ 0)
392
393 if (required > quint64(d->buf->size())) { // capacity exceeded
394 // The following must hold, since qsizetype covers half the virtual address space:
395 Q_ASSERT(required <= quint64((std::numeric_limits<qsizetype>::max)()));
396 d->buf->resize(qsizetype(required));
397 if (quint64(d->buf->size()) != required) { // could not resize
398 qWarning("QBuffer::writeData: Memory allocation error");
399 return -1;
400 }
401 }
402
403 memcpy(d->buf->data() + pos(), data, size_t(len));
404
405#ifndef QT_NO_QOBJECT
406 d->writtenSinceLastEmit += len;
407 if (d->signalConnectionCount && !d->signalsEmitted && !signalsBlocked()) {
408 d->signalsEmitted = true;
409 QMetaObject::invokeMethod(this, [](QBuffer *q) {
410 auto d = q->d_func();
411 emit q->bytesWritten(d->writtenSinceLastEmit);
412 d->writtenSinceLastEmit = 0;
413 emit q->readyRead();
414 d->signalsEmitted = false;
415 }, Qt::QueuedConnection, this);
416 }
417#endif
418 return len;
419}
420
421#ifndef QT_NO_QOBJECT
422static bool is_tracked_signal(const QMetaMethod &signal)
423{
424 // dynamic initialization: minimize the number of guard variables:
425 static const struct {
426 QMetaMethod readyReadSignal = QMetaMethod::fromSignal(&QBuffer::readyRead);
427 QMetaMethod bytesWrittenSignal = QMetaMethod::fromSignal(&QBuffer::bytesWritten);
428 } sigs;
429 return signal == sigs.readyReadSignal || signal == sigs.bytesWrittenSignal;
430}
431/*!
432 \reimp
433 \internal
434*/
435void QBuffer::connectNotify(const QMetaMethod &signal)
436{
437 if (is_tracked_signal(signal))
438 d_func()->signalConnectionCount++;
439}
440
441/*!
442 \reimp
443 \internal
444*/
445void QBuffer::disconnectNotify(const QMetaMethod &signal)
446{
447 if (signal.isValid()) {
448 if (is_tracked_signal(signal))
449 d_func()->signalConnectionCount--;
450 } else {
451 d_func()->signalConnectionCount = 0;
452 }
453}
454#endif
455
456QT_END_NAMESPACE
457
458#ifndef QT_NO_QOBJECT
459# include "moc_qbuffer.cpp"
460#endif
static bool is_tracked_signal(const QMetaMethod &signal)
Definition qbuffer.cpp:422