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 "qnoncontiguousbytedevice_p.h"
5#include <qbuffer.h>
6#include <qdebug.h>
7#include <qfile.h>
8
9QT_BEGIN_NAMESPACE
10
11/*!
12 \class QNonContiguousByteDevice
13 \inmodule QtCore
14 \brief A QNonContiguousByteDevice is a representation of a
15 file, array or buffer that allows access with a read pointer.
16 \since 4.6
17
18 The goal of this class is to have a data representation that
19 allows us to avoid doing a memcpy as we have to do with QIODevice.
20
21 \sa QNonContiguousByteDeviceFactory
22
23 \internal
24*/
25/*!
26 \fn virtual const char* QNonContiguousByteDevice::readPointer(qint64 maximumLength, qint64 &len)
27
28 Return a byte pointer for at most \a maximumLength bytes of that device.
29 if \a maximumLength is -1, the caller does not care about the length and
30 the device may return what it desires to.
31 The actual number of bytes the pointer is valid for is returned in
32 the \a len variable.
33 \a len will be -1 if EOF or an error occurs.
34 If it was really EOF can then afterwards be checked with atEnd()
35 Returns 0 if it is not possible to read at that position.
36
37 \sa atEnd()
38
39 \internal
40*/
41/*!
42 \fn virtual bool QNonContiguousByteDevice::advanceReadPointer(qint64 amount)
43
44 will advance the internal read pointer by \a amount bytes.
45 The old readPointer is invalid after this call.
46
47 \sa readPointer()
48
49 \internal
50*/
51/*!
52 \fn virtual bool QNonContiguousByteDevice::atEnd() const
53
54 Returns \c true if everything has been read and the read
55 pointer cannot be advanced anymore.
56
57 \sa readPointer(), advanceReadPointer(), reset()
58
59 \internal
60*/
61/*!
62 \fn virtual bool QNonContiguousByteDevice::reset()
63
64 Moves the internal read pointer back to the beginning.
65 Returns \c false if this was not possible.
66
67 \sa atEnd()
68
69 \internal
70*/
71/*!
72 \fn virtual qint64 QNonContiguousByteDevice::size() const
73
74 Returns the size of the complete device or -1 if unknown.
75 May also return less/more than what can be actually read with readPointer()
76
77 \internal
78*/
79/*!
80 \fn void QNonContiguousByteDevice::readyRead()
81
82 Emitted when there is data available
83
84 \internal
85*/
86/*!
87 \fn void QNonContiguousByteDevice::readProgress(qint64 current, qint64 total)
88
89 Emitted when data has been "read" by advancing the read pointer
90
91 \internal
92*/
93
94QNonContiguousByteDevice::QNonContiguousByteDevice() : QObject((QObject*)nullptr)
95{
96}
97
98QNonContiguousByteDevice::~QNonContiguousByteDevice()
99{
100}
101
102// FIXME we should scrap this whole implementation and instead change the ByteArrayImpl to be able to cope with sub-arrays?
103QNonContiguousByteDeviceBufferImpl::QNonContiguousByteDeviceBufferImpl(QBuffer *b) : QNonContiguousByteDevice()
104{
105 buffer = b;
106 byteArray = QByteArray::fromRawData(data: buffer->buffer().constData() + buffer->pos(), size: buffer->size() - buffer->pos());
107 arrayImpl = new QNonContiguousByteDeviceByteArrayImpl(&byteArray);
108 arrayImpl->setParent(this);
109 connect(asender: arrayImpl, SIGNAL(readyRead()), SIGNAL(readyRead()));
110 connect(asender: arrayImpl, SIGNAL(readProgress(qint64,qint64)), SIGNAL(readProgress(qint64,qint64)));
111}
112
113QNonContiguousByteDeviceBufferImpl::~QNonContiguousByteDeviceBufferImpl()
114{
115}
116
117const char* QNonContiguousByteDeviceBufferImpl::readPointer(qint64 maximumLength, qint64 &len)
118{
119 return arrayImpl->readPointer(maximumLength, len);
120}
121
122bool QNonContiguousByteDeviceBufferImpl::advanceReadPointer(qint64 amount)
123{
124 return arrayImpl->advanceReadPointer(amount);
125}
126
127bool QNonContiguousByteDeviceBufferImpl::atEnd() const
128{
129 return arrayImpl->atEnd();
130}
131
132bool QNonContiguousByteDeviceBufferImpl::reset()
133{
134 return arrayImpl->reset();
135}
136
137qint64 QNonContiguousByteDeviceBufferImpl::size() const
138{
139 return arrayImpl->size();
140}
141
142QNonContiguousByteDeviceByteArrayImpl::QNonContiguousByteDeviceByteArrayImpl(QByteArray *ba) : QNonContiguousByteDevice(), currentPosition(0)
143{
144 byteArray = ba;
145}
146
147QNonContiguousByteDeviceByteArrayImpl::~QNonContiguousByteDeviceByteArrayImpl()
148{
149}
150
151const char* QNonContiguousByteDeviceByteArrayImpl::readPointer(qint64 maximumLength, qint64 &len)
152{
153 if (atEnd()) {
154 len = -1;
155 return nullptr;
156 }
157
158 if (maximumLength != -1)
159 len = qMin(a: maximumLength, b: size() - currentPosition);
160 else
161 len = size() - currentPosition;
162
163 return byteArray->constData() + currentPosition;
164}
165
166bool QNonContiguousByteDeviceByteArrayImpl::advanceReadPointer(qint64 amount)
167{
168 currentPosition += amount;
169 emit readProgress(current: currentPosition, total: size());
170 return true;
171}
172
173bool QNonContiguousByteDeviceByteArrayImpl::atEnd() const
174{
175 return currentPosition >= size();
176}
177
178bool QNonContiguousByteDeviceByteArrayImpl::reset()
179{
180 currentPosition = 0;
181 return true;
182}
183
184qint64 QNonContiguousByteDeviceByteArrayImpl::size() const
185{
186 return byteArray->size();
187}
188
189qint64 QNonContiguousByteDeviceByteArrayImpl::pos() const
190{
191 return currentPosition;
192}
193
194QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(std::shared_ptr<QRingBuffer> rb)
195 : QNonContiguousByteDevice(), ringBuffer(std::move(rb))
196{
197}
198
199QNonContiguousByteDeviceRingBufferImpl::~QNonContiguousByteDeviceRingBufferImpl()
200{
201}
202
203const char* QNonContiguousByteDeviceRingBufferImpl::readPointer(qint64 maximumLength, qint64 &len)
204{
205 if (atEnd()) {
206 len = -1;
207 return nullptr;
208 }
209
210 const char *returnValue = ringBuffer->readPointerAtPosition(pos: currentPosition, length&: len);
211
212 if (maximumLength != -1)
213 len = qMin(a: len, b: maximumLength);
214
215 return returnValue;
216}
217
218bool QNonContiguousByteDeviceRingBufferImpl::advanceReadPointer(qint64 amount)
219{
220 currentPosition += amount;
221 emit readProgress(current: currentPosition, total: size());
222 return true;
223}
224
225bool QNonContiguousByteDeviceRingBufferImpl::atEnd() const
226{
227 return currentPosition >= size();
228}
229
230qint64 QNonContiguousByteDeviceRingBufferImpl::pos() const
231{
232 return currentPosition;
233}
234
235bool QNonContiguousByteDeviceRingBufferImpl::reset()
236{
237 currentPosition = 0;
238 return true;
239}
240
241qint64 QNonContiguousByteDeviceRingBufferImpl::size() const
242{
243 return ringBuffer->size();
244}
245
246QNonContiguousByteDeviceIoDeviceImpl::QNonContiguousByteDeviceIoDeviceImpl(QIODevice *d)
247 : QNonContiguousByteDevice(),
248 currentReadBuffer(nullptr), currentReadBufferSize(16*1024),
249 currentReadBufferAmount(0), currentReadBufferPosition(0), totalAdvancements(0),
250 eof(false)
251{
252 device = d;
253 initialPosition = d->pos();
254 connect(sender: device, SIGNAL(readyRead()), receiver: this, SIGNAL(readyRead()), Qt::QueuedConnection);
255 connect(sender: device, SIGNAL(readChannelFinished()), receiver: this, SIGNAL(readyRead()), Qt::QueuedConnection);
256}
257
258QNonContiguousByteDeviceIoDeviceImpl::~QNonContiguousByteDeviceIoDeviceImpl()
259{
260 delete currentReadBuffer;
261}
262
263const char *QNonContiguousByteDeviceIoDeviceImpl::readPointer(qint64 maximumLength, qint64 &len)
264{
265 if (eof == true) {
266 len = -1;
267 return nullptr;
268 }
269
270 if (currentReadBuffer == nullptr)
271 currentReadBuffer = new QByteArray(currentReadBufferSize, '\0'); // lazy alloc
272
273 if (maximumLength == -1)
274 maximumLength = currentReadBufferSize;
275
276 if (currentReadBufferAmount - currentReadBufferPosition > 0) {
277 len = currentReadBufferAmount - currentReadBufferPosition;
278 return currentReadBuffer->data() + currentReadBufferPosition;
279 }
280
281 qint64 haveRead = device->read(data: currentReadBuffer->data(), maxlen: qMin(a: maximumLength, b: currentReadBufferSize));
282
283 if ((haveRead == -1) || (haveRead == 0 && device->atEnd() && !device->isSequential())) {
284 eof = true;
285 len = -1;
286 // size was unknown before, emit a readProgress with the final size
287 if (size() == -1)
288 emit readProgress(current: totalAdvancements, total: totalAdvancements);
289 return nullptr;
290 }
291
292 currentReadBufferAmount = haveRead;
293 currentReadBufferPosition = 0;
294
295 len = haveRead;
296 return currentReadBuffer->data();
297}
298
299bool QNonContiguousByteDeviceIoDeviceImpl::advanceReadPointer(qint64 amount)
300{
301 totalAdvancements += amount;
302
303 // normal advancement
304 currentReadBufferPosition += amount;
305
306 if (size() == -1)
307 emit readProgress(current: totalAdvancements, total: totalAdvancements);
308 else
309 emit readProgress(current: totalAdvancements, total: size());
310
311 // advancing over that what has actually been read before
312 if (currentReadBufferPosition > currentReadBufferAmount) {
313 qint64 i = currentReadBufferPosition - currentReadBufferAmount;
314 while (i > 0) {
315 if (device->getChar(c: nullptr) == false) {
316 emit readProgress(current: totalAdvancements - i, total: size());
317 return false; // ### FIXME handle eof
318 }
319 i--;
320 }
321
322 currentReadBufferPosition = 0;
323 currentReadBufferAmount = 0;
324 }
325
326 return true;
327}
328
329bool QNonContiguousByteDeviceIoDeviceImpl::atEnd() const
330{
331 return eof == true;
332}
333
334bool QNonContiguousByteDeviceIoDeviceImpl::reset()
335{
336 bool reset = (initialPosition == 0) ? device->reset() : device->seek(pos: initialPosition);
337 if (reset) {
338 eof = false; // assume eof is false, it will be true after a read has been attempted
339 totalAdvancements = 0; // reset the progress counter
340 if (currentReadBuffer) {
341 delete currentReadBuffer;
342 currentReadBuffer = nullptr;
343 }
344 currentReadBufferAmount = 0;
345 currentReadBufferPosition = 0;
346 return true;
347 }
348
349 return false;
350}
351
352qint64 QNonContiguousByteDeviceIoDeviceImpl::size() const
353{
354 // note that this is different from the size() implementation of QIODevice!
355
356 if (device->isSequential())
357 return -1;
358
359 return device->size() - initialPosition;
360}
361
362qint64 QNonContiguousByteDeviceIoDeviceImpl::pos() const
363{
364 if (device->isSequential())
365 return -1;
366
367 return device->pos();
368}
369
370QByteDeviceWrappingIoDevice::QByteDeviceWrappingIoDevice(QNonContiguousByteDevice *bd) : QIODevice((QObject*)nullptr)
371{
372 byteDevice = bd;
373 connect(asender: bd, SIGNAL(readyRead()), SIGNAL(readyRead()));
374
375 open(mode: ReadOnly);
376}
377
378QByteDeviceWrappingIoDevice::~QByteDeviceWrappingIoDevice()
379{
380
381}
382
383bool QByteDeviceWrappingIoDevice::isSequential() const
384{
385 return (byteDevice->size() == -1);
386}
387
388bool QByteDeviceWrappingIoDevice::atEnd() const
389{
390 return byteDevice->atEnd();
391}
392
393bool QByteDeviceWrappingIoDevice::reset()
394{
395 return byteDevice->reset();
396}
397
398qint64 QByteDeviceWrappingIoDevice::size() const
399{
400 if (isSequential())
401 return 0;
402
403 return byteDevice->size();
404}
405
406qint64 QByteDeviceWrappingIoDevice::readData(char *data, qint64 maxSize)
407{
408 qint64 len;
409 const char *readPointer = byteDevice->readPointer(maximumLength: maxSize, len);
410 if (len == -1)
411 return -1;
412
413 memcpy(dest: data, src: readPointer, n: len);
414 byteDevice->advanceReadPointer(amount: len);
415 return len;
416}
417
418qint64 QByteDeviceWrappingIoDevice::writeData(const char *data, qint64 maxSize)
419{
420 Q_UNUSED(data);
421 Q_UNUSED(maxSize);
422 return -1;
423}
424
425/*!
426 \class QNonContiguousByteDeviceFactory
427 \inmodule QtCore
428 \since 4.6
429
430 Creates a QNonContiguousByteDevice out of a QIODevice,
431 QByteArray etc.
432
433 \sa QNonContiguousByteDevice
434
435 \internal
436*/
437
438/*!
439 \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QIODevice *device)
440
441 Create a QNonContiguousByteDevice out of a QIODevice.
442 For QFile, QBuffer and all other QIoDevice, sequential or not.
443
444 \internal
445*/
446QNonContiguousByteDevice *QNonContiguousByteDeviceFactory::create(QIODevice *device)
447{
448 // shortcut if it is a QBuffer
449 if (QBuffer *buffer = qobject_cast<QBuffer *>(object: device)) {
450 return new QNonContiguousByteDeviceBufferImpl(buffer);
451 }
452
453 // ### FIXME special case if device is a QFile that supports map()
454 // then we can actually deal with the file without using read/peek
455
456 // generic QIODevice
457 return new QNonContiguousByteDeviceIoDeviceImpl(device); // FIXME
458}
459
460/*!
461 Create a QNonContiguousByteDevice out of a QIODevice, return it in a std::shared_ptr.
462 For QFile, QBuffer and all other QIODevice, sequential or not.
463
464 \internal
465*/
466std::shared_ptr<QNonContiguousByteDevice> QNonContiguousByteDeviceFactory::createShared(QIODevice *device)
467{
468 // shortcut if it is a QBuffer
469 if (QBuffer *buffer = qobject_cast<QBuffer*>(object: device))
470 return std::make_shared<QNonContiguousByteDeviceBufferImpl>(args&: buffer);
471
472 // ### FIXME special case if device is a QFile that supports map()
473 // then we can actually deal with the file without using read/peek
474
475 // generic QIODevice
476 return std::make_shared<QNonContiguousByteDeviceIoDeviceImpl>(args&: device); // FIXME
477}
478
479/*!
480 \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(std::shared_ptr<QRingBuffer> ringBuffer)
481
482 Create a QNonContiguousByteDevice out of a QRingBuffer.
483
484 \internal
485*/
486QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(std::shared_ptr<QRingBuffer> ringBuffer)
487{
488 return new QNonContiguousByteDeviceRingBufferImpl(ringBuffer);
489}
490
491/*!
492 Create a QNonContiguousByteDevice out of a QRingBuffer, return it in a std::shared_ptr.
493
494 \internal
495*/
496std::shared_ptr<QNonContiguousByteDevice> QNonContiguousByteDeviceFactory::createShared(std::shared_ptr<QRingBuffer> ringBuffer)
497{
498 return std::make_shared<QNonContiguousByteDeviceRingBufferImpl>(args: std::move(ringBuffer));
499}
500
501/*!
502 \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QByteArray *byteArray)
503
504 Create a QNonContiguousByteDevice out of a QByteArray.
505
506 \internal
507*/
508QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QByteArray *byteArray)
509{
510 return new QNonContiguousByteDeviceByteArrayImpl(byteArray);
511}
512
513/*!
514 Create a QNonContiguousByteDevice out of a QByteArray.
515
516 \internal
517*/
518std::shared_ptr<QNonContiguousByteDevice> QNonContiguousByteDeviceFactory::createShared(QByteArray *byteArray)
519{
520 return std::make_shared<QNonContiguousByteDeviceByteArrayImpl>(args&: byteArray);
521}
522
523/*!
524 \fn static QIODevice* QNonContiguousByteDeviceFactory::wrap(QNonContiguousByteDevice* byteDevice)
525
526 Wrap the \a byteDevice (possibly again) into a QIODevice.
527
528 \internal
529*/
530QIODevice *QNonContiguousByteDeviceFactory::wrap(QNonContiguousByteDevice *byteDevice)
531{
532 // ### FIXME if it already has been based on QIoDevice, we could that one out again
533 // and save some calling
534
535 // needed for FTP backend
536
537 return new QByteDeviceWrappingIoDevice(byteDevice);
538}
539
540QT_END_NAMESPACE
541
542#include "moc_qnoncontiguousbytedevice_p.cpp"
543

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