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

source code of qtbase/src/corelib/io/qbuffer.cpp