/* This file is part of Mitsuba, a physically based rendering system. Copyright (c) 2007-2012 by Wenzel Jakob and others. Mitsuba is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 3 as published by the Free Software Foundation. Mitsuba 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 GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #if !defined(WIN32) # include #else # include #endif MTS_NAMESPACE_BEGIN struct FileStream::FileStreamPrivate { #if defined(WIN32) HANDLE file; #else FILE* file; #endif bool write; bool read; FileStream::EFileMode mode; fs::path path; FileStreamPrivate() : file(NULL) {} }; FileStream::FileStream() : d(new FileStreamPrivate) { } FileStream::FileStream(const fs::path &path, EFileMode mode) : d(new FileStreamPrivate) { open(path, mode); } FileStream::~FileStream() { if (d->file != 0) close(); } const fs::path& FileStream::getPath() const { return d->path; } std::string FileStream::toString() const { std::ostringstream oss; oss << "FileStream[" << Stream::toString() << ", path=\"" << d->path.string() << "\", mode=" << d->mode << "]"; return oss.str(); } void FileStream::open(const fs::path &path, EFileMode mode) { AssertEx(d->file == 0, "A file has already been opened using this stream"); Log(ETrace, "Opening \"%s\"", path.string().c_str()); d->path = path; d->mode = mode; d->write = true; d->read = true; #ifdef WIN32 DWORD dwDesiredAccess = GENERIC_READ; DWORD dwCreationDisposition = OPEN_EXISTING; switch (d->mode) { case EReadOnly: d->write = false; break; case EReadWrite: dwDesiredAccess |= GENERIC_WRITE; break; case ETruncWrite: d->read = false; dwDesiredAccess = GENERIC_WRITE; dwCreationDisposition = CREATE_ALWAYS; break; case ETruncReadWrite: dwDesiredAccess |= GENERIC_WRITE; dwCreationDisposition = CREATE_ALWAYS; break; case EAppendWrite: d->read = false; dwDesiredAccess = GENERIC_WRITE; break; case EAppendReadWrite: dwDesiredAccess |= GENERIC_WRITE; break; default: Log(EError, "Unknown file mode"); break; } d->file = CreateFileW(path.c_str(), dwDesiredAccess, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, 0); if (d->file == INVALID_HANDLE_VALUE) Log(EError, "Error while trying to open file \"%s\": %s", d->path.string().c_str(), lastErrorText().c_str()); if (d->mode == EAppendWrite || d->mode == EAppendReadWrite) seek(getSize()); #else const char *modeString = NULL; switch (d->mode) { case EReadOnly: modeString = "rb"; d->write = false; break; case EReadWrite: modeString = "rb+"; break; case ETruncWrite: modeString = "wb"; d->read = false; break; case ETruncReadWrite: modeString = "wb+"; break; case EAppendWrite: modeString = "ab"; d->read = false; break; case EAppendReadWrite: modeString = "ab+"; break; default: Log(EError, "Unknown file mode"); break; }; d->file = fopen(d->path.string().c_str(), modeString); if (d->file == NULL) { Log(EError, "Error while trying to open file \"%s\": %s", d->path.string().c_str(), strerror(errno)); } #endif } void FileStream::close() { AssertEx(d->file != 0, "No file is currently open"); Log(ETrace, "Closing \"%s\"", d->path.string().c_str()); #ifdef WIN32 if (!CloseHandle(d->file)) { Log(EError, "Error while trying to close file \"%s\": %s", d->path.string().c_str(), lastErrorText().c_str()); } #else if (fclose(d->file)) { Log(EError, "Error while trying to close file \"%s\": %s", d->path.string().c_str(), strerror(errno)); } #endif d->file = 0; } void FileStream::remove() { close(); Log(EDebug, "Removing \"%s\"", d->path.string().c_str()); fs::remove(d->path); } void FileStream::seek(size_t pos) { AssertEx(d->file != 0, "No file is currently open"); #ifdef WIN32 LARGE_INTEGER fpos; fpos.QuadPart = pos; if (SetFilePointerEx(d->file, fpos, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { Log(EError, "Error while trying to seek to position %i in file \"%s\": %s", pos, d->path.string().c_str(), lastErrorText().c_str()); } #else if (fseek(d->file, pos, SEEK_SET)) { Log(EError, "Error while trying to seek to position %i in file \"%s\": %s", pos, d->path.string().c_str(), strerror(errno)); } #endif } size_t FileStream::getPos() const { AssertEx(d->file != 0, "No file is currently open"); #ifdef WIN32 DWORD pos = SetFilePointer(d->file, 0, 0, FILE_CURRENT); if (pos == INVALID_SET_FILE_POINTER) { Log(EError, "Error while looking up the position in file \"%s\": %s", d->path.string().c_str(), lastErrorText().c_str()); } return (size_t) pos; #else long pos; pos = ftell(d->file); if (pos == -1) { Log(EError, "Error while looking up the position in file \"%s\": %s", d->path.string().c_str(), strerror(errno)); } return (size_t) pos; #endif } size_t FileStream::getSize() const { AssertEx(d->file != 0, "No file is currently open"); #ifdef WIN32 LARGE_INTEGER result; if (GetFileSizeEx(d->file, &result) == 0) { Log(EError, "Error while getting the file size of \"%s\": %s", d->path.string().c_str(), lastErrorText().c_str()); } return (size_t) result.QuadPart; #else size_t size, tmp; tmp = getPos(); if (fseek(d->file, 0, SEEK_END)) { Log(EError, "Error while seeking within \"%s\": %s", d->path.string().c_str(), strerror(errno)); } size = getPos(); if (fseek(d->file, tmp, SEEK_SET)) { Log(EError, "Error while seeking within \"%s\": %s", d->path.string().c_str(), strerror(errno)); } return size; #endif } void FileStream::truncate(size_t size) { AssertEx(d->file != 0, "No file is currently open"); AssertEx(d->write, "File is not open with write access"); size_t pos = getPos(); if (pos > size) pos = size; #ifdef WIN32 seek(size); if (!SetEndOfFile(d->file)) { Log(EError, "Error while truncating file \"%s\": %s", d->path.string().c_str(), lastErrorText().c_str()); } #else seek(pos); flush(); if (ftruncate(fileno(d->file), size)) { Log(EError, "Error while truncating file \"%s\": %s", d->path.string().c_str(), strerror(errno)); } #endif seek(pos); } void FileStream::flush() { AssertEx(d->file != 0, "No file is currently open"); AssertEx(d->write, "File is not open with write access"); #ifdef WIN32 if (!FlushFileBuffers(d->file)) { Log(EError, "Error while flusing the buffers of \"%s\": %s", d->path.string().c_str(), lastErrorText().c_str()); } #else if (fflush(d->file) != 0) { Log(EError, "Error while flusing the buffers of \"%s\": %s", d->path.string().c_str(), strerror(errno)); } #endif } void FileStream::read(void *pPtr, size_t size) { AssertEx(d->file != 0, "No file is currently open"); AssertEx(d->read, "File is not open with read access"); if (size == 0) return; #ifdef WIN32 DWORD lpNumberOfBytesRead; if (!ReadFile(d->file, pPtr, (DWORD) size, &lpNumberOfBytesRead, 0)) { Log(EError, "Error while reading from file \"%s\": %s", d->path.string().c_str(), lastErrorText().c_str()); } if (lpNumberOfBytesRead != (DWORD) size) throw EOFException(formatString("Read less data than expected (%i bytes required) " "from file \"%s\"", size, d->path.string().c_str()), (size_t) lpNumberOfBytesRead); #else size_t bytesRead; if ((bytesRead = fread(pPtr, 1, size, d->file)) != size) { if (ferror(d->file) != 0) { Log(EError, "Error while reading from file \"%s\": %s", d->path.string().c_str(), strerror(errno)); } throw EOFException(formatString("Read less data than expected (%i bytes required) " "from file \"%s\"", size, d->path.string().c_str()), bytesRead); } #endif } void FileStream::write(const void *pPtr, size_t size) { AssertEx(d->file != 0, "No file is currently open"); AssertEx(d->write, "File is not open with write access"); if (size == 0) return; #ifdef WIN32 DWORD lpNumberOfBytesWritten; if (!WriteFile(d->file, pPtr, (DWORD) size, &lpNumberOfBytesWritten, 0)) { Log(EError, "Error while writing to file \"%s\": %s", d->path.string().c_str(), lastErrorText().c_str()); } if (lpNumberOfBytesWritten != (DWORD) size) throw EOFException(formatString("Wrote less data than expected (%i bytes required) " "to file \"%s\"", size, d->path.string().c_str()), (size_t) lpNumberOfBytesWritten); #else size_t bytesWritten; if ((bytesWritten = fwrite(pPtr, 1, size, d->file)) != size) { if (ferror(d->file)) Log(EError, "Error while writing to file \"%s\": %s", d->path.string().c_str(), strerror(errno)); throw EOFException(formatString("Wrote less data than expected (%i bytes required) " "to file \"%s\"", size, d->path.string().c_str()), bytesWritten); } #endif } bool FileStream::canRead() const { AssertEx(d->file != 0, "No file is currently open"); return d->read; } bool FileStream::canWrite() const { AssertEx(d->file != 0, "No file is currently open"); return d->write; } MTS_IMPLEMENT_CLASS(FileStream, false, Stream) MTS_NAMESPACE_END