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:
Matej Kenda 2024-05-08 11:53:43 +02:00 committed by GitHub
parent 1a0355f1b7
commit 065f9a0ff9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 131 additions and 13 deletions

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.5.0)
cmake_policy(VERSION 3.15.0)
project(Poco)

View File

@ -60,9 +60,11 @@ Friedrich Wilckens
Pavle Dragišić
Nino Belušić
Kari Argillander
Alexander B
Alexander Bychuk
Andrew Auclair
Jochen Sprickerhof
Jesse Hoogervorst
Aron Budea
zhuzeitou
Daniel Grunwald

View File

@ -56,6 +56,16 @@ public:
~FileIOS();
/// 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();
/// Closes the file stream.
///
@ -108,7 +118,7 @@ public:
~FileInputStream();
/// 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
/// will always include std::ios::in (even if not specified).
///
@ -151,7 +161,7 @@ public:
~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
/// always includes std::ios::out, even if not specified.
///
@ -196,7 +206,7 @@ public:
~FileStream();
/// 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.
///
/// Throws a FileException (or a similar exception) if the file

View File

@ -42,6 +42,9 @@ public:
void open(const std::string& path, std::ios::openmode 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();
/// Closes the File stream buffer. Returns true if successful,
/// false otherwise.

View File

@ -41,6 +41,9 @@ public:
void open(const std::string& path, std::ios::openmode 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();
/// Closes the File stream buffer. Returns true if successful,
/// false otherwise.

View File

@ -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()
{
if (!_buf.close())
@ -77,7 +91,7 @@ FileInputStream::FileInputStream():
FileInputStream::FileInputStream(const std::string& path, std::ios::openmode mode):
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)
{
clear();
_buf.open(path, mode | std::ios::in);
FileIOS::open(path, mode | std::ios::in);
}
@ -102,7 +115,7 @@ FileOutputStream::FileOutputStream():
FileOutputStream::FileOutputStream(const std::string& path, std::ios::openmode mode):
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)
{
clear();
_buf.open(path, mode | std::ios::out);
FileIOS::open(path, mode | std::ios::out);
}
@ -138,8 +150,7 @@ FileStream::~FileStream()
void FileStream::open(const std::string& path, std::ios::openmode mode)
{
clear();
_buf.open(path, mode);
FileIOS::open(path, mode);
}

View File

@ -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)
{
if (_fd == -1) return -1;

View File

@ -15,7 +15,6 @@
#include "Poco/FileStream.h"
#include "Poco/File.h"
#include "Poco/Exception.h"
#include "Poco/UnicodeConverter.h"
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)
{
if (INVALID_HANDLE_VALUE == _handle || !(getMode() & std::ios::in))
@ -85,7 +100,14 @@ int FileStreamBuf::readFromDevice(char* buffer, std::streamsize length)
DWORD bytesRead(0);
BOOL rc = ReadFile(_handle, buffer, static_cast<DWORD>(length), &bytesRead, NULL);
if (rc == 0)
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
// Read from closed pipe -> treat as EOF
return 0;
}
File::handleLastError(_path);
}
_pos += bytesRead;

View File

@ -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()
{
#if defined(POCO_OS_FAMILY_WINDOWS)
@ -304,6 +352,7 @@ CppUnit::Test* FileStreamTest::suite()
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("FileStreamTest");
CppUnit_addTest(pSuite, FileStreamTest, testRead);
CppUnit_addTest(pSuite, FileStreamTest, testWriteReadNativeHandle);
CppUnit_addTest(pSuite, FileStreamTest, testWrite);
CppUnit_addTest(pSuite, FileStreamTest, testReadWrite);
CppUnit_addTest(pSuite, FileStreamTest, testOpen);

View File

@ -25,6 +25,7 @@ public:
~FileStreamTest();
void testRead();
void testWriteReadNativeHandle();
void testWrite();
void testReadWrite();
void testOpen();