File: storagewriteoperation.cpp

package info (click to toggle)
qflipper 1.3.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 12,320 kB
  • sloc: cpp: 18,500; sh: 247; ansic: 191; xml: 38; python: 14; makefile: 5
file content (98 lines) | stat: -rw-r--r-- 3,180 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include "storagewriteoperation.h"

#include <QIODevice>

#include "protobufplugininterface.h"
#include "statusresponseinterface.h"
#include "mainresponseinterface.h"

static constexpr qint64 CHUNK_SIZE = 1024;
static constexpr qint64 CHUNKS_PER_PING_CAP = 1000;

using namespace Flipper;
using namespace Zero;

StorageWriteOperation::StorageWriteOperation(uint32_t id, const QByteArray &path, QIODevice *file, QObject *parent):
    AbstractStorageOperation(id, path, parent),
    m_file(file),
    m_subRequest(StorageWrite),
    m_chunksPerPing(0),
    m_chunksWritten(0)
{
    connect(this, &AbstractOperation::finished, m_file, [=]() {
        m_file->close();
    });
}

const QString StorageWriteOperation::description() const
{
    return QStringLiteral("Storage Write @%1").arg(QString(path()));
}

bool StorageWriteOperation::hasMoreData() const
{
    return m_file->bytesAvailable() > 0;
}

// Custom feedResponse() implementation to accommodate interspersed pings
void StorageWriteOperation::feedResponse(QObject *response)
{
    auto *mainResponse = qobject_cast<MainResponseInterface*>(response);

    if(mainResponse->isError()) {
        finishWithError(BackendError::ProtocolError, QStringLiteral("Device replied with error: %1").arg(mainResponse->errorString()));
    } else if(!processResponse(response)) {
        finishWithError(BackendError::ProtocolError, QStringLiteral("Operation finished with error: %1").arg(mainResponse->errorString()));
    } else if(!qobject_cast<StatusPingResponseInterface*>(response)) {
        finish();
    } else {
        startTimeout();
    }
}

const QByteArray StorageWriteOperation::encodeRequest(ProtobufPluginInterface *encoder)
{
    if(m_subRequest == StorageWrite) {
        // Never true for files smaller than 100 chunks
        // Such small files don't get progress reports at all
        if(++m_chunksWritten == m_chunksPerPing) {
            m_chunksWritten = 0;
            m_subRequest = StatusPing;
        }

        const auto buf = m_file->read(CHUNK_SIZE);
        const auto hasNext = m_file->bytesAvailable() > 0;
        return encoder->storageWrite(id(), path(), buf, hasNext);

    } else if(m_subRequest == StatusPing) {
        m_subRequest = StorageWrite;
        return encoder->statusPing(id());
    }

    return QByteArray();
}

bool StorageWriteOperation::begin()
{
    if(!m_file->open(QIODevice::ReadOnly)) {
        setError(BackendError::DiskError, QStringLiteral("Failed to open file for reading: %1").arg(m_file->errorString()));
        return false;
    }

    const auto fileSize = m_file->bytesAvailable();

    if(fileSize >= CHUNK_SIZE * 100) {
        // Insert a ping for roughly each 1% of the file size
        m_chunksPerPing = qMin<qint64>(fileSize / (CHUNK_SIZE * 100), CHUNKS_PER_PING_CAP);
        m_percentPerPing = 100.0 * (double)(CHUNK_SIZE * m_chunksPerPing) / fileSize;
    }

    return true;
}

bool StorageWriteOperation::processResponse(QObject *response)
{
    setProgress(m_chunksPerPing ? progress() + m_percentPerPing : 100.0);
    return qobject_cast<StatusPingResponseInterface*>(response) ||
           qobject_cast<EmptyResponseInterface*>(response);
}