mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-27 11:06:50 +01:00
feature(FileStream): Allow using Poco::FileStream to wrap arbitrary file handles/descriptors as C++ streams (#4424)
* Allow using Poco::FileStream to wrap arbitrary file handles/descriptors as C++ streams (#3444). * Allow opening a file descriptor/HANDLE as C++ stream. * FileStream: treat read from closed pipe as EOF. * chore(Filestream): conde style (naming) Co-Authored-By: Alex Fabijanic <alex@pocoproject.org> Co-Authored-By: Matej Kenda <matejken@gmail.com> * enh(FileStream): make FileIOS::open a virtual function. (#3444) * test(FileStream): unit test for FileStream::openHandle (#3444) * Update CONTRIBUTORS. * test(FileStream): Win32 unit test fix. * build(CMake): Require policy minimum version 3.15. --------- Co-authored-by: Daniel Grunwald <grunwald@axivion.com> Co-authored-by: Alex Fabijanic <alex@pocoproject.org>
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.5.0)
|
cmake_minimum_required(VERSION 3.5.0)
|
||||||
|
cmake_policy(VERSION 3.15.0)
|
||||||
|
|
||||||
project(Poco)
|
project(Poco)
|
||||||
|
|
||||||
|
|||||||
@@ -60,9 +60,11 @@ Friedrich Wilckens
|
|||||||
Pavle Dragišić
|
Pavle Dragišić
|
||||||
Nino Belušić
|
Nino Belušić
|
||||||
Kari Argillander
|
Kari Argillander
|
||||||
Alexander B
|
Alexander Bychuk
|
||||||
Andrew Auclair
|
Andrew Auclair
|
||||||
Jochen Sprickerhof
|
Jochen Sprickerhof
|
||||||
Jesse Hoogervorst
|
Jesse Hoogervorst
|
||||||
Aron Budea
|
Aron Budea
|
||||||
zhuzeitou
|
zhuzeitou
|
||||||
|
Daniel Grunwald
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,16 @@ public:
|
|||||||
~FileIOS();
|
~FileIOS();
|
||||||
/// Destroys the stream.
|
/// Destroys the stream.
|
||||||
|
|
||||||
|
virtual void open(const std::string& path, std::ios::openmode mode);
|
||||||
|
/// Opens the file specified by path, using the given mode.
|
||||||
|
///
|
||||||
|
/// Throws a FileException (or a similar exception) if the file
|
||||||
|
/// does not exist or is not accessible for other reasons and
|
||||||
|
/// a new file cannot be created.
|
||||||
|
|
||||||
|
void openHandle(NativeHandle handle, std::ios::openmode mode);
|
||||||
|
/// Takes ownership of the handle.
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
/// Closes the file stream.
|
/// Closes the file stream.
|
||||||
///
|
///
|
||||||
@@ -108,7 +118,7 @@ public:
|
|||||||
~FileInputStream();
|
~FileInputStream();
|
||||||
/// Destroys the stream.
|
/// Destroys the stream.
|
||||||
|
|
||||||
void open(const std::string& path, std::ios::openmode mode = std::ios::in);
|
void open(const std::string& path, std::ios::openmode mode = std::ios::in) override;
|
||||||
/// Opens the file specified by path, using the given mode, which
|
/// Opens the file specified by path, using the given mode, which
|
||||||
/// will always include std::ios::in (even if not specified).
|
/// will always include std::ios::in (even if not specified).
|
||||||
///
|
///
|
||||||
@@ -151,7 +161,7 @@ public:
|
|||||||
~FileOutputStream();
|
~FileOutputStream();
|
||||||
/// Destroys the FileOutputStream.
|
/// Destroys the FileOutputStream.
|
||||||
|
|
||||||
void open(const std::string& path, std::ios::openmode mode = std::ios::out | std::ios::trunc);
|
void open(const std::string& path, std::ios::openmode mode = std::ios::out | std::ios::trunc) override;
|
||||||
/// Opens the file specified by path, using the given mode, which
|
/// Opens the file specified by path, using the given mode, which
|
||||||
/// always includes std::ios::out, even if not specified.
|
/// always includes std::ios::out, even if not specified.
|
||||||
///
|
///
|
||||||
@@ -196,7 +206,7 @@ public:
|
|||||||
~FileStream();
|
~FileStream();
|
||||||
/// Destroys the FileOutputStream.
|
/// Destroys the FileOutputStream.
|
||||||
|
|
||||||
void open(const std::string& path, std::ios::openmode mode = std::ios::out | std::ios::in);
|
void open(const std::string& path, std::ios::openmode mode = std::ios::out | std::ios::in) override;
|
||||||
/// Opens the file specified by path, using the given mode.
|
/// Opens the file specified by path, using the given mode.
|
||||||
///
|
///
|
||||||
/// Throws a FileException (or a similar exception) if the file
|
/// Throws a FileException (or a similar exception) if the file
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ public:
|
|||||||
void open(const std::string& path, std::ios::openmode mode);
|
void open(const std::string& path, std::ios::openmode mode);
|
||||||
/// Opens the given file in the given mode.
|
/// Opens the given file in the given mode.
|
||||||
|
|
||||||
|
void openHandle(NativeHandle fd, std::ios::openmode mode);
|
||||||
|
/// Take ownership of the given file descriptor.
|
||||||
|
|
||||||
bool close();
|
bool close();
|
||||||
/// Closes the File stream buffer. Returns true if successful,
|
/// Closes the File stream buffer. Returns true if successful,
|
||||||
/// false otherwise.
|
/// false otherwise.
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ public:
|
|||||||
void open(const std::string& path, std::ios::openmode mode);
|
void open(const std::string& path, std::ios::openmode mode);
|
||||||
/// Opens the given file in the given mode.
|
/// Opens the given file in the given mode.
|
||||||
|
|
||||||
|
void openHandle(NativeHandle handle, std::ios::openmode mode);
|
||||||
|
/// Take ownership of the given HANDLE.
|
||||||
|
|
||||||
bool close();
|
bool close();
|
||||||
/// Closes the File stream buffer. Returns true if successful,
|
/// Closes the File stream buffer. Returns true if successful,
|
||||||
/// false otherwise.
|
/// false otherwise.
|
||||||
|
|||||||
@@ -35,6 +35,20 @@ FileIOS::~FileIOS()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FileIOS::open(const std::string& path, std::ios::openmode mode)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
_buf.open(path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FileIOS::openHandle(NativeHandle handle, std::ios::openmode mode)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
_buf.openHandle(handle, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void FileIOS::close()
|
void FileIOS::close()
|
||||||
{
|
{
|
||||||
if (!_buf.close())
|
if (!_buf.close())
|
||||||
@@ -77,7 +91,7 @@ FileInputStream::FileInputStream():
|
|||||||
FileInputStream::FileInputStream(const std::string& path, std::ios::openmode mode):
|
FileInputStream::FileInputStream(const std::string& path, std::ios::openmode mode):
|
||||||
std::istream(&_buf)
|
std::istream(&_buf)
|
||||||
{
|
{
|
||||||
open(path, mode | std::ios::in);
|
open(path, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -88,8 +102,7 @@ FileInputStream::~FileInputStream()
|
|||||||
|
|
||||||
void FileInputStream::open(const std::string& path, std::ios::openmode mode)
|
void FileInputStream::open(const std::string& path, std::ios::openmode mode)
|
||||||
{
|
{
|
||||||
clear();
|
FileIOS::open(path, mode | std::ios::in);
|
||||||
_buf.open(path, mode | std::ios::in);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -102,7 +115,7 @@ FileOutputStream::FileOutputStream():
|
|||||||
FileOutputStream::FileOutputStream(const std::string& path, std::ios::openmode mode):
|
FileOutputStream::FileOutputStream(const std::string& path, std::ios::openmode mode):
|
||||||
std::ostream(&_buf)
|
std::ostream(&_buf)
|
||||||
{
|
{
|
||||||
open(path, mode | std::ios::out);
|
open(path, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -113,8 +126,7 @@ FileOutputStream::~FileOutputStream()
|
|||||||
|
|
||||||
void FileOutputStream::open(const std::string& path, std::ios::openmode mode)
|
void FileOutputStream::open(const std::string& path, std::ios::openmode mode)
|
||||||
{
|
{
|
||||||
clear();
|
FileIOS::open(path, mode | std::ios::out);
|
||||||
_buf.open(path, mode | std::ios::out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -138,8 +150,7 @@ FileStream::~FileStream()
|
|||||||
|
|
||||||
void FileStream::open(const std::string& path, std::ios::openmode mode)
|
void FileStream::open(const std::string& path, std::ios::openmode mode)
|
||||||
{
|
{
|
||||||
clear();
|
FileIOS::open(path, mode);
|
||||||
_buf.open(path, mode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,22 @@ void FileStreamBuf::open(const std::string& path, std::ios::openmode mode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FileStreamBuf::openHandle(NativeHandle fd, std::ios::openmode mode)
|
||||||
|
{
|
||||||
|
poco_assert(_fd == -1);
|
||||||
|
poco_assert(fd != -1);
|
||||||
|
|
||||||
|
_pos = 0;
|
||||||
|
setMode(mode);
|
||||||
|
resetBuffers();
|
||||||
|
|
||||||
|
_fd = fd;
|
||||||
|
|
||||||
|
if ((mode & std::ios::app) || (mode & std::ios::ate))
|
||||||
|
seekoff(0, std::ios::end, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int FileStreamBuf::readFromDevice(char* buffer, std::streamsize length)
|
int FileStreamBuf::readFromDevice(char* buffer, std::streamsize length)
|
||||||
{
|
{
|
||||||
if (_fd == -1) return -1;
|
if (_fd == -1) return -1;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#include "Poco/FileStream.h"
|
#include "Poco/FileStream.h"
|
||||||
#include "Poco/File.h"
|
#include "Poco/File.h"
|
||||||
#include "Poco/Exception.h"
|
#include "Poco/Exception.h"
|
||||||
#include "Poco/UnicodeConverter.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace Poco {
|
namespace Poco {
|
||||||
@@ -74,6 +73,22 @@ void FileStreamBuf::open(const std::string& path, std::ios::openmode mode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FileStreamBuf::openHandle(NativeHandle handle, std::ios::openmode mode)
|
||||||
|
{
|
||||||
|
poco_assert(_handle == INVALID_HANDLE_VALUE);
|
||||||
|
poco_assert(handle != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
_pos = 0;
|
||||||
|
setMode(mode);
|
||||||
|
resetBuffers();
|
||||||
|
|
||||||
|
_handle = handle;
|
||||||
|
|
||||||
|
if ((mode & std::ios::ate) || (mode & std::ios::app))
|
||||||
|
seekoff(0, std::ios::end, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int FileStreamBuf::readFromDevice(char* buffer, std::streamsize length)
|
int FileStreamBuf::readFromDevice(char* buffer, std::streamsize length)
|
||||||
{
|
{
|
||||||
if (INVALID_HANDLE_VALUE == _handle || !(getMode() & std::ios::in))
|
if (INVALID_HANDLE_VALUE == _handle || !(getMode() & std::ios::in))
|
||||||
@@ -85,7 +100,14 @@ int FileStreamBuf::readFromDevice(char* buffer, std::streamsize length)
|
|||||||
DWORD bytesRead(0);
|
DWORD bytesRead(0);
|
||||||
BOOL rc = ReadFile(_handle, buffer, static_cast<DWORD>(length), &bytesRead, NULL);
|
BOOL rc = ReadFile(_handle, buffer, static_cast<DWORD>(length), &bytesRead, NULL);
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
|
{
|
||||||
|
if (GetLastError() == ERROR_BROKEN_PIPE)
|
||||||
|
{
|
||||||
|
// Read from closed pipe -> treat as EOF
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
File::handleLastError(_path);
|
File::handleLastError(_path);
|
||||||
|
}
|
||||||
|
|
||||||
_pos += bytesRead;
|
_pos += bytesRead;
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,54 @@ void FileStreamTest::testRead()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(POCO_OS_FAMILY_WINDOWS)
|
||||||
|
#include "Poco/UnicodeConverter.h"
|
||||||
|
#else
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void FileStreamTest::testWriteReadNativeHandle()
|
||||||
|
{
|
||||||
|
Poco::FileOutputStream fos;
|
||||||
|
Poco::FileInputStream fis;
|
||||||
|
Poco::FileIOS::NativeHandle outHandle;
|
||||||
|
|
||||||
|
#if defined(POCO_OS_FAMILY_WINDOWS)
|
||||||
|
char tmp[]={'\xc3', '\x84', '\xc3', '\x96', '\xc3', '\x9c', '\xc3', '\xa4', '\xc3', '\xb6', '\xc3', '\xbc', '\0'};
|
||||||
|
std::string file(tmp);
|
||||||
|
file.append(".txt");
|
||||||
|
std::wstring utf16Path;
|
||||||
|
Poco::UnicodeConverter::toUTF16(file, utf16Path);
|
||||||
|
outHandle = CreateFileW(utf16Path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
assertTrue(outHandle != INVALID_HANDLE_VALUE);
|
||||||
|
#else
|
||||||
|
std::string file("testfile.txt");
|
||||||
|
outHandle = ::open(file.c_str(), O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Poco::TemporaryFile::registerForDeletion(file);
|
||||||
|
|
||||||
|
fos.openHandle(outHandle, std::ios::binary | std::ios::out | std::ios::trunc);
|
||||||
|
fos << "sometestdata";
|
||||||
|
fos.close();
|
||||||
|
|
||||||
|
Poco::FileIOS::NativeHandle inHandle;
|
||||||
|
|
||||||
|
#if defined(POCO_OS_FAMILY_WINDOWS)
|
||||||
|
inHandle = CreateFileW(utf16Path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
assertTrue(inHandle != INVALID_HANDLE_VALUE);
|
||||||
|
#else
|
||||||
|
inHandle = ::open(file.c_str(), O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fis.openHandle(inHandle, std::ios::in);
|
||||||
|
assertTrue (fis.good());
|
||||||
|
std::string read;
|
||||||
|
fis >> read;
|
||||||
|
assertTrue (!read.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void FileStreamTest::testWrite()
|
void FileStreamTest::testWrite()
|
||||||
{
|
{
|
||||||
#if defined(POCO_OS_FAMILY_WINDOWS)
|
#if defined(POCO_OS_FAMILY_WINDOWS)
|
||||||
@@ -304,6 +352,7 @@ CppUnit::Test* FileStreamTest::suite()
|
|||||||
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("FileStreamTest");
|
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("FileStreamTest");
|
||||||
|
|
||||||
CppUnit_addTest(pSuite, FileStreamTest, testRead);
|
CppUnit_addTest(pSuite, FileStreamTest, testRead);
|
||||||
|
CppUnit_addTest(pSuite, FileStreamTest, testWriteReadNativeHandle);
|
||||||
CppUnit_addTest(pSuite, FileStreamTest, testWrite);
|
CppUnit_addTest(pSuite, FileStreamTest, testWrite);
|
||||||
CppUnit_addTest(pSuite, FileStreamTest, testReadWrite);
|
CppUnit_addTest(pSuite, FileStreamTest, testReadWrite);
|
||||||
CppUnit_addTest(pSuite, FileStreamTest, testOpen);
|
CppUnit_addTest(pSuite, FileStreamTest, testOpen);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public:
|
|||||||
~FileStreamTest();
|
~FileStreamTest();
|
||||||
|
|
||||||
void testRead();
|
void testRead();
|
||||||
|
void testWriteReadNativeHandle();
|
||||||
void testWrite();
|
void testWrite();
|
||||||
void testReadWrite();
|
void testReadWrite();
|
||||||
void testOpen();
|
void testOpen();
|
||||||
|
|||||||
Reference in New Issue
Block a user