Skip to content

Commit

Permalink
New autogen architecture
Browse files Browse the repository at this point in the history
This rework prepares Fibre servers for the upcoming
rework of the low level protocol (that will add remote
coroutine call support) and the rework of the object
model (that add support for dynamically adding/removing
objects).

It also decouples the user's server application from
autogenerated Fibre interfaces such that user classes
no longer need to inherit from autogenerated classes.
This is expected to reduce compile time and improve
user code organization.
  • Loading branch information
samuelsadok committed Mar 25, 2021
1 parent 1817a56 commit 7f3bb3c
Show file tree
Hide file tree
Showing 28 changed files with 1,409 additions and 453 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"name": "C++ Test Server",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/test/test_server.elf",
"program": "${workspaceFolder}/test/build/test_server.elf",
"stopAtEntry": false,
"cwd": "${workspaceFolder}/test",
"environment": [{"name": "FIBRE_LOG", "value": "5"}],
Expand Down
121 changes: 121 additions & 0 deletions cpp/codecs.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#ifndef __FIBRE_CODECS_HPP
#define __FIBRE_CODECS_HPP

#include "logging.hpp"
#include "static_exports.hpp"
#include <fibre/simple_serdes.hpp>
#include <fibre/bufptr.hpp>
#include <optional>
#include "property.hpp"

DEFINE_LOG_TOPIC(CODEC);
#define current_log_topic LOG_TOPIC_CODEC

namespace fibre {

template<typename T, typename = void>
struct Codec {
static std::optional<T> decode(Domain* domain, cbufptr_t* buffer) {
printf("unknown decoder for %s\n", typeid(T).name());
return std::nullopt; }
};

template<> struct Codec<bool> {
static std::optional<bool> decode(Domain* domain, cbufptr_t* buffer) { return (buffer->begin() == buffer->end()) ? std::nullopt : std::make_optional((bool)*(buffer->begin()++)); }
static bool encode(bool value, bufptr_t* buffer) { return SimpleSerializer<uint8_t, false>::write(value, &(buffer->begin()), buffer->end()); }
};
template<> struct Codec<int8_t> {
static std::optional<int8_t> decode(Domain* domain, cbufptr_t* buffer) { return SimpleSerializer<int8_t, false>::read(&(buffer->begin()), buffer->end()); }
static bool encode(int8_t value, bufptr_t* buffer) { return SimpleSerializer<int8_t, false>::write(value, &(buffer->begin()), buffer->end()); }
};
template<> struct Codec<uint8_t> {
static std::optional<uint8_t> decode(Domain* domain, cbufptr_t* buffer) { return SimpleSerializer<uint8_t, false>::read(&(buffer->begin()), buffer->end()); }
static bool encode(uint8_t value, bufptr_t* buffer) { return SimpleSerializer<uint8_t, false>::write(value, &(buffer->begin()), buffer->end()); }
};
template<> struct Codec<int16_t> {
static std::optional<int16_t> decode(Domain* domain, cbufptr_t* buffer) { return SimpleSerializer<int16_t, false>::read(&(buffer->begin()), buffer->end()); }
static bool encode(int16_t value, bufptr_t* buffer) { return SimpleSerializer<int16_t, false>::write(value, &(buffer->begin()), buffer->end()); }
};
template<> struct Codec<uint16_t> {
static std::optional<uint16_t> decode(Domain* domain, cbufptr_t* buffer) { return SimpleSerializer<uint16_t, false>::read(&(buffer->begin()), buffer->end()); }
static bool encode(uint16_t value, bufptr_t* buffer) { return SimpleSerializer<uint16_t, false>::write(value, &(buffer->begin()), buffer->end()); }
};
template<> struct Codec<int32_t> {
static std::optional<int32_t> decode(Domain* domain, cbufptr_t* buffer) { return SimpleSerializer<int32_t, false>::read(&(buffer->begin()), buffer->end()); }
static bool encode(int32_t value, bufptr_t* buffer) { return SimpleSerializer<int32_t, false>::write(value, &(buffer->begin()), buffer->end()); }
};
template<> struct Codec<uint32_t> {
static std::optional<uint32_t> decode(Domain* domain, cbufptr_t* buffer) { return SimpleSerializer<uint32_t, false>::read(&(buffer->begin()), buffer->end()); }
static bool encode(uint32_t value, bufptr_t* buffer) { return SimpleSerializer<uint32_t, false>::write(value, &(buffer->begin()), buffer->end()); }
};
template<> struct Codec<int64_t> {
static std::optional<int64_t> decode(Domain* domain, cbufptr_t* buffer) { return SimpleSerializer<int64_t, false>::read(&(buffer->begin()), buffer->end()); }
static bool encode(int64_t value, bufptr_t* buffer) { return SimpleSerializer<int64_t, false>::write(value, &(buffer->begin()), buffer->end()); }
};
template<> struct Codec<uint64_t> {
static std::optional<uint64_t> decode(Domain* domain, cbufptr_t* buffer) { return SimpleSerializer<uint64_t, false>::read(&(buffer->begin()), buffer->end()); }
static bool encode(uint64_t value, bufptr_t* buffer) { return SimpleSerializer<uint64_t, false>::write(value, &(buffer->begin()), buffer->end()); }
};
template<> struct Codec<float> {
static std::optional<float> decode(Domain* domain, cbufptr_t* buffer) {
std::optional<uint32_t> int_val = Codec<uint32_t>::decode(domain, buffer);
return int_val.has_value() ? std::optional<float>(*reinterpret_cast<float*>(&*int_val)) : std::nullopt;
}
static bool encode(float value, bufptr_t* buffer) {
void* ptr = &value;
return Codec<uint32_t>::encode(*reinterpret_cast<uint32_t*>(ptr), buffer);
}
};
template<typename T>
struct Codec<T, std::enable_if_t<std::is_enum<T>::value>> {
using int_type = std::underlying_type_t<T>;
static std::optional<T> decode(Domain* domain, cbufptr_t* buffer) {
std::optional<int_type> int_val = SimpleSerializer<int_type, false>::read(&(buffer->begin()), buffer->end());
return int_val.has_value() ? std::make_optional(static_cast<T>(*int_val)) : std::nullopt;
}
static bool encode(T value, bufptr_t* buffer) { return SimpleSerializer<int_type, false>::write(value, &(buffer->begin()), buffer->end()); }
};

//template<> struct Codec<endpoint_ref_t> {
// static std::optional<endpoint_ref_t> decode(Domain* domain, cbufptr_t* buffer) {
// std::optional<uint16_t> val0 = SimpleSerializer<uint16_t, false>::read(&(buffer->begin()), buffer->end());
// std::optional<uint16_t> val1 = SimpleSerializer<uint16_t, false>::read(&(buffer->begin()), buffer->end());
// return (val0.has_value() && val1.has_value()) ? std::make_optional(endpoint_ref_t{*val1, *val0}) : std::nullopt;
// }
// static bool encode(endpoint_ref_t value, bufptr_t* buffer) {
// return SimpleSerializer<uint16_t, false>::write(value.endpoint_id, &(buffer->begin()), buffer->end())
// && SimpleSerializer<uint16_t, false>::write(value.json_crc, &(buffer->begin()), buffer->end());
// }
//};


//, std::enable_if_t<(get_interface_id<T>(), true)
template<typename T> struct Codec<T*> {
static std::optional<T*> decode(Domain* domain, cbufptr_t* buffer) {
uint8_t idx = (*buffer)[0]; // TODO: define actual decoder

// Check object type
ServerObjectDefinition* obj_entry = domain->get_server_object(idx);

if (!obj_entry) {
FIBRE_LOG(W) << "index out of range";
return std::nullopt;
}

if (obj_entry->interface != get_interface_id<T>()) {
FIBRE_LOG(W) << "incompatile interface: expected " << (int)obj_entry->interface << " but got " << (int)get_interface_id<T>();
return std::nullopt;
}

*buffer = buffer->skip(1);
return (T*)obj_entry->ptr;
}

static bool encode(bool value, bufptr_t* buffer) { return false; }
};

}

