Implement NativeFileStream under Unix/Linux/Android as well

* Like the Windows implementation this is disabled by default and
  can be enabled via compile-time switch which will affect the ABI.
* Add support for opening a file from a file descriptor.
This commit is contained in:
Martchus 2018-09-05 00:21:40 +02:00
parent 826c43ef8b
commit 5c9a834236
2 changed files with 142 additions and 31 deletions

View File

@ -1,21 +1,88 @@
#include "./nativefilestream.h"
#if defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER) && (defined(PLATFORM_MINGW) || defined(PLATFORM_LINUX))
#include "./catchiofailure.h"
#ifdef PLATFORM_MINGW
#include "catchiofailure.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <io.h>
#include <sys/stat.h> // yes, this is needed under Windows (https://msdn.microsoft.com/en-US/library/5yhhz3y7.aspx)
#include <windows.h>
#endif
#ifdef PLATFORM_LINUX
#include <cstdio>
#endif
#endif
using namespace std;
namespace IoUtilities {
#if !defined(PLATFORM_MINGW) || !defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER)
#if defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER) && (defined(PLATFORM_MINGW) || defined(PLATFORM_UNIX))
// just use std::fstream
struct NativeFileParams {
#ifdef PLATFORM_MINGW
NativeFileParams(ios_base::openmode cppOpenMode)
: openMode(cppOpenMode & ios_base::binary ? _O_BINARY : 0)
, flags(cppOpenMode & ios_base::binary ? 0 : _O_TEXT)
, permissions(0)
{
if ((cppOpenMode & ios_base::out) && (cppOpenMode & ios_base::in)) {
openMode |= _O_RDWR;
} else if (cppOpenMode & ios_base::out) {
openMode |= _O_WRONLY | _O_CREAT;
permissions = _S_IREAD | _S_IWRITE;
} else if (cppOpenMode & ios_base::in) {
openMode |= _O_RDONLY;
flags |= _O_RDONLY;
}
if (cppOpenMode & ios_base::app) {
openMode |= _O_APPEND;
flags |= _O_APPEND;
}
if (cppOpenMode & ios_base::trunc) {
openMode |= _O_TRUNC;
}
}
int openMode;
int flags;
int permissions;
#else
NativeFileParams(ios_base::openmode cppOpenMode)
{
if ((cppOpenMode & ios_base::in) && (cppOpenMode & ios_base::out)) {
if (cppOpenMode & ios_base::app) {
openMode = "a+";
} else if (cppOpenMode & ios_base::trunc) {
openMode = "w+";
} else {
openMode = "r+";
}
} else if (cppOpenMode & ios_base::in) {
openMode = 'r';
} else if (cppOpenMode & ios_base::out) {
if (cppOpenMode & ios_base::app) {
openMode = 'a';
} else if (cppOpenMode & ios_base::trunc) {
openMode = 'w';
} else {
openMode = "r+";
}
}
if (cppOpenMode & ios_base::binary) {
openMode += 'b';
}
}
std::string openMode;
#endif
};
NativeFileStream::NativeFileStream()
: m_filebuf(new __gnu_cxx::stdio_filebuf<char>)
@ -23,12 +90,19 @@ NativeFileStream::NativeFileStream()
rdbuf(m_filebuf.get());
}
NativeFileStream::NativeFileStream(NativeFileStream &&other)
: m_filebuf(std::move(other.m_filebuf))
, m_cfile(other.m_cfile)
{
}
NativeFileStream::~NativeFileStream()
{
}
void NativeFileStream::open(const string &path, ios_base::openmode flags)
void NativeFileStream::open(const string &path, ios_base::openmode openMode)
{
#ifdef PLATFORM_MINGW
// convert path to UTF-16
int requiredSize = MultiByteToWideChar(CP_UTF8, 0, path.data(), -1, nullptr, 0);
if (requiredSize <= 0) {
@ -39,26 +113,45 @@ void NativeFileStream::open(const string &path, ios_base::openmode flags)
if (requiredSize <= 0) {
::IoUtilities::throwIoFailure("Unable to convert path to UTF-16");
}
// translate flags
int nativeFlags = (flags & ios_base::binary ? _O_BINARY : 0);
int permissions = 0;
if ((flags & ios_base::out) && (flags & ios_base::in)) {
nativeFlags |= _O_RDWR;
} else if (flags & ios_base::out) {
nativeFlags |= _O_WRONLY | _O_CREAT;
permissions = _S_IREAD | _S_IWRITE;
} else if (flags & ios_base::in) {
nativeFlags |= _O_RDONLY;
}
if (flags & ios_base::trunc) {
nativeFlags |= _O_TRUNC;
}
// initialize stdio_filebuf
int fd = _wopen(widePath.get(), nativeFlags, permissions);
if (fd == -1) {
const NativeFileParams nativeParams(openMode);
const int fileHandle = _wopen(widePath.get(), nativeParams.openMode, nativeParams.permissions);
if (fileHandle == -1) {
::IoUtilities::throwIoFailure("_wopen failed");
}
m_filebuf = make_unique<__gnu_cxx::stdio_filebuf<char>>(fd, flags);
#else
// initialize stdio_filebuf
const NativeFileParams nativeParams(openMode);
const auto fileHandle = fopen(path.data(), nativeParams.openMode.data());
if (!fileHandle) {
::IoUtilities::throwIoFailure("fopen failed");
}
#endif
m_filebuf = make_unique<__gnu_cxx::stdio_filebuf<char>>(fileHandle, openMode);
rdbuf(m_filebuf.get());
}
void NativeFileStream::openFromFileDescriptor(int fileDescriptor, ios_base::openmode openMode)
{
const NativeFileParams nativeParams(openMode);
#ifdef PLATFORM_MINGW
const auto fileHandle = _get_osfhandle(fileDescriptor);
if (fileHandle == -1) {
::IoUtilities::throwIoFailure("_get_osfhandle failed");
}
const auto osFileHandle = _open_osfhandle(fileHandle, nativeParams.flags);
if (osFileHandle == -1) {
::IoUtilities::throwIoFailure("_open_osfhandle failed");
}
#else
const auto fileHandle = fdopen(fileDescriptor, nativeParams.openMode.data());
if (!fileHandle) {
::IoUtilities::throwIoFailure("fdopen failed");
}
#endif
m_filebuf = make_unique<__gnu_cxx::stdio_filebuf<char>>(fileHandle, openMode);
rdbuf(m_filebuf.get());
}
@ -69,5 +162,9 @@ void NativeFileStream::close()
}
}
#else
// std::fstream is used
#endif
} // namespace IoUtilities

View File

@ -3,31 +3,35 @@
#include "../global.h"
#ifndef PLATFORM_MINGW
#include <fstream>
#else
#if defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER)
#if defined(PLATFORM_MINGW) || defined(PLATFORM_LINUX)
#include <ext/stdio_filebuf.h>
#include <iostream>
#include <memory>
#include <string>
#else
#error "Platform not supported by NativeFileStream."
#endif
#else
#include <fstream>
#endif
namespace IoUtilities {
#if !defined(PLATFORM_MINGW) || !defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER)
typedef std::fstream NativeFileStream;
#else
#if defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER) && (defined(PLATFORM_MINGW) || defined(PLATFORM_UNIX))
class CPP_UTILITIES_EXPORT NativeFileStream : public std::iostream {
public:
NativeFileStream();
NativeFileStream(NativeFileStream &&);
~NativeFileStream();
bool is_open() const;
void open(const std::string &path, std::ios_base::openmode flags);
void open(const std::string &path, std::ios_base::openmode openMode);
void openFromFileDescriptor(int fileDescriptor, std::ios_base::openmode openMode);
void close();
std::__c_file fileHandle();
private:
std::unique_ptr<__gnu_cxx::stdio_filebuf<char>> m_filebuf;
@ -39,7 +43,17 @@ inline bool NativeFileStream::is_open() const
return m_filebuf && m_filebuf->is_open();
}
inline std::__c_file NativeFileStream::fileHandle()
{
return m_cfile;
}
#else
typedef std::fstream NativeFileStream;
#endif
} // namespace IoUtilities
#endif // IOUTILITIES_NATIVE_FILE_STREAM