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 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | /** QBufferPrivate **/ |
13 | class QBufferPrivate : public QIODevicePrivate |
14 | { |
15 | Q_DECLARE_PUBLIC(QBuffer) |
16 | |
17 | public: |
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 |
37 | void 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 | |
47 | qint64 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 | |
54 | QByteArray 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 |
112 | QBuffer::QBuffer() |
113 | : QIODevice(*new QBufferPrivate) |
114 | { |
115 | Q_D(QBuffer); |
116 | d->buf = &d->defaultBuf; |
117 | } |
118 | QBuffer::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 | */ |
133 | QBuffer::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 | */ |
157 | QBuffer::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 | |
170 | QBuffer::~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 | |
196 | void 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 | |
218 | QByteArray &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 | |
230 | const 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 | |
245 | const 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 | */ |
259 | void 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 | */ |
278 | void 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 | */ |
296 | bool 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 | */ |
316 | void QBuffer::close() |
317 | { |
318 | QIODevice::close(); |
319 | } |
320 | |
321 | /*! |
322 | \reimp |
323 | */ |
324 | qint64 QBuffer::pos() const |
325 | { |
326 | return QIODevice::pos(); |
327 | } |
328 | |
329 | /*! |
330 | \reimp |
331 | */ |
332 | qint64 QBuffer::size() const |
333 | { |
334 | Q_D(const QBuffer); |
335 | return qint64(d->buf->size()); |
336 | } |
337 | |
338 | /*! |
339 | \reimp |
340 | */ |
341 | bool 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 | */ |
365 | bool QBuffer::atEnd() const |
366 | { |
367 | return QIODevice::atEnd(); |
368 | } |
369 | |
370 | /*! |
371 | \reimp |
372 | */ |
373 | bool 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 | */ |
385 | qint64 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 | */ |
397 | qint64 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 |
425 | static 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 | */ |
438 | void QBuffer::connectNotify(const QMetaMethod &signal) |
439 | { |
440 | if (is_tracked_signal(signal)) |
441 | d_func()->signalConnectionCount++; |
442 | } |
443 | |
444 | /*! |
445 | \reimp |
446 | \internal |
447 | */ |
448 | void 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 | |
459 | QT_END_NAMESPACE |
460 | |
461 | #ifndef QT_NO_QOBJECT |
462 | # include "moc_qbuffer.cpp" |
463 | #endif |
464 | |
465 | |