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

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