mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-11 11:47:09 +02:00
856 lines
26 KiB
C++
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
|