mirror of
https://github.com/DarkStore-3DS/Project_CTR.git
synced 2026-07-03 16:59:04 +00:00
752 lines
20 KiB
C++
752 lines
20 KiB
C++
#include <tc/io/FileStream.h>
|
|
#include <tc/PlatformErrorHandlingUtil.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <direct.h>
|
|
#include <cstdlib>
|
|
#else
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h> /* For O_RDWR */
|
|
#include <unistd.h> /* For open(), creat() */
|
|
#endif
|
|
|
|
const std::string tc::io::FileStream::kClassName = "tc::io::FileStream";
|
|
|
|
tc::io::FileStream::FileHandle::~FileHandle()
|
|
{
|
|
#ifdef _WIN32
|
|
CloseHandle(handle);
|
|
#else
|
|
::close(handle);
|
|
#endif
|
|
}
|
|
|
|
tc::io::FileStream::FileStream() :
|
|
mCanRead(false),
|
|
mCanWrite(false),
|
|
mCanSeek(false),
|
|
mIsAppendRestrictSeekCall(false),
|
|
mFileHandle()
|
|
{}
|
|
|
|
tc::io::FileStream::FileStream(FileStream&& other) :
|
|
FileStream()
|
|
{
|
|
*this = std::move(other);
|
|
}
|
|
|
|
tc::io::FileStream::FileStream(const tc::io::Path& path, FileMode mode, FileAccess access) :
|
|
FileStream()
|
|
{
|
|
// dispose stream before opening new stream
|
|
dispose();
|
|
|
|
open_impl(path, mode, access);
|
|
}
|
|
|
|
tc::io::FileStream& tc::io::FileStream::operator=(tc::io::FileStream&& other)
|
|
{
|
|
mCanRead = other.mCanRead;
|
|
mCanWrite = other.mCanWrite;
|
|
mCanSeek = other.mCanSeek;
|
|
mIsAppendRestrictSeekCall = other.mIsAppendRestrictSeekCall;
|
|
mFileHandle = std::move(other.mFileHandle);
|
|
other.dispose();
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool tc::io::FileStream::canRead() const
|
|
{
|
|
return mFileHandle == nullptr ? false : mCanRead;
|
|
}
|
|
|
|
bool tc::io::FileStream::canWrite() const
|
|
{
|
|
return mFileHandle == nullptr ? false : mCanWrite;
|
|
}
|
|
bool tc::io::FileStream::canSeek() const
|
|
{
|
|
return mFileHandle == nullptr || mIsAppendRestrictSeekCall == true ? false : mCanSeek;
|
|
}
|
|
|
|
int64_t tc::io::FileStream::length()
|
|
{
|
|
return mFileHandle == nullptr ? 0 : length_impl();
|
|
}
|
|
|
|
int64_t tc::io::FileStream::position()
|
|
{
|
|
if (mFileHandle == nullptr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (mCanSeek == false)
|
|
{
|
|
throw tc::NotSupportedException(kClassName+"::position()", "This method is not supported for streams that do not support seeking");
|
|
}
|
|
|
|
return seek_impl(0, SeekOrigin::Current);
|
|
}
|
|
|
|
size_t tc::io::FileStream::read(byte_t* ptr, size_t count)
|
|
{
|
|
if (mFileHandle == nullptr)
|
|
{
|
|
throw tc::ObjectDisposedException(kClassName+"::read()", "Failed to read from stream (stream is disposed)");
|
|
}
|
|
|
|
if (mCanRead == false)
|
|
{
|
|
throw tc::NotSupportedException(kClassName+"::read()", "Stream does not support reading");
|
|
}
|
|
|
|
if (ptr == nullptr)
|
|
{
|
|
throw tc::ArgumentNullException(kClassName+"::read()", "ptr was null");
|
|
}
|
|
|
|
if (count < 0)
|
|
{
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::read()", "count was negative");
|
|
}
|
|
|
|
return read_impl(ptr, count);
|
|
}
|
|
|
|
size_t tc::io::FileStream::write(const byte_t* ptr, size_t count)
|
|
{
|
|
if (mFileHandle == nullptr)
|
|
{
|
|
throw tc::ObjectDisposedException(kClassName+"::write()", "Failed to write to stream (no file open)");
|
|
}
|
|
|
|
if (mCanWrite == false)
|
|
{
|
|
throw tc::NotSupportedException(kClassName+"::write()", "Stream does not support writing");
|
|
}
|
|
|
|
if (ptr == nullptr)
|
|
{
|
|
throw tc::ArgumentNullException(kClassName+"::write()", "ptr was null");
|
|
}
|
|
|
|
if (count < 0)
|
|
{
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::write()", "count was negative");
|
|
}
|
|
|
|
return write_impl(ptr, count);
|
|
}
|
|
|
|
int64_t tc::io::FileStream::seek(int64_t offset, SeekOrigin origin)
|
|
{
|
|
if (mFileHandle == nullptr)
|
|
{
|
|
throw tc::ObjectDisposedException(kClassName+"::seek()", "Failed to set stream position (stream is disposed)");
|
|
}
|
|
|
|
if (mCanSeek == false)
|
|
{
|
|
throw tc::NotSupportedException(kClassName+"::seek()", "Stream does not support seeking");
|
|
}
|
|
|
|
if (mIsAppendRestrictSeekCall == true)
|
|
{
|
|
throw tc::io::IOException(kClassName+"::seek()", "Streams opened in Append mode are not allowed to change file position.");
|
|
}
|
|
|
|
return seek_impl(offset, origin);
|
|
}
|
|
|
|
void tc::io::FileStream::setLength(int64_t length)
|
|
{
|
|
if (mFileHandle == nullptr)
|
|
{
|
|
throw tc::ObjectDisposedException(kClassName+"::setLength()", "Failed to set stream length (stream is disposed)");
|
|
}
|
|
|
|
if (mCanWrite == false || mCanSeek == false)
|
|
{
|
|
throw tc::NotSupportedException(kClassName+"::setLength()", "Stream does not support both writing and seeking");
|
|
}
|
|
|
|
setLength_impl(length);
|
|
}
|
|
|
|
void tc::io::FileStream::flush()
|
|
{
|
|
if (mFileHandle != nullptr)
|
|
{
|
|
flush_impl();
|
|
}
|
|
}
|
|
|
|
void tc::io::FileStream::dispose()
|
|
{
|
|
if (mFileHandle.get() != nullptr)
|
|
{
|
|
flush();
|
|
mFileHandle.reset();
|
|
}
|
|
mCanRead = false;
|
|
mCanWrite = false;
|
|
mCanSeek = false;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
#pragma warning(disable : 4065) // disable warning for switch case with only default case
|
|
|
|
void tc::io::FileStream::open_impl(const tc::io::Path& path, FileMode mode, FileAccess access)
|
|
{
|
|
// convert Path to unicode string
|
|
std::u16string unicode_path = path.to_u16string(tc::io::Path::Format::Win32);
|
|
|
|
DWORD access_flag = 0;
|
|
DWORD share_mode_flag = 0;
|
|
DWORD creation_flag = 0;
|
|
|
|
// process mode
|
|
switch (mode)
|
|
{
|
|
case (FileMode::CreateNew):
|
|
// create file if does not exist | return error if file does not exist
|
|
creation_flag = CREATE_NEW;
|
|
break;
|
|
case (FileMode::Create):
|
|
// create file if does not exist | truncate file if it exists
|
|
creation_flag = CREATE_ALWAYS;
|
|
break;
|
|
case (FileMode::Open):
|
|
// no flags
|
|
creation_flag = OPEN_EXISTING;
|
|
break;
|
|
case (FileMode::OpenOrCreate):
|
|
// create file if does not exist
|
|
creation_flag = access == FileAccess::Read ? OPEN_EXISTING : OPEN_ALWAYS;
|
|
break;
|
|
case (FileMode::Truncate):
|
|
// truncate file if file exists
|
|
creation_flag = TRUNCATE_EXISTING;
|
|
break;
|
|
case (FileMode::Append):
|
|
// open in append mode
|
|
creation_flag = OPEN_ALWAYS;
|
|
break;
|
|
default:
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::open()", "Illegal value for mode");
|
|
}
|
|
|
|
// process access
|
|
switch (access)
|
|
{
|
|
case (FileAccess::Read):
|
|
// read access
|
|
access_flag = GENERIC_READ;
|
|
// shared read lock
|
|
share_mode_flag = FILE_SHARE_READ;
|
|
break;
|
|
case (FileAccess::Write):
|
|
// write access
|
|
access_flag = GENERIC_WRITE;
|
|
// exclusive lock
|
|
share_mode_flag = 0;
|
|
break;
|
|
case (FileAccess::ReadWrite):
|
|
// read/write access
|
|
access_flag = GENERIC_READ | GENERIC_WRITE;
|
|
// exclusive lock
|
|
share_mode_flag = 0;
|
|
break;
|
|
default:
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::open()", "Illegal value for access");
|
|
}
|
|
|
|
// validate use of write dependent flags (open existing is the only one that supports no write flag)
|
|
if (creation_flag != OPEN_EXISTING && !(access_flag & GENERIC_WRITE))
|
|
{
|
|
throw tc::ArgumentException(kClassName + "::open()", "Stream open mode requires write access, but write access was not allowed");
|
|
}
|
|
|
|
// append can only open in write only mode
|
|
if (mode == tc::io::FileMode::Append && (access_flag & GENERIC_READ | GENERIC_WRITE) != GENERIC_WRITE)
|
|
{
|
|
throw tc::ArgumentException(kClassName + "::open()", "Stream opened in Append mode can only work with Write access. ReadWrite is not permitted");
|
|
}
|
|
|
|
// open file
|
|
HANDLE file_handle = CreateFileW((LPCWSTR)unicode_path.c_str(),
|
|
access_flag,
|
|
share_mode_flag,
|
|
0,
|
|
creation_flag,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
// check file handle
|
|
if (file_handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD error = GetLastError();
|
|
switch (error)
|
|
{
|
|
case (ERROR_FILE_NOT_FOUND):
|
|
case (ERROR_PATH_NOT_FOUND):
|
|
throw tc::io::FileNotFoundException(kClassName+"::open()", PlatformErrorHandlingUtil::GetLastErrorString(error));
|
|
case (ERROR_FILE_EXISTS):
|
|
throw tc::io::FileExistsException(kClassName+"::open()", PlatformErrorHandlingUtil::GetLastErrorString(error));
|
|
case (ERROR_INVALID_PARAMETER):
|
|
throw tc::ArgumentException(kClassName + "::open()", PlatformErrorHandlingUtil::GetLastErrorString(error));
|
|
case (ERROR_ACCESS_DENIED):
|
|
throw tc::UnauthorisedAccessException(kClassName+"::open()", PlatformErrorHandlingUtil::GetLastErrorString(error));
|
|
default:
|
|
throw tc::io::IOException(kClassName+"::open()", "Failed to open file stream (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
|
}
|
|
}
|
|
|
|
// store file handle
|
|
mFileHandle = std::unique_ptr<tc::io::FileStream::FileHandle>(new tc::io::FileStream::FileHandle(file_handle));
|
|
|
|
// seek to end of file if in append mode
|
|
if (mode == FileMode::Append)
|
|
{
|
|
seek_impl(0, SeekOrigin::End);
|
|
mIsAppendRestrictSeekCall = true;
|
|
}
|
|
|
|
|
|
// set state flags
|
|
mCanRead = (access_flag & GENERIC_READ) ? true : false;
|
|
mCanWrite = (access_flag & GENERIC_WRITE) ? true : false;
|
|
mCanSeek = GetFileType(mFileHandle->handle) == FILE_TYPE_DISK ? true : false;
|
|
}
|
|
|
|
int64_t tc::io::FileStream::length_impl()
|
|
{
|
|
LARGE_INTEGER stream_length;
|
|
|
|
if (GetFileSizeEx(mFileHandle->handle, &stream_length) == false)
|
|
{
|
|
DWORD error = GetLastError();
|
|
switch (error)
|
|
{
|
|
// TODO: Directly handle usual errors for custom exceptions
|
|
default:
|
|
throw tc::io::IOException(kClassName+"::length()", "Failed to get stream length (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
|
}
|
|
}
|
|
|
|
return (int64_t) stream_length.QuadPart;
|
|
}
|
|
|
|
size_t tc::io::FileStream::read_impl(byte_t* ptr, size_t count)
|
|
{
|
|
DWORD bytes_read;
|
|
|
|
if (ReadFile(mFileHandle->handle, ptr, (DWORD)count, &bytes_read, NULL) == false)
|
|
{
|
|
DWORD error = GetLastError();
|
|
switch (error)
|
|
{
|
|
// TODO: Directly handle usual errors for custom exceptions
|
|
default:
|
|
throw tc::io::IOException(kClassName+"::read()", "Failed to read from stream (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
|
}
|
|
}
|
|
|
|
return bytes_read;
|
|
}
|
|
|
|
size_t tc::io::FileStream::write_impl(const byte_t* ptr, size_t count)
|
|
{
|
|
DWORD bytes_written;
|
|
|
|
if (WriteFile(mFileHandle->handle, ptr, (DWORD)count, &bytes_written, NULL) == false)
|
|
{
|
|
DWORD error = GetLastError();
|
|
switch (error)
|
|
{
|
|
// TODO: Directly handle usual errors for custom exceptions
|
|
default:
|
|
throw tc::io::IOException(kClassName+"::write()", "Failed to write to stream (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
|
}
|
|
}
|
|
|
|
return bytes_written;
|
|
}
|
|
|
|
int64_t tc::io::FileStream::seek_impl(int64_t offset, SeekOrigin origin)
|
|
{
|
|
DWORD seek_flag = 0;
|
|
switch(origin)
|
|
{
|
|
case (SeekOrigin::Begin):
|
|
seek_flag = FILE_BEGIN;
|
|
break;
|
|
case (SeekOrigin::Current):
|
|
seek_flag = FILE_CURRENT;
|
|
break;
|
|
case (SeekOrigin::End):
|
|
seek_flag = FILE_END;
|
|
break;
|
|
default:
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::seek()", "Unknown SeekOrigin value");
|
|
}
|
|
|
|
LARGE_INTEGER win_pos, out;
|
|
win_pos.QuadPart = offset;
|
|
if (SetFilePointerEx(
|
|
mFileHandle->handle,
|
|
win_pos,
|
|
&out,
|
|
seek_flag
|
|
) == false)
|
|
{
|
|
DWORD error = GetLastError();
|
|
switch (error)
|
|
{
|
|
case (ERROR_NEGATIVE_SEEK):
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::seek()", PlatformErrorHandlingUtil::GetLastErrorString(error));
|
|
default:
|
|
throw tc::io::IOException(kClassName+"::seek()", "Failed to set stream position (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
|
}
|
|
}
|
|
|
|
return out.QuadPart;
|
|
}
|
|
|
|
void tc::io::FileStream::setLength_impl(int64_t length)
|
|
{
|
|
seek(length, tc::io::SeekOrigin::Begin);
|
|
|
|
if (SetEndOfFile(
|
|
mFileHandle->handle
|
|
) == false)
|
|
{
|
|
DWORD error = GetLastError();
|
|
switch (error)
|
|
{
|
|
default:
|
|
throw tc::io::IOException(kClassName+"::setLength()", "Failed to set end of file (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
|
}
|
|
}
|
|
}
|
|
|
|
void tc::io::FileStream::flush_impl()
|
|
{
|
|
if (mCanWrite)
|
|
{
|
|
// flush buffers only applies to written data
|
|
FlushFileBuffers(mFileHandle->handle);
|
|
}
|
|
}
|
|
|
|
#pragma warning(default : 4065) // reenable warning for switch case with only default case
|
|
|
|
#else
|
|
void tc::io::FileStream::open_impl(const tc::io::Path& path, FileMode mode, FileAccess access)
|
|
{
|
|
// convert Path to unicode string
|
|
std::string unicode_path = path.to_string(tc::io::Path::Format::POSIX);
|
|
|
|
// open file
|
|
int open_flag = 0;
|
|
|
|
// process mode
|
|
switch (mode)
|
|
{
|
|
case (FileMode::CreateNew):
|
|
// create file if does not exist | return error if file does not exist
|
|
open_flag |= O_CREAT | O_EXCL;
|
|
break;
|
|
case (FileMode::Create):
|
|
// create file if does not exist | truncate file if it exists
|
|
open_flag |= O_CREAT | O_TRUNC;
|
|
break;
|
|
case (FileMode::Open):
|
|
// no flags
|
|
open_flag |= 0;
|
|
break;
|
|
case (FileMode::OpenOrCreate):
|
|
// create file if does not exist (however only enable create flag if write access is enabled)
|
|
open_flag |= (access == FileAccess::ReadWrite || access == FileAccess::Write) ? O_CREAT : 0;
|
|
break;
|
|
case (FileMode::Truncate):
|
|
// truncate file if file exists
|
|
open_flag |= O_TRUNC;
|
|
break;
|
|
case (FileMode::Append):
|
|
// open in append mode (create file if doesn't exist)
|
|
open_flag |= O_APPEND | O_CREAT;
|
|
break;
|
|
default:
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::open()", "Illegal value for mode");
|
|
}
|
|
|
|
// process access
|
|
switch (access)
|
|
{
|
|
case (FileAccess::Read):
|
|
// read access
|
|
open_flag |= O_RDONLY;
|
|
#ifdef O_SHLOCK
|
|
// shared lock
|
|
open_flag |= O_SHLOCK;
|
|
#endif
|
|
break;
|
|
case (FileAccess::Write):
|
|
// write access
|
|
open_flag |= O_WRONLY;
|
|
#ifdef O_EXLOCK
|
|
// exclusive lock
|
|
open_flag |= O_EXLOCK;
|
|
#endif
|
|
break;
|
|
case (FileAccess::ReadWrite):
|
|
// read/write access
|
|
open_flag |= O_RDWR;
|
|
#ifdef O_EXLOCK
|
|
// exclusive lock
|
|
open_flag |= O_EXLOCK;
|
|
#endif
|
|
break;
|
|
default:
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::open()", "Illegal value for access");
|
|
}
|
|
|
|
// validate use of write dependent flags
|
|
if ((open_flag & (O_APPEND | O_TRUNC | O_CREAT)) && !(open_flag & (O_WRONLY|O_RDWR)))
|
|
{
|
|
throw tc::ArgumentException(kClassName+"::open()", "Stream open mode requires write access, but write access was not allowed");
|
|
}
|
|
// explicitly check APPEND as being write only
|
|
if ((open_flag & (O_APPEND)) && (open_flag & (O_RDWR)))
|
|
{
|
|
throw tc::ArgumentException(kClassName+"::open()", "Stream opened in Append mode can only work with Write access. ReadWrite is not permitted");
|
|
}
|
|
|
|
// open file handle with Read/Write for User, Read for Group, nothing for others
|
|
int file_handle = ::open(unicode_path.c_str(), open_flag, S_IRUSR | S_IWUSR | S_IRGRP);
|
|
|
|
// handle error
|
|
if (file_handle == -1)
|
|
{
|
|
switch (errno)
|
|
{
|
|
case (EACCES):
|
|
case (EROFS):
|
|
throw tc::UnauthorisedAccessException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (ENAMETOOLONG):
|
|
throw tc::io::PathTooLongException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (ENOENT):
|
|
throw tc::io::FileNotFoundException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (EEXIST):
|
|
throw tc::io::FileExistsException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (EINVAL):
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (EFAULT):
|
|
throw tc::AccessViolationException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (EISDIR):
|
|
throw tc::io::FileNotFoundException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (EDQUOT):
|
|
case (EFBIG):
|
|
case (EINTR):
|
|
case (ELOOP):
|
|
case (EMFILE):
|
|
case (ENFILE):
|
|
case (ENOMEM):
|
|
case (ENOSPC):
|
|
case (ENXIO):
|
|
case (EOVERFLOW):
|
|
case (EPERM):
|
|
case (ETXTBSY):
|
|
case (EWOULDBLOCK):
|
|
default:
|
|
throw tc::io::IOException(kClassName+"::open()", "Failed to open file stream (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
|
}
|
|
}
|
|
|
|
// store file handle
|
|
mFileHandle = std::unique_ptr<tc::io::FileStream::FileHandle>(new tc::io::FileStream::FileHandle(file_handle));
|
|
|
|
// get stat info on file
|
|
struct stat stat_buf;
|
|
if (fstat(mFileHandle->handle, &stat_buf) == -1)
|
|
{
|
|
throw tc::io::IOException(kClassName+"::open()", "Failed to check stream properties using fstat() (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
|
}
|
|
|
|
// if this is a directory throw an exception
|
|
if (S_ISDIR(stat_buf.st_mode))
|
|
{
|
|
throw tc::io::FileNotFoundException(kClassName+"::open()", "Path refers to a directory not a file");
|
|
}
|
|
|
|
// set state flags
|
|
// would check O_RDONLY but that resolves to 0 so it can't be bitmask checked
|
|
mCanRead = (open_flag & O_RDWR) || !(open_flag & O_WRONLY) ? true : false;
|
|
mCanWrite = (open_flag & (O_WRONLY|O_RDWR)) ? true : false;
|
|
mCanSeek = S_ISREG(stat_buf.st_mode) ? true : false;
|
|
|
|
// seek to end of file if in append mode
|
|
if (mode == FileMode::Append)
|
|
{
|
|
seek_impl(0, SeekOrigin::End);
|
|
mIsAppendRestrictSeekCall = true;
|
|
}
|
|
}
|
|
|
|
int64_t tc::io::FileStream::length_impl()
|
|
{
|
|
int64_t length;
|
|
|
|
// get stat info on file
|
|
struct stat stat_buf;
|
|
if (fstat(mFileHandle->handle, &stat_buf) == -1)
|
|
{
|
|
throw tc::io::IOException(kClassName+"::length()", "Failed to check stream properties using fstat() (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
|
}
|
|
|
|
if (S_ISREG(stat_buf.st_mode))
|
|
{
|
|
length = stat_buf.st_size;
|
|
}
|
|
else
|
|
{
|
|
throw tc::NotSupportedException(kClassName+"::length()", "length() cannot be used with device-files or pipes");
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
size_t tc::io::FileStream::read_impl(byte_t* ptr, size_t count)
|
|
{
|
|
int64_t read_len = ::read(mFileHandle->handle, ptr, count);
|
|
|
|
// handle error
|
|
if (read_len == -1)
|
|
{
|
|
switch (errno)
|
|
{
|
|
case (EINVAL):
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::read()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (EFAULT):
|
|
throw tc::AccessViolationException(kClassName+"::read()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (EISDIR):
|
|
case (EBADF):
|
|
case (EAGAIN):
|
|
case (EINTR):
|
|
case (EIO):
|
|
default:
|
|
throw tc::io::IOException(kClassName+"::read()", "Failed to read from stream (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
|
}
|
|
}
|
|
|
|
return size_t(read_len);
|
|
}
|
|
|
|
size_t tc::io::FileStream::write_impl(const byte_t* ptr, size_t count)
|
|
{
|
|
int64_t write_len = ::write(mFileHandle->handle, ptr, count);
|
|
|
|
// handle error
|
|
if (write_len == -1)
|
|
{
|
|
switch (errno)
|
|
{
|
|
case (EINVAL):
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::write()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (EFAULT):
|
|
throw tc::AccessViolationException(kClassName+"::write()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (EFBIG):
|
|
case (EAGAIN):
|
|
case (EDESTADDRREQ):
|
|
case (EDQUOT):
|
|
case (EINTR):
|
|
case (EIO):
|
|
case (ENOSPC):
|
|
case (EPERM):
|
|
case (EPIPE):
|
|
default:
|
|
throw tc::io::IOException(kClassName+"::write()", "Failed to write to stream (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
|
}
|
|
}
|
|
|
|
return size_t(write_len);
|
|
}
|
|
|
|
int64_t tc::io::FileStream::seek_impl(int64_t offset, SeekOrigin origin)
|
|
{
|
|
int seek_flag = 0;
|
|
switch(origin)
|
|
{
|
|
case (SeekOrigin::Begin):
|
|
seek_flag = SEEK_SET;
|
|
break;
|
|
case (SeekOrigin::Current):
|
|
seek_flag = SEEK_CUR;
|
|
break;
|
|
case (SeekOrigin::End):
|
|
seek_flag = SEEK_END;
|
|
break;
|
|
default:
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::seek()", "Unknown SeekOrigin value");
|
|
}
|
|
|
|
#ifdef _LARGEFILE64_SOURCE
|
|
int64_t fpos = lseek64(mFileHandle->handle, offset, seek_flag);
|
|
#else
|
|
int64_t fpos = lseek(mFileHandle->handle, offset, seek_flag);
|
|
#endif
|
|
|
|
// handle error
|
|
if (fpos == -1)
|
|
{
|
|
switch (errno)
|
|
{
|
|
case (EINVAL):
|
|
throw tc::ArgumentOutOfRangeException(kClassName+"::seek()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (EOVERFLOW):
|
|
throw tc::OverflowException(kClassName+"::seek()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
|
case (EBADF):
|
|
case (ESPIPE):
|
|
case (ENXIO):
|
|
default:
|
|
throw tc::io::IOException(kClassName+"::seek()", "Failed to set stream position (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
|
}
|
|
}
|
|
|
|
return fpos;
|
|
}
|
|
|
|
void tc::io::FileStream::setLength_impl(int64_t length)
|
|
{
|
|
#ifdef _LARGEFILE64_SOURCE
|
|
int trun_res = ftruncate64(mFileHandle->handle, length);
|
|
#else
|
|
int trun_res = ftruncate(mFileHandle->handle, length);
|
|
#endif
|
|
|
|
if (trun_res == -1)
|
|
{
|
|
switch (errno)
|
|
{
|
|
case (EINTR):
|
|
case (EINVAL):
|
|
case (EFBIG):
|
|
case (EIO):
|
|
case (EBADF):
|
|
default:
|
|
throw tc::io::IOException(kClassName+"::seek()", "Failed to set stream position (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void tc::io::FileStream::flush_impl()
|
|
{
|
|
// open/read/write are non-buffered
|
|
}
|
|
#endif |