Skip to content

Commit 7edacc5

Browse files
committed
fix: Refactor file saving with memory validation
- Added memory checks before document processing - Implemented TextFileSaver class for safer handling - Optimized with chunked writes and progress events Log: Improved file saving reliability with memory checks.
1 parent fa0511a commit 7edacc5

5 files changed

Lines changed: 152 additions & 270 deletions

File tree

src/common/text_file_saver.cpp

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
*/
99

1010
#include "text_file_saver.h"
11+
#include "utils.h"
1112
#include "../encodes/detectcode.h"
1213

1314
#include <QSaveFile>
15+
#include <QApplication>
1416
#include <QFileInfo>
1517
#include <QTextCodec>
1618
#include <QDebug>
@@ -122,6 +124,14 @@ bool TextFileSaver::saveToFile(QFileDevice &file)
122124
return false;
123125
}
124126

127+
auto characterCount = m_document->characterCount();
128+
// Check memory for document content (QChar is 2 bytes)
129+
qlonglong docMemoryNeeded = characterCount * 2;
130+
if (!Utils::isMemorySufficientForOperation(Utils::OperationType::RawOperation, docMemoryNeeded, characterCount)) {
131+
m_errorString = QObject::tr("Insufficient memory to load document content");
132+
return false;
133+
}
134+
125135
const QString content = m_document->toPlainText();
126136
const ushort *data = content.utf16();
127137
const int length = content.length();
@@ -132,8 +142,16 @@ bool TextFileSaver::saveToFile(QFileDevice &file)
132142
for (int i = 0; i < length; i += chunkSize) {
133143
int currentChunkSize = qMin(chunkSize, length - i);
134144
QByteArray input(reinterpret_cast<const char *>(data + i), currentChunkSize * sizeof(ushort));
135-
QByteArray outData;
136145

146+
// Check memory for encoding conversion (input + estimated output size)
147+
qlonglong conversionMemoryNeeded = input.size() * 2; // Estimate 2x for worst case
148+
if (!Utils::isMemorySufficientForOperation(
149+
Utils::OperationType::RawOperation, conversionMemoryNeeded, characterCount)) {
150+
m_errorString = QObject::tr("Insufficient memory for encoding conversion");
151+
return false;
152+
}
153+
154+
QByteArray outData;
137155
if (!convertEncoding(input, outData)) {
138156
m_errorString = QObject::tr("Encoding conversion failed");
139157
return false;
@@ -144,6 +162,8 @@ bool TextFileSaver::saveToFile(QFileDevice &file)
144162
return false;
145163
}
146164

165+
QApplication::processEvents();
166+
147167
qint64 written = file.write(outData);
148168
if (written != outData.size()) {
149169
m_errorString = file.errorString();
@@ -164,15 +184,6 @@ bool TextFileSaver::saveToFile(QFileDevice &file)
164184
}
165185
}
166186

