Compare commits
84 Commits
Author | SHA1 | Date |
---|---|---|
Claudius Holeksa | af61465a19 | |
Claudius Holeksa | 9ecf425911 | |
Claudius Holeksa | 4f10da77eb | |
Claudius Holeksa | a83826552e | |
Claudius Holeksa | 59451d3a4c | |
Claudius Holeksa | d87b91d5fc | |
Claudius Holeksa | f8bcdbb859 | |
Claudius Holeksa | 3be68973ef | |
Claudius Holeksa | 32aa36a43d | |
Claudius Holeksa | 03d8924cf4 | |
Claudius Holeksa | 30d363c5f2 | |
Claudius Holeksa | dc2ca2774b | |
Claudius Holeksa | 642337656f | |
Claudius Holeksa | 9992a70a3a | |
Claudius Holeksa | b64df67c27 | |
Claudius Holeksa | 5939699bf8 | |
Claudius Holeksa | 98723260b8 | |
Claudius Holeksa | 1662ed2f33 | |
Claudius Holeksa | a1a718967d | |
Claudius Holeksa | 6968a80518 | |
Claudius Holeksa | 55a6d4dd9b | |
Claudius Holeksa | 4384f8acfa | |
Claudius Holeksa | 77acd00720 | |
Claudius Holeksa | 3004dec554 | |
Claudius Holeksa | 9330d1ee74 | |
Claudius Holeksa | c51bb63e9b | |
Claudius Holeksa | 389d23bd28 | |
Claudius Holeksa | 30937d98c5 | |
Claudius Holeksa | 4fa6ed0d59 | |
Claudius Holeksa | 33188f213e | |
Claudius Holeksa | 7aacdc357c | |
Claudius Holeksa | 21a1bde479 | |
Claudius Holeksa | a963bdff6c | |
Claudius Holeksa | d845848449 | |
Claudius Holeksa | 37ed2ebc8e | |
Claudius Holeksa | e4b6204edd | |
Claudius Holeksa | 536aa05298 | |
Claudius Holeksa | 74663daeef | |
keldu | 91a8f680b2 | |
Claudius Holeksa | 2e9d96a1d0 | |
Claudius Holeksa | 3651aea384 | |
keldu | 7fecac92c0 | |
keldu | cdb096a44f | |
Claudius Holeksa | 5d3796c751 | |
Claudius Holeksa | fb9312acb1 | |
keldu | f695b047e9 | |
Claudius Holeksa | 7518dd7ed4 | |
Claudius Holeksa | afbe54c795 | |
Claudius Holeksa | 9e4260cc04 | |
Claudius Holeksa | f0f1c8f2e8 | |
keldu | 5ce093cb6a | |
Claudius Holeksa | b8b461a4c6 | |
keldu | f92ebab88a | |
keldu | 01ebd991ae | |
keldu | f38a122efa | |
keldu | 80c5fd6bfb | |
Claudius Holeksa | c0241fe666 | |
keldu | b25b28a560 | |
keldu | 54608ac410 | |
keldu | 9136e902f1 | |
Claudius Holeksa | 2ea95b8b3e | |
Claudius Holeksa | dc5ad0e62a | |
Claudius Holeksa | 883cfc32a5 | |
Claudius Holeksa | 2af95c3664 | |
Claudius Holeksa | c82d717c2d | |
Claudius Holeksa | 0d06a58798 | |
Claudius Holeksa | d172f458a3 | |
Claudius Holeksa | e571a7ce90 | |
Claudius Holeksa | a5cfca7a12 | |
Claudius Holeksa | ffed345df1 | |
keldu | 0808db94ee | |
Claudius Holeksa | f44b6a1dc8 | |
Claudius Holeksa | b0991ce29b | |
Claudius Holeksa | 60d710cab7 | |
Claudius Holeksa | 28ea7d6708 | |
Claudius Holeksa | 3ff512bfca | |
Claudius Holeksa | 2d8889983a | |
Claudius Holeksa | 5a6f63eadb | |
Claudius Holeksa | 911db65409 | |
Claudius Holeksa | f29d1c6512 | |
Claudius Holeksa | 3cb0434e49 | |
Claudius Holeksa | 7117f23fcd | |
Claudius Holeksa | 6624960f86 | |
Claudius Holeksa | 12c61f46dc |
|
@ -0,0 +1,9 @@
|
|||
Checks: '-*,readability-identifier-naming'
|
||||
CheckOptions:
|
||||
- { key: readability-identifier-naming.PrivateMemberSuffix, value: "_" }
|
||||
- { key: readability-identifier-naming.AggressiveDependentMemberLookup, value: true }
|
||||
- { key: readability-identifier-naming.ProtectedMemberSuffix, value: "_" }
|
||||
- { key: readability-identifier-naming.ClassCase, value: "lower_case" }
|
||||
- { key: readability-identifier-naming.StructCase, value: "lower_case" }
|
||||
- { key: readability-identifier-naming.FunctionCase, value: "lower_case" }
|
||||
- { key: readability-identifier-naming.MethodCase, value: "lower_case" }
|
|
@ -71,3 +71,8 @@ thoughts
|
|||
*.swp
|
||||
vgcore.*
|
||||
*.pdf
|
||||
|
||||
# cache file for ale
|
||||
.cache
|
||||
*.plist
|
||||
result
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
with import <nixpkgs> {};
|
||||
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
pkgs.mkShell {
|
||||
name = "forstio";
|
||||
nativeBuildInputs = [ scons gnutls clang_12 clang-tools];
|
||||
|
||||
buildPhase = ''
|
||||
'';
|
||||
}
|
45
SConstruct
45
SConstruct
|
@ -46,8 +46,11 @@ env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=['#source/forstio','
|
|||
CXX='clang++',
|
||||
CPPDEFINES=['SAW_UNIX'],
|
||||
CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'],
|
||||
LIBS=['gnutls'])
|
||||
LIBS=[])
|
||||
env.__class__.add_source_files = add_kel_source_files
|
||||
env.Tool('compilation_db');
|
||||
env.cdb = env.CompilationDatabase('compile_commands.json');
|
||||
|
||||
|
||||
env.objects = []
|
||||
env.sources = []
|
||||
|
@ -60,26 +63,8 @@ env.driver_sources = []
|
|||
env.driver_headers = []
|
||||
|
||||
Export('env')
|
||||
SConscript('source/forstio/SConscript')
|
||||
SConscript('driver/SConscript')
|
||||
|
||||
# Library build
|
||||
|
||||
env_library = env.Clone()
|
||||
|
||||
env.objects_shared = []
|
||||
env_library.add_source_files(env.objects_shared, env.sources + env.driver_sources + env.tls_sources, shared=True)
|
||||
env.library_shared = env_library.SharedLibrary('#build/forstio', [env.objects_shared])
|
||||
|
||||
env.objects_static = []
|
||||
env_library.add_source_files(env.objects_static, env.sources + env.driver_sources + env.tls_sources)
|
||||
env.library_static = env_library.StaticLibrary('#build/forstio', [env.objects_static])
|
||||
|
||||
env.Alias('library', [env.library_shared, env.library_static])
|
||||
env.Alias('library_shared', env.library_shared)
|
||||
env.Alias('library_static', env.library_static)
|
||||
|
||||
env.Default('library')
|
||||
SConscript('source/forstio/SConscript')
|
||||
|
||||
# Tests
|
||||
SConscript('test/SConscript')
|
||||
|
@ -88,17 +73,29 @@ env.Alias('test', env.test_program)
|
|||
|
||||
# Clang format part
|
||||
env.Append(BUILDERS={'ClangFormat' : Builder(action = 'clang-format --style=file -i $SOURCE')})
|
||||
env.format_actions = []
|
||||
env.format_actions = [];
|
||||
def format_iter(env,files):
|
||||
for f in files:
|
||||
env.format_actions.append(env.AlwaysBuild(env.ClangFormat(target=f+"-clang-format",source=f)))
|
||||
pass
|
||||
env.Append(BUILDERS={'ClangTidy' : Builder(action = 'clang-tidy -extra-arg-before=-xc++ $SOURCE')})
|
||||
env.tidy_actions = [];
|
||||
def tidy_iter(env,files):
|
||||
string_of_files = "";
|
||||
for f in files:
|
||||
if(f != "/home/keldu/workspace/forstio/forstio/source/forstio/async.tmpl.h" and f != "/home/keldu/workspace/forstio/forstio/source/forstio/io_peer.tmpl.h" ):
|
||||
env.tidy_actions.append(env.AlwaysBuild(env.ClangTidy(target=f+"-clang-tidy",source=f)));
|
||||
pass
|
||||
|
||||
format_iter(env,env.sources + env.driver_sources + env.headers + env.driver_headers)
|
||||
format_iter(env,env.sources + env.driver_sources + env.headers + env.driver_headers);
|
||||
tidy_iter(env,env.sources + env.driver_sources + env.headers + env.driver_headers);
|
||||
|
||||
env.Alias('format', env.format_actions)
|
||||
env.Alias('format', env.format_actions);
|
||||
env.Alias('tidy', env.tidy_actions);
|
||||
|
||||
env.Alias('all', ['format', 'library_shared', 'library_static', 'test'])
|
||||
env.Alias('cdb', env.cdb);
|
||||
env.Alias('all', ['format', 'library', 'test'])
|
||||
env.Default('all')
|
||||
|
||||
env.Install('$prefix/lib/', [env.library_shared, env.library_static])
|
||||
env.Install('$prefix/include/forstio/', [env.headers])
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
with import <nixpkgs> {};
|
||||
|
||||
stdenvNoCC.mkDerivation {
|
||||
name = "forstio";
|
||||
src = ./.;
|
||||
|
||||
nativeBuildInputs = [
|
||||
scons
|
||||
clang_14
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
gnutls
|
||||
];
|
||||
}
|
|
@ -1,422 +0,0 @@
|
|||
#include "driver/io-unix.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace saw {
|
||||
namespace unix {
|
||||
IFdOwner::IFdOwner(UnixEventPort &event_port, int file_descriptor, int fd_flags,
|
||||
uint32_t event_mask)
|
||||
: event_port{event_port}, file_descriptor{file_descriptor},
|
||||
fd_flags{fd_flags}, event_mask{event_mask} {
|
||||
event_port.subscribe(*this, file_descriptor, event_mask);
|
||||
}
|
||||
|
||||
IFdOwner::~IFdOwner() {
|
||||
if (file_descriptor >= 0) {
|
||||
event_port.unsubscribe(file_descriptor);
|
||||
::close(file_descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t unixRead(int fd, void *buffer, size_t length) {
|
||||
return ::recv(fd, buffer, length, 0);
|
||||
}
|
||||
|
||||
ssize_t unixWrite(int fd, const void *buffer, size_t length) {
|
||||
return ::send(fd, buffer, length, 0);
|
||||
}
|
||||
|
||||
UnixIoStream::UnixIoStream(UnixEventPort &event_port, int file_descriptor,
|
||||
int fd_flags, uint32_t event_mask)
|
||||
: IFdOwner{event_port, file_descriptor, fd_flags, event_mask | EPOLLRDHUP} {
|
||||
}
|
||||
|
||||
ErrorOr<size_t> UnixIoStream::read(void *buffer, size_t length) {
|
||||
ssize_t read_bytes = unixRead(fd(), buffer, length);
|
||||
if (read_bytes > 0) {
|
||||
return static_cast<size_t>(read_bytes);
|
||||
} else if (read_bytes == 0) {
|
||||
return criticalError("Disconnected", Error::Code::Disconnected);
|
||||
}
|
||||
|
||||
return recoverableError("Currently busy");
|
||||
}
|
||||
|
||||
Conveyor<void> UnixIoStream::readReady() {
|
||||
auto caf = newConveyorAndFeeder<void>();
|
||||
read_ready = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
Conveyor<void> UnixIoStream::onReadDisconnected() {
|
||||
auto caf = newConveyorAndFeeder<void>();
|
||||
on_read_disconnect = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
ErrorOr<size_t> UnixIoStream::write(const void *buffer, size_t length) {
|
||||
ssize_t write_bytes = unixWrite(fd(), buffer, length);
|
||||
if (write_bytes > 0) {
|
||||
return static_cast<size_t>(write_bytes);
|
||||
}
|
||||
|
||||
int error = errno;
|
||||
|
||||
if (error == EAGAIN || error == EWOULDBLOCK) {
|
||||
return recoverableError("Currently busy");
|
||||
}
|
||||
|
||||
return criticalError("Disconnected", Error::Code::Disconnected);
|
||||
}
|
||||
|
||||
Conveyor<void> UnixIoStream::writeReady() {
|
||||
auto caf = newConveyorAndFeeder<void>();
|
||||
write_ready = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
void UnixIoStream::notify(uint32_t mask) {
|
||||
if (mask & EPOLLOUT) {
|
||||
if (write_ready) {
|
||||
write_ready->feed();
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & EPOLLIN) {
|
||||
if (read_ready) {
|
||||
read_ready->feed();
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & EPOLLRDHUP) {
|
||||
if (on_read_disconnect) {
|
||||
on_read_disconnect->feed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnixServer::UnixServer(UnixEventPort &event_port, int file_descriptor,
|
||||
int fd_flags)
|
||||
: IFdOwner{event_port, file_descriptor, fd_flags, EPOLLIN} {}
|
||||
|
||||
Conveyor<Own<IoStream>> UnixServer::accept() {
|
||||
auto caf = newConveyorAndFeeder<Own<IoStream>>();
|
||||
accept_feeder = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
void UnixServer::notify(uint32_t mask) {
|
||||
if (mask & EPOLLIN) {
|
||||
if (accept_feeder) {
|
||||
struct ::sockaddr_storage address;
|
||||
socklen_t address_length = sizeof(address);
|
||||
|
||||
int accept_fd =
|
||||
::accept4(fd(), reinterpret_cast<struct ::sockaddr *>(&address),
|
||||
&address_length, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
if (accept_fd < 0) {
|
||||
return;
|
||||
}
|
||||
auto fd_stream = heap<UnixIoStream>(event_port, accept_fd, 0,
|
||||
EPOLLIN | EPOLLOUT);
|
||||
accept_feeder->feed(std::move(fd_stream));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnixDatagram::UnixDatagram(UnixEventPort &event_port, int file_descriptor,
|
||||
int fd_flags)
|
||||
: IFdOwner{event_port, file_descriptor, fd_flags, EPOLLIN | EPOLLOUT} {}
|
||||
|
||||
namespace {
|
||||
ssize_t unixReadMsg(int fd, void *buffer, size_t length) {
|
||||
struct ::sockaddr_storage their_addr;
|
||||
socklen_t addr_len = sizeof(sockaddr_storage);
|
||||
return ::recvfrom(fd, buffer, length, 0,
|
||||
reinterpret_cast<struct ::sockaddr *>(&their_addr),
|
||||
&addr_len);
|
||||
}
|
||||
|
||||
ssize_t unixWriteMsg(int fd, const void *buffer, size_t length,
|
||||
::sockaddr *dest_addr, socklen_t dest_addr_len) {
|
||||
|
||||
return ::sendto(fd, buffer, length, 0, dest_addr, dest_addr_len);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ErrorOr<size_t> UnixDatagram::read(void *buffer, size_t length) {
|
||||
ssize_t read_bytes = unixReadMsg(fd(), buffer, length);
|
||||
if (read_bytes > 0) {
|
||||
return static_cast<size_t>(read_bytes);
|
||||
}
|
||||
return recoverableError("Currently busy");
|
||||
}
|
||||
|
||||
Conveyor<void> UnixDatagram::readReady() {
|
||||
auto caf = newConveyorAndFeeder<void>();
|
||||
read_ready = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
ErrorOr<size_t> UnixDatagram::write(const void *buffer, size_t length,
|
||||
NetworkAddress &dest) {
|
||||
UnixNetworkAddress &unix_dest = static_cast<UnixNetworkAddress &>(dest);
|
||||
SocketAddress &sock_addr = unix_dest.unixAddress();
|
||||
socklen_t sock_addr_length = sock_addr.getRawLength();
|
||||
ssize_t write_bytes = unixWriteMsg(fd(), buffer, length, sock_addr.getRaw(),
|
||||
sock_addr_length);
|
||||
if (write_bytes > 0) {
|
||||
return static_cast<size_t>(write_bytes);
|
||||
}
|
||||
return recoverableError("Currently busy");
|
||||
}
|
||||
|
||||
Conveyor<void> UnixDatagram::writeReady() {
|
||||
auto caf = newConveyorAndFeeder<void>();
|
||||
write_ready = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
void UnixDatagram::notify(uint32_t mask) {
|
||||
if (mask & EPOLLOUT) {
|
||||
if (write_ready) {
|
||||
write_ready->feed();
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & EPOLLIN) {
|
||||
if (read_ready) {
|
||||
read_ready->feed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool beginsWith(const std::string_view &viewed,
|
||||
const std::string_view &begins) {
|
||||
return viewed.size() >= begins.size() &&
|
||||
viewed.compare(0, begins.size(), begins) == 0;
|
||||
}
|
||||
|
||||
std::variant<UnixNetworkAddress, UnixNetworkAddress *>
|
||||
translateNetworkAddressToUnixNetworkAddress(NetworkAddress &addr) {
|
||||
auto addr_variant = addr.representation();
|
||||
std::variant<UnixNetworkAddress, UnixNetworkAddress *> os_addr = std::visit(
|
||||
[](auto &arg)
|
||||
-> std::variant<UnixNetworkAddress, UnixNetworkAddress *> {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, OsNetworkAddress *>) {
|
||||
return static_cast<UnixNetworkAddress *>(arg);
|
||||
}
|
||||
|
||||
auto sock_addrs = SocketAddress::parse(
|
||||
std::string_view{arg->address()}, arg->port());
|
||||
|
||||
return UnixNetworkAddress{arg->address(), arg->port(),
|
||||
std::move(sock_addrs)};
|
||||
},
|
||||
addr_variant);
|
||||
return os_addr;
|
||||
}
|
||||
|
||||
UnixNetworkAddress &translateToUnixAddressRef(
|
||||
std::variant<UnixNetworkAddress, UnixNetworkAddress *> &addr_variant) {
|
||||
return std::visit(
|
||||
[](auto &arg) -> UnixNetworkAddress & {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, UnixNetworkAddress>) {
|
||||
return arg;
|
||||
} else if constexpr (std::is_same_v<T, UnixNetworkAddress *>) {
|
||||
return *arg;
|
||||
} else {
|
||||
static_assert(true, "Cases exhausted");
|
||||
}
|
||||
},
|
||||
addr_variant);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Own<Server> UnixNetwork::listen(NetworkAddress &addr) {
|
||||
auto unix_addr_storage = translateNetworkAddressToUnixNetworkAddress(addr);
|
||||
UnixNetworkAddress &address = translateToUnixAddressRef(unix_addr_storage);
|
||||
|
||||
assert(address.unixAddressSize() > 0);
|
||||
if (address.unixAddressSize() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int fd = address.unixAddress(0).socket(SOCK_STREAM);
|
||||
if (fd < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int val = 1;
|
||||
int rc = ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
|
||||
if (rc < 0) {
|
||||
::close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool failed = address.unixAddress(0).bind(fd);
|
||||
if (failed) {
|
||||
::close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
::listen(fd, SOMAXCONN);
|
||||
|
||||
return heap<UnixServer>(event_port, fd, 0);
|
||||
}
|
||||
|
||||
Conveyor<Own<IoStream>> UnixNetwork::connect(NetworkAddress &addr) {
|
||||
auto unix_addr_storage = translateNetworkAddressToUnixNetworkAddress(addr);
|
||||
UnixNetworkAddress &address = translateToUnixAddressRef(unix_addr_storage);
|
||||
|
||||
assert(address.unixAddressSize() > 0);
|
||||
if (address.unixAddressSize() == 0) {
|
||||
return Conveyor<Own<IoStream>>{criticalError("No address found")};
|
||||
}
|
||||
|
||||
int fd = address.unixAddress(0).socket(SOCK_STREAM);
|
||||
if (fd < 0) {
|
||||
return Conveyor<Own<IoStream>>{criticalError("Couldn't open socket")};
|
||||
}
|
||||
|
||||
Own<UnixIoStream> io_stream =
|
||||
heap<UnixIoStream>(event_port, fd, 0, EPOLLIN | EPOLLOUT);
|
||||
|
||||
bool success = false;
|
||||
for (size_t i = 0; i < address.unixAddressSize(); ++i) {
|
||||
SocketAddress &addr_iter = address.unixAddress(i);
|
||||
int status =
|
||||
::connect(fd, addr_iter.getRaw(), addr_iter.getRawLength());
|
||||
if (status < 0) {
|
||||
int error = errno;
|
||||
/*
|
||||
* It's not connected yet...
|
||||
* But edge triggered epolling means that it'll
|
||||
* be ready when the signal is triggered
|
||||
*/
|
||||
|
||||
/// @todo Add limit node when implemented
|
||||
if (error == EINPROGRESS) {
|
||||
/*
|
||||
Conveyor<void> write_ready = io_stream->writeReady();
|
||||
return write_ready.then(
|
||||
[ios{std::move(io_stream)}]() mutable {
|
||||
ios->write_ready = nullptr;
|
||||
return std::move(ios);
|
||||
});
|
||||
*/
|
||||
success = true;
|
||||
break;
|
||||
} else if (error != EINTR) {
|
||||
/// @todo Push error message from
|
||||
return Conveyor<Own<IoStream>>{
|
||||
criticalError("Couldn't connect")};
|
||||
}
|
||||
} else {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
return criticalError("Couldn't connect");
|
||||
}
|
||||
|
||||
return Conveyor<Own<IoStream>>{std::move(io_stream)};
|
||||
}
|
||||
|
||||
Own<Datagram> UnixNetwork::datagram(NetworkAddress &addr) {
|
||||
auto unix_addr_storage = translateNetworkAddressToUnixNetworkAddress(addr);
|
||||
UnixNetworkAddress &address = translateToUnixAddressRef(unix_addr_storage);
|
||||
|
||||
SAW_ASSERT(address.unixAddressSize() > 0) { return nullptr; }
|
||||
|
||||
int fd = address.unixAddress(0).socket(SOCK_DGRAM);
|
||||
|
||||
int optval = 1;
|
||||
int rc =
|
||||
::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
||||
if (rc < 0) {
|
||||
::close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool failed = address.unixAddress(0).bind(fd);
|
||||
if (failed) {
|
||||
::close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
/// @todo
|
||||
return heap<UnixDatagram>(event_port, fd, 0);
|
||||
}
|
||||
|
||||
const std::string &UnixNetworkAddress::address() const { return path; }
|
||||
|
||||
uint16_t UnixNetworkAddress::port() const { return port_hint; }
|
||||
|
||||
SocketAddress &UnixNetworkAddress::unixAddress(size_t i) {
|
||||
assert(i < addresses.size());
|
||||
/// @todo change from list to vector?
|
||||
return addresses.at(i);
|
||||
}
|
||||
|
||||
size_t UnixNetworkAddress::unixAddressSize() const { return addresses.size(); }
|
||||
|
||||
UnixNetwork::UnixNetwork(UnixEventPort &event) : event_port{event} {}
|
||||
|
||||
Conveyor<Own<NetworkAddress>> UnixNetwork::parseAddress(const std::string &path,
|
||||
uint16_t port_hint) {
|
||||
std::string_view addr_view{path};
|
||||
{
|
||||
std::string_view begins_with = "unix:";
|
||||
if (beginsWith(addr_view, begins_with)) {
|
||||
addr_view.remove_prefix(begins_with.size());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SocketAddress> addresses =
|
||||
SocketAddress::parse(addr_view, port_hint);
|
||||
|
||||
return Conveyor<Own<NetworkAddress>>{
|
||||
heap<UnixNetworkAddress>(path, port_hint, std::move(addresses))};
|
||||
}
|
||||
|
||||
UnixIoProvider::UnixIoProvider(UnixEventPort &port_ref, Own<EventPort> port)
|
||||
: event_port{port_ref}, event_loop{std::move(port)}, unix_network{
|
||||
port_ref} {}
|
||||
|
||||
Own<InputStream> UnixIoProvider::wrapInputFd(int fd) {
|
||||
return heap<UnixIoStream>(event_port, fd, 0, EPOLLIN);
|
||||
}
|
||||
|
||||
Network &UnixIoProvider::network() {
|
||||
return static_cast<Network &>(unix_network);
|
||||
}
|
||||
|
||||
EventLoop &UnixIoProvider::eventLoop() { return event_loop; }
|
||||
|
||||
} // namespace unix
|
||||
|
||||
ErrorOr<AsyncIoContext> setupAsyncIo() {
|
||||
using namespace unix;
|
||||
try {
|
||||
Own<UnixEventPort> prt = heap<UnixEventPort>();
|
||||
UnixEventPort &prt_ref = *prt;
|
||||
|
||||
Own<UnixIoProvider> io_provider =
|
||||
heap<UnixIoProvider>(prt_ref, std::move(prt));
|
||||
|
||||
EventLoop &loop_ref = io_provider->eventLoop();
|
||||
|
||||
return {{std::move(io_provider), loop_ref, prt_ref}};
|
||||
} catch (std::bad_alloc &) {
|
||||
return criticalError("Out of memory");
|
||||
}
|
||||
}
|
||||
} // namespace saw
|
467
driver/io-unix.h
467
driver/io-unix.h
|
@ -1,467 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef SAW_UNIX
|
||||
#error "Don't include this"
|
||||
#endif
|
||||
|
||||
#include <csignal>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "forstio/io.h"
|
||||
|
||||
namespace saw {
|
||||
namespace unix {
|
||||
constexpr int MAX_EPOLL_EVENTS = 256;
|
||||
|
||||
class UnixEventPort;
|
||||
class IFdOwner {
|
||||
protected:
|
||||
UnixEventPort &event_port;
|
||||
|
||||
private:
|
||||
int file_descriptor;
|
||||
int fd_flags;
|
||||
uint32_t event_mask;
|
||||
|
||||
public:
|
||||
IFdOwner(UnixEventPort &event_port, int file_descriptor, int fd_flags,
|
||||
uint32_t event_mask);
|
||||
|
||||
virtual ~IFdOwner();
|
||||
|
||||
virtual void notify(uint32_t mask) = 0;
|
||||
|
||||
int fd() const { return file_descriptor; }
|
||||
};
|
||||
|
||||
class UnixEventPort final : public EventPort {
|
||||
private:
|
||||
int epoll_fd;
|
||||
int signal_fd;
|
||||
|
||||
sigset_t signal_fd_set;
|
||||
|
||||
std::unordered_multimap<Signal, Own<ConveyorFeeder<void>>> signal_conveyors;
|
||||
|
||||
int pipefds[2];
|
||||
|
||||
std::vector<int> toUnixSignal(Signal signal) const {
|
||||
switch (signal) {
|
||||
case Signal::User1:
|
||||
return {SIGUSR1};
|
||||
case Signal::Terminate:
|
||||
default:
|
||||
return {SIGTERM, SIGQUIT, SIGINT};
|
||||
}
|
||||
}
|
||||
|
||||
Signal fromUnixSignal(int signal) const {
|
||||
switch (signal) {
|
||||
case SIGUSR1:
|
||||
return Signal::User1;
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
case SIGQUIT:
|
||||
default:
|
||||
return Signal::Terminate;
|
||||
}
|
||||
}
|
||||
|
||||
void notifySignalListener(int sig) {
|
||||
Signal signal = fromUnixSignal(sig);
|
||||
|
||||
auto equal_range = signal_conveyors.equal_range(signal);
|
||||
for (auto iter = equal_range.first; iter != equal_range.second;
|
||||
++iter) {
|
||||
|
||||
if (iter->second) {
|
||||
if (iter->second->space() > 0) {
|
||||
iter->second->feed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool pollImpl(int time) {
|
||||
epoll_event events[MAX_EPOLL_EVENTS];
|
||||
int nfds = 0;
|
||||
do {
|
||||
nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, time);
|
||||
|
||||
if (nfds < 0) {
|
||||
/// @todo error_handling
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nfds; ++i) {
|
||||
if (events[i].data.u64 == 0) {
|
||||
while (1) {
|
||||
struct ::signalfd_siginfo siginfo;
|
||||
ssize_t n =
|
||||
::read(signal_fd, &siginfo, sizeof(siginfo));
|
||||
if (n < 0) {
|
||||
break;
|
||||
}
|
||||
assert(n == sizeof(siginfo));
|
||||
|
||||
notifySignalListener(siginfo.ssi_signo);
|
||||
}
|
||||
} else if (events[i].data.u64 == 1) {
|
||||
uint8_t i;
|
||||
if (pipefds[0] < 0) {
|
||||
continue;
|
||||
}
|
||||
while (1) {
|
||||
ssize_t n = ::recv(pipefds[0], &i, sizeof(i), 0);
|
||||
if (n < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
IFdOwner *owner =
|
||||
reinterpret_cast<IFdOwner *>(events[i].data.ptr);
|
||||
if (owner) {
|
||||
owner->notify(events[i].events);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (nfds == MAX_EPOLL_EVENTS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
UnixEventPort() : epoll_fd{-1}, signal_fd{-1} {
|
||||
::signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
epoll_fd = ::epoll_create1(EPOLL_CLOEXEC);
|
||||
if (epoll_fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
::sigemptyset(&signal_fd_set);
|
||||
signal_fd = ::signalfd(-1, &signal_fd_set, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
if (signal_fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct epoll_event event;
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.events = EPOLLIN;
|
||||
event.data.u64 = 0;
|
||||
::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, signal_fd, &event);
|
||||
|
||||
int rc = ::pipe2(pipefds, O_NONBLOCK | O_CLOEXEC);
|
||||
if (rc < 0) {
|
||||
return;
|
||||
}
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.events = EPOLLIN;
|
||||
event.data.u64 = 1;
|
||||
::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0], &event);
|
||||
}
|
||||
|
||||
~UnixEventPort() {
|
||||
::close(epoll_fd);
|
||||
::close(signal_fd);
|
||||
::close(pipefds[0]);
|
||||
::close(pipefds[1]);
|
||||
}
|
||||
|
||||
Conveyor<void> onSignal(Signal signal) override {
|
||||
auto caf = newConveyorAndFeeder<void>();
|
||||
|
||||
signal_conveyors.insert(std::make_pair(signal, std::move(caf.feeder)));
|
||||
|
||||
std::vector<int> sig = toUnixSignal(signal);
|
||||
|
||||
for (auto iter = sig.begin(); iter != sig.end(); ++iter) {
|
||||
::sigaddset(&signal_fd_set, *iter);
|
||||
}
|
||||
::sigprocmask(SIG_BLOCK, &signal_fd_set, nullptr);
|
||||
::signalfd(signal_fd, &signal_fd_set, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
|
||||
auto node_and_storage =
|
||||
Conveyor<void>::fromConveyor(std::move(caf.conveyor));
|
||||
return Conveyor<void>::toConveyor(std::move(node_and_storage.first),
|
||||
node_and_storage.second);
|
||||
}
|
||||
|
||||
void poll() override { pollImpl(0); }
|
||||
|
||||
void wait() override { pollImpl(-1); }
|
||||
|
||||
void wait(const std::chrono::steady_clock::duration &duration) override {
|
||||
pollImpl(std::chrono::duration_cast<std::chrono::milliseconds>(duration)
|
||||
.count());
|
||||
}
|
||||
void
|
||||
wait(const std::chrono::steady_clock::time_point &time_point) override {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (time_point <= now) {
|
||||
poll();
|
||||
} else {
|
||||
pollImpl(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
time_point - now)
|
||||
.count());
|
||||
}
|
||||
}
|
||||
|
||||
void wake() override {
|
||||
/// @todo pipe() in the beginning and write something minor into it like
|
||||
/// uint8_t or sth the value itself doesn't matter
|
||||
if (pipefds[1] < 0) {
|
||||
return;
|
||||
}
|
||||
uint8_t i = 0;
|
||||
::send(pipefds[1], &i, sizeof(i), MSG_DONTWAIT);
|
||||
}
|
||||
|
||||
void subscribe(IFdOwner &owner, int fd, uint32_t event_mask) {
|
||||
if (epoll_fd < 0 || fd < 0) {
|
||||
return;
|
||||
}
|
||||
::epoll_event event;
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.events = event_mask | EPOLLET;
|
||||
event.data.ptr = &owner;
|
||||
|
||||
if (::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0) {
|
||||
/// @todo error_handling
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void unsubscribe(int fd) {
|
||||
if (epoll_fd < 0 || fd < 0) {
|
||||
return;
|
||||
}
|
||||
if (::epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr) < 0) {
|
||||
/// @todo error_handling
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ssize_t unixRead(int fd, void *buffer, size_t length);
|
||||
ssize_t unixWrite(int fd, const void *buffer, size_t length);
|
||||
|
||||
class UnixIoStream final : public IoStream, public IFdOwner {
|
||||
private:
|
||||
Own<ConveyorFeeder<void>> read_ready = nullptr;
|
||||
Own<ConveyorFeeder<void>> on_read_disconnect = nullptr;
|
||||
Own<ConveyorFeeder<void>> write_ready = nullptr;
|
||||
|
||||
public:
|
||||
UnixIoStream(UnixEventPort &event_port, int file_descriptor, int fd_flags,
|
||||
uint32_t event_mask);
|
||||
|
||||
ErrorOr<size_t> read(void *buffer, size_t length) override;
|
||||
|
||||
Conveyor<void> readReady() override;
|
||||
|
||||
Conveyor<void> onReadDisconnected() override;
|
||||
|
||||
ErrorOr<size_t> write(const void *buffer, size_t length) override;
|
||||
|
||||
Conveyor<void> writeReady() override;
|
||||
|
||||
/*
|
||||
void read(void *buffer, size_t min_length, size_t max_length) override;
|
||||
Conveyor<size_t> readDone() override;
|
||||
Conveyor<void> readReady() override;
|
||||
|
||||
Conveyor<void> onReadDisconnected() override;
|
||||
|
||||
void write(const void *buffer, size_t length) override;
|
||||
Conveyor<size_t> writeDone() override;
|
||||
Conveyor<void> writeReady() override;
|
||||
*/
|
||||
|
||||
void notify(uint32_t mask) override;
|
||||
};
|
||||
|
||||
class UnixServer final : public Server, public IFdOwner {
|
||||
private:
|
||||
Own<ConveyorFeeder<Own<IoStream>>> accept_feeder = nullptr;
|
||||
|
||||
public:
|
||||
UnixServer(UnixEventPort &event_port, int file_descriptor, int fd_flags);
|
||||
|
||||
Conveyor<Own<IoStream>> accept() override;
|
||||
|
||||
void notify(uint32_t mask) override;
|
||||
};
|
||||
|
||||
class UnixDatagram final : public Datagram, public IFdOwner {
|
||||
private:
|
||||
Own<ConveyorFeeder<void>> read_ready = nullptr;
|
||||
Own<ConveyorFeeder<void>> write_ready = nullptr;
|
||||
|
||||
public:
|
||||
UnixDatagram(UnixEventPort &event_port, int file_descriptor, int fd_flags);
|
||||
|
||||
ErrorOr<size_t> read(void *buffer, size_t length) override;
|
||||
Conveyor<void> readReady() override;
|
||||
|
||||
ErrorOr<size_t> write(const void *buffer, size_t length,
|
||||
NetworkAddress &dest) override;
|
||||
Conveyor<void> writeReady() override;
|
||||
|
||||
void notify(uint32_t mask) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper class which provides potential addresses to NetworkAddress
|
||||
*/
|
||||
class SocketAddress {
|
||||
private:
|
||||
union {
|
||||
struct sockaddr generic;
|
||||
struct sockaddr_un unix;
|
||||
struct sockaddr_in inet;
|
||||
struct sockaddr_in6 inet6;
|
||||
struct sockaddr_storage storage;
|
||||
} address;
|
||||
|
||||
socklen_t address_length;
|
||||
bool wildcard;
|
||||
|
||||
SocketAddress() : wildcard{false} {}
|
||||
|
||||
public:
|
||||
SocketAddress(const void *sockaddr, socklen_t len, bool wildcard)
|
||||
: address_length{len}, wildcard{wildcard} {
|
||||
assert(len <= sizeof(address));
|
||||
memcpy(&address.generic, sockaddr, len);
|
||||
}
|
||||
|
||||
int socket(int type) const {
|
||||
type |= SOCK_NONBLOCK | SOCK_CLOEXEC;
|
||||
|
||||
int result = ::socket(address.generic.sa_family, type, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bind(int fd) const {
|
||||
if (wildcard) {
|
||||
int value = 0;
|
||||
::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
|
||||
}
|
||||
int error = ::bind(fd, &address.generic, address_length);
|
||||
return error < 0;
|
||||
}
|
||||
|
||||
struct ::sockaddr *getRaw() {
|
||||
return &address.generic;
|
||||
}
|
||||
|
||||
const struct ::sockaddr *getRaw() const { return &address.generic; }
|
||||
|
||||
socklen_t getRawLength() const { return address_length; }
|
||||
|
||||
static std::vector<SocketAddress> parse(std::string_view str,
|
||||
uint16_t port_hint) {
|
||||
std::vector<SocketAddress> results;
|
||||
|
||||
struct ::addrinfo *head;
|
||||
struct ::addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
|
||||
std::string port_string = std::to_string(port_hint);
|
||||
bool wildcard = str == "*" || str == "::";
|
||||
std::string address_string{str};
|
||||
|
||||
int error = ::getaddrinfo(address_string.c_str(), port_string.c_str(),
|
||||
&hints, &head);
|
||||
|
||||
if (error) {
|
||||
return {};
|
||||
}
|
||||
|
||||
for (struct ::addrinfo *it = head; it != nullptr; it = it->ai_next) {
|
||||
if (it->ai_addrlen > sizeof(SocketAddress::address)) {
|
||||
continue;
|
||||
}
|
||||
results.push_back({it->ai_addr, it->ai_addrlen, wildcard});
|
||||
}
|
||||
::freeaddrinfo(head);
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
class UnixNetworkAddress final : public OsNetworkAddress {
|
||||
private:
|
||||
const std::string path;
|
||||
uint16_t port_hint;
|
||||
std::vector<SocketAddress> addresses;
|
||||
|
||||
public:
|
||||
UnixNetworkAddress(const std::string &path, uint16_t port_hint,
|
||||
std::vector<SocketAddress> &&addr)
|
||||
: path{path}, port_hint{port_hint}, addresses{std::move(addr)} {}
|
||||
|
||||
const std::string &address() const override;
|
||||
|
||||
uint16_t port() const override;
|
||||
|
||||
// Custom address info
|
||||
SocketAddress &unixAddress(size_t i = 0);
|
||||
size_t unixAddressSize() const;
|
||||
};
|
||||
|
||||
class UnixNetwork final : public Network {
|
||||
private:
|
||||
UnixEventPort &event_port;
|
||||
|
||||
public:
|
||||
UnixNetwork(UnixEventPort &event_port);
|
||||
|
||||
Conveyor<Own<NetworkAddress>> parseAddress(const std::string &address,
|
||||
uint16_t port_hint = 0) override;
|
||||
|
||||
Own<Server> listen(NetworkAddress &addr) override;
|
||||
|
||||
Conveyor<Own<IoStream>> connect(NetworkAddress &addr) override;
|
||||
|
||||
Own<Datagram> datagram(NetworkAddress &addr) override;
|
||||
};
|
||||
|
||||
class UnixIoProvider final : public IoProvider {
|
||||
private:
|
||||
UnixEventPort &event_port;
|
||||
EventLoop event_loop;
|
||||
|
||||
UnixNetwork unix_network;
|
||||
|
||||
public:
|
||||
UnixIoProvider(UnixEventPort &port_ref, Own<EventPort> port);
|
||||
|
||||
Network &network() override;
|
||||
|
||||
Own<InputStream> wrapInputFd(int fd) override;
|
||||
|
||||
EventLoop &eventLoop();
|
||||
};
|
||||
} // namespace unix
|
||||
} // namespace saw
|
|
@ -0,0 +1,434 @@
|
|||
#include "io_unix.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace saw {
|
||||
namespace unix {
|
||||
i_fd_owner::i_fd_owner(unix_event_port &event_port, int file_descriptor,
|
||||
int fd_flags, uint32_t event_mask)
|
||||
: event_port_{event_port}, file_descriptor_{file_descriptor},
|
||||
fd_flags_{fd_flags}, event_mask_{event_mask} {
|
||||
event_port_.subscribe(*this, file_descriptor, event_mask);
|
||||
}
|
||||
|
||||
i_fd_owner::~i_fd_owner() {
|
||||
if (file_descriptor_ >= 0) {
|
||||
event_port_.unsubscribe(file_descriptor_);
|
||||
::close(file_descriptor_);
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t unix_read(int fd, void *buffer, size_t length) {
|
||||
return ::recv(fd, buffer, length, 0);
|
||||
}
|
||||
|
||||
ssize_t unix_write(int fd, const void *buffer, size_t length) {
|
||||
return ::send(fd, buffer, length, 0);
|
||||
}
|
||||
|
||||
unix_io_stream::unix_io_stream(unix_event_port &event_port, int file_descriptor,
|
||||
int fd_flags, uint32_t event_mask)
|
||||
: i_fd_owner{event_port, file_descriptor, fd_flags,
|
||||
event_mask | EPOLLRDHUP} {}
|
||||
|
||||
error_or<size_t> unix_io_stream::read(void *buffer, size_t length) {
|
||||
ssize_t read_bytes = unix_read(fd(), buffer, length);
|
||||
if (read_bytes > 0) {
|
||||
return static_cast<size_t>(read_bytes);
|
||||
} else if (read_bytes == 0) {
|
||||
return critical_error("Disconnected", error::code::Disconnected);
|
||||
}
|
||||
|
||||
return recoverable_error("Currently busy");
|
||||
}
|
||||
|
||||
conveyor<void> unix_io_stream::read_ready() {
|
||||
auto caf = new_conveyor_and_feeder<void>();
|
||||
read_ready_ = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
conveyor<void> unix_io_stream::on_read_disconnected() {
|
||||
auto caf = new_conveyor_and_feeder<void>();
|
||||
on_read_disconnect_ = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
error_or<size_t> unix_io_stream::write(const void *buffer, size_t length) {
|
||||
ssize_t write_bytes = unix_write(fd(), buffer, length);
|
||||
if (write_bytes > 0) {
|
||||
return static_cast<size_t>(write_bytes);
|
||||
}
|
||||
|
||||
int error = errno;
|
||||
|
||||
if (error == EAGAIN || error == EWOULDBLOCK) {
|
||||
return recoverable_error("Currently busy");
|
||||
}
|
||||
|
||||
return critical_error("Disconnected", error::code::Disconnected);
|
||||
}
|
||||
|
||||
conveyor<void> unix_io_stream::write_ready() {
|
||||
auto caf = new_conveyor_and_feeder<void>();
|
||||
write_ready_ = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
void unix_io_stream::notify(uint32_t mask) {
|
||||
if (mask & EPOLLOUT) {
|
||||
if (write_ready_) {
|
||||
write_ready_->feed();
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & EPOLLIN) {
|
||||
if (read_ready_) {
|
||||
read_ready_->feed();
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & EPOLLRDHUP) {
|
||||
if (on_read_disconnect_) {
|
||||
on_read_disconnect_->feed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unix_server::unix_server(unix_event_port &event_port, int file_descriptor,
|
||||
int fd_flags)
|
||||
: i_fd_owner{event_port, file_descriptor, fd_flags, EPOLLIN} {}
|
||||
|
||||
conveyor<own<io_stream>> unix_server::accept() {
|
||||
auto caf = new_conveyor_and_feeder<own<io_stream>>();
|
||||
accept_feeder_ = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
void unix_server::notify(uint32_t mask) {
|
||||
if (mask & EPOLLIN) {
|
||||
if (accept_feeder_) {
|
||||
struct ::sockaddr_storage address;
|
||||
socklen_t address_length = sizeof(address);
|
||||
|
||||
int accept_fd =
|
||||
::accept4(fd(), reinterpret_cast<struct ::sockaddr *>(&address),
|
||||
&address_length, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
if (accept_fd < 0) {
|
||||
return;
|
||||
}
|
||||
auto fd_stream = heap<unix_io_stream>(event_port_, accept_fd, 0,
|
||||
EPOLLIN | EPOLLOUT);
|
||||
accept_feeder_->feed(std::move(fd_stream));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unix_datagram::unix_datagram(unix_event_port &event_port, int file_descriptor,
|
||||
int fd_flags)
|
||||
: i_fd_owner{event_port, file_descriptor, fd_flags, EPOLLIN | EPOLLOUT} {}
|
||||
|
||||
namespace {
|
||||
ssize_t unix_read_msg(int fd, void *buffer, size_t length) {
|
||||
struct ::sockaddr_storage their_addr;
|
||||
socklen_t addr_len = sizeof(sockaddr_storage);
|
||||
return ::recvfrom(fd, buffer, length, 0,
|
||||
reinterpret_cast<struct ::sockaddr *>(&their_addr),
|
||||
&addr_len);
|
||||
}
|
||||
|
||||
ssize_t unix_write_msg(int fd, const void *buffer, size_t length,
|
||||
::sockaddr *dest_addr, socklen_t dest_addr_len) {
|
||||
|
||||
return ::sendto(fd, buffer, length, 0, dest_addr, dest_addr_len);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
error_or<size_t> unix_datagram::read(void *buffer, size_t length) {
|
||||
ssize_t read_bytes = unix_read_msg(fd(), buffer, length);
|
||||
if (read_bytes > 0) {
|
||||
return static_cast<size_t>(read_bytes);
|
||||
}
|
||||
return recoverable_error("Currently busy");
|
||||
}
|
||||
|
||||
conveyor<void> unix_datagram::read_ready() {
|
||||
auto caf = new_conveyor_and_feeder<void>();
|
||||
read_ready_ = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
error_or<size_t> unix_datagram::write(const void *buffer, size_t length,
|
||||
network_address &dest) {
|
||||
unix_network_address &unix_dest = static_cast<unix_network_address &>(dest);
|
||||
socket_address &sock_addr = unix_dest.unix_address();
|
||||
socklen_t sock_addr_length = sock_addr.get_raw_length();
|
||||
ssize_t write_bytes = unix_write_msg(fd(), buffer, length,
|
||||
sock_addr.get_raw(), sock_addr_length);
|
||||
if (write_bytes > 0) {
|
||||
return static_cast<size_t>(write_bytes);
|
||||
}
|
||||
return recoverable_error("Currently busy");
|
||||
}
|
||||
|
||||
conveyor<void> unix_datagram::write_ready() {
|
||||
auto caf = new_conveyor_and_feeder<void>();
|
||||
write_ready_ = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
void unix_datagram::notify(uint32_t mask) {
|
||||
if (mask & EPOLLOUT) {
|
||||
if (write_ready_) {
|
||||
write_ready_->feed();
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & EPOLLIN) {
|
||||
if (read_ready_) {
|
||||
read_ready_->feed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool begins_with(const std::string_view &viewed,
|
||||
const std::string_view &begins) {
|
||||
return viewed.size() >= begins.size() &&
|
||||
viewed.compare(0, begins.size(), begins) == 0;
|
||||
}
|
||||
|
||||
std::variant<unix_network_address, unix_network_address *>
|
||||
translate_network_address_to_unix_network_address(network_address &addr) {
|
||||
auto addr_variant = addr.representation();
|
||||
std::variant<unix_network_address, unix_network_address *> os_addr =
|
||||
std::visit(
|
||||
[](auto &arg)
|
||||
-> std::variant<unix_network_address, unix_network_address *> {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, os_network_address *>) {
|
||||
return static_cast<unix_network_address *>(arg);
|
||||
}
|
||||
|
||||
auto sock_addrs = socket_address::resolve(
|
||||
std::string_view{arg->address()}, arg->port());
|
||||
|
||||
return unix_network_address{arg->address(), arg->port(),
|
||||
std::move(sock_addrs)};
|
||||
},
|
||||
addr_variant);
|
||||
return os_addr;
|
||||
}
|
||||
|
||||
unix_network_address &translate_to_unix_address_ref(
|
||||
std::variant<unix_network_address, unix_network_address *> &addr_variant) {
|
||||
return std::visit(
|
||||
[](auto &arg) -> unix_network_address & {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, unix_network_address>) {
|
||||
return arg;
|
||||
} else if constexpr (std::is_same_v<T, unix_network_address *>) {
|
||||
return *arg;
|
||||
} else {
|
||||
static_assert(true, "Cases exhausted");
|
||||
}
|
||||
},
|
||||
addr_variant);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
own<server> unix_network::listen(network_address &addr) {
|
||||
auto unix_addr_storage =
|
||||
translate_network_address_to_unix_network_address(addr);
|
||||
unix_network_address &address =
|
||||
translate_to_unix_address_ref(unix_addr_storage);
|
||||
|
||||
assert(address.unix_address_size() > 0);
|
||||
if (address.unix_address_size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int fd = address.unix_address(0).socket(SOCK_STREAM);
|
||||
if (fd < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int val = 1;
|
||||
int rc = ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
|
||||
if (rc < 0) {
|
||||
::close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool failed = address.unix_address(0).bind(fd);
|
||||
if (failed) {
|
||||
::close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
::listen(fd, SOMAXCONN);
|
||||
|
||||
return heap<unix_server>(event_port_, fd, 0);
|
||||
}
|
||||
|
||||
conveyor<own<io_stream>> unix_network::connect(network_address &addr) {
|
||||
auto unix_addr_storage =
|
||||
translate_network_address_to_unix_network_address(addr);
|
||||
unix_network_address &address =
|
||||
translate_to_unix_address_ref(unix_addr_storage);
|
||||
|
||||
assert(address.unix_address_size() > 0);
|
||||
if (address.unix_address_size() == 0) {
|
||||
return conveyor<own<io_stream>>{critical_error("No address found")};
|
||||
}
|
||||
|
||||
int fd = address.unix_address(0).socket(SOCK_STREAM);
|
||||
if (fd < 0) {
|
||||
return conveyor<own<io_stream>>{critical_error("Couldn't open socket")};
|
||||
}
|
||||
|
||||
own<unix_io_stream> io_str =
|
||||
heap<unix_io_stream>(event_port_, fd, 0, EPOLLIN | EPOLLOUT);
|
||||
|
||||
bool success = false;
|
||||
for (size_t i = 0; i < address.unix_address_size(); ++i) {
|
||||
socket_address &addr_iter = address.unix_address(i);
|
||||
int status =
|
||||
::connect(fd, addr_iter.get_raw(), addr_iter.get_raw_length());
|
||||
if (status < 0) {
|
||||
int error = errno;
|
||||
/*
|
||||
* It's not connected yet...
|
||||
* But edge triggered epolling means that it'll
|
||||
* be ready when the signal is triggered
|
||||
*/
|
||||
|
||||
/// @todo Add limit node when implemented
|
||||
if (error == EINPROGRESS) {
|
||||
/*
|
||||
Conveyor<void> write_ready = io_stream->writeReady();
|
||||
return write_ready.then(
|
||||
[ios{std::move(io_stream)}]() mutable {
|
||||
ios->write_ready = nullptr;
|
||||
return std::move(ios);
|
||||
});
|
||||
*/
|
||||
success = true;
|
||||
break;
|
||||
} else if (error != EINTR) {
|
||||
/// @todo Push error message from
|
||||
return conveyor<own<io_stream>>{
|
||||
critical_error("Couldn't connect")};
|
||||
}
|
||||
} else {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
return critical_error("Couldn't connect");
|
||||
}
|
||||
|
||||
return conveyor<own<io_stream>>{std::move(io_str)};
|
||||
}
|
||||
|
||||
own<datagram> unix_network::datagram(network_address &addr) {
|
||||
auto unix_addr_storage =
|
||||
translate_network_address_to_unix_network_address(addr);
|
||||
unix_network_address &address =
|
||||
translate_to_unix_address_ref(unix_addr_storage);
|
||||
|
||||
SAW_ASSERT(address.unix_address_size() > 0) { return nullptr; }
|
||||
|
||||
int fd = address.unix_address(0).socket(SOCK_DGRAM);
|
||||
|
||||
int optval = 1;
|
||||
int rc =
|
||||
::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
||||
if (rc < 0) {
|
||||
::close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool failed = address.unix_address(0).bind(fd);
|
||||
if (failed) {
|
||||
::close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
/// @todo
|
||||
return heap<unix_datagram>(event_port_, fd, 0);
|
||||
}
|
||||
|
||||
const std::string &unix_network_address::address() const { return path_; }
|
||||
|
||||
uint16_t unix_network_address::port() const { return port_hint_; }
|
||||
|
||||
socket_address &unix_network_address::unix_address(size_t i) {
|
||||
assert(i < addresses_.size());
|
||||
/// @todo change from list to vector?
|
||||
return addresses_.at(i);
|
||||
}
|
||||
|
||||
size_t unix_network_address::unix_address_size() const {
|
||||
return addresses_.size();
|
||||
}
|
||||
|
||||
unix_network::unix_network(unix_event_port &event) : event_port_{event} {}
|
||||
|
||||
conveyor<own<network_address>>
|
||||
unix_network::resolve_address(const std::string &path, uint16_t port_hint) {
|
||||
std::string_view addr_view{path};
|
||||
{
|
||||
std::string_view str_begins_with = "unix:";
|
||||
if (begins_with(addr_view, str_begins_with)) {
|
||||
addr_view.remove_prefix(str_begins_with.size());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<socket_address> addresses =
|
||||
socket_address::resolve(addr_view, port_hint);
|
||||
|
||||
return conveyor<own<network_address>>{
|
||||
heap<unix_network_address>(path, port_hint, std::move(addresses))};
|
||||
}
|
||||
|
||||
unix_io_provider::unix_io_provider(unix_event_port &port_ref,
|
||||
own<event_port> port)
|
||||
: event_port_{port_ref}, event_loop_{std::move(port)}, unix_network_{
|
||||
port_ref} {}
|
||||
|
||||
own<input_stream> unix_io_provider::wrap_input_fd(int fd) {
|
||||
return heap<unix_io_stream>(event_port_, fd, 0, EPOLLIN);
|
||||
}
|
||||
|
||||
class network &unix_io_provider::network() {
|
||||
return static_cast<class network &>(unix_network_);
|
||||
}
|
||||
|
||||
class event_loop &unix_io_provider::event_loop() {
|
||||
return event_loop_;
|
||||
}
|
||||
|
||||
} // namespace unix
|
||||
|
||||
error_or<async_io_context> setup_async_io() {
|
||||
using namespace unix;
|
||||
try {
|
||||
own<unix_event_port> prt = heap<unix_event_port>();
|
||||
unix_event_port &prt_ref = *prt;
|
||||
|
||||
own<unix_io_provider> io_provider =
|
||||
heap<unix_io_provider>(prt_ref, std::move(prt));
|
||||
|
||||
event_loop &loop_ref = io_provider->event_loop();
|
||||
|
||||
return {{std::move(io_provider), loop_ref, prt_ref}};
|
||||
} catch (std::bad_alloc &) {
|
||||
return critical_error("Out of memory");
|
||||
}
|
||||
}
|
||||
} // namespace saw
|
|
@ -0,0 +1,470 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef SAW_UNIX
|
||||
#error "Don't include this"
|
||||
#endif
|
||||
|
||||
#include <csignal>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "io.h"
|
||||
|
||||
namespace saw {
|
||||
namespace unix {
|
||||
constexpr int MAX_EPOLL_EVENTS = 256;
|
||||
|
||||
class unix_event_port;
|
||||
class i_fd_owner {
|
||||
protected:
|
||||
unix_event_port &event_port_;
|
||||
|
||||
private:
|
||||
int file_descriptor_;
|
||||
int fd_flags_;
|
||||
uint32_t event_mask_;
|
||||
|
||||
public:
|
||||
i_fd_owner(unix_event_port &event_port, int file_descriptor, int fd_flags,
|
||||
uint32_t event_mask);
|
||||
|
||||
virtual ~i_fd_owner();
|
||||
|
||||
virtual void notify(uint32_t mask) = 0;
|
||||
|
||||
int fd() const { return file_descriptor_; }
|
||||
};
|
||||
|
||||
class unix_event_port final : public event_port {
|
||||
private:
|
||||
int epoll_fd_;
|
||||
int signal_fd_;
|
||||
|
||||
sigset_t signal_fd_set_;
|
||||
|
||||
std::unordered_multimap<Signal, own<conveyor_feeder<void>>>
|
||||
signal_conveyors_;
|
||||
|
||||
int pipefds_[2];
|
||||
|
||||
std::vector<int> to_unix_signal(Signal signal) const {
|
||||
switch (signal) {
|
||||
case Signal::User1:
|
||||
return {SIGUSR1};
|
||||
case Signal::Terminate:
|
||||
default:
|
||||
return {SIGTERM, SIGQUIT, SIGINT};
|
||||
}
|
||||
}
|
||||
|
||||
Signal from_unix_signal(int signal) const {
|
||||
switch (signal) {
|
||||
case SIGUSR1:
|
||||
return Signal::User1;
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
case SIGQUIT:
|
||||
default:
|
||||
return Signal::Terminate;
|
||||
}
|
||||
}
|
||||
|
||||
void notify_signal_listener(int sig) {
|
||||
Signal signal = from_unix_signal(sig);
|
||||
|
||||
auto equal_range = signal_conveyors_.equal_range(signal);
|
||||
for (auto iter = equal_range.first; iter != equal_range.second;
|
||||
++iter) {
|
||||
|
||||
if (iter->second) {
|
||||
if (iter->second->space() > 0) {
|
||||
iter->second->feed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool poll_impl(int time) {
|
||||
epoll_event events[MAX_EPOLL_EVENTS];
|
||||
int nfds = 0;
|
||||
do {
|
||||
nfds = epoll_wait(epoll_fd_, events, MAX_EPOLL_EVENTS, time);
|
||||
|
||||
if (nfds < 0) {
|
||||
/// @todo error_handling
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nfds; ++i) {
|
||||
if (events[i].data.u64 == 0) {
|
||||
while (1) {
|
||||
struct ::signalfd_siginfo siginfo;
|
||||
ssize_t n =
|
||||
::read(signal_fd_, &siginfo, sizeof(siginfo));
|
||||
if (n < 0) {
|
||||
break;
|
||||
}
|
||||
assert(n == sizeof(siginfo));
|
||||
|
||||
notify_signal_listener(siginfo.ssi_signo);
|
||||
}
|
||||
} else if (events[i].data.u64 == 1) {
|
||||
uint8_t i;
|
||||
if (pipefds_[0] < 0) {
|
||||
continue;
|
||||
}
|
||||
while (1) {
|
||||
ssize_t n = ::recv(pipefds_[0], &i, sizeof(i), 0);
|
||||
if (n < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i_fd_owner *owner =
|
||||
reinterpret_cast<i_fd_owner *>(events[i].data.ptr);
|
||||
if (owner) {
|
||||
owner->notify(events[i].events);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (nfds == MAX_EPOLL_EVENTS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
unix_event_port() : epoll_fd_{-1}, signal_fd_{-1} {
|
||||
::signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
epoll_fd_ = ::epoll_create1(EPOLL_CLOEXEC);
|
||||
if (epoll_fd_ < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
::sigemptyset(&signal_fd_set_);
|
||||
signal_fd_ =
|
||||
::signalfd(-1, &signal_fd_set_, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
if (signal_fd_ < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct epoll_event event;
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.events = EPOLLIN;
|
||||
event.data.u64 = 0;
|
||||
::epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, signal_fd_, &event);
|
||||
|
||||
int rc = ::pipe2(pipefds_, O_NONBLOCK | O_CLOEXEC);
|
||||
if (rc < 0) {
|
||||
return;
|
||||
}
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.events = EPOLLIN;
|
||||
event.data.u64 = 1;
|
||||
::epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, pipefds_[0], &event);
|
||||
}
|
||||
|
||||
~unix_event_port() {
|
||||
::close(epoll_fd_);
|
||||
::close(signal_fd_);
|
||||
::close(pipefds_[0]);
|
||||
::close(pipefds_[1]);
|
||||
}
|
||||
|
||||
conveyor<void> on_signal(Signal signal) override {
|
||||
auto caf = new_conveyor_and_feeder<void>();
|
||||
|
||||
signal_conveyors_.insert(std::make_pair(signal, std::move(caf.feeder)));
|
||||
|
||||
std::vector<int> sig = to_unix_signal(signal);
|
||||
|
||||
for (auto iter = sig.begin(); iter != sig.end(); ++iter) {
|
||||
::sigaddset(&signal_fd_set_, *iter);
|
||||
}
|
||||
::sigprocmask(SIG_BLOCK, &signal_fd_set_, nullptr);
|
||||
::signalfd(signal_fd_, &signal_fd_set_, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
|
||||
auto node = conveyor<void>::from_conveyor(std::move(caf.conveyor));
|
||||
return conveyor<void>::to_conveyor(std::move(node));
|
||||
}
|
||||
|
||||
void poll() override { poll_impl(0); }
|
||||
|
||||
void wait() override { poll_impl(-1); }
|
||||
|
||||
void wait(const std::chrono::steady_clock::duration &duration) override {
|
||||
poll_impl(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(duration)
|
||||
.count());
|
||||
}
|
||||
void
|
||||
wait(const std::chrono::steady_clock::time_point &time_point) override {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (time_point <= now) {
|
||||
poll();
|
||||
} else {
|
||||
poll_impl(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
time_point - now)
|
||||
.count());
|
||||
}
|
||||
}
|
||||
|
||||
void wake() override {
|
||||
/// @todo pipe() in the beginning and write something minor into it like
|
||||
/// uint8_t or sth the value itself doesn't matter
|
||||
if (pipefds_[1] < 0) {
|
||||
return;
|
||||
}
|
||||
uint8_t i = 0;
|
||||
::send(pipefds_[1], &i, sizeof(i), MSG_DONTWAIT);
|
||||
}
|
||||
|
||||
void subscribe(i_fd_owner &owner, int fd, uint32_t event_mask) {
|
||||
if (epoll_fd_ < 0 || fd < 0) {
|
||||
return;
|
||||
}
|
||||
::epoll_event event;
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.events = event_mask | EPOLLET;
|
||||
event.data.ptr = &owner;
|
||||
|
||||
if (::epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event) < 0) {
|
||||
/// @todo error_handling
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void unsubscribe(int fd) {
|
||||
if (epoll_fd_ < 0 || fd < 0) {
|
||||
return;
|
||||
}
|
||||
if (::epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) < 0) {
|
||||
/// @todo error_handling
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ssize_t unix_read(int fd, void *buffer, size_t length);
|
||||
ssize_t unix_write(int fd, const void *buffer, size_t length);
|
||||
|
||||
class unix_io_stream final : public io_stream, public i_fd_owner {
|
||||
private:
|
||||
own<conveyor_feeder<void>> read_ready_ = nullptr;
|
||||
own<conveyor_feeder<void>> on_read_disconnect_ = nullptr;
|
||||
own<conveyor_feeder<void>> write_ready_ = nullptr;
|
||||
|
||||
public:
|
||||
unix_io_stream(unix_event_port &event_port, int file_descriptor,
|
||||
int fd_flags, uint32_t event_mask);
|
||||
|
||||
error_or<size_t> read(void *buffer, size_t length) override;
|
||||
|
||||
conveyor<void> read_ready() override;
|
||||
|
||||
conveyor<void> on_read_disconnected() override;
|
||||
|
||||
error_or<size_t> write(const void *buffer, size_t length) override;
|
||||
|
||||
conveyor<void> write_ready() override;
|
||||
|
||||
/*
|
||||
void read(void *buffer, size_t min_length, size_t max_length) override;
|
||||
Conveyor<size_t> readDone() override;
|
||||
Conveyor<void> readReady() override;
|
||||
|
||||
Conveyor<void> onReadDisconnected() override;
|
||||
|
||||
void write(const void *buffer, size_t length) override;
|
||||
Conveyor<size_t> writeDone() override;
|
||||
Conveyor<void> writeReady() override;
|
||||
*/
|
||||
|
||||
void notify(uint32_t mask) override;
|
||||
};
|
||||
|
||||
class unix_server final : public server, public i_fd_owner {
|
||||
private:
|
||||
own<conveyor_feeder<own<io_stream>>> accept_feeder_ = nullptr;
|
||||
|
||||
public:
|
||||
unix_server(unix_event_port &event_port, int file_descriptor, int fd_flags);
|
||||
|
||||
conveyor<own<io_stream>> accept() override;
|
||||
|
||||
void notify(uint32_t mask) override;
|
||||
};
|
||||
|
||||
class unix_datagram final : public datagram, public i_fd_owner {
|
||||
private:
|
||||
own<conveyor_feeder<void>> read_ready_ = nullptr;
|
||||
own<conveyor_feeder<void>> write_ready_ = nullptr;
|
||||
|
||||
public:
|
||||
unix_datagram(unix_event_port &event_port, int file_descriptor,
|
||||
int fd_flags);
|
||||
|
||||
error_or<size_t> read(void *buffer, size_t length) override;
|
||||
conveyor<void> read_ready() override;
|
||||
|
||||
error_or<size_t> write(const void *buffer, size_t length,
|
||||
network_address &dest) override;
|
||||
conveyor<void> write_ready() override;
|
||||
|
||||
void notify(uint32_t mask) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper class which provides potential addresses to NetworkAddress
|
||||
*/
|
||||
class socket_address {
|
||||
private:
|
||||
union {
|
||||
struct sockaddr generic;
|
||||
struct sockaddr_un unix;
|
||||
struct sockaddr_in inet;
|
||||
struct sockaddr_in6 inet6;
|
||||
struct sockaddr_storage storage;
|
||||
} address_;
|
||||
|
||||
socklen_t address_length_;
|
||||
bool wildcard_;
|
||||
|
||||
socket_address() : wildcard_{false} {}
|
||||
|
||||
public:
|
||||
socket_address(const void *sockaddr, socklen_t len, bool wildcard)
|
||||
: address_length_{len}, wildcard_{wildcard} {
|
||||
assert(len <= sizeof(address_));
|
||||
memcpy(&address_.generic, sockaddr, len);
|
||||
}
|
||||
|
||||
int socket(int type) const {
|
||||
type |= SOCK_NONBLOCK | SOCK_CLOEXEC;
|
||||
|
||||
int result = ::socket(address_.generic.sa_family, type, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bind(int fd) const {
|
||||
if (wildcard_) {
|
||||
int value = 0;
|
||||
::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
|
||||
}
|
||||
int error = ::bind(fd, &address_.generic, address_length_);
|
||||
return error < 0;
|
||||
}
|
||||
|
||||
struct ::sockaddr *get_raw() {
|
||||
return &address_.generic;
|
||||
}
|
||||
|
||||
const struct ::sockaddr *get_raw() const { return &address_.generic; }
|
||||
|
||||
socklen_t get_raw_length() const { return address_length_; }
|
||||
|
||||
static std::vector<socket_address> resolve(std::string_view str,
|
||||
uint16_t port_hint) {
|
||||
std::vector<socket_address> results;
|
||||
|
||||
struct ::addrinfo *head;
|
||||
struct ::addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
|
||||
std::string port_string = std::to_string(port_hint);
|
||||
bool wildcard = str == "*" || str == "::";
|
||||
std::string address_string{str};
|
||||
|
||||
int error = ::getaddrinfo(address_string.c_str(), port_string.c_str(),
|
||||
&hints, &head);
|
||||
|
||||
if (error) {
|
||||
return {};
|
||||
}
|
||||
|
||||
for (struct ::addrinfo *it = head; it != nullptr; it = it->ai_next) {
|
||||
if (it->ai_addrlen > sizeof(socket_address::address_)) {
|
||||
continue;
|
||||
}
|
||||
results.push_back({it->ai_addr, it->ai_addrlen, wildcard});
|
||||
}
|
||||
::freeaddrinfo(head);
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
class unix_network_address final : public os_network_address {
|
||||
private:
|
||||
const std::string path_;
|
||||
uint16_t port_hint_;
|
||||
std::vector<socket_address> addresses_;
|
||||
|
||||
public:
|
||||
unix_network_address(const std::string &path, uint16_t port_hint,
|
||||
std::vector<socket_address> &&addr)
|
||||
: path_{path}, port_hint_{port_hint}, addresses_{std::move(addr)} {}
|
||||
|
||||
const std::string &address() const override;
|
||||
|
||||
uint16_t port() const override;
|
||||
|
||||
// Custom address info
|
||||
socket_address &unix_address(size_t i = 0);
|
||||
size_t unix_address_size() const;
|
||||
};
|
||||
|
||||
class unix_network final : public network {
|
||||
private:
|
||||
unix_event_port &event_port_;
|
||||
|
||||
public:
|
||||
unix_network(unix_event_port &event_port);
|
||||
|
||||
conveyor<own<network_address>>
|
||||
resolve_address(const std::string &address,
|
||||
uint16_t port_hint = 0) override;
|
||||
|
||||
own<server> listen(network_address &addr) override;
|
||||
|
||||
conveyor<own<io_stream>> connect(network_address &addr) override;
|
||||
|
||||
own<class datagram> datagram(network_address &addr) override;
|
||||
};
|
||||
|
||||
class unix_io_provider final : public io_provider {
|
||||
private:
|
||||
unix_event_port &event_port_;
|
||||
class event_loop event_loop_;
|
||||
|
||||
unix_network unix_network_;
|
||||
|
||||
public:
|
||||
unix_io_provider(unix_event_port &port_ref, own<event_port> port);
|
||||
|
||||
class network &network() override;
|
||||
|
||||
own<input_stream> wrap_input_fd(int fd) override;
|
||||
|
||||
class event_loop &event_loop();
|
||||
};
|
||||
} // namespace unix
|
||||
} // namespace saw
|
|
@ -0,0 +1,15 @@
|
|||
with import <nixpkgs> {};
|
||||
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
pkgs.mkShellNoCC {
|
||||
name = "forstio";
|
||||
nativeBuildInputs = [
|
||||
scons
|
||||
clang-tools_14
|
||||
clang-analyzer
|
||||
clang_14
|
||||
cppcheck
|
||||
];
|
||||
|
||||
buildInputs = [ gnutls ];
|
||||
}
|
|
@ -12,5 +12,25 @@ dir_path = Dir('.').abspath
|
|||
env.sources += sorted(glob.glob(dir_path + "/*.cpp"))
|
||||
env.headers += sorted(glob.glob(dir_path + "/*.h"))
|
||||
|
||||
env.tls_sources += sorted(glob.glob(dir_path + "/tls/*.cpp"))
|
||||
env.tls_headers += sorted(glob.glob(dir_path + "/tls/*.h"))
|
||||
# Environment for base library
|
||||
base_lib_env = env.Clone();
|
||||
|
||||
## Shared lib
|
||||
objects_shared = []
|
||||
base_lib_env.add_source_files(objects_shared, env.sources + env.driver_sources, shared=True);
|
||||
env.library_shared = base_lib_env.SharedLibrary('#build/forstio', [objects_shared]);
|
||||
|
||||
## Static lib
|
||||
objects_static = []
|
||||
base_lib_env.add_source_files(objects_static, env.sources + env.driver_sources, shared=False);
|
||||
env.library_static = base_lib_env.StaticLibrary('#build/forstio', [objects_static]);
|
||||
|
||||
# Export to other libs
|
||||
Export('base_lib_env')
|
||||
|
||||
#SConscript("rpc/SConscript");
|
||||
SConscript("tls/SConscript");
|
||||
|
||||
# Set Alias
|
||||
env.Alias('library_base', [env.library_shared, env.library_static]);
|
||||
env.Alias('library', ['library_base', 'library_tls']);
|
||||
|
|
|
@ -1,188 +1,226 @@
|
|||
#include "async.h"
|
||||
#include "common.h"
|
||||
#include "error.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
namespace saw {
|
||||
namespace {
|
||||
thread_local EventLoop *local_loop = nullptr;
|
||||
thread_local event_loop *local_loop = nullptr;
|
||||
|
||||
EventLoop ¤tEventLoop() {
|
||||
EventLoop *loop = local_loop;
|
||||
event_loop ¤t_event_loop() {
|
||||
event_loop *loop = local_loop;
|
||||
assert(loop);
|
||||
return *loop;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ConveyorNode::ConveyorNode() {}
|
||||
conveyor_node::conveyor_node() {}
|
||||
|
||||
ConveyorStorage::ConveyorStorage(ConveyorStorage *c) : child_storage{c} {}
|
||||
conveyor_node_with_child_mixin::conveyor_node_with_child_mixin(
|
||||
own<conveyor_node> &&child_, conveyor_node &owner)
|
||||
: child{std::move(child_)} {
|
||||
assert(child);
|
||||
|
||||
ConveyorStorage::~ConveyorStorage() {
|
||||
if (parent) {
|
||||
parent->unlinkChild();
|
||||
}
|
||||
child->notify_parent_attached(owner);
|
||||
}
|
||||
|
||||
void ConveyorStorage::unlinkChild() { child_storage = nullptr; }
|
||||
error_or<own<conveyor_node>>
|
||||
conveyor_node_with_child_mixin::swap_child(own<conveyor_node> &&swapee) {
|
||||
SAW_ASSERT(child) {
|
||||
return critical_error("Child should exist if this function is called");
|
||||
}
|
||||
own<conveyor_node> old_child = std::move(child);
|
||||
|
||||
void ConveyorEventStorage::setParent(ConveyorStorage *p) {
|
||||
/**
|
||||
* We need the parent of the old_child's next storage
|
||||
*/
|
||||
conveyor_storage *old_storage = old_child->next_storage();
|
||||
conveyor_storage *old_storage_parent =
|
||||
old_storage ? old_storage->get_parent() : nullptr;
|
||||
|
||||
/**
|
||||
* Swap in the new child
|
||||
*/
|
||||
if (swapee) {
|
||||
child = std::move(swapee);
|
||||
|
||||
/**
|
||||
* Then we need to set the new child's storage parent since the next
|
||||
* storage has a nullptr set And if the old_storage_parent is a nullptr,
|
||||
* then it doesn't matter. So we don't check for it
|
||||
*/
|
||||
conveyor_storage *swapee_storage = child->next_storage();
|
||||
if (swapee_storage) {
|
||||
swapee_storage->set_parent(old_storage_parent);
|
||||
}
|
||||
}
|
||||
|
||||
return old_child;
|
||||
}
|
||||
|
||||
conveyor_storage::conveyor_storage() {}
|
||||
|
||||
conveyor_storage::~conveyor_storage() {}
|
||||
|
||||
conveyor_storage *conveyor_storage::get_parent() const { return parent_; }
|
||||
|
||||
void conveyor_event_storage::set_parent(conveyor_storage *p) {
|
||||
/*
|
||||
* parent check isn't needed, but is used
|
||||
* for the assert, because the storage should
|
||||
* be armed if there was an element present
|
||||
* and a valid parent
|
||||
*/
|
||||
if (/*!parent && */ p && !isArmed() && queued() > 0) {
|
||||
assert(!parent);
|
||||
if (/*!parent && */ p && !is_armed() && queued() > 0) {
|
||||
assert(!parent_);
|
||||
if (p->space() > 0) {
|
||||
armLater();
|
||||
arm_later();
|
||||
}
|
||||
}
|
||||
|
||||
parent = p;
|
||||
parent_ = p;
|
||||
}
|
||||
|
||||
ConveyorEventStorage::ConveyorEventStorage(ConveyorStorage *c)
|
||||
: ConveyorStorage{c} {}
|
||||
conveyor_event_storage::conveyor_event_storage() : conveyor_storage{} {}
|
||||
|
||||
ConveyorBase::ConveyorBase(Own<ConveyorNode> &&node_p,
|
||||
ConveyorStorage *storage_p)
|
||||
: node{std::move(node_p)}, storage{storage_p} {}
|
||||
conveyor_base::conveyor_base(own<conveyor_node> &&node_p)
|
||||
: node_{std::move(node_p)} {}
|
||||
|
||||
Error PropagateError::operator()(const Error &error) const {
|
||||
return error.copyError();
|
||||
error propagate_error::operator()(const error &error) const {
|
||||
return error.copy_error();
|
||||
}
|
||||
|
||||
Error PropagateError::operator()(Error &&error) { return std::move(error); }
|
||||
error propagate_error::operator()(error &&err) { return std::move(err); }
|
||||
|
||||
Event::Event() : Event(currentEventLoop()) {}
|
||||
event::event() : event(current_event_loop()) {}
|
||||
|
||||
Event::Event(EventLoop &loop) : loop{loop} {}
|
||||
event::event(event_loop &loop) : loop_{loop} {}
|
||||
|
||||
Event::~Event() { disarm(); }
|
||||
event::~event() { disarm(); }
|
||||
|
||||
void Event::armNext() {
|
||||
assert(&loop == local_loop);
|
||||
if (prev == nullptr) {
|
||||
void event::arm_next() {
|
||||
assert(&loop_ == local_loop);
|
||||
if (prev_ == nullptr) {
|
||||
// Push the next_insert_point back by one
|
||||
// and inserts itself before that
|
||||
next = *loop.next_insert_point;
|
||||
prev = loop.next_insert_point;
|
||||
*prev = this;
|
||||
if (next) {
|
||||
next->prev = &next;
|
||||
next_ = *loop_.next_insert_point_;
|
||||
prev_ = loop_.next_insert_point_;
|
||||
*prev_ = this;
|
||||
if (next_) {
|
||||
next_->prev_ = &next_;
|
||||
}
|
||||
|
||||
// Set the new insertion ptr location to next
|
||||
loop.next_insert_point = &next;
|
||||
loop_.next_insert_point_ = &next_;
|
||||
|
||||
// Pushes back the later insert point if it was pointing at the
|
||||
// previous event
|
||||
if (loop.later_insert_point == prev) {
|
||||
loop.later_insert_point = &next;
|
||||
if (loop_.later_insert_point_ == prev_) {
|
||||
loop_.later_insert_point_ = &next_;
|
||||
}
|
||||
|
||||
// If tail points at the same location then
|
||||
// we are at the end and have to update tail then.
|
||||
// If tail_ points at the same location then
|
||||
// we are at the end and have to update tail_ then.
|
||||
// Technically should be possible by checking if
|
||||
// next is a `nullptr`
|
||||
if (loop.tail == prev) {
|
||||
loop.tail = &next;
|
||||
if (loop_.tail_ == prev_) {
|
||||
loop_.tail_ = &next_;
|
||||
}
|
||||
|
||||
loop.setRunnable(true);
|
||||
loop_.set_runnable(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Event::armLater() {
|
||||
assert(&loop == local_loop);
|
||||
void event::arm_later() {
|
||||
assert(&loop_ == local_loop);
|
||||
|
||||
if (prev == nullptr) {
|
||||
next = *loop.later_insert_point;
|
||||
prev = loop.later_insert_point;
|
||||
*prev = this;
|
||||
if (next) {
|
||||
next->prev = &next;
|
||||
if (prev_ == nullptr) {
|
||||
next_ = *loop_.later_insert_point_;
|
||||
prev_ = loop_.later_insert_point_;
|
||||
*prev_ = this;
|
||||
if (next_) {
|
||||
next_->prev_ = &next_;
|
||||
}
|
||||
|
||||
loop.later_insert_point = &next;
|
||||
if (loop.tail == prev) {
|
||||
loop.tail = &next;
|
||||
loop_.later_insert_point_ = &next_;
|
||||
if (loop_.tail_ == prev_) {
|
||||
loop_.tail_ = &next_;
|
||||
}
|
||||
|
||||
loop.setRunnable(true);
|
||||
loop_.set_runnable(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Event::armLast() {
|
||||
assert(&loop == local_loop);
|
||||
void event::arm_last() {
|
||||
assert(&loop_ == local_loop);
|
||||
|
||||
if (prev == nullptr) {
|
||||
next = *loop.later_insert_point;
|
||||
prev = loop.later_insert_point;
|
||||
*prev = this;
|
||||
if (next) {
|
||||
next->prev = &next;
|
||||
if (prev_ == nullptr) {
|
||||
next_ = *loop_.later_insert_point_;
|
||||
prev_ = loop_.later_insert_point_;
|
||||
*prev_ = this;
|
||||
if (next_) {
|
||||
next_->prev_ = &next_;
|
||||
}
|
||||
|
||||
if (loop.tail == prev) {
|
||||
loop.tail = &next;
|
||||
if (loop_.tail_ == prev_) {
|
||||
loop_.tail_ = &next_;
|
||||
}
|
||||
|
||||
loop.setRunnable(true);
|
||||
loop_.set_runnable(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Event::disarm() {
|
||||
if (prev != nullptr) {
|
||||
if (loop.tail == &next) {
|
||||
loop.tail = prev;
|
||||
void event::disarm() {
|
||||
if (prev_ != nullptr) {
|
||||
if (loop_.tail_ == &next_) {
|
||||
loop_.tail_ = prev_;
|
||||
}
|
||||
|
||||
if (loop.next_insert_point == &next) {
|
||||
loop.next_insert_point = prev;
|
||||
if (loop_.next_insert_point_ == &next_) {
|
||||
loop_.next_insert_point_ = prev_;
|
||||
}
|
||||
|
||||
*prev = next;
|
||||
if (next) {
|
||||
next->prev = prev;
|
||||
*prev_ = next_;
|
||||
if (next_) {
|
||||
next_->prev_ = prev_;
|
||||
}
|
||||
|
||||
prev = nullptr;
|
||||
next = nullptr;
|
||||
prev_ = nullptr;
|
||||
next_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Event::isArmed() const { return prev != nullptr; }
|
||||
bool event::is_armed() const { return prev_ != nullptr; }
|
||||
|
||||
SinkConveyor::SinkConveyor() : node{nullptr} {}
|
||||
conveyor_sink::conveyor_sink() : node_{nullptr} {}
|
||||
|
||||
SinkConveyor::SinkConveyor(Own<ConveyorNode> &&node_p)
|
||||
: node{std::move(node_p)} {}
|
||||
conveyor_sink::conveyor_sink(own<conveyor_node> &&node_p)
|
||||
: node_{std::move(node_p)} {}
|
||||
|
||||
void EventLoop::setRunnable(bool runnable) { is_runnable = runnable; }
|
||||
void event_loop::set_runnable(bool runnable) { is_runnable_ = runnable; }
|
||||
|
||||
EventLoop::EventLoop() {}
|
||||
event_loop::event_loop() {}
|
||||
|
||||
EventLoop::EventLoop(Own<EventPort> &&event_port)
|
||||
: event_port{std::move(event_port)} {}
|
||||
event_loop::event_loop(own<class event_port> &&ep)
|
||||
: event_port_{std::move(ep)} {}
|
||||
|
||||
EventLoop::~EventLoop() { assert(local_loop != this); }
|
||||
event_loop::~event_loop() { assert(local_loop != this); }
|
||||
|
||||
void EventLoop::enterScope() {
|
||||
void event_loop::enter_scope() {
|
||||
assert(!local_loop);
|
||||
local_loop = this;
|
||||
}
|
||||
|
||||
void EventLoop::leaveScope() {
|
||||
void event_loop::leave_scope() {
|
||||
assert(local_loop == this);
|
||||
local_loop = nullptr;
|
||||
}
|
||||
|
||||
bool EventLoop::turnLoop() {
|
||||
bool event_loop::turn_loop() {
|
||||
size_t turn_step = 0;
|
||||
while (head && turn_step < 65536) {
|
||||
while (head_ && turn_step < 65536) {
|
||||
if (!turn()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -191,157 +229,191 @@ bool EventLoop::turnLoop() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool EventLoop::turn() {
|
||||
Event *event = head;
|
||||
bool event_loop::turn() {
|
||||
event *event = head_;
|
||||
|
||||
if (!event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
head = event->next;
|
||||
if (head) {
|
||||
head->prev = &head;
|
||||
head_ = event->next_;
|
||||
if (head_) {
|
||||
head_->prev_ = &head_;
|
||||
}
|
||||
|
||||
next_insert_point = &head;
|
||||
if (later_insert_point == &event->next) {
|
||||
later_insert_point = &head;
|
||||
next_insert_point_ = &head_;
|
||||
if (later_insert_point_ == &event->next_) {
|
||||
later_insert_point_ = &head_;
|
||||
}
|
||||
if (tail == &event->next) {
|
||||
tail = &head;
|
||||
if (tail_ == &event->next_) {
|
||||
tail_ = &head_;
|
||||
}
|
||||
|
||||
event->next = nullptr;
|
||||
event->prev = nullptr;
|
||||
event->next_ = nullptr;
|
||||
event->prev_ = nullptr;
|
||||
|
||||
next_insert_point = &head;
|
||||
next_insert_point_ = &head_;
|
||||
|
||||
event->fire();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EventLoop::wait(const std::chrono::steady_clock::duration &duration) {
|
||||
if (event_port) {
|
||||
event_port->wait(duration);
|
||||
bool event_loop::wait(const std::chrono::steady_clock::duration &duration) {
|
||||
if (event_port_) {
|
||||
event_port_->wait(duration);
|
||||
}
|
||||
|
||||
return turnLoop();
|
||||
return turn_loop();
|
||||
}
|
||||
|
||||
bool EventLoop::wait(const std::chrono::steady_clock::time_point &time_point) {
|
||||
if (event_port) {
|
||||
event_port->wait(time_point);
|
||||
bool event_loop::wait(const std::chrono::steady_clock::time_point &time_point) {
|
||||
if (event_port_) {
|
||||
event_port_->wait(time_point);
|
||||
}
|
||||
|
||||
return turnLoop();
|
||||
return turn_loop();
|
||||
}
|
||||
|
||||
bool EventLoop::wait() {
|
||||
if (event_port) {
|
||||
event_port->wait();
|
||||
bool event_loop::wait() {
|
||||
if (event_port_) {
|
||||
event_port_->wait();
|
||||
}
|
||||
|
||||
return turnLoop();
|
||||
return turn_loop();
|
||||
}
|
||||
|
||||
bool EventLoop::poll() {
|
||||
if (event_port) {
|
||||
event_port->poll();
|
||||
bool event_loop::poll() {
|
||||
if (event_port_) {
|
||||
event_port_->poll();
|
||||
}
|
||||
|
||||
return turnLoop();
|
||||
return turn_loop();
|
||||
}
|
||||
|
||||
EventPort *EventLoop::eventPort() { return event_port.get(); }
|
||||
event_port *event_loop::event_port() { return event_port_.get(); }
|
||||
|
||||
ConveyorSinks &EventLoop::daemon() {
|
||||
if (!daemon_sink) {
|
||||
daemon_sink = heap<ConveyorSinks>();
|
||||
conveyor_sink_set &event_loop::daemon() {
|
||||
if (!daemon_sink_) {
|
||||
daemon_sink_ = heap<conveyor_sink_set>();
|
||||
}
|
||||
return *daemon_sink;
|
||||
return *daemon_sink_;
|
||||
}
|
||||
|
||||
WaitScope::WaitScope(EventLoop &loop) : loop{loop} { loop.enterScope(); }
|
||||
wait_scope::wait_scope(event_loop &loop) : loop_{loop} { loop_.enter_scope(); }
|
||||
|
||||
WaitScope::~WaitScope() { loop.leaveScope(); }
|
||||
wait_scope::~wait_scope() { loop_.leave_scope(); }
|
||||
|
||||
void WaitScope::wait() { loop.wait(); }
|
||||
void wait_scope::wait() { loop_.wait(); }
|
||||
|
||||
void WaitScope::wait(const std::chrono::steady_clock::duration &duration) {
|
||||
loop.wait(duration);
|
||||
void wait_scope::wait(const std::chrono::steady_clock::duration &duration) {
|
||||
loop_.wait(duration);
|
||||
}
|
||||
|
||||
void WaitScope::wait(const std::chrono::steady_clock::time_point &time_point) {
|
||||
loop.wait(time_point);
|
||||
void wait_scope::wait(const std::chrono::steady_clock::time_point &time_point) {
|
||||
loop_.wait(time_point);
|
||||
}
|
||||
|
||||
void WaitScope::poll() { loop.poll(); }
|
||||
void wait_scope::poll() { loop_.poll(); }
|
||||
|
||||
ImmediateConveyorNodeBase::ImmediateConveyorNodeBase()
|
||||
: ConveyorEventStorage{nullptr} {}
|
||||
error_or<own<conveyor_node>>
|
||||
convert_conveyor_node_base::swap_child(own<conveyor_node> &&swapee) noexcept {
|
||||
return child_mixin_.swap_child(std::move(swapee));
|
||||
}
|
||||
|
||||
MergeConveyorNodeBase::MergeConveyorNodeBase()
|
||||
: ConveyorEventStorage{nullptr} {}
|
||||
conveyor_storage *convert_conveyor_node_base::next_storage() noexcept {
|
||||
if (!child_mixin_.child) {
|
||||
return nullptr;
|
||||
}
|
||||
return child_mixin_.child->next_storage();
|
||||
}
|
||||
|
||||
void ConveyorSinks::destroySinkConveyorNode(ConveyorNode &node) {
|
||||
if (!isArmed()) {
|
||||
armLast();
|
||||
immediate_conveyor_node_base::immediate_conveyor_node_base()
|
||||
: conveyor_event_storage{} {}
|
||||
|
||||
merge_conveyor_node_base::merge_conveyor_node_base()
|
||||
: conveyor_event_storage{} {}
|
||||
|
||||
error_or<own<conveyor_node>> queue_buffer_conveyor_node_base::swap_child(
|
||||
own<conveyor_node> &&swapee_) noexcept {
|
||||
return child_mixin_.swap_child(std::move(swapee_));
|
||||
}
|
||||
|
||||
void conveyor_sink_set::destroy_sink_conveyor_node(conveyor_node &node) {
|
||||
if (!is_armed()) {
|
||||
arm_last();
|
||||
}
|
||||
|
||||
delete_nodes.push(&node);
|
||||
delete_nodes_.push(&node);
|
||||
}
|
||||
|
||||
void ConveyorSinks::fail(Error &&error) {
|
||||
void conveyor_sink_set::fail(error &&error) {
|
||||
/// @todo call error_handler
|
||||
}
|
||||
|
||||
ConveyorSinks::ConveyorSinks(EventLoop &event_loop) : Event{event_loop} {}
|
||||
conveyor_sink_set::conveyor_sink_set(event_loop &event_loop)
|
||||
: event{event_loop} {}
|
||||
|
||||
void ConveyorSinks::add(Conveyor<void> &&sink) {
|
||||
auto nas = Conveyor<void>::fromConveyor(std::move(sink));
|
||||
void conveyor_sink_set::add(conveyor<void> &&sink) {
|
||||
auto nas = conveyor<void>::from_conveyor(std::move(sink));
|
||||
SAW_ASSERT(nas) { return; }
|
||||
conveyor_storage *storage = nas->next_storage();
|
||||
|
||||
Own<SinkConveyorNode> sink_node = nullptr;
|
||||
own<sink_conveyor_node> sink_node = nullptr;
|
||||
try {
|
||||
sink_node =
|
||||
heap<SinkConveyorNode>(nas.second, std::move(nas.first), *this);
|
||||
sink_node = heap<sink_conveyor_node>(std::move(nas), *this);
|
||||
} catch (std::bad_alloc &) {
|
||||
return;
|
||||
}
|
||||
if (nas.second) {
|
||||
nas.second->setParent(sink_node.get());
|
||||
if (storage) {
|
||||
storage->set_parent(sink_node.get());
|
||||
}
|
||||
|
||||
sink_nodes.emplace_back(std::move(sink_node));
|
||||
sink_nodes_.emplace_back(std::move(sink_node));
|
||||
}
|
||||
|
||||
void ConveyorSinks::fire() {
|
||||
while (!delete_nodes.empty()) {
|
||||
ConveyorNode *node = delete_nodes.front();
|
||||
/*auto erased = */ std::remove_if(sink_nodes.begin(), sink_nodes.end(),
|
||||
[node](Own<ConveyorNode> &element) {
|
||||
void conveyor_sink_set::fire() {
|
||||
while (!delete_nodes_.empty()) {
|
||||
conveyor_node *node = delete_nodes_.front();
|
||||
/*auto erased = */ std::remove_if(sink_nodes_.begin(),
|
||||
sink_nodes_.end(),
|
||||
[node](own<conveyor_node> &element) {
|
||||
return node == element.get();
|
||||
});
|
||||
delete_nodes.pop();
|
||||
delete_nodes_.pop();
|
||||
}
|
||||
}
|
||||
|
||||
ConvertConveyorNodeBase::ConvertConveyorNodeBase(Own<ConveyorNode> &&dep)
|
||||
: child{std::move(dep)} {}
|
||||
convert_conveyor_node_base::convert_conveyor_node_base(own<conveyor_node> &&dep)
|
||||
: child_mixin_{std::move(dep), *this} {}
|
||||
|
||||
void ConvertConveyorNodeBase::getResult(ErrorOrValue &err_or_val) {
|
||||
getImpl(err_or_val);
|
||||
void convert_conveyor_node_base::get_result(error_or_value &err_or_val) {
|
||||
get_impl(err_or_val);
|
||||
}
|
||||
|
||||
void AttachConveyorNodeBase::getResult(ErrorOrValue &err_or_val) noexcept {
|
||||
if (child) {
|
||||
child->getResult(err_or_val);
|
||||
void attach_conveyor_node_base::get_result(
|
||||
error_or_value &err_or_val) noexcept {
|
||||
if (child_mixin_.child) {
|
||||
child_mixin_.child->get_result(err_or_val);
|
||||
}
|
||||
}
|
||||
|
||||
void detachConveyor(Conveyor<void> &&conveyor) {
|
||||
EventLoop &loop = currentEventLoop();
|
||||
ConveyorSinks &sink = loop.daemon();
|
||||
error_or<own<conveyor_node>>
|
||||
attach_conveyor_node_base::swap_child(own<conveyor_node> &&swapee_) noexcept {
|
||||
return child_mixin_.swap_child(std::move(swapee_));
|
||||
}
|
||||
|
||||
conveyor_storage *attach_conveyor_node_base::next_storage() noexcept {
|
||||
if (!child_mixin_.child) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return child_mixin_.child->next_storage();
|
||||
}
|
||||
|
||||
void detach_conveyor(conveyor<void> &&conveyor) {
|
||||
event_loop &loop = current_event_loop();
|
||||
conveyor_sink_set &sink = loop.daemon();
|
||||
sink.add(std::move(conveyor));
|
||||
}
|
||||
} // namespace saw
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -7,225 +7,225 @@
|
|||
#include <sstream>
|
||||
|
||||
namespace saw {
|
||||
Error Buffer::push(const uint8_t &value) {
|
||||
size_t write_remain = writeCompositeLength();
|
||||
error buffer::push(const uint8_t &value) {
|
||||
size_t write_remain = write_composite_length();
|
||||
if (write_remain > 0) {
|
||||
write() = value;
|
||||
writeAdvance(1);
|
||||
write_advance(1);
|
||||
} else {
|
||||
return recoverableError("Buffer too small");
|
||||
return recoverable_error("Buffer too small");
|
||||
}
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
|
||||
Error Buffer::push(const uint8_t &buffer, size_t size) {
|
||||
Error error = writeRequireLength(size);
|
||||
error buffer::push(const uint8_t &buffer, size_t size) {
|
||||
error error = write_require_length(size);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
}
|
||||
const uint8_t *buffer_ptr = &buffer;
|
||||
while (size > 0) {
|
||||
size_t segment = std::min(writeSegmentLength(), size);
|
||||
size_t segment = std::min(write_segment_length(), size);
|
||||
memcpy(&write(), buffer_ptr, segment);
|
||||
writeAdvance(segment);
|
||||
write_advance(segment);
|
||||
size -= segment;
|
||||
buffer_ptr += segment;
|
||||
}
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
|
||||
Error Buffer::pop(uint8_t &value) {
|
||||
if (readCompositeLength() > 0) {
|
||||
error buffer::pop(uint8_t &value) {
|
||||
if (read_composite_length() > 0) {
|
||||
value = read();
|
||||
readAdvance(1);
|
||||
read_advance(1);
|
||||
} else {
|
||||
return recoverableError("Buffer too small");
|
||||
return recoverable_error("Buffer too small");
|
||||
}
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
|
||||
Error Buffer::pop(uint8_t &buffer, size_t size) {
|
||||
if (readCompositeLength() >= size) {
|
||||
error buffer::pop(uint8_t &buffer, size_t size) {
|
||||
if (read_composite_length() >= size) {
|
||||
uint8_t *buffer_ptr = &buffer;
|
||||
while (size > 0) {
|
||||
size_t segment = std::min(readSegmentLength(), size);
|
||||
size_t segment = std::min(read_segment_length(), size);
|
||||
memcpy(buffer_ptr, &read(), segment);
|
||||
readAdvance(segment);
|
||||
read_advance(segment);
|
||||
size -= segment;
|
||||
buffer_ptr += segment;
|
||||
}
|
||||
} else {
|
||||
return recoverableError("Buffer too small");
|
||||
return recoverable_error("Buffer too small");
|
||||
}
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
|
||||
std::string Buffer::toString() const {
|
||||
std::string buffer::to_string() const {
|
||||
std::ostringstream oss;
|
||||
for (size_t i = 0; i < readCompositeLength(); ++i) {
|
||||
for (size_t i = 0; i < read_composite_length(); ++i) {
|
||||
oss << read(i);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string Buffer::toHex() const {
|
||||
std::string buffer::to_hex() const {
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << std::setfill('0');
|
||||
for (size_t i = 0; i < readCompositeLength(); ++i) {
|
||||
for (size_t i = 0; i < read_composite_length(); ++i) {
|
||||
oss << std::setw(2) << (uint16_t)read(i);
|
||||
if ((i + 1) < readCompositeLength()) {
|
||||
if ((i + 1) < read_composite_length()) {
|
||||
oss << ((i % 4 == 3) ? '\n' : ' ');
|
||||
}
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
BufferView::BufferView(Buffer &buffer)
|
||||
: buffer{buffer}, read_offset{0}, write_offset{0} {}
|
||||
buffer_view::buffer_view(buffer &buffer)
|
||||
: buffer_{buffer}, read_offset_{0}, write_offset_{0} {}
|
||||
|
||||
size_t BufferView::readPosition() const {
|
||||
return read_offset + buffer.readPosition();
|
||||
size_t buffer_view::read_position() const {
|
||||
return read_offset_ + buffer_.read_position();
|
||||
}
|
||||
|
||||
size_t BufferView::readCompositeLength() const {
|
||||
assert(read_offset <= buffer.readCompositeLength());
|
||||
if (read_offset > buffer.readCompositeLength()) {
|
||||
size_t buffer_view::read_composite_length() const {
|
||||
assert(read_offset_ <= buffer_.read_composite_length());
|
||||
if (read_offset_ > buffer_.read_composite_length()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return buffer.readCompositeLength() - read_offset;
|
||||
return buffer_.read_composite_length() - read_offset_;
|
||||
}
|
||||
|
||||
size_t BufferView::readSegmentLength(size_t offset) const {
|
||||
size_t off = offset + read_offset;
|
||||
assert(off <= buffer.readCompositeLength());
|
||||
if (off > buffer.readCompositeLength()) {
|
||||
size_t buffer_view::read_segment_length(size_t offset) const {
|
||||
size_t off = offset + read_offset_;
|
||||
assert(off <= buffer_.read_composite_length());
|
||||
if (off > buffer_.read_composite_length()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return buffer.readSegmentLength(off);
|
||||
return buffer_.read_segment_length(off);
|
||||
}
|
||||
|
||||
void BufferView::readAdvance(size_t bytes) {
|
||||
size_t offset = bytes + read_offset;
|
||||
assert(offset <= buffer.readCompositeLength());
|
||||
if (offset > buffer.readCompositeLength()) {
|
||||
read_offset += buffer.readCompositeLength();
|
||||
void buffer_view::read_advance(size_t bytes) {
|
||||
size_t offset = bytes + read_offset_;
|
||||
assert(offset <= buffer_.read_composite_length());
|
||||
if (offset > buffer_.read_composite_length()) {
|
||||
read_offset_ += buffer_.read_composite_length();
|
||||
return;
|
||||
}
|
||||
|
||||
read_offset += bytes;
|
||||
read_offset_ += bytes;
|
||||
}
|
||||
|
||||
uint8_t &BufferView::read(size_t i) {
|
||||
size_t pos = i + read_offset;
|
||||
uint8_t &buffer_view::read(size_t i) {
|
||||
size_t pos = i + read_offset_;
|
||||
|
||||
assert(pos < buffer.readCompositeLength());
|
||||
assert(pos < buffer_.read_composite_length());
|
||||
|
||||
return buffer.read(pos);
|
||||
return buffer_.read(pos);
|
||||
}
|
||||
|
||||
const uint8_t &BufferView::read(size_t i) const {
|
||||
size_t pos = i + read_offset;
|
||||
const uint8_t &buffer_view::read(size_t i) const {
|
||||
size_t pos = i + read_offset_;
|
||||
|
||||
assert(pos < buffer.readCompositeLength());
|
||||
assert(pos < buffer_.read_composite_length());
|
||||
|
||||
return buffer.read(pos);
|
||||
return buffer_.read(pos);
|
||||
}
|
||||
|
||||
size_t BufferView::writePosition() const {
|
||||
return write_offset + buffer.writePosition();
|
||||
size_t buffer_view::write_position() const {
|
||||
return write_offset_ + buffer_.write_position();
|
||||
}
|
||||
|
||||
size_t BufferView::writeCompositeLength() const {
|
||||
assert(write_offset <= buffer.writeCompositeLength());
|
||||
if (write_offset > buffer.writeCompositeLength()) {
|
||||
size_t buffer_view::write_composite_length() const {
|
||||
assert(write_offset_ <= buffer_.write_composite_length());
|
||||
if (write_offset_ > buffer_.write_composite_length()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return buffer.writeCompositeLength() - write_offset;
|
||||
return buffer_.write_composite_length() - write_offset_;
|
||||
}
|
||||
|
||||
size_t BufferView::writeSegmentLength(size_t offset) const {
|
||||
size_t off = offset + write_offset;
|
||||
assert(off <= buffer.writeCompositeLength());
|
||||
if (off > buffer.writeCompositeLength()) {
|
||||
size_t buffer_view::write_segment_length(size_t offset) const {
|
||||
size_t off = offset + write_offset_;
|
||||
assert(off <= buffer_.write_composite_length());
|
||||
if (off > buffer_.write_composite_length()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return buffer.writeSegmentLength(off);
|
||||
return buffer_.write_segment_length(off);
|
||||
}
|
||||
|
||||
void BufferView::writeAdvance(size_t bytes) {
|
||||
size_t offset = bytes + write_offset;
|
||||
assert(offset <= buffer.writeCompositeLength());
|
||||
if (offset > buffer.writeCompositeLength()) {
|
||||
write_offset += buffer.writeCompositeLength();
|
||||
void buffer_view::write_advance(size_t bytes) {
|
||||
size_t offset = bytes + write_offset_;
|
||||
assert(offset <= buffer_.write_composite_length());
|
||||
if (offset > buffer_.write_composite_length()) {
|
||||
write_offset_ += buffer_.write_composite_length();
|
||||
return;
|
||||
}
|
||||
|
||||
write_offset += bytes;
|
||||
write_offset_ += bytes;
|
||||
}
|
||||
|
||||
uint8_t &BufferView::write(size_t i) {
|
||||
size_t pos = i + write_offset;
|
||||
uint8_t &buffer_view::write(size_t i) {
|
||||
size_t pos = i + write_offset_;
|
||||
|
||||
assert(pos < buffer.writeCompositeLength());
|
||||
assert(pos < buffer_.write_composite_length());
|
||||
|
||||
return buffer.write(pos);
|
||||
return buffer_.write(pos);
|
||||
}
|
||||
|
||||
const uint8_t &BufferView::write(size_t i) const {
|
||||
size_t pos = i + write_offset;
|
||||
const uint8_t &buffer_view::write(size_t i) const {
|
||||
size_t pos = i + write_offset_;
|
||||
|
||||
assert(pos < buffer.writeCompositeLength());
|
||||
assert(pos < buffer_.write_composite_length());
|
||||
|
||||
return buffer.write(pos);
|
||||
return buffer_.write(pos);
|
||||
}
|
||||
|
||||
Error BufferView::writeRequireLength(size_t bytes) {
|
||||
return buffer.writeRequireLength(bytes + write_offset);
|
||||
error buffer_view::write_require_length(size_t bytes) {
|
||||
return buffer_.write_require_length(bytes + write_offset_);
|
||||
}
|
||||
|
||||
size_t BufferView::readOffset() const { return read_offset; }
|
||||
size_t buffer_view::read_offset() const { return read_offset_; }
|
||||
|
||||
size_t BufferView::writeOffset() const { return write_offset; }
|
||||
size_t buffer_view::write_offset() const { return write_offset_; }
|
||||
|
||||
RingBuffer::RingBuffer() : read_position{0}, write_position{0} {
|
||||
buffer.resize(RING_BUFFER_MAX_SIZE);
|
||||
ring_buffer::ring_buffer() : read_position_{0}, write_position_{0} {
|
||||
buffer_.resize(RING_BUFFER_MAX_SIZE);
|
||||
}
|
||||
|
||||
RingBuffer::RingBuffer(size_t size) : read_position{0}, write_position{0} {
|
||||
buffer.resize(size);
|
||||
ring_buffer::ring_buffer(size_t size) : read_position_{0}, write_position_{0} {
|
||||
buffer_.resize(size);
|
||||
}
|
||||
|
||||
size_t RingBuffer::readPosition() const { return read_position; }
|
||||
size_t ring_buffer::read_position() const { return read_position_; }
|
||||
|
||||
/*
|
||||
* If write is ahead of read it is a simple distance, but if read ist ahead of
|
||||
* write then there are two segments
|
||||
*
|
||||
*/
|
||||
size_t RingBuffer::readCompositeLength() const {
|
||||
return writePosition() < readPosition()
|
||||
? buffer.size() - (readPosition() - writePosition())
|
||||
: (write_reached_read ? buffer.size()
|
||||
: writePosition() - readPosition());
|
||||
size_t ring_buffer::read_composite_length() const {
|
||||
return write_position() < read_position()
|
||||
? buffer_.size() - (read_position() - write_position())
|
||||
: (write_reached_read_ ? buffer_.size()
|
||||
: write_position() - read_position());
|
||||
}
|
||||
|
||||
/*
|
||||
* If write is ahead then it's the simple distance again. If read is ahead it's
|
||||
* until the end of the buffer/segment
|
||||
*/
|
||||
size_t RingBuffer::readSegmentLength(size_t offset) const {
|
||||
size_t read_composite = readCompositeLength();
|
||||
size_t ring_buffer::read_segment_length(size_t offset) const {
|
||||
size_t read_composite = read_composite_length();
|
||||
assert(offset <= read_composite);
|
||||
offset = std::min(offset, read_composite);
|
||||
size_t remaining = read_composite - offset;
|
||||
|
||||
size_t read_offset = readPosition() + offset;
|
||||
read_offset = read_offset >= buffer.size() ? read_offset - buffer.size()
|
||||
: read_offset;
|
||||
size_t read_offset = read_position() + offset;
|
||||
read_offset = read_offset >= buffer_.size() ? read_offset - buffer_.size()
|
||||
: read_offset;
|
||||
|
||||
// case 1 write is located before read and reached read
|
||||
// then offset can be used normally
|
||||
|
@ -233,98 +233,99 @@ size_t RingBuffer::readSegmentLength(size_t offset) const {
|
|||
// then it is set to zero by readCompositeLength()
|
||||
// case 3 write is located after read
|
||||
// since std::min you can use simple subtraction
|
||||
if (writePosition() < read_offset) {
|
||||
return buffer.size() - read_offset;
|
||||
if (write_position() < read_offset) {
|
||||
return buffer_.size() - read_offset;
|
||||
}
|
||||
|
||||
if (writePosition() == read_offset) {
|
||||
if (write_position() == read_offset) {
|
||||
if (remaining > 0) {
|
||||
return buffer.size() - read_offset;
|
||||
return buffer_.size() - read_offset;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return writePosition() - read_offset;
|
||||
return write_position() - read_offset;
|
||||
}
|
||||
|
||||
void RingBuffer::readAdvance(size_t bytes) {
|
||||
size_t read_composite = readCompositeLength();
|
||||
void ring_buffer::read_advance(size_t bytes) {
|
||||
size_t read_composite = read_composite_length();
|
||||
|
||||
assert(bytes <= read_composite);
|
||||
bytes = std::min(bytes, read_composite);
|
||||
size_t advanced = read_position + bytes;
|
||||
read_position = advanced >= buffer.size() ? advanced - buffer.size()
|
||||
: advanced;
|
||||
write_reached_read = bytes > 0 ? false : write_reached_read;
|
||||
size_t advanced = read_position_ + bytes;
|
||||
read_position_ = advanced >= buffer_.size() ? advanced - buffer_.size()
|
||||
: advanced;
|
||||
write_reached_read_ = bytes > 0 ? false : write_reached_read_;
|
||||
}
|
||||
|
||||
uint8_t &RingBuffer::read(size_t i) {
|
||||
assert(i < readCompositeLength());
|
||||
size_t pos = read_position + i;
|
||||
pos = pos >= buffer.size() ? pos - buffer.size() : pos;
|
||||
return buffer[pos];
|
||||
uint8_t &ring_buffer::read(size_t i) {
|
||||
assert(i < read_composite_length());
|
||||
size_t pos = read_position_ + i;
|
||||
pos = pos >= buffer_.size() ? pos - buffer_.size() : pos;
|
||||
return buffer_[pos];
|
||||
}
|
||||
|
||||
const uint8_t &RingBuffer::read(size_t i) const {
|
||||
assert(i < readCompositeLength());
|
||||
size_t pos = read_position + i;
|
||||
pos = pos >= buffer.size() ? pos - buffer.size() : pos;
|
||||
return buffer[pos];
|
||||
const uint8_t &ring_buffer::read(size_t i) const {
|
||||
assert(i < read_composite_length());
|
||||
size_t pos = read_position_ + i;
|
||||
pos = pos >= buffer_.size() ? pos - buffer_.size() : pos;
|
||||
return buffer_[pos];
|
||||
}
|
||||
|
||||
size_t RingBuffer::writePosition() const { return write_position; }
|
||||
size_t ring_buffer::write_position() const { return write_position_; }
|
||||
|
||||
size_t RingBuffer::writeCompositeLength() const {
|
||||
return readPosition() > writePosition()
|
||||
? (readPosition() - writePosition())
|
||||
: (write_reached_read
|
||||
size_t ring_buffer::write_composite_length() const {
|
||||
return read_position() > write_position()
|
||||
? (read_position() - write_position())
|
||||
: (write_reached_read_
|
||||
? 0
|
||||
: buffer.size() - (writePosition() - readPosition()));
|
||||
: buffer_.size() - (write_position() - read_position()));
|
||||
}
|
||||
|
||||
size_t RingBuffer::writeSegmentLength(size_t offset) const {
|
||||
size_t write_composite = writeCompositeLength();
|
||||
size_t ring_buffer::write_segment_length(size_t offset) const {
|
||||
size_t write_composite = write_composite_length();
|
||||
assert(offset <= write_composite);
|
||||
offset = std::min(offset, write_composite);
|
||||
|
||||
size_t write_offset = writePosition() + offset;
|
||||
write_offset = write_offset >= buffer.size() ? write_offset - buffer.size()
|
||||
: write_offset;
|
||||
size_t write_offset = write_position() + offset;
|
||||
write_offset = write_offset >= buffer_.size()
|
||||
? write_offset - buffer_.size()
|
||||
: write_offset;
|
||||
|
||||
if (read_position > write_offset) {
|
||||
return read_position - write_offset;
|
||||
if (read_position_ > write_offset) {
|
||||
return read_position_ - write_offset;
|
||||
}
|
||||
|
||||
if (write_reached_read) {
|
||||
if (write_reached_read_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return buffer.size() - write_offset;
|
||||
return buffer_.size() - write_offset;
|
||||
}
|
||||
|
||||
void RingBuffer::writeAdvance(size_t bytes) {
|
||||
assert(bytes <= writeCompositeLength());
|
||||
size_t advanced = write_position + bytes;
|
||||
write_position = advanced >= buffer.size() ? advanced - buffer.size()
|
||||
: advanced;
|
||||
void ring_buffer::write_advance(size_t bytes) {
|
||||
assert(bytes <= write_composite_length());
|
||||
size_t advanced = write_position_ + bytes;
|
||||
write_position_ = advanced >= buffer_.size() ? advanced - buffer_.size()
|
||||
: advanced;
|
||||
|
||||
write_reached_read =
|
||||
(write_position == read_position && bytes > 0 ? true : false);
|
||||
write_reached_read_ =
|
||||
(write_position_ == read_position_ && bytes > 0 ? true : false);
|
||||
}
|
||||
|
||||
uint8_t &RingBuffer::write(size_t i) {
|
||||
assert(i < writeCompositeLength());
|
||||
size_t pos = write_position + i;
|
||||
pos = pos >= buffer.size() ? pos - buffer.size() : pos;
|
||||
return buffer[pos];
|
||||
uint8_t &ring_buffer::write(size_t i) {
|
||||
assert(i < write_composite_length());
|
||||
size_t pos = write_position_ + i;
|
||||
pos = pos >= buffer_.size() ? pos - buffer_.size() : pos;
|
||||
return buffer_[pos];
|
||||
}
|
||||
|
||||
const uint8_t &RingBuffer::write(size_t i) const {
|
||||
assert(i < writeCompositeLength());
|
||||
size_t pos = write_position + i;
|
||||
pos = pos >= buffer.size() ? pos - buffer.size() : pos;
|
||||
return buffer[pos];
|
||||
const uint8_t &ring_buffer::write(size_t i) const {
|
||||
assert(i < write_composite_length());
|
||||
size_t pos = write_position_ + i;
|
||||
pos = pos >= buffer_.size() ? pos - buffer_.size() : pos;
|
||||
return buffer_[pos];
|
||||
}
|
||||
/*
|
||||
Error RingBuffer::increaseSize(size_t size){
|
||||
|
@ -344,89 +345,90 @@ const uint8_t &RingBuffer::write(size_t i) const {
|
|||
return noError();
|
||||
}
|
||||
*/
|
||||
Error RingBuffer::writeRequireLength(size_t bytes) {
|
||||
size_t write_remain = writeCompositeLength();
|
||||
error ring_buffer::write_require_length(size_t bytes) {
|
||||
size_t write_remain = write_composite_length();
|
||||
if (bytes > write_remain) {
|
||||
return recoverableError("Buffer too small");
|
||||
return recoverable_error("Buffer too small");
|
||||
}
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
|
||||
ArrayBuffer::ArrayBuffer(size_t size) : read_position{0}, write_position{0} {
|
||||
buffer.resize(size);
|
||||
array_buffer::array_buffer(size_t size)
|
||||
: read_position_{0}, write_position_{0} {
|
||||
buffer_.resize(size);
|
||||
}
|
||||
|
||||
size_t ArrayBuffer::readPosition() const { return read_position; }
|
||||
size_t array_buffer::read_position() const { return read_position_; }
|
||||
|
||||
size_t ArrayBuffer::readCompositeLength() const {
|
||||
return write_position - read_position;
|
||||
size_t array_buffer::read_composite_length() const {
|
||||
return write_position_ - read_position_;
|
||||
}
|
||||
|
||||
size_t ArrayBuffer::readSegmentLength(size_t offset) const {
|
||||
size_t read_composite = readCompositeLength();
|
||||
size_t array_buffer::read_segment_length(size_t offset) const {
|
||||
size_t read_composite = read_composite_length();
|
||||
assert(offset <= read_composite);
|
||||
|
||||
offset = std::min(read_composite, offset);
|
||||
size_t read_offset = read_position + offset;
|
||||
size_t read_offset = read_position_ + offset;
|
||||
|
||||
return write_position - read_offset;
|
||||
return write_position_ - read_offset;
|
||||
}
|
||||
|
||||
void ArrayBuffer::readAdvance(size_t bytes) {
|
||||
assert(bytes <= readCompositeLength());
|
||||
read_position += bytes;
|
||||
void array_buffer::read_advance(size_t bytes) {
|
||||
assert(bytes <= read_composite_length());
|
||||
read_position_ += bytes;
|
||||
}
|
||||
|
||||
uint8_t &ArrayBuffer::read(size_t i) {
|
||||
assert(i < readCompositeLength());
|
||||
uint8_t &array_buffer::read(size_t i) {
|
||||
assert(i < read_composite_length());
|
||||
|
||||
return buffer[i + read_position];
|
||||
return buffer_[i + read_position_];
|
||||
}
|
||||
|
||||
const uint8_t &ArrayBuffer::read(size_t i) const {
|
||||
assert(i + read_position < buffer.size());
|
||||
const uint8_t &array_buffer::read(size_t i) const {
|
||||
assert(i + read_position_ < buffer_.size());
|
||||
|
||||
return buffer[i + read_position];
|
||||
return buffer_[i + read_position_];
|
||||
}
|
||||
|
||||
size_t ArrayBuffer::writePosition() const { return write_position; }
|
||||
size_t array_buffer::write_position() const { return write_position_; }
|
||||
|
||||
size_t ArrayBuffer::writeCompositeLength() const {
|
||||
assert(write_position <= buffer.size());
|
||||
return buffer.size() - write_position;
|
||||
size_t array_buffer::write_composite_length() const {
|
||||
assert(write_position_ <= buffer_.size());
|
||||
return buffer_.size() - write_position_;
|
||||
}
|
||||
|
||||
size_t ArrayBuffer::writeSegmentLength(size_t offset) const {
|
||||
assert(write_position <= buffer.size());
|
||||
size_t write_composite = writeCompositeLength();
|
||||
size_t array_buffer::write_segment_length(size_t offset) const {
|
||||
assert(write_position_ <= buffer_.size());
|
||||
size_t write_composite = write_composite_length();
|
||||
|
||||
assert(offset <= write_composite);
|
||||
offset = std::min(write_composite, offset);
|
||||
size_t write_offset = write_position + offset;
|
||||
size_t write_offset = write_position_ + offset;
|
||||
|
||||
return buffer.size() - write_offset;
|
||||
return buffer_.size() - write_offset;
|
||||
}
|
||||
|
||||
void ArrayBuffer::writeAdvance(size_t bytes) {
|
||||
assert(bytes <= writeCompositeLength());
|
||||
write_position += bytes;
|
||||
void array_buffer::write_advance(size_t bytes) {
|
||||
assert(bytes <= write_composite_length());
|
||||
write_position_ += bytes;
|
||||
}
|
||||
|
||||
uint8_t &ArrayBuffer::write(size_t i) {
|
||||
assert(i < writeCompositeLength());
|
||||
return buffer[i + write_position];
|
||||
uint8_t &array_buffer::write(size_t i) {
|
||||
assert(i < write_composite_length());
|
||||
return buffer_[i + write_position_];
|
||||
}
|
||||
|
||||
const uint8_t &ArrayBuffer::write(size_t i) const {
|
||||
assert(i < writeCompositeLength());
|
||||
return buffer[i + write_position];
|
||||
const uint8_t &array_buffer::write(size_t i) const {
|
||||
assert(i < write_composite_length());
|
||||
return buffer_[i + write_position_];
|
||||
}
|
||||
Error ArrayBuffer::writeRequireLength(size_t bytes) {
|
||||
size_t write_remain = writeCompositeLength();
|
||||
error array_buffer::write_require_length(size_t bytes) {
|
||||
size_t write_remain = write_composite_length();
|
||||
if (bytes > write_remain) {
|
||||
return recoverableError("Buffer too small");
|
||||
return recoverable_error("Buffer too small");
|
||||
}
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
|
||||
} // namespace saw
|
||||
|
|
|
@ -13,23 +13,23 @@ namespace saw {
|
|||
/*
|
||||
* Access class to reduce templated BufferSegments bloat
|
||||
*/
|
||||
class Buffer {
|
||||
class buffer {
|
||||
protected:
|
||||
~Buffer() = default;
|
||||
~buffer() = default;
|
||||
|
||||
public:
|
||||
virtual size_t readPosition() const = 0;
|
||||
virtual size_t readCompositeLength() const = 0;
|
||||
virtual size_t readSegmentLength(size_t offset = 0) const = 0;
|
||||
virtual void readAdvance(size_t bytes) = 0;
|
||||
virtual size_t read_position() const = 0;
|
||||
virtual size_t read_composite_length() const = 0;
|
||||
virtual size_t read_segment_length(size_t offset = 0) const = 0;
|
||||
virtual void read_advance(size_t bytes) = 0;
|
||||
|
||||
virtual uint8_t &read(size_t i = 0) = 0;
|
||||
virtual const uint8_t &read(size_t i = 0) const = 0;
|
||||
|
||||
virtual size_t writePosition() const = 0;
|
||||
virtual size_t writeCompositeLength() const = 0;
|
||||
virtual size_t writeSegmentLength(size_t offset = 0) const = 0;
|
||||
virtual void writeAdvance(size_t bytes) = 0;
|
||||
virtual size_t write_position() const = 0;
|
||||
virtual size_t write_composite_length() const = 0;
|
||||
virtual size_t write_segment_length(size_t offset = 0) const = 0;
|
||||
virtual void write_advance(size_t bytes) = 0;
|
||||
|
||||
virtual uint8_t &write(size_t i = 0) = 0;
|
||||
virtual const uint8_t &write(size_t i = 0) const = 0;
|
||||
|
@ -40,50 +40,53 @@ public:
|
|||
* There is nothing you can do if read hasn't been filled, but at
|
||||
* least write can be increased if it is demanded.
|
||||
*/
|
||||
virtual Error writeRequireLength(size_t bytes) = 0;
|
||||
virtual error write_require_length(size_t bytes) = 0;
|
||||
|
||||
Error push(const uint8_t &value);
|
||||
Error push(const uint8_t &buffer, size_t size);
|
||||
Error pop(uint8_t &value);
|
||||
Error pop(uint8_t &buffer, size_t size);
|
||||
error push(const uint8_t &value);
|
||||
error push(const uint8_t &buffer, size_t size);
|
||||
error pop(uint8_t &value);
|
||||
error pop(uint8_t &buffer, size_t size);
|
||||
|
||||
std::string toString() const;
|
||||
std::string toHex() const;
|
||||
/*
|
||||
* Subject to change
|
||||
*/
|
||||
std::string to_string() const;
|
||||
std::string to_hex() const;
|
||||
};
|
||||
|
||||
/*
|
||||
* A viewer class for buffers.
|
||||
* Working on the reference buffer invalidates the buffer view
|
||||
*/
|
||||
class BufferView : public Buffer {
|
||||
class buffer_view : public buffer {
|
||||
private:
|
||||
Buffer &buffer;
|
||||
size_t read_offset;
|
||||
size_t write_offset;
|
||||
buffer &buffer_;
|
||||
size_t read_offset_;
|
||||
size_t write_offset_;
|
||||
|
||||
public:
|
||||
BufferView(Buffer &);
|
||||
buffer_view(buffer &);
|
||||
|
||||
size_t readPosition() const override;
|
||||
size_t readCompositeLength() const override;
|
||||
size_t readSegmentLength(size_t offset = 0) const override;
|
||||
void readAdvance(size_t bytes) override;
|
||||
size_t read_position() const override;
|
||||
size_t read_composite_length() const override;
|
||||
size_t read_segment_length(size_t offset = 0) const override;
|
||||
void read_advance(size_t bytes) override;
|
||||
|
||||
uint8_t &read(size_t i = 0) override;
|
||||
const uint8_t &read(size_t i = 0) const override;
|
||||
|
||||
size_t writePosition() const override;
|
||||
size_t writeCompositeLength() const override;
|
||||
size_t writeSegmentLength(size_t offset = 0) const override;
|
||||
void writeAdvance(size_t bytes) override;
|
||||
size_t write_position() const override;
|
||||
size_t write_composite_length() const override;
|
||||
size_t write_segment_length(size_t offset = 0) const override;
|
||||
void write_advance(size_t bytes) override;
|
||||
|
||||
uint8_t &write(size_t i = 0) override;
|
||||
const uint8_t &write(size_t i = 0) const override;
|
||||
|
||||
Error writeRequireLength(size_t bytes) override;
|
||||
error write_require_length(size_t bytes) override;
|
||||
|
||||
size_t readOffset() const;
|
||||
size_t writeOffset() const;
|
||||
size_t read_offset() const;
|
||||
size_t write_offset() const;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -94,99 +97,99 @@ constexpr size_t RING_BUFFER_MAX_SIZE = 4096;
|
|||
/*
|
||||
* Buffer wrapping around if read caught up
|
||||
*/
|
||||
class RingBuffer final : public Buffer {
|
||||
class ring_buffer final : public buffer {
|
||||
private:
|
||||
std::vector<uint8_t> buffer;
|
||||
size_t read_position;
|
||||
size_t write_position;
|
||||
bool write_reached_read = false;
|
||||
std::vector<uint8_t> buffer_;
|
||||
size_t read_position_;
|
||||
size_t write_position_;
|
||||
bool write_reached_read_ = false;
|
||||
|
||||
public:
|
||||
RingBuffer();
|
||||
RingBuffer(size_t size);
|
||||
ring_buffer();
|
||||
ring_buffer(size_t size);
|
||||
|
||||
inline size_t size() const { return buffer.size(); }
|
||||
inline size_t size() const { return buffer_.size(); }
|
||||
|
||||
inline uint8_t &operator[](size_t i) { return buffer[i]; }
|
||||
inline const uint8_t &operator[](size_t i) const { return buffer[i]; }
|
||||
inline uint8_t &operator[](size_t i) { return buffer_[i]; }
|
||||
inline const uint8_t &operator[](size_t i) const { return buffer_[i]; }
|
||||
|
||||
size_t readPosition() const override;
|
||||
size_t readCompositeLength() const override;
|
||||
size_t readSegmentLength(size_t offset = 0) const override;
|
||||
void readAdvance(size_t bytes) override;
|
||||
size_t read_position() const override;
|
||||
size_t read_composite_length() const override;
|
||||
size_t read_segment_length(size_t offset = 0) const override;
|
||||
void read_advance(size_t bytes) override;
|
||||
|
||||
uint8_t &read(size_t i = 0) override;
|
||||
const uint8_t &read(size_t i = 0) const override;
|
||||
|
||||
size_t writePosition() const override;
|
||||
size_t writeCompositeLength() const override;
|
||||
size_t writeSegmentLength(size_t offset = 0) const override;
|
||||
void writeAdvance(size_t bytes) override;
|
||||
size_t write_position() const override;
|
||||
size_t write_composite_length() const override;
|
||||
size_t write_segment_length(size_t offset = 0) const override;
|
||||
void write_advance(size_t bytes) override;
|
||||
|
||||
uint8_t &write(size_t i = 0) override;
|
||||
const uint8_t &write(size_t i = 0) const override;
|
||||
|
||||
Error writeRequireLength(size_t bytes) override;
|
||||
error write_require_length(size_t bytes) override;
|
||||
};
|
||||
|
||||
/*
|
||||
* One time buffer
|
||||
*/
|
||||
class ArrayBuffer : public Buffer {
|
||||
class array_buffer : public buffer {
|
||||
private:
|
||||
std::vector<uint8_t> buffer;
|
||||
std::vector<uint8_t> buffer_;
|
||||
|
||||
size_t read_position;
|
||||
size_t write_position;
|
||||
size_t read_position_;
|
||||
size_t write_position_;
|
||||
|
||||
public:
|
||||
ArrayBuffer(size_t size);
|
||||
array_buffer(size_t size);
|
||||
|
||||
size_t readPosition() const override;
|
||||
size_t readCompositeLength() const override;
|
||||
size_t readSegmentLength(size_t offset = 0) const override;
|
||||
void readAdvance(size_t bytes) override;
|
||||
size_t read_position() const override;
|
||||
size_t read_composite_length() const override;
|
||||
size_t read_segment_length(size_t offset = 0) const override;
|
||||
void read_advance(size_t bytes) override;
|
||||
|
||||
uint8_t &read(size_t i = 0) override;
|
||||
const uint8_t &read(size_t i = 0) const override;
|
||||
|
||||
size_t writePosition() const override;
|
||||
size_t writeCompositeLength() const override;
|
||||
size_t writeSegmentLength(size_t offset = 0) const override;
|
||||
void writeAdvance(size_t bytes) override;
|
||||
size_t write_position() const override;
|
||||
size_t write_composite_length() const override;
|
||||
size_t write_segment_length(size_t offset = 0) const override;
|
||||
void write_advance(size_t bytes) override;
|
||||
|
||||
uint8_t &write(size_t i = 0) override;
|
||||
const uint8_t &write(size_t i = 0) const override;
|
||||
|
||||
Error writeRequireLength(size_t bytes) override;
|
||||
error write_require_length(size_t bytes) override;
|
||||
};
|
||||
|
||||
class ChainArrayBuffer : public Buffer {
|
||||
class chain_array_buffer : public buffer {
|
||||
private:
|
||||
std::deque<ArrayBuffer> buffer;
|
||||
std::deque<array_buffer> buffer_;
|
||||
|
||||
size_t read_position;
|
||||
size_t write_position;
|
||||
size_t read_position_;
|
||||
size_t write_position_;
|
||||
|
||||
public:
|
||||
ChainArrayBuffer();
|
||||
chain_array_buffer();
|
||||
|
||||
size_t readPosition() const override;
|
||||
size_t readCompositeLength() const override;
|
||||
size_t readSegmentLength(size_t offset = 0) const override;
|
||||
void readAdvance(size_t bytes) override;
|
||||
size_t read_position() const override;
|
||||
size_t read_composite_length() const override;
|
||||
size_t read_segment_length(size_t offset = 0) const override;
|
||||
void read_advance(size_t bytes) override;
|
||||
|
||||
uint8_t &read(size_t i = 0) override;
|
||||
const uint8_t &read(size_t i = 0) const override;
|
||||
|
||||
size_t writePosition() const override;
|
||||
size_t writeCompositeLength() const override;
|
||||
size_t writeSegmentLength(size_t offset = 0) const override;
|
||||
void writeAdvance(size_t bytes) override;
|
||||
size_t write_position() const override;
|
||||
size_t write_composite_length() const override;
|
||||
size_t write_segment_length(size_t offset = 0) const override;
|
||||
void write_advance(size_t bytes) override;
|
||||
|
||||
uint8_t &write(size_t i = 0) override;
|
||||
const uint8_t &write(size_t i = 0) const override;
|
||||
|
||||
Error writeRequireLength(size_t bytes) override;
|
||||
error write_require_length(size_t bytes) override;
|
||||
};
|
||||
} // namespace saw
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env false
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import glob
|
||||
|
||||
|
||||
Import('env');
|
||||
|
||||
dir_path = Dir('.').abspath
|
||||
|
||||
codec_json_env = env.Clone();
|
||||
|
||||
|
|
@ -27,46 +27,49 @@ namespace saw {
|
|||
classname(classname &&) = default; \
|
||||
classname &operator=(classname &&) = default
|
||||
|
||||
// In case of C++20
|
||||
#define SAW_ASSERT(expression) \
|
||||
assert(expression); \
|
||||
if (!(expression))
|
||||
if (!(expression)) [[unlikely]]
|
||||
|
||||
template <typename T> using Maybe = std::optional<T>;
|
||||
template <typename T> using maybe = std::optional<T>;
|
||||
|
||||
template <typename T> using Own = std::unique_ptr<T>;
|
||||
template <typename T> using own = std::unique_ptr<T>;
|
||||
|
||||
template <typename T> using Our = std::shared_ptr<T>;
|
||||
template <typename T> using our = std::shared_ptr<T>;
|
||||
|
||||
template <typename T> using Lent = std::weak_ptr<T>;
|
||||
template <typename T> using lent = std::weak_ptr<T>;
|
||||
|
||||
template <typename T, class... Args> Own<T> heap(Args &&...args) {
|
||||
return Own<T>(new T(std::forward<Args>(args)...));
|
||||
template <typename T, class... Args> own<T> heap(Args &&...args) {
|
||||
return own<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename T, class... Args> Our<T> share(Args &&...args) {
|
||||
template <typename T, class... Args> our<T> share(Args &&...args) {
|
||||
return std::make_shared<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T> T instance() noexcept;
|
||||
|
||||
template <typename Func, typename T> struct ReturnTypeHelper {
|
||||
template <typename Func, typename T> struct return_type_helper {
|
||||
typedef decltype(instance<Func>()(instance<T>())) Type;
|
||||
};
|
||||
template <typename Func> struct ReturnTypeHelper<Func, void> {
|
||||
template <typename Func> struct return_type_helper<Func, void> {
|
||||
typedef decltype(instance<Func>()()) Type;
|
||||
};
|
||||
|
||||
template <typename Func, typename T>
|
||||
using ReturnType = typename ReturnTypeHelper<Func, T>::Type;
|
||||
using return_type = typename return_type_helper<Func, T>::Type;
|
||||
|
||||
struct Void {};
|
||||
struct void_t {};
|
||||
|
||||
template <typename T> struct VoidFix { typedef T Type; };
|
||||
template <> struct VoidFix<void> { typedef Void Type; };
|
||||
template <typename T> using FixVoid = typename VoidFix<T>::Type;
|
||||
template <typename T> struct void_fix { typedef T Type; };
|
||||
template <> struct void_fix<void> { typedef void_t Type; };
|
||||
template <typename T> using fix_void = typename void_fix<T>::Type;
|
||||
|
||||
template <typename T> struct VoidUnfix { typedef T Type; };
|
||||
template <> struct VoidUnfix<Void> { typedef void Type; };
|
||||
template <typename T> using UnfixVoid = typename VoidUnfix<T>::Type;
|
||||
template <typename T> struct void_unfix { typedef T Type; };
|
||||
template <> struct void_unfix<void_t> { typedef void Type; };
|
||||
template <typename T> using unfix_void = typename void_unfix<T>::Type;
|
||||
|
||||
template <typename... T> constexpr bool always_false = false;
|
||||
|
||||
} // namespace saw
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
#include "error.h"
|
||||
|
||||
namespace saw {
|
||||
Error::Error() : error_{static_cast<Error::Code>(0)} {}
|
||||
error::error() : error_{static_cast<error::code>(0)} {}
|
||||
|
||||
Error::Error(const std::string_view &msg, Error::Code code)
|
||||
: error_message{msg}, error_{static_cast<Error::Code>(code)} {}
|
||||
error::error(const std::string_view &msg, error::code code)
|
||||
: error_message_{msg}, error_{static_cast<error::code>(code)} {}
|
||||
|
||||
Error::Error(std::string &&msg, Error::Code code)
|
||||
: error_message{std::move(msg)}, error_{static_cast<Error::Code>(code)} {}
|
||||
error::error(std::string &&msg, error::code code)
|
||||
: error_message_{std::move(msg)}, error_{static_cast<error::code>(code)} {}
|
||||
|
||||
Error::Error(Error &&error)
|
||||
: error_message{std::move(error.error_message)}, error_{std::move(
|
||||
error.error_)} {}
|
||||
error::error(error &&error)
|
||||
: error_message_{std::move(error.error_message_)}, error_{std::move(
|
||||
error.error_)} {}
|
||||
|
||||
const std::string_view Error::message() const {
|
||||
const std::string_view error::message() const {
|
||||
|
||||
return std::visit(
|
||||
[this](auto &&arg) -> const std::string_view {
|
||||
|
@ -27,47 +27,47 @@ const std::string_view Error::message() const {
|
|||
return "Error in class Error. Good luck :)";
|
||||
}
|
||||
},
|
||||
error_message);
|
||||
error_message_);
|
||||
}
|
||||
|
||||
bool Error::failed() const {
|
||||
return static_cast<std::underlying_type_t<Error::Code>>(error_) != 0;
|
||||
bool error::failed() const {
|
||||
return static_cast<std::underlying_type_t<error::code>>(error_) != 0;
|
||||
}
|
||||
|
||||
bool Error::isCritical() const {
|
||||
return static_cast<std::underlying_type_t<Error::Code>>(error_) < 0;
|
||||
bool error::is_critical() const {
|
||||
return static_cast<std::underlying_type_t<error::code>>(error_) < 0;
|
||||
}
|
||||
|
||||
bool Error::isRecoverable() const {
|
||||
return static_cast<std::underlying_type_t<Error::Code>>(error_) > 0;
|
||||
bool error::is_recoverable() const {
|
||||
return static_cast<std::underlying_type_t<error::code>>(error_) > 0;
|
||||
}
|
||||
|
||||
Error Error::copyError() const {
|
||||
Error error;
|
||||
error error::copy_error() const {
|
||||
error error;
|
||||
error.error_ = error_;
|
||||
try {
|
||||
error.error_message = error_message;
|
||||
error.error_message_ = error_message_;
|
||||
} catch (const std::bad_alloc &) {
|
||||
error.error_message =
|
||||
error.error_message_ =
|
||||
std::string_view{"Error while copying Error string. Out of memory"};
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
Error::Code Error::code() const { return static_cast<Error::Code>(error_); }
|
||||
error::code error::id() const { return static_cast<error::code>(error_); }
|
||||
|
||||
Error makeError(const std::string_view &generic, Error::Code code) {
|
||||
return Error{generic, code};
|
||||
error make_error(const std::string_view &generic, error::code code) {
|
||||
return error{generic, code};
|
||||
}
|
||||
|
||||
Error criticalError(const std::string_view &generic, Error::Code c) {
|
||||
return makeError(generic, c);
|
||||
error critical_error(const std::string_view &generic, error::code c) {
|
||||
return make_error(generic, c);
|
||||
}
|
||||
|
||||
Error recoverableError(const std::string_view &generic, Error::Code c) {
|
||||
return makeError(generic, c);
|
||||
error recoverable_error(const std::string_view &generic, error::code c) {
|
||||
return make_error(generic, c);
|
||||
}
|
||||
|
||||
Error noError() { return Error{}; }
|
||||
error no_error() { return error{}; }
|
||||
|
||||
} // namespace saw
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
namespace saw {
|
||||
|
@ -12,9 +14,9 @@ namespace saw {
|
|||
* critical and recoverable errors. Additional code ids can be provided to the
|
||||
* constructor if additional distinctions are necessary.
|
||||
*/
|
||||
class Error {
|
||||
class error {
|
||||
public:
|
||||
enum class Code : int16_t {
|
||||
enum class code : int16_t {
|
||||
GenericCritical = -1,
|
||||
GenericRecoverable = 1,
|
||||
Disconnected = -99,
|
||||
|
@ -22,120 +24,126 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
std::variant<std::string_view, std::string> error_message;
|
||||
Code error_;
|
||||
std::variant<std::string_view, std::string> error_message_;
|
||||
code error_;
|
||||
|
||||
public:
|
||||
Error();
|
||||
Error(const std::string_view &msg, Error::Code code);
|
||||
Error(std::string &&msg, Error::Code code);
|
||||
Error(Error &&error);
|
||||
error();
|
||||
error(const std::string_view &msg, error::code id);
|
||||
error(std::string &&msg, error::code id);
|
||||
error(error &&error);
|
||||
|
||||
SAW_FORBID_COPY(Error);
|
||||
SAW_FORBID_COPY(error);
|
||||
|
||||
Error &operator=(Error &&) = default;
|
||||
error &operator=(error &&) = default;
|
||||
|
||||
const std::string_view message() const;
|
||||
bool failed() const;
|
||||
|
||||
bool isCritical() const;
|
||||
bool isRecoverable() const;
|
||||
bool is_critical() const;
|
||||
bool is_recoverable() const;
|
||||
|
||||
Error copyError() const;
|
||||
error copy_error() const;
|
||||
|
||||
Code code() const;
|
||||
code id() const;
|
||||
};
|
||||
|
||||
Error makeError(const std::string_view &generic, Error::Code c);
|
||||
error make_error(const std::string_view &generic, error::code c);
|
||||
|
||||
template <typename Formatter>
|
||||
Error makeError(const Formatter &formatter, Error::Code code,
|
||||
const std::string_view &generic) {
|
||||
error make_error(const Formatter &formatter, error::code code,
|
||||
const std::string_view &generic) {
|
||||
try {
|
||||
std::string error_msg = formatter();
|
||||
return Error{std::move(error_msg), code};
|
||||
return error{std::move(error_msg), code};
|
||||
} catch (std::bad_alloc &) {
|
||||
return Error{generic, code};
|
||||
return error{generic, code};
|
||||
}
|
||||
}
|
||||
|
||||
Error criticalError(const std::string_view &generic,
|
||||
Error::Code c = Error::Code::GenericCritical);
|
||||
error critical_error(const std::string_view &generic,
|
||||
error::code c = error::code::GenericCritical);
|
||||
|
||||
template <typename Formatter>
|
||||
Error criticalError(const Formatter &formatter, const std::string_view &generic,
|
||||
Error::Code c = Error::Code::GenericCritical) {
|
||||
return makeError(formatter, c, generic);
|
||||
error critical_error(const Formatter &formatter,
|
||||
const std::string_view &generic,
|
||||
error::code c = error::code::GenericCritical) {
|
||||
return make_error(formatter, c, generic);
|
||||
}
|
||||
|
||||
Error recoverableError(const std::string_view &generic,
|
||||
Error::Code c = Error::Code::GenericRecoverable);
|
||||
error recoverable_error(const std::string_view &generic,
|
||||
error::code c = error::code::GenericRecoverable);
|
||||
|
||||
template <typename Formatter>
|
||||
Error recoverableError(const Formatter &formatter,
|
||||
const std::string_view &generic,
|
||||
Error::Code c = Error::Code::GenericRecoverable) {
|
||||
return makeError(formatter, c, generic);
|
||||
error recoverable_error(const Formatter &formatter,
|
||||
const std::string_view &generic,
|
||||
error::code c = error::code::GenericRecoverable) {
|
||||
return make_error(formatter, c, generic);
|
||||
}
|
||||
|
||||
Error noError();
|
||||
error no_error();
|
||||
|
||||
/**
|
||||
* Exception alternative. Since I code without exceptions this class is
|
||||
* essentially a kind of exception replacement.
|
||||
*/
|
||||
template <typename T> class ErrorOr;
|
||||
template <typename T> class error_or;
|
||||
|
||||
class ErrorOrValue {
|
||||
class error_or_value {
|
||||
public:
|
||||
virtual ~ErrorOrValue() = default;
|
||||
virtual ~error_or_value() = default;
|
||||
|
||||
template <typename T> ErrorOr<UnfixVoid<T>> &as() {
|
||||
return static_cast<ErrorOr<UnfixVoid<T>> &>(*this);
|
||||
template <typename T> error_or<unfix_void<T>> &as() {
|
||||
return static_cast<error_or<unfix_void<T>> &>(*this);
|
||||
}
|
||||
|
||||
template <typename T> const ErrorOr<UnfixVoid<T>> &as() const {
|
||||
return static_cast<const ErrorOr<UnfixVoid<T>> &>(*this);
|
||||
template <typename T> const error_or<unfix_void<T>> &as() const {
|
||||
return static_cast<const error_or<unfix_void<T>> &>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> class ErrorOr final : public ErrorOrValue {
|
||||
template <typename T> class error_or final : public error_or_value {
|
||||
private:
|
||||
std::variant<Error, FixVoid<T>> value_or_error;
|
||||
std::variant<error, fix_void<T>> value_or_error_;
|
||||
|
||||
static_assert(!std::is_same_v<T, Void>, "Don't use internal private types");
|
||||
static_assert(!std::is_same_v<T, void_t>,
|
||||
"Don't use internal private types");
|
||||
|
||||
public:
|
||||
ErrorOr() = default;
|
||||
ErrorOr(const FixVoid<T> &value) : value_or_error{value} {}
|
||||
error_or() = default;
|
||||
error_or(const fix_void<T> &value) : value_or_error_{value} {}
|
||||
|
||||
ErrorOr(FixVoid<T> &&value) : value_or_error{std::move(value)} {}
|
||||
error_or(fix_void<T> &&value) : value_or_error_{std::move(value)} {}
|
||||
|
||||
ErrorOr(const Error &error) : value_or_error{error} {}
|
||||
ErrorOr(Error &&error) : value_or_error{std::move(error)} {}
|
||||
error_or(const error &error) : value_or_error_{error} {}
|
||||
error_or(error &&error) : value_or_error_{std::move(error)} {}
|
||||
|
||||
bool isValue() const {
|
||||
return std::holds_alternative<FixVoid<T>>(value_or_error);
|
||||
bool is_value() const {
|
||||
return std::holds_alternative<fix_void<T>>(value_or_error_);
|
||||
}
|
||||
|
||||
bool isError() const {
|
||||
return std::holds_alternative<Error>(value_or_error);
|
||||
bool is_error() const {
|
||||
return std::holds_alternative<class error>(value_or_error_);
|
||||
}
|
||||
|
||||
Error &error() { return std::get<Error>(value_or_error); }
|
||||
class error &error() {
|
||||
return std::get<class error>(value_or_error_);
|
||||
}
|
||||
|
||||
const Error &error() const { return std::get<Error>(value_or_error); }
|
||||
const class error &error() const {
|
||||
return std::get<class error>(value_or_error_);
|
||||
}
|
||||
|
||||
FixVoid<T> &value() { return std::get<FixVoid<T>>(value_or_error); }
|
||||
fix_void<T> &value() { return std::get<fix_void<T>>(value_or_error_); }
|
||||
|
||||
const FixVoid<T> &value() const {
|
||||
return std::get<FixVoid<T>>(value_or_error);
|
||||
const fix_void<T> &value() const {
|
||||
return std::get<fix_void<T>>(value_or_error_);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> class ErrorOr<ErrorOr<T>> {
|
||||
template <typename T> class error_or<error_or<T>> {
|
||||
private:
|
||||
ErrorOr() = delete;
|
||||
error_or() = delete;
|
||||
};
|
||||
|
||||
} // namespace saw
|
||||
|
|
|
@ -4,68 +4,67 @@
|
|||
|
||||
namespace saw {
|
||||
|
||||
AsyncIoStream::AsyncIoStream(Own<IoStream> str)
|
||||
: stream{std::move(str)}, read_ready{stream->readReady()
|
||||
.then([this]() {
|
||||
read_stepper.readStep(*stream);
|
||||
})
|
||||
.sink()},
|
||||
write_ready{stream->writeReady()
|
||||
.then([this]() { write_stepper.writeStep(*stream); })
|
||||
async_io_stream::async_io_stream(own<io_stream> str)
|
||||
: stream_{std::move(str)},
|
||||
read_ready_{stream_->read_ready()
|
||||
.then([this]() { read_stepper_.read_step(*stream_); })
|
||||
.sink()},
|
||||
read_disconnected{stream->onReadDisconnected()
|
||||
.then([this]() {
|
||||
if (read_stepper.on_read_disconnect) {
|
||||
read_stepper.on_read_disconnect->feed();
|
||||
}
|
||||
})
|
||||
.sink()} {}
|
||||
write_ready_{stream_->write_ready()
|
||||
.then([this]() { write_stepper_.write_step(*stream_); })
|
||||
.sink()},
|
||||
read_disconnected_{stream_->on_read_disconnected()
|
||||
.then([this]() {
|
||||
if (read_stepper_.on_read_disconnect) {
|
||||
read_stepper_.on_read_disconnect->feed();
|
||||
}
|
||||
})
|
||||
.sink()} {}
|
||||
|
||||
void AsyncIoStream::read(void *buffer, size_t min_length, size_t max_length) {
|
||||
void async_io_stream::read(void *buffer, size_t min_length, size_t max_length) {
|
||||
SAW_ASSERT(buffer && max_length >= min_length && min_length > 0) { return; }
|
||||
|
||||
SAW_ASSERT(!read_stepper.read_task.has_value()) { return; }
|
||||
SAW_ASSERT(!read_stepper_.read_task.has_value()) { return; }
|
||||
|
||||
read_stepper.read_task =
|
||||
ReadTaskAndStepHelper::ReadIoTask{buffer, min_length, max_length, 0};
|
||||
read_stepper.readStep(*stream);
|
||||
read_stepper_.read_task = read_task_and_step_helper::read_io_task{
|
||||
buffer, min_length, max_length, 0};
|
||||
read_stepper_.read_step(*stream_);
|
||||
}
|
||||
|
||||
Conveyor<size_t> AsyncIoStream::readDone() {
|
||||
auto caf = newConveyorAndFeeder<size_t>();
|
||||
read_stepper.read_done = std::move(caf.feeder);
|
||||
conveyor<size_t> async_io_stream::read_done() {
|
||||
auto caf = new_conveyor_and_feeder<size_t>();
|
||||
read_stepper_.read_done = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
Conveyor<void> AsyncIoStream::onReadDisconnected() {
|
||||
auto caf = newConveyorAndFeeder<void>();
|
||||
read_stepper.on_read_disconnect = std::move(caf.feeder);
|
||||
conveyor<void> async_io_stream::on_read_disconnected() {
|
||||
auto caf = new_conveyor_and_feeder<void>();
|
||||
read_stepper_.on_read_disconnect = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
void AsyncIoStream::write(const void *buffer, size_t length) {
|
||||
void async_io_stream::write(const void *buffer, size_t length) {
|
||||
SAW_ASSERT(buffer && length > 0) { return; }
|
||||
|
||||
SAW_ASSERT(!write_stepper.write_task.has_value()) { return; }
|
||||
SAW_ASSERT(!write_stepper_.write_task.has_value()) { return; }
|
||||
|
||||
write_stepper.write_task =
|
||||
WriteTaskAndStepHelper::WriteIoTask{buffer, length, 0};
|
||||
write_stepper.writeStep(*stream);
|
||||
write_stepper_.write_task =
|
||||
write_task_and_step_helper::write_io_task{buffer, length, 0};
|
||||
write_stepper_.write_step(*stream_);
|
||||
}
|
||||
|
||||
Conveyor<size_t> AsyncIoStream::writeDone() {
|
||||
auto caf = newConveyorAndFeeder<size_t>();
|
||||
write_stepper.write_done = std::move(caf.feeder);
|
||||
conveyor<size_t> async_io_stream::write_done() {
|
||||
auto caf = new_conveyor_and_feeder<size_t>();
|
||||
write_stepper_.write_done = std::move(caf.feeder);
|
||||
return std::move(caf.conveyor);
|
||||
}
|
||||
|
||||
StringNetworkAddress::StringNetworkAddress(const std::string &address,
|
||||
uint16_t port)
|
||||
: address_value{address}, port_value{port} {}
|
||||
string_network_address::string_network_address(const std::string &address,
|
||||
uint16_t port)
|
||||
: address_value_{address}, port_value_{port} {}
|
||||
|
||||
const std::string &StringNetworkAddress::address() const {
|
||||
return address_value;
|
||||
const std::string &string_network_address::address() const {
|
||||
return address_value_;
|
||||
}
|
||||
|
||||
uint16_t StringNetworkAddress::port() const { return port_value; }
|
||||
uint16_t string_network_address::port() const { return port_value_; }
|
||||
} // namespace saw
|
||||
|
|
|
@ -11,186 +11,195 @@ namespace saw {
|
|||
/*
|
||||
* Input stream
|
||||
*/
|
||||
class InputStream {
|
||||
class input_stream {
|
||||
public:
|
||||
virtual ~InputStream() = default;
|
||||
virtual ~input_stream() = default;
|
||||
|
||||
virtual ErrorOr<size_t> read(void *buffer, size_t length) = 0;
|
||||
virtual error_or<size_t> read(void *buffer, size_t length) = 0;
|
||||
|
||||
virtual Conveyor<void> readReady() = 0;
|
||||
virtual conveyor<void> read_ready() = 0;
|
||||
|
||||
virtual Conveyor<void> onReadDisconnected() = 0;
|
||||
virtual conveyor<void> on_read_disconnected() = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Output stream
|
||||
*/
|
||||
class OutputStream {
|
||||
class output_stream {
|
||||
public:
|
||||
virtual ~OutputStream() = default;
|
||||
virtual ~output_stream() = default;
|
||||
|
||||
virtual ErrorOr<size_t> write(const void *buffer, size_t length) = 0;
|
||||
virtual error_or<size_t> write(const void *buffer, size_t length) = 0;
|
||||
|
||||
virtual Conveyor<void> writeReady() = 0;
|
||||
virtual conveyor<void> write_ready() = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Io stream
|
||||
*/
|
||||
class IoStream : public InputStream, public OutputStream {
|
||||
class io_stream : public input_stream, public output_stream {
|
||||
public:
|
||||
virtual ~IoStream() = default;
|
||||
virtual ~io_stream() = default;
|
||||
};
|
||||
|
||||
class AsyncInputStream {
|
||||
class async_input_stream {
|
||||
public:
|
||||
virtual ~AsyncInputStream() = default;
|
||||
virtual ~async_input_stream() = default;
|
||||
|
||||
virtual void read(void *buffer, size_t min_length, size_t max_length) = 0;
|
||||
|
||||
virtual Conveyor<size_t> readDone() = 0;
|
||||
virtual Conveyor<void> onReadDisconnected() = 0;
|
||||
virtual conveyor<size_t> read_done() = 0;
|
||||
virtual conveyor<void> on_read_disconnected() = 0;
|
||||
};
|
||||
|
||||
class AsyncOutputStream {
|
||||
class async_output_stream {
|
||||
public:
|
||||
virtual ~AsyncOutputStream() = default;
|
||||
virtual ~async_output_stream() = default;
|
||||
|
||||
virtual void write(const void *buffer, size_t length) = 0;
|
||||
|
||||
virtual Conveyor<size_t> writeDone() = 0;
|
||||
virtual conveyor<size_t> write_done() = 0;
|
||||
};
|
||||
|
||||
class AsyncIoStream final : public AsyncInputStream, public AsyncOutputStream {
|
||||
class async_io_stream final : public async_input_stream,
|
||||
public async_output_stream {
|
||||
private:
|
||||
Own<IoStream> stream;
|
||||
own<io_stream> stream_;
|
||||
|
||||
SinkConveyor read_ready;
|
||||
SinkConveyor write_ready;
|
||||
SinkConveyor read_disconnected;
|
||||
conveyor_sink read_ready_;
|
||||
conveyor_sink write_ready_;
|
||||
conveyor_sink read_disconnected_;
|
||||
|
||||
ReadTaskAndStepHelper read_stepper;
|
||||
WriteTaskAndStepHelper write_stepper;
|
||||
read_task_and_step_helper read_stepper_;
|
||||
write_task_and_step_helper write_stepper_;
|
||||
|
||||
public:
|
||||
AsyncIoStream(Own<IoStream> str);
|
||||
async_io_stream(own<io_stream> str);
|
||||
|
||||
SAW_FORBID_COPY(AsyncIoStream);
|
||||
SAW_FORBID_MOVE(AsyncIoStream);
|
||||
SAW_FORBID_COPY(async_io_stream);
|
||||
SAW_FORBID_MOVE(async_io_stream);
|
||||
|
||||
void read(void *buffer, size_t length, size_t max_length) override;
|
||||
|
||||
Conveyor<size_t> readDone() override;
|
||||
conveyor<size_t> read_done() override;
|
||||
|
||||
Conveyor<void> onReadDisconnected() override;
|
||||
conveyor<void> on_read_disconnected() override;
|
||||
|
||||
void write(const void *buffer, size_t length) override;
|
||||
|
||||
Conveyor<size_t> writeDone() override;
|
||||
conveyor<size_t> write_done() override;
|
||||
};
|
||||
|
||||
class Server {
|
||||
class server {
|
||||
public:
|
||||
virtual ~Server() = default;
|
||||
virtual ~server() = default;
|
||||
|
||||
virtual Conveyor<Own<IoStream>> accept() = 0;
|
||||
virtual conveyor<own<io_stream>> accept() = 0;
|
||||
};
|
||||
|
||||
class NetworkAddress;
|
||||
class network_address;
|
||||
/**
|
||||
* Datagram class. Bound to a local address it is able to receive inbound
|
||||
* datagram messages and send them as well as long as an address is provided as
|
||||
* well
|
||||
*/
|
||||
class Datagram {
|
||||
class datagram {
|
||||
public:
|
||||
virtual ~Datagram() = default;
|
||||
virtual ~datagram() = default;
|
||||
|
||||
virtual ErrorOr<size_t> read(void *buffer, size_t length) = 0;
|
||||
virtual Conveyor<void> readReady() = 0;
|
||||
virtual error_or<size_t> read(void *buffer, size_t length) = 0;
|
||||
virtual conveyor<void> read_ready() = 0;
|
||||
|
||||
virtual ErrorOr<size_t> write(const void *buffer, size_t length,
|
||||
NetworkAddress &dest) = 0;
|
||||
virtual Conveyor<void> writeReady() = 0;
|
||||
virtual error_or<size_t> write(const void *buffer, size_t length,
|
||||
network_address &dest) = 0;
|
||||
virtual conveyor<void> write_ready() = 0;
|
||||
};
|
||||
|
||||
class OsNetworkAddress;
|
||||
class StringNetworkAddress;
|
||||
class os_network_address;
|
||||
class string_network_address;
|
||||
|
||||
class NetworkAddress {
|
||||
class network_address {
|
||||
public:
|
||||
using ChildVariant =
|
||||
std::variant<OsNetworkAddress *, StringNetworkAddress *>;
|
||||
using child_variant =
|
||||
std::variant<os_network_address *, string_network_address *>;
|
||||
|
||||
virtual ~NetworkAddress() = default;
|
||||
virtual ~network_address() = default;
|
||||
|
||||
virtual NetworkAddress::ChildVariant representation() = 0;
|
||||
virtual network_address::child_variant representation() = 0;
|
||||
|
||||
virtual const std::string &address() const = 0;
|
||||
virtual uint16_t port() const = 0;
|
||||
};
|
||||
|
||||
class OsNetworkAddress : public NetworkAddress {
|
||||
class os_network_address : public network_address {
|
||||
public:
|
||||
virtual ~OsNetworkAddress() = default;
|
||||
virtual ~os_network_address() = default;
|
||||
|
||||
NetworkAddress::ChildVariant representation() override { return this; }
|
||||
network_address::child_variant representation() override { return this; }
|
||||
};
|
||||
|
||||
class StringNetworkAddress final : public NetworkAddress {
|
||||
class string_network_address final : public network_address {
|
||||
private:
|
||||
std::string address_value;
|
||||
uint16_t port_value;
|
||||
std::string address_value_;
|
||||
uint16_t port_value_;
|
||||
|
||||
public:
|
||||
StringNetworkAddress(const std::string &address, uint16_t port);
|
||||
string_network_address(const std::string &address, uint16_t port);
|
||||
|
||||
const std::string &address() const override;
|
||||
uint16_t port() const override;
|
||||
|
||||
NetworkAddress::ChildVariant representation() override { return this; }
|
||||
network_address::child_variant representation() override { return this; }
|
||||
};
|
||||
|
||||
class Network {
|
||||
class network {
|
||||
public:
|
||||
virtual ~Network() = default;
|
||||
virtual ~network() = default;
|
||||
|
||||
/**
|
||||
* Resolve the provided string and uint16 to the preferred storage method
|
||||
*/
|
||||
virtual conveyor<own<network_address>>
|
||||
resolve_address(const std::string &addr, uint16_t port_hint = 0) = 0;
|
||||
|
||||
/**
|
||||
* Parse the provided string and uint16 to the preferred storage method
|
||||
* Since no dns request is made here, no async conveyors have to be used.
|
||||
*/
|
||||
virtual Conveyor<Own<NetworkAddress>>
|
||||
parseAddress(const std::string &addr, uint16_t port_hint = 0) = 0;
|
||||
/// @todo implement
|
||||
// virtual Own<NetworkAddress> parseAddress(const std::string& addr,
|
||||
// uint16_t port_hint = 0) = 0;
|
||||
|
||||
/**
|
||||
* Set up a listener on this address
|
||||
*/
|
||||
virtual Own<Server> listen(NetworkAddress &bind_addr) = 0;
|
||||
virtual own<server> listen(network_address &bind_addr) = 0;
|
||||
|
||||
/**
|
||||
* Connect to a remote address
|
||||
*/
|
||||
virtual Conveyor<Own<IoStream>> connect(NetworkAddress &address) = 0;
|
||||
virtual conveyor<own<io_stream>> connect(network_address &address) = 0;
|
||||
|
||||
/**
|
||||
* Bind a datagram socket at this address.
|
||||
*/
|
||||
virtual Own<Datagram> datagram(NetworkAddress &address) = 0;
|
||||
virtual own<datagram> datagram(network_address &address) = 0;
|
||||
};
|
||||
|
||||
class IoProvider {
|
||||
class io_provider {
|
||||
public:
|
||||
virtual ~IoProvider() = default;
|
||||
virtual ~io_provider() = default;
|
||||
|
||||
virtual Own<InputStream> wrapInputFd(int fd) = 0;
|
||||
virtual own<input_stream> wrap_input_fd(int fd) = 0;
|
||||
|
||||
virtual Network &network() = 0;
|
||||
virtual network &network() = 0;
|
||||
};
|
||||
|
||||
struct AsyncIoContext {
|
||||
Own<IoProvider> io;
|
||||
EventLoop &event_loop;
|
||||
EventPort &event_port;
|
||||
struct async_io_context {
|
||||
own<io_provider> io;
|
||||
event_loop &event_loop;
|
||||
event_port &event_port;
|
||||
};
|
||||
|
||||
ErrorOr<AsyncIoContext> setupAsyncIo();
|
||||
error_or<async_io_context> setup_async_io();
|
||||
} // namespace saw
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#include "io_auth.h"
|
||||
|
||||
namespace saw {
|
||||
peer::peer(const std::string &identity_) : identity_value_{identity_} {}
|
||||
peer::peer(std::string &&identity_) : identity_value_{std::move(identity_)} {}
|
||||
|
||||
const std::string &peer::identity() const { return identity_value_; }
|
||||
} // namespace saw
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include "io.h"
|
||||
|
||||
namespace saw {
|
||||
class peer {
|
||||
public:
|
||||
peer(const std::string &ident);
|
||||
peer(std::string &&ident);
|
||||
|
||||
const std::string &identity() const;
|
||||
|
||||
private:
|
||||
std::string identity_value_;
|
||||
};
|
||||
|
||||
class authenticated_io_stream {
|
||||
public:
|
||||
// This is the easiest way to implement Authenticated streams.
|
||||
// This is a simple pair of the stream and the peer.
|
||||
|
||||
own<io_stream> stream;
|
||||
maybe<own<peer>> peer;
|
||||
};
|
||||
|
||||
class authenticated_server {
|
||||
public:
|
||||
virtual ~authenticated_server() = default;
|
||||
|
||||
virtual conveyor<authenticated_io_stream> accept() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Authenticated Network class which provides a peer identity when connecting
|
||||
*/
|
||||
class authenticated_network {
|
||||
public:
|
||||
virtual ~authenticated_network() = default;
|
||||
|
||||
/**
|
||||
* Connects to the provided address.
|
||||
* Returns as soon as it is authenticated or fails
|
||||
*/
|
||||
virtual conveyor<authenticated_io_stream>
|
||||
connect(network_address &address) = 0;
|
||||
|
||||
/**
|
||||
* Creates a server listening for connections
|
||||
*/
|
||||
virtual own<authenticated_server> listen() = 0;
|
||||
};
|
||||
|
||||
} // namespace saw
|
|
@ -1,26 +1,26 @@
|
|||
#include "io_helpers.h"
|
||||
#include "io_helpers.h"
|
||||
|
||||
#include "io.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace saw {
|
||||
void ReadTaskAndStepHelper::readStep(InputStream &reader) {
|
||||
void read_task_and_step_helper::read_step(input_stream &reader) {
|
||||
while (read_task.has_value()) {
|
||||
ReadIoTask &task = *read_task;
|
||||
read_io_task &task = *read_task;
|
||||
|
||||
ErrorOr<size_t> n_err = reader.read(task.buffer, task.max_length);
|
||||
if (n_err.isError()) {
|
||||
const Error &error = n_err.error();
|
||||
if (error.isCritical()) {
|
||||
error_or<size_t> n_err = reader.read(task.buffer, task.max_length);
|
||||
if (n_err.is_error()) {
|
||||
const error &error = n_err.error();
|
||||
if (error.is_critical()) {
|
||||
if (read_done) {
|
||||
read_done->fail(error.copyError());
|
||||
read_done->fail(error.copy_error());
|
||||
}
|
||||
read_task = std::nullopt;
|
||||
}
|
||||
|
||||
break;
|
||||
} else if (n_err.isValue()) {
|
||||
} else if (n_err.is_value()) {
|
||||
size_t n = n_err.value();
|
||||
if (static_cast<size_t>(n) >= task.min_length &&
|
||||
static_cast<size_t>(n) <= task.max_length) {
|
||||
|
@ -37,20 +37,20 @@ void ReadTaskAndStepHelper::readStep(InputStream &reader) {
|
|||
|
||||
} else {
|
||||
if (read_done) {
|
||||
read_done->fail(criticalError("Read failed"));
|
||||
read_done->fail(critical_error("Read failed"));
|
||||
}
|
||||
read_task = std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteTaskAndStepHelper::writeStep(OutputStream &writer) {
|
||||
void write_task_and_step_helper::write_step(output_stream &writer) {
|
||||
while (write_task.has_value()) {
|
||||
WriteIoTask &task = *write_task;
|
||||
write_io_task &task = *write_task;
|
||||
|
||||
ErrorOr<size_t> n_err = writer.write(task.buffer, task.length);
|
||||
error_or<size_t> n_err = writer.write(task.buffer, task.length);
|
||||
|
||||
if (n_err.isValue()) {
|
||||
if (n_err.is_value()) {
|
||||
|
||||
size_t n = n_err.value();
|
||||
assert(n <= task.length);
|
||||
|
@ -64,22 +64,22 @@ void WriteTaskAndStepHelper::writeStep(OutputStream &writer) {
|
|||
task.length -= n;
|
||||
task.already_written += n;
|
||||
}
|
||||
} else if (n_err.isError()) {
|
||||
const Error &error = n_err.error();
|
||||
if (error.isCritical()) {
|
||||
} else if (n_err.is_error()) {
|
||||
const error &error = n_err.error();
|
||||
if (error.is_critical()) {
|
||||
if (write_done) {
|
||||
write_done->fail(error.copyError());
|
||||
write_done->fail(error.copy_error());
|
||||
}
|
||||
write_task = std::nullopt;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (write_done) {
|
||||
write_done->fail(criticalError("Write failed"));
|
||||
write_done->fail(critical_error("Write failed"));
|
||||
}
|
||||
write_task = std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace saw
|
||||
} // namespace saw
|
||||
|
|
|
@ -16,38 +16,38 @@ namespace saw {
|
|||
* and gnutls doesn't let me write or read into buffers I have to have this kind
|
||||
* of strange abstraction. This may also be reusable for windows/macOS though.
|
||||
*/
|
||||
class InputStream;
|
||||
class input_stream;
|
||||
|
||||
class ReadTaskAndStepHelper {
|
||||
class read_task_and_step_helper {
|
||||
public:
|
||||
struct ReadIoTask {
|
||||
struct read_io_task {
|
||||
void *buffer;
|
||||
size_t min_length;
|
||||
size_t max_length;
|
||||
size_t already_read = 0;
|
||||
};
|
||||
std::optional<ReadIoTask> read_task;
|
||||
Own<ConveyorFeeder<size_t>> read_done = nullptr;
|
||||
std::optional<read_io_task> read_task;
|
||||
own<conveyor_feeder<size_t>> read_done = nullptr;
|
||||
|
||||
Own<ConveyorFeeder<void>> on_read_disconnect = nullptr;
|
||||
own<conveyor_feeder<void>> on_read_disconnect = nullptr;
|
||||
|
||||
public:
|
||||
void readStep(InputStream &reader);
|
||||
void read_step(input_stream &reader);
|
||||
};
|
||||
|
||||
class OutputStream;
|
||||
class output_stream;
|
||||
|
||||
class WriteTaskAndStepHelper {
|
||||
class write_task_and_step_helper {
|
||||
public:
|
||||
struct WriteIoTask {
|
||||
struct write_io_task {
|
||||
const void *buffer;
|
||||
size_t length;
|
||||
size_t already_written = 0;
|
||||
};
|
||||
std::optional<WriteIoTask> write_task;
|
||||
Own<ConveyorFeeder<size_t>> write_done = nullptr;
|
||||
std::optional<write_io_task> write_task;
|
||||
own<conveyor_feeder<size_t>> write_done = nullptr;
|
||||
|
||||
public:
|
||||
void writeStep(OutputStream &writer);
|
||||
void write_step(output_stream &writer);
|
||||
};
|
||||
} // namespace saw
|
||||
|
|
|
@ -1,41 +1,103 @@
|
|||
#pragma once
|
||||
|
||||
#include "async.h"
|
||||
#include "message.h"
|
||||
#include "buffer.h"
|
||||
#include "io.h"
|
||||
#include "message.h"
|
||||
|
||||
namespace saw {
|
||||
|
||||
template <typename Codec, typename Incoming, typename Outgoing, typename InContainer = MessageContainer<Incoming>, typename OutContainer = MessageContainer<Outgoing>, typename BufferT = RingBuffer>
|
||||
class StreamingIoPeer {
|
||||
private:
|
||||
Own<ConveyorFeeder<HeapMessageRoot<Incoming, InContainer>>> incoming_feeder = nullptr;
|
||||
|
||||
Own<AsyncIoStream> io_stream;
|
||||
|
||||
Codec codec;
|
||||
|
||||
BufferT in_buffer;
|
||||
BufferT out_buffer;
|
||||
|
||||
SinkConveyor sink_read;
|
||||
SinkConveyor sink_write;
|
||||
|
||||
template <typename Codec, typename Incoming, typename Outgoing,
|
||||
typename InContainer = message_container<Incoming>,
|
||||
typename OutContainer = message_container<Outgoing>,
|
||||
typename BufferT = ring_buffer>
|
||||
class streaming_io_peer {
|
||||
public:
|
||||
StreamingIoPeer(Own<ConveyorFeeder<HeapMessageRoot<Incoming, InContainer>>> feed, Own<AsyncIoStream> stream, Codec codec, BufferT in, BufferT out);
|
||||
StreamingIoPeer(Own<ConveyorFeeder<HeapMessageRoot<Incoming, InContainer>>> feed, Own<AsyncIoStream> stream);
|
||||
/**
|
||||
*
|
||||
*/
|
||||
streaming_io_peer(
|
||||
own<conveyor_feeder<heap_message_root<Incoming, InContainer>>> feed,
|
||||
own<async_io_stream> stream, Codec codec, BufferT in, BufferT out);
|
||||
/**
|
||||
*
|
||||
*/
|
||||
streaming_io_peer(
|
||||
own<conveyor_feeder<heap_message_root<Incoming, InContainer>>> feed,
|
||||
own<async_io_stream> stream);
|
||||
|
||||
void send(HeapMessageRoot<Outgoing, OutContainer> builder);
|
||||
/**
|
||||
* Deleted copy and move constructors
|
||||
*/
|
||||
SAW_FORBID_COPY(streaming_io_peer);
|
||||
SAW_FORBID_MOVE(streaming_io_peer);
|
||||
|
||||
Conveyor<void> onReadDisconnected();
|
||||
/**
|
||||
* Send a message to the remote peer
|
||||
*/
|
||||
error send(heap_message_root<Outgoing, OutContainer> builder);
|
||||
|
||||
/**
|
||||
* A phantom conveyor feeder. Meant for interfacing with other components
|
||||
*/
|
||||
// conveyor_feeder<heap_message_root<Outgoing, OutContainer>> &feeder();
|
||||
|
||||
conveyor<void> on_read_disconnected();
|
||||
|
||||
private:
|
||||
/// @unimplemented
|
||||
class peer_conveyor_feeder final
|
||||
: public conveyor_feeder<heap_message_root<Outgoing, OutContainer>> {
|
||||
public:
|
||||
peer_conveyor_feeder(
|
||||
streaming_io_peer<Codec, Incoming, Outgoing, InContainer,
|
||||
OutContainer, BufferT> &peer_)
|
||||
: peer_{peer_} {}
|
||||
|
||||
void feed(heap_message_root<Outgoing, OutContainer> &&data) override {
|
||||
(void)data;
|
||||
}
|
||||
|
||||
void fail(error &&error) override { (void)error; }
|
||||
|
||||
size_t space() const override { return 0; }
|
||||
|
||||
size_t queued() const override { return 0; }
|
||||
|
||||
private:
|
||||
streaming_io_peer<Codec, Incoming, Outgoing, InContainer, OutContainer,
|
||||
BufferT> &peer_;
|
||||
};
|
||||
|
||||
private:
|
||||
own<conveyor_feeder<heap_message_root<Incoming, InContainer>>>
|
||||
incoming_feeder_ = nullptr;
|
||||
|
||||
own<async_io_stream> io_stream_;
|
||||
|
||||
Codec codec_;
|
||||
|
||||
BufferT in_buffer_;
|
||||
BufferT out_buffer_;
|
||||
|
||||
conveyor_sink sink_read_;
|
||||
conveyor_sink sink_write_;
|
||||
|
||||
peer_conveyor_feeder conveyor_feeder_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup new streaming io peer with the provided network protocols.
|
||||
* This is a convenience wrapper intended for a faster setup of
|
||||
* This is a convenience wrapper intended for a faster setup of this class
|
||||
*/
|
||||
template <typename Codec, typename Incoming, typename Outgoing, typename InContainer = MessageContainer<Incoming>, typename OutContainer = MessageContainer<Outgoing>, typename BufferT = RingBuffer>
|
||||
std::pair<StreamingIoPeer<Codec, Incoming, Outgoing, InContainer, OutContainer, BufferT>, Conveyor<HeapMessageRoot<Incoming, InContainer>>> newStreamingIoPeer(Own<AsyncIoStream> stream);
|
||||
template <typename Codec, typename Incoming, typename Outgoing,
|
||||
typename InContainer = message_container<Incoming>,
|
||||
typename OutContainer = message_container<Outgoing>,
|
||||
typename BufferT = ring_buffer>
|
||||
std::pair<own<streaming_io_peer<Codec, Incoming, Outgoing, InContainer,
|
||||
OutContainer, BufferT>>,
|
||||
conveyor<heap_message_root<Incoming, InContainer>>>
|
||||
new_streaming_io_peer(own<async_io_stream> stream);
|
||||
|
||||
} // namespace saw
|
||||
|
||||
|
|
|
@ -1,96 +1,117 @@
|
|||
namespace saw {
|
||||
|
||||
template <typename Codec, typename Incoming, typename Outgoing, typename InContainer = MessageContainer<Incoming>, typename OutContainer = MessageContainer<Outgoing>, typename BufferT = RingBuffer>
|
||||
StreamingIoPeer<Codec, Incoming, Outgoing, InContainer, OutContainer, BufferT>::StreamingIoPeer(
|
||||
Own<ConveyorFeeder<HeapMessageRoot<Incoming, InContainer>>> feed,
|
||||
Own<AsyncIoStream> str
|
||||
):
|
||||
StreamingIoPeer{std::move(feed), std::move(str), {}, {}, {}}
|
||||
{
|
||||
template <typename Codec, typename Incoming, typename Outgoing,
|
||||
typename InContainer, typename OutContainer, typename BufferT>
|
||||
streaming_io_peer<Codec, Incoming, Outgoing, InContainer, OutContainer,
|
||||
BufferT>::
|
||||
streaming_io_peer(
|
||||
own<conveyor_feeder<heap_message_root<Incoming, InContainer>>> feed,
|
||||
own<async_io_stream> str)
|
||||
: streaming_io_peer{std::move(feed), std::move(str), {}, {}, {}} {}
|
||||
|
||||
template <typename Codec, typename Incoming, typename Outgoing,
|
||||
typename InContainer, typename OutContainer, typename BufferT>
|
||||
streaming_io_peer<Codec, Incoming, Outgoing, InContainer, OutContainer,
|
||||
BufferT>::
|
||||
streaming_io_peer(
|
||||
own<conveyor_feeder<heap_message_root<Incoming, InContainer>>> feed,
|
||||
own<async_io_stream> stream, Codec codec, BufferT in, BufferT out)
|
||||
: incoming_feeder_{std::move(feed)},
|
||||
io_stream_{std::move(stream)}, codec_{std::move(codec)},
|
||||
in_buffer_{std::move(in)}, out_buffer_{std::move(out)},
|
||||
sink_read_{
|
||||
io_stream_->read_done()
|
||||
.then([this](size_t bytes) -> error_or<void> {
|
||||
in_buffer_.write_advance(bytes);
|
||||
|
||||
if (in_buffer_.write_segment_length() == 0) {
|
||||
return critical_error("Message too long");
|
||||
}
|
||||
|
||||
io_stream_->read(&in_buffer_.write(), 1,
|
||||
in_buffer_.write_segment_length());
|
||||
|
||||
while (true) {
|
||||
auto root = heap_message_root<Incoming, InContainer>();
|
||||
auto builder = root.build();
|
||||
|
||||
error err = codec_.template decode<Incoming, InContainer>(
|
||||
builder, in_buffer_);
|
||||
if (err.is_critical()) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!err.failed()) {
|
||||
incoming_feeder_->feed(std::move(root));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return void_t{};
|
||||
})
|
||||
.sink([this](error err) {
|
||||
incoming_feeder_->fail(err.copy_error());
|
||||
|
||||
return err;
|
||||
})},
|
||||
sink_write_{io_stream_->write_done()
|
||||
.then([this](size_t bytes) -> error_or<void> {
|
||||
out_buffer_.read_advance(bytes);
|
||||
if (out_buffer_.readCompositeLength() > 0) {
|
||||
io_stream_->write(
|
||||
&out_buffer_.read(),
|
||||
out_buffer_.read_segment_length());
|
||||
}
|
||||
|
||||
return void_t{};
|
||||
})
|
||||
.sink()} {
|
||||
io_stream_->read(&in_buffer_.write(), 1, in_buffer_.write_segment_length());
|
||||
}
|
||||
|
||||
template <typename Codec, typename Incoming, typename Outgoing, typename InContainer = MessageContainer<Incoming>, typename OutContainer = MessageContainer<Outgoing>, typename BufferT = RingBuffer>
|
||||
StreamingIoPeer<Codec, Incoming, Outgoing, InContainer, OutContainer, BufferT>::StreamingIoPeer(
|
||||
Own<ConveyorFeeder<HeapMessageRoot<Incoming, InContainer>>> feed,
|
||||
Own<AsyncIoStream> str, Codec codec, BufferT in, BufferT out):
|
||||
incoming_feeder{std::move(feed)},
|
||||
io_stream{std::move(str)},
|
||||
codec{std::move(codec)},
|
||||
in_buffer{std::move(in)},
|
||||
out_buffer{std::move(out)},
|
||||
sink_read{
|
||||
io_stream->readDone().then([this](size_t bytes) -> ErrorOr<void> {
|
||||
in_buffer.writeAdvance(bytes);
|
||||
template <typename Codec, typename Incoming, typename Outgoing,
|
||||
typename InContainer, typename OutContainer, typename BufferT>
|
||||
error streaming_io_peer<Codec, Incoming, Outgoing, InContainer, OutContainer,
|
||||
BufferT>::send(heap_message_root<Outgoing, OutContainer>
|
||||
msg) {
|
||||
bool restart_write = out_buffer_.read_segment_length() == 0;
|
||||
|
||||
if(in_buffer.writeSegmentLength() == 0){
|
||||
return criticalError("Message too long");
|
||||
}
|
||||
|
||||
io_stream->read(&in_buffer.write(), 1, in_buffer.writeSegmentLength());
|
||||
|
||||
while(true){
|
||||
auto root = heapMessageRoot<Incoming, InContainer>();
|
||||
auto builder = root.build();
|
||||
|
||||
Error error = codec.template decode<Incoming, InContainer>(builder, in_buffer);
|
||||
if(error.isCritical()){
|
||||
return error;
|
||||
}
|
||||
|
||||
if(!error.failed()){
|
||||
incoming_feeder->handle(std::move(root));
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Void{};
|
||||
}).sink([this](Error error){
|
||||
incoming_feeder->fail(error);
|
||||
|
||||
return error;
|
||||
})
|
||||
},
|
||||
sink_write{
|
||||
io_stream->writeDone().then([this](size_bytes) -> ErrorOr<void> {
|
||||
out_buffer.readAdvance(bytes);
|
||||
if(out_buffer.readCompositeLength() > 0){
|
||||
io_stream->write(&out_buffer.read(), out_buffer.readSegmengtLength());
|
||||
}
|
||||
|
||||
return Void{};
|
||||
}).sink();
|
||||
}
|
||||
{
|
||||
io_stream->read(&in_buffer.write(), 1, in_buffer.writeSegmentLength());
|
||||
}
|
||||
|
||||
template <typename Codec, typename Incoming, typename Outgoing, typename InContainer = MessageContainer<Incoming>, typename OutContainer = MessageContainer<Outgoing>, typename BufferT = RingBuffer>
|
||||
void StreamingIoPeer<Codec, Incoming, Outgoing, InContainer, OutContainer, BufferT>::send(HeapMessageRoot<Outgoing, OutContainer> msg){
|
||||
bool restart_write = out_buffer.readSegmentLength() == 0;
|
||||
|
||||
Error error = codec.template encode<Outgoing, OutContainer>(msg.read(), out_buffer);
|
||||
if(error.failed()){
|
||||
return error;
|
||||
error err =
|
||||
codec_.template encode<Outgoing, OutContainer>(msg.read(), out_buffer_);
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if(restart_write){
|
||||
io_stream->write(&out_buffer.read(), out_buffer.readSegmentLength());
|
||||
if (restart_write) {
|
||||
io_stream_->write(&out_buffer_.read(),
|
||||
out_buffer_.read_segment_length());
|
||||
}
|
||||
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
|
||||
template <typename Codec, typename Incoming, typename Outgoing, typename InContainer = MessageContainer<Incoming>, typename OutContainer = MessageContainer<Outgoing>, typename BufferT = RingBuffer>
|
||||
Conveyor<void> StreamingIoPeer<Codec, Incoming, Outgoing, InContainer, OutContainer, BufferT>::onReadDisconnected(){
|
||||
return io_stream->onReadDisconnected();
|
||||
template <typename Codec, typename Incoming, typename Outgoing,
|
||||
typename InContainer, typename OutContainer, typename BufferT>
|
||||
conveyor<void>
|
||||
streaming_io_peer<Codec, Incoming, Outgoing, InContainer, OutContainer,
|
||||
BufferT>::on_read_disconnected() {
|
||||
return io_stream_->on_read_disconnected();
|
||||
}
|
||||
|
||||
template <typename Codec, typename Incoming, typename Outgoing, typename InContainer = MessageContainer<Incoming>, typename OutContainer = MessageContainer<Outgoing>, typename BufferT = RingBuffer>
|
||||
std::pair<StreamingIoPeer<Codec, Incoming, Outgoing, InContainer, OutContainer, BufferT>, Conveyor<HeapMessageRoot<Incoming, InContainer>>> newStreamingIoPeer(Own<AsyncIoStream> stream){
|
||||
auto caf = newConveyorAndFeeder<HeapMessageRoot<Incoming, InContainer>>();
|
||||
template <typename Codec, typename Incoming, typename Outgoing,
|
||||
typename InContainer, typename OutContainer, typename BufferT>
|
||||
std::pair<own<streaming_io_peer<Codec, Incoming, Outgoing, InContainer,
|
||||
OutContainer, BufferT>>,
|
||||
conveyor<heap_message_root<Incoming, InContainer>>>
|
||||
newstreaming_io_peer(own<async_io_stream> stream) {
|
||||
auto caf =
|
||||
new_conveyor_and_feeder<heap_message_root<Incoming, InContainer>>();
|
||||
|
||||
return {{std::move(caf.feeder), std::move(stream)}, std::move(caf.conveyor)};
|
||||
return {heap<streaming_io_peer<Codec, Incoming, Outgoing, InContainer,
|
||||
OutContainer, BufferT>>(
|
||||
std::move(caf.feeder), std::move(stream)),
|
||||
std::move(caf.conveyor)};
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace saw
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "log.h"
|
||||
|
||||
namespace saw {
|
||||
LogIo::LogIo(EventLoop &loop) : loop{loop} {}
|
||||
log_io::log_io(event_loop &loop) : loop_{loop} {}
|
||||
|
||||
Log::Log(LogIo ¢ral, EventLoop &loop) : central{central}, loop{loop} {}
|
||||
} // namespace saw
|
||||
log::log(log_io ¢ral, event_loop &loop) : central_{central}, loop_{loop} {}
|
||||
} // namespace saw
|
||||
|
|
|
@ -3,24 +3,24 @@
|
|||
#include "common.h"
|
||||
|
||||
namespace saw {
|
||||
class EventLoop;
|
||||
class LogIo;
|
||||
class Log {
|
||||
class event_loop;
|
||||
class log_io;
|
||||
class log {
|
||||
public:
|
||||
enum class Type : uint8_t { Info, Warning, Error, Debug };
|
||||
|
||||
private:
|
||||
LogIo ¢ral;
|
||||
EventLoop &loop;
|
||||
log_io ¢ral_;
|
||||
event_loop &loop_;
|
||||
|
||||
public:
|
||||
Log(LogIo ¢ral, EventLoop &loop);
|
||||
log(log_io ¢ral, event_loop &loop);
|
||||
};
|
||||
class LogIo {
|
||||
class log_io {
|
||||
private:
|
||||
EventLoop &loop;
|
||||
event_loop &loop_;
|
||||
|
||||
public:
|
||||
LogIo(EventLoop &loop);
|
||||
log_io(event_loop &loop);
|
||||
};
|
||||
} // namespace saw
|
||||
} // namespace saw
|
||||
|
|
|
@ -15,18 +15,18 @@
|
|||
#include "string_literal.h"
|
||||
|
||||
namespace saw {
|
||||
class MessageBase {
|
||||
class message_base {
|
||||
protected:
|
||||
bool set_explicitly = false;
|
||||
bool set_explicitly_ = false;
|
||||
|
||||
public:
|
||||
template <class T> T &as() {
|
||||
static_assert(std::is_base_of<MessageBase, T>());
|
||||
static_assert(std::is_base_of<message_base, T>());
|
||||
return dynamic_cast<T &>(*this);
|
||||
}
|
||||
|
||||
template <class T> const T &as() const {
|
||||
static_assert(std::is_base_of<MessageBase, T>());
|
||||
static_assert(std::is_base_of<message_base, T>());
|
||||
return dynamic_cast<const T &>(*this);
|
||||
}
|
||||
};
|
||||
|
@ -44,184 +44,184 @@ public:
|
|||
/*
|
||||
* Struct Message
|
||||
*/
|
||||
template <class... V, StringLiteral... Keys, class Container>
|
||||
class Message<schema::Struct<schema::NamedMember<V, Keys>...>, Container> final
|
||||
: public MessageBase {
|
||||
template <class... V, string_literal... Keys, class Container>
|
||||
class message<schema::Struct<schema::NamedMember<V, Keys>...>, Container> final
|
||||
: public message_base {
|
||||
private:
|
||||
using SchemaType = schema::Struct<schema::NamedMember<V, Keys>...>;
|
||||
using MessageType = Message<SchemaType, Container>;
|
||||
Container container;
|
||||
using MessageType = message<SchemaType, Container>;
|
||||
Container container_;
|
||||
|
||||
static_assert(std::is_same_v<SchemaType, typename Container::SchemaType>,
|
||||
"Container should have same the schema as Message");
|
||||
|
||||
friend class Builder;
|
||||
friend class Reader;
|
||||
friend class builder;
|
||||
friend class reader;
|
||||
|
||||
public:
|
||||
class Reader;
|
||||
class Builder {
|
||||
class reader;
|
||||
class builder {
|
||||
private:
|
||||
MessageType &message;
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Builder(MessageType &msg) : message{msg} {}
|
||||
builder(MessageType &msg) : message_{msg} {}
|
||||
|
||||
Reader asReader() { return Reader{message}; }
|
||||
reader as_reader() { return reader{message_}; }
|
||||
|
||||
/*
|
||||
* Initialize a member by index
|
||||
*/
|
||||
template <size_t i>
|
||||
typename std::enable_if<
|
||||
!SchemaIsArray<
|
||||
typename MessageParameterPackType<i, V...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::Builder>::type
|
||||
!schema_is_array<
|
||||
typename message_parameter_pack_type<i, V...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::builder>::type
|
||||
init() {
|
||||
return typename Container::template ElementType<i>::Builder{
|
||||
message.container.template get<i>()};
|
||||
return typename Container::template ElementType<i>::builder{
|
||||
message_.container_.template get<i>()};
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a member by their name
|
||||
* This is the preferred method for schema::Struct messages
|
||||
*/
|
||||
template <StringLiteral Literal>
|
||||
template <string_literal Literal>
|
||||
typename std::enable_if<
|
||||
!SchemaIsArray<typename MessageParameterPackType<
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value,
|
||||
!schema_is_array<typename message_parameter_pack_type<
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value,
|
||||
V...>::Type>::Value,
|
||||
typename Container::template ElementType<
|
||||
MessageParameterKeyPackIndex<Literal,
|
||||
Keys...>::Value>::Builder>::type
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value>::
|
||||
builder>::type
|
||||
init() {
|
||||
constexpr size_t i =
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value;
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value;
|
||||
|
||||
return init<i>();
|
||||
}
|
||||
|
||||
template <size_t i>
|
||||
typename std::enable_if<
|
||||
SchemaIsArray<
|
||||
typename MessageParameterPackType<i, V...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::Builder>::type
|
||||
schema_is_array<
|
||||
typename message_parameter_pack_type<i, V...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::builder>::type
|
||||
init(size_t size = 0) {
|
||||
auto array_builder =
|
||||
typename Container::template ElementType<i>::Builder{
|
||||
message.container.template get<i>(), size};
|
||||
typename Container::template ElementType<i>::builder{
|
||||
message_.container_.template get<i>(), size};
|
||||
return array_builder;
|
||||
}
|
||||
|
||||
/*
|
||||
* Version for array schema type
|
||||
*/
|
||||
template <StringLiteral Literal>
|
||||
template <string_literal Literal>
|
||||
typename std::enable_if<
|
||||
SchemaIsArray<typename MessageParameterPackType<
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value,
|
||||
schema_is_array<typename message_parameter_pack_type<
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value,
|
||||
V...>::Type>::Value,
|
||||
typename Container::template ElementType<
|
||||
MessageParameterKeyPackIndex<Literal,
|
||||
Keys...>::Value>::Builder>::type
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value>::
|
||||
builder>::type
|
||||
init(size_t size) {
|
||||
constexpr size_t i =
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value;
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value;
|
||||
|
||||
return init<i>(size);
|
||||
}
|
||||
};
|
||||
|
||||
class Reader {
|
||||
class reader {
|
||||
private:
|
||||
MessageType &message;
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Reader(MessageType &msg) : message{msg} {}
|
||||
reader(MessageType &msg) : message_{msg} {}
|
||||
|
||||
Builder asBuilder() { return Builder{message}; }
|
||||
builder as_builder() { return builder{message_}; }
|
||||
|
||||
/*
|
||||
* Get member by index
|
||||
*/
|
||||
template <size_t i>
|
||||
typename Container::template ElementType<i>::Reader get() {
|
||||
return typename Container::template ElementType<i>::Reader{
|
||||
message.container.template get<i>()};
|
||||
typename Container::template ElementType<i>::reader get() {
|
||||
return typename Container::template ElementType<i>::reader{
|
||||
message_.container_.template get<i>()};
|
||||
}
|
||||
|
||||
/*
|
||||
* Get member by name
|
||||
* This is the preferred method for schema::Struct messages
|
||||
*/
|
||||
template <StringLiteral Literal>
|
||||
template <string_literal Literal>
|
||||
typename Container::template ElementType<
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value>::Reader
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value>::reader
|
||||
get() {
|
||||
// The index of the first match
|
||||
constexpr size_t i =
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value;
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value;
|
||||
|
||||
return get<i>();
|
||||
}
|
||||
};
|
||||
|
||||
Builder build() { return Builder{*this}; }
|
||||
builder build() { return builder{*this}; }
|
||||
|
||||
Reader read() { return Reader{*this}; }
|
||||
reader read() { return reader{*this}; }
|
||||
};
|
||||
|
||||
/*
|
||||
* Union message class. Wrapper object
|
||||
*/
|
||||
template <class... V, StringLiteral... Keys, class Container>
|
||||
class Message<schema::Union<schema::NamedMember<V, Keys>...>, Container> final
|
||||
: public MessageBase {
|
||||
template <class... V, string_literal... Keys, class Container>
|
||||
class message<schema::Union<schema::NamedMember<V, Keys>...>, Container> final
|
||||
: public message_base {
|
||||
private:
|
||||
using SchemaType = schema::Union<schema::NamedMember<V, Keys>...>;
|
||||
using MessageType = Message<SchemaType, Container>;
|
||||
using MessageType = message<SchemaType, Container>;
|
||||
|
||||
Container container;
|
||||
Container container_;
|
||||
|
||||
static_assert(std::is_same_v<SchemaType, typename Container::SchemaType>,
|
||||
"Container should have same the schema as Message");
|
||||
|
||||
friend class Builder;
|
||||
friend class Reader;
|
||||
friend class builder;
|
||||
friend class reader;
|
||||
|
||||
public:
|
||||
class Reader;
|
||||
class Builder {
|
||||
class reader;
|
||||
class builder {
|
||||
private:
|
||||
MessageType &message;
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Builder(MessageType &msg) : message{msg} {}
|
||||
builder(MessageType &msg) : message_{msg} {}
|
||||
|
||||
Reader asReader() { return Reader{message}; }
|
||||
reader as_reader() { return reader{message_}; }
|
||||
|
||||
template <size_t i>
|
||||
typename std::enable_if<
|
||||
!SchemaIsArray<
|
||||
typename MessageParameterPackType<i, V...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::Builder>::type
|
||||
!schema_is_array<
|
||||
typename message_parameter_pack_type<i, V...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::builder>::type
|
||||
|
||||
init() {
|
||||
return typename Container::template ElementType<i>::Builder{
|
||||
message.container.template get<i>()};
|
||||
return typename Container::template ElementType<i>::builder{
|
||||
message_.container_.template get<i>()};
|
||||
}
|
||||
|
||||
template <StringLiteral Literal>
|
||||
template <string_literal Literal>
|
||||
typename std::enable_if<
|
||||
!SchemaIsArray<typename MessageParameterPackType<
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value,
|
||||
!schema_is_array<typename message_parameter_pack_type<
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value,
|
||||
V...>::Type>::Value,
|
||||
typename Container::template ElementType<
|
||||
MessageParameterKeyPackIndex<Literal,
|
||||
Keys...>::Value>::Builder>::type
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value>::
|
||||
builder>::type
|
||||
init() {
|
||||
constexpr size_t i =
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value;
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value;
|
||||
|
||||
return init<i>();
|
||||
}
|
||||
|
@ -231,65 +231,65 @@ public:
|
|||
*/
|
||||
template <size_t i>
|
||||
typename std::enable_if<
|
||||
SchemaIsArray<
|
||||
typename MessageParameterPackType<i, V...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::Builder>::type
|
||||
schema_is_array<
|
||||
typename message_parameter_pack_type<i, V...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::builder>::type
|
||||
init(size_t size = 0) {
|
||||
return typename Container::template ElementType<i>::Builder{
|
||||
message.container.template get<i>(), size};
|
||||
return typename Container::template ElementType<i>::builder{
|
||||
message_.container_.template get<i>(), size};
|
||||
}
|
||||
|
||||
template <StringLiteral Literal>
|
||||
template <string_literal Literal>
|
||||
typename std::enable_if<
|
||||
SchemaIsArray<typename MessageParameterPackType<
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value,
|
||||
schema_is_array<typename message_parameter_pack_type<
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value,
|
||||
V...>::Type>::Value,
|
||||
typename Container::template ElementType<
|
||||
MessageParameterKeyPackIndex<Literal,
|
||||
Keys...>::Value>::Builder>::type
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value>::
|
||||
builder>::type
|
||||
init(size_t size) {
|
||||
constexpr size_t i =
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value;
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value;
|
||||
|
||||
return init<i>(size);
|
||||
}
|
||||
};
|
||||
|
||||
class Reader {
|
||||
class reader {
|
||||
private:
|
||||
MessageType &message;
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Reader(MessageType &msg) : message{msg} {}
|
||||
reader(MessageType &msg) : message_{msg} {}
|
||||
|
||||
Builder asBuilder() { return Builder{message}; }
|
||||
builder as_builder() { return builder{message_}; }
|
||||
|
||||
template <size_t i>
|
||||
typename Container::template ElementType<i>::Reader get() {
|
||||
return typename Container::template ElementType<i>::Reader{
|
||||
message.container.template get<i>()};
|
||||
typename Container::template ElementType<i>::reader get() {
|
||||
return typename Container::template ElementType<i>::reader{
|
||||
message_.container_.template get<i>()};
|
||||
}
|
||||
|
||||
template <StringLiteral Literal>
|
||||
template <string_literal Literal>
|
||||
typename Container::template ElementType<
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value>::Reader
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value>::reader
|
||||
get() {
|
||||
// The index of the first match
|
||||
constexpr size_t i =
|
||||
MessageParameterKeyPackIndex<Literal, Keys...>::Value;
|
||||
message_parameter_key_pack_index<Literal, Keys...>::Value;
|
||||
|
||||
return get<i>();
|
||||
}
|
||||
|
||||
template <StringLiteral Literal>
|
||||
constexpr size_t toIndex() const noexcept {
|
||||
return MessageParameterKeyPackIndex<Literal, Keys...>::Value;
|
||||
template <string_literal Literal>
|
||||
static constexpr size_t to_index() noexcept {
|
||||
return message_parameter_key_pack_index<Literal, Keys...>::Value;
|
||||
}
|
||||
|
||||
size_t index() const noexcept { return message.container.index(); }
|
||||
size_t index() const noexcept { return message_.container_.index(); }
|
||||
|
||||
template <StringLiteral Literal> bool hasAlternative() const {
|
||||
return index() == toIndex<Literal>();
|
||||
template <string_literal Literal> bool has_alternative() const {
|
||||
return index() == to_index<Literal>();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -298,59 +298,59 @@ public:
|
|||
* Array message class. Wrapper around an array schema element
|
||||
*/
|
||||
template <class T, class Container>
|
||||
class Message<schema::Array<T>, Container> final : public MessageBase {
|
||||
class message<schema::Array<T>, Container> final : public message_base {
|
||||
private:
|
||||
using SchemaType = schema::Array<T>;
|
||||
using MessageType = Message<SchemaType, Container>;
|
||||
using MessageType = message<SchemaType, Container>;
|
||||
|
||||
Container container;
|
||||
Container container_;
|
||||
|
||||
static_assert(std::is_same_v<SchemaType, typename Container::SchemaType>,
|
||||
"Container should have same Schema as Message");
|
||||
|
||||
friend class Builder;
|
||||
friend class Reader;
|
||||
friend class builder;
|
||||
friend class reader;
|
||||
|
||||
public:
|
||||
class Reader;
|
||||
class Builder {
|
||||
class reader;
|
||||
class builder {
|
||||
private:
|
||||
MessageType &message;
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Builder(MessageType &msg, size_t size) : message{msg} {
|
||||
builder(MessageType &msg, size_t size) : message_{msg} {
|
||||
if (size > 0) {
|
||||
message.container.resize(size);
|
||||
message_.container_.resize(size);
|
||||
}
|
||||
}
|
||||
|
||||
Reader asReader() { return Reader{message}; }
|
||||
reader as_reader() { return reader{message_}; }
|
||||
|
||||
typename Container::ElementType::Builder init(size_t i) {
|
||||
return typename Container::ElementType::Builder{
|
||||
message.container.get(i)};
|
||||
typename Container::ElementType::builder init(size_t i) {
|
||||
return typename Container::ElementType::builder{
|
||||
message_.container_.get(i)};
|
||||
}
|
||||
|
||||
size_t size() const { return message.container.size(); }
|
||||
size_t size() const { return message_.container_.size(); }
|
||||
|
||||
void resize(size_t size) { message.container.resize(size); }
|
||||
void resize(size_t size) { message_.container_.resize(size); }
|
||||
};
|
||||
|
||||
class Reader {
|
||||
class reader {
|
||||
private:
|
||||
MessageType &message;
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Reader(MessageType &msg) : message{msg} {}
|
||||
reader(MessageType &msg) : message_{msg} {}
|
||||
|
||||
Builder asBuilder() { return Builder{message, 0}; }
|
||||
builder as_builder() { return builder{message_, 0}; }
|
||||
|
||||
typename Container::ElementType::Reader get(size_t i) {
|
||||
return typename Container::ElementType::Reader{
|
||||
message.container.get(i)};
|
||||
typename Container::ElementType::reader get(size_t i) {
|
||||
return typename Container::ElementType::reader{
|
||||
message_.container_.get(i)};
|
||||
}
|
||||
|
||||
size_t size() const { return message.container.size(); }
|
||||
size_t size() const { return message_.container_.size(); }
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -358,62 +358,62 @@ public:
|
|||
* Tuple message class. Wrapper around a tuple schema
|
||||
*/
|
||||
template <class... T, class Container>
|
||||
class Message<schema::Tuple<T...>, Container> final : public MessageBase {
|
||||
class message<schema::Tuple<T...>, Container> final : public message_base {
|
||||
private:
|
||||
using SchemaType = schema::Tuple<T...>;
|
||||
using MessageType = Message<SchemaType, Container>;
|
||||
using MessageType = message<SchemaType, Container>;
|
||||
|
||||
Container container;
|
||||
Container container_;
|
||||
|
||||
static_assert(std::is_same_v<SchemaType, typename Container::SchemaType>,
|
||||
"Container should have same the schema as Message");
|
||||
|
||||
friend class Builder;
|
||||
friend class Reader;
|
||||
friend class builder;
|
||||
friend class reader;
|
||||
|
||||
public:
|
||||
class Reader;
|
||||
class Builder {
|
||||
MessageType &message;
|
||||
class reader;
|
||||
class builder {
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Builder(MessageType &msg) : message{msg} {}
|
||||
builder(MessageType &msg) : message_{msg} {}
|
||||
|
||||
Reader asReader() { return Reader{message}; }
|
||||
reader as_reader() { return reader{message_}; }
|
||||
|
||||
template <size_t i>
|
||||
typename std::enable_if<
|
||||
!SchemaIsArray<
|
||||
typename MessageParameterPackType<i, T...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::Builder>::type
|
||||
!schema_is_array<
|
||||
typename message_parameter_pack_type<i, T...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::builder>::type
|
||||
init() {
|
||||
return typename Container::template ElementType<i>::Builder{
|
||||
message.container.template get<i>()};
|
||||
return typename Container::template ElementType<i>::builder{
|
||||
message_.container_.template get<i>()};
|
||||
}
|
||||
|
||||
template <size_t i>
|
||||
typename std::enable_if<
|
||||
SchemaIsArray<
|
||||
typename MessageParameterPackType<i, T...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::Builder>::type
|
||||
schema_is_array<
|
||||
typename message_parameter_pack_type<i, T...>::Type>::Value,
|
||||
typename Container::template ElementType<i>::builder>::type
|
||||
init(size_t size = 0) {
|
||||
return typename Container::template ElementType<i>::Builder{
|
||||
message.container.template get<i>(), size};
|
||||
return typename Container::template ElementType<i>::builder{
|
||||
message_.container_.template get<i>(), size};
|
||||
}
|
||||
};
|
||||
class Reader {
|
||||
class reader {
|
||||
private:
|
||||
MessageType &message;
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Reader(MessageType &msg) : message{msg} {}
|
||||
reader(MessageType &msg) : message_{msg} {}
|
||||
|
||||
Builder asBuilder() { return Builder{message}; }
|
||||
builder as_builder() { return builder{message_}; }
|
||||
|
||||
template <size_t i>
|
||||
typename Container::template ElementType<i>::Reader get() {
|
||||
return typename Container::template ElementType<i>::Reader{
|
||||
message.container.template get<i>()};
|
||||
typename Container::template ElementType<i>::reader get() {
|
||||
return typename Container::template ElementType<i>::reader{
|
||||
message_.container_.template get<i>()};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -423,140 +423,142 @@ public:
|
|||
* int16_t, int32_t, int64_t) message class
|
||||
*/
|
||||
template <class T, size_t N, class Container>
|
||||
class Message<schema::Primitive<T, N>, Container> final : public MessageBase {
|
||||
class message<schema::Primitive<T, N>, Container> final : public message_base {
|
||||
private:
|
||||
using SchemaType = schema::Primitive<T, N>;
|
||||
using MessageType = Message<SchemaType, Container>;
|
||||
using MessageType = message<SchemaType, Container>;
|
||||
|
||||
Container container;
|
||||
Container container_;
|
||||
|
||||
static_assert(std::is_same_v<SchemaType, typename Container::SchemaType>,
|
||||
"Container should have same the schema as Message");
|
||||
|
||||
friend class Builder;
|
||||
friend class Reader;
|
||||
friend class builder;
|
||||
friend class reader;
|
||||
|
||||
public:
|
||||
class Reader;
|
||||
class Builder {
|
||||
class reader;
|
||||
class builder {
|
||||
private:
|
||||
MessageType &message;
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Builder(MessageType &msg) : message{msg} {}
|
||||
builder(MessageType &msg) : message_{msg} {}
|
||||
|
||||
Reader asReader() { return Reader{message}; }
|
||||
reader as_reader() { return reader{message_}; }
|
||||
|
||||
void set(const typename Container::ValueType &value) {
|
||||
message.container.set(value);
|
||||
message_.container_.set(value);
|
||||
}
|
||||
};
|
||||
|
||||
class Reader {
|
||||
class reader {
|
||||
private:
|
||||
MessageType &message;
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Reader(Message &msg) : message{msg} {}
|
||||
reader(message &msg) : message_{msg} {}
|
||||
|
||||
Builder asBuilder() { return Builder{message}; }
|
||||
builder as_builder() { return builder{message_}; }
|
||||
|
||||
const typename Container::ValueType &get() const {
|
||||
return message.container.get();
|
||||
return message_.container_.get();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
class Message<schema::String, Container> final : public MessageBase {
|
||||
class message<schema::String, Container> final : public message_base {
|
||||
private:
|
||||
using SchemaType = schema::String;
|
||||
using MessageType = Message<SchemaType, Container>;
|
||||
using MessageType = message<SchemaType, Container>;
|
||||
|
||||
Container container;
|
||||
Container container_;
|
||||
|
||||
static_assert(std::is_same_v<SchemaType, typename Container::SchemaType>,
|
||||
"Container should have same the schema as Message");
|
||||
|
||||
friend class Builder;
|
||||
friend class Reader;
|
||||
friend class builder;
|
||||
friend class reader;
|
||||
|
||||
public:
|
||||
class Reader;
|
||||
class Builder {
|
||||
class reader;
|
||||
class builder {
|
||||
private:
|
||||
MessageType &message;
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Builder(MessageType &msg) : message{msg} {}
|
||||
builder(MessageType &msg) : message_{msg} {}
|
||||
|
||||
Reader asReader() { return Reader{message}; }
|
||||
reader as_reader() { return reader{message_}; }
|
||||
|
||||
void set(std::string &&str) { message.container.set(std::move(str)); }
|
||||
void set(const std::string_view str) { message.container.set(str); }
|
||||
void set(std::string &&str) { message_.container_.set(std::move(str)); }
|
||||
void set(const std::string_view str) { message_.container_.set(str); }
|
||||
void set(const char *str) { set(std::string_view{str}); }
|
||||
};
|
||||
|
||||
class Reader {
|
||||
class reader {
|
||||
private:
|
||||
MessageType &message;
|
||||
MessageType &message_;
|
||||
|
||||
public:
|
||||
Reader(MessageType &msg) : message{msg} {}
|
||||
reader(MessageType &msg) : message_{msg} {}
|
||||
|
||||
Builder asBuilder() { return Builder{message}; }
|
||||
builder as_builder() { return builder{message_}; }
|
||||
|
||||
const std::string_view get() const { return message.container.get(); }
|
||||
const std::string_view get() const { return message_.container_.get(); }
|
||||
};
|
||||
};
|
||||
|
||||
template <class Schema, class Container = MessageContainer<Schema>>
|
||||
class HeapMessageRoot {
|
||||
template <class Schema, class Container = message_container<Schema>>
|
||||
class heap_message_root {
|
||||
private:
|
||||
Own<Message<Schema, Container>> root;
|
||||
own<message<Schema, Container>> root_;
|
||||
|
||||
public:
|
||||
HeapMessageRoot(Own<Message<Schema, Container>> r) : root{std::move(r)} {}
|
||||
heap_message_root(own<message<Schema, Container>> r)
|
||||
: root_{std::move(r)} {}
|
||||
|
||||
typename Message<Schema, Container>::Builder build() {
|
||||
assert(root);
|
||||
return typename Message<Schema, Container>::Builder{*root};
|
||||
typename message<Schema, Container>::builder build() {
|
||||
assert(root_);
|
||||
return typename message<Schema, Container>::builder{*root_};
|
||||
}
|
||||
|
||||
typename Message<Schema, Container>::Reader read() {
|
||||
assert(root);
|
||||
return typename Message<Schema, Container>::Reader{*root};
|
||||
typename message<Schema, Container>::reader read() {
|
||||
assert(root_);
|
||||
return typename message<Schema, Container>::reader{*root_};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class Container>
|
||||
class HeapMessageRoot<schema::Array<T>, Container> {
|
||||
class heap_message_root<schema::Array<T>, Container> {
|
||||
public:
|
||||
using Schema = schema::Array<T>;
|
||||
|
||||
private:
|
||||
Own<Message<Schema, Container>> root;
|
||||
own<message<Schema, Container>> root_;
|
||||
|
||||
public:
|
||||
HeapMessageRoot(Own<Message<Schema, Container>> r) : root{std::move(r)} {}
|
||||
heap_message_root(own<message<Schema, Container>> r)
|
||||
: root_{std::move(r)} {}
|
||||
|
||||
typename Message<Schema, Container>::Builder build(size_t size) {
|
||||
assert(root);
|
||||
return typename Message<Schema, Container>::Builder{*root, size};
|
||||
typename message<Schema, Container>::builder build(size_t size) {
|
||||
assert(root_);
|
||||
return typename message<Schema, Container>::builder{*root_, size};
|
||||
}
|
||||
|
||||
typename Message<Schema, Container>::Reader read() {
|
||||
assert(root);
|
||||
return typename Message<Schema, Container>::Reader{*root};
|
||||
typename message<Schema, Container>::reader read() {
|
||||
assert(root_);
|
||||
return typename message<Schema, Container>::reader{*root_};
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Minor helper for creating a message root
|
||||
*/
|
||||
template <class Schema, class Container = MessageContainer<Schema>>
|
||||
inline HeapMessageRoot<Schema, Container> heapMessageRoot() {
|
||||
Own<Message<Schema, Container>> root = heap<Message<Schema, Container>>();
|
||||
return HeapMessageRoot<Schema, Container>{std::move(root)};
|
||||
template <class Schema, class Container = message_container<Schema>>
|
||||
inline heap_message_root<Schema, Container> new_heap_message_root() {
|
||||
own<message<Schema, Container>> root = heap<message<Schema, Container>>();
|
||||
return heap_message_root<Schema, Container>{std::move(root)};
|
||||
}
|
||||
} // namespace saw
|
||||
|
|
|
@ -2,21 +2,25 @@
|
|||
|
||||
#include "schema.h"
|
||||
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace saw {
|
||||
template <class T> class MessageContainer;
|
||||
template <class T> class message_container;
|
||||
|
||||
template <class T, class Container = MessageContainer<T>> class Message;
|
||||
template <class T, class Container = message_container<T>> class message;
|
||||
|
||||
template <size_t N, class... T> struct MessageParameterPackType;
|
||||
template <size_t N, class... T> struct message_parameter_pack_type;
|
||||
|
||||
template <class TN, class... T> struct MessageParameterPackType<0, TN, T...> {
|
||||
template <class TN, class... T>
|
||||
struct message_parameter_pack_type<0, TN, T...> {
|
||||
using Type = TN;
|
||||
};
|
||||
|
||||
template <size_t N, class TN, class... T>
|
||||
struct MessageParameterPackType<N, TN, T...> {
|
||||
struct message_parameter_pack_type<N, TN, T...> {
|
||||
static_assert(sizeof...(T) > 0, "Exhausted parameters");
|
||||
using Type = typename MessageParameterPackType<N - 1, T...>::Type;
|
||||
using Type = typename message_parameter_pack_type<N - 1, T...>::Type;
|
||||
};
|
||||
|
||||
template <class T, class... TL> struct MessageParameterPackIndex;
|
||||
|
@ -36,117 +40,118 @@ struct MessageParameterPackIndex<T, TL0, TL...> {
|
|||
* StringLiterals cannot be resolved as non-type primitive template values can.
|
||||
* This is the workaround
|
||||
*/
|
||||
template <StringLiteral V, StringLiteral Key0, StringLiteral... Keys>
|
||||
struct MessageParameterKeyPackIndexHelper {
|
||||
template <string_literal V, string_literal Key0, string_literal... Keys>
|
||||
struct message_parameter_key_pack_index_helper {
|
||||
static constexpr size_t Value =
|
||||
(V == Key0)
|
||||
? (0u)
|
||||
: (1u + MessageParameterKeyPackIndexHelper<V, Keys...>::Value);
|
||||
: (1u + message_parameter_key_pack_index_helper<V, Keys...>::Value);
|
||||
};
|
||||
|
||||
template <StringLiteral V, StringLiteral Key0>
|
||||
struct MessageParameterKeyPackIndexHelper<V, Key0> {
|
||||
template <string_literal V, string_literal Key0>
|
||||
struct message_parameter_key_pack_index_helper<V, Key0> {
|
||||
static constexpr size_t Value = (V == Key0) ? (0u) : (1u);
|
||||
};
|
||||
|
||||
template <StringLiteral V, StringLiteral... Keys>
|
||||
struct MessageParameterKeyPackIndex {
|
||||
template <string_literal V, string_literal... Keys>
|
||||
struct message_parameter_key_pack_index {
|
||||
static constexpr size_t Value =
|
||||
MessageParameterKeyPackIndexHelper<V, Keys...>::Value;
|
||||
message_parameter_key_pack_index_helper<V, Keys...>::Value;
|
||||
static_assert(Value < sizeof...(Keys),
|
||||
"Provided StringLiteral doesn't exist in searched list");
|
||||
};
|
||||
|
||||
template <class T> struct SchemaIsArray {
|
||||
template <class T> struct schema_is_array {
|
||||
constexpr static bool Value = false;
|
||||
};
|
||||
|
||||
template <class T> struct SchemaIsArray<schema::Array<T>> {
|
||||
template <class T> struct schema_is_array<schema::Array<T>> {
|
||||
constexpr static bool Value = true;
|
||||
};
|
||||
|
||||
template <class... V, StringLiteral... Keys>
|
||||
class MessageContainer<schema::Struct<schema::NamedMember<V, Keys>...>> {
|
||||
template <class... V, string_literal... Keys>
|
||||
class message_container<schema::Struct<schema::NamedMember<V, Keys>...>> {
|
||||
private:
|
||||
using ValueType = std::tuple<Message<V, MessageContainer<V>>...>;
|
||||
ValueType values;
|
||||
using ValueType = std::tuple<message<V, message_container<V>>...>;
|
||||
ValueType values_;
|
||||
|
||||
public:
|
||||
using SchemaType = schema::Struct<schema::NamedMember<V, Keys>...>;
|
||||
|
||||
template <size_t i>
|
||||
using ElementType = typename MessageParameterPackType<
|
||||
i, Message<V, MessageContainer<V>>...>::Type;
|
||||
using ElementType = typename message_parameter_pack_type<
|
||||
i, message<V, message_container<V>>...>::Type;
|
||||
|
||||
template <size_t i> ElementType<i> &get() { return std::get<i>(values); }
|
||||
template <size_t i> ElementType<i> &get() { return std::get<i>(values_); }
|
||||
};
|
||||
|
||||
/*
|
||||
* Union storage
|
||||
*/
|
||||
template <class... V, StringLiteral... Keys>
|
||||
class MessageContainer<schema::Union<schema::NamedMember<V, Keys>...>> {
|
||||
template <class... V, string_literal... Keys>
|
||||
class message_container<schema::Union<schema::NamedMember<V, Keys>...>> {
|
||||
private:
|
||||
using ValueType = std::variant<Message<V, MessageContainer<V>>...>;
|
||||
ValueType value;
|
||||
using ValueType = std::variant<message<V, message_container<V>>...>;
|
||||
ValueType value_;
|
||||
|
||||
public:
|
||||
using SchemaType = schema::Union<schema::NamedMember<V, Keys>...>;
|
||||
|
||||
template <size_t i>
|
||||
using ElementType = typename MessageParameterPackType<
|
||||
i, Message<V, MessageContainer<V>>...>::Type;
|
||||
using ElementType = typename message_parameter_pack_type<
|
||||
i, message<V, message_container<V>>...>::Type;
|
||||
|
||||
template <size_t i> ElementType<i> &get() {
|
||||
if (i != value.index()) {
|
||||
using MessageIV = typename MessageParameterPackType<i, V...>::Type;
|
||||
value = Message<MessageIV, MessageContainer<MessageIV>>{};
|
||||
if (i != value_.index()) {
|
||||
using MessageIV =
|
||||
typename message_parameter_pack_type<i, V...>::Type;
|
||||
value_ = message<MessageIV, message_container<MessageIV>>{};
|
||||
}
|
||||
return std::get<i>(value);
|
||||
return std::get<i>(value_);
|
||||
}
|
||||
|
||||
size_t index() const noexcept { return value.index(); }
|
||||
size_t index() const noexcept { return value_.index(); }
|
||||
};
|
||||
|
||||
/*
|
||||
* Array storage
|
||||
*/
|
||||
|
||||
template <class T> class MessageContainer<schema::Array<T>> {
|
||||
template <class T> class message_container<schema::Array<T>> {
|
||||
private:
|
||||
using ValueType = std::vector<Message<T, MessageContainer<T>>>;
|
||||
ValueType values;
|
||||
using ValueType = std::vector<message<T, message_container<T>>>;
|
||||
ValueType values_;
|
||||
|
||||
public:
|
||||
using SchemaType = schema::Array<T>;
|
||||
|
||||
using ElementType = Message<T, MessageContainer<T>>;
|
||||
using ElementType = message<T, message_container<T>>;
|
||||
|
||||
Message<T, MessageContainer<T>> &get(size_t index) {
|
||||
return values.at(index);
|
||||
message<T, message_container<T>> &get(size_t index) {
|
||||
return values_.at(index);
|
||||
}
|
||||
|
||||
void resize(size_t size) { values.resize(size); }
|
||||
void resize(size_t size) { values_.resize(size); }
|
||||
|
||||
size_t size() const { return values.size(); }
|
||||
size_t size() const { return values_.size(); }
|
||||
};
|
||||
|
||||
/*
|
||||
* Tuple storage
|
||||
*/
|
||||
template <class... T> class MessageContainer<schema::Tuple<T...>> {
|
||||
template <class... T> class message_container<schema::Tuple<T...>> {
|
||||
private:
|
||||
using ValueType = std::tuple<Message<T, MessageContainer<T>>...>;
|
||||
ValueType values;
|
||||
using ValueType = std::tuple<message<T, message_container<T>>...>;
|
||||
ValueType values_;
|
||||
|
||||
public:
|
||||
using SchemaType = schema::Tuple<T...>;
|
||||
|
||||
template <size_t i>
|
||||
using ElementType = typename MessageParameterPackType<
|
||||
i, Message<T, MessageContainer<T>>...>::Type;
|
||||
using ElementType = typename message_parameter_pack_type<
|
||||
i, message<T, message_container<T>>...>::Type;
|
||||
|
||||
template <size_t i> ElementType<i> &get() { return std::get<i>(values); }
|
||||
template <size_t i> ElementType<i> &get() { return std::get<i>(values_); }
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -205,37 +210,37 @@ struct PrimitiveTypeHelper<schema::Primitive<schema::FloatingPoint, 8>> {
|
|||
using Type = double;
|
||||
};
|
||||
|
||||
template <class T, size_t N> class MessageContainer<schema::Primitive<T, N>> {
|
||||
template <class T, size_t N> class message_container<schema::Primitive<T, N>> {
|
||||
public:
|
||||
using SchemaType = schema::Primitive<T, N>;
|
||||
using ValueType =
|
||||
typename PrimitiveTypeHelper<schema::Primitive<T, N>>::Type;
|
||||
|
||||
private:
|
||||
ValueType value;
|
||||
ValueType value_;
|
||||
|
||||
public:
|
||||
MessageContainer() : value{0} {}
|
||||
message_container() : value_{0} {}
|
||||
|
||||
void set(const ValueType &v) { value = v; }
|
||||
void set(const ValueType &v) { value_ = v; }
|
||||
|
||||
const ValueType &get() const { return value; }
|
||||
const ValueType &get() const { return value_; }
|
||||
};
|
||||
|
||||
template <> class MessageContainer<schema::String> {
|
||||
template <> class message_container<schema::String> {
|
||||
public:
|
||||
using SchemaType = schema::String;
|
||||
using ValueType = std::string;
|
||||
using ValueViewType = std::string_view;
|
||||
|
||||
private:
|
||||
ValueType value;
|
||||
ValueType value_;
|
||||
|
||||
public:
|
||||
void set(ValueType &&v) { value = std::move(v); }
|
||||
void set(const ValueType &v) { value = v; }
|
||||
void set(const ValueViewType v) { value = std::string{v}; }
|
||||
void set(ValueType &&v) { value_ = std::move(v); }
|
||||
void set(const ValueType &v) { value_ = v; }
|
||||
void set(const ValueViewType v) { value_ = std::string{v}; }
|
||||
|
||||
const ValueType &get() const { return value; }
|
||||
const ValueType &get() const { return value_; }
|
||||
};
|
||||
} // namespace saw
|
||||
|
|
|
@ -5,306 +5,307 @@
|
|||
#include "stream_endian.h"
|
||||
|
||||
namespace saw {
|
||||
/// @todo replace types with these
|
||||
/*
|
||||
* I'm not really sure if anyone will use a union which is
|
||||
* bigger than uint32_t max. At least I hope noone would do this
|
||||
*/
|
||||
using msg_union_id_t = uint32_t;
|
||||
using msg_array_length_t = uint64_t;
|
||||
using msg_packet_length_t = uint64_t;
|
||||
class proto_kel_codec {
|
||||
public:
|
||||
using UnionIdT = uint32_t;
|
||||
using ArrayLengthT = uint64_t;
|
||||
using PacketLengthT = uint64_t;
|
||||
|
||||
class ProtoKelCodec {
|
||||
private:
|
||||
struct ReadContext {
|
||||
Buffer &buffer;
|
||||
struct read_context {
|
||||
buffer &buffer;
|
||||
size_t offset = 0;
|
||||
};
|
||||
|
||||
template <typename T> friend struct ProtoKelEncodeImpl;
|
||||
template <typename T> friend struct proto_kel_encode_impl;
|
||||
|
||||
template <typename T> friend struct ProtoKelDecodeImpl;
|
||||
template <typename T> friend struct proto_kel_decode_impl;
|
||||
|
||||
public:
|
||||
struct Limits {
|
||||
msg_packet_length_t packet_size;
|
||||
struct limits {
|
||||
proto_kel_codec::PacketLengthT packet_size;
|
||||
|
||||
Limits() : packet_size{4096} {}
|
||||
Limits(msg_packet_length_t ps) : packet_size{ps} {}
|
||||
limits() : packet_size{4096} {}
|
||||
limits(proto_kel_codec::PacketLengthT ps) : packet_size{ps} {}
|
||||
};
|
||||
|
||||
struct Version {
|
||||
struct version {
|
||||
size_t major;
|
||||
size_t minor;
|
||||
size_t security;
|
||||
};
|
||||
|
||||
const Version version() const { return Version{0, 0, 0}; }
|
||||
const version get_version() const { return version{0, 0, 0}; }
|
||||
|
||||
template <class Schema, class Container = MessageContainer<Schema>>
|
||||
Error encode(typename Message<Schema, Container>::Reader reader,
|
||||
Buffer &buffer);
|
||||
template <class Schema, class Container = message_container<Schema>>
|
||||
error encode(typename message<Schema, Container>::reader reader,
|
||||
buffer &buffer);
|
||||
|
||||
template <class Schema, class Container = MessageContainer<Schema>>
|
||||
Error decode(typename Message<Schema, Container>::Builder builder,
|
||||
Buffer &buffer, const Limits &limits = Limits{});
|
||||
template <class Schema, class Container = message_container<Schema>>
|
||||
error decode(typename message<Schema, Container>::builder builder,
|
||||
buffer &buffer, const limits &lim = limits{});
|
||||
};
|
||||
|
||||
template <class T> struct ProtoKelEncodeImpl;
|
||||
template <class T> struct proto_kel_encode_impl;
|
||||
|
||||
template <class T, size_t N, class Container>
|
||||
struct ProtoKelEncodeImpl<Message<schema::Primitive<T, N>, Container>> {
|
||||
static Error
|
||||
encode(typename Message<schema::Primitive<T, N>, Container>::Reader data,
|
||||
Buffer &buffer) {
|
||||
Error error = StreamValue<typename PrimitiveTypeHelper<
|
||||
struct proto_kel_encode_impl<message<schema::Primitive<T, N>, Container>> {
|
||||
static error
|
||||
encode(typename message<schema::Primitive<T, N>, Container>::reader data,
|
||||
buffer &buffer) {
|
||||
error err = stream_value<typename PrimitiveTypeHelper<
|
||||
schema::Primitive<T, N>>::Type>::encode(data.get(), buffer);
|
||||
return error;
|
||||
return err;
|
||||
}
|
||||
|
||||
static size_t
|
||||
size(typename Message<schema::Primitive<T, N>, Container>::Reader) {
|
||||
return StreamValue<typename PrimitiveTypeHelper<
|
||||
size(typename message<schema::Primitive<T, N>, Container>::reader) {
|
||||
return stream_value<typename PrimitiveTypeHelper<
|
||||
schema::Primitive<T, N>>::Type>::size();
|
||||
}
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
struct ProtoKelEncodeImpl<Message<schema::String, Container>> {
|
||||
static Error
|
||||
encode(typename Message<schema::String, Container>::Reader data,
|
||||
Buffer &buffer) {
|
||||
struct proto_kel_encode_impl<message<schema::String, Container>> {
|
||||
static error
|
||||
encode(typename message<schema::String, Container>::reader data,
|
||||
buffer &buffer) {
|
||||
std::string_view view = data.get();
|
||||
size_t size = view.size();
|
||||
|
||||
Error error = buffer.writeRequireLength(sizeof(size) + size);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
error err = buffer.write_require_length(sizeof(size) + size);
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
|
||||
error = StreamValue<size_t>::encode(size, buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
err = stream_value<size_t>::encode(size, buffer);
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
for (size_t i = 0; i < view.size(); ++i) {
|
||||
buffer.write(i) = view[i];
|
||||
}
|
||||
buffer.writeAdvance(view.size());
|
||||
return noError();
|
||||
buffer.write_advance(view.size());
|
||||
return no_error();
|
||||
}
|
||||
|
||||
static size_t
|
||||
size(typename Message<schema::String, Container>::Reader reader) {
|
||||
size(typename message<schema::String, Container>::reader reader) {
|
||||
return sizeof(size_t) + reader.get().size();
|
||||
}
|
||||
};
|
||||
|
||||
template <class... T, class Container>
|
||||
struct ProtoKelEncodeImpl<Message<schema::Tuple<T...>, Container>> {
|
||||
struct proto_kel_encode_impl<message<schema::Tuple<T...>, Container>> {
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if<i == sizeof...(T), Error>::type
|
||||
encodeMembers(typename Message<schema::Tuple<T...>, Container>::Reader,
|
||||
Buffer &) {
|
||||
return noError();
|
||||
static typename std::enable_if<i == sizeof...(T), error>::type
|
||||
encode_members(typename message<schema::Tuple<T...>, Container>::reader,
|
||||
buffer &) {
|
||||
return no_error();
|
||||
}
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if<(i < sizeof...(T)), Error>::type
|
||||
encodeMembers(typename Message<schema::Tuple<T...>, Container>::Reader data,
|
||||
Buffer &buffer) {
|
||||
Error error =
|
||||
ProtoKelEncodeImpl<typename Container::template ElementType<i>>::
|
||||
static typename std::enable_if<(i < sizeof...(T)), error>::type
|
||||
encode_members(
|
||||
typename message<schema::Tuple<T...>, Container>::reader data,
|
||||
buffer &buffer) {
|
||||
error err =
|
||||
proto_kel_encode_impl<typename Container::template ElementType<i>>::
|
||||
encode(data.template get<i>(), buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return encodeMembers<i + 1>(data, buffer);
|
||||
return encode_members<i + 1>(data, buffer);
|
||||
}
|
||||
|
||||
static Error
|
||||
encode(typename Message<schema::Tuple<T...>, Container>::Reader data,
|
||||
Buffer &buffer) {
|
||||
return encodeMembers<0>(data, buffer);
|
||||
static error
|
||||
encode(typename message<schema::Tuple<T...>, Container>::reader data,
|
||||
buffer &buffer) {
|
||||
return encode_members<0>(data, buffer);
|
||||
}
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if<i == sizeof...(T), size_t>::type
|
||||
sizeMembers(typename Message<schema::Tuple<T...>, Container>::Reader) {
|
||||
size_members(typename message<schema::Tuple<T...>, Container>::reader) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if <
|
||||
i<sizeof...(T), size_t>::type sizeMembers(
|
||||
typename Message<schema::Tuple<T...>, Container>::Reader reader) {
|
||||
return ProtoKelEncodeImpl<typename Container::template ElementType<i>>::
|
||||
size(reader.template get<i>()) +
|
||||
sizeMembers<i + 1>(reader);
|
||||
i<sizeof...(T), size_t>::type size_members(
|
||||
typename message<schema::Tuple<T...>, Container>::reader reader) {
|
||||
return proto_kel_encode_impl<typename Container::template ElementType<
|
||||
i>>::size(reader.template get<i>()) +
|
||||
size_members<i + 1>(reader);
|
||||
}
|
||||
|
||||
static size_t
|
||||
size(typename Message<schema::Tuple<T...>, Container>::Reader reader) {
|
||||
return sizeMembers<0>(reader);
|
||||
size(typename message<schema::Tuple<T...>, Container>::reader reader) {
|
||||
return size_members<0>(reader);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... V, StringLiteral... K, class Container>
|
||||
struct ProtoKelEncodeImpl<
|
||||
Message<schema::Struct<schema::NamedMember<V, K>...>, Container>> {
|
||||
template <typename... V, string_literal... K, class Container>
|
||||
struct proto_kel_encode_impl<
|
||||
message<schema::Struct<schema::NamedMember<V, K>...>, Container>> {
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if<i == sizeof...(V), Error>::type
|
||||
encodeMembers(typename Message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader,
|
||||
Buffer &) {
|
||||
return noError();
|
||||
static typename std::enable_if<i == sizeof...(V), error>::type
|
||||
encode_members(
|
||||
typename message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::reader,
|
||||
buffer &) {
|
||||
return no_error();
|
||||
}
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if <
|
||||
i<sizeof...(V), Error>::type encodeMembers(
|
||||
typename Message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader data,
|
||||
Buffer &buffer) {
|
||||
i<sizeof...(V), error>::type encode_members(
|
||||
typename message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::reader data,
|
||||
buffer &buffer) {
|
||||
|
||||
Error error =
|
||||
ProtoKelEncodeImpl<typename Container::template ElementType<i>>::
|
||||
error err =
|
||||
proto_kel_encode_impl<typename Container::template ElementType<i>>::
|
||||
encode(data.template get<i>(), buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
return encodeMembers<i + 1>(data, buffer);
|
||||
return encode_members<i + 1>(data, buffer);
|
||||
}
|
||||
|
||||
static Error
|
||||
encode(typename Message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader data,
|
||||
Buffer &buffer) {
|
||||
return encodeMembers<0>(data, buffer);
|
||||
static error
|
||||
encode(typename message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::reader data,
|
||||
buffer &buffer) {
|
||||
return encode_members<0>(data, buffer);
|
||||
}
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if<i == sizeof...(V), size_t>::type
|
||||
sizeMembers(typename Message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader) {
|
||||
size_members(typename message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::reader) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if <
|
||||
i<sizeof...(V), size_t>::type sizeMembers(
|
||||
typename Message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader reader) {
|
||||
return ProtoKelEncodeImpl<typename Container::template ElementType<i>>::
|
||||
size(reader.template get<i>()) +
|
||||
sizeMembers<i + 1>(reader);
|
||||
i<sizeof...(V), size_t>::type size_members(
|
||||
typename message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::reader reader) {
|
||||
return proto_kel_encode_impl<typename Container::template ElementType<
|
||||
i>>::size(reader.template get<i>()) +
|
||||
size_members<i + 1>(reader);
|
||||
}
|
||||
|
||||
static size_t
|
||||
size(typename Message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader reader) {
|
||||
return sizeMembers<0>(reader);
|
||||
size(typename message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::reader reader) {
|
||||
return size_members<0>(reader);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... V, StringLiteral... K, class Container>
|
||||
struct ProtoKelEncodeImpl<
|
||||
Message<schema::Union<schema::NamedMember<V, K>...>, Container>> {
|
||||
template <typename... V, string_literal... K, class Container>
|
||||
struct proto_kel_encode_impl<
|
||||
message<schema::Union<schema::NamedMember<V, K>...>, Container>> {
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if<i == sizeof...(V), Error>::type
|
||||
encodeMembers(typename Message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader,
|
||||
Buffer &) {
|
||||
return noError();
|
||||
static typename std::enable_if<i == sizeof...(V), error>::type
|
||||
encode_members(typename message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::reader,
|
||||
buffer &) {
|
||||
return no_error();
|
||||
}
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if <
|
||||
i<sizeof...(V), Error>::type encodeMembers(
|
||||
typename Message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader reader,
|
||||
Buffer &buffer) {
|
||||
i<sizeof...(V), error>::type encode_members(
|
||||
typename message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::reader reader,
|
||||
buffer &buffer) {
|
||||
if (reader.index() == i) {
|
||||
Error error = StreamValue<msg_union_id_t>::encode(i, buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
error err =
|
||||
stream_value<proto_kel_codec::UnionIdT>::encode(i, buffer);
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
return ProtoKelEncodeImpl<typename Container::template ElementType<
|
||||
i>>::encode(reader.template get<i>(), buffer);
|
||||
return proto_kel_encode_impl<
|
||||
typename Container::template ElementType<i>>::
|
||||
encode(reader.template get<i>(), buffer);
|
||||
}
|
||||
return encodeMembers<i + 1>(reader, buffer);
|
||||
return encode_members<i + 1>(reader, buffer);
|
||||
}
|
||||
|
||||
static Error
|
||||
encode(typename Message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader reader,
|
||||
Buffer &buffer) {
|
||||
return encodeMembers<0>(reader, buffer);
|
||||
static error
|
||||
encode(typename message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::reader reader,
|
||||
buffer &buffer) {
|
||||
return encode_members<0>(reader, buffer);
|
||||
}
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if<i == sizeof...(V), size_t>::type
|
||||
sizeMembers(typename Message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader) {
|
||||
size_members(typename message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::reader) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if <
|
||||
i<sizeof...(V), size_t>::type sizeMembers(
|
||||
typename Message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader reader) {
|
||||
i<sizeof...(V), size_t>::type size_members(
|
||||
typename message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::reader reader) {
|
||||
if (reader.index() == i) {
|
||||
return ProtoKelEncodeImpl<typename Container::template ElementType<
|
||||
i>>::size(reader.template get<i>());
|
||||
return proto_kel_encode_impl<
|
||||
typename Container::template ElementType<i>>::
|
||||
size(reader.template get<i>());
|
||||
}
|
||||
return sizeMembers<i + 1>(reader);
|
||||
return size_members<i + 1>(reader);
|
||||
}
|
||||
|
||||
/*
|
||||
* Size of union id + member size
|
||||
*/
|
||||
static size_t
|
||||
size(typename Message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::Reader reader) {
|
||||
return sizeof(msg_union_id_t) + sizeMembers<0>(reader);
|
||||
size(typename message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::reader reader) {
|
||||
return sizeof(proto_kel_codec::UnionIdT) + size_members<0>(reader);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class Container>
|
||||
struct ProtoKelEncodeImpl<Message<schema::Array<T>, Container>> {
|
||||
static Error
|
||||
encode(typename Message<schema::Array<T>, Container>::Reader data,
|
||||
Buffer &buffer) {
|
||||
msg_array_length_t array_length = data.size();
|
||||
struct proto_kel_encode_impl<message<schema::Array<T>, Container>> {
|
||||
static error
|
||||
encode(typename message<schema::Array<T>, Container>::reader data,
|
||||
buffer &buffer) {
|
||||
proto_kel_codec::ArrayLengthT array_length = data.size();
|
||||
{
|
||||
Error error =
|
||||
StreamValue<msg_array_length_t>::encode(array_length, buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
error err = stream_value<proto_kel_codec::ArrayLengthT>::encode(
|
||||
array_length, buffer);
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < array_length; ++i) {
|
||||
Error error =
|
||||
ProtoKelEncodeImpl<typename Container::ElementType>::encode(
|
||||
error err =
|
||||
proto_kel_encode_impl<typename Container::ElementType>::encode(
|
||||
data.get(i), buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static size_t
|
||||
size(typename Message<schema::Array<T>, Container>::Reader data) {
|
||||
size_t members = sizeof(msg_array_length_t);
|
||||
size(typename message<schema::Array<T>, Container>::reader data) {
|
||||
size_t members = sizeof(proto_kel_codec::ArrayLengthT);
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
members +=
|
||||
ProtoKelEncodeImpl<typename Container::ElementType>::size(
|
||||
proto_kel_encode_impl<typename Container::ElementType>::size(
|
||||
data.get(i));
|
||||
}
|
||||
|
||||
|
@ -315,248 +316,249 @@ struct ProtoKelEncodeImpl<Message<schema::Array<T>, Container>> {
|
|||
/*
|
||||
* Decode Implementations
|
||||
*/
|
||||
template <typename T> struct ProtoKelDecodeImpl;
|
||||
template <typename T> struct proto_kel_decode_impl;
|
||||
|
||||
template <class T, size_t N, class Container>
|
||||
struct ProtoKelDecodeImpl<Message<schema::Primitive<T, N>, Container>> {
|
||||
static Error
|
||||
decode(typename Message<schema::Primitive<T, N>, Container>::Builder data,
|
||||
Buffer &buffer) {
|
||||
struct proto_kel_decode_impl<message<schema::Primitive<T, N>, Container>> {
|
||||
static error
|
||||
decode(typename message<schema::Primitive<T, N>, Container>::builder data,
|
||||
buffer &buffer) {
|
||||
typename PrimitiveTypeHelper<schema::Primitive<T, N>>::Type val = 0;
|
||||
Error error = StreamValue<typename PrimitiveTypeHelper<
|
||||
error err = stream_value<typename PrimitiveTypeHelper<
|
||||
schema::Primitive<T, N>>::Type>::decode(val, buffer);
|
||||
data.set(val);
|
||||
return error;
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
struct ProtoKelDecodeImpl<Message<schema::String, Container>> {
|
||||
static Error
|
||||
decode(typename Message<schema::String, Container>::Builder data,
|
||||
Buffer &buffer) {
|
||||
struct proto_kel_decode_impl<message<schema::String, Container>> {
|
||||
static error
|
||||
decode(typename message<schema::String, Container>::builder data,
|
||||
buffer &buffer) {
|
||||
size_t size = 0;
|
||||
if (sizeof(size) > buffer.readCompositeLength()) {
|
||||
return recoverableError("Buffer too small");
|
||||
if (sizeof(size) > buffer.read_composite_length()) {
|
||||
return recoverable_error("Buffer too small");
|
||||
}
|
||||
|
||||
Error error = StreamValue<size_t>::decode(size, buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
error err = stream_value<size_t>::decode(size, buffer);
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (size > buffer.readCompositeLength()) {
|
||||
return recoverableError("Buffer too small");
|
||||
if (size > buffer.read_composite_length()) {
|
||||
return recoverable_error("Buffer too small");
|
||||
}
|
||||
|
||||
std::string value;
|
||||
value.resize(size);
|
||||
|
||||
if (size > buffer.readCompositeLength()) {
|
||||
return recoverableError("Buffer too small");
|
||||
if (size > buffer.read_composite_length()) {
|
||||
return recoverable_error("Buffer too small");
|
||||
}
|
||||
for (size_t i = 0; i < value.size(); ++i) {
|
||||
value[i] = buffer.read(i);
|
||||
}
|
||||
buffer.readAdvance(value.size());
|
||||
buffer.read_advance(value.size());
|
||||
data.set(std::move(value));
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
};
|
||||
|
||||
template <class... T, class Container>
|
||||
struct ProtoKelDecodeImpl<Message<schema::Tuple<T...>, Container>> {
|
||||
struct proto_kel_decode_impl<message<schema::Tuple<T...>, Container>> {
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if<i == sizeof...(T), Error>::type
|
||||
decodeMembers(typename Message<schema::Tuple<T...>, Container>::Builder,
|
||||
Buffer &) {
|
||||
return noError();
|
||||
static typename std::enable_if<i == sizeof...(T), error>::type
|
||||
decode_members(typename message<schema::Tuple<T...>, Container>::builder,
|
||||
buffer &) {
|
||||
return no_error();
|
||||
}
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if <
|
||||
i<sizeof...(T), Error>::type decodeMembers(
|
||||
typename Message<schema::Tuple<T...>, Container>::Builder builder,
|
||||
Buffer &buffer) {
|
||||
i<sizeof...(T), error>::type decode_members(
|
||||
typename message<schema::Tuple<T...>, Container>::builder builder,
|
||||
buffer &buffer) {
|
||||
|
||||
Error error =
|
||||
ProtoKelDecodeImpl<typename Container::template ElementType<i>>::
|
||||
error err =
|
||||
proto_kel_decode_impl<typename Container::template ElementType<i>>::
|
||||
decode(builder.template init<i>(), buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
return decodeMembers<i + 1>(builder, buffer);
|
||||
return decode_members<i + 1>(builder, buffer);
|
||||
}
|
||||
|
||||
static Error
|
||||
decode(typename Message<schema::Tuple<T...>, Container>::Builder builder,
|
||||
Buffer &buffer) {
|
||||
return decodeMembers<0>(builder, buffer);
|
||||
static error
|
||||
decode(typename message<schema::Tuple<T...>, Container>::builder builder,
|
||||
buffer &buffer) {
|
||||
return decode_members<0>(builder, buffer);
|
||||
}
|
||||
};
|
||||
|
||||
template <class... V, StringLiteral... K, class Container>
|
||||
struct ProtoKelDecodeImpl<
|
||||
Message<schema::Struct<schema::NamedMember<V, K>...>, Container>> {
|
||||
template <class... V, string_literal... K, class Container>
|
||||
struct proto_kel_decode_impl<
|
||||
message<schema::Struct<schema::NamedMember<V, K>...>, Container>> {
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if<i == sizeof...(V), Error>::type
|
||||
decodeMembers(typename Message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::Builder,
|
||||
Buffer &) {
|
||||
return noError();
|
||||
static typename std::enable_if<i == sizeof...(V), error>::type
|
||||
decode_members(
|
||||
typename message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::builder,
|
||||
buffer &) {
|
||||
return no_error();
|
||||
}
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if <
|
||||
i<sizeof...(V), Error>::type decodeMembers(
|
||||
typename Message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::Builder builder,
|
||||
Buffer &buffer) {
|
||||
i<sizeof...(V), error>::type decode_members(
|
||||
typename message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::builder builder,
|
||||
buffer &buffer) {
|
||||
|
||||
Error error =
|
||||
ProtoKelDecodeImpl<typename Container::template ElementType<i>>::
|
||||
error err =
|
||||
proto_kel_decode_impl<typename Container::template ElementType<i>>::
|
||||
decode(builder.template init<i>(), buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
return decodeMembers<i + 1>(builder, buffer);
|
||||
return decode_members<i + 1>(builder, buffer);
|
||||
}
|
||||
|
||||
static Error
|
||||
decode(typename Message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::Builder builder,
|
||||
Buffer &buffer) {
|
||||
return decodeMembers<0>(builder, buffer);
|
||||
static error
|
||||
decode(typename message<schema::Struct<schema::NamedMember<V, K>...>,
|
||||
Container>::builder builder,
|
||||
buffer &buffer) {
|
||||
return decode_members<0>(builder, buffer);
|
||||
}
|
||||
};
|
||||
|
||||
template <class... V, StringLiteral... K, class Container>
|
||||
struct ProtoKelDecodeImpl<
|
||||
Message<schema::Union<schema::NamedMember<V, K>...>, Container>> {
|
||||
template <class... V, string_literal... K, class Container>
|
||||
struct proto_kel_decode_impl<
|
||||
message<schema::Union<schema::NamedMember<V, K>...>, Container>> {
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if<i == sizeof...(V), Error>::type
|
||||
decodeMembers(typename Message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::Builder,
|
||||
Buffer &, msg_union_id_t) {
|
||||
return noError();
|
||||
static typename std::enable_if<i == sizeof...(V), error>::type
|
||||
decode_members(typename message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::builder,
|
||||
buffer &, proto_kel_codec::UnionIdT) {
|
||||
return no_error();
|
||||
}
|
||||
|
||||
template <size_t i = 0>
|
||||
static typename std::enable_if <
|
||||
i<sizeof...(V), Error>::type decodeMembers(
|
||||
typename Message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::Builder builder,
|
||||
Buffer &buffer, msg_union_id_t id) {
|
||||
i<sizeof...(V), error>::type decode_members(
|
||||
typename message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::builder builder,
|
||||
buffer &buffer, proto_kel_codec::UnionIdT id) {
|
||||
|
||||
if (id == i) {
|
||||
Error error =
|
||||
ProtoKelDecodeImpl<typename Container::template ElementType<
|
||||
error err =
|
||||
proto_kel_decode_impl<typename Container::template ElementType<
|
||||
i>>::decode(builder.template init<i>(), buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return decodeMembers<i + 1>(builder, buffer, id);
|
||||
return decode_members<i + 1>(builder, buffer, id);
|
||||
}
|
||||
|
||||
static Error
|
||||
decode(typename Message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::Builder builder,
|
||||
Buffer &buffer) {
|
||||
msg_union_id_t id = 0;
|
||||
Error error = StreamValue<msg_union_id_t>::decode(id, buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
static error
|
||||
decode(typename message<schema::Union<schema::NamedMember<V, K>...>,
|
||||
Container>::builder builder,
|
||||
buffer &buffer) {
|
||||
proto_kel_codec::UnionIdT id = 0;
|
||||
error err = stream_value<proto_kel_codec::UnionIdT>::decode(id, buffer);
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
if (id >= sizeof...(V)) {
|
||||
return criticalError("Union doesn't have this many id's");
|
||||
return critical_error("Union doesn't have this many id's");
|
||||
}
|
||||
|
||||
return decodeMembers<0>(builder, buffer, id);
|
||||
return decode_members<0>(builder, buffer, id);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class Container>
|
||||
struct ProtoKelDecodeImpl<Message<schema::Array<T>, Container>> {
|
||||
static Error
|
||||
decode(typename Message<schema::Array<T>, Container>::Builder data,
|
||||
Buffer &buffer) {
|
||||
msg_array_length_t array_length = 0;
|
||||
struct proto_kel_decode_impl<message<schema::Array<T>, Container>> {
|
||||
static error
|
||||
decode(typename message<schema::Array<T>, Container>::builder data,
|
||||
buffer &buffer) {
|
||||
proto_kel_codec::ArrayLengthT array_length = 0;
|
||||
{
|
||||
Error error =
|
||||
StreamValue<msg_array_length_t>::decode(array_length, buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
error err = stream_value<proto_kel_codec::ArrayLengthT>::decode(
|
||||
array_length, buffer);
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
data.resize(array_length);
|
||||
|
||||
for (size_t i = 0; i < array_length; ++i) {
|
||||
Error error =
|
||||
ProtoKelDecodeImpl<typename Container::ElementType>::decode(
|
||||
error err =
|
||||
proto_kel_decode_impl<typename Container::ElementType>::decode(
|
||||
data.init(i), buffer);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
};
|
||||
|
||||
template <class Schema, class Container>
|
||||
Error ProtoKelCodec::encode(typename Message<Schema, Container>::Reader reader,
|
||||
Buffer &buffer) {
|
||||
BufferView view{buffer};
|
||||
error proto_kel_codec::encode(
|
||||
typename message<Schema, Container>::reader reader, buffer &buffer) {
|
||||
buffer_view view{buffer};
|
||||
|
||||
msg_packet_length_t packet_length =
|
||||
ProtoKelEncodeImpl<Message<Schema, Container>>::size(reader);
|
||||
proto_kel_codec::PacketLengthT packet_length =
|
||||
proto_kel_encode_impl<message<Schema, Container>>::size(reader);
|
||||
// Check the size of the packet for the first
|
||||
// message length description
|
||||
|
||||
Error error =
|
||||
view.writeRequireLength(packet_length + sizeof(msg_packet_length_t));
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
error err = view.write_require_length(
|
||||
packet_length + sizeof(proto_kel_codec::PacketLengthT));
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
|
||||
{
|
||||
Error error =
|
||||
StreamValue<msg_packet_length_t>::encode(packet_length, view);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
error err = stream_value<proto_kel_codec::PacketLengthT>::encode(
|
||||
packet_length, view);
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
{
|
||||
Error error = ProtoKelEncodeImpl<Message<Schema, Container>>::encode(
|
||||
error err = proto_kel_encode_impl<message<Schema, Container>>::encode(
|
||||
reader, view);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.writeAdvance(view.writeOffset());
|
||||
return noError();
|
||||
buffer.write_advance(view.write_offset());
|
||||
return no_error();
|
||||
}
|
||||
|
||||
template <class Schema, class Container>
|
||||
Error ProtoKelCodec::decode(
|
||||
typename Message<Schema, Container>::Builder builder, Buffer &buffer,
|
||||
const Limits &limits) {
|
||||
BufferView view{buffer};
|
||||
error proto_kel_codec::decode(
|
||||
typename message<Schema, Container>::builder builder, buffer &buffer,
|
||||
const limits &limits) {
|
||||
buffer_view view{buffer};
|
||||
|
||||
msg_packet_length_t packet_length = 0;
|
||||
proto_kel_codec::PacketLengthT packet_length = 0;
|
||||
{
|
||||
Error error =
|
||||
StreamValue<msg_packet_length_t>::decode(packet_length, view);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
error err = stream_value<proto_kel_codec::PacketLengthT>::decode(
|
||||
packet_length, view);
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (packet_length > limits.packet_size) {
|
||||
return criticalError(
|
||||
return critical_error(
|
||||
[packet_length]() {
|
||||
return std::string{"Packet size too big: "} +
|
||||
std::to_string(packet_length);
|
||||
|
@ -565,21 +567,21 @@ Error ProtoKelCodec::decode(
|
|||
}
|
||||
|
||||
{
|
||||
Error error = ProtoKelDecodeImpl<Message<Schema, Container>>::decode(
|
||||
error err = proto_kel_decode_impl<message<Schema, Container>>::decode(
|
||||
builder, view);
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
if (err.failed()) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
{
|
||||
if (ProtoKelEncodeImpl<Message<Schema, Container>>::size(
|
||||
builder.asReader()) != packet_length) {
|
||||
return criticalError("Bad packet format");
|
||||
if (proto_kel_encode_impl<message<Schema, Container>>::size(
|
||||
builder.as_reader()) != packet_length) {
|
||||
return critical_error("Bad packet format");
|
||||
}
|
||||
}
|
||||
|
||||
buffer.readAdvance(view.readOffset());
|
||||
return noError();
|
||||
buffer.read_advance(view.read_offset());
|
||||
return no_error();
|
||||
}
|
||||
|
||||
} // namespace saw
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "schema.h"
|
||||
|
||||
namespace saw {
|
||||
template<typename Codec, typename Schema> class rpc;
|
||||
|
||||
/**
|
||||
* Provides the interface for messages with an rpc type behaviour
|
||||
*/
|
||||
template<typename Codec, typename... Responses, typename... Requests, StringLiteral... Literals>
|
||||
class rpc<schema::Interface<schema::Function<Responses, Requests, Literals>...>> {
|
||||
public:
|
||||
|
||||
class client {
|
||||
public:
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<StringLiteral FunctionLiteral>
|
||||
conveyor<Message<FunctionResponse>> request(message<FunctionRequest> parameters);
|
||||
};
|
||||
|
||||
class service {
|
||||
public:
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<StringLiteral FunctionLiteral>
|
||||
conveyor<Message<FunctionResponse>> handle(message<FunctionRequest> request);
|
||||
};
|
||||
|
||||
class server {
|
||||
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,24 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "string_literal.h"
|
||||
|
||||
namespace saw {
|
||||
namespace schema {
|
||||
// NOLINTBEGIN
|
||||
template <typename T, string_literal Literal> struct NamedMember {};
|
||||
|
||||
template <class T, StringLiteral Literal> struct NamedMember {};
|
||||
template <typename... T> struct Struct {
|
||||
static_assert(
|
||||
always_false<T...>,
|
||||
"This schema template doesn't support this type of template argument");
|
||||
};
|
||||
|
||||
template <class... T> struct Struct;
|
||||
|
||||
template <class... V, StringLiteral... K>
|
||||
template <typename... V, string_literal... K>
|
||||
struct Struct<NamedMember<V, K>...> {};
|
||||
|
||||
template <class... T> struct Union;
|
||||
template <typename... T> struct Union {
|
||||
static_assert(
|
||||
always_false<T...>,
|
||||
"This schema template doesn't support this type of template argument");
|
||||
};
|
||||
|
||||
template <class... V, StringLiteral... K> struct Union<NamedMember<V, K>...> {};
|
||||
template <typename... V, string_literal... K>
|
||||
struct Union<NamedMember<V, K>...> {};
|
||||
|
||||
template <class T> struct Array {};
|
||||
template <typename T> struct Array {};
|
||||
|
||||
template <class... T> struct Tuple {};
|
||||
template <typename... T> struct Tuple {};
|
||||
|
||||
struct String {};
|
||||
|
||||
|
@ -47,5 +57,21 @@ using UInt64 = Primitive<UnsignedInteger, 8>;
|
|||
using Float32 = Primitive<FloatingPoint, 4>;
|
||||
using Float64 = Primitive<FloatingPoint, 8>;
|
||||
|
||||
/**
|
||||
* Classes enabling Rpc calls
|
||||
*/
|
||||
template <class Request, class Response, string_literal Literal>
|
||||
struct Function {};
|
||||
|
||||
template <class... T> struct Interface {
|
||||
static_assert(
|
||||
always_false<T...>,
|
||||
"This schema template doesn't support this type of template argument");
|
||||
};
|
||||
|
||||
template <class... Request, class... Response, string_literal... Literal>
|
||||
struct Interface<Function<Request, Response, Literal>...> {};
|
||||
|
||||
// NOLINTEND
|
||||
} // namespace schema
|
||||
} // namespace saw
|
||||
|
|
|
@ -15,16 +15,16 @@ namespace saw {
|
|||
* platform independent. So it does not matter if the memory layout is
|
||||
* little endian or big endian
|
||||
*/
|
||||
template <typename T, size_t size = sizeof(T)> class ShiftStreamValue;
|
||||
template <typename T, size_t size = sizeof(T)> class shift_stream_value;
|
||||
|
||||
template <typename T> class ShiftStreamValue<T, 1> {
|
||||
template <typename T> class shift_stream_value<T, 1> {
|
||||
public:
|
||||
inline static Error decode(T &val, Buffer &buffer) {
|
||||
inline static error decode(T &val, buffer &buffer) {
|
||||
uint8_t &raw = reinterpret_cast<uint8_t &>(val);
|
||||
return buffer.pop(raw, sizeof(T));
|
||||
}
|
||||
|
||||
inline static Error encode(const T &val, Buffer &buffer) {
|
||||
inline static error encode(const T &val, buffer &buffer) {
|
||||
const uint8_t &raw = reinterpret_cast<const uint8_t &>(val);
|
||||
return buffer.push(raw, sizeof(T));
|
||||
}
|
||||
|
@ -32,11 +32,11 @@ public:
|
|||
inline static size_t size() { return sizeof(T); }
|
||||
};
|
||||
|
||||
template <typename T> class ShiftStreamValue<T, 2> {
|
||||
template <typename T> class shift_stream_value<T, 2> {
|
||||
public:
|
||||
inline static Error decode(T &val, Buffer &buffer) {
|
||||
if (buffer.readCompositeLength() < sizeof(T)) {
|
||||
return recoverableError("Buffer too small");
|
||||
inline static error decode(T &val, buffer &buffer) {
|
||||
if (buffer.read_composite_length() < sizeof(T)) {
|
||||
return recoverable_error("Buffer too small");
|
||||
}
|
||||
|
||||
uint16_t raw = 0;
|
||||
|
@ -45,13 +45,13 @@ public:
|
|||
raw |= (static_cast<uint16_t>(buffer.read(i)) << (i * 8));
|
||||
}
|
||||
memcpy(&val, &raw, sizeof(T));
|
||||
buffer.readAdvance(sizeof(T));
|
||||
buffer.read_advance(sizeof(T));
|
||||
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
|
||||
inline static Error encode(const T &val, Buffer &buffer) {
|
||||
Error error = buffer.writeRequireLength(sizeof(T));
|
||||
inline static error encode(const T &val, buffer &buffer) {
|
||||
error error = buffer.write_require_length(sizeof(T));
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
}
|
||||
|
@ -63,18 +63,18 @@ public:
|
|||
buffer.write(i) = raw >> (i * 8);
|
||||
}
|
||||
|
||||
buffer.writeAdvance(sizeof(T));
|
||||
return noError();
|
||||
buffer.write_advance(sizeof(T));
|
||||
return no_error();
|
||||
}
|
||||
|
||||
inline static size_t size() { return sizeof(T); }
|
||||
};
|
||||
|
||||
template <typename T> class ShiftStreamValue<T, 4> {
|
||||
template <typename T> class shift_stream_value<T, 4> {
|
||||
public:
|
||||
inline static Error decode(T &val, Buffer &buffer) {
|
||||
if (buffer.readCompositeLength() < sizeof(T)) {
|
||||
return recoverableError("Buffer too small");
|
||||
inline static error decode(T &val, buffer &buffer) {
|
||||
if (buffer.read_composite_length() < sizeof(T)) {
|
||||
return recoverable_error("Buffer too small");
|
||||
}
|
||||
|
||||
uint32_t raw = 0;
|
||||
|
@ -83,13 +83,13 @@ public:
|
|||
raw |= (static_cast<uint32_t>(buffer.read(i)) << (i * 8));
|
||||
}
|
||||
memcpy(&val, &raw, sizeof(T));
|
||||
buffer.readAdvance(sizeof(T));
|
||||
buffer.read_advance(sizeof(T));
|
||||
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
|
||||
inline static Error encode(const T &val, Buffer &buffer) {
|
||||
Error error = buffer.writeRequireLength(sizeof(T));
|
||||
inline static error encode(const T &val, buffer &buffer) {
|
||||
error error = buffer.write_require_length(sizeof(T));
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
}
|
||||
|
@ -101,18 +101,18 @@ public:
|
|||
buffer.write(i) = raw >> (i * 8);
|
||||
}
|
||||
|
||||
buffer.writeAdvance(sizeof(T));
|
||||
return noError();
|
||||
buffer.write_advance(sizeof(T));
|
||||
return no_error();
|
||||
}
|
||||
|
||||
inline static size_t size() { return sizeof(T); }
|
||||
};
|
||||
|
||||
template <typename T> class ShiftStreamValue<T, 8> {
|
||||
template <typename T> class shift_stream_value<T, 8> {
|
||||
public:
|
||||
inline static Error decode(T &val, Buffer &buffer) {
|
||||
if (buffer.readCompositeLength() < sizeof(T)) {
|
||||
return recoverableError("Buffer too small");
|
||||
inline static error decode(T &val, buffer &buffer) {
|
||||
if (buffer.read_composite_length() < sizeof(T)) {
|
||||
return recoverable_error("Buffer too small");
|
||||
}
|
||||
|
||||
uint64_t raw = 0;
|
||||
|
@ -122,13 +122,13 @@ public:
|
|||
}
|
||||
|
||||
memcpy(&val, &raw, sizeof(T));
|
||||
buffer.readAdvance(sizeof(T));
|
||||
buffer.read_advance(sizeof(T));
|
||||
|
||||
return noError();
|
||||
return no_error();
|
||||
}
|
||||
|
||||
inline static Error encode(const T &val, Buffer &buffer) {
|
||||
Error error = buffer.writeRequireLength(sizeof(T));
|
||||
inline static error encode(const T &val, buffer &buffer) {
|
||||
error error = buffer.write_require_length(sizeof(T));
|
||||
if (error.failed()) {
|
||||
return error;
|
||||
}
|
||||
|
@ -140,13 +140,13 @@ public:
|
|||
buffer.write(i) = raw >> (i * 8);
|
||||
}
|
||||
|
||||
buffer.writeAdvance(sizeof(T));
|
||||
return noError();
|
||||
buffer.write_advance(sizeof(T));
|
||||
return no_error();
|
||||
}
|
||||
|
||||
inline static size_t size() { return sizeof(T); }
|
||||
};
|
||||
|
||||
template <typename T> using StreamValue = ShiftStreamValue<T>;
|
||||
template <typename T> using stream_value = shift_stream_value<T>;
|
||||
|
||||
} // namespace saw
|
||||
|
|
|
@ -9,9 +9,9 @@ namespace saw {
|
|||
* literal. It guarantees compile time uniqueness and thus allows using strings
|
||||
* in template parameters.
|
||||
*/
|
||||
template <class CharT, size_t N> class StringLiteral {
|
||||
template <class CharT, size_t N> class string_literal {
|
||||
public:
|
||||
constexpr StringLiteral(const CharT (&input)[N]) noexcept {
|
||||
constexpr string_literal(const CharT (&input)[N]) noexcept {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
data[i] = input[i];
|
||||
}
|
||||
|
@ -24,17 +24,17 @@ public:
|
|||
}
|
||||
|
||||
constexpr bool
|
||||
operator==(const StringLiteral<CharT, N> &) const noexcept = default;
|
||||
operator==(const string_literal<CharT, N> &) const noexcept = default;
|
||||
|
||||
template <class CharTR, size_t NR>
|
||||
constexpr bool
|
||||
operator==(const StringLiteral<CharTR, NR> &) const noexcept {
|
||||
operator==(const string_literal<CharTR, NR> &) const noexcept {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, T... Chars>
|
||||
constexpr StringLiteral<T, sizeof...(Chars)> operator""_key() {
|
||||
return StringLiteral<T, sizeof...(Chars) + 1u>{Chars..., '\0'};
|
||||
constexpr string_literal<T, sizeof...(Chars)> operator""_key() {
|
||||
return string_literal<T, sizeof...(Chars) + 1u>{Chars..., '\0'};
|
||||
}
|
||||
} // namespace saw
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/false
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import glob
|
||||
|
||||
|
||||
Import('base_lib_env');
|
||||
Import('env');
|
||||
|
||||
dir_path = Dir('.').abspath
|
||||
|
||||
env.tls_sources += sorted(glob.glob(dir_path + "/*.cpp"))
|
||||
env.tls_headers += sorted(glob.glob(dir_path + "/*.h"))
|
||||
|
||||
tls_lib_env = base_lib_env.Clone();
|
||||
|
||||
tls_lib_env.Append(LIBS=['gnutls']);
|
||||
|
||||
### Shared lib
|
||||
|
||||
objects_shared = []
|
||||
tls_lib_env.add_source_files(objects_shared, env.tls_sources, shared=True);
|
||||
env.tls_library_shared = tls_lib_env.SharedLibrary('#build/forstio-tls', [objects_shared, env.library_shared]);
|
||||
|
||||
### Static lib
|
||||
|
||||
objects_static = []
|
||||
tls_lib_env.add_source_files(objects_static, env.tls_sources, shared=False);
|
||||
env.tls_library_static = tls_lib_env.StaticLibrary('#build/forstio-tls', [objects_static, env.library_static]);
|
||||
|
||||
|
||||
### Set alias
|
||||
env.Alias('library_tls', [env.tls_library_shared + env.tls_library_static]);
|
||||
|
|
@ -38,60 +38,60 @@ Tls::~Tls() {}
|
|||
|
||||
Tls::Impl &Tls::getImpl() { return *impl; }
|
||||
|
||||
class TlsIoStream final : public IoStream {
|
||||
class TlsIoStream final : public io_stream {
|
||||
private:
|
||||
Own<IoStream> internal;
|
||||
own<io_stream> internal;
|
||||
gnutls_session_t session_handle;
|
||||
|
||||
public:
|
||||
TlsIoStream(Own<IoStream> internal_) : internal{std::move(internal_)} {}
|
||||
TlsIoStream(own<io_stream> internal_) : internal{std::move(internal_)} {}
|
||||
|
||||
~TlsIoStream() { gnutls_bye(session_handle, GNUTLS_SHUT_RDWR); }
|
||||
|
||||
ErrorOr<size_t> read(void *buffer, size_t length) override {
|
||||
error_or<size_t> read(void *buffer, size_t length) override {
|
||||
ssize_t size = gnutls_record_recv(session_handle, buffer, length);
|
||||
if (size < 0) {
|
||||
if(gnutls_error_is_fatal(size) == 0){
|
||||
return recoverableError([size](){return std::string{"Read recoverable Error "}+std::string{gnutls_strerror(size)};}, "Error read r");
|
||||
return recoverable_error([size](){return std::string{"Read recoverable Error "}+std::string{gnutls_strerror(size)};}, "Error read r");
|
||||
}else{
|
||||
return criticalError([size](){return std::string{"Read critical Error "}+std::string{gnutls_strerror(size)};}, "Error read c");
|
||||
return critical_error([size](){return std::string{"Read critical Error "}+std::string{gnutls_strerror(size)};}, "Error read c");
|
||||
}
|
||||
}else if(size == 0){
|
||||
return criticalError("Disconnected");
|
||||
return critical_error("Disconnected");
|
||||
}
|
||||
|
||||
return static_cast<size_t>(length);
|
||||
}
|
||||
|
||||
Conveyor<void> readReady() override { return internal->readReady(); }
|
||||
conveyor<void> read_ready() override { return internal->read_ready(); }
|
||||
|
||||
Conveyor<void> onReadDisconnected() override {
|
||||
return internal->onReadDisconnected();
|
||||
conveyor<void> on_read_disconnected() override {
|
||||
return internal->on_read_disconnected();
|
||||
}
|
||||
|
||||
ErrorOr<size_t> write(const void *buffer, size_t length) override {
|
||||
error_or<size_t> write(const void *buffer, size_t length) override {
|
||||
ssize_t size = gnutls_record_send(session_handle, buffer, length);
|
||||
if(size < 0){
|
||||
if(gnutls_error_is_fatal(size) == 0){
|
||||
return recoverableError([size](){return std::string{"Write recoverable Error "}+std::string{gnutls_strerror(size)} + " " + std::to_string(size);}, "Error write r");
|
||||
return recoverable_error([size](){return std::string{"Write recoverable Error "}+std::string{gnutls_strerror(size)} + " " + std::to_string(size);}, "Error write r");
|
||||
}else{
|
||||
return criticalError([size](){return std::string{"Write critical Error "}+std::string{gnutls_strerror(size)} + " " + std::to_string(size);}, "Error write c");
|
||||
return critical_error([size](){return std::string{"Write critical Error "}+std::string{gnutls_strerror(size)} + " " + std::to_string(size);}, "Error write c");
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<size_t>(size);
|
||||
}
|
||||
|
||||
Conveyor<void> writeReady() override { return internal->writeReady(); }
|
||||
conveyor<void> write_ready() override { return internal->write_ready(); }
|
||||
|
||||
gnutls_session_t &session() { return session_handle; }
|
||||
};
|
||||
|
||||
TlsServer::TlsServer(Own<Server> srv) : internal{std::move(srv)} {}
|
||||
TlsServer::TlsServer(own<server> srv) : internal{std::move(srv)} {}
|
||||
|
||||
Conveyor<Own<IoStream>> TlsServer::accept() {
|
||||
SAW_ASSERT(internal) { return Conveyor<Own<IoStream>>{nullptr, nullptr}; }
|
||||
return internal->accept().then([](Own<IoStream> stream) -> Own<IoStream> {
|
||||
conveyor<own<io_stream>> TlsServer::accept() {
|
||||
SAW_ASSERT(internal) { return conveyor<own<io_stream>>{fix_void<own<io_stream>>{nullptr}}; }
|
||||
return internal->accept().then([](own<io_stream> stream) -> own<io_stream> {
|
||||
/// @todo handshake
|
||||
|
||||
|
||||
|
@ -105,14 +105,14 @@ namespace {
|
|||
*/
|
||||
struct TlsClientStreamHelper {
|
||||
public:
|
||||
Own<ConveyorFeeder<Own<IoStream>>> feeder;
|
||||
SinkConveyor connection_sink;
|
||||
SinkConveyor stream_reader;
|
||||
SinkConveyor stream_writer;
|
||||
own<conveyor_feeder<own<io_stream>>> feeder;
|
||||
conveyor_sink connection_sink;
|
||||
conveyor_sink stream_reader;
|
||||
conveyor_sink stream_writer;
|
||||
|
||||
Own<TlsIoStream> stream = nullptr;
|
||||
own<TlsIoStream> stream = nullptr;
|
||||
public:
|
||||
TlsClientStreamHelper(Own<ConveyorFeeder<Own<IoStream>>> f):
|
||||
TlsClientStreamHelper(own<conveyor_feeder<own<io_stream>>> f):
|
||||
feeder{std::move(f)}
|
||||
{}
|
||||
|
||||
|
@ -121,11 +121,11 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
stream_reader = stream->readReady().then([this](){
|
||||
stream_reader = stream->read_ready().then([this](){
|
||||
turn();
|
||||
}).sink();
|
||||
|
||||
stream_writer = stream->writeReady().then([this](){
|
||||
stream_writer = stream->write_ready().then([this](){
|
||||
turn();
|
||||
}).sink();
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ public:
|
|||
} while ( (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) && gnutls_error_is_fatal(ret) == 0);
|
||||
|
||||
if(gnutls_error_is_fatal(ret)){
|
||||
feeder->fail(criticalError("Couldn't create Tls connection"));
|
||||
feeder->fail(critical_error("Couldn't create Tls connection"));
|
||||
stream = nullptr;
|
||||
}else if(ret == GNUTLS_E_SUCCESS){
|
||||
feeder->feed(std::move(stream));
|
||||
|
@ -155,20 +155,20 @@ public:
|
|||
};
|
||||
}
|
||||
|
||||
Own<Server> TlsNetwork::listen(NetworkAddress& address) {
|
||||
own<server> TlsNetwork::listen(network_address& address) {
|
||||
return heap<TlsServer>(internal.listen(address));
|
||||
}
|
||||
|
||||
Conveyor<Own<IoStream>> TlsNetwork::connect(NetworkAddress& address) {
|
||||
conveyor<own<io_stream>> TlsNetwork::connect(network_address& address) {
|
||||
// Helper setups
|
||||
auto caf = newConveyorAndFeeder<Own<IoStream>>();
|
||||
Own<TlsClientStreamHelper> helper = heap<TlsClientStreamHelper>(std::move(caf.feeder));
|
||||
auto caf = new_conveyor_and_feeder<own<io_stream>>();
|
||||
own<TlsClientStreamHelper> helper = heap<TlsClientStreamHelper>(std::move(caf.feeder));
|
||||
TlsClientStreamHelper* hlp_ptr = helper.get();
|
||||
|
||||
// Conveyor entangled structure
|
||||
auto prim_conv = internal.connect(address).then([this, hlp_ptr, addr = address.address()](
|
||||
Own<IoStream> stream) -> ErrorOr<void> {
|
||||
IoStream* inner_stream = stream.get();
|
||||
own<io_stream> stream) -> error_or<void> {
|
||||
io_stream* inner_stream = stream.get();
|
||||
auto tls_stream = heap<TlsIoStream>(std::move(stream));
|
||||
|
||||
auto &session = tls_stream->session();
|
||||
|
@ -193,7 +193,7 @@ Conveyor<Own<IoStream>> TlsNetwork::connect(NetworkAddress& address) {
|
|||
hlp_ptr->setupTurn();
|
||||
hlp_ptr->turn();
|
||||
|
||||
return Void{};
|
||||
return void_t{};
|
||||
});
|
||||
|
||||
helper->connection_sink = prim_conv.sink();
|
||||
|
@ -201,20 +201,20 @@ Conveyor<Own<IoStream>> TlsNetwork::connect(NetworkAddress& address) {
|
|||
return caf.conveyor.attach(std::move(helper));
|
||||
}
|
||||
|
||||
Own<Datagram> TlsNetwork::datagram(NetworkAddress& address){
|
||||
own<datagram> TlsNetwork::datagram(network_address& address){
|
||||
///@unimplemented
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static ssize_t forst_tls_push_func(gnutls_transport_ptr_t p, const void *data,
|
||||
size_t size) {
|
||||
IoStream *stream = reinterpret_cast<IoStream *>(p);
|
||||
io_stream *stream = reinterpret_cast<io_stream *>(p);
|
||||
if (!stream) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ErrorOr<size_t> length = stream->write(data, size);
|
||||
if (length.isError() || !length.isValue()) {
|
||||
error_or<size_t> length = stream->write(data, size);
|
||||
if (length.is_error() || !length.is_value()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -222,29 +222,29 @@ static ssize_t forst_tls_push_func(gnutls_transport_ptr_t p, const void *data,
|
|||
}
|
||||
|
||||
static ssize_t forst_tls_pull_func(gnutls_transport_ptr_t p, void *data, size_t size) {
|
||||
IoStream *stream = reinterpret_cast<IoStream *>(p);
|
||||
io_stream *stream = reinterpret_cast<io_stream *>(p);
|
||||
if (!stream) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ErrorOr<size_t> length = stream->read(data, size);
|
||||
if (length.isError() || !length.isValue()) {
|
||||
error_or<size_t> length = stream->read(data, size);
|
||||
if (length.is_error() || !length.is_value()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return static_cast<ssize_t>(length.value());
|
||||
}
|
||||
|
||||
TlsNetwork::TlsNetwork(Network &network) : internal{network} {}
|
||||
TlsNetwork::TlsNetwork(Tls& tls_, network &network) : tls{tls_},internal{network} {}
|
||||
|
||||
Conveyor<Own<NetworkAddress>> TlsNetwork::parseAddress(const std::string &addr,
|
||||
conveyor<own<network_address>> TlsNetwork::resolve_address(const std::string &addr,
|
||||
uint16_t port) {
|
||||
/// @todo tls server name needed. Check validity. Won't matter later on, because gnutls should fail anyway. But
|
||||
/// it's better to find the error source sooner rather than later
|
||||
return internal.parseAddress(addr, port);
|
||||
return internal.resolve_address(addr, port);
|
||||
}
|
||||
|
||||
std::optional<Own<TlsNetwork>> setupTlsNetwork(Network &network) {
|
||||
std::optional<own<TlsNetwork>> setupTlsNetwork(network &network) {
|
||||
return std::nullopt;
|
||||
}
|
||||
} // namespace saw
|
||||
|
|
|
@ -7,49 +7,64 @@
|
|||
#include <variant>
|
||||
|
||||
namespace saw {
|
||||
class Tls;
|
||||
|
||||
class TlsServer final : public server {
|
||||
private:
|
||||
own<server> internal;
|
||||
|
||||
public:
|
||||
TlsServer(own<server> srv);
|
||||
|
||||
conveyor<own<io_stream>> accept() override;
|
||||
};
|
||||
|
||||
class TlsNetwork final : public network {
|
||||
private:
|
||||
Tls& tls;
|
||||
network &internal;
|
||||
public:
|
||||
TlsNetwork(Tls& tls_, network &network_);
|
||||
|
||||
conveyor<own<network_address>> resolve_address(const std::string &addr, uint16_t port = 0) override;
|
||||
|
||||
own<server> listen(network_address& address) override;
|
||||
|
||||
conveyor<own<io_stream>> connect(network_address& address) override;
|
||||
|
||||
own<class datagram> datagram(network_address& address) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tls context class.
|
||||
* Provides tls network class which ensures the usage of tls encrypted connections
|
||||
*/
|
||||
class Tls {
|
||||
private:
|
||||
class Impl;
|
||||
Own<Impl> impl;
|
||||
|
||||
own<Impl> impl;
|
||||
public:
|
||||
Tls();
|
||||
~Tls();
|
||||
|
||||
class Options {
|
||||
public:
|
||||
struct Version {
|
||||
struct Tls_1_0{};
|
||||
struct Tls_1_1{};
|
||||
struct Tls_1_2{};
|
||||
};
|
||||
|
||||
struct Options {
|
||||
public:
|
||||
Version version;
|
||||
};
|
||||
|
||||
network& tlsNetwork();
|
||||
|
||||
Impl &getImpl();
|
||||
};
|
||||
|
||||
class TlsServer final : public Server {
|
||||
private:
|
||||
Own<Server> internal;
|
||||
|
||||
public:
|
||||
TlsServer(Own<Server> srv);
|
||||
|
||||
Conveyor<Own<IoStream>> accept() override;
|
||||
Options options;
|
||||
};
|
||||
|
||||
class TlsNetwork final : public Network {
|
||||
private:
|
||||
Tls tls;
|
||||
Network &internal;
|
||||
|
||||
public:
|
||||
TlsNetwork(Network &network);
|
||||
|
||||
Conveyor<Own<NetworkAddress>> parseAddress(const std::string &addr, uint16_t port = 0) override;
|
||||
|
||||
Own<Server> listen(NetworkAddress& address) override;
|
||||
|
||||
Conveyor<Own<IoStream>> connect(NetworkAddress& address) override;
|
||||
|
||||
Own<Datagram> datagram(NetworkAddress& address) override;
|
||||
};
|
||||
|
||||
std::optional<Own<TlsNetwork>> setupTlsNetwork(Network &network);
|
||||
std::optional<own<TlsNetwork>> setupTlsNetwork(network &network);
|
||||
|
||||
} // namespace saw
|
||||
|
|
160
test/async.cpp
160
test/async.cpp
|
@ -6,38 +6,38 @@ namespace {
|
|||
SAW_TEST("Async Immediate"){
|
||||
using namespace saw;
|
||||
|
||||
EventLoop event_loop;
|
||||
WaitScope wait_scope{event_loop};
|
||||
event_loop event_loop;
|
||||
wait_scope wait_scope{event_loop};
|
||||
|
||||
Conveyor<size_t> number{5};
|
||||
conveyor<size_t> number{5};
|
||||
|
||||
Conveyor<bool> is_number = number.then([](size_t val){
|
||||
conveyor<bool> is_number = number.then([](size_t val){
|
||||
return val == 5;
|
||||
});
|
||||
|
||||
wait_scope.poll();
|
||||
|
||||
ErrorOr<bool> error_or_number = is_number.take();
|
||||
error_or<bool> error_or_number = is_number.take();
|
||||
|
||||
SAW_EXPECT(!error_or_number.isError(), error_or_number.error().message());
|
||||
SAW_EXPECT(error_or_number.isValue(), "Return is not a value");
|
||||
SAW_EXPECT(!error_or_number.is_error(), error_or_number.error().message());
|
||||
SAW_EXPECT(error_or_number.is_value(), "Return is not a value");
|
||||
SAW_EXPECT(error_or_number.value(), "Value is not 5");
|
||||
}
|
||||
|
||||
SAW_TEST("Async Adapt"){
|
||||
using namespace saw;
|
||||
|
||||
EventLoop event_loop;
|
||||
WaitScope wait_scope{event_loop};
|
||||
event_loop event_loop;
|
||||
wait_scope wait_scope{event_loop};
|
||||
|
||||
auto feeder_conveyor = newConveyorAndFeeder<size_t>();
|
||||
auto feeder_conveyor = new_conveyor_and_feeder<size_t>();
|
||||
|
||||
feeder_conveyor.feeder->feed(5);
|
||||
|
||||
ErrorOr<size_t> foo = feeder_conveyor.conveyor.take();
|
||||
error_or<size_t> foo = feeder_conveyor.conveyor.take();
|
||||
|
||||
SAW_EXPECT(!foo.isError(), foo.error().message());
|
||||
SAW_EXPECT(foo.isValue(), "Return is not a value");
|
||||
SAW_EXPECT(!foo.is_error(), foo.error().message());
|
||||
SAW_EXPECT(foo.is_value(), "Return is not a value");
|
||||
SAW_EXPECT(foo.value() == 5, "Values not 5, but " + std::to_string(foo.value()));
|
||||
}
|
||||
|
||||
|
@ -45,72 +45,72 @@ SAW_TEST("Async Adapt"){
|
|||
SAW_TEST("Async Adapt Multiple"){
|
||||
using namespace saw;
|
||||
|
||||
EventLoop event_loop;
|
||||
WaitScope wait_scope{event_loop};
|
||||
event_loop event_loop;
|
||||
wait_scope wait_scope{event_loop};
|
||||
|
||||
auto feeder_conveyor = newConveyorAndFeeder<size_t>();
|
||||
auto feeder_conveyor = new_conveyor_and_feeder<size_t>();
|
||||
|
||||
feeder_conveyor.feeder->feed(5);
|
||||
|
||||
ErrorOr<size_t> foo = feeder_conveyor.conveyor.take();
|
||||
error_or<size_t> foo = feeder_conveyor.conveyor.take();
|
||||
|
||||
SAW_EXPECT(!foo.isError(), foo.error().message());
|
||||
SAW_EXPECT(foo.isValue(), "Return is not a value");
|
||||
SAW_EXPECT(!foo.is_error(), foo.error().message());
|
||||
SAW_EXPECT(foo.is_value(), "Return is not a value");
|
||||
SAW_EXPECT(foo.value() == 5, "Values not 5, but " + std::to_string(foo.value()));
|
||||
|
||||
feeder_conveyor.feeder->feed(10);
|
||||
|
||||
ErrorOr<size_t> bar = feeder_conveyor.conveyor.take();
|
||||
error_or<size_t> bar = feeder_conveyor.conveyor.take();
|
||||
|
||||
SAW_EXPECT(!foo.isError(), bar.error().message());
|
||||
SAW_EXPECT(bar.isValue(), "Return is not a value");
|
||||
SAW_EXPECT(!foo.is_error(), bar.error().message());
|
||||
SAW_EXPECT(bar.is_value(), "Return is not a value");
|
||||
SAW_EXPECT(bar.value() == 10, "Values not 10, but " + std::to_string(bar.value()));
|
||||
|
||||
feeder_conveyor.feeder->feed(2);
|
||||
feeder_conveyor.feeder->feed(4234);
|
||||
|
||||
ErrorOr<size_t> a = feeder_conveyor.conveyor.take();
|
||||
ErrorOr<size_t> b = feeder_conveyor.conveyor.take();
|
||||
error_or<size_t> a = feeder_conveyor.conveyor.take();
|
||||
error_or<size_t> b = feeder_conveyor.conveyor.take();
|
||||
|
||||
SAW_EXPECT(!foo.isError(), a.error().message());
|
||||
SAW_EXPECT(a.isValue(), "Return is not a value");
|
||||
SAW_EXPECT(!foo.is_error(), a.error().message());
|
||||
SAW_EXPECT(a.is_value(), "Return is not a value");
|
||||
SAW_EXPECT(a.value() == 2, "Values not 2, but " + std::to_string(a.value()));
|
||||
|
||||
SAW_EXPECT(!foo.isError(), b.error().message());
|
||||
SAW_EXPECT(b.isValue(), "Return is not a value");
|
||||
SAW_EXPECT(!foo.is_error(), b.error().message());
|
||||
SAW_EXPECT(b.is_value(), "Return is not a value");
|
||||
SAW_EXPECT(b.value() == 4234, "Values not 4234, but " + std::to_string(b.value()));
|
||||
}
|
||||
|
||||
SAW_TEST("Async Conversion"){
|
||||
using namespace saw;
|
||||
|
||||
EventLoop event_loop;
|
||||
WaitScope wait_scope{event_loop};
|
||||
event_loop event_loop;
|
||||
wait_scope wait_scope{event_loop};
|
||||
|
||||
auto feeder_conveyor = newConveyorAndFeeder<size_t>();
|
||||
auto feeder_conveyor = new_conveyor_and_feeder<size_t>();
|
||||
|
||||
Conveyor<std::string> string_conveyor = feeder_conveyor.conveyor.then([](size_t foo){
|
||||
conveyor<std::string> string_conveyor = feeder_conveyor.conveyor.then([](size_t foo){
|
||||
return std::to_string(foo);
|
||||
});
|
||||
|
||||
feeder_conveyor.feeder->feed(10);
|
||||
|
||||
ErrorOr<std::string> foo = string_conveyor.take();
|
||||
error_or<std::string> foo = string_conveyor.take();
|
||||
|
||||
SAW_EXPECT(!foo.isError(), foo.error().message());
|
||||
SAW_EXPECT(foo.isValue(), "Return is not a value");
|
||||
SAW_EXPECT(!foo.is_error(), foo.error().message());
|
||||
SAW_EXPECT(foo.is_value(), "Return is not a value");
|
||||
SAW_EXPECT(foo.value() == std::to_string(10), "Values is not 10, but " + foo.value());
|
||||
}
|
||||
|
||||
SAW_TEST("Async Conversion Multistep"){
|
||||
using namespace saw;
|
||||
|
||||
EventLoop event_loop;
|
||||
WaitScope wait_scope{event_loop};
|
||||
event_loop event_loop;
|
||||
wait_scope wait_scope{event_loop};
|
||||
|
||||
auto feeder_conveyor = newConveyorAndFeeder<size_t>();
|
||||
auto feeder_conveyor = new_conveyor_and_feeder<size_t>();
|
||||
|
||||
Conveyor<bool> conveyor = feeder_conveyor.conveyor.then([](size_t foo){
|
||||
conveyor<bool> conveyor = feeder_conveyor.conveyor.then([](size_t foo){
|
||||
return std::to_string(foo);
|
||||
}).then([](const std::string& value){
|
||||
return value != "10";
|
||||
|
@ -120,29 +120,29 @@ SAW_TEST("Async Conversion Multistep"){
|
|||
|
||||
feeder_conveyor.feeder->feed(10);
|
||||
|
||||
ErrorOr<bool> foo = conveyor.take();
|
||||
error_or<bool> foo = conveyor.take();
|
||||
|
||||
SAW_EXPECT(!foo.isError(), foo.error().message());
|
||||
SAW_EXPECT(foo.isValue(), "Return is not a value");
|
||||
SAW_EXPECT(!foo.is_error(), foo.error().message());
|
||||
SAW_EXPECT(foo.is_value(), "Return is not a value");
|
||||
SAW_EXPECT(foo.value(), "Values is not true");
|
||||
}
|
||||
|
||||
SAW_TEST("Async Scheduling"){
|
||||
using namespace saw;
|
||||
|
||||
EventLoop event_loop;
|
||||
WaitScope wait_scope{event_loop};
|
||||
event_loop event_loop;
|
||||
wait_scope wait_scope{event_loop};
|
||||
|
||||
auto feeder_conveyor = newConveyorAndFeeder<size_t>();
|
||||
auto feeder_conveyor = new_conveyor_and_feeder<size_t>();
|
||||
|
||||
/*
|
||||
* Attach node test data
|
||||
*/
|
||||
Own<size_t> counter = heap<size_t>();
|
||||
own<size_t> counter = heap<size_t>();
|
||||
size_t* ctr_ptr = counter.get();
|
||||
*ctr_ptr = 0;
|
||||
|
||||
Conveyor<std::string> string_conveyor = feeder_conveyor.conveyor
|
||||
conveyor<std::string> string_conveyor = feeder_conveyor.conveyor
|
||||
.then([ctr_ptr](size_t foo){
|
||||
return std::to_string(foo + ++(*ctr_ptr));
|
||||
})
|
||||
|
@ -163,34 +163,34 @@ SAW_TEST("Async Scheduling"){
|
|||
|
||||
wait_scope.poll();
|
||||
|
||||
ErrorOr<std::string> foo_10 = string_conveyor.take();
|
||||
error_or<std::string> foo_10 = string_conveyor.take();
|
||||
|
||||
SAW_EXPECT(!foo_10.isError(), foo_10.error().message());
|
||||
SAW_EXPECT(foo_10.isValue(), "Return is not a value");
|
||||
SAW_EXPECT(!foo_10.is_error(), foo_10.error().message());
|
||||
SAW_EXPECT(foo_10.is_value(), "Return is not a value");
|
||||
SAW_EXPECT(foo_10.value() == (std::string{"pre"} + std::to_string(11) + std::string{"post"}), "Values is not pre11post, but " + foo_10.value());
|
||||
|
||||
ErrorOr<std::string> foo_20 = string_conveyor.take();
|
||||
error_or<std::string> foo_20 = string_conveyor.take();
|
||||
|
||||
SAW_EXPECT(!foo_20.isError(), foo_20.error().message());
|
||||
SAW_EXPECT(foo_20.isValue(), "Return is not a value");
|
||||
SAW_EXPECT(!foo_20.is_error(), foo_20.error().message());
|
||||
SAW_EXPECT(foo_20.is_value(), "Return is not a value");
|
||||
SAW_EXPECT(foo_20.value() == (std::string{"pre"} + std::to_string(22) + std::string{"post"}), "Values is not pre22post, but " + foo_20.value());
|
||||
|
||||
ErrorOr<std::string> foo_30 = string_conveyor.take();
|
||||
error_or<std::string> foo_30 = string_conveyor.take();
|
||||
|
||||
SAW_EXPECT(!foo_30.isError(), foo_30.error().message());
|
||||
SAW_EXPECT(foo_30.isValue(), "Return is not a value");
|
||||
SAW_EXPECT(!foo_30.is_error(), foo_30.error().message());
|
||||
SAW_EXPECT(foo_30.is_value(), "Return is not a value");
|
||||
SAW_EXPECT(foo_30.value() == (std::string{"pre"} + std::to_string(33) + std::string{"post"}), "Values is not pre33post, but " + foo_30.value());
|
||||
}
|
||||
|
||||
SAW_TEST("Async Detach"){
|
||||
using namespace saw;
|
||||
|
||||
EventLoop event_loop;
|
||||
WaitScope wait_scope{event_loop};
|
||||
event_loop event_loop;
|
||||
wait_scope wait_scope{event_loop};
|
||||
|
||||
int num = 0;
|
||||
|
||||
Conveyor<int>{10}.then([&num](int bar){
|
||||
conveyor<int>{10}.then([&num](int bar){
|
||||
num = bar;
|
||||
}).detach();
|
||||
|
||||
|
@ -202,14 +202,14 @@ SAW_TEST("Async Detach"){
|
|||
SAW_TEST("Async Merge"){
|
||||
using namespace saw;
|
||||
|
||||
EventLoop event_loop;
|
||||
WaitScope wait_scope{event_loop};
|
||||
event_loop event_loop;
|
||||
wait_scope wait_scope{event_loop};
|
||||
|
||||
auto cam = Conveyor<int>{10}.merge();
|
||||
auto cam = conveyor<int>{10}.merge();
|
||||
|
||||
cam.second.attach(Conveyor<int>{11});
|
||||
cam.second.attach(conveyor<int>{11});
|
||||
|
||||
cam.second.attach(Conveyor<int>{14});
|
||||
cam.second.attach(conveyor<int>{14});
|
||||
|
||||
size_t elements_passed = 0;
|
||||
bool wrong_value = false;
|
||||
|
@ -227,4 +227,36 @@ SAW_TEST("Async Merge"){
|
|||
SAW_EXPECT(!wrong_value, std::string{"Expected values 10 or 11"});
|
||||
SAW_EXPECT(elements_passed == 3, std::string{"Expected 2 passed elements, got only "} + std::to_string(elements_passed));
|
||||
}
|
||||
|
||||
SAW_TEST("Async Connect"){
|
||||
using namespace saw;
|
||||
|
||||
event_loop event_loop;
|
||||
wait_scope wait_scope{event_loop};
|
||||
|
||||
auto caf1 = new_conveyor_and_feeder<int>();
|
||||
|
||||
auto caf2 = new_conveyor_and_feeder<float>();
|
||||
|
||||
bool val_passed = false;
|
||||
auto conveyor1 = caf1.conveyor.then([&val_passed](int val) -> float{
|
||||
val_passed = (val == 10);
|
||||
|
||||
return static_cast<float>(val);
|
||||
}).buffer(1);
|
||||
|
||||
bool val_passed_2 = false;
|
||||
auto sink = caf2.conveyor.then([&val_passed_2](float val) {
|
||||
val_passed_2 = (val == 10.f);
|
||||
}).sink();
|
||||
|
||||
caf1.feeder->feed(10);
|
||||
|
||||
caf2.feeder->swap(std::move(conveyor1));
|
||||
|
||||
wait_scope.poll();
|
||||
|
||||
SAW_EXPECT(val_passed, std::string{"Expected value 10"});
|
||||
SAW_EXPECT(val_passed_2, std::string{"Expected value 10.f"});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ using TestTuple = schema::Tuple<schema::UInt32, schema::String>;
|
|||
SAW_TEST("Message Tuple"){
|
||||
std::string test_string_1 = "banana";
|
||||
|
||||
auto root = saw::heapMessageRoot<TestTuple>();
|
||||
auto root = saw::new_heap_message_root<TestTuple>();
|
||||
auto builder = root.build();
|
||||
auto uint = builder.init<0>();
|
||||
uint.set(10);
|
||||
|
@ -36,7 +36,7 @@ SAW_TEST("Message Tuple nested"){
|
|||
std::string test_string_1 = "banana";
|
||||
std::string test_string_2 = "bat";
|
||||
|
||||
auto root = saw::heapMessageRoot<NestedTestTuple>();
|
||||
auto root = saw::new_heap_message_root<NestedTestTuple>();
|
||||
auto builder = root.build();
|
||||
auto inner_list = builder.init<0>();
|
||||
auto uint = inner_list.init<0>();
|
||||
|
@ -64,7 +64,7 @@ using TestStruct = schema::Struct<
|
|||
|
||||
SAW_TEST("Message Struct"){
|
||||
std::string test_string = "foo";
|
||||
auto root = saw::heapMessageRoot<TestStruct>();
|
||||
auto root = saw::new_heap_message_root<TestStruct>();
|
||||
auto builder = root.build();
|
||||
auto uint = builder.init<"test_uint">();
|
||||
uint.set(23);
|
||||
|
@ -88,7 +88,7 @@ SAW_TEST("Message Struct"){
|
|||
|
||||
using TestArray = schema::Array<schema::UInt32>;
|
||||
|
||||
void arrayCheck(saw::Message<TestArray>::Builder builder){
|
||||
void arrayCheck(saw::message<TestArray>::builder builder){
|
||||
auto one = builder.init(0);
|
||||
auto two = builder.init(1);
|
||||
auto three = builder.init(2);
|
||||
|
@ -97,13 +97,13 @@ void arrayCheck(saw::Message<TestArray>::Builder builder){
|
|||
two.set(45);
|
||||
three.set(1230);
|
||||
|
||||
auto reader = builder.asReader();
|
||||
auto reader = builder.as_reader();
|
||||
|
||||
SAW_EXPECT(reader.get(0).get() == 24 && reader.get(1).get() == 45 && reader.get(2).get(), "Wrong values");
|
||||
}
|
||||
|
||||
SAW_TEST("Message Array"){
|
||||
auto root = saw::heapMessageRoot<TestArray>();
|
||||
auto root = saw::new_heap_message_root<TestArray>();
|
||||
|
||||
auto builder = root.build(3);
|
||||
|
||||
|
@ -115,7 +115,7 @@ using TestArrayStruct = schema::Struct<
|
|||
>;
|
||||
|
||||
SAW_TEST("Message Array in Struct"){
|
||||
auto root = saw::heapMessageRoot<TestArrayStruct>();
|
||||
auto root = saw::new_heap_message_root<TestArrayStruct>();
|
||||
|
||||
auto builder = root.build();
|
||||
|
||||
|
|
|
@ -27,26 +27,26 @@ SAW_TEST("Primitive Encoding"){
|
|||
using namespace saw;
|
||||
uint32_t value = 5;
|
||||
|
||||
auto root = heapMessageRoot<TestSize>();
|
||||
auto root = new_heap_message_root<TestSize>();
|
||||
auto builder = root.build();
|
||||
|
||||
builder.set(value);
|
||||
|
||||
RingBuffer temp_buffer;
|
||||
ProtoKelCodec codec;
|
||||
ring_buffer temp_buffer;
|
||||
proto_kel_codec codec;
|
||||
|
||||
Error error = codec.encode<TestSize>(root.read(), temp_buffer);
|
||||
error err = codec.encode<TestSize>(root.read(), temp_buffer);
|
||||
|
||||
SAW_EXPECT(!error.failed(), error.message());
|
||||
SAW_EXPECT(temp_buffer.readCompositeLength() == (sizeof(value)+sizeof(msg_packet_length_t)), "Bad Size: " + std::to_string(temp_buffer.readCompositeLength()));
|
||||
constexpr size_t pkt_shift = sizeof(msg_packet_length_t);
|
||||
SAW_EXPECT(!err.failed(), err.message());
|
||||
SAW_EXPECT(temp_buffer.read_composite_length() == (sizeof(value)+sizeof(proto_kel_codec::PacketLengthT)), "Bad Size: " + std::to_string(temp_buffer.read_composite_length()));
|
||||
constexpr size_t pkt_shift = sizeof(proto_kel_codec::PacketLengthT);
|
||||
SAW_EXPECT(temp_buffer[pkt_shift] == 5 && temp_buffer[pkt_shift+1] == 0 && temp_buffer[pkt_shift+2] == 0 && temp_buffer[pkt_shift+3] == 0, "Wrong encoded values");
|
||||
}
|
||||
|
||||
SAW_TEST("List Encoding"){
|
||||
using namespace saw;
|
||||
|
||||
auto root = heapMessageRoot<TestTuple>();
|
||||
auto root = new_heap_message_root<TestTuple>();
|
||||
auto builder = root.build();
|
||||
|
||||
auto first = builder.init<0>();
|
||||
|
@ -54,20 +54,20 @@ SAW_TEST("List Encoding"){
|
|||
auto second = builder.init<1>();
|
||||
second.set(43871);
|
||||
|
||||
RingBuffer buffer;
|
||||
ProtoKelCodec codec;
|
||||
ring_buffer buffer;
|
||||
proto_kel_codec codec;
|
||||
|
||||
Error error = codec.encode<TestTuple>(root.read(), buffer);
|
||||
error err = codec.encode<TestTuple>(root.read(), buffer);
|
||||
|
||||
SAW_EXPECT(!error.failed(), error.message());
|
||||
SAW_EXPECT(buffer.readCompositeLength() == 14, "Bad Size: " + std::to_string(buffer.readCompositeLength()));
|
||||
SAW_EXPECT("06 00 00 00\n00 00 00 00\nbf 94 20 00\n5f ab" == buffer.toHex(), "Not equal encoding\n"+buffer.toHex());
|
||||
SAW_EXPECT(!err.failed(), err.message());
|
||||
SAW_EXPECT(buffer.read_composite_length() == 14, "Bad Size: " + std::to_string(buffer.read_composite_length()));
|
||||
SAW_EXPECT("06 00 00 00\n00 00 00 00\nbf 94 20 00\n5f ab" == buffer.to_hex(), "Not equal encoding\n"+buffer.to_hex());
|
||||
}
|
||||
|
||||
SAW_TEST("Struct Encoding"){
|
||||
using namespace saw;
|
||||
|
||||
auto root = heapMessageRoot<TestStruct>();
|
||||
auto root = new_heap_message_root<TestStruct>();
|
||||
auto builder = root.build();
|
||||
|
||||
auto test_uint = builder.init<"test_uint">();
|
||||
|
@ -80,52 +80,52 @@ SAW_TEST("Struct Encoding"){
|
|||
auto string_name = builder.init<"test_name">();
|
||||
string_name.set("test_name");
|
||||
|
||||
RingBuffer buffer;
|
||||
ProtoKelCodec codec;
|
||||
ring_buffer buffer;
|
||||
proto_kel_codec codec;
|
||||
|
||||
Error error = codec.encode<TestStruct>(builder.asReader(), buffer);
|
||||
error err = codec.encode<TestStruct>(builder.as_reader(), buffer);
|
||||
|
||||
SAW_EXPECT(!error.failed(), error.message());
|
||||
SAW_EXPECT(buffer.readCompositeLength() == 40, "Bad Size: " + std::to_string(buffer.readCompositeLength()));
|
||||
SAW_EXPECT(!err.failed(), err.message());
|
||||
SAW_EXPECT(buffer.read_composite_length() == 40, "Bad Size: " + std::to_string(buffer.read_composite_length()));
|
||||
SAW_EXPECT("20 00 00 00\n00 00 00 00\n17 00 00 00\n03 00 00 00\n00 00 00 00\n66 6f 6f 09\n00 00 00 00\n00 00 00 74\n65 73 74 5f\n6e 61 6d 65"
|
||||
== buffer.toHex(), "Not equal encoding:\n"+buffer.toHex());
|
||||
== buffer.to_hex(), "Not equal encoding:\n"+buffer.to_hex());
|
||||
}
|
||||
|
||||
SAW_TEST("Union Encoding"){
|
||||
using namespace saw;
|
||||
{
|
||||
auto root = heapMessageRoot<TestUnion>();
|
||||
auto root = new_heap_message_root<TestUnion>();
|
||||
auto builder = root.build();
|
||||
|
||||
auto test_uint = builder.init<"test_uint">();
|
||||
test_uint.set(23);
|
||||
|
||||
RingBuffer buffer;
|
||||
ProtoKelCodec codec;
|
||||
ring_buffer buffer;
|
||||
proto_kel_codec codec;
|
||||
|
||||
Error error = codec.encode<TestUnion>(builder.asReader(), buffer);
|
||||
error err = codec.encode<TestUnion>(builder.as_reader(), buffer);
|
||||
|
||||
SAW_EXPECT(!error.failed(), error.message());
|
||||
SAW_EXPECT(buffer.readCompositeLength() == 16, "Bad Size: " + std::to_string(buffer.readCompositeLength()));
|
||||
SAW_EXPECT(!err.failed(), err.message());
|
||||
SAW_EXPECT(buffer.read_composite_length() == 16, "Bad Size: " + std::to_string(buffer.read_composite_length()));
|
||||
SAW_EXPECT("08 00 00 00\n00 00 00 00\n00 00 00 00\n17 00 00 00"
|
||||
== buffer.toHex(), "Not equal encoding:\n"+buffer.toHex());
|
||||
== buffer.to_hex(), "Not equal encoding:\n"+buffer.to_hex());
|
||||
}
|
||||
{
|
||||
auto root = heapMessageRoot<TestUnion>();
|
||||
auto root = new_heap_message_root<TestUnion>();
|
||||
auto builder = root.build();
|
||||
|
||||
auto test_string = builder.init<"test_string">();
|
||||
test_string.set("foo");
|
||||
|
||||
RingBuffer buffer;
|
||||
ProtoKelCodec codec;
|
||||
ring_buffer buffer;
|
||||
proto_kel_codec codec;
|
||||
|
||||
Error error = codec.encode<TestUnion>(builder.asReader(), buffer);
|
||||
error err = codec.encode<TestUnion>(builder.as_reader(), buffer);
|
||||
|
||||
SAW_EXPECT(!error.failed(), error.message());
|
||||
SAW_EXPECT(buffer.readCompositeLength() == 23, "Bad Size: " + std::to_string(buffer.readCompositeLength()));
|
||||
SAW_EXPECT(!err.failed(), err.message());
|
||||
SAW_EXPECT(buffer.read_composite_length() == 23, "Bad Size: " + std::to_string(buffer.read_composite_length()));
|
||||
SAW_EXPECT("0f 00 00 00\n00 00 00 00\n01 00 00 00\n03 00 00 00\n00 00 00 00\n66 6f 6f"
|
||||
== buffer.toHex(), "Not equal encoding:\n"+buffer.toHex());
|
||||
== buffer.to_hex(), "Not equal encoding:\n"+buffer.to_hex());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,18 +133,18 @@ SAW_TEST("Tuple Decoding"){
|
|||
using namespace saw;
|
||||
const uint8_t buffer_raw[] = {0x06, 0, 0, 0, 0, 0, 0, 0, 0xbf, 0x94, 0x20, 0x00, 0x5f, 0xab};
|
||||
|
||||
RingBuffer buffer;
|
||||
ring_buffer buffer;
|
||||
buffer.push(*buffer_raw, sizeof(buffer_raw));
|
||||
|
||||
ProtoKelCodec codec;
|
||||
proto_kel_codec codec;
|
||||
|
||||
auto root = heapMessageRoot<TestTuple>();
|
||||
auto root = new_heap_message_root<TestTuple>();
|
||||
auto builder = root.build();
|
||||
|
||||
Error error = codec.decode<TestTuple>(builder, buffer);
|
||||
SAW_EXPECT(!error.failed(), error.message());
|
||||
error err = codec.decode<TestTuple>(builder, buffer);
|
||||
SAW_EXPECT(!err.failed(), err.message());
|
||||
|
||||
auto reader = builder.asReader();
|
||||
auto reader = builder.as_reader();
|
||||
|
||||
auto first = reader.get<0>();
|
||||
auto second = reader.get<1>();
|
||||
|
@ -156,22 +156,22 @@ SAW_TEST("Struct Decoding"){
|
|||
using namespace saw;
|
||||
const uint8_t buffer_raw[] = {0x20,0,0,0,0,0,0,0,0x17,0,0,0,0x03,0,0,0,0,0,0,0,0x66,0x6f,0x6f,0x09,0,0,0,0,0,0,0,0x74,0x65,0x73,0x74,0x5f,0x6e,0x61,0x6d,0x65};
|
||||
|
||||
RingBuffer buffer;
|
||||
ring_buffer buffer;
|
||||
buffer.push(*buffer_raw, sizeof(buffer_raw));
|
||||
|
||||
ProtoKelCodec codec;
|
||||
proto_kel_codec codec;
|
||||
|
||||
auto root = heapMessageRoot<TestStruct>();
|
||||
auto root = new_heap_message_root<TestStruct>();
|
||||
auto builder = root.build();
|
||||
|
||||
Error error = codec.decode<TestStruct>(builder, buffer);
|
||||
auto reader = builder.asReader();
|
||||
error err = codec.decode<TestStruct>(builder, buffer);
|
||||
auto reader = builder.as_reader();
|
||||
|
||||
auto foo_string = reader.get<"test_string">();
|
||||
auto test_uint = reader.get<"test_uint">();
|
||||
auto test_name = reader.get<"test_name">();
|
||||
|
||||
SAW_EXPECT(!error.failed(), error.message());
|
||||
SAW_EXPECT(!err.failed(), err.message());
|
||||
SAW_EXPECT(foo_string.get() == "foo" && test_uint.get() == 23 && test_name.get() == "test_name", "Values not correctly decoded");
|
||||
}
|
||||
|
||||
|
@ -179,19 +179,19 @@ SAW_TEST("Union Decoding"){
|
|||
using namespace saw;
|
||||
const uint8_t buffer_raw[] = {0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x6f};
|
||||
|
||||
RingBuffer buffer;
|
||||
ring_buffer buffer;
|
||||
buffer.push(*buffer_raw, sizeof(buffer_raw));
|
||||
|
||||
ProtoKelCodec codec;
|
||||
proto_kel_codec codec;
|
||||
|
||||
auto root = heapMessageRoot<TestUnion>();
|
||||
auto root = new_heap_message_root<TestUnion>();
|
||||
auto builder = root.build();
|
||||
auto reader = builder.asReader();
|
||||
auto reader = builder.as_reader();
|
||||
|
||||
Error error = codec.decode<TestUnion>(builder, buffer);
|
||||
error err = codec.decode<TestUnion>(builder, buffer);
|
||||
|
||||
SAW_EXPECT(!error.failed(), error.message());
|
||||
SAW_EXPECT(reader.hasAlternative<"test_string">(), "Wrong union value");
|
||||
SAW_EXPECT(!err.failed(), err.message());
|
||||
SAW_EXPECT(reader.has_alternative<"test_string">(), "Wrong union value");
|
||||
auto str_rd = reader.get<"test_string">();
|
||||
SAW_EXPECT(str_rd.get() == "foo", "Wrong value: " + std::string{str_rd.get()});
|
||||
}
|
||||
|
@ -203,8 +203,8 @@ using TestArrayStruct = schema::Array<
|
|||
SAW_TEST("Array Encoding"){
|
||||
using namespace saw;
|
||||
|
||||
ProtoKelCodec codec;
|
||||
auto root = heapMessageRoot<TestArrayStruct>();
|
||||
proto_kel_codec codec;
|
||||
auto root = new_heap_message_root<TestArrayStruct>();
|
||||
auto builder = root.build(2);
|
||||
|
||||
auto one = builder.init(0);
|
||||
|
@ -218,10 +218,10 @@ SAW_TEST("Array Encoding"){
|
|||
two.init<"test_string">().set("bar");
|
||||
two.init<"test_name">().set("Bravo");
|
||||
|
||||
RingBuffer buffer;
|
||||
ring_buffer buffer;
|
||||
|
||||
Error error = codec.encode<TestArrayStruct>(root.read(), buffer);
|
||||
error err = codec.encode<TestArrayStruct>(root.read(), buffer);
|
||||
|
||||
SAW_EXPECT(!error.failed(), "Error occured");
|
||||
SAW_EXPECT(!err.failed(), "Error occured");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <forstio/error.h>
|
||||
|
||||
namespace saw {
|
||||
namespace tools {
|
||||
/// @todo implement from test code base
|
||||
template<typename Schema, typename Codec>
|
||||
Error cliMessageAnalyzer(Codec& codec, int argc, char** argv){
|
||||
// Parse Args
|
||||
|
||||
// If read is required
|
||||
// Read file into buffer
|
||||
|
||||
// If read is required
|
||||
// Decode file
|
||||
|
||||
// Execute commands
|
||||
|
||||
// If write is required
|
||||
// Encode into buffer
|
||||
// Write to file from buffer
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue