Merge branch 'dev'

master
Claudius Holeksa 2021-12-28 11:17:45 +01:00
commit 7824acf553
9 changed files with 330 additions and 52 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 Claudius "keldu" Holeksa
Copyright (c) 2020,2021 Claudius "keldu" Holeksa
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -2,7 +2,7 @@
Asynchronous framework mostly inspired by [Capn'Proto](https://github.com/capnproto/capnproto) with the key difference of not
using Promises, but more reusable Pipelines/Conveyors. This introduces some challenges since I can't assume that only one
element gets passed along the chain, but it is managable. The advantage is that you have zero heap overhead by recreating the chain after every use.
element gets passed along the chain, but it is managable. The advantage is that you have zero heap overhead by not recreating the chain after every use.
Very early stage. I am currently rewriting my software to find a good interface solution by checking if I am comfortable with the current design.

View File

@ -73,7 +73,11 @@ public:
* Initialize a member by index
*/
template <size_t i>
typename Container::template ElementType<i>::Builder init() {
typename std::enable_if<
!SchemaIsArray<
typename MessageParameterPackType<i, V...>::Type>::Value,
typename Container::template ElementType<i>::Builder>::type
init() {
return typename Container::template ElementType<i>::Builder{
message.container.template get<i>()};
}
@ -83,14 +87,49 @@ public:
* This is the preferred method for schema::Struct messages
*/
template <StringLiteral Literal>
typename Container::template ElementType<
MessageParameterKeyPackIndex<Literal, Keys...>::Value>::Builder
typename std::enable_if<
!SchemaIsArray<typename MessageParameterPackType<
MessageParameterKeyPackIndex<Literal, Keys...>::Value,
V...>::Type>::Value,
typename Container::template ElementType<
MessageParameterKeyPackIndex<Literal,
Keys...>::Value>::Builder>::type
init() {
constexpr size_t i =
MessageParameterKeyPackIndex<Literal, Keys...>::Value;
return init<i>();
}
template <size_t i>
typename std::enable_if<
SchemaIsArray<
typename MessageParameterPackType<i, V...>::Type>::Value,
typename Container::template ElementType<i>::Builder>::type
init(size_t size) {
auto array_builder =
typename Container::template ElementType<i>::Builder{
message.container.template get<i>(), size};
return array_builder;
}
/*
* Version for array schema type
*/
template <StringLiteral Literal>
typename std::enable_if<
SchemaIsArray<typename MessageParameterPackType<
MessageParameterKeyPackIndex<Literal, Keys...>::Value,
V...>::Type>::Value,
typename Container::template ElementType<
MessageParameterKeyPackIndex<Literal,
Keys...>::Value>::Builder>::type
init(size_t size) {
constexpr size_t i =
MessageParameterKeyPackIndex<Literal, Keys...>::Value;
return init<i>(size);
}
};
class Reader {
@ -162,20 +201,58 @@ public:
Reader asReader() { return Reader{message}; }
template <size_t i>
typename Container::template ElementType<i>::Builder init() {
typename std::enable_if<
!SchemaIsArray<
typename MessageParameterPackType<i, V...>::Type>::Value,
typename Container::template ElementType<i>::Builder>::type
init() {
return typename Container::template ElementType<i>::Builder{
message.container.template get<i>()};
}
template <StringLiteral Literal>
typename Container::template ElementType<
MessageParameterKeyPackIndex<Literal, Keys...>::Value>::Builder
typename std::enable_if<
!SchemaIsArray<typename MessageParameterPackType<
MessageParameterKeyPackIndex<Literal, Keys...>::Value,
V...>::Type>::Value,
typename Container::template ElementType<
MessageParameterKeyPackIndex<Literal,
Keys...>::Value>::Builder>::type
init() {
constexpr size_t i =
MessageParameterKeyPackIndex<Literal, Keys...>::Value;
return init<i>();
}
/*
* If Schema is Array
*/
template <size_t i>
typename std::enable_if<
SchemaIsArray<
typename MessageParameterPackType<i, V...>::Type>::Value,
typename Container::template ElementType<i>::Builder>::type
init(size_t size) {
return typename Container::template ElementType<i>::Builder{
message.container.template get<i>(), size};
}
template <StringLiteral Literal>
typename std::enable_if<
SchemaIsArray<typename MessageParameterPackType<
MessageParameterKeyPackIndex<Literal, Keys...>::Value,
V...>::Type>::Value,
typename Container::template ElementType<
MessageParameterKeyPackIndex<Literal,
Keys...>::Value>::Builder>::type
init(size_t size) {
constexpr size_t i =
MessageParameterKeyPackIndex<Literal, Keys...>::Value;
return init<i>(size);
}
};
class Reader {
@ -219,11 +296,8 @@ public:
/*
* Array message class. Wrapper around an array schema element
* @todo Array class needs either a resize function or each message class has an
* individual call for Array children
*/
/*
template<class T, class Container>
template <class T, class Container>
class Message<schema::Array<T>, Container> final : public MessageBase {
private:
using SchemaType = schema::Array<T>;
@ -241,35 +315,42 @@ public:
class Reader;
class Builder {
private:
MessageType & message;
MessageType &message;
public:
Builder(MessageType& msg):message{msg}{}
Reader asReader(){return Reader{message};}
template<size_t i>
typename Container::MessageType::Builder init(){
return typename
Container::MessageType::Builder{message.container.get<i>()};
Builder(MessageType &msg, size_t size) : message{msg} {
if (size > 0) {
message.container.resize(size);
}
}
Reader asReader() { return Reader{message}; }
typename Container::ElementType::Builder init(size_t i) {
return typename Container::ElementType::Builder{
message.container.get(i)};
}
size_t size() const { return message.container.size(); }
};
class Reader {
private:
MessageType& message;
MessageType &message;
public:
Reader(MessageType& msg):message{msg}{}
Reader(MessageType &msg) : message{msg} {}
Builder asBuilder(){return Builder{message};}
Builder asBuilder() { return Builder{message, 0}; }
template<size_t i>
typename Container::MessageType::Reader get(){
return typename
Container::MessageType::Reader{message.container.get<i>()};
typename Container::ElementType::Reader get(size_t i) {
return typename Container::ElementType::Reader{
message.container.get(i)};
}
size_t size() const { return message.container.size(); }
};
};
*/
/*
* Tuple message class. Wrapper around a tuple schema
@ -299,10 +380,24 @@ public:
Reader asReader() { return Reader{message}; }
template <size_t i>
typename Container::template ElementType<i>::Builder init() {
typename std::enable_if<
!SchemaIsArray<
typename MessageParameterPackType<i, T...>::Type>::Value,
typename Container::template ElementType<i>::Builder>::type
init() {
return typename Container::template ElementType<i>::Builder{
message.container.template get<i>()};
}
template <size_t i>
typename std::enable_if<
SchemaIsArray<
typename MessageParameterPackType<i, T...>::Type>::Value,
typename Container::template ElementType<i>::Builder>::type
init(size_t size) {
return typename Container::template ElementType<i>::Builder{
message.container.template get<i>(), size};
}
};
class Reader {
private:
@ -395,7 +490,9 @@ public:
Reader asReader() { return Reader{message}; }
void set(const std::string &str) { message.container.set(str); }
void set(std::string &&str) { message.container.set(std::move(str)); }
void set(const std::string_view str) { message.container.set(str); }
void set(const char *str) { set(std::string_view{str}); }
};
class Reader {
@ -407,7 +504,7 @@ public:
Builder asBuilder() { return Builder{message}; }
std::string_view get() { return message.container.get(); }
const std::string_view get() const { return message.container.get(); }
};
};
@ -430,6 +527,28 @@ public:
}
};
template <class T, class Container>
class HeapMessageRoot<schema::Array<T>, Container> {
public:
using Schema = schema::Array<T>;
private:
Own<Message<Schema, Container>> root;
public:
HeapMessageRoot(Own<Message<Schema, Container>> r) : root{std::move(r)} {}
typename Message<Schema, Container>::Builder build(size_t size) {
assert(root);
return typename Message<Schema, Container>::Builder{*root, size};
}
typename Message<Schema, Container>::Reader read() {
assert(root);
return typename Message<Schema, Container>::Reader{*root};
}
};
/*
* Minor helper for creating a message root
*/

View File

@ -5,7 +5,7 @@
namespace gin {
template <class T> class MessageContainer;
template <class T, class Container> class Message;
template <class T, class Container = MessageContainer<T>> class Message;
template <size_t N, class... T> struct MessageParameterPackType;
@ -57,6 +57,14 @@ struct MessageParameterKeyPackIndex {
"Provided StringLiteral doesn't exist in searched list");
};
template <class T> struct SchemaIsArray {
constexpr static bool Value = false;
};
template <class T> struct SchemaIsArray<schema::Array<T>> {
constexpr static bool Value = true;
};
template <class... V, StringLiteral... Keys>
class MessageContainer<schema::Struct<schema::NamedMember<V, Keys>...>> {
private:
@ -104,20 +112,24 @@ public:
* Array storage
*/
/*
template<class T>
class MessageContainer<schema::Array> {
template <class T> class MessageContainer<schema::Array<T>> {
private:
using ValueType = std::vector<Message<T,MessageContainer<T>>>;
using ValueType = std::vector<Message<T, MessageContainer<T>>>;
ValueType values;
public:
using SchemaType = schema::Array<T>;
template<size_t i> Message<T,MessageContainer<T>>& get(){
return values.at(i);
using ElementType = Message<T, MessageContainer<T>>;
Message<T, MessageContainer<T>> &get(size_t index) {
return values.at(index);
}
void resize(size_t size) { values.resize(size); }
size_t size() const { return values.size(); }
};
*/
/*
* Tuple storage
@ -214,12 +226,15 @@ template <> class MessageContainer<schema::String> {
public:
using SchemaType = schema::String;
using ValueType = std::string;
using ValueViewType = std::string_view;
private:
ValueType value;
public:
void set(ValueType &&v) { value = std::move(v); }
void set(const ValueType &v) { value = v; }
void set(const ValueViewType v) { value = std::string{v}; }
const ValueType &get() const { return value; }
};

View File

@ -13,6 +13,7 @@ namespace gin {
* bigger than uint32_t max. At least I hope noone would do this
*/
using msg_union_id_t = uint32_t;
using msg_array_length_t = uint64_t;
using msg_packet_length_t = uint64_t;
class ProtoKelCodec {
@ -271,6 +272,48 @@ struct ProtoKelEncodeImpl<
return sizeof(msg_union_id_t) + sizeMembers<0>(reader);
}
};
template <class T, class Container>
struct ProtoKelEncodeImpl<Message<schema::Array<T>, Container>> {
static Error
encode(typename Message<schema::Array<T>, Container>::Reader data,
Buffer &buffer) {
msg_array_length_t array_length = data.size();
{
Error error =
StreamValue<msg_array_length_t>::encode(array_length, buffer);
if (error.failed()) {
return error;
}
}
for (size_t i = 0; i < array_length; ++i) {
Error error =
ProtoKelEncodeImpl<typename Container::ElementType>::encode(
data.get(i), buffer);
if (error.failed()) {
return error;
}
}
return noError();
}
/*
*
*/
static size_t
size(typename Message<schema::Array<T>, Container>::Reader data) {
size_t members = sizeof(msg_array_length_t);
for (size_t i = 0; i < data.size(); ++i) {
members +=
ProtoKelEncodeImpl<typename Container::ElementType>::size(
data.get(i));
}
return members;
}
};
/*
* Decode Implementations
*/
@ -436,6 +479,32 @@ struct ProtoKelDecodeImpl<
}
};
template <class T, class Container>
struct ProtoKelDecodeImpl<Message<schema::Array<T>, Container>> {
static Error
decode(typename Message<schema::Array<T>, Container>::Builder data,
Buffer &buffer) {
msg_array_length_t array_length = 0;
{
Error error =
StreamValue<msg_array_length_t>::decode(array_length, buffer);
if (error.failed()) {
return error;
}
}
for (size_t i = 0; i < array_length; ++i) {
Error error =
ProtoKelDecodeImpl<typename Container::ElementType>::decode(
data.init(i), buffer);
if (error.failed()) {
return error;
}
}
return noError();
}
};
template <class Schema, class Container>
Error ProtoKelCodec::encode(typename Message<Schema, Container>::Reader reader,
Buffer &buffer) {
@ -487,7 +556,12 @@ Error ProtoKelCodec::decode(
}
if (packet_length > limits.packet_size) {
return criticalError("Packet size too big");
return criticalError(
[packet_length]() {
return std::string{"Packet size too big: "} +
std::to_string(packet_length);
},
"Packet size too big");
}
{

View File

@ -6,6 +6,8 @@
#include <cstdint>
#include <cstring>
#include <iostream>
namespace gin {
/**
* Helper class to encode/decode any primtive type into/from litte endian.
@ -40,7 +42,7 @@ public:
uint16_t raw = 0;
for (size_t i = 0; i < sizeof(T); ++i) {
raw |= buffer.read(i) << (i * 8);
raw |= (static_cast<uint16_t>(buffer.read(i)) << (i * 8));
}
memcpy(&val, &raw, sizeof(T));
buffer.readAdvance(sizeof(T));
@ -78,7 +80,7 @@ public:
uint32_t raw = 0;
for (size_t i = 0; i < sizeof(T); ++i) {
raw |= buffer.read(i) << (i * 8);
raw |= (static_cast<uint32_t>(buffer.read(i)) << (i * 8));
}
memcpy(&val, &raw, sizeof(T));
buffer.readAdvance(sizeof(T));
@ -116,8 +118,9 @@ public:
uint64_t raw = 0;
for (size_t i = 0; i < sizeof(T); ++i) {
raw |= buffer.read(i) << (i * 8);
raw |= (static_cast<uint64_t>(buffer.read(i)) << (i * 8));
}
memcpy(&val, &raw, sizeof(T));
buffer.readAdvance(sizeof(T));
@ -146,4 +149,4 @@ public:
template <typename T> using StreamValue = ShiftStreamValue<T>;
} // namespace gin
} // namespace gin

View File

@ -182,7 +182,7 @@ GIN_TEST("Async Scheduling"){
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"){
GIN_TEST("Async Detach"){
using namespace gin;
EventLoop event_loop;
@ -227,4 +227,4 @@ GIN_TEST("Async Merge"){
GIN_EXPECT(!wrong_value, std::string{"Expected values 10 or 11"});
GIN_EXPECT(elements_passed == 3, std::string{"Expected 2 passed elements, got only "} + std::to_string(elements_passed));
}
}
}

View File

@ -13,7 +13,7 @@ namespace schema {
using TestTuple = schema::Tuple<schema::UInt32, schema::String>;
GIN_TEST("MessageList"){
GIN_TEST("Message Tuple"){
std::string test_string_1 = "banana";
auto root = gin::heapMessageRoot<TestTuple>();
@ -21,7 +21,7 @@ GIN_TEST("MessageList"){
auto uint = builder.init<0>();
uint.set(10);
auto string = builder.init<1>();
string.set(test_string_1);
string.set(std::string_view{test_string_1});
auto reader = root.read();
auto uint_reader = reader.get<0>();
@ -32,7 +32,7 @@ GIN_TEST("MessageList"){
using NestedTestTuple = schema::Tuple<schema::Tuple<schema::UInt32, schema::String>, schema::String>;
GIN_TEST("MessageList nested"){
GIN_TEST("Message Tuple nested"){
std::string test_string_1 = "banana";
std::string test_string_2 = "bat";
@ -62,7 +62,7 @@ using TestStruct = schema::Struct<
schema::NamedMember<schema::String, "test_name">
>;
GIN_TEST("MessageStruct"){
GIN_TEST("Message Struct"){
std::string test_string = "foo";
auto root = gin::heapMessageRoot<TestStruct>();
auto builder = root.build();
@ -83,6 +83,44 @@ GIN_TEST("MessageStruct"){
*/
test_string = "foo2";
GIN_EXPECT(uint_reader.get() == 23 && string_reader.get() != test_string && string_reader.get() == "foo" && name_reader.get() == "test_name", "wrong values");
GIN_EXPECT(uint_reader.get() == 23 && string_reader.get() != test_string && string_reader.get() == "foo" && name_reader.get() == "test_name", "Wrong values");
}
using TestArray = schema::Array<schema::UInt32>;
void arrayCheck(gin::Message<TestArray>::Builder builder){
auto one = builder.init(0);
auto two = builder.init(1);
auto three = builder.init(2);
one.set(24);
two.set(45);
three.set(1230);
auto reader = builder.asReader();
GIN_EXPECT(reader.get(0).get() == 24 && reader.get(1).get() == 45 && reader.get(2).get(), "Wrong values");
}
GIN_TEST("Message Array"){
auto root = gin::heapMessageRoot<TestArray>();
auto builder = root.build(3);
arrayCheck(builder);
}
using TestArrayStruct = schema::Struct<
schema::NamedMember<TestArray, "array">
>;
GIN_TEST("Message Array in Struct"){
auto root = gin::heapMessageRoot<TestArrayStruct>();
auto builder = root.build();
auto array = builder.init<"array">(3);
arrayCheck(array);
}
}

View File

@ -195,4 +195,33 @@ GIN_TEST("Union Decoding"){
auto str_rd = reader.get<"test_string">();
GIN_EXPECT(str_rd.get() == "foo", "Wrong value: " + std::string{str_rd.get()});
}
using TestArrayStruct = schema::Array<
TestStruct
>;
GIN_TEST("Array Encoding"){
using namespace gin;
ProtoKelCodec codec;
auto root = heapMessageRoot<TestArrayStruct>();
auto builder = root.build(2);
auto one = builder.init(0);
auto two = builder.init(1);
one.init<"test_uint">().set(4);
one.init<"test_string">().set("foo");
one.init<"test_name">().set("Fedor");
two.init<"test_uint">().set(9);
two.init<"test_string">().set("bar");
two.init<"test_name">().set("Bravo");
RingBuffer buffer;
Error error = codec.encode<TestArrayStruct>(root.read(), buffer);
GIN_EXPECT(!error.failed(), "Error occured");
}
}