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

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