diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:08:06 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:08:06 +0200 |
commit | fbe76102e941b9f1edaf236788e42678f05fdf9a (patch) | |
tree | f5f538316019fa89be8dc478103490c3a826f3ac /shared/file_io.cpp | |
parent | 3.8 (diff) | |
download | FreeFileSync-fbe76102e941b9f1edaf236788e42678f05fdf9a.tar.gz FreeFileSync-fbe76102e941b9f1edaf236788e42678f05fdf9a.tar.bz2 FreeFileSync-fbe76102e941b9f1edaf236788e42678f05fdf9a.zip |
3.9
Diffstat (limited to 'shared/file_io.cpp')
-rw-r--r-- | shared/file_io.cpp | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/shared/file_io.cpp b/shared/file_io.cpp new file mode 100644 index 00000000..345234fd --- /dev/null +++ b/shared/file_io.cpp @@ -0,0 +1,195 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** +// +#include "file_io.h" +#include <wx/intl.h> +#include "string_conv.h" +#include "system_func.h" + +#ifdef FFS_WIN +#include "long_path_prefix.h" +#elif defined FFS_LINUX +#include <errno.h> +#endif + +using namespace ffs3; + + +FileInput::FileInput(const Zstring& filename) : //throw FileError() +#ifdef FFS_WIN + eofReached(false), +#endif + filename_(filename) +{ +#ifdef FFS_WIN + fileHandle = ::CreateFile(ffs3::applyLongPathPrefix(filename).c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //all shared modes are required to read files that are open in other applications + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + /* possible values: (Reference http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx#caching_behavior) + FILE_FLAG_NO_BUFFERING + FILE_FLAG_RANDOM_ACCESS + FILE_FLAG_SEQUENTIAL_SCAN + + tests on Win7 x64 show that FILE_FLAG_SEQUENTIAL_SCAN provides best performance for binary comparison in all cases: + - comparing different physical disks (DVD <-> HDD and HDD <-> HDD) + - even on same physical disk! (HDD <-> HDD) + - independent from client buffer size! + + tests on XP show that FILE_FLAG_SEQUENTIAL_SCAN provides best performance for binary comparison when + - comparing different physical disks (DVD <-> HDD) + + while FILE_FLAG_RANDOM_ACCESS offers best performance for + - same physical disk (HDD <-> HDD) + + Problem: bad XP implementation of prefetch makes flag FILE_FLAG_SEQUENTIAL_SCAN effectively load two files at once from one drive + swapping every 64 kB (or similar). File access times explode! + => For XP it is critical to use FILE_FLAG_RANDOM_ACCESS (to disable prefetch) if reading two files on same disk and + FILE_FLAG_SEQUENTIAL_SCAN when reading from different disk (e.g. massive performance improvement compared to random access for DVD <-> HDD!) + => there is no compromise that satisfies all cases! (on XP) + + for FFS most comparisons are probably between different disks => let's use FILE_FLAG_SEQUENTIAL_SCAN + */ + NULL); + if (fileHandle == INVALID_HANDLE_VALUE) + { + const DWORD lastError = ::GetLastError(); + const wxString& errorMessage = wxString(_("Error opening file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") + wxT("\n\n") + ffs3::getLastErrorFormatted(lastError); + if (lastError == ERROR_FILE_NOT_FOUND) + throw ErrorNotExisting(errorMessage); + else + throw FileError(errorMessage); + } +#elif defined FFS_LINUX + fileHandle = ::fopen(filename.c_str(), "rb,type=record,noseek"); //utilize UTF-8 filename + if (fileHandle == NULL) + { + const int lastError = errno; + const wxString& errorMessage = wxString(_("Error opening file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") + wxT("\n\n") + ffs3::getLastErrorFormatted(lastError); + if (lastError == ENOENT) + throw ErrorNotExisting(errorMessage); + else + throw FileError(errorMessage); + } +#endif +} + + +FileInput::~FileInput() +{ +#ifdef FFS_WIN + ::CloseHandle(fileHandle); +#elif defined FFS_LINUX + ::fclose(fileHandle); //NEVER allow passing NULL to fclose! -> crash!; fileHandle != NULL in this context! +#endif +} + + +size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number of bytes read; throw FileError() +{ +#ifdef FFS_WIN + DWORD bytesRead = 0; + + if (!::ReadFile( + fileHandle, //__in HANDLE hFile, + buffer, //__out LPVOID lpBuffer, + static_cast<DWORD>(bytesToRead), //__in DWORD nNumberOfBytesToRead, + &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead, + NULL) //__inout_opt LPOVERLAPPED lpOverlapped + || bytesRead > bytesToRead) //must be fulfilled + throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") + + wxT("\n\n") + ffs3::getLastErrorFormatted()); + + if (bytesRead < bytesToRead) + eofReached = true; + + return bytesRead; +#elif defined FFS_LINUX + const size_t bytesRead = ::fread(buffer, 1, bytesToRead, fileHandle); + if (::ferror(fileHandle) != 0) + throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") + + wxT("\n\n") + ffs3::getLastErrorFormatted()); + return bytesRead; +#endif +} + + +bool FileInput::eof() //end of file reached +{ +#ifdef FFS_WIN + return eofReached; +#elif defined FFS_LINUX + return ::feof(fileHandle) != 0; +#endif +} + + +FileOutput::FileOutput(const Zstring& filename) : //throw FileError() + filename_(filename) +{ +#ifdef FFS_WIN + fileHandle = ::CreateFile(ffs3::applyLongPathPrefix(filename).c_str(), + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if (fileHandle == INVALID_HANDLE_VALUE) + throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") + + wxT("\n\n") + ffs3::getLastErrorFormatted()); +#elif defined FFS_LINUX + fileHandle = ::fopen(filename.c_str(), "wb,type=record,noseek"); //utilize UTF-8 filename + if (!fileHandle) + throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") + + wxT("\n\n") + ffs3::getLastErrorFormatted()); +#endif +} + + +FileOutput::~FileOutput() +{ + close(); //may be called more than once +} + + +void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileError() +{ +#ifdef FFS_WIN + DWORD bytesWritten = 0; + + if (!::WriteFile( + fileHandle, //__in HANDLE hFile, + buffer, //__out LPVOID lpBuffer, + static_cast<DWORD>(bytesToWrite), //__in DWORD nNumberOfBytesToRead, + &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, + NULL) //__inout_opt LPOVERLAPPED lpOverlapped + || bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes! + throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") + + wxT("\n\n") + ffs3::getLastErrorFormatted() + wxT(" (w)")); //w -> distinguish from fopen error message! +#elif defined FFS_LINUX + const size_t bytesWritten = ::fwrite(buffer, 1, bytesToWrite, fileHandle); + if (::ferror(fileHandle) != 0 || bytesWritten != bytesToWrite) + throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") + + wxT("\n\n") + ffs3::getLastErrorFormatted() + wxT(" (w)")); //w -> distinguish from fopen error message! +#endif +} + + +void FileOutput::close() //close file stream +{ + if (fileHandle != NULL) //NEVER allow passing NULL to fclose! -> crash! + { +#ifdef FFS_WIN + ::CloseHandle(fileHandle); +#elif defined FFS_LINUX + ::fclose(fileHandle); +#endif + fileHandle = NULL; + } +} |