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*)0)
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 0;
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 0;
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(0), 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 0;
305 }
306
307 if (currentReadBuffer == 0)
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 0;
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(0) == 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
364 return true;
365}
366
367bool QNonContiguousByteDeviceIoDeviceImpl::atEnd() const
368{
369 return eof == true;
370}
371
372bool QNonContiguousByteDeviceIoDeviceImpl::reset()
373{
374 bool reset = (initialPosition == 0) ? device->reset() : device->seek(initialPosition);
375 if (reset) {
376 eof = false; // assume eof is false, it will be true after a read has been attempted
377 totalAdvancements = 0; //reset the progress counter
378 if (currentReadBuffer) {
379 delete currentReadBuffer;
380 currentReadBuffer = 0;
381 }
382 currentReadBufferAmount = 0;
383 currentReadBufferPosition = 0;
384 return true;
385 }
386
387 return false;
388}
389
390qint64 QNonContiguousByteDeviceIoDeviceImpl::size() const
391{
392 // note that this is different from the size() implementation of QIODevice!
393
394 if (device->isSequential())
395 return -1;
396
397 return device->size() - initialPosition;
398}
399
400qint64 QNonContiguousByteDeviceIoDeviceImpl::pos() const
401{
402 if (device->isSequential())
403 return -1;
404
405 return device->pos();
406}
407
408QByteDeviceWrappingIoDevice::QByteDeviceWrappingIoDevice(QNonContiguousByteDevice *bd) : QIODevice((QObject*)0)
409{
410 byteDevice = bd;
411 connect(bd, SIGNAL(readyRead()), SIGNAL(readyRead()));
412
413 open(ReadOnly);
414}
415
416QByteDeviceWrappingIoDevice::~QByteDeviceWrappingIoDevice()
417{
418
419}
420
421bool QByteDeviceWrappingIoDevice::isSequential() const
422{
423 return (byteDevice->size() == -1);
424}
425
426bool QByteDeviceWrappingIoDevice::atEnd() const
427{
428 return byteDevice->atEnd();
429}
430
431bool QByteDeviceWrappingIoDevice::reset()
432{
433 return byteDevice->reset();
434}
435
436qint64 QByteDeviceWrappingIoDevice::size() const
437{
438 if (isSequential())
439 return 0;
440
441 return byteDevice->size();
442}
443
444
445qint64 QByteDeviceWrappingIoDevice::readData( char * data, qint64 maxSize)
446{
447 qint64 len;
448 const char *readPointer = byteDevice->readPointer(maxSize, len);
449 if (len == -1)
450 return -1;
451
452 memcpy(data, readPointer, len);
453 byteDevice->advanceReadPointer(len);
454 return len;
455}
456
457qint64 QByteDeviceWrappingIoDevice::writeData( const char* data, qint64 maxSize)
458{
459 Q_UNUSED(data);
460 Q_UNUSED(maxSize);
461 return -1;
462}
463
464/*!
465 \class QNonContiguousByteDeviceFactory
466 \inmodule QtCore
467 \since 4.6
468
469 Creates a QNonContiguousByteDevice out of a QIODevice,
470 QByteArray etc.
471
472 \sa QNonContiguousByteDevice
473
474 \internal
475*/
476
477/*!
478 \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QIODevice *device)
479
480 Create a QNonContiguousByteDevice out of a QIODevice.
481 For QFile, QBuffer and all other QIoDevice, sequential or not.
482
483 \internal
484*/
485QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QIODevice *device)
486{
487 // shortcut if it is a QBuffer
488 if (QBuffer* buffer = qobject_cast<QBuffer*>(device)) {
489 return new QNonContiguousByteDeviceBufferImpl(buffer);
490 }
491
492 // ### FIXME special case if device is a QFile that supports map()
493 // then we can actually deal with the file without using read/peek
494
495 // generic QIODevice
496 return new QNonContiguousByteDeviceIoDeviceImpl(device); // FIXME
497}
498
499/*!
500 Create a QNonContiguousByteDevice out of a QIODevice, return it in a QSharedPointer.
501 For QFile, QBuffer and all other QIODevice, sequential or not.
502
503 \internal
504*/
505QSharedPointer<QNonContiguousByteDevice> QNonContiguousByteDeviceFactory::createShared(QIODevice *device)
506{
507 // shortcut if it is a QBuffer
508 if (QBuffer *buffer = qobject_cast<QBuffer*>(device))
509 return QSharedPointer<QNonContiguousByteDeviceBufferImpl>::create(buffer);
510
511 // ### FIXME special case if device is a QFile that supports map()
512 // then we can actually deal with the file without using read/peek
513
514 // generic QIODevice
515 return QSharedPointer<QNonContiguousByteDeviceIoDeviceImpl>::create(device); // FIXME
516}
517
518/*!
519 \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QSharedPointer<QRingBuffer> ringBuffer)
520
521 Create a QNonContiguousByteDevice out of a QRingBuffer.
522
523 \internal
524*/
525QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QSharedPointer<QRingBuffer> ringBuffer)
526{
527 return new QNonContiguousByteDeviceRingBufferImpl(ringBuffer);
528}
529
530/*!
531 Create a QNonContiguousByteDevice out of a QRingBuffer, return it in a QSharedPointer.
532
533 \internal
534*/
535QSharedPointer<QNonContiguousByteDevice> QNonContiguousByteDeviceFactory::createShared(QSharedPointer<QRingBuffer> ringBuffer)
536{
537 return QSharedPointer<QNonContiguousByteDeviceRingBufferImpl>::create(std::move(ringBuffer));
538}
539
540/*!
541 \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QByteArray *byteArray)
542
543 Create a QNonContiguousByteDevice out of a QByteArray.
544
545 \internal
546*/
547QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QByteArray *byteArray)
548{
549 return new QNonContiguousByteDeviceByteArrayImpl(byteArray);
550}
551
552/*!
553 Create a QNonContiguousByteDevice out of a QByteArray.
554
555 \internal
556*/
557QSharedPointer<QNonContiguousByteDevice> QNonContiguousByteDeviceFactory::createShared(QByteArray *byteArray)
558{
559 return QSharedPointer<QNonContiguousByteDeviceByteArrayImpl>::create(byteArray);
560}
561
562/*!
563 \fn static QIODevice* QNonContiguousByteDeviceFactory::wrap(QNonContiguousByteDevice* byteDevice)
564
565 Wrap the \a byteDevice (possibly again) into a QIODevice.
566
567 \internal
568*/
569QIODevice* QNonContiguousByteDeviceFactory::wrap(QNonContiguousByteDevice* byteDevice)
570{
571 // ### FIXME if it already has been based on QIoDevice, we could that one out again
572 // and save some calling
573
574 // needed for FTP backend
575
576 return new QByteDeviceWrappingIoDevice(byteDevice);
577}
578
579QT_END_NAMESPACE
580
581#include "moc_qnoncontiguousbytedevice_p.cpp"
582