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