tls preparations

This commit is contained in:
keldu.magnus 2021-05-09 15:19:09 +02:00
commit 7ce304aad2
39 changed files with 2823 additions and 1759 deletions

View File

@ -37,3 +37,4 @@ Currently no examples except in test.
* Windows/Mac Support
* Buffer flexibility
* Multithreaded conveyor communication
* Logger implementation

View File

@ -29,7 +29,7 @@ def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, ta
sources.append( self.StaticObject( target=target_name, source=path ) )
pass
env=Environment(CPPPATH=['#source','#','#driver'],
env=Environment(CPPPATH=['#source/kelgin','#source','#','#driver'],
CXX='c++',
CPPDEFINES=['GIN_UNIX'],
CXXFLAGS=['-std=c++17','-g','-Wall','-Wextra'],
@ -41,7 +41,7 @@ env.headers = []
env.objects = []
Export('env')
SConscript('source/SConscript')
SConscript('source/kelgin/SConscript')
SConscript('driver/SConscript')
# Library build

View File

@ -1,4 +1,3 @@
#ifdef GIN_UNIX
#include "driver/io-unix.h"
#include <sstream>
@ -18,11 +17,6 @@ IFdOwner::~IFdOwner() {
}
}
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} {
}
ssize_t UnixIoStream::dataRead(void *buffer, size_t length) {
return ::read(fd(), buffer, length);
}
@ -30,6 +24,84 @@ ssize_t UnixIoStream::dataRead(void *buffer, size_t length) {
ssize_t UnixIoStream::dataWrite(const void *buffer, size_t length) {
return ::write(fd(), buffer, length);
}
/*
void UnixIoStream::readStep() {
if (read_ready) {
read_ready->feed();
}
while (!read_tasks.empty()) {
ReadIoTask &task = read_tasks.front();
ssize_t n = ::read(fd(), task.buffer, task.max_length);
if (n <= 0) {
if (n == 0) {
if (on_read_disconnect) {
on_read_disconnect->feed();
}
break;
}
int error = errno;
if (error == EAGAIN || error == EWOULDBLOCK) {
break;
} else {
if (read_done) {
read_done->fail(criticalError("Read failed"));
}
read_tasks.pop();
}
} else if (static_cast<size_t>(n) >= task.min_length &&
static_cast<size_t>(n) <= task.max_length) {
if (read_done) {
read_done->feed(static_cast<size_t>(n));
}
size_t max_len = task.max_length;
read_tasks.pop();
} else {
task.buffer = reinterpret_cast<uint8_t *>(task.buffer) + n;
task.min_length -= static_cast<size_t>(n);
task.max_length -= static_cast<size_t>(n);
}
}
}
void UnixIoStream::writeStep() {
if (write_ready) {
write_ready->feed();
}
while (!write_tasks.empty()) {
WriteIoTask &task = write_tasks.front();
ssize_t n = ::write(fd(), task.buffer, task.length);
if (n < 0) {
int error = errno;
if (error == EAGAIN || error == EWOULDBLOCK) {
break;
} else {
if (write_done) {
write_done->fail(criticalError("Write failed"));
}
write_tasks.pop();
}
} else if (static_cast<size_t>(n) == task.length) {
if (write_done) {
write_done->feed(static_cast<size_t>(task.length));
}
write_tasks.pop();
} else {
task.buffer = reinterpret_cast<const uint8_t *>(task.buffer) +
static_cast<size_t>(n);
task.length -= static_cast<size_t>(n);
}
}
}
*/
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} {
}
void UnixIoStream::read(void *buffer, size_t min_length, size_t max_length) {
bool is_ready = read_helper.read_tasks.empty();
@ -81,11 +153,11 @@ Conveyor<void> UnixIoStream::writeReady() {
void UnixIoStream::notify(uint32_t mask) {
if (mask & EPOLLOUT) {
write_helper.writeStep(*this);
writeStep();
}
if (mask & EPOLLIN) {
read_helper.readStep(*this);
readStep();
}
if (mask & EPOLLRDHUP) {
@ -153,15 +225,15 @@ Own<Server> UnixNetworkAddress::listen() {
return heap<UnixServer>(event_port, fd, 0);
}
Own<IoStream> UnixNetworkAddress::connect() {
Conveyor<Own<IoStream>> UnixNetworkAddress::connect() {
assert(addresses.size() > 0);
if (addresses.size() == 0) {
return nullptr;
return Conveyor<Own<IoStream>>{criticalError("No address found")};
}
int fd = addresses.front().socket(SOCK_STREAM);
if (fd < 0) {
return nullptr;
return Conveyor<Own<IoStream>>{criticalError("Couldn't open socket")};
}
Own<UnixIoStream> io_stream =
@ -176,43 +248,46 @@ Own<IoStream> UnixNetworkAddress::connect() {
* 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();
break;
/*
* Future function return
Conveyor<void> write_ready = io_stream->writeReady();
return write_ready.then(
[io_stream{std::move(io_stream)}]() mutable {
io_stream->write_ready = nullptr;
return std::move(io_stream);
[ios{std::move(io_stream)}]() mutable {
ios->write_ready = nullptr;
return std::move(ios);
});
*/
break;
} else if (error != EINTR) {
return nullptr;
/// @todo Push error message from
return Conveyor<Own<IoStream>>{
criticalError("Some error happened.")};
}
} else {
break;
}
}
return io_stream;
// @todo change function into a safe return type.
// return Conveyor<Own<IoStream>>{std::move(io_stream)};
return Conveyor<Own<IoStream>>{std::move(io_stream)};
}
std::string UnixNetworkAddress::toString() const {
std::ostringstream oss;
oss << "Address: " << path;
if (port_hint > 0) {
oss << "\nPort: " << port_hint;
try {
std::ostringstream oss;
oss << "Address: " << path;
if (port_hint > 0) {
oss << "\nPort: " << port_hint;
}
return oss.str();
} catch (std::bad_alloc &) {
return {};
}
return oss.str();
}
UnixNetwork::UnixNetwork(UnixEventPort &event_port) : event_port{event_port} {}
Own<NetworkAddress> UnixNetwork::parseAddress(const std::string &path,
uint16_t port_hint) {
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:";
@ -224,34 +299,38 @@ Own<NetworkAddress> UnixNetwork::parseAddress(const std::string &path,
std::list<SocketAddress> addresses =
SocketAddress::parse(addr_view, port_hint);
return heap<UnixNetworkAddress>(event_port, path, port_hint,
std::move(addresses));
return Conveyor<Own<NetworkAddress>>{heap<UnixNetworkAddress>(
event_port, path, port_hint, std::move(addresses))};
}
UnixAsyncIoProvider::UnixAsyncIoProvider(UnixEventPort &port_ref,
Own<EventPort> &&port)
: event_port{port_ref}, event_loop{std::move(port)}, wait_scope{event_loop},
unix_network{port_ref} {}
Own<EventPort> port)
: event_port{port_ref}, event_loop{std::move(port)}, unix_network{
port_ref} {}
Own<InputStream> UnixAsyncIoProvider::wrapInputFd(int fd) {
return heap<UnixIoStream>(event_port, fd, 0, EPOLLIN);
}
Network &UnixAsyncIoProvider::network() {
return static_cast<Network &>(unix_network);
}
EventLoop &UnixAsyncIoProvider::eventLoop() { return event_loop; }
WaitScope &UnixAsyncIoProvider::waitScope() { return wait_scope; }
ErrorOr<AsyncIoContext> setupAsyncIo() {
try {
Own<UnixEventPort> prt = heap<UnixEventPort>();
UnixEventPort &prt_ref = *prt;
Network &UnixAsyncIoProvider::network() { return unix_network; }
Own<UnixAsyncIoProvider> io_provider =
heap<UnixAsyncIoProvider>(prt_ref, std::move(prt));
AsyncIoContext setupAsyncIo() {
Own<UnixEventPort> prt = heap<UnixEventPort>();
UnixEventPort &prt_ref = *prt;
Own<UnixAsyncIoProvider> io_provider =
heap<UnixAsyncIoProvider>(prt_ref, std::move(prt));
EventLoop &loop_ref = io_provider->eventLoop();
EventLoop &event_loop = io_provider->eventLoop();
WaitScope &wait_scope = io_provider->waitScope();
return {std::move(io_provider), prt_ref, wait_scope};
return {{std::move(io_provider), loop_ref, prt_ref}};
} catch (std::bad_alloc &) {
return criticalError("Out of memory");
}
}
} // namespace gin
#endif // GIN_UNIX

View File

@ -7,6 +7,7 @@
#include <csignal>
#include <sys/signalfd.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/epoll.h>
@ -61,6 +62,8 @@ private:
std::unordered_multimap<Signal, Own<ConveyorFeeder<void>>> signal_conveyors;
int pipefds[2];
std::vector<int> toUnixSignal(Signal signal) const {
switch (signal) {
case Signal::User1:
@ -106,13 +109,12 @@ private:
if (nfds < 0) {
/// @todo error_handling
assert(false);
return false;
}
for (int i = 0; i < nfds; ++i) {
if (events[i].data.u64 == 0) {
for (;;) {
while (1) {
struct ::signalfd_siginfo siginfo;
ssize_t n =
::read(signal_fd, &siginfo, sizeof(siginfo));
@ -123,6 +125,17 @@ private:
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);
@ -156,11 +169,22 @@ public:
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 {
@ -202,6 +226,16 @@ public:
}
}
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;
@ -240,6 +274,9 @@ private:
ssize_t dataRead(void *buffer, size_t length) override;
ssize_t dataWrite(const void *buffer, size_t length) override;
void readStep();
void writeStep();
public:
UnixIoStream(UnixEventPort &event_port, int file_descriptor, int fd_flags,
uint32_t event_mask);
@ -357,7 +394,7 @@ public:
addresses{std::move(addr)} {}
Own<Server> listen() override;
Own<IoStream> connect() override;
Conveyor<Own<IoStream>> connect() override;
std::string toString() const override;
};
@ -369,26 +406,24 @@ private:
public:
UnixNetwork(UnixEventPort &event_port);
Own<NetworkAddress> parseAddress(const std::string &,
uint16_t port_hint = 0) override;
Conveyor<Own<NetworkAddress>> parseAddress(const std::string &,
uint16_t port_hint = 0) override;
};
class UnixAsyncIoProvider final : public AsyncIoProvider {
private:
UnixEventPort &event_port;
EventLoop event_loop;
WaitScope wait_scope;
UnixNetwork unix_network;
public:
UnixAsyncIoProvider(UnixEventPort &port_ref, Own<EventPort> &&port);
UnixAsyncIoProvider(UnixEventPort &port_ref, Own<EventPort> port);
Network &network() override;
Own<InputStream> wrapInputFd(int fd) override;
EventLoop &eventLoop();
WaitScope &waitScope();
Network &network() override;
};
} // namespace gin

View File

@ -1,198 +0,0 @@
#include "buffer.h"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <iomanip>
#include <sstream>
namespace gin {
RingBuffer::RingBuffer() : 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);
}
size_t RingBuffer::readPosition() 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());
}
/*
* 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() const {
return writePosition() < readPosition()
? (buffer.size() - readPosition())
: (write_reached_read
? (buffer.size() - readPosition())
: writePosition() -
readPosition()); //(writePosition() -
// readPosition()) :
//(write_reached_read ? () : 0 );
}
void RingBuffer::readAdvance(size_t bytes) {
assert(bytes <= readCompositeLength());
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];
}
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];
}
size_t RingBuffer::writePosition() const { return write_position; }
size_t RingBuffer::writeCompositeLength() const {
return readPosition() > writePosition()
? (readPosition() - writePosition())
: (write_reached_read
? 0
: buffer.size() - (writePosition() - readPosition()));
}
size_t RingBuffer::writeSegmentLength() const {
return readPosition() > writePosition()
? (readPosition() - writePosition())
: (write_reached_read ? 0 : (buffer.size() - writePosition()));
}
void RingBuffer::writeAdvance(size_t bytes) {
assert(bytes <= writeCompositeLength());
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);
}
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];
}
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];
}
/*
Error RingBuffer::increaseSize(size_t size){
size_t old_size = buffer.size();
size_t new_size = old_size + size;
buffer.resize(new_size);
if(readPosition() > writePosition() || (readPosition() ==
writePosition() && write_reached_read)){ size_t remaining = old_size -
writePosition(); size_t real_remaining = 0; while(remaining > 0){ size_t
segment = std::min(remaining, size); memcpy(&buffer[new_size-segment],
&buffer[old_size-segment], segment); remaining -= segment; size -= segment;
old_size -= segment;
new_size -= segment;
}
}
return noError();
}
*/
Error RingBuffer::push(const uint8_t &value) {
size_t write_remain = writeCompositeLength();
if (write_remain > 0) {
write() = value;
writeAdvance(1);
} else {
return recoverableError("Buffer too small");
}
return noError();
}
Error RingBuffer::push(const uint8_t &buffer, size_t size) {
if (writeCompositeLength() >= size) {
const uint8_t *buffer_ptr = &buffer;
while (size > 0) {
size_t segment = std::min(writeSegmentLength(), size);
memcpy(&write(), buffer_ptr, segment);
writeAdvance(segment);
size -= segment;
buffer_ptr += segment;
}
} else {
return recoverableError("Buffer too small");
}
return noError();
}
Error RingBuffer::pop(uint8_t &value) {
if (readCompositeLength() > 0) {
value = read();
readAdvance(1);
} else {
return recoverableError("Buffer too small");
}
return noError();
}
Error RingBuffer::pop(uint8_t &buffer, size_t size) {
if (readCompositeLength() >= size) {
uint8_t *buffer_ptr = &buffer;
while (size > 0) {
size_t segment = std::min(readSegmentLength(), size);
memcpy(buffer_ptr, &read(), segment);
readAdvance(segment);
size -= segment;
buffer_ptr += segment;
}
} else {
return recoverableError("Buffer too small");
}
return noError();
}
std::string RingBuffer::toString() const {
std::ostringstream oss;
for (size_t i = 0; i < readCompositeLength(); ++i) {
oss << read(i);
}
return oss.str();
}
std::string RingBuffer::toHex() const {
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (size_t i = 0; i < readCompositeLength(); ++i) {
oss << std::setw(2) << (uint16_t)read(i);
if ((i + 1) < readCompositeLength()) {
oss << ((i % 4 == 3) ? '\n' : ' ');
}
}
return oss.str();
}
} // namespace gin

View File

@ -1,31 +0,0 @@
#include "error.h"
namespace gin {
Error::Error() : error_{0} {}
Error::Error(const std::string &msg) : error_message{msg}, error_{1} {}
Error::Error(const std::string &msg, int8_t code)
: error_message{msg}, error_{code} {}
Error::Error(const Error &error)
: error_message{error.error_message}, error_{error.error_} {}
Error::Error(Error &&error)
: error_message{std::move(error.error_message)}, error_{std::move(
error.error_)} {}
const std::string &Error::message() const { return error_message; }
bool Error::failed() const { return error_ != 0; }
bool Error::isCritical() const { return error_ < 0; }
bool Error::isRecoverable() const { return error_ > 0; }
Error criticalError(const std::string &msg) { return Error{msg, -1}; }
Error recoverableError(const std::string &msg) { return Error{msg, 1}; }
Error noError() { return Error{}; }
} // namespace gin

View File

@ -1,78 +0,0 @@
#pragma once
#include <string>
#include <variant>
#include "common.h"
namespace gin {
class Error {
private:
std::string error_message;
int8_t error_;
public:
Error();
Error(const std::string &msg);
Error(const std::string &msg, int8_t code);
Error(const Error &error);
Error(Error &&error);
Error &operator=(const Error &) = default;
Error &operator=(Error &&) = default;
const std::string &message() const;
bool failed() const;
bool isCritical() const;
bool isRecoverable() const;
};
Error criticalError(const std::string &msg);
Error recoverableError(const std::string &msg);
Error noError();
template <typename T> class ErrorOr;
class ErrorOrValue {
public:
virtual ~ErrorOrValue() = default;
template <typename T> ErrorOr<T> &as() {
return reinterpret_cast<ErrorOr<T> &>(*this);
}
template <typename T> const ErrorOr<T> &as() const {
return reinterpret_cast<const ErrorOr<T> &>(*this);
}
};
template <typename T> class ErrorOr : public ErrorOrValue {
private:
std::variant<T, Error> value_or_error;
public:
ErrorOr() = default;
ErrorOr(const T &value) : value_or_error{value} {}
ErrorOr(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)} {}
bool isValue() const { return std::holds_alternative<T>(value_or_error); }
bool isError() const {
return std::holds_alternative<Error>(value_or_error);
}
Error &error() { return std::get<Error>(value_or_error); }
const Error &error() const { return std::get<Error>(value_or_error); }
T &value() { return std::get<T>(value_or_error); }
const T &value() const { return std::get<T>(value_or_error); }
};
} // namespace gin

