tls preparations
This commit is contained in:
commit
7ce304aad2
|
@ -37,3 +37,4 @@ Currently no examples except in test.
|
|||
* Windows/Mac Support
|
||||
* Buffer flexibility
|
||||
* Multithreaded conveyor communication
|
||||
* Logger implementation
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
840
source/json.h
840
source/json.h
|
@ -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
|
|
@ -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() {
|
|
@ -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"
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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}; }
|
|
@ -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}; }
|
||||
};
|
||||
|
|
@ -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
|
|
@ -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));
|
||||
|
|
@ -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...,
|
|
@ -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
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})";
|
||||
}
|
130
test/json.cpp
130
test/json.cpp
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()});
|
||||
|
|
|
@ -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}};\
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue