From 5c9a834236b53796102eca19182675f0fa7a8745 Mon Sep 17 00:00:00 2001 From: Martchus Date: Wed, 5 Sep 2018 00:21:40 +0200 Subject: [PATCH] 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. --- io/nativefilestream.cpp | 141 +++++++++++++++++++++++++++++++++------- io/nativefilestream.h | 32 ++++++--- 2 files changed, 142 insertions(+), 31 deletions(-) diff --git a/io/nativefilestream.cpp b/io/nativefilestream.cpp index 36acd7d..251e6cb 100644 --- a/io/nativefilestream.cpp +++ b/io/nativefilestream.cpp @@ -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 -#include +#include +#include // yes, this is needed under Windows (https://msdn.microsoft.com/en-US/library/5yhhz3y7.aspx) #include #endif +#ifdef PLATFORM_LINUX +#include +#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) @@ -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>(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>(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>(fileHandle, openMode); rdbuf(m_filebuf.get()); } @@ -69,5 +162,9 @@ void NativeFileStream::close() } } +#else + +// std::fstream is used + #endif } // namespace IoUtilities diff --git a/io/nativefilestream.h b/io/nativefilestream.h index 5d4d4ec..873c044 100644 --- a/io/nativefilestream.h +++ b/io/nativefilestream.h @@ -3,31 +3,35 @@ #include "../global.h" -#ifndef PLATFORM_MINGW -#include -#else +#if defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER) +#if defined(PLATFORM_MINGW) || defined(PLATFORM_LINUX) #include #include #include #include +#else +#error "Platform not supported by NativeFileStream." +#endif + +#else +#include #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> 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