#undef current_log_topic

#endif // __FIBRE_CODECS_HPP
7 changes: 4 additions & 3 deletions cpp/endpoints_template.j2
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
#ifndef __FIBRE_ENDPOINTS_HPP
#define __FIBRE_ENDPOINTS_HPP

#include <fibre/introspection.hpp>
//#include <fibre/introspection.hpp>
#include <fibre/../../legacy_protocol.hpp>
#include <fibre/../../protocol.hpp>
#include <fibre/../../crc.hpp>

// Note: with -Og the functions with large switch statements reserves a huge amount
Expand All @@ -32,7 +33,7 @@ const unsigned char embedded_json[] = [[embedded_endpoint_definitions | to_c_str
const size_t embedded_json_length = sizeof(embedded_json) - 1;
const uint16_t json_crc_ = calc_crc16<CANONICAL_CRC16_POLYNOMIAL>(PROTOCOL_VERSION, embedded_json, embedded_json_length);
const uint32_t json_version_id_ = (json_crc_ << 16) | calc_crc16<CANONICAL_CRC16_POLYNOMIAL>(json_crc_, embedded_json, embedded_json_length);

/*
static void get_property(Introspectable& result, size_t idx) {
switch (idx) {
[%- for endpoint in endpoints %]
Expand Down Expand Up @@ -84,7 +85,7 @@ bool set_endpoint_from_float(endpoint_ref_t endpoint_ref, float value) {
const FloatSettableTypeInfo* type_info = dynamic_cast<const FloatSettableTypeInfo*>(property.get_type_info());
return type_info && type_info->set_float(property, value);
}

*/
}

#pragma GCC pop_options
Expand Down
18 changes: 17 additions & 1 deletion cpp/fibre.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <memory>
#include <algorithm>
#include <array>
#include "static_exports.hpp"

#if FIBRE_ALLOW_HEAP
#include <unordered_map>
Expand Down Expand Up @@ -240,7 +241,7 @@ void Domain::add_channels(ChannelDiscoveryResult result) {

#if FIBRE_ENABLE_CLIENT || FIBRE_ENABLE_SERVER
// Deleted during on_stopped()
auto protocol = new fibre::LegacyProtocolPacketBased(result.rx_channel, result.tx_channel, result.mtu);
auto protocol = new fibre::LegacyProtocolPacketBased(this, result.rx_channel, result.tx_channel, result.mtu);
#if FIBRE_ENABLE_CLIENT
protocol->start(MEMBER_CB(this, on_found_root_object), MEMBER_CB(this, on_lost_root_object), MEMBER_CB(this, on_stopped));
#else
Expand Down Expand Up @@ -268,3 +269,18 @@ void Domain::on_lost_root_object(LegacyObjectClient* obj_client, std::shared_ptr
void Domain::on_stopped(LegacyProtocolPacketBased* protocol, StreamStatus status) {
delete protocol;
}

#if FIBRE_ENABLE_SERVER
ServerFunctionDefinition* Domain::get_server_function(ServerFunctionId id) {
if (id < n_static_server_functions) {
return &static_server_function_table[id];
}
return nullptr;
}
ServerObjectDefinition* Domain::get_server_object(ServerObjectId id) {
if (id < n_static_server_objects) {
return &static_server_object_table[id];
}
return nullptr;
}
#endif
5 changes: 5 additions & 0 deletions cpp/include/fibre/callback.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ function_traits<_TRet, _TObj, _TArgs...> make_function_traits(_TRet (_TObj::*)(_
return {};
}

template<typename _TRet, typename _TObj, typename ... _TArgs>
function_traits<_TRet, _TObj, _TArgs...> make_function_traits(_TRet (_TObj::*)(_TArgs...) const) {
return {};
}

template<typename T1, T1 T2, typename T3, typename T4, typename T5>
struct MemberCallback;

Expand Down
23 changes: 23 additions & 0 deletions cpp/include/fibre/cpp_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,8 @@ class function_traits {
* For an empty TypeList, the return type is void. For a list with
* one type, the return type is equal to that type. For a list with
* more than one items, the return type is a tuple.
*
* TODO: this is redundant with as_tuple
*/
template<typename ... Types>
struct return_type;
Expand Down Expand Up @@ -1041,6 +1043,11 @@ struct result_of<TRet(&)(TArgs...)> {
using type = TRet;
};

template<typename TRet, typename TObj, typename... TArgs>
struct result_of<TRet(TObj::*)(TArgs...)> {
using type = TRet;
};

template<typename TRet, typename TObj, typename... TArgs>
struct result_of<TRet(TObj::*)(TArgs...) const> {
using type = TRet;
Expand Down Expand Up @@ -1079,16 +1086,32 @@ using tuple_cat_t = decltype(std::tuple_cat<TTuples...>(std::declval<TTuples>().
template<typename T = void>
struct as_tuple {
using type = std::tuple<T>;

template<typename TFunc>
static std::tuple<T> wrap_result(TFunc func) {
return {func()};
}
};

template<>
struct as_tuple<void> {
using type = std::tuple<>;

template<typename TFunc>
static std::tuple<> wrap_result(TFunc func) {
func();
return {};
}
};

template<typename... Ts>
struct as_tuple<std::tuple<Ts...>> {
using type = std::tuple<Ts...>;

template<typename TFunc>
static std::tuple<Ts...> wrap_result(TFunc func) {
return func();
}
};

template<typename T>
Expand Down
23 changes: 23 additions & 0 deletions cpp/include/fibre/fibre.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,24 @@ struct LegacyProtocolPacketBased;
class LegacyObjectClient;
struct LegacyObject;

#if FIBRE_ENABLE_SERVER
typedef uint8_t ServerFunctionId;
typedef uint8_t ServerInterfaceId;

// TODO: Use pointer instead? The codec that decodes the object still needs a table
// to prevent arbitrary memory access.
typedef uint8_t ServerObjectId;

struct ServerFunctionDefinition {
Callback<std::optional<CallBufferRelease>, Domain*, bool, bufptr_t, CallBuffers, Callback<std::optional<CallBuffers>, CallBufferRelease>> impl;
};

struct ServerObjectDefinition {
void* ptr;
ServerInterfaceId interface; // TODO: use pointer instead of index? Faster but needs more memory
};
#endif

class Domain {
friend struct Context;
public:
Expand All @@ -102,6 +120,11 @@ class Domain {

void add_channels(ChannelDiscoveryResult result);

#if FIBRE_ENABLE_SERVER
ServerFunctionDefinition* get_server_function(ServerFunctionId id);
ServerObjectDefinition* get_server_object(ServerObjectId id);
#endif

Context* ctx;
private:
#if FIBRE_ENABLE_CLIENT
Expand Down
Loading

0 comments on commit 7f3bb3c

Please sign in to comment.