#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2013 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_STORAGE_FILESYSTEM_GENERIC_GENERICWINDOWSFILE_H #define NUCLEX_STORAGE_FILESYSTEM_GENERIC_GENERICWINDOWSFILE_H #include "Nuclex/Storage/Config.h" #if defined(NUCLEX_STORAGE_WIN32) || defined(NUCLEX_STORAGE_WINRT) #include "Nuclex/Storage/FileSystem/File.h" #include "../../Helpers/WindowsApi.h" #include "../../Helpers/DateHelper.h" #include #include namespace Nuclex { namespace Storage { namespace FileSystem { // ------------------------------------------------------------------------------------------- // /// File on a Windows system using either Win32 or WinRT /// /// A class wrapping the API-specific file system methods /// /// /// A class containing helper methods for working with paths on the platform /// /// /// The differences between Win32 and WinRT are minor as far as file handling /// is concerned, so both share this class to perform their file acccesses, /// using the FileApi and PathHelper to handle the API differences. /// template class GenericWindowsFile : public File { /// Accesses a file on windows /// Path of the file that will be opened or created public: GenericWindowsFile(const std::string &path) : path(path), name(TPathHelper::GetLastPathElement(path)), fileHandle(INVALID_HANDLE_VALUE), lastLocation(0), isOpenedWritable(false) {} /// Frees all resources owned by the instance public: virtual ~GenericWindowsFile() { if(this->fileHandle != INVALID_HANDLE_VALUE) { TFileApi::CloseFile(this->fileHandle); } } /// Determines the size of the file /// The size of the file in bytes public: std::uint64_t GetSize() const { std::lock_guard scope(this->mutex); { if(this->fileHandle == INVALID_HANDLE_VALUE) { WIN32_FILE_ATTRIBUTE_DATA fileAttributeData; if(TFileApi::GetFileAttributesEx(this->path, fileAttributeData)) { return (static_cast(fileAttributeData.nFileSizeHigh) << 32) | fileAttributeData.nFileSizeLow; } else { return 0; // File doesn't exist yet - not an error! } } else { // File was already opened return TFileApi::GetFileSize(this->fileHandle); } } // mutex } /// Returns the name of the file /// The file's name public: const std::string &GetName() const { return this->name; } /// Changes the name of the file /// New name the file will be known under public: void SetName(const std::string &name) { std::lock_guard scope(this->mutex); { if(this->fileHandle != INVALID_HANDLE_VALUE) { close(); } std::string newPath = TPathHelper::CombinePaths( TPathHelper::RemoveLastElement(this->path), name ); TFileApi::MoveFile(this->path, newPath); this->name = name; this->path = newPath; } } /// Returns the path the file is stored at in the native format /// The file's absolute path in the native OS format public: const std::string &GetNativePath() const { return this->path; } /// Retrieves the time at which the file has been last modified /// The since since the last modification of the file took place public: std::time_t GetLastModificationTime() const { using Nuclex::Storage::Helpers::DateHelper; std::lock_guard scope(this->mutex); { if(this->fileHandle == INVALID_HANDLE_VALUE) { WIN32_FILE_ATTRIBUTE_DATA fileAttributeData; if(TFileApi::GetFileAttributesEx(this->path, fileAttributeData)) { return DateHelper::PosixTimeFromFileTime( fileAttributeData.ftLastWriteTime.dwHighDateTime, fileAttributeData.ftLastWriteTime.dwLowDateTime ); } else { return 0; // File doesn't exist yet - not an error! } } else { // File was already opened return DateHelper::PosixTimeFromFileTime( TFileApi::GetLastModificationTime(this->fileHandle) ); } } // mutex } /// Reads raw data from the file /// Absolute file position data will be read from /// Buffer into which data will be read /// Number of bytes that will be read public: void ReadAt( std::uint64_t location, void *buffer, std::size_t count ) const { std::lock_guard scope(this->mutex); { // Make sure the file is opened so that we can read from it if(this->fileHandle == INVALID_HANDLE_VALUE) { this->fileHandle = TFileApi::OpenFileForReading(this->path); } // If the file pointer is not where we need it to be, move it if(location != this->lastLocation) { moveFilePointer(location); } // Perform the actual read through the Windows API std::size_t actualCount = TFileApi::Read(this->fileHandle, buffer, count); // Whether the read was cut off at the end of the file or not, the file pointer will // still have been moved and we need to account for this. this->lastLocation += actualCount; if(actualCount != count) { throw std::out_of_range("Attempted to read beyond the end of the file"); } } // mutex } /// Writes raw data into the file /// Absolute file position data will be written to /// Buffer from which data will be taken /// Number of bytes that will be written public: void WriteAt( std::uint64_t location, const void *buffer, std::size_t count ) { std::lock_guard scope(this->mutex); { // If the file is still closed or has been opened in the wrong mode, // make sure that it is opened in the right mode for the write. if(this->fileHandle == INVALID_HANDLE_VALUE) { this->fileHandle = TFileApi::OpenFileForWriting(this->path); } else if(!this->isOpenedWritable) { close(); this->fileHandle = TFileApi::OpenFileForWriting(this->path); } // If the file pointer is not where we need it to be, move it if(location != this->lastLocation) { moveFilePointer(location); } // Perform the actual write through the Windows API std::size_t actualCount = TFileApi::Write(this->fileHandle, buffer, count); // Whether the write was aborted early or not, as long as the method returned success, // the file pointer will still have moved, so we need to account for this. this->lastLocation += actualCount; if(actualCount != count) { throw std::out_of_range("Write was aborted early"); } } // mutex } /// Ensures changes to the file have been written to disk public: void Flush() { std::lock_guard scope(this->mutex); { if(this->fileHandle != INVALID_HANDLE_VALUE) { TFileApi::FlushFileBuffers(this->fileHandle); } } } /// Closes the file private: void close() { if(this->fileHandle != INVALID_HANDLE_VALUE) { TFileApi::CloseFile(this->fileHandle); this->fileHandle = INVALID_HANDLE_VALUE; this->lastLocation = 0; } } /// Moves the file pointer to the specified location /// Location the file pointer will be moved to /// /// Having a const method that promises a state changing operation might seem icky. /// Here's the reason this exists: the File class abstracts away the entire concept /// of file cursors. Thus, as far as the user is concerned, there is no current /// location and a const file instance can be read from without second thought. /// The Win32 file API however relies very much on file cursors. /// private: void moveFilePointer(std::uint64_t location) const { std::uint64_t actualLocation = TFileApi::SetFilePointer(this->fileHandle, location); this->lastLocation = actualLocation; if(actualLocation != location) { throw std::out_of_range("Invalid file offset"); } } /// Handle of the opened file for Windows' file functions private: mutable HANDLE fileHandle; /// Synchronize file accesses from multiple threads /// /// This is required because the Windows file implementation still forces /// a file cursor upon us. Opening multiple file handles would only inrease /// complexity when the underlying hardware cannot do parallel reads anyway. /// private: mutable std::mutex mutex; /// Location of the last read or write private: mutable std::uint64_t lastLocation; /// Whether the file is currently opened for writing private: mutable bool isOpenedWritable; /// Absolute path of the file private: std::string path; /// The file's name private: std::string name; }; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Storage::FileSystem #endif // defined(NUCLEX_STORAGE_WIN32) || defined(NUCLEX_STORAGE_WINRT) #endif // NUCLEX_STORAGE_FILESYSTEM_GENERIC_GENERICWINDOWSFILE_H