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->assign(first: data, last: data + size);
286}
287
288/*!
289 Opens the buffer using \a mode flags, returning \c true if successful;
290 otherwise returns \c false.
291
292 The flags for \a mode must include \l QIODeviceBase::ReadOnly,
293 \l WriteOnly, or \l ReadWrite. If not, an error is printed and
294 the method fails. In any other case, it succeeds.
295
296 Unlike \l QFile::open(), opening a QBuffer with \l WriteOnly
297 does not truncate it. However, \l pos() is set to \c{0}.
298 Use \l Append or \l Truncate to change either behavior.
299*/
300bool QBuffer::open(OpenMode mode)
301{
302 Q_D(QBuffer);
303
304 if ((mode & (Append | Truncate)) != 0)
305 mode |= WriteOnly;
306 if ((mode & (ReadOnly | WriteOnly)) == 0) {
307 qWarning(msg: "QBuffer::open: Buffer access not specified");
308 return false;
309 }
310
311 if ((mode & Truncate) == Truncate)
312 d->buf->resize(size: 0);
313
314 return QIODevice::open(mode: mode | QIODevice::Unbuffered);
315}
316
317/*!
318 \reimp
319*/
320void QBuffer::close()
321{
322 QIODevice::close();
323}
324
325/*!
326 \reimp
327*/
328qint64 QBuffer::pos() const
329{
330 return QIODevice::pos();
331}
332
333/*!
334 \reimp
335*/
336qint64 QBuffer::size() const
337{
338 Q_D(const QBuffer);
339 return qint64(d->buf->size());
340}
341
342/*!
343 \reimp
344*/
345bool QBuffer::seek(qint64 pos)
346{
347 Q_D(QBuffer);
348 const auto oldBufSize = d->buf->size();
349 constexpr qint64 MaxSeekPos = (std::numeric_limits<decltype(oldBufSize)>::max)();
350 if (pos <= MaxSeekPos && pos > oldBufSize && isWritable()) {
351 QT_TRY {
352 d->buf->resize(size: qsizetype(pos), c: '\0');
353 } QT_CATCH(const std::bad_alloc &) {} // swallow, failure case is handled below
354 if (d->buf->size() != pos) {
355 qWarning(msg: "QBuffer::seek: Unable to fill gap");
356 return false;
357 }
358 }
359 if (pos > d->buf->size() || pos < 0) {
360 qWarning(msg: "QBuffer::seek: Invalid pos: %lld", pos);
361 return false;
362 }
363 return QIODevice::seek(pos);
364}
365
366/*!
367 \reimp
368*/
369bool QBuffer::atEnd() const
370{
371 return QIODevice::atEnd();
372}
373
374/*!
375 \reimp
376*/
377bool QBuffer::canReadLine() const
378{
379 Q_D(const QBuffer);
380 if (!isOpen())
381 return false;
382
383 return d->buf->indexOf(c: '\n', from: int(pos())) != -1 || QIODevice::canReadLine();
384}
385
386/*!
387 \reimp
388*/
389qint64 QBuffer::readData(char *data, qint64 len)
390{
391 Q_D(QBuffer);
392 if ((len = qMin(a: len, b: qint64(d->buf->size()) - pos())) <= 0)
393 return qint64(0);
394 memcpy(dest: data, src: d->buf->constData() + pos(), n: len);
395 return len;
396}
397
398/*!
399 \reimp
400*/
401qint64 QBuffer::writeData(const char *data, qint64 len)
402{
403 Q_D(QBuffer);
404 const quint64 required = quint64(pos()) + quint64(len); // cannot overflow (pos() ≥ 0, len ≥ 0)
405
406 if (required > quint64(d->buf->size())) { // capacity exceeded
407 // The following must hold, since qsizetype covers half the virtual address space:
408 Q_ASSERT(required <= quint64((std::numeric_limits<qsizetype>::max)()));
409 d->buf->resize(size: qsizetype(required));
410 if (quint64(d->buf->size()) != required) { // could not resize
411 qWarning(msg: "QBuffer::writeData: Memory allocation error");
412 return -1;
413 }
414 }
415
416 memcpy(dest: d->buf->data() + pos(), src: data, n: size_t(len));
417
418#ifndef QT_NO_QOBJECT
419 d->writtenSinceLastEmit += len;
420 if (d->signalConnectionCount && !d->signalsEmitted && !signalsBlocked()) {
421 d->signalsEmitted = true;
422 QMetaObject::invokeMethod(obj: this, member: "_q_emitSignals", c: Qt::QueuedConnection);
423 }
424#endif
425 return len;
426}
427
428#ifndef QT_NO_QOBJECT
429static bool is_tracked_signal(const QMetaMethod &signal)
430{
431 // dynamic initialization: minimize the number of guard variables:
432 static const struct {
433 QMetaMethod readyReadSignal = QMetaMethod::fromSignal(signal: &QBuffer::readyRead);
434 QMetaMethod bytesWrittenSignal = QMetaMethod::fromSignal(signal: &QBuffer::bytesWritten);
435 } sigs;
436 return signal == sigs.readyReadSignal || signal == sigs.bytesWrittenSignal;
437}
438/*!
439 \reimp
440 \internal
441*/
442void QBuffer::connectNotify(const QMetaMethod &signal)
443{
444 if (is_tracked_signal(signal))
445 d_func()->signalConnectionCount++;
446}
447
448/*!
449 \reimp
450 \internal
451*/
452void QBuffer::disconnectNotify(const QMetaMethod &signal)
453{
454 if (signal.isValid()) {
455 if (is_tracked_signal(signal))
456 d_func()->signalConnectionCount--;
457 } else {
458 d_func()->signalConnectionCount = 0;
459 }
460}
461#endif
462
463QT_END_NAMESPACE
464
465#ifndef QT_NO_QOBJECT
466# include "moc_qbuffer.cpp"
467#endif
468
469

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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