167-
/**
168-
* @brief Gets the document content as plain text
169-
* @return The document content as QString
170-
*/
171-
QString TextFileSaver::getDocumentContent() const
172-
{
173-
return m_document->toPlainText();
174-
}
175-
176187
/**
177188
* @brief Converts between character encodings
178189
* @param input The input byte array to convert

src/common/text_file_saver.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ class TextFileSaver
2525

2626
private:
2727
bool saveToFile(QFileDevice &file);
28-
QString getDocumentContent() const;
2928
bool convertEncoding(QByteArray &input, QByteArray &output) const;
3029

3130
private:

src/common/utils.cpp

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,38 +1191,55 @@ bool Utils::isMemorySufficientForOperation(OperationType operationType, qlonglon
11911191
qlonglong totalMemoryBytes = memoryTotal * DATA_SIZE_1024;
11921192

11931193
// Judge based on operation type
1194-
if (operationType == OperationType::CopyOperation) {
1195-
// Copy operation: Estimated memory consumption = data size * factor
1196-
qlonglong estimatedMemoryNeeded = operationDataSize * COPY_CONSUME_MEMORY_MULTIPLE;
1197-
if (estimatedMemoryNeeded > availableMemoryBytes) {
1198-
qWarning() << "Utils: Insufficient memory for copy operation. Needed(est):" << estimatedMemoryNeeded << "Available:" << availableMemoryBytes;
1199-
return false;
1200-
}
1201-
} else if (operationType == OperationType::PasteOperation) {
1202-
// Paste operation: Estimated memory consumption = paste data size * factor
1203-
qlonglong estimatedMemoryNeededForPaste = operationDataSize * PASTE_CONSUME_MEMORY_MULTIPLE;
1204-
// Estimated total document memory after paste
1205-
qlonglong estimatedTotalDocMemory = (currentDocumentSize + operationDataSize) * PASTE_CONSUME_MEMORY_MULTIPLE; // Estimate using paste factor
1206-
1207-
// Check if pasting the data itself would cause insufficient memory
1208-
if (estimatedMemoryNeededForPaste > availableMemoryBytes) {
1209-
qWarning() << "Utils: Insufficient memory for paste operation (paste data). Needed(est):" << estimatedMemoryNeededForPaste << "Available:" << availableMemoryBytes;
1210-
return false;
1194+
switch (operationType) {
1195+
case OperationType::RawOperation:
1196+
// Raw operation: Directly compare the data size with available memory
1197+
if (operationDataSize > availableMemoryBytes) {
1198+
qWarning() << "Utils: Insufficient memory for raw operation. Needed:" << operationDataSize << "Available:" << availableMemoryBytes;
1199+
return false;
1200+
}
1201+
break;
1202+
1203+
case OperationType::CopyOperation: {
1204+
// Copy operation: Estimated memory consumption = data size * factor
1205+
qlonglong estimatedMemoryNeeded = operationDataSize * COPY_CONSUME_MEMORY_MULTIPLE;
1206+
if (estimatedMemoryNeeded > availableMemoryBytes) {
1207+
qWarning() << "Utils: Insufficient memory for copy operation. Needed(est):" << estimatedMemoryNeeded << "Available:" << availableMemoryBytes;
1208+
return false;
1209+
}
1210+
break;
12111211
}
1212+
1213+
case OperationType::PasteOperation: {
1214+
// Paste operation: Estimated memory consumption = paste data size * factor
1215+
qlonglong estimatedMemoryNeededForPaste = operationDataSize * PASTE_CONSUME_MEMORY_MULTIPLE;
1216+
// Estimated total document memory after paste
1217+
qlonglong estimatedTotalDocMemory = (currentDocumentSize + operationDataSize) * PASTE_CONSUME_MEMORY_MULTIPLE;
1218+
1219+
// Check if pasting the data itself would cause insufficient memory
1220+
if (estimatedMemoryNeededForPaste > availableMemoryBytes) {
1221+
qWarning() << "Utils: Insufficient memory for paste operation (paste data). Needed(est):" << estimatedMemoryNeededForPaste << "Available:" << availableMemoryBytes;
1222+
return false;
1223+
}
12121224

1213-
// Check if the estimated total document size after paste exceeds total system memory (very rough check)
1214-
if (estimatedTotalDocMemory > totalMemoryBytes) {
1215-
qWarning() << "Utils: Paste operation might exceed total system memory. Estimated total doc memory:" << estimatedTotalDocMemory << "Total system memory:" << totalMemoryBytes;
1216-
return false;
1217-
}
1225+
// Check if the estimated total document size after paste exceeds total system memory
1226+
if (estimatedTotalDocMemory > totalMemoryBytes) {
1227+
qWarning() << "Utils: Paste operation might exceed total system memory. Estimated total doc memory:" << estimatedTotalDocMemory << "Total system memory:" << totalMemoryBytes;
1228+
return false;
1229+
}
12181230

1219-
// Check specific threshold: Restrict paste size if document reaches 800MB
1220-
const qlonglong DOC_SIZE_LIMIT_800MB = 800LL * DATA_SIZE_1024 * DATA_SIZE_1024;
1221-
const qlonglong PASTE_SIZE_LIMIT_500KB = 500LL * DATA_SIZE_1024;
1222-
if (currentDocumentSize > DOC_SIZE_LIMIT_800MB && operationDataSize > PASTE_SIZE_LIMIT_500KB) {
1223-
qWarning() << "Utils: Paste operation restricted. Document size exceeds 800MB and paste data exceeds 500KB.";
1224-
return false;
1231+
// Check specific threshold: Restrict paste size if document reaches 800MB
1232+
const qlonglong DOC_SIZE_LIMIT_800MB = 800LL * DATA_SIZE_1024 * DATA_SIZE_1024;
1233+
const qlonglong PASTE_SIZE_LIMIT_500KB = 500LL * DATA_SIZE_1024;
1234+
if (currentDocumentSize > DOC_SIZE_LIMIT_800MB && operationDataSize > PASTE_SIZE_LIMIT_500KB) {
1235+
qWarning() << "Utils: Paste operation restricted. Document size exceeds 800MB and paste data exceeds 500KB.";
1236+
return false;
1237+
}
1238+
break;
12251239
}
1240+
1241+
default:
1242+
break;
12261243
}
12271244

12281245
return true; // Memory is sufficient

src/common/utils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ class Utils
5252
*/
5353
enum OperationType {
5454
CopyOperation,
55-
PasteOperation
55+
PasteOperation,
56+
RawOperation
5657
};
5758

5859
/**

0 commit comments

Comments
 (0)