View File

@ -1,840 +0,0 @@
#pragma once
#include "buffer.h"
#include "message.h"
#include "message_dynamic.h"
#include "error.h"
#include <cassert>
#include <charconv>
#include <sstream>
#include <string_view>
#include <tuple>
#include <iostream>
namespace gin {
template <typename T> struct JsonEncodeImpl;
template <typename T> struct JsonEncodeImpl<MessagePrimitive<T>> {
static Error encode(typename MessagePrimitive<T>::Reader data,
Buffer &buffer) {
std::string stringified = std::to_string(data.get());
Error error =
buffer.push(*reinterpret_cast<const uint8_t *>(stringified.data()),
stringified.size());
if (error.failed()) {
return error;
}
return Error{};
}
};
template <> struct JsonEncodeImpl<MessagePrimitive<std::string>> {
static Error encode(typename MessagePrimitive<std::string>::Reader data,
Buffer &buffer) {
std::string str =
std::string{"\""} + std::string{data.get()} + std::string{"\""};
Error error = buffer.push(
*reinterpret_cast<const uint8_t *>(str.data()), str.size());
if (error.failed()) {
return error;
}
return Error{};
}
};
template <typename... T> struct JsonEncodeImpl<MessageList<T...>> {
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(T), Error>::type
encodeMembers(typename MessageList<T...>::Reader data, Buffer &buffer) {
(void)data;
(void)buffer;
return Error{};
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(T), Error>::type
encodeMembers(typename MessageList<T...>::Reader data, Buffer &buffer) {
{
Error error =
JsonEncodeImpl<typename ParameterPackType<i, T...>::type>::
encode(data.template get<i>(), buffer);
if (error.failed()) {
return error;
}
}
if constexpr ((i + 1u) < sizeof...(T)) {
if (buffer.push(',').failed()) {
return recoverableError("Failed buffer push");
}
}
{
Error error =
JsonEncodeImpl<MessageList<T...>>::encodeMembers<i + 1>(data,
buffer);
if (error.failed()) {
return error;
}
}
return noError();
}
static Error encode(typename MessageList<T...>::Reader data,
Buffer &buffer) {
if (buffer.push('[').failed()) {
return recoverableError("Failed buffer push");
}
Error error =
JsonEncodeImpl<MessageList<T...>>::encodeMembers<0>(data, buffer);
if (error.failed()) {
return error;
}
if (buffer.push(']').failed()) {
return recoverableError("Failed buffer push");
}
return noError();
}
};
template <typename... V, typename... K>
struct JsonEncodeImpl<MessageStruct<MessageStructMember<V, K>...>> {
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(V), Error>::type
encodeMembers(
typename MessageStruct<MessageStructMember<V, K>...>::Reader data,
Buffer &buffer) {
(void)data;
(void)buffer;
return Error{};
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(V), Error>::type encodeMembers(
typename MessageStruct<MessageStructMember<V, K>...>::Reader data,
Buffer &buffer) {
{
Error error = buffer.push('\"');
if (error.failed()) {
return error;
}
std::string_view view = ParameterPackType<i, K...>::type::view();
error = buffer.push(*reinterpret_cast<const uint8_t *>(view.data()),
view.size());
if (error.failed()) {
return error;
}
error = buffer.push('\"');
if (error.failed()) {
return error;
}
error = buffer.push(':');
if (error.failed()) {
return error;
}
}
{
Error error =
JsonEncodeImpl<typename ParameterPackType<i, V...>::type>::
encode(data.template get<i>(), buffer);
if (error.failed()) {
return error;
}
}
if constexpr ((i + 1u) < sizeof...(V)) {
if (buffer.push(',').failed()) {
return recoverableError("Failed buffer push");
}
}
{
Error error =
JsonEncodeImpl<MessageStruct<MessageStructMember<V, K>...>>::
encodeMembers<i + 1>(data, buffer);
if (error.failed()) {
return error;
}
}
return noError();
}
static Error
encode(typename MessageStruct<MessageStructMember<V, K>...>::Reader data,
Buffer &buffer) {
if (buffer.push('{').failed()) {
return recoverableError("Failed buffer push");
}
Error error =
JsonEncodeImpl<MessageStruct<MessageStructMember<V, K>...>>::
encodeMembers<0>(data, buffer);
if (error.failed()) {
return error;
}
if (buffer.push('}').failed()) {
return recoverableError("Failed buffer push");
}
return noError();
}
};
template <typename... V, typename... K>
struct JsonEncodeImpl<MessageUnion<MessageUnionMember<V, K>...>> {
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(V), Error>::type encodeMember(
typename MessageUnion<MessageUnionMember<V, K>...>::Reader data,
Buffer &buffer) {
(void)data;
(void)buffer;
return noError();
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(V), Error>::type encodeMember(
typename MessageUnion<MessageUnionMember<V, K>...>::Reader reader,
Buffer &buffer) {
/// @todo only encode if alternative is set, skip in other cases
/// use holds_alternative
if (reader.template holdsAlternative<
typename ParameterPackType<i, K...>::type>()) {
{
Error error = buffer.push('{');
if (error.failed()) {
return error;
}
}
{
Error error = buffer.push('\"');
if (error.failed()) {
return error;
}
std::string_view view =
ParameterPackType<i, K...>::type::view();
error =
buffer.push(*reinterpret_cast<const uint8_t *>(view.data()),
view.size());
if (error.failed()) {
return error;
}
error = buffer.push('\"');
if (error.failed()) {
return error;
}
error = buffer.push(':');
if (error.failed()) {
return error;
}
}
Error error =
JsonEncodeImpl<typename ParameterPackType<i, V...>::type>::
encode(reader.template get<i>(), buffer);
if (error.failed()) {
return error;
}
{
Error error = buffer.push('}');
if (error.failed()) {
return error;
}
}
return noError();
}
Error error =
JsonEncodeImpl<MessageUnion<MessageUnionMember<V, K>...>>::
encodeMember<i + 1>(reader, buffer);
if (error.failed()) {
return error;
}
return noError();
}
static Error
encode(typename MessageUnion<MessageUnionMember<V, K>...>::Reader reader,
Buffer &buffer) {
return encodeMember<0>(reader, buffer);
}
};
/*
* For JSON decoding we need a dynamic where we can query information from
*/
template <typename T> struct JsonDecodeImpl;
template <typename T> struct JsonDecodeImpl<MessagePrimitive<T>> {
// static void decode(BufferView view, typename
// MessagePrimitive<T>::Builder){}
static Error decode(typename MessagePrimitive<T>::Builder,
DynamicMessage::DynamicReader) {
return noError();
}
};
template <> struct JsonDecodeImpl<MessagePrimitive<int64_t>> {
// static void decode(BufferView view, typename
// MessagePrimitive<T>::Builder){}
static Error decode(typename MessagePrimitive<int64_t>::Builder data,
DynamicMessage::DynamicReader reader) {
if (reader.type() != DynamicMessage::Type::Signed) {
return criticalError("Not an integer");
}
DynamicMessageSigned::Reader s_reader =
reader.as<DynamicMessageSigned>();
data.set(s_reader.get());
return noError();
}
};
template <> struct JsonDecodeImpl<MessagePrimitive<uint32_t>> {
// static void decode(BufferView view, typename
// MessagePrimitive<T>::Builder){}
static Error decode(typename MessagePrimitive<uint32_t>::Builder builder,
DynamicMessage::DynamicReader reader) {
if (reader.type() != DynamicMessage::Type::Signed) {
return criticalError("Not an integer");
}
DynamicMessageSigned::Reader s_reader =
reader.as<DynamicMessageSigned>();
int64_t val = s_reader.get();
if (val < 0) {
return criticalError("Not an unsigned integer");
}
builder.set(static_cast<uint32_t>(val));
return noError();
}
};
template <> struct JsonDecodeImpl<MessagePrimitive<int32_t>> {
// static void decode(BufferView view, typename
// MessagePrimitive<T>::Builder){}
static Error decode(typename MessagePrimitive<int32_t>::Builder data,
DynamicMessage::DynamicReader reader) {
if (reader.type() != DynamicMessage::Type::Signed) {
return criticalError("Not an integer");
}
DynamicMessageSigned::Reader s_reader =
reader.as<DynamicMessageSigned>();
int64_t val = s_reader.get();
data.set(static_cast<int32_t>(val));
return noError();
}
};
template <> struct JsonDecodeImpl<MessagePrimitive<std::string>> {
static Error decode(typename MessagePrimitive<std::string>::Builder builder,
DynamicMessage::DynamicReader reader) {
if (reader.type() != DynamicMessage::Type::String) {
return criticalError("Not a string");
}
DynamicMessageString::Reader s_reader =
reader.as<DynamicMessageString>();
builder.set(s_reader.get());
return noError();
}
};
template <typename... V, typename... K>
struct JsonDecodeImpl<MessageStruct<MessageStructMember<V, K>...>> {
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(V), Error>::type
decodeMembers(typename MessageStruct<MessageStructMember<V, K>...>::Builder,
DynamicMessageStruct::Reader reader) {
return noError();
}
template <size_t i = 0>
static typename std::enable_if <
i<sizeof...(V), Error>::type decodeMembers(
typename MessageStruct<MessageStructMember<V, K>...>::Builder
builder,
DynamicMessageStruct::Reader reader) {
DynamicMessage::DynamicReader member_reader =
reader.get(ParameterPackType<i, K...>::type::view());
{
Error error =
JsonDecodeImpl<typename ParameterPackType<i, V...>::type>::
decode(builder.template init<i>(), member_reader);
if (error.failed()) {
return error;
}
}
{
Error error =
JsonDecodeImpl<MessageStruct<MessageStructMember<V, K>...>>::
decodeMembers<i + 1>(builder, reader);
if (error.failed()) {
return error;
}
}
return noError();
}
static Error decode(
typename MessageStruct<MessageStructMember<V, K>...>::Builder builder,
DynamicMessage::DynamicReader reader) {
if (reader.type() != DynamicMessage::Type::Struct) {
return criticalError("Not a struct");
}
Error error =
JsonDecodeImpl<MessageStruct<MessageStructMember<V, K>...>>::
decodeMembers<0>(builder, reader.as<DynamicMessageStruct>());
if (error.failed()) {
return error;
}
return noError();
}
};
class JsonCodec {
private:
bool isWhitespace(int8_t letter) {
return letter == '\t' || letter == ' ' || letter == '\r' ||
letter == '\n';
}
void skipWhitespace(Buffer &buffer) {
while (buffer.readCompositeLength() > 0 &&
isWhitespace(buffer.read())) {
buffer.readAdvance(1);
}
}
Error decodeBool(DynamicMessageBool::Builder message, Buffer &buffer) {
/// @todo unimplemented
return noError();
}
// Not yet clear if double or integer
Error decodeNumber(Own<DynamicMessage> &message, Buffer &buffer) {
assert((buffer.read() >= '0' && buffer.read() <= '9') ||
buffer.read() == '+' || buffer.read() == '-');
size_t offset = 0;
if (buffer.read() == '-') {
++offset;
} else if (buffer.read() == '+') {
return criticalError("Not a valid number with +");
}
if (offset >= buffer.readCompositeLength()) {
return recoverableError("Buffer too short");
}
bool integer = true;
if (buffer.read(offset) >= '1' && buffer.read(offset) <= '9') {
++offset;
if (offset >= buffer.readCompositeLength()) {
return recoverableError("Buffer too short");
}
while (1) {
if (buffer.read(offset) >= '0' && buffer.read(offset) <= '9') {
++offset;
if (offset >= buffer.readCompositeLength()) {
return recoverableError("Buffer too short");
}
continue;
}
break;
}
} else if (buffer.read(offset) == '0') {
++offset;
} else {
return criticalError("Not a JSON number");
}
if (offset >= buffer.readCompositeLength()) {
return recoverableError("Buffer too short");
}
if (buffer.read(offset) == '.') {
integer = false;
++offset;
if (offset >= buffer.readCompositeLength()) {
return recoverableError("Buffer too short");
}
size_t partial_start = offset;
while (1) {
if (buffer.read(offset) >= '0' && buffer.read(offset) <= '9') {
++offset;
if (offset >= buffer.readCompositeLength()) {
return recoverableError("Buffer too short");
}
continue;
}
break;
}
if (offset == partial_start) {
return criticalError("No numbers after '.'");
}
}
if (buffer.read(offset) == 'e' || buffer.read(offset) == 'E') {
integer = false;
++offset;
if (offset >= buffer.readCompositeLength()) {
return recoverableError("Buffer too short");
}
if (buffer.read(offset) == '+' || buffer.read(offset) == '-') {
++offset;
if (offset >= buffer.readCompositeLength()) {
return recoverableError("Buffer too short");
}
}
size_t exp_start = offset;
while (1) {
if (buffer.read(offset) >= '0' && buffer.read(offset) <= '9') {
++offset;
if (offset >= buffer.readCompositeLength()) {
return recoverableError("Buffer too short");
}
continue;
}
break;
}
if (offset == exp_start) {
return criticalError("No numbers after exponent token");
}
}
if (offset >= buffer.readCompositeLength()) {
return recoverableError("Buffer too short");
}
std::string_view number_view{reinterpret_cast<char *>(&buffer.read()),
offset};
if (integer) {
int64_t result;
auto fc_result = std::from_chars(
number_view.data(), number_view.data() + number_view.size(),
result);
if (fc_result.ec != std::errc{}) {
return criticalError("Not an integer");
}
auto int_msg = std::make_unique<DynamicMessageSigned>();
DynamicMessageSigned::Builder builder{*int_msg};
builder.set(result);
message = std::move(int_msg);
} else {
std::string number_copy{number_view};
double result;
try {
result = std::stod(number_copy);
} catch (std::exception &e) {
return criticalError("Not a double");
}
//
auto dbl_msg = std::make_unique<DynamicMessageDouble>();
DynamicMessageDouble::Builder builder{*dbl_msg};
builder.set(result);
message = std::move(dbl_msg);
}
buffer.readAdvance(offset);
skipWhitespace(buffer);
return noError();
}
Error decodeNull(Buffer &buffer) {
/// @todo unimplemented
return noError();
}
Error decodeValue(Own<DynamicMessage> &message, Buffer &buffer) {
skipWhitespace(buffer);
if (buffer.readCompositeLength() == 0) {
return recoverableError("Buffer too short");
}
switch (buffer.read()) {
case '"': {
std::string str;
Error error = decodeRawString(str, buffer);
if (error.failed()) {
return error;
}
Own<DynamicMessageString> msg_string =
std::make_unique<DynamicMessageString>();
DynamicMessageString::Builder builder{*msg_string};
builder.set(std::move(str));
message = std::move(msg_string);
} break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '+':
case '-': {
Error error = decodeNumber(message, buffer);
if (error.failed()) {
return error;
}
} break;
case 't':
case 'T':
case 'f':
case 'F': {
Own<DynamicMessageBool> msg_bool =
std::make_unique<DynamicMessageBool>();
decodeBool(DynamicMessageBool::Builder{*msg_bool}, buffer);
message = std::move(msg_bool);
} break;
case '{': {
Own<DynamicMessageStruct> msg_struct =
std::make_unique<DynamicMessageStruct>();
Error error = decodeStruct(
DynamicMessageStruct::Builder{*msg_struct}, buffer);
if (error.failed()) {
return error;
}
message = std::move(msg_struct);
} break;
case '[': {
Own<DynamicMessageList> msg_list =
std::make_unique<DynamicMessageList>();
decodeList(DynamicMessageList::Builder{*msg_list}, buffer);
message = std::move(msg_list);
} break;
case 'n':
case 'N': {
Own<DynamicMessageNull> msg_null =
std::make_unique<DynamicMessageNull>();
decodeNull(buffer);
message = std::move(msg_null);
} break;
default: {
return criticalError("Cannot identify next JSON value");
}
}
skipWhitespace(buffer);
return noError();
}
Error decodeRawString(std::string &raw, Buffer &buffer) {
assert(buffer.read() == '"');
buffer.readAdvance(1);
std::stringstream iss;
bool string_done = false;
while (!string_done) {
if (buffer.readCompositeLength() == 0) {
return recoverableError("Buffer too short");
}
switch (buffer.read()) {
case '\\':
buffer.readAdvance(1);
if (buffer.readCompositeLength() == 0) {
return recoverableError("Buffer too short");
}
switch (buffer.read()) {
case '\\':
case '/':
case '"':
iss << buffer.read();
break;
case 'b':
iss << '\b';
break;
case 'f':
iss << '\f';
break;
case 'n':
iss << '\n';
break;
case 'r':
iss << '\r';
break;
case 't':
iss << '\t';
break;
case 'u': {
buffer.readAdvance(1);
if (buffer.readCompositeLength() < 4) {
return recoverableError(
"Broken unicode or short buffer");
}
/// @todo correct unicode handling
iss << '?'; // dummy line
iss << '?';
iss << '?';
iss << '?';
// There is alway a skip at the end so here we skip 3
// instead of 4 bytes
buffer.readAdvance(3);
} break;
}
break;
case '"':
string_done = true;
break;
default:
iss << buffer.read();
break;
}
buffer.readAdvance(1);
}
raw = iss.str();
return noError();
}
Error decodeList(DynamicMessageList::Builder builder, Buffer &buffer) {
assert(buffer.read() == '[');
buffer.readAdvance(1);
skipWhitespace(buffer);
if (buffer.readCompositeLength() == 0) {
return recoverableError("Buffer too short");
}
if (buffer.read() == ']') {
return noError();
}
Own<DynamicMessage> message = nullptr;
{ Error error = decodeValue(message, buffer); }
while (buffer.read() != ']') {
}
/// @todo unimplemented
return noError();
}
Error decodeStruct(DynamicMessageStruct::Builder message, Buffer &buffer) {
assert(buffer.read() == '{');
buffer.readAdvance(1);
skipWhitespace(buffer);
if (buffer.readCompositeLength() == 0) {
return recoverableError("Buffer too short");
}
if (buffer.read() == '}') {
buffer.readAdvance(1);
return noError();
}
while (buffer.read() != '}') {
if (buffer.read() == '"') {
std::string key_string;
{
Error error = decodeRawString(key_string, buffer);
if (error.failed()) {
return error;
}
}
skipWhitespace(buffer);
if (buffer.readCompositeLength() == 0) {
return recoverableError("Buffer too short");
}
if (buffer.read() != ':') {
return criticalError("Expecting a ':' token");
}
buffer.readAdvance(1);
Own<DynamicMessage> msg = nullptr;
{
Error error = decodeValue(msg, buffer);
if (error.failed()) {
return error;
}
}
message.init(key_string, std::move(msg));
if (buffer.readCompositeLength() == 0) {
return recoverableError("Buffer too short");
}
/*
skipWhitespace(buffer);
if(buffer.readCompositeLength() == 0){
return recoverableError("Buffer too short");
}
/// @todo value decode
skipWhitespace(buffer);
*/
switch (buffer.read()) {
case '}':
break;
case ',':
buffer.readAdvance(1);
skipWhitespace(buffer);
if (buffer.readCompositeLength() == 0) {
return recoverableError("Buffer too short");
}
break;
default:
return criticalError("Not a JSON Object");
}
} else {
return criticalError("Not a JSON Object");
}
}
buffer.readAdvance(1);
return noError();
}
ErrorOr<Own<DynamicMessage>> decodeDynamic(Buffer &buffer) {
skipWhitespace(buffer);
if (buffer.readCompositeLength() == 0) {
return recoverableError("Buffer too short");
}
if (buffer.read() == '{') {
Own<DynamicMessageStruct> message =
std::make_unique<DynamicMessageStruct>();
Error error =
decodeStruct(DynamicMessageStruct::Builder{*message}, buffer);
if (error.failed()) {
return error;
}
skipWhitespace(buffer);
return Own<DynamicMessage>{std::move(message)};
} else if (buffer.read() == '[') {
Own<DynamicMessageList> message =
std::make_unique<DynamicMessageList>();
Error error = decodeList(*message, buffer);
if (error.failed()) {
return error;
}
skipWhitespace(buffer);
return Own<DynamicMessage>{std::move(message)};
} else {
return criticalError("Not a JSON Object");
}
}
public:
template <typename T>
Error encode(typename T::Reader reader, Buffer &buffer) {
return JsonEncodeImpl<T>::encode(reader, buffer);
}
template <typename T>
Error decode(typename T::Builder builder, Buffer &buffer) {
ErrorOr<Own<DynamicMessage>> error_or_message = decodeDynamic(buffer);
if (error_or_message.isError()) {
return error_or_message.error();
}
Own<DynamicMessage> message = std::move(error_or_message.value());
if (!message) {
return criticalError("No message object created");
}
if (message->type() == DynamicMessage::Type::Null) {
return criticalError("Can't decode to json");
}
DynamicMessage::DynamicReader reader{*message};
Error static_error = JsonDecodeImpl<T>::decode(builder, reader);
return static_error;
}
};
} // namespace gin

View File

@ -29,6 +29,7 @@ void ConveyorStorage::setParent(ConveyorStorage *p) {
assert(!parent);
armNext();
}
parent = p;
}
@ -37,8 +38,7 @@ ConveyorBase::ConveyorBase(Own<ConveyorNode> &&node_p,
: node{std::move(node_p)}, storage{storage_p} {}
Error PropagateError::operator()(const Error &error) const {
Error err{error};
return err;
return error.copyError();
}
Error PropagateError::operator()(Error &&error) { return std::move(error); }
@ -143,6 +143,8 @@ void Event::disarm() {
bool Event::isArmed() const { return prev != nullptr; }
SinkConveyor::SinkConveyor() : node{nullptr} {}
SinkConveyor::SinkConveyor(Own<ConveyorNode> &&node_p)
: node{std::move(node_p)} {}
@ -275,15 +277,22 @@ void ConveyorSinks::fail(Error &&error) {
/// @todo call error_handler
}
ConveyorSinks::ConveyorSinks(EventLoop &event_loop) : Event{event_loop} {}
void ConveyorSinks::add(Conveyor<void> &&sink) {
auto nas = Conveyor<void>::fromConveyor(std::move(sink));
Own<SinkConveyorNode> sink_node =
heap<SinkConveyorNode>(std::move(nas.first), *this);
Own<SinkConveyorNode> sink_node = nullptr;
try {
sink_node = heap<SinkConveyorNode>(std::move(nas.first), *this);
} catch (std::bad_alloc &) {
return;
}
if (nas.second) {
nas.second->setParent(sink_node.get());
}
sink_nodes.push_back(std::move(sink_node));
sink_nodes.emplace_back(std::move(sink_node));
}
void ConveyorSinks::fire() {

View File

@ -24,6 +24,10 @@ public:
};
class EventLoop;
/*
* Event class similar to capn'proto.
* https://github.com/capnproto/capnproto
*/
class Event {
private:
EventLoop &loop;
@ -84,11 +88,18 @@ template <typename T> Conveyor<T> chainedConveyorType(T *);
template <typename T> Conveyor<T> chainedConveyorType(Conveyor<T> *);
template <typename T> T reduceErrorOrType(T *);
template <typename T> T reduceErrorOrType(ErrorOr<T> *);
template <typename T>
using ReduceErrorOr = decltype(reduceErrorOrType((T *)nullptr));
template <typename T>
using ChainedConveyors = decltype(chainedConveyorType((T *)nullptr));
template <typename Func, typename T>
using ConveyorResult = ChainedConveyors<ReturnType<Func, T>>;
using ConveyorResult = ChainedConveyors<ReduceErrorOr<ReturnType<Func, T>>>;
struct PropagateError {
public:
@ -101,63 +112,88 @@ private:
Own<ConveyorNode> node;
public:
SinkConveyor();
SinkConveyor(Own<ConveyorNode> &&node);
SinkConveyor(SinkConveyor &&) = default;
SinkConveyor &operator=(SinkConveyor &&) = default;
};
template <typename T> class Conveyor : public ConveyorBase {
/**
* Main interface for async operations.
*/
template <typename T> class Conveyor final : public ConveyorBase {
public:
/*
* Construct a immediately fulfilled node
/**
* Construct an immediately fulfilled node
*/
Conveyor(FixVoid<T> value);
/*
* empty promise
* @todo remove this
/**
* Construct an immediately failed node
*/
Conveyor(Error &&error);
/**
* Construct a conveyor with a child node and the next storage point
*/
Conveyor(Own<ConveyorNode> &&node_p, ConveyorStorage *storage_p);
Conveyor(Conveyor<T> &&) = default;
Conveyor<T> &operator=(Conveyor<T> &&) = default;
/*
* This method converts passed values or errors from children
/**
* This method converts values or errors from children
*/
template <typename Func, typename ErrorFunc = PropagateError>
ConveyorResult<Func, T> then(Func &&func,
ErrorFunc &&error_func = PropagateError());
/*
* This method adds a buffer node in the conveyor chains and acts as a
* scheduler interruption point.
/**
* This method adds a buffer node in the conveyor chains which acts as a
* scheduler interrupt point and collects elements up to the supplied limit.
*/
Conveyor<T> buffer(size_t limit = std::numeric_limits<size_t>::max());
/*
* This method just takes ownership of any supplied types
/**
* This method just takes ownership of any supplied types,
* which are destroyed when the chain gets destroyed.
* Useful for resource lifetime control.
*/
template <typename... Args> Conveyor<T> attach(Args &&... args);
template <typename... Args> Conveyor<T> attach(Args &&...args);
/*
*
/** @todo implement
* This method limits the total amount of passed elements
* Be careful where you place this node into the chain.
* If you meant to fork it and destroy paths you shouldn't place
* an interrupt point between the fork and this limiter
*/
Conveyor<T> limit(size_t val = std::numeric_limits<size_t>::max());
Conveyor<T> limit(size_t val = 1);
/*
*
/**
* Moves the conveyor chain into a thread local storage point which drops
* every element. Use sink() if you want to control the lifetime of a
* conveyor chain
*/
template <typename ErrorFunc> void detach(ErrorFunc &&err_func);
template <typename ErrorFunc = PropagateError>
void detach(ErrorFunc &&err_func = PropagateError());
/*
*
/**
* Creates a local sink which drops elements, but lifetime control remains
* in your hand.
*/
template <typename ErrorFunc> SinkConveyor sink(ErrorFunc &&error_func);
template <typename ErrorFunc = PropagateError>
SinkConveyor sink(ErrorFunc &&error_func = PropagateError());
// Waiting and resolving
/**
* If no sink() or detach() is used you have to take elements out of the
* chain yourself.
*/
ErrorOr<FixVoid<T>> take();
/** @todo implement
* Specifically pump elements through this chain
*/
void poll();
// helper
@ -169,6 +205,13 @@ public:
fromConveyor(Conveyor<T> &&conveyor);
};
/*
* Join Conveyors into a single one
*/
// template<typename... Args>
// Conveyor<std::tuple<Args...>> joinConveyors(std::tuple<Conveyor<Args...>>&
// conveyors);
template <typename T> class ConveyorFeeder {
public:
virtual ~ConveyorFeeder() = default;
@ -202,6 +245,11 @@ template <typename T> ConveyorAndFeeder<T> oneTimeConveyorAndFeeder();
enum class Signal : uint8_t { Terminate, User1 };
/**
* Class which acts as a correspondent between the running framework and outside
* events which may be signals from the operating system or just other threads.
* Default EventPorts are supplied by setupAsyncIo() in io.h
*/
class EventPort {
public:
virtual ~EventPort() = default;
@ -212,11 +260,13 @@ public:
virtual void wait() = 0;
virtual void wait(const std::chrono::steady_clock::duration &) = 0;
virtual void wait(const std::chrono::steady_clock::time_point &) = 0;
virtual void wake() = 0;
};
class SinkConveyorNode;
class ConveyorSinks : public Event {
class ConveyorSinks final : public Event {
private:
friend class SinkConveyorNode;
@ -231,12 +281,17 @@ private:
public:
ConveyorSinks() = default;
ConveyorSinks(EventLoop &event_loop);
void add(Conveyor<void> &&node);
void fire() override;
};
/*
* EventLoop class similar to capn'proto.
* https://github.com/capnproto/capnproto
*/
class EventLoop {
private:
friend class Event;
@ -266,6 +321,9 @@ public:
EventLoop(Own<EventPort> &&port);
~EventLoop();
EventLoop(EventLoop &&) = default;
EventLoop &operator=(EventLoop &&) = default;
bool wait();
bool wait(const std::chrono::steady_clock::duration &);
bool wait(const std::chrono::steady_clock::time_point &);
@ -276,6 +334,10 @@ public:
ConveyorSinks &daemon();
};
/*
* WaitScope class similar to capn'proto.
* https://github.com/capnproto/capnproto
*/
class WaitScope {
private:
EventLoop &loop;
@ -332,7 +394,7 @@ template <> struct FixVoidCaller<Void, Void> {
template <typename T> class AdaptConveyorNode;
template <typename T>
class AdaptConveyorFeeder : public ConveyorFeeder<UnfixVoid<T>> {
class AdaptConveyorFeeder final : public ConveyorFeeder<UnfixVoid<T>> {
private:
AdaptConveyorNode<T> *feedee = nullptr;
@ -349,7 +411,7 @@ public:
};
template <typename T>
class AdaptConveyorNode : public ConveyorNode, public ConveyorStorage {
class AdaptConveyorNode final : public ConveyorNode, public ConveyorStorage {
private:
AdaptConveyorFeeder<T> *feeder = nullptr;
@ -379,7 +441,7 @@ public:
template <typename T> class OneTimeConveyorNode;
template <typename T>
class OneTimeConveyorFeeder : public ConveyorFeeder<UnfixVoid<T>> {
class OneTimeConveyorFeeder final : public ConveyorFeeder<UnfixVoid<T>> {
private:
OneTimeConveyorNode<T> *feedee = nullptr;
@ -396,7 +458,7 @@ public:
};
template <typename T>
class OneTimeConveyorNode : public ConveyorNode, public ConveyorStorage {
class OneTimeConveyorNode final : public ConveyorNode, public ConveyorStorage {
private:
OneTimeConveyorFeeder<T> *feeder = nullptr;
@ -433,7 +495,7 @@ public:
};
template <typename T>
class QueueBufferConveyorNode : public QueueBufferConveyorNodeBase {
class QueueBufferConveyorNode final : public QueueBufferConveyorNodeBase {
private:
std::queue<ErrorOr<T>> storage;
size_t max_store;
@ -487,16 +549,18 @@ public:
AttachConveyorNodeBase(Own<ConveyorNode> &&dep)
: ConveyorNode(std::move(dep)) {}
virtual ~AttachConveyorNodeBase() = default;
void getResult(ErrorOrValue &err_or_val) override;
};
template <typename... Args>
class AttachConveyorNode : public AttachConveyorNodeBase {
class AttachConveyorNode final : public AttachConveyorNodeBase {
private:
std::tuple<Args...> attached_data;
public:
AttachConveyorNode(Own<ConveyorNode> &&dep, Args &&... args)
AttachConveyorNode(Own<ConveyorNode> &&dep, Args &&...args)
: AttachConveyorNodeBase(std::move(dep)), attached_data{
std::move(args...)} {}
};
@ -504,6 +568,7 @@ public:
class ConvertConveyorNodeBase : public ConveyorNode {
public:
ConvertConveyorNodeBase(Own<ConveyorNode> &&dep);
virtual ~ConvertConveyorNodeBase() = default;
void getResult(ErrorOrValue &err_or_val) override;
@ -511,7 +576,7 @@ public:
};
template <typename T, typename DepT, typename Func, typename ErrorFunc>
class ConvertConveyorNode : public ConvertConveyorNodeBase {
class ConvertConveyorNode final : public ConvertConveyorNodeBase {
private:
Func func;
ErrorFunc error_func;
@ -522,14 +587,22 @@ public:
: ConvertConveyorNodeBase(std::move(dep)), func{std::move(func)},
error_func{std::move(error_func)} {}
void getImpl(ErrorOrValue &err_or_val) override {
void getImpl(ErrorOrValue &err_or_val) noexcept override {
ErrorOr<DepT> dep_eov;
ErrorOr<T> &eov = err_or_val.as<T>();
if (child) {
child->getResult(dep_eov);
if (dep_eov.isValue()) {
eov = FixVoidCaller<T, DepT>::apply(func,
std::move(dep_eov.value()));
try {
eov = FixVoidCaller<T, DepT>::apply(
func, std::move(dep_eov.value()));
} catch (const std::bad_alloc &) {
eov = criticalError("Out of memory");
} catch (const std::exception &) {
eov = criticalError(
"Exception in chain occured. Return ErrorOr<T> if you "
"want to handle errors which are recoverable");
}
} else if (dep_eov.isError()) {
eov = error_func(std::move(dep_eov.error()));
} else {
@ -541,7 +614,7 @@ public:
}
};
class SinkConveyorNode : public ConveyorNode, public ConveyorStorage {
class SinkConveyorNode final : public ConveyorNode, public ConveyorStorage {
private:
ConveyorSinks *conveyor_sink;
@ -569,7 +642,7 @@ public:
size_t queued() const override { return 0; }
// ConveyorNode
void getResult(ErrorOrValue &err_or_val) override {
void getResult(ErrorOrValue &err_or_val) noexcept override {
err_or_val.as<Void>() =
criticalError("In a sink node no result can be returned");
}
@ -596,16 +669,18 @@ public:
class ImmediateConveyorNodeBase : public ConveyorNode, public ConveyorStorage {
private:
public:
virtual ~ImmediateConveyorNodeBase() = default;
};
template <typename T>
class ImmediateConveyorNode : public ImmediateConveyorNodeBase {
class ImmediateConveyorNode final : public ImmediateConveyorNodeBase {
private:
FixVoid<T> value;
bool retrieved;
ErrorOr<FixVoid<T>> value;
uint8_t retrieved;
public:
ImmediateConveyorNode(FixVoid<T> &&val);
ImmediateConveyorNode(Error &&error);
// ConveyorStorage
size_t space() const override;
@ -614,337 +689,19 @@ public:
void childFired() override;
// ConveyorNode
void getResult(ErrorOrValue &err_or_val) override {
void getResult(ErrorOrValue &err_or_val) noexcept override {
if (retrieved) {
err_or_val.as<FixVoid<T>>() = criticalError("Already taken value");
} else {
err_or_val.as<FixVoid<T>>() = std::move(value);
retrieved = true;
}
++retrieved;
}
// Event
void fire() override;
};
template <typename T>
ImmediateConveyorNode<T>::ImmediateConveyorNode(FixVoid<T> &&val)
: value{std::move(val)}, retrieved{false} {}
template <typename T> size_t ImmediateConveyorNode<T>::space() const {
return 0;
}
template <typename T> size_t ImmediateConveyorNode<T>::queued() const {
return retrieved ? 0 : 1;
}
template <typename T> void ImmediateConveyorNode<T>::childFired() {
// Impossible
}
template <typename T> void ImmediateConveyorNode<T>::fire() {
if (parent) {
parent->childFired();
}
}
} // namespace gin
#include <cassert>
// Template inlining
namespace gin {
template <typename T> T reduceErrorOrType(T *);
template <typename T> T reduceErrorOrType(ErrorOr<T> *);
template <typename T>
using ReduceErrorOr = decltype(reduceErrorOrType((T *)nullptr));
template <typename T>
Conveyor<T>::Conveyor(FixVoid<T> value) : ConveyorBase(nullptr, nullptr) {
// Is there any way to do this?
// @todo new ConveyorBase constructor for Immediate values
auto immediate = heap<ImmediateConveyorNode<FixVoid<T>>>(std::move(value));
storage = reinterpret_cast<ConveyorStorage *>(immediate.get());
node = std::move(immediate);
}
template <typename T>
Conveyor<T>::Conveyor(Own<ConveyorNode> &&node_p, ConveyorStorage *storage_p)
: ConveyorBase(std::move(node_p), storage_p) {}
template <typename T>
template <typename Func, typename ErrorFunc>
ConveyorResult<Func, T> Conveyor<T>::then(Func &&func, ErrorFunc &&error_func) {
Own<ConveyorNode> conversion_node =
heap<ConvertConveyorNode<FixVoid<ReduceErrorOr<ReturnType<Func, T>>>,
FixVoid<T>, Func, ErrorFunc>>(
std::move(node), std::move(func), std::move(error_func));
return Conveyor<ReduceErrorOr<ReturnType<Func, T>>>::toConveyor(
std::move(conversion_node), storage);
}
template <typename T> Conveyor<T> Conveyor<T>::buffer(size_t size) {
Own<QueueBufferConveyorNode<FixVoid<T>>> storage_node =
heap<QueueBufferConveyorNode<FixVoid<T>>>(std::move(node), size);
ConveyorStorage *storage_ptr =
static_cast<ConveyorStorage *>(storage_node.get());
storage->setParent(storage_ptr);
return Conveyor<T>{std::move(storage_node), storage_ptr};
}
template <typename T>
template <typename... Args>
Conveyor<T> Conveyor<T>::attach(Args &&... args) {
Own<AttachConveyorNode<Args...>> attach_node =
heap<AttachConveyorNode<Args...>>(std::move(node), std::move(args...));
return Conveyor<T>{std::move(attach_node), storage};
}
template <>
template <typename ErrorFunc>
SinkConveyor Conveyor<void>::sink(ErrorFunc &&error_func) {
Own<SinkConveyorNode> sink_node = heap<SinkConveyorNode>(std::move(node));
ConveyorStorage *storage_ptr =
static_cast<ConveyorStorage *>(sink_node.get());
if (storage) {
storage->setParent(storage_ptr);
}
return SinkConveyor{std::move(sink_node)};
}
void detachConveyor(Conveyor<void> &&conveyor);
template <typename T>
template <typename ErrorFunc>
void Conveyor<T>::detach(ErrorFunc &&func) {
detachConveyor(std::move(then([](T &&) {}, std::move(func))));
}
template <>
template <typename ErrorFunc>
void Conveyor<void>::detach(ErrorFunc &&func) {
detachConveyor(std::move(then([]() {}, std::move(func))));
}
template <typename T>
Conveyor<T> Conveyor<T>::toConveyor(Own<ConveyorNode> &&node,
ConveyorStorage *storage) {
return Conveyor<T>{std::move(node), storage};
}
template <typename T>
std::pair<Own<ConveyorNode>, ConveyorStorage *>
Conveyor<T>::fromConveyor(Conveyor<T> &&conveyor) {
return std::make_pair(std::move(conveyor.node), conveyor.storage);
}
template <typename T> ErrorOr<FixVoid<T>> Conveyor<T>::take() {
if (storage) {
if (storage->queued() > 0) {
ErrorOr<FixVoid<T>> result;
node->getResult(result);
return ErrorOr<FixVoid<T>>{result};
} else {
return ErrorOr<FixVoid<T>>{
recoverableError("Conveyor buffer has no elements")};
}
} else {
return ErrorOr<FixVoid<T>>{criticalError("Conveyor in invalid state")};
}
}
template <typename T> ConveyorAndFeeder<T> newConveyorAndFeeder() {
Own<AdaptConveyorFeeder<FixVoid<T>>> feeder =
heap<AdaptConveyorFeeder<FixVoid<T>>>();
Own<AdaptConveyorNode<FixVoid<T>>> node =
heap<AdaptConveyorNode<FixVoid<T>>>();
feeder->setFeedee(node.get());
node->setFeeder(feeder.get());
ConveyorStorage *storage_ptr = static_cast<ConveyorStorage *>(node.get());
return ConveyorAndFeeder<T>{
std::move(feeder),
Conveyor<T>::toConveyor(std::move(node), storage_ptr)};
}
template <typename T> AdaptConveyorFeeder<T>::~AdaptConveyorFeeder() {
if (feedee) {
feedee->setFeeder(nullptr);
feedee = nullptr;
}
}
template <typename T>
void AdaptConveyorFeeder<T>::setFeedee(AdaptConveyorNode<T> *feedee_p) {
feedee = feedee_p;
}
template <typename T> void AdaptConveyorFeeder<T>::feed(T &&value) {
if (feedee) {
feedee->feed(std::move(value));
}
}
template <typename T> void AdaptConveyorFeeder<T>::fail(Error &&error) {
if (feedee) {
feedee->fail(std::move(error));
}
}
template <typename T> size_t AdaptConveyorFeeder<T>::queued() const {
if (feedee) {
return feedee->queued();
}
return 0;
}
template <typename T> size_t AdaptConveyorFeeder<T>::space() const {
if (feedee) {
return feedee->space();
}
return 0;
}
template <typename T> AdaptConveyorNode<T>::~AdaptConveyorNode() {
if (feeder) {
feeder->setFeedee(nullptr);
feeder = nullptr;
}
}
template <typename T>
void AdaptConveyorNode<T>::setFeeder(AdaptConveyorFeeder<T> *feeder_p) {
feeder = feeder_p;
}
template <typename T> void AdaptConveyorNode<T>::feed(T &&value) {
storage.push(std::move(value));
armNext();
}
template <typename T> void AdaptConveyorNode<T>::fail(Error &&error) {
storage.push(std::move(error));
armNext();
}
template <typename T> size_t AdaptConveyorNode<T>::queued() const {
return storage.size();
}
template <typename T> size_t AdaptConveyorNode<T>::space() const {
return std::numeric_limits<size_t>::max() - storage.size();
}
template <typename T>
void AdaptConveyorNode<T>::getResult(ErrorOrValue &err_or_val) {
if (!storage.empty()) {
err_or_val.as<T>() = std::move(storage.front());
storage.pop();
} else {
err_or_val.as<T>() =
criticalError("Signal for retrieval of storage sent even though no "
"data is present");
}
}
template <typename T> void AdaptConveyorNode<T>::fire() {
if (parent) {
parent->childFired();
if (storage.size() > 0) {
armLater();
}
}
}
template <typename T> OneTimeConveyorFeeder<T>::~OneTimeConveyorFeeder() {
if (feedee) {
feedee->setFeeder(nullptr);
feedee = nullptr;
}
}
template <typename T>
void OneTimeConveyorFeeder<T>::setFeedee(OneTimeConveyorNode<T> *feedee_p) {
feedee = feedee_p;
}
template <typename T> void OneTimeConveyorFeeder<T>::feed(T &&value) {
if (feedee) {
feedee->feed(std::move(value));
}
}
template <typename T> void OneTimeConveyorFeeder<T>::fail(Error &&error) {
if (feedee) {
feedee->fail(std::move(error));
}
}
template <typename T> size_t OneTimeConveyorFeeder<T>::queued() const {
if (feedee) {
return feedee->queued();
}
return 0;
}
template <typename T> size_t OneTimeConveyorFeeder<T>::space() const {
if (feedee) {
return feedee->space();
}
return 0;
}
template <typename T> OneTimeConveyorNode<T>::~OneTimeConveyorNode() {
if (feeder) {
feeder->setFeedee(nullptr);
feeder = nullptr;
}
}
template <typename T>
void OneTimeConveyorNode<T>::setFeeder(OneTimeConveyorFeeder<T> *feeder_p) {
feeder = feeder_p;
}
template <typename T> void OneTimeConveyorNode<T>::feed(T &&value) {
storage = std::move(value);
armNext();
}
template <typename T> void OneTimeConveyorNode<T>::fail(Error &&error) {
storage = std::move(error);
armNext();
}
template <typename T> size_t OneTimeConveyorNode<T>::queued() const {
return storage.has_value() ? 1 : 0;
}
template <typename T> size_t OneTimeConveyorNode<T>::space() const {
return passed ? 0 : 1;
}
template <typename T>
void OneTimeConveyorNode<T>::getResult(ErrorOrValue &err_or_val) {
if (storage.has_value()) {
err_or_val.as<T>() = std::move(storage.value());
storage = std::nullopt;
} else {
err_or_val.as<T>() =
criticalError("Signal for retrieval of storage sent even though no "
"data is present");
}
}
template <typename T> void OneTimeConveyorNode<T>::fire() {
if (parent) {
parent->childFired();
}
}
} // namespace gin
#include "async.tmpl.h"

344
source/kelgin/async.tmpl.h Normal file
View File

@ -0,0 +1,344 @@
#pragma once
#include <cassert>
// Template inlining
namespace gin {
template <typename T>
ImmediateConveyorNode<T>::ImmediateConveyorNode(FixVoid<T> &&val)
: value{std::move(val)}, retrieved{0} {}
template <typename T>
ImmediateConveyorNode<T>::ImmediateConveyorNode(Error &&error)
: value{std::move(error)}, retrieved{0} {}
template <typename T> size_t ImmediateConveyorNode<T>::space() const {
return 0;
}
template <typename T> size_t ImmediateConveyorNode<T>::queued() const {
return retrieved > 1 ? 0 : 1;
}
template <typename T> void ImmediateConveyorNode<T>::childFired() {
// Impossible case
assert(false);
}
template <typename T> void ImmediateConveyorNode<T>::fire() {
if (parent) {
parent->childFired();
}
if (queued() > 0) {
armLast();
}
}
template <typename T>
Conveyor<T>::Conveyor(FixVoid<T> value) : ConveyorBase(nullptr, nullptr) {
// Is there any way to do this?
// @todo new ConveyorBase constructor for Immediate values
Own<ImmediateConveyorNode<FixVoid<T>>> immediate =
heap<ImmediateConveyorNode<FixVoid<T>>>(std::move(value));
if (!immediate) {
return;
}
storage = static_cast<ConveyorStorage *>(immediate.get());
node = std::move(immediate);
}
template <typename T>
Conveyor<T>::Conveyor(Error &&error) : ConveyorBase(nullptr, nullptr) {
Own<ImmediateConveyorNode<FixVoid<T>>> immediate =
heap<ImmediateConveyorNode<FixVoid<T>>>(std::move(error));
if (!immediate) {
return;
}
storage = static_cast<ConveyorStorage *>(immediate.get());
node = std::move(immediate);
}
template <typename T>
Conveyor<T>::Conveyor(Own<ConveyorNode> &&node_p, ConveyorStorage *storage_p)
: ConveyorBase(std::move(node_p), storage_p) {}
template <typename T>
template <typename Func, typename ErrorFunc>
ConveyorResult<Func, T> Conveyor<T>::then(Func &&func, ErrorFunc &&error_func) {
Own<ConveyorNode> conversion_node =
heap<ConvertConveyorNode<FixVoid<ReduceErrorOr<ReturnType<Func, T>>>,
FixVoid<T>, Func, ErrorFunc>>(
std::move(node), std::move(func), std::move(error_func));
return Conveyor<ReduceErrorOr<ReturnType<Func, T>>>::toConveyor(
std::move(conversion_node), storage);
}
template <typename T> Conveyor<T> Conveyor<T>::buffer(size_t size) {
Own<QueueBufferConveyorNode<FixVoid<T>>> storage_node =
heap<QueueBufferConveyorNode<FixVoid<T>>>(std::move(node), size);
ConveyorStorage *storage_ptr =
static_cast<ConveyorStorage *>(storage_node.get());
storage->setParent(storage_ptr);
return Conveyor<T>{std::move(storage_node), storage_ptr};
}
template <typename T>
template <typename... Args>
Conveyor<T> Conveyor<T>::attach(Args &&...args) {
Own<AttachConveyorNode<Args...>> attach_node =
heap<AttachConveyorNode<Args...>>(std::move(node), std::move(args...));
return Conveyor<T>{std::move(attach_node), storage};
}
template <>
template <typename ErrorFunc>
SinkConveyor Conveyor<void>::sink(ErrorFunc &&error_func) {
Own<SinkConveyorNode> sink_node = heap<SinkConveyorNode>(std::move(node));
ConveyorStorage *storage_ptr =
static_cast<ConveyorStorage *>(sink_node.get());
if (storage) {
storage->setParent(storage_ptr);
}
return SinkConveyor{std::move(sink_node)};
}
void detachConveyor(Conveyor<void> &&conveyor);
template <typename T>
template <typename ErrorFunc>
void Conveyor<T>::detach(ErrorFunc &&func) {
detachConveyor(std::move(then([](T &&) {}, std::move(func))));
}
template <>
template <typename ErrorFunc>
void Conveyor<void>::detach(ErrorFunc &&func) {
detachConveyor(std::move(then([]() {}, std::move(func))));
}
template <typename T>
Conveyor<T> Conveyor<T>::toConveyor(Own<ConveyorNode> &&node,
ConveyorStorage *storage) {
return Conveyor<T>{std::move(node), storage};
}
template <typename T>
std::pair<Own<ConveyorNode>, ConveyorStorage *>
Conveyor<T>::fromConveyor(Conveyor<T> &&conveyor) {
return std::make_pair(std::move(conveyor.node), conveyor.storage);
}
template <typename T> ErrorOr<FixVoid<T>> Conveyor<T>::take() {
if (storage) {
if (storage->queued() > 0) {
ErrorOr<FixVoid<T>> result;
node->getResult(result);
return result;
} else {
return ErrorOr<FixVoid<T>>{
recoverableError("Conveyor buffer has no elements")};
}
} else {
return ErrorOr<FixVoid<T>>{criticalError("Conveyor in invalid state")};
}
}
template <typename T> ConveyorAndFeeder<T> newConveyorAndFeeder() {
Own<AdaptConveyorFeeder<FixVoid<T>>> feeder =
heap<AdaptConveyorFeeder<FixVoid<T>>>();
Own<AdaptConveyorNode<FixVoid<T>>> node =
heap<AdaptConveyorNode<FixVoid<T>>>();
feeder->setFeedee(node.get());
node->setFeeder(feeder.get());
ConveyorStorage *storage_ptr = static_cast<ConveyorStorage *>(node.get());
return ConveyorAndFeeder<T>{
std::move(feeder),
Conveyor<T>::toConveyor(std::move(node), storage_ptr)};
}
template <typename T> AdaptConveyorFeeder<T>::~AdaptConveyorFeeder() {
if (feedee) {
feedee->setFeeder(nullptr);
feedee = nullptr;
}
}
template <typename T>
void AdaptConveyorFeeder<T>::setFeedee(AdaptConveyorNode<T> *feedee_p) {
feedee = feedee_p;
}
template <typename T> void AdaptConveyorFeeder<T>::feed(T &&value) {
if (feedee) {
feedee->feed(std::move(value));
}
}
template <typename T> void AdaptConveyorFeeder<T>::fail(Error &&error) {
if (feedee) {
feedee->fail(std::move(error));
}
}
template <typename T> size_t AdaptConveyorFeeder<T>::queued() const {
if (feedee) {
return feedee->queued();
}
return 0;
}
template <typename T> size_t AdaptConveyorFeeder<T>::space() const {
if (feedee) {
return feedee->space();
}
return 0;
}
template <typename T> AdaptConveyorNode<T>::~AdaptConveyorNode() {
if (feeder) {
feeder->setFeedee(nullptr);
feeder = nullptr;
}
}
template <typename T>
void AdaptConveyorNode<T>::setFeeder(AdaptConveyorFeeder<T> *feeder_p) {
feeder = feeder_p;
}
template <typename T> void AdaptConveyorNode<T>::feed(T &&value) {
storage.push(std::move(value));
armNext();
}
template <typename T> void AdaptConveyorNode<T>::fail(Error &&error) {
storage.push(std::move(error));
armNext();
}
template <typename T> size_t AdaptConveyorNode<T>::queued() const {
return storage.size();
}
template <typename T> size_t AdaptConveyorNode<T>::space() const {
return std::numeric_limits<size_t>::max() - storage.size();
}
template <typename T>
void AdaptConveyorNode<T>::getResult(ErrorOrValue &err_or_val) {
if (!storage.empty()) {
err_or_val.as<T>() = std::move(storage.front());
storage.pop();
} else {
err_or_val.as<T>() =
criticalError("Signal for retrieval of storage sent even though no "
"data is present");
}
}
template <typename T> void AdaptConveyorNode<T>::fire() {
if (parent) {
parent->childFired();
if (storage.size() > 0) {
armLater();
}
}
}
template <typename T> OneTimeConveyorFeeder<T>::~OneTimeConveyorFeeder() {
if (feedee) {
feedee->setFeeder(nullptr);
feedee = nullptr;
}
}
template <typename T>
void OneTimeConveyorFeeder<T>::setFeedee(OneTimeConveyorNode<T> *feedee_p) {
feedee = feedee_p;
}
template <typename T> void OneTimeConveyorFeeder<T>::feed(T &&value) {
if (feedee) {
feedee->feed(std::move(value));
}
}
template <typename T> void OneTimeConveyorFeeder<T>::fail(Error &&error) {
if (feedee) {
feedee->fail(std::move(error));
}
}
template <typename T> size_t OneTimeConveyorFeeder<T>::queued() const {
if (feedee) {
return feedee->queued();
}
return 0;
}
template <typename T> size_t OneTimeConveyorFeeder<T>::space() const {
if (feedee) {
return feedee->space();
}
return 0;
}
template <typename T> OneTimeConveyorNode<T>::~OneTimeConveyorNode() {
if (feeder) {
feeder->setFeedee(nullptr);
feeder = nullptr;
}
}
template <typename T>
void OneTimeConveyorNode<T>::setFeeder(OneTimeConveyorFeeder<T> *feeder_p) {
feeder = feeder_p;
}
template <typename T> void OneTimeConveyorNode<T>::feed(T &&value) {
storage = std::move(value);
armNext();
}
template <typename T> void OneTimeConveyorNode<T>::fail(Error &&error) {
storage = std::move(error);
armNext();
}
template <typename T> size_t OneTimeConveyorNode<T>::queued() const {
return storage.has_value() ? 1 : 0;
}
template <typename T> size_t OneTimeConveyorNode<T>::space() const {
return passed ? 0 : 1;
}
template <typename T>
void OneTimeConveyorNode<T>::getResult(ErrorOrValue &err_or_val) {
if (storage.has_value()) {
err_or_val.as<T>() = std::move(storage.value());
storage = std::nullopt;
} else {
err_or_val.as<T>() =
criticalError("Signal for retrieval of storage sent even though no "
"data is present");
}
}
template <typename T> void OneTimeConveyorNode<T>::fire() {
if (parent) {
parent->childFired();
}
}
} // namespace gin

431
source/kelgin/buffer.cpp Normal file
View File

@ -0,0 +1,431 @@
#include "buffer.h"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <iomanip>
#include <sstream>
namespace gin {
Error Buffer::push(const uint8_t &value) {
size_t write_remain = writeCompositeLength();
if (write_remain > 0) {
write() = value;
writeAdvance(1);
} else {
return recoverableError("Buffer too small");
}
return noError();
}
Error Buffer::push(const uint8_t &buffer, size_t size) {
Error error = writeRequireLength(size);
if (error.failed()) {
return error;
}
const uint8_t *buffer_ptr = &buffer;
while (size > 0) {
size_t segment = std::min(writeSegmentLength(), size);
memcpy(&write(), buffer_ptr, segment);
writeAdvance(segment);
size -= segment;
buffer_ptr += segment;
}
return noError();
}
Error Buffer::pop(uint8_t &value) {
if (readCompositeLength() > 0) {
value = read();
readAdvance(1);
} else {
return recoverableError("Buffer too small");
}
return noError();
}
Error Buffer::pop(uint8_t &buffer, size_t size) {
if (readCompositeLength() >= size) {
uint8_t *buffer_ptr = &buffer;
while (size > 0) {
size_t segment = std::min(readSegmentLength(), size);
memcpy(buffer_ptr, &read(), segment);
readAdvance(segment);
size -= segment;
buffer_ptr += segment;
}
} else {
return recoverableError("Buffer too small");
}
return noError();
}
std::string Buffer::toString() const {
std::ostringstream oss;
for (size_t i = 0; i < readCompositeLength(); ++i) {
oss << read(i);
}
return oss.str();
}
std::string Buffer::toHex() const {
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (size_t i = 0; i < readCompositeLength(); ++i) {
oss << std::setw(2) << (uint16_t)read(i);
if ((i + 1) < readCompositeLength()) {
oss << ((i % 4 == 3) ? '\n' : ' ');
}
}
return oss.str();
}
BufferView::BufferView(Buffer &buffer)
: buffer{buffer}, read_offset{0}, write_offset{0} {}
size_t BufferView::readPosition() const {
return read_offset + buffer.readPosition();
}
size_t BufferView::readCompositeLength() const {
assert(read_offset <= buffer.readCompositeLength());
if (read_offset > buffer.readCompositeLength()) {
return 0;
}
return buffer.readCompositeLength() - read_offset;
}
size_t BufferView::readSegmentLength(size_t offset) const {
size_t off = offset + read_offset;
assert(off <= buffer.readCompositeLength());
if (off > buffer.readCompositeLength()) {
return 0;
}
return buffer.readSegmentLength(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();
return;
}
read_offset += bytes;
}
uint8_t &BufferView::read(size_t i) {
size_t pos = i + read_offset;
assert(pos < buffer.readCompositeLength());
return buffer.read(pos);
}
const uint8_t &BufferView::read(size_t i) const {
size_t pos = i + read_offset;
assert(pos < buffer.readCompositeLength());
return buffer.read(pos);
}
size_t BufferView::writePosition() const {
return write_offset + buffer.writePosition();
}
size_t BufferView::writeCompositeLength() const {
assert(write_offset <= buffer.writeCompositeLength());
if (write_offset > buffer.writeCompositeLength()) {
return 0;
}
return buffer.writeCompositeLength() - write_offset;
}
size_t BufferView::writeSegmentLength(size_t offset) const {
size_t off = offset + write_offset;
assert(off <= buffer.writeCompositeLength());
if (off > buffer.writeCompositeLength()) {
return 0;
}
return buffer.writeSegmentLength(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();
return;
}
write_offset += bytes;
}
uint8_t &BufferView::write(size_t i) {
size_t pos = i + write_offset;
assert(pos < buffer.writeCompositeLength());
return buffer.write(pos);
}
const uint8_t &BufferView::write(size_t i) const {
size_t pos = i + write_offset;
assert(pos < buffer.writeCompositeLength());
return buffer.write(pos);
}
Error BufferView::writeRequireLength(size_t bytes) {
return buffer.writeRequireLength(bytes + write_offset);
}
size_t BufferView::readOffset() const { return read_offset; }
size_t BufferView::writeOffset() const { return write_offset; }
RingBuffer::RingBuffer() : 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);
}
size_t RingBuffer::readPosition() 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());
}
/*
* 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();
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;
// case 1 write is located before read and reached read
// then offset can be used normally
// case 2 write is located at read, but read reached write
// 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 (writePosition() == read_offset) {
if (remaining > 0) {
return buffer.size() - read_offset;
} else {
return 0;
}
}
return writePosition() - read_offset;
}
void RingBuffer::readAdvance(size_t bytes) {
size_t read_composite = readCompositeLength();
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;
}
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];
}
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];
}
size_t RingBuffer::writePosition() const { return write_position; }
size_t RingBuffer::writeCompositeLength() const {
return readPosition() > writePosition()
? (readPosition() - writePosition())
: (write_reached_read
? 0
: buffer.size() - (writePosition() - readPosition()));
}
size_t RingBuffer::writeSegmentLength(size_t offset) const {
size_t write_composite = writeCompositeLength();
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;
if (read_position > write_offset) {
return read_position - write_offset;
}
if (write_reached_read) {
return 0;
}
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;
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];
}
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];
}
/*
Error RingBuffer::increaseSize(size_t size){
size_t old_size = buffer.size();
size_t new_size = old_size + size;
buffer.resize(new_size);
if(readPosition() > writePosition() || (readPosition() ==
writePosition() && write_reached_read)){ size_t remaining = old_size -
writePosition(); size_t real_remaining = 0; while(remaining > 0){ size_t
segment = std::min(remaining, size); memcpy(&buffer[new_size-segment],
&buffer[old_size-segment], segment); remaining -= segment; size -= segment;
old_size -= segment;
new_size -= segment;
}
}
return noError();
}
*/
Error RingBuffer::writeRequireLength(size_t bytes) {
size_t write_remain = writeCompositeLength();
if (bytes > write_remain) {
return recoverableError("Buffer too small");
}
return noError();
}
ArrayBuffer::ArrayBuffer(size_t size) : read_position{0}, write_position{0} {
buffer.resize(size);
}
size_t ArrayBuffer::readPosition() const { return read_position; }
size_t ArrayBuffer::readCompositeLength() const {
return write_position - read_position;
}
size_t ArrayBuffer::readSegmentLength(size_t offset) const {
size_t read_composite = readCompositeLength();
assert(offset <= read_composite);
offset = std::min(read_composite, offset);
size_t read_offset = read_position + offset;
return write_position - read_offset;
}
void ArrayBuffer::readAdvance(size_t bytes) {
assert(bytes <= readCompositeLength());
read_position += bytes;
}
uint8_t &ArrayBuffer::read(size_t i) {
assert(i < readCompositeLength());
return buffer[i + read_position];
}
const uint8_t &ArrayBuffer::read(size_t i) const {
assert(i + read_position < buffer.size());
return buffer[i + read_position];
}
size_t ArrayBuffer::writePosition() const { return write_position; }
size_t ArrayBuffer::writeCompositeLength() 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();
assert(offset <= write_composite);
offset = std::min(write_composite, offset);
size_t write_offset = write_position + offset;
return buffer.size() - write_offset;
}
void ArrayBuffer::writeAdvance(size_t bytes) {
assert(bytes <= writeCompositeLength());
write_position += bytes;
}
uint8_t &ArrayBuffer::write(size_t i) {
assert(i < writeCompositeLength());
return buffer[i + write_position];
}
const uint8_t &ArrayBuffer::write(size_t i) const {
assert(i < writeCompositeLength());
return buffer[i + write_position];
}
Error ArrayBuffer::writeRequireLength(size_t bytes) {
size_t write_remain = writeCompositeLength();
if (bytes > write_remain) {
return recoverableError("Buffer too small");
}
return noError();
}
} // namespace gin

View File

@ -14,14 +14,13 @@ namespace gin {
* Access class to reduce templated BufferSegments bloat
*/
class Buffer {
private:
friend class RingBuffer;
protected:
~Buffer() = default;
public:
virtual size_t readPosition() const = 0;
virtual size_t readCompositeLength() const = 0;
virtual size_t readSegmentLength() const = 0;
virtual size_t readSegmentLength(size_t offset = 0) const = 0;
virtual void readAdvance(size_t bytes) = 0;
virtual uint8_t &read(size_t i = 0) = 0;
@ -29,37 +28,72 @@ public:
virtual size_t writePosition() const = 0;
virtual size_t writeCompositeLength() const = 0;
virtual size_t writeSegmentLength() const = 0;
virtual size_t writeSegmentLength(size_t offset = 0) const = 0;
virtual void writeAdvance(size_t bytes) = 0;
virtual uint8_t &write(size_t i = 0) = 0;
virtual const uint8_t &write(size_t i = 0) const = 0;
/*
* Sometime buffers need to grow with a little more control
* than with push and pop for more efficient calls.
* There is nothing you can do if read hasn't been filled, but at
* least write can be increased if it is demanded.
*/
/// @todo uncomment and implement in child classes
// virtual Error writeRequireLength(size_t bytes) = 0;
* Sometime buffers need to grow with a little more control
* than with push and pop for more efficient calls.
* 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 push(const uint8_t &value) = 0;
virtual Error push(const uint8_t &buffer, size_t size) = 0;
virtual Error pop(uint8_t &value) = 0;
virtual Error pop(uint8_t &buffer, size_t size) = 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);
virtual std::string toString() const = 0;
virtual std::string toHex() const = 0;
std::string toString() const;
std::string toHex() const;
};
/*
* Buffer size meant for default allocation size of the ringbuffer since
* this class currently doesn't support proper resizing
*/
* A viewer class for buffers.
* Working on the reference buffer invalidates the buffer view
*/
class BufferView : public Buffer {
private:
Buffer &buffer;
size_t read_offset;
size_t write_offset;
public:
BufferView(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;
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;
uint8_t &write(size_t i = 0) override;
const uint8_t &write(size_t i = 0) const override;
Error writeRequireLength(size_t bytes) override;
size_t readOffset() const;
size_t writeOffset() const;
};
/*
* Buffer size meant for default allocation size of the ringbuffer since
* this class currently doesn't support proper resizing
*/
constexpr size_t RING_BUFFER_MAX_SIZE = 4096;
/*
* Buffer wrapping around if read caught up
*/
* Buffer wrapping around if read caught up
*/
class RingBuffer final : public Buffer {
private:
std::vector<uint8_t> buffer;
@ -78,7 +112,7 @@ public:
size_t readPosition() const override;
size_t readCompositeLength() const override;
size_t readSegmentLength() const override;
size_t readSegmentLength(size_t offset = 0) const override;
void readAdvance(size_t bytes) override;
uint8_t &read(size_t i = 0) override;
@ -86,19 +120,13 @@ public:
size_t writePosition() const override;
size_t writeCompositeLength() const override;
size_t writeSegmentLength() const override;
size_t writeSegmentLength(size_t offset = 0) const override;
void writeAdvance(size_t bytes) override;
uint8_t &write(size_t i = 0) override;
const uint8_t &write(size_t i = 0) const override;
Error push(const uint8_t &value) override;
Error push(const uint8_t &buffer, size_t size) override;
Error pop(uint8_t &value) override;
Error pop(uint8_t &buffer, size_t size) override;
std::string toString() const override;
std::string toHex() const override;
Error writeRequireLength(size_t bytes) override;
};
/*
@ -116,7 +144,7 @@ public:
size_t readPosition() const override;
size_t readCompositeLength() const override;
size_t readSegmentLength() const override;
size_t readSegmentLength(size_t offset = 0) const override;
void readAdvance(size_t bytes) override;
uint8_t &read(size_t i = 0) override;
@ -124,16 +152,13 @@ public:
size_t writePosition() const override;
size_t writeCompositeLength() const override;
size_t writeSegmentLength() const override;
size_t writeSegmentLength(size_t offset = 0) const override;
void writeAdvance(size_t bytes) override;
uint8_t &write(size_t i = 0) override;
const uint8_t &write(size_t i = 0) const override;
Error push(const uint8_t &value) override;
Error push(const uint8_t &buffer, size_t size) override;
Error pop(uint8_t &value) override;
Error pop(uint8_t &buffer, size_t size) override;
Error writeRequireLength(size_t bytes) override;
};
class ChainArrayBuffer : public Buffer {
@ -148,7 +173,7 @@ public:
size_t readPosition() const override;
size_t readCompositeLength() const override;
size_t readSegmentLength() const override;
size_t readSegmentLength(size_t offset = 0) const override;
void readAdvance(size_t bytes) override;
uint8_t &read(size_t i = 0) override;
@ -156,15 +181,12 @@ public:
size_t writePosition() const override;
size_t writeCompositeLength() const override;
size_t writeSegmentLength() const override;
size_t writeSegmentLength(size_t offset = 0) const override;
void writeAdvance(size_t bytes) override;
uint8_t &write(size_t i = 0) override;
const uint8_t &write(size_t i = 0) const override;
Error push(const uint8_t &value) override;
Error push(const uint8_t &buffer, size_t size) override;
Error pop(uint8_t &value) override;
Error pop(uint8_t &buffer, size_t size) override;
Error writeRequireLength(size_t bytes) override;
};
} // namespace gin

View File

@ -15,6 +15,8 @@ namespace gin {
classname(const classname &) = delete; \
classname &operator=(const classname &) = delete
#define GIN_ASSERT(expression) assert(expression) if (!expression)
template <typename T> using Maybe = std::optional<T>;
template <typename T> using Own = std::unique_ptr<T>;
@ -23,11 +25,11 @@ template <typename T> using Our = std::shared_ptr<T>;
template <typename T> using Lent = std::weak_ptr<T>;
template <typename T, class... Args> Own<T> heap(Args &&... args) {
return std::make_unique<T>(std::forward<Args>(args)...);
template <typename T, class... Args> Own<T> heap(Args &&...args) {
return Own<T>(new (std::nothrow) 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)...);
}
@ -53,4 +55,4 @@ template <typename T> using UnfixVoid = typename VoidUnfix<T>::Type;
template <typename Func, typename T>
using ReturnType = typename ReturnTypeHelper<Func, T>::Type;
} // namespace gin
} // namespace gin

61
source/kelgin/error.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "error.h"
namespace gin {
Error::Error() : error_{0} {}
Error::Error(const std::string_view &msg, int8_t code)
: error_message{msg}, error_{code} {}
Error::Error(std::string &&msg, int8_t code)
: error_message{std::move(msg)}, error_{code} {}
Error::Error(Error &&error)
: error_message{std::move(error.error_message)}, error_{std::move(
error.error_)} {}
const std::string_view Error::message() const {
return std::visit(
[this](auto &&arg) -> const std::string_view {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, std::string>) {
return std::string_view{arg};
} else if constexpr (std::is_same_v<T, std::string_view>) {
return arg;
} else {
return "Error in class Error. Good luck :)";
}
},
error_message);
}
bool Error::failed() const { return error_ != 0; }
bool Error::isCritical() const { return error_ < 0; }
bool Error::isRecoverable() const { return error_ > 0; }
Error Error::copyError() const {
Error error;
error.error_ = error_;
try {
error.error_message = error_message;
} catch (const std::bad_alloc &) {
error.error_message =
std::string_view{"Error while copying Error string. Out of memory"};
}
return error;
}
Error criticalError(const std::string_view &generic) {
return Error{generic, -1};
}
Error recoverableError(const std::string_view &generic) {
return Error{generic, 1};
}
Error noError() { return Error{}; }
} // namespace gin

117
source/kelgin/error.h Normal file
View File

@ -0,0 +1,117 @@
#pragma once
#include <string>
#include <string_view>
#include <variant>
#include "common.h"
namespace gin {
/**
* Utility class for generating errors. Has a base distinction between
* critical and recoverable errors. Additional code ids can be provided to the
* constructor if additional distinctions are necessary.
*/
class Error {
private:
std::variant<std::string_view, std::string> error_message;
int8_t error_;
public:
Error();
Error(const std::string_view &msg, int8_t code);
Error(std::string &&msg, int8_t code);
Error(Error &&error);
GIN_FORBID_COPY(Error);
Error &operator=(Error &&) = default;
const std::string_view message() const;
bool failed() const;
bool isCritical() const;
bool isRecoverable() const;
Error copyError() const;
};
template <typename Formatter>
Error makeError(const Formatter &formatter, int8_t code,
const std::string_view &generic) {
try {
std::string error_msg = formatter();
return Error{std::move(error_msg), code};
} catch (std::bad_alloc &) {
return Error{generic, code};
}
}
Error criticalError(const std::string_view &generic);
template <typename Formatter>
Error criticalError(const Formatter &formatter,
const std::string_view &generic) {
return makeError(formatter, -1, generic);
}
Error recoverableError(const std::string_view &generic);
template <typename Formatter>
Error recoverableError(const Formatter &formatter,
const std::string_view &generic) {
return makeError(formatter, -1, generic);
}
Error noError();
/**
* Exception alternative. Since I code without exceptions this class is
* essentially a kind of exception replacement.
*/
template <typename T> class ErrorOr;
class ErrorOrValue {
public:
virtual ~ErrorOrValue() = default;
template <typename T> ErrorOr<T> &as() {
return dynamic_cast<ErrorOr<T> &>(*this);
}
template <typename T> const ErrorOr<T> &as() const {
return dynamic_cast<const ErrorOr<T> &>(*this);
}
};
template <typename T> class ErrorOr : public ErrorOrValue {
private:
std::variant<FixVoid<T>, Error> value_or_error;
public:
ErrorOr() = default;
ErrorOr(const FixVoid<T> &value) : value_or_error{value} {}
ErrorOr(FixVoid<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)} {}
bool isValue() const { return std::holds_alternative<T>(value_or_error); }
bool isError() const {
return std::holds_alternative<Error>(value_or_error);
}
Error &error() { return std::get<Error>(value_or_error); }
const Error &error() const { return std::get<Error>(value_or_error); }
FixVoid<T> &value() { return std::get<FixVoid<T>>(value_or_error); }
const FixVoid<T> &value() const {
return std::get<FixVoid<T>>(value_or_error);
}
};
} // namespace gin

View File

@ -66,7 +66,7 @@ public:
* Listen on this address
*/
virtual Own<Server> listen() = 0;
virtual Own<IoStream> connect() = 0;
virtual Conveyor<Own<IoStream>> connect() = 0;
virtual std::string toString() const = 0;
};
@ -75,8 +75,8 @@ class Network {
public:
virtual ~Network() = default;
virtual Own<NetworkAddress> parseAddress(const std::string &,
uint16_t port_hint = 0) = 0;
virtual Conveyor<Own<NetworkAddress>>
parseAddress(const std::string &addr, uint16_t port_hint = 0) = 0;
};
class AsyncIoProvider {
@ -88,26 +88,11 @@ public:
virtual Network &network() = 0;
};
/*
* Future of io context structure ?
*
struct AsyncIoContext {
EventLoop event_loop;
EventPort& event_port;
WaitScope wait_scope;
Own<AsyncIoProvider> io_provider;
Network& network;
};
*/
struct AsyncIoContext {
Own<AsyncIoProvider> io;
EventLoop &event_loop;
EventPort &event_port;
WaitScope &wait_scope;
};
/*
* Setup a default Context with an active waitscope
*/
AsyncIoContext setupAsyncIo();
} // namespace gin
ErrorOr<AsyncIoContext> setupAsyncIo();
} // namespace gin

1094
source/kelgin/json.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@
#include <cstdint>
#include <tuple>
#include <variant>
#include <vector>
#include "common.h"
@ -67,6 +68,7 @@ public:
Builder asBuilder() { return Builder{message}; }
};
};
template <> class MessagePrimitive<std::string> : public Message {
private:
std::string value;
@ -159,9 +161,56 @@ public:
bool isSetExplicitly() const { return message.set_explicitly; }
Builder asBuilder() { return Reader{message}; }
Builder asBuilder() { return Builder{message}; }
};
};
/// @todo how to do initialization?
template <typename T> class MessageArray : public Message {
private:
using array_type = std::vector<T>;
array_type elements;
friend class Builder;
friend class Reader;
public:
class Reader;
class Builder {
private:
MessageArray<T> &message;
public:
Builder(MessageArray<T> &message) : message{message} {
message.set_explicitly = true;
}
constexpr typename T::Builder init(size_t i) {
T &msg_ref = message.elements.at(i);
return typename T::Builder{msg_ref};
}
Reader asReader() { return Reader{message}; }
};
class Reader {
private:
MessageArray<T> &message;
public:
Reader(MessageArray<T> &message) : message{message} {}
constexpr typename T::Reader get(size_t i) {
return message.elements.at(i);
}
size_t size() const { return elements.size(); }
bool isSetExplicitly() const { return message.set_explicitly; }
Builder asBuilder() { return Builder{message}; }
};
};
template <typename T, typename K> struct MessageStructMember;
template <typename T, typename C, C... Chars>
@ -279,6 +328,8 @@ public:
constexpr size_t size() { return std::tuple_size<value_type>::value; }
bool isSetExplicitly() const { return message.set_explicitly; }
Builder asBuilder() { return Builder{message}; }
};
};
@ -376,6 +427,8 @@ public:
size_t index() const { return message.values.index(); }
bool isSetExplicitly() const { return message.set_explicitly; }
constexpr size_t size() { return std::variant_size<value_type>::value; }
Builder asBuilder() { return Builder{message}; }

View File

@ -239,6 +239,12 @@ public:
return builder;
}
DynamicMessage::DynamicBuilder push(Own<DynamicMessage> &&msg) {
DynamicMessage::DynamicBuilder builder{*msg};
message.messages.push_back(std::move(msg));
return builder;
}
Reader asReader() const { return Reader{message}; }
};

View File

@ -15,6 +15,41 @@ namespace gin {
using msg_union_id_t = uint32_t;
using msg_packet_length_t = uint64_t;
class ProtoKelCodec {
private:
struct ReadContext {
Buffer &buffer;
size_t offset = 0;
};
template <typename T> friend struct ProtoKelEncodeImpl;
template <typename T> friend struct ProtoKelDecodeImpl;
public:
struct Limits {
msg_packet_length_t packet_size;
Limits() : packet_size{4096} {}
Limits(msg_packet_length_t ps) : packet_size{ps} {}
};
struct Version {
size_t major;
size_t minor;
size_t security;
};
const Version version() const { return Version{0, 0, 0}; }
template <typename T>
Error encode(typename T::Reader reader, Buffer &buffer);
template <typename T>
Error decode(typename T::Builder builder, Buffer &buffer,
const Limits &limits = Limits{});
};
template <typename T> struct ProtoKelEncodeImpl;
template <typename T> struct ProtoKelEncodeImpl<MessagePrimitive<T>> {
@ -34,10 +69,13 @@ template <> struct ProtoKelEncodeImpl<MessagePrimitive<std::string>> {
Buffer &buffer) {
std::string_view view = data.get();
size_t size = view.size();
if ((sizeof(size) + size) > buffer.writeCompositeLength()) {
return recoverableError("Buffer too small");
Error error = buffer.writeRequireLength(sizeof(size) + size);
if (error.failed()) {
return error;
}
Error error = StreamValue<size_t>::encode(size, buffer);
error = StreamValue<size_t>::encode(size, buffer);
if (error.failed()) {
return error;
}
@ -81,7 +119,7 @@ template <typename... T> struct ProtoKelEncodeImpl<MessageList<T...>> {
template <size_t i = 0>
static typename std::enable_if<i == sizeof...(T), size_t>::type
sizeMembers(typename MessageList<T...>::Reader data) {
sizeMembers(typename MessageList<T...>::Reader) {
return 0;
}
@ -357,67 +395,70 @@ struct ProtoKelDecodeImpl<MessageUnion<MessageUnionMember<V, K>...>> {
}
};
class ProtoKelCodec {
public:
struct Version {
size_t major;
size_t minor;
size_t security;
};
template <typename T>
Error ProtoKelCodec::encode(typename T::Reader reader, Buffer &buffer) {
BufferView view{buffer};
const Version version() const { return Version{0, 0, 0}; }
msg_packet_length_t packet_length = ProtoKelEncodeImpl<T>::size(reader);
// Check the size of the packet for the first
// message length description
template <typename T>
Error encode(typename T::Reader reader, Buffer &buffer) {
msg_packet_length_t packet_length = ProtoKelEncodeImpl<T>::size(reader);
// Check the size of the packet for the first
// message length description
if (buffer.writeCompositeLength() <
(packet_length + sizeof(msg_packet_length_t))) {
return recoverableError("Buffer too small");
}
{
Error error =
StreamValue<msg_packet_length_t>::encode(packet_length, buffer);
if (error.failed()) {
return error;
}
}
{
Error error = ProtoKelEncodeImpl<T>::encode(reader, buffer);
if (error.failed()) {
return error;
}
}
return noError();
};
template <typename T>
Error decode(typename T::Builder builder, Buffer &buffer) {
msg_packet_length_t packet_length = 0;
{
Error error =
StreamValue<msg_packet_length_t>::decode(packet_length, buffer);
if (error.failed()) {
return error;
}
}
{
Error error = ProtoKelDecodeImpl<T>::decode(builder, buffer);
if (error.failed()) {
return error;
}
}
{
if (ProtoKelEncodeImpl<T>::size(builder.asReader()) !=
packet_length) {
return criticalError("Bad packet format");
}
}
return noError();
Error error =
view.writeRequireLength(packet_length + sizeof(msg_packet_length_t));
if (error.failed()) {
return error;
}
};
{
Error error =
StreamValue<msg_packet_length_t>::encode(packet_length, view);
if (error.failed()) {
return error;
}
}
{
Error error = ProtoKelEncodeImpl<T>::encode(reader, view);
if (error.failed()) {
return error;
}
}
buffer.writeAdvance(view.writeOffset());
return noError();
}
template <typename T>
Error ProtoKelCodec::decode(typename T::Builder builder, Buffer &buffer,
const Limits &limits) {
BufferView view{buffer};
msg_packet_length_t packet_length = 0;
{
Error error =
StreamValue<msg_packet_length_t>::decode(packet_length, view);
if (error.failed()) {
return error;
}
}
if (packet_length > limits.packet_size) {
return criticalError("Packet size too big");
}
{
Error error = ProtoKelDecodeImpl<T>::decode(builder, view);
if (error.failed()) {
return error;
}
}
{
if (ProtoKelEncodeImpl<T>::size(builder.asReader()) != packet_length) {
return criticalError("Bad packet format");
}
}
buffer.readAdvance(view.readOffset());
return noError();
}
} // namespace gin

View File

@ -7,17 +7,23 @@
#include <cstring>
namespace gin {
/**
* Helper class to encode/decode any primtive type into/from litte endian.
* The shift class does this by shifting bytes. This type of procedure is
* 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> class ShiftStreamValue<T, 1> {
public:
inline static Error decode(T &val, Buffer &buffer) {
uint8_t& raw = reinterpret_cast<uint8_t&>(val);
uint8_t &raw = reinterpret_cast<uint8_t &>(val);
return buffer.pop(raw, sizeof(T));
}
inline static Error encode(const T &val, Buffer &buffer) {
uint8_t& raw = reinterpret_cast<uint8_t&>(val);
const uint8_t &raw = reinterpret_cast<const uint8_t &>(val);
return buffer.push(raw, sizeof(T));
}
@ -30,26 +36,24 @@ public:
if (buffer.readCompositeLength() < sizeof(T)) {
return recoverableError("Buffer too small");
}
uint16_t& raw = reinterpret_cast<uint16_t&>(val);
uint8_t buf[sizeof(T)] = 0;
Error error = buffer.pop(buf, sizeof(T));
if(error.failed()){
return error;
}
uint16_t raw = 0;
for (size_t i = 0; i < sizeof(T); ++i) {
raw |= buf[i] << (i * 8);
raw |= buffer.read(i) << (i * 8);
}
memcpy(&val, &raw, sizeof(T));
buffer.readAdvance(sizeof(T));
return noError();
}
inline static Error encode(const T &val, Buffer &buffer) {
if (buffer.writeCompositeLength() < sizeof(T)) {
return recoverableError("Buffer too small");
Error error = buffer.writeRequireLength(sizeof(T));
if (error.failed()) {
return error;
}
uint16_t raw;
memcpy(&raw, &val, sizeof(T));
@ -83,9 +87,11 @@ public:
}
inline static Error encode(const T &val, Buffer &buffer) {
if (buffer.writeCompositeLength() < sizeof(T)) {
return recoverableError("Buffer too small");
Error error = buffer.writeRequireLength(sizeof(T));
if (error.failed()) {
return error;
}
uint32_t raw;
memcpy(&raw, &val, sizeof(T));
@ -119,9 +125,11 @@ public:
}
inline static Error encode(const T &val, Buffer &buffer) {
if (buffer.writeCompositeLength() < sizeof(T)) {
return recoverableError("Buffer too small");
Error error = buffer.writeRequireLength(sizeof(T));
if (error.failed()) {
return error;
}
uint64_t raw;
memcpy(&raw, &val, sizeof(T));

View File

@ -4,6 +4,11 @@
#include <string_view>
namespace gin {
/**
* Helper object which creates a templated string from the provided string
* literal. It guarantees compile time uniqueness and thus allows using strings
* in template parameters.
*/
template <typename T, T... Chars> class StringLiteral {
public:
static constexpr std::array<T, sizeof...(Chars) + 1u> data = {Chars...,

View File

@ -1,6 +1,7 @@
#pragma once
#include <kelgin/common.h>
#include <kelgin/io.h>
namespace gin {
class TlsContext {
@ -17,15 +18,13 @@ private:
public:
TlsContext();
~TlsContext();
};
class TlsNetwork : public Network {
class TlsNetwork final : public Network {
private:
public:
Own<NetworkAddress> parseAddress(const std::string &,
uint16_t port_hint = 0) override;
Conveyor<Own<NetworkAddress>> parseAddress(const std::string &,
uint16_t port_hint = 0) override;
};
} // namespace gin

View File

@ -1,6 +1,6 @@
#include "suite/suite.h"
#include "source/async.h"
#include "source/kelgin/async.h"
namespace {
GIN_TEST("Async Immediate"){
@ -19,7 +19,7 @@ GIN_TEST("Async Immediate"){
ErrorOr<bool> error_or_number = is_number.take();
GIN_EXPECT(!error_or_number.isError(), "Return is an error: " + error_or_number.error().message());
GIN_EXPECT(!error_or_number.isError(), error_or_number.error().message());
GIN_EXPECT(error_or_number.isValue(), "Return is not a value");
GIN_EXPECT(error_or_number.value(), "Value is not 5");
}
@ -36,7 +36,7 @@ GIN_TEST("Async Adapt"){
ErrorOr<size_t> foo = feeder_conveyor.conveyor.take();
GIN_EXPECT(!foo.isError(), "Return is an error: " + foo.error().message());
GIN_EXPECT(!foo.isError(), foo.error().message());
GIN_EXPECT(foo.isValue(), "Return is not a value");
GIN_EXPECT(foo.value() == 5, "Values not 5, but " + std::to_string(foo.value()));
}
@ -54,7 +54,7 @@ GIN_TEST("Async Adapt Multiple"){
ErrorOr<size_t> foo = feeder_conveyor.conveyor.take();
GIN_EXPECT(!foo.isError(), "Return is an error: " + foo.error().message());
GIN_EXPECT(!foo.isError(), foo.error().message());
GIN_EXPECT(foo.isValue(), "Return is not a value");
GIN_EXPECT(foo.value() == 5, "Values not 5, but " + std::to_string(foo.value()));
@ -62,7 +62,7 @@ GIN_TEST("Async Adapt Multiple"){
ErrorOr<size_t> bar = feeder_conveyor.conveyor.take();
GIN_EXPECT(!bar.isError(), "Return is an error: " + bar.error().message());
GIN_EXPECT(!foo.isError(), bar.error().message());
GIN_EXPECT(bar.isValue(), "Return is not a value");
GIN_EXPECT(bar.value() == 10, "Values not 10, but " + std::to_string(bar.value()));
@ -72,11 +72,11 @@ GIN_TEST("Async Adapt Multiple"){
ErrorOr<size_t> a = feeder_conveyor.conveyor.take();
ErrorOr<size_t> b = feeder_conveyor.conveyor.take();
GIN_EXPECT(!a.isError(), "Return is an error: " + a.error().message());
GIN_EXPECT(!foo.isError(), a.error().message());
GIN_EXPECT(a.isValue(), "Return is not a value");
GIN_EXPECT(a.value() == 2, "Values not 2, but " + std::to_string(a.value()));
GIN_EXPECT(!b.isError(), "Return is an error: " + b.error().message());
GIN_EXPECT(!foo.isError(), b.error().message());
GIN_EXPECT(b.isValue(), "Return is not a value");
GIN_EXPECT(b.value() == 4234, "Values not 4234, but " + std::to_string(b.value()));
}
@ -97,7 +97,7 @@ GIN_TEST("Async Conversion"){
ErrorOr<std::string> foo = string_conveyor.take();
GIN_EXPECT(!foo.isError(), "Return is an error: " + foo.error().message());
GIN_EXPECT(!foo.isError(), foo.error().message());
GIN_EXPECT(foo.isValue(), "Return is not a value");
GIN_EXPECT(foo.value() == std::to_string(10), "Values is not 10, but " + foo.value());
}
@ -122,7 +122,7 @@ GIN_TEST("Async Conversion Multistep"){
ErrorOr<bool> foo = conveyor.take();
GIN_EXPECT(!foo.isError(), "Return is an error: " + foo.error().message());
GIN_EXPECT(!foo.isError(), foo.error().message());
GIN_EXPECT(foo.isValue(), "Return is not a value");
GIN_EXPECT(foo.value(), "Values is not true");
}
@ -165,20 +165,37 @@ GIN_TEST("Async Scheduling"){
ErrorOr<std::string> foo_10 = string_conveyor.take();
GIN_EXPECT(!foo_10.isError(), "Return is an error: " + foo_10.error().message());
GIN_EXPECT(!foo_10.isError(), foo_10.error().message());
GIN_EXPECT(foo_10.isValue(), "Return is not a value");
GIN_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();
GIN_EXPECT(!foo_20.isError(), "Return is an error: " + foo_20.error().message());
GIN_EXPECT(!foo_20.isError(), foo_20.error().message());
GIN_EXPECT(foo_20.isValue(), "Return is not a value");
GIN_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();
GIN_EXPECT(!foo_30.isError(), "Return is an error: " + foo_30.error().message());
GIN_EXPECT(!foo_30.isError(), foo_30.error().message());
GIN_EXPECT(foo_30.isValue(), "Return is not a value");
GIN_EXPECT(foo_30.value() == (std::string{"pre"} + std::to_string(33) + std::string{"post"}), "Values is not pre33post, but " + foo_30.value());
}
GIN_TEST("Async detach"){
using namespace gin;
EventLoop event_loop;
WaitScope wait_scope{event_loop};
int num = 0;
Conveyor<int>{10}.then([&num](int bar){
num = bar;
}).detach();
wait_scope.poll();
GIN_EXPECT(num == 10, std::string{"Bad value: Expected 10, but got "} + std::to_string(num));
}
}

31
test/data/json.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <string_view>
#include "message.h"
namespace gin {
const std::string_view json_org_example =
R"({
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
})";
}

View File

@ -4,8 +4,10 @@
#include <string>
#include "buffer.h"
#include "source/message.h"
#include "source/json.h"
#include "source/kelgin/message.h"
#include "source/kelgin/json.h"
#include "./data/json.h"
using gin::MessageList;
using gin::MessageStruct;
@ -40,7 +42,8 @@ GIN_TEST("JSON List Encoding"){
typedef MessageStruct<
MessageStructMember<MessagePrimitive<uint32_t>, decltype("test_uint"_t)>,
MessageStructMember<MessagePrimitive<std::string>, decltype("test_string"_t)>,
MessageStructMember<MessagePrimitive<std::string>, decltype("test_name"_t)>
MessageStructMember<MessagePrimitive<std::string>, decltype("test_name"_t)>,
MessageStructMember<MessagePrimitive<bool>, decltype("test_bool"_t)>
> TestStruct;
GIN_TEST("JSON Struct Encoding"){
@ -57,11 +60,13 @@ GIN_TEST("JSON Struct Encoding"){
auto string_name = root.init<decltype("test_name"_t)>();
string_name.set("test_name"_t.view());
root.init<decltype("test_bool"_t)>().set(false);
JsonCodec codec;
RingBuffer temp_buffer;
codec.encode<TestStruct>(root.asReader(), temp_buffer);
std::string expected_result{"{\"test_uint\":23,\"test_string\":\"foo\",\"test_name\":\"test_name\"}"};
std::string expected_result{"{\"test_uint\":23,\"test_string\":\"foo\",\"test_name\":\"test_name\",\"test_bool\":false}"};
std::string tmp_string = temp_buffer.toString();
GIN_EXPECT(tmp_string == expected_result, std::string{"Bad encoding:\n"} + tmp_string);
@ -86,7 +91,7 @@ GIN_TEST("JSON Union Encoding"){
Error error = codec.encode<TestUnion>(root.asReader(), buffer);
GIN_EXPECT(!error.failed(), "Error: " + error.message());
GIN_EXPECT(!error.failed(), error.message());
std::string expected_result{"{\"test_uint\":23}"};
@ -105,7 +110,7 @@ GIN_TEST("JSON Union Encoding"){
Error error = codec.encode<TestUnion>(root.asReader(), buffer);
GIN_EXPECT(!error.failed(), "Error: " + error.message());
GIN_EXPECT(!error.failed(), error.message());
std::string expected_result{"{\"test_string\":\"foo\"}"};
@ -119,7 +124,8 @@ GIN_TEST("JSON Struct Decoding"){
{
"test_string" :"banana" ,
"test_uint" : 5,
"test_name" : "keldu"
"test_name" : "keldu",
"test_bool" : true
})";
auto builder = heapMessageBuilder();
@ -135,6 +141,28 @@ GIN_TEST("JSON Struct Decoding"){
GIN_EXPECT( reader.get<decltype("test_string"_t)>().get() == "banana", "Test String has wrong value" );
GIN_EXPECT( reader.get<decltype("test_uint"_t)>().get() == 5, "Test Unsigned has wrong value" );
GIN_EXPECT( reader.get<decltype("test_name"_t)>().get() == "keldu", "Test Name has wrong value" );
GIN_EXPECT( reader.get<decltype("test_bool"_t)>().get() == true, "Test Bool has wrong value" );
}
GIN_TEST("JSON List Decoding"){
std::string json_string = R"(
[
12,
"free"
])";
auto builder = heapMessageBuilder();
TestList::Builder root = builder.initRoot<TestList>();
JsonCodec codec;
RingBuffer temp_buffer;
temp_buffer.push(*reinterpret_cast<const uint8_t*>(json_string.data()), json_string.size());
Error error = codec.decode<TestList>(root, temp_buffer);
GIN_EXPECT( !error.failed(), error.message() );
auto reader = root.asReader();
GIN_EXPECT( reader.get<0>().get() == 12, "Test Unsigned has wrong value" );
GIN_EXPECT( reader.get<1>().get() == "free", "Test String has wrong value" );
}
typedef MessageStruct<
@ -149,7 +177,8 @@ GIN_TEST("JSON Struct Decoding Two layer"){
"test_struct" :{
"test_string" : "banana",
"test_uint": 40,
"test_name":"HaDiKo"
"test_name":"HaDiKo",
"test_bool" :false
},
"test_uint": 5,
"test_name" : "keldu"
@ -171,7 +200,90 @@ GIN_TEST("JSON Struct Decoding Two layer"){
GIN_EXPECT( inner_reader.get<decltype("test_string"_t)>().get() == "banana", "Test String has wrong value" );
GIN_EXPECT( inner_reader.get<decltype("test_uint"_t)>().get() == 40, "Test Unsigned has wrong value" );
GIN_EXPECT( inner_reader.get<decltype("test_name"_t)>().get() == "HaDiKo", "Test Name has wrong value" );
GIN_EXPECT( inner_reader.get<decltype("test_bool"_t)>().get() == false, "Test Bool has wrong value" );
GIN_EXPECT( reader.get<decltype("test_uint"_t)>().get() == 5, "Test Unsigned has wrong value" );
GIN_EXPECT( reader.get<decltype("test_name"_t)>().get() == "keldu", "Test Name has wrong value" );
}
}
typedef MessageStruct<
MessageStructMember<
MessageStruct<
MessageStructMember< MessagePrimitive<std::string>, decltype("title"_t)>,
MessageStructMember<
MessageStruct<
MessageStructMember<MessagePrimitive<std::string>,decltype("title"_t)>,
MessageStructMember<
MessageStruct<
MessageStructMember<
MessageStruct<
MessageStructMember<MessagePrimitive<std::string>,decltype("ID"_t)>,
MessageStructMember<MessagePrimitive<std::string>,decltype("SortAs"_t)>,
MessageStructMember<MessagePrimitive<std::string>,decltype("GlossTerm"_t)>,
MessageStructMember<MessagePrimitive<std::string>,decltype("Acronym"_t)>,
MessageStructMember<MessagePrimitive<std::string>,decltype("Abbrev"_t)>,
MessageStructMember<
MessageStruct<
MessageStructMember<MessagePrimitive<std::string>, decltype("para"_t)>,
MessageStructMember<
MessageList<
MessagePrimitive<std::string>,
MessagePrimitive<std::string>
>
, decltype("GlossSeeAlso"_t)>
>
, decltype("GlossDef"_t)>,
MessageStructMember<MessagePrimitive<std::string>, decltype("GlossSee"_t)>
>
, decltype("GlossEntry"_t)>
>
, decltype("GlossList"_t)>
>
, decltype("GlossDiv"_t)>
>
, decltype("glossary"_t)>
> TestJsonOrgExample;
GIN_TEST ("JSON.org Decoding Example"){
auto builder = heapMessageBuilder();
TestJsonOrgExample::Builder root = builder.initRoot<TestJsonOrgExample>();
JsonCodec codec;
RingBuffer temp_buffer;
temp_buffer.push(*reinterpret_cast<const uint8_t*>(gin::json_org_example.data()), gin::json_org_example.size());
Error error = codec.decode<TestJsonOrgExample>(root, temp_buffer);
GIN_EXPECT(!error.failed(), error.message());
auto reader = root.asReader();
auto glossary_reader = reader.get<decltype("glossary"_t)>();
GIN_EXPECT(glossary_reader.get<decltype("title"_t)>().get() == "example glossary", "Bad glossary title");
auto gloss_div_reader = glossary_reader.get<decltype("GlossDiv"_t)>();
GIN_EXPECT(gloss_div_reader.get<decltype("title"_t)>().get() == "S", "bad gloss div value" );
auto gloss_list_reader = gloss_div_reader.get<decltype("GlossList"_t)>();
auto gloss_entry_reader = gloss_list_reader.get<decltype("GlossEntry"_t)>();
GIN_EXPECT(gloss_entry_reader.get<decltype("ID"_t)>().get() == "SGML", "bad ID value" );
GIN_EXPECT(gloss_entry_reader.get<decltype("SortAs"_t)>().get() == "SGML", "bad SortAs value" );
GIN_EXPECT(gloss_entry_reader.get<decltype("GlossTerm"_t)>().get() == "Standard Generalized Markup Language", "bad GlossTerm value" );
GIN_EXPECT(gloss_entry_reader.get<decltype("Acronym"_t)>().get() == "SGML", "bad Acronym value" );
GIN_EXPECT(gloss_entry_reader.get<decltype("Abbrev"_t)>().get() == "ISO 8879:1986", "bad Abbrev value" );
GIN_EXPECT(gloss_entry_reader.get<decltype("GlossSee"_t)>().get() == "markup", "bad GlossSee value" );
auto gloss_def_reader = gloss_entry_reader.get<decltype("GlossDef"_t)>();
GIN_EXPECT(gloss_def_reader.get<decltype("para"_t)>().get() == "A meta-markup language, used to create markup languages such as DocBook.", "para value wrong");
auto gloss_see_also_reader = gloss_def_reader.get<decltype("GlossSeeAlso"_t)>();
GIN_EXPECT(gloss_see_also_reader.get<0>().get() == "GML", "List 0 value wrong");
GIN_EXPECT(gloss_see_also_reader.get<1>().get() == "XML", "List 1 value wrong");
// (void) gloss_div_reader;
}
}

View File

@ -3,7 +3,7 @@
#include <cstdint>
#include <string>
#include "source/message.h"
#include "source/kelgin/message.h"
using gin::MessageList;
using gin::MessageStruct;
using gin::MessageStructMember;

View File

@ -1,6 +1,6 @@
#include "suite/suite.h"
#include "source/proto_kel.h"
#include "source/kelgin/proto_kel.h"
#include <iostream>
@ -33,7 +33,7 @@ GIN_TEST("Primitive Encoding"){
Error error = ProtoKelEncodeImpl<TestSize>::encode(root.asReader(), temp_buffer);
GIN_EXPECT(!error.failed(), "Error: " + error.message());
GIN_EXPECT(!error.failed(), error.message());
GIN_EXPECT(temp_buffer.readCompositeLength() == sizeof(value), "Bad Size: " + std::to_string(temp_buffer.readCompositeLength()));
GIN_EXPECT(temp_buffer[0] == 5 && temp_buffer[1] == 0 && temp_buffer[2] == 0 && temp_buffer[3] == 0, "Wrong encoded values");
}
@ -54,7 +54,7 @@ GIN_TEST("List Encoding"){
Error error = codec.encode<TestList>(root.asReader(), buffer);
GIN_EXPECT(!error.failed(), "Error: " + error.message());
GIN_EXPECT(!error.failed(), error.message());
GIN_EXPECT(buffer.readCompositeLength() == 14, "Bad Size: " + std::to_string(buffer.readCompositeLength()));
GIN_EXPECT("06 00 00 00\n00 00 00 00\nbf 94 20 00\n5f ab" == buffer.toHex(), "Not equal encoding\n"+buffer.toHex());
}
@ -80,7 +80,7 @@ GIN_TEST("Struct Encoding"){
Error error = codec.encode<TestStruct>(root.asReader(), buffer);
GIN_EXPECT(!error.failed(), "Error: " + error.message());
GIN_EXPECT(!error.failed(), error.message());
GIN_EXPECT(buffer.readCompositeLength() == 40, "Bad Size: " + std::to_string(buffer.readCompositeLength()));
GIN_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());
@ -100,7 +100,7 @@ GIN_TEST("Union Encoding"){
Error error = codec.encode<TestUnion>(root.asReader(), buffer);
GIN_EXPECT(!error.failed(), "Error: " + error.message());
GIN_EXPECT(!error.failed(), error.message());
GIN_EXPECT(buffer.readCompositeLength() == 16, "Bad Size: " + std::to_string(buffer.readCompositeLength()));
GIN_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());
@ -117,7 +117,7 @@ GIN_TEST("Union Encoding"){
Error error = codec.encode<TestUnion>(root.asReader(), buffer);
GIN_EXPECT(!error.failed(), "Error: " + error.message());
GIN_EXPECT(!error.failed(), error.message());
GIN_EXPECT(buffer.readCompositeLength() == 23, "Bad Size: " + std::to_string(buffer.readCompositeLength()));
GIN_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());
@ -137,7 +137,7 @@ GIN_TEST("List Decoding"){
auto root = builder.initRoot<TestList>();
Error error = codec.decode<TestList>(root, buffer);
GIN_EXPECT(!error.failed(), std::string{"Error: "} + error.message());
GIN_EXPECT(!error.failed(), error.message());
auto reader = root.asReader();
@ -166,7 +166,7 @@ GIN_TEST("Struct Decoding"){
auto test_uint = reader.get<decltype("test_uint"_t)>();
auto test_name = reader.get<decltype("test_name"_t)>();
GIN_EXPECT(!error.failed(), std::string{"Error: "} + error.message());
GIN_EXPECT(!error.failed(), error.message());
GIN_EXPECT(foo_string.get() == "foo" && test_uint.get() == 23 && test_name.get() == "test_name", "Values not correctly decoded");
}
@ -185,7 +185,7 @@ GIN_TEST("Union Decoding"){
Error error = codec.decode<TestUnion>(root, buffer);
GIN_EXPECT(!error.failed(), "Error: " + error.message());
GIN_EXPECT(!error.failed(), error.message());
GIN_EXPECT(reader.holdsAlternative<decltype("test_string"_t)>(), "Wrong union value");
auto str_rd = reader.get<decltype("test_string"_t)>();
GIN_EXPECT(str_rd.get() == "foo", "Wrong value: " + std::string{str_rd.get()});

View File

@ -3,6 +3,7 @@
#include <string>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include "common.h"
@ -35,7 +36,8 @@ public:
}GIN_UNIQUE_NAME(testCase); \
void GIN_UNIQUE_NAME(TestCase)::run()
#define GIN_EXPECT(expr, msg) \
#define GIN_EXPECT(expr, msg_split) \
if( ! (expr) ){ \
throw std::runtime_error{msg}; \
auto msg = msg_split; \
throw std::runtime_error{std::string{msg}};\
}