AyuGramDesktop/Telegram/SourceFiles/ayu/libs/pipe.hpp
2023-07-10 14:13:20 +00:00

856 lines
26 KiB
C++

///////////////////////////////////////////////////////////
/// Copyright 2019 Alexy Pellegrini
///
/// Permission is hereby granted, free of charge,
/// to any person obtaining a copy of this software
/// and associated documentation files (the "Software"),
/// to deal in the Software without restriction,
/// including without limitation the rights to use,
/// copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit
/// persons to whom the Software is furnished to do so,
/// subject to the following conditions:
///
/// The above copyright notice and this permission notice
/// shall be included in all copies or substantial portions
/// of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
/// OR OTHER DEALINGS IN THE SOFTWARE.
///////////////////////////////////////////////////////////
#ifndef NOT_ENOUGH_STANDARDS_PIPE
#define NOT_ENOUGH_STANDARDS_PIPE
#if defined(_WIN32)
#define NES_WIN32_PIPE
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
#define NES_POSIX_PIPE
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#else
#error "Not enough standards does not support this environment."
#endif
#include <vector>
#include <algorithm>
#include <streambuf>
#include <istream>
#include <ostream>
#include <memory>
#include <cassert>
#include <utility>
#if defined(NES_WIN32_PIPE)
namespace nes
{
inline constexpr const char pipe_root[] = "\\\\.\\pipe\\";
template<typename CharT, typename Traits>
class basic_pipe_istream;
template<typename CharT, typename Traits>
class basic_pipe_ostream;
template<typename CharT = char, typename Traits = std::char_traits<CharT>>
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe();
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_streambuf : public std::basic_streambuf<CharT, Traits>
{
private:
using parent_type = std::basic_streambuf<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
static constexpr std::size_t buf_size{4096};
public:
basic_pipe_streambuf() = default;
explicit basic_pipe_streambuf(const std::string& name, std::ios_base::openmode mode)
{
open(name, mode);
}
virtual ~basic_pipe_streambuf()
{
close();
}
basic_pipe_streambuf(const basic_pipe_streambuf&) = delete;
basic_pipe_streambuf& operator=(const basic_pipe_streambuf&) = delete;
basic_pipe_streambuf(basic_pipe_streambuf&& other) noexcept
:parent_type{other}
,m_buffer{std::move(other.m_buffer)}
,m_handle{std::exchange(other.m_handle, INVALID_HANDLE_VALUE)}
,m_mode{std::exchange(other.m_mode, std::ios_base::openmode{})}
{
}
basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other) noexcept
{
parent_type::operator=(other);
m_buffer = std::move(other.m_buffer);
m_handle = std::exchange(other.m_handle, m_handle);
m_mode = std::exchange(other.m_mode, m_mode);
return *this;
}
bool open(const std::string& name, std::ios_base::openmode mode)
{
assert(!((mode & std::ios_base::in) && (mode & std::ios_base::out)) && "nes::basic_pipe_streambuf::open called with mode = std::ios_base::in | std::ios_base::out.");
close();
const auto native_name{to_wide(pipe_root + name)};
DWORD native_mode{mode & std::ios_base::in ? GENERIC_READ : GENERIC_WRITE};
HANDLE handle = CreateFileW(std::data(native_name), native_mode, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if(handle == INVALID_HANDLE_VALUE)
{
if(GetLastError() == ERROR_FILE_NOT_FOUND)
{
native_mode = mode & std::ios_base::in ? PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND;
handle = CreateNamedPipeW(std::data(native_name), native_mode, PIPE_TYPE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, buf_size, buf_size, 0, nullptr);
if(handle == INVALID_HANDLE_VALUE)
return false;
if(!ConnectNamedPipe(handle, nullptr))
{
CloseHandle(handle);
return false;
}
}
}
m_buffer.resize(buf_size);
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
m_handle = handle;
m_mode = mode;
return true;
}
bool is_open() const noexcept
{
return m_handle != INVALID_HANDLE_VALUE;
}
void close()
{
if(is_open())
{
sync();
m_mode = std::ios_base::openmode{};
CloseHandle(std::exchange(m_handle, INVALID_HANDLE_VALUE));
parent_type::setp(nullptr, nullptr);
parent_type::setg(nullptr, nullptr, nullptr);
}
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_streambuf(HANDLE handle, std::ios_base::openmode mode)
:m_handle{handle}
,m_mode{mode}
{
m_buffer.resize(buf_size);
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
protected:
virtual int sync() override
{
if(m_mode & std::ios_base::out)
{
const std::ptrdiff_t count{parent_type::pptr() - parent_type::pbase()};
DWORD written{};
if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(std::data(m_buffer)), static_cast<DWORD>(count)* sizeof(char_type), &written, nullptr))
return -1;
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
return 0;
}
virtual int_type overflow(int_type c = traits_type::eof()) override
{
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
if(traits_type::eq_int_type(c, traits_type::eof()))
{
DWORD written{};
if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(std::data(m_buffer)), static_cast<DWORD>(buf_size) * sizeof(char_type), &written, nullptr))
return traits_type::eof();
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
else
{
*parent_type::pptr() = traits_type::to_char_type(c);
parent_type::pbump(1);
}
return traits_type::not_eof(c);
}
virtual std::streamsize xsputn(const char_type* s, std::streamsize count) override
{
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
DWORD written{};
if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(s), static_cast<DWORD>(count) * sizeof(char_type), &written, nullptr))
return 0;
return static_cast<std::streamsize>(written);
}
virtual int_type underflow() override
{
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
if(parent_type::gptr() == parent_type::egptr())
{
DWORD readed{};
if(!ReadFile(m_handle, reinterpret_cast<CHAR*>(std::data(m_buffer)), static_cast<DWORD>(buf_size * sizeof(char_type)), &readed, nullptr) || readed == 0)
return traits_type::eof();
parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (readed / sizeof(char_type)));
}
return traits_type::to_int_type(*parent_type::gptr());
}
virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override
{
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
DWORD readed{};
if(!ReadFile(m_handle, reinterpret_cast<CHAR*>(s), static_cast<DWORD>(count) * sizeof(char_type), &readed, nullptr))
return 0;
return static_cast<std::streamsize>(readed / sizeof(char_type));
}
private:
std::wstring to_wide(std::string path)
{
assert(std::size(path) < 0x7FFFFFFFu && "Wrong path.");
if(std::empty(path))
return {};
std::transform(std::begin(path), std::end(path), std::begin(path), [](char c){return c == '/' ? '\\' : c;});
std::wstring out_path{};
out_path.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), nullptr, 0)));
if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), std::data(out_path), static_cast<int>(std::size(out_path))))
throw std::runtime_error{"Failed to convert the path to wide."};
return out_path;
}
private:
std::vector<CharT> m_buffer{};
HANDLE m_handle{INVALID_HANDLE_VALUE};
std::ios_base::openmode m_mode{};
};
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_istream : public std::basic_istream<CharT, Traits>
{
private:
using parent_type = std::basic_istream<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
basic_pipe_istream() = default;
explicit basic_pipe_istream(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
:parent_type{nullptr}
{
parent_type::rdbuf(m_buffer.get());
open(name, mode);
}
virtual ~basic_pipe_istream() = default;
basic_pipe_istream(const basic_pipe_istream&) = delete;
basic_pipe_istream& operator=(const basic_pipe_istream&) = delete;
basic_pipe_istream(basic_pipe_istream&& other) noexcept
:parent_type{std::move(other)}
{
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
}
basic_pipe_istream& operator=(basic_pipe_istream&& other) noexcept
{
parent_type::operator=(std::move(other));
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
return *this;
}
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
{
m_buffer->open(name, mode);
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
}
bool is_open() const noexcept
{
return m_buffer->is_open();
}
void close()
{
m_buffer->close();
}
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
{
return m_buffer.get();
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_istream(basic_pipe_streambuf<char_type, traits_type> buffer)
:parent_type{nullptr}
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
{
parent_type::rdbuf(m_buffer.get());
}
private:
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
};
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_ostream : public std::basic_ostream<CharT, Traits>
{
private:
using parent_type = std::basic_ostream<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
basic_pipe_ostream() = default;
explicit basic_pipe_ostream(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
:parent_type{nullptr}
{
parent_type::rdbuf(m_buffer.get());
open(name, mode);
}
virtual ~basic_pipe_ostream() = default;
basic_pipe_ostream(const basic_pipe_ostream&) = delete;
basic_pipe_ostream& operator=(const basic_pipe_ostream&) = delete;
basic_pipe_ostream(basic_pipe_ostream&& other) noexcept
:parent_type{std::move(other)}
{
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
}
basic_pipe_ostream& operator=(basic_pipe_ostream&& other) noexcept
{
parent_type::operator=(std::move(other));
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
return *this;
}
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
{
m_buffer->open(name, mode);
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
}
bool is_open() const noexcept
{
return m_buffer->is_open();
}
void close()
{
m_buffer->close();
}
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
{
return m_buffer.get();
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_ostream(basic_pipe_streambuf<char_type, traits_type> buffer)
:parent_type{nullptr}
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
{
parent_type::rdbuf(m_buffer.get());
}
private:
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
};
template<typename CharT, typename Traits>
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()
{
HANDLE input{};
HANDLE output{};
if(!CreatePipe(&input, &output, nullptr, 0))
throw std::runtime_error{"Failed to create pipe"};
return std::make_pair(basic_pipe_istream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{input, std::ios_base::in}},
basic_pipe_ostream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{output, std::ios_base::out}});
}
using pipe_streambuf = basic_pipe_streambuf<char>;
using pipe_istream = basic_pipe_istream<char>;
using pipe_ostream = basic_pipe_ostream<char>;
}
#elif defined(NES_POSIX_PIPE)
namespace nes
{
inline constexpr const char pipe_root[] = "/tmp/";
template<typename CharT, typename Traits>
class basic_pipe_istream;
template<typename CharT, typename Traits>
class basic_pipe_ostream;
template<typename CharT = char, typename Traits = std::char_traits<CharT>>
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe();
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_streambuf : public std::basic_streambuf<CharT, Traits>
{
private:
using parent_type = std::basic_streambuf<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
static constexpr std::size_t buf_size{1024};
public:
basic_pipe_streambuf() = default;
explicit basic_pipe_streambuf(const std::string& name, std::ios_base::openmode mode)
:parent_type{nullptr}
{
open(name, mode);
}
virtual ~basic_pipe_streambuf()
{
close();
}
basic_pipe_streambuf(const basic_pipe_streambuf&) = delete;
basic_pipe_streambuf& operator=(const basic_pipe_streambuf&) = delete;
basic_pipe_streambuf(basic_pipe_streambuf&& other) noexcept
:parent_type{std::move(other)}
,m_buffer{std::move(other.m_buffer)}
,m_handle{std::exchange(other.m_handle, 0)}
,m_mode{std::exchange(other.m_mode, std::ios_base::openmode{})}
{
}
basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other) noexcept
{
parent_type::operator=(std::move(other));
m_buffer = std::move(other.m_buffer);
m_handle = std::exchange(other.m_handle, m_handle);
m_mode = std::exchange(other.m_mode, m_mode);
return *this;
}
bool open(const std::string& name, std::ios_base::openmode mode)
{
assert(!((mode & std::ios_base::in) && (mode & std::ios_base::out)) && "nes::basic_pipe_streambuf::open called with mode = std::ios_base::in | std::ios_base::out.");
close();
const auto native_name{pipe_root + name};
if(mkfifo(std::data(native_name), 0660) != 0 && errno != EEXIST)
return false;
const int native_mode{mode & std::ios_base::in ? O_RDONLY : O_WRONLY};
int handle = ::open(std::data(native_name), native_mode);
if(handle < 0)
return false;
m_buffer.resize(buf_size);
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
m_handle = handle;
m_mode = mode;
return true;
}
bool is_open() const noexcept
{
return m_handle;
}
void close()
{
if(is_open())
{
sync();
m_mode = std::ios_base::openmode{};
::close(std::exchange(m_handle, 0));
parent_type::setp(nullptr, nullptr);
parent_type::setg(nullptr, nullptr, nullptr);
}
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_streambuf(int handle, std::ios_base::openmode mode)
:m_handle{handle}
,m_mode{mode}
{
m_buffer.resize(buf_size);
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
protected:
virtual int sync() override
{
if(m_mode & std::ios_base::out)
{
const std::ptrdiff_t count{parent_type::pptr() - parent_type::pbase()};
if(write(m_handle, reinterpret_cast<const char*>(std::data(m_buffer)), count * sizeof(char_type)) < 0)
return -1;
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
return 0;
}
virtual int_type overflow(int_type c = traits_type::eof()) override
{
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
if(traits_type::eq_int_type(c, traits_type::eof()))
{
if(write(m_handle, reinterpret_cast<const char*>(std::data(m_buffer)), std::size(m_buffer) * sizeof(char_type)))
return traits_type::eof();
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
else
{
*parent_type::pptr() = traits_type::to_char_type(c);
parent_type::pbump(1);
}
return traits_type::not_eof(c);
}
virtual std::streamsize xsputn(const char_type* s, std::streamsize count) override
{
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
const auto written = write(m_handle, reinterpret_cast<const char*>(s), count * sizeof(char_type));
if(written < 0)
return 0;
return static_cast<std::streamsize>(written);
}
virtual int_type underflow() override
{
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
if(parent_type::gptr() == parent_type::egptr())
{
const auto readed = read(m_handle, reinterpret_cast<char*>(std::data(m_buffer)), buf_size * sizeof(char_type));
if(readed <= 0)
return traits_type::eof();
parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (readed / sizeof(char_type)));
}
return traits_type::to_int_type(*parent_type::gptr());
}
virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override
{
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
const auto readed = read(m_handle, reinterpret_cast<char*>(s), count * sizeof(char_type));
if(readed < 0)
return 0;
return static_cast<std::streamsize>(readed / sizeof(char_type));
}
private:
std::vector<CharT> m_buffer{};
int m_handle{};
std::ios_base::openmode m_mode{};
};
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_istream : public std::basic_istream<CharT, Traits>
{
private:
using parent_type = std::basic_istream<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
basic_pipe_istream() = default;
explicit basic_pipe_istream(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
:parent_type{nullptr}
{
parent_type::rdbuf(m_buffer.get());
open(name, mode);
}
virtual ~basic_pipe_istream() = default;
basic_pipe_istream(const basic_pipe_istream&) = delete;
basic_pipe_istream& operator=(const basic_pipe_istream&) = delete;
basic_pipe_istream(basic_pipe_istream&& other) noexcept
:parent_type{std::move(other)}
{
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
}
basic_pipe_istream& operator=(basic_pipe_istream&& other) noexcept
{
parent_type::operator=(std::move(other));
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
return *this;
}
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
{
m_buffer->open(name, mode);
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
}
bool is_open() const noexcept
{
return m_buffer->is_open();
}
void close()
{
m_buffer->close();
}
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
{
return m_buffer.get();
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_istream(basic_pipe_streambuf<char_type, traits_type> buffer)
:parent_type{nullptr}
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
{
parent_type::rdbuf(m_buffer.get());
}
private:
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
};
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_ostream : public std::basic_ostream<CharT, Traits>
{
private:
using parent_type = std::basic_ostream<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
basic_pipe_ostream() = default;
explicit basic_pipe_ostream(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
:parent_type{nullptr}
{
parent_type::rdbuf(m_buffer.get());
open(name, mode);
}
virtual ~basic_pipe_ostream() = default;
basic_pipe_ostream(const basic_pipe_ostream&) = delete;
basic_pipe_ostream& operator=(const basic_pipe_ostream&) = delete;
basic_pipe_ostream(basic_pipe_ostream&& other) noexcept
:parent_type{std::move(other)}
{
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
}
basic_pipe_ostream& operator=(basic_pipe_ostream&& other) noexcept
{
parent_type::operator=(std::move(other));
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
return *this;
}
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
{
m_buffer->open(name, mode);
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
}
bool is_open() const noexcept
{
return m_buffer->is_open();
}
void close()
{
m_buffer->close();
}
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
{
return m_buffer.get();
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_ostream(basic_pipe_streambuf<char_type, traits_type> buffer)
:parent_type{nullptr}
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
{
parent_type::rdbuf(m_buffer.get());
}
private:
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
};
template<typename CharT, typename Traits>
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()
{
int fd[2];
if(pipe(fd))
throw std::runtime_error{"Failed to create pipe"};
return std::make_pair(basic_pipe_istream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{fd[0], std::ios_base::in}},
basic_pipe_ostream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{fd[1], std::ios_base::out}});
}
using pipe_streambuf = basic_pipe_streambuf<char>;
using pipe_istream = basic_pipe_istream<char>;
using pipe_ostream = basic_pipe_ostream<char>;
}
#endif
#endif