Skip to content

Commit 124e819

Browse files
committed
Performing file copy optimized and improved
1 parent 4bcc8c6 commit 124e819

File tree

8 files changed

+112
-85
lines changed

8 files changed

+112
-85
lines changed

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@
1717
[submodule "image-processing"]
1818
path = image-processing
1919
url = https://github.com/VioletGiraffe/image-processing
20+
[submodule "thin_io"]
21+
path = thin_io
22+
url = https://github.com/VioletGiraffe/thin_io

file-commander-core/config.pri

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ android {
2828
} else {
2929
Release:OUTPUT_DIR=release/$${ARCHITECTURE}
3030
Debug:OUTPUT_DIR=debug/$${ARCHITECTURE}
31+
32+
Release:OUTPUT_DIR_NO_ARCH=release/
33+
Debug:OUTPUT_DIR_NO_ARCH=debug/
3134
}
3235

3336
win*{
@@ -62,4 +65,5 @@ INCLUDEPATH += \
6265
include \
6366
../qtutils \
6467
../cpputils \
65-
../cpp-template-utils
68+
../cpp-template-utils \
69+
../thin_io/src

file-commander-core/file-commander-core.pro

+5-3
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@ TARGET = core
33

44
include(config.pri)
55

6-
DESTDIR = ../bin/$${OUTPUT_DIR}
6+
DESTDIR = ../bin/$${OUTPUT_DIR}
7+
DESTDIR_NO_ARCH = ../bin/$${OUTPUT_DIR_NO_ARCH}
78
OBJECTS_DIR = ../build/$${OUTPUT_DIR}/$${TARGET}
89
MOC_DIR = ../build/$${OUTPUT_DIR}/$${TARGET}
910
UI_DIR = ../build/$${OUTPUT_DIR}/$${TARGET}
1011
RCC_DIR = ../build/$${OUTPUT_DIR}/$${TARGET}
1112

12-
LIBS += -L$${DESTDIR} -lcpputils -lqtutils
13-
1413
mac*|linux*|freebsd{
1514
PRE_TARGETDEPS += $${DESTDIR}/libqtutils.a $${DESTDIR}/libcpputils.a
15+
PRE_TARGETDEPS += $${DESTDIR_NO_ARCH}/libthin_io.a
1616
}
1717

18+
LIBS += -L$${DESTDIR} -lcpputils -lqtutils
19+
1820
HEADERS += \
1921
src/cfilesystemobject.h \
2022
src/ccontroller.h \

file-commander-core/src/cfilemanipulator.cpp

+81-73
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ RESTORE_COMPILER_WARNINGS
1818
#include <unistd.h>
1919
#endif
2020

21+
inline static QString getLastFileError()
22+
{
23+
return QString::fromStdString(thin_io::file::text_for_last_error());
24+
}
25+
2126
static constexpr QFileDevice::FileTime supportedFileTimeTypes[] {
2227
QFileDevice::FileAccessTime,
2328
#ifndef __linux__
@@ -30,17 +35,18 @@ static constexpr QFileDevice::FileTime supportedFileTimeTypes[] {
3035
};
3136

3237
// Operations
33-
CFileManipulator::CFileManipulator(CFileSystemObject object) : _object{std::move(object)}
38+
CFileManipulator::CFileManipulator(const CFileSystemObject& object) :
39+
_srcObject{ object }
3440
{
3541
}
3642

3743
FileOperationResultCode CFileManipulator::copyAtomically(const QString& destFolder, const QString& newName, TransferPermissions transferPermissions)
3844
{
39-
assert_r(_object.isFile());
45+
assert_r(_srcObject.isFile());
4046
assert_r(QFileInfo{destFolder}.isDir());
4147

42-
QFile file(_object.fullAbsolutePath());
43-
const QString newFilePath = destFolder + (newName.isEmpty() ? _object.fullName() : newName);
48+
QFile file(_srcObject.fullAbsolutePath());
49+
const QString newFilePath = destFolder + (newName.isEmpty() ? _srcObject.fullName() : newName);
4450
bool succ = file.copy(newFilePath);
4551
if (succ)
4652
{
@@ -58,22 +64,22 @@ FileOperationResultCode CFileManipulator::copyAtomically(const QString& destFold
5864

5965
FileOperationResultCode CFileManipulator::moveAtomically(const QString& destFolder, const QString& newName, OverwriteExistingFile overwriteExistingFile)
6066
{
61-
if (!_object.exists())
67+
if (!_srcObject.exists())
6268
return FileOperationResultCode::ObjectDoesntExist;
63-
else if (_object.isCdUp())
69+
else if (_srcObject.isCdUp())
6470
return FileOperationResultCode::Fail;
6571

6672
assert_debug_only(QFileInfo{ destFolder }.isDir());
67-
const QString fullNewName = destFolder % '/' % (newName.isEmpty() ? _object.fullName() : newName);
73+
const QString fullNewName = destFolder % '/' % (newName.isEmpty() ? _srcObject.fullName() : newName);
6874
CFileSystemObject destInfo(fullNewName);
69-
const bool newNameDiffersOnlyInLetterCase = destInfo.fullAbsolutePath().compare(_object.fullAbsolutePath(), Qt::CaseInsensitive) == 0;
75+
const bool newNameDiffersOnlyInLetterCase = destInfo.fullAbsolutePath().compare(_srcObject.fullAbsolutePath(), Qt::CaseInsensitive) == 0;
7076

7177
// If the file system is case-insensitive, and the source and destination only differ by case, renaming is allowed even though formally the destination already exists (fix for #102)
7278
if ((caseSensitiveFilesystem() || !newNameDiffersOnlyInLetterCase) && destInfo.exists())
7379
{
74-
if (_object.isDir())
80+
if (_srcObject.isDir())
7581
return FileOperationResultCode::TargetAlreadyExists;
76-
else if (overwriteExistingFile == true && _object.isFile() && destInfo.isFile())
82+
else if (overwriteExistingFile == true && _srcObject.isFile() && destInfo.isFile())
7783
{
7884
// Special case: it may be allowed to replace the existing file (https://github.com/VioletGiraffe/file-commander/issues/123)
7985
if (remove(destInfo) != FileOperationResultCode::Ok)
@@ -89,27 +95,27 @@ FileOperationResultCode CFileManipulator::moveAtomically(const QString& destFold
8995
// Windows: QFile::rename and QDir::rename fail to handle names that only differ by letter case (https://bugreports.qt.io/browse/QTBUG-3570)
9096
// Also, QFile::rename will attempt to painfully copy the file if it's locked.
9197
#ifdef _WIN32
92-
if (MoveFileW(reinterpret_cast<const WCHAR*>(_object.fullAbsolutePath().utf16()), reinterpret_cast<const WCHAR*>(destInfo.fullAbsolutePath().utf16())) != 0)
98+
if (MoveFileW(reinterpret_cast<const WCHAR*>(_srcObject.fullAbsolutePath().utf16()), reinterpret_cast<const WCHAR*>(destInfo.fullAbsolutePath().utf16())) != 0)
9399
return FileOperationResultCode::Ok;
94100

95101
_lastErrorMessage = QString::fromStdString(ErrorStringFromLastError());
96102
return FileOperationResultCode::Fail;
97103
#else
98-
if (_object.isFile())
104+
if (_srcObject.isFile())
99105
{
100-
QFile file(_object.fullAbsolutePath());
106+
QFile file(_srcObject.fullAbsolutePath());
101107
if (!file.rename(fullNewName))
102108
{
103109
_lastErrorMessage = file.errorString();
104110
return FileOperationResultCode::Fail;
105111
}
106112

107-
_object.refreshInfo(); // TODO: what is this for?
113+
_srcObject.refreshInfo(); // TODO: what is this for?
108114
return FileOperationResultCode::Ok;
109115
}
110-
else if (_object.isDir())
116+
else if (_srcObject.isDir())
111117
{
112-
return QDir{}.rename(_object.fullAbsolutePath(), fullNewName) ? FileOperationResultCode::Ok : FileOperationResultCode::Fail;
118+
return QDir{}.rename(_srcObject.fullAbsolutePath(), fullNewName) ? FileOperationResultCode::Ok : FileOperationResultCode::Fail;
113119
}
114120
else
115121
return FileOperationResultCode::Fail;
@@ -132,22 +138,22 @@ FileOperationResultCode CFileManipulator::moveAtomically(const CFileSystemObject
132138
FileOperationResultCode CFileManipulator::copyChunk(size_t chunkSize, const QString& destFolder, const QString& newName /*= QString()*/, const bool transferPermissions, const bool transferDates)
133139
{
134140
assert_debug_only(bool(_thisFile) == bool(_destFile));
135-
assert_debug_only(_object.isFile());
141+
assert_debug_only(_srcObject.isFile());
136142
assert_debug_only(QFileInfo(destFolder).isDir());
137143

138144
if (!copyOperationInProgress())
139145
{
140146
_pos = 0;
141147

142148
// Creating files
143-
_thisFile = std::make_unique<QFile>(_object.fullAbsolutePath());
144-
_destFile = std::make_unique<QFile>(destFolder + (newName.isEmpty() ? _object.fullName() : newName));
149+
_thisFile = std::make_unique<QFile>(_srcObject.fullAbsolutePath());
150+
_destFile = std::make_unique<thin_io::file>();
145151

146152
for (const auto fileTimeType: supportedFileTimeTypes)
147153
_sourceFileTime[fileTimeType] = _thisFile->fileTime(fileTimeType);
148154

149155
// Initializing - opening files
150-
if (!_thisFile->open(QFile::ReadOnly))
156+
if (!_thisFile->open(QFile::ReadOnly | QFile::Unbuffered))
151157
{
152158
_lastErrorMessage = _thisFile->errorString();
153159

@@ -157,35 +163,34 @@ FileOperationResultCode CFileManipulator::copyChunk(size_t chunkSize, const QStr
157163
return FileOperationResultCode::Fail;
158164
}
159165

160-
if (!_destFile->open(QFile::ReadWrite))
166+
_destinationFilePath = destFolder + (newName.isEmpty() ? _srcObject.fullName() : newName);
167+
168+
if (!_destFile->open(_destinationFilePath.toUtf8().constData(), thin_io::file_definitions::Write))
161169
{
162-
_lastErrorMessage = _destFile->errorString();
170+
_lastErrorMessage = getLastFileError();
163171

164172
_thisFile.reset();
165173
_destFile.reset();
166174

167175
return FileOperationResultCode::Fail;
168176
}
169177

170-
if (!_destFile->resize((qint64)_object.size()))
171-
{
172-
_lastErrorMessage.clear(); // QFile provides no meaningful message for this case.
173-
_destFile->close();
174-
assert_r(_destFile->remove());
178+
//if (!_destFile->truncate(_srcObject.size()))
179+
//{
180+
// _lastErrorMessage = getLastFileError();
181+
// assert_r(_destFile->close());
182+
// assert_r(QFile::remove(_destinationFilePath));
175183

176-
_thisFile.reset();
177-
_destFile.reset();
178-
179-
return FileOperationResultCode::NotEnoughSpaceAvailable;
180-
}
181-
182-
// Store the original file's attributes to later mirror them onto the copy
184+
// _thisFile.reset();
185+
// _destFile.reset();
183186

187+
// return FileOperationResultCode::NotEnoughSpaceAvailable;
188+
//}
184189
}
185190

186-
assert_r(_destFile->isOpen() == _thisFile->isOpen());
191+
assert_r(_destFile->is_open() == _thisFile->isOpen());
187192

188-
const auto actualChunkSize = std::min(chunkSize, (size_t)(_object.size() - _pos));
193+
const auto actualChunkSize = std::min(chunkSize, (size_t)(_srcObject.size() - _pos));
189194

190195
if (actualChunkSize != 0)
191196
{
@@ -196,45 +201,48 @@ FileOperationResultCode CFileManipulator::copyChunk(size_t chunkSize, const QStr
196201
return FileOperationResultCode::Fail;
197202
}
198203

199-
auto* dest = _destFile->map((qint64)_pos, (qint64)actualChunkSize);
200-
if (dest == nullptr)
201-
{
202-
_lastErrorMessage = _destFile->errorString();
203-
return FileOperationResultCode::Fail;
204-
}
205-
206-
::memcpy(dest, src, actualChunkSize);
204+
// TODO: error handling!
205+
_destFile->write(src, actualChunkSize);
207206
_pos += actualChunkSize;
208-
209-
_thisFile->unmap(src);
210-
_destFile->unmap(dest);
207+
208+
[[maybe_unused]] const bool unmapResult = _thisFile->unmap(src);
209+
assert_debug_only(unmapResult);
211210
}
212211

213212
// TODO: '<=' ?
214213
if (actualChunkSize < chunkSize)
215214
{
216-
// Copying complete
217-
if (transferPermissions)
218-
_lastErrorMessage = copyPermissions(*_thisFile, *_destFile);
215+
// TODO: error handling!
216+
_destFile->close();
217+
_destFile.reset();
219218

220-
if (transferDates)
219+
// Copying complete
220+
if (transferPermissions || transferDates)
221221
{
222-
// Note: The file must be open to use setFileTime()
223-
224-
for (const auto fileTimeType: supportedFileTimeTypes)
225-
assert_r(_destFile->setFileTime(_sourceFileTime[fileTimeType], fileTimeType));
222+
assert_debug_only(!_destinationFilePath.isEmpty());
223+
// TODO: this is ineffective; need to support permission and date transfer in thin_io
224+
QFile qDstFile(_destinationFilePath);
225+
if (!qDstFile.open(QFile::ReadOnly | QFile::WriteOnly))
226+
{
227+
// TODO: error handling!
228+
}
229+
230+
if (transferPermissions)
231+
_lastErrorMessage = copyPermissions(*_thisFile, qDstFile);
232+
233+
if (transferDates)
234+
{
235+
// Note: The file must be open to use setFileTime()
236+
237+
for (const auto fileTimeType : supportedFileTimeTypes)
238+
assert_r(qDstFile.setFileTime(_sourceFileTime[fileTimeType], fileTimeType));
239+
}
226240
}
227241

228242
_thisFile.reset();
229-
_destFile.reset();
230243

231244
if (transferPermissions && !_lastErrorMessage.isEmpty())
232245
return FileOperationResultCode::Fail;
233-
234-
if (transferDates)
235-
{
236-
237-
}
238246
}
239247

240248
return FileOperationResultCode::Ok;
@@ -251,7 +259,7 @@ bool CFileManipulator::copyOperationInProgress() const
251259
return false;
252260

253261
const bool isOpen = _thisFile->isOpen();
254-
assert_r(isOpen == _destFile->isOpen());
262+
assert_r(isOpen == _destFile->is_open());
255263
return isOpen;
256264
}
257265

@@ -268,18 +276,18 @@ FileOperationResultCode CFileManipulator::cancelCopy()
268276
_thisFile->close();
269277
_destFile->close();
270278

271-
const bool succ = _destFile->remove();
279+
const bool succ = thin_io::file::delete_file(_destinationFilePath.toUtf8().constData());
272280
_thisFile.reset();
273281
_destFile.reset();
274282
return succ ? FileOperationResultCode::Ok : FileOperationResultCode::Fail;
275283
}
276284

277285
bool CFileManipulator::makeWritable(bool writable)
278286
{
279-
assert_and_return_message_r(_object.isFile(), "This method only works for files", false);
287+
assert_and_return_message_r(_srcObject.isFile(), "This method only works for files", false);
280288

281289
#ifdef _WIN32
282-
const QString UNCPath = toUncPath(_object.fullAbsolutePath());
290+
const QString UNCPath = toUncPath(_srcObject.fullAbsolutePath());
283291
const DWORD attributes = GetFileAttributesW(reinterpret_cast<LPCWSTR>(UNCPath.utf16()));
284292
if (attributes == INVALID_FILE_ATTRIBUTES)
285293
{
@@ -297,7 +305,7 @@ bool CFileManipulator::makeWritable(bool writable)
297305
#else
298306
struct stat fileInfo;
299307

300-
const QByteArray fileName = _object.fullAbsolutePath().toUtf8();
308+
const QByteArray fileName = _srcObject.fullAbsolutePath().toUtf8();
301309
if (stat(fileName.constData(), &fileInfo) != 0)
302310
{
303311
_lastErrorMessage = strerror(errno);
@@ -321,11 +329,11 @@ bool CFileManipulator::makeWritable(const CFileSystemObject& object, bool writab
321329

322330
FileOperationResultCode CFileManipulator::remove()
323331
{
324-
assert_and_return_message_r(_object.exists(), "Object doesn't exist", FileOperationResultCode::ObjectDoesntExist);
332+
assert_and_return_message_r(_srcObject.exists(), "Object doesn't exist", FileOperationResultCode::ObjectDoesntExist);
325333

326-
if (_object.isFile())
334+
if (_srcObject.isFile())
327335
{
328-
QFile file(_object.fullAbsolutePath());
336+
QFile file(_srcObject.fullAbsolutePath());
329337
if (file.remove())
330338
return FileOperationResultCode::Ok;
331339
else
@@ -334,14 +342,14 @@ FileOperationResultCode CFileManipulator::remove()
334342
return FileOperationResultCode::Fail;
335343
}
336344
}
337-
else if (_object.isDir())
345+
else if (_srcObject.isDir())
338346
{
339-
assert_r(_object.isEmptyDir());
347+
assert_r(_srcObject.isEmptyDir());
340348
errno = 0;
341-
if (!QDir{_object.fullAbsolutePath()}.rmdir(QStringLiteral(".")))
349+
if (!QDir{_srcObject.fullAbsolutePath()}.rmdir(QStringLiteral(".")))
342350
{
343351
#if defined __linux || defined __APPLE__ || defined __FreeBSD__
344-
if (::rmdir(_object.fullAbsolutePath().toLocal8Bit().constData()) == 0)
352+
if (::rmdir(_srcObject.fullAbsolutePath().toLocal8Bit().constData()) == 0)
345353
return FileOperationResultCode::Ok;
346354

347355
_lastErrorMessage = strerror(errno);

0 commit comments

Comments
 (0)