*
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
#ifndef __FIBRE_ASYNC_STREAM_HPP
|
||||
#define __FIBRE_ASYNC_STREAM_HPP
|
||||
|
||||
#include <fibre/bufptr.hpp>
|
||||
#include <fibre/callback.hpp>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fibre {
|
||||
|
||||
enum StreamStatus {
|
||||
kStreamOk,
|
||||
kStreamCancelled,
|
||||
kStreamClosed,
|
||||
kStreamError
|
||||
};
|
||||
|
||||
|
||||
struct ReadResult {
|
||||
StreamStatus status;
|
||||
|
||||
/**
|
||||
* @brief The pointer to one position after the last byte that was
|
||||
* transferred.
|
||||
* This must always be in [buffer.begin(), buffer.end()], even if the
|
||||
* transfer was not succesful.
|
||||
* If the status is kStreamError or kStreamCancelled then the accuracy
|
||||
* of this field is not guaranteed.
|
||||
*/
|
||||
unsigned char* end;
|
||||
};
|
||||
|
||||
struct WriteResult {
|
||||
StreamStatus status;
|
||||
|
||||
/**
|
||||
* @brief The pointer to one position after the last byte that was
|
||||
* transferred.
|
||||
* This must always be in [buffer.begin(), buffer.end()], even if the
|
||||
* transfer was not succesful.
|
||||
* If the status is kStreamError or kStreamCancelled then the accuracy
|
||||
* of this field is not guaranteed.
|
||||
*/
|
||||
const unsigned char* end;
|
||||
};
|
||||
|
||||
|
||||
using TransferHandle = uintptr_t;
|
||||
|
||||
/**
|
||||
* @brief Base class for asynchronous stream sources.
|
||||
*/
|
||||
class AsyncStreamSource {
|
||||
public:
|
||||
/**
|
||||
* @brief Starts a read operation. Once the read operation completes,
|
||||
* on_finished.complete() is called.
|
||||
*
|
||||
* Most implementations only allow one transfer to be active at a time.
|
||||
*
|
||||
* TODO: specify if `completer` can be called directly within this function.
|
||||
*
|
||||
* @param buffer: The buffer where the data to be written shall be fetched from.
|
||||
* Must remain valid until `completer` is satisfied.
|
||||
* @param handle: The variable pointed to by this argument is set to an
|
||||
* opaque transfer handle that can be passed to cancel_read() as
|
||||
* long as the operation has not yet completed.
|
||||
* If the completer is invoked directly from start_read() then the
|
||||
* handle is not modified after this invokation. That means it's safe
|
||||
* for the completion handler to reuse the handle variable.
|
||||
* @param completer: The completer that will be completed once the operation
|
||||
* finishes, whether successful or not.
|
||||
* Must remain valid until it is satisfied.
|
||||
*/
|
||||
virtual void start_read(bufptr_t buffer, TransferHandle* handle, Callback<void, ReadResult> completer) = 0;
|
||||
|
||||
/**
|
||||
* @brief Cancels an operation that was previously started with start_read().
|
||||
*
|
||||
* The transfer is cancelled asynchronously and the associated completer
|
||||
* will eventually be completed with kStreamCancelled. Until then the
|
||||
* transfer must be considered still in progress and associated resources
|
||||
* must not be freed.
|
||||
*
|
||||
* TODO: specify if an implementation is allowed to return something other
|
||||
* than kStreamCancelled when the transfer was cancelled.
|
||||
*
|
||||
* This function must not be called once the stream has started to invoke
|
||||
* the associated completion handler. It must also not be called twice for
|
||||
* the same transfer.
|
||||
*/
|
||||
virtual void cancel_read(TransferHandle transfer_handle) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Base class for asynchronous stream sources.
|
||||
*
|
||||
* Thread-safety: Implementations are generally not required to provide thread
|
||||
* safety. Users should only call the functions of this class on the same thread
|
||||
* as the event loop on which the stream runs.
|
||||
*/
|
||||
class AsyncStreamSink {
|
||||
public:
|
||||
/**
|
||||
* @brief Starts a write operation. Once the write operation completes,
|
||||
* on_finished.complete() is called.
|
||||
*
|
||||
* Most implementations only allow one transfer to be active at a time.
|
||||
*
|
||||
* TODO: specify if `completer` can be called directly within this function.
|
||||
*
|
||||
* @param buffer: The buffer where the data to be written shall be fetched from.
|
||||
* Must remain valid until `completer` is satisfied.
|
||||
* @param handle: The variable pointed to by this argument is set to an
|
||||
* opaque transfer handle that can be passed to cancel_write() as
|
||||
* long as the operation has not yet completed.
|
||||
* If the completer is invoked directly from start_write() then the
|
||||
* handle is not modified after this invokation. That means it's safe
|
||||
* for the completion handler to reuse the handle variable.
|
||||
* @param completer: The completer that will be completed once the operation
|
||||
* finishes, whether successful or not.
|
||||
* Must remain valid until it is satisfied.
|
||||
*/
|
||||
virtual void start_write(cbufptr_t buffer, TransferHandle* handle, Callback<void, WriteResult> completer) = 0;
|
||||
|
||||
/**
|
||||
* @brief Cancels an operation that was previously started with start_write().
|
||||
*
|
||||
* The transfer is cancelled asynchronously and the associated completer
|
||||
* will eventually be completed with kStreamCancelled. Until then the
|
||||
* transfer must be considered still in progress and associated resources
|
||||
* must not be freed.
|
||||
*
|
||||
* TODO: specify if an implementation is allowed to return something other
|
||||
* than kStreamCancelled when the transfer was cancelled.
|
||||
*
|
||||
* This function must not be called once the stream has started to invoke
|
||||
* the associated completion handler. It must also not be called twice for
|
||||
* the same transfer.
|
||||
*/
|
||||
virtual void cancel_write(TransferHandle transfer_handle) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __FIBRE_ASYNC_STREAM_HPP
|
||||
@@ -0,0 +1,100 @@
|
||||
#ifndef __FIBRE_BUFPTR_HPP
|
||||
#define __FIBRE_BUFPTR_HPP
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
namespace fibre {
|
||||
|
||||
static inline bool soft_assert(bool expr) { return expr; } // TODO: implement
|
||||
|
||||
/**
|
||||
* @brief Holds a reference to a buffer and a length.
|
||||
* Since this class implements begin() and end(), you can use it with many
|
||||
* standard algorithms that operate on iterable objects.
|
||||
*/
|
||||
template<typename T>
|
||||
struct generic_bufptr_t {
|
||||
using iterator = T*;
|
||||
using const_iterator = const T*;
|
||||
|
||||
generic_bufptr_t(T* begin, size_t length) : begin_(begin), end_(begin + length) {}
|
||||
|
||||
generic_bufptr_t(T* begin, T* end) : begin_(begin), end_(end) {}
|
||||
|
||||
generic_bufptr_t() : begin_(nullptr), end_(nullptr) {}
|
||||
|
||||
template<size_t I>
|
||||
generic_bufptr_t(T (&begin)[I]) : generic_bufptr_t(begin, I) {}
|
||||
|
||||
generic_bufptr_t(std::vector<typename std::remove_const<T>::type>& vector)
|
||||
: generic_bufptr_t(vector.data(), vector.size()) {}
|
||||
|
||||
generic_bufptr_t(const std::vector<typename std::remove_const<T>::type>& vector)
|
||||
: generic_bufptr_t(vector.data(), vector.size()) {}
|
||||
|
||||
generic_bufptr_t(const generic_bufptr_t<typename std::remove_const<T>::type>& other)
|
||||
: generic_bufptr_t(other.begin(), other.end()) {}
|
||||
|
||||
generic_bufptr_t& operator+=(size_t num) {
|
||||
if (!soft_assert(num <= size())) {
|
||||
num = size();
|
||||
}
|
||||
begin_ += num;
|
||||
return *this;
|
||||
}
|
||||
|
||||
generic_bufptr_t operator++(int) {
|
||||
generic_bufptr_t result = *this;
|
||||
*this += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
T& operator*() {
|
||||
return *begin_;
|
||||
}
|
||||
|
||||
generic_bufptr_t take(size_t num) const {
|
||||
if (!soft_assert(num <= size())) {
|
||||
num = size();
|
||||
}
|
||||
generic_bufptr_t result = {begin_, num};
|
||||
return result;
|
||||
}
|
||||
|
||||
generic_bufptr_t skip(size_t num, size_t* processed_bytes = nullptr) const {
|
||||
if (!soft_assert(num <= size())) {
|
||||
num = size();
|
||||
}
|
||||
if (processed_bytes)
|
||||
(*processed_bytes) += num;
|
||||
return {begin_ + num, end_};
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return end_ - begin_;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
T*& begin() { return begin_; }
|
||||
T*& end() { return end_; }
|
||||
T* const & begin() const { return begin_; }
|
||||
T* const & end() const { return end_; }
|
||||
T& front() const { return *begin(); }
|
||||
T& back() const { return *(end() - 1); }
|
||||
T& operator[](size_t idx) { return *(begin() + idx); }
|
||||
|
||||
private:
|
||||
T* begin_;
|
||||
T* end_;
|
||||
};
|
||||
|
||||
using cbufptr_t = generic_bufptr_t<const unsigned char>;
|
||||
using bufptr_t = generic_bufptr_t<unsigned char>;
|
||||
|
||||
}
|
||||
|
||||
#endif // __FIBRE_BUFPTR_HPP
|
||||
@@ -0,0 +1,126 @@
|
||||
#ifndef __CALLBACK_HPP
|
||||
#define __CALLBACK_HPP
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <typeinfo>
|
||||
#include <tuple>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace fibre {
|
||||
|
||||
namespace detail {
|
||||
template<typename T> struct get_default { static T val() { return {}; } };
|
||||
template<> struct get_default<void> { static void val() {} };
|
||||
}
|
||||
|
||||
template<typename TRet, typename ... TArgs>
|
||||
class Callback {
|
||||
public:
|
||||
Callback() : cb_(nullptr), ctx_(nullptr) {}
|
||||
Callback(std::nullptr_t) : cb_(nullptr), ctx_(nullptr) {}
|
||||
Callback(TRet(*callback)(void*, TArgs...), void* ctx) :
|
||||
cb_(callback), ctx_(ctx) {}
|
||||
|
||||
/**
|
||||
* @brief Creates a copy of another Callback instance.
|
||||
*
|
||||
* This is only works it the other callback has identical template parameters.
|
||||
* This constructor is templated so that construction from an incompatible
|
||||
* callback gives a useful error message.
|
||||
*/
|
||||
//template<typename TRetOther, typename ... TArgsOther>
|
||||
//Callback(const Callback<TRetOther, TArgsOther...>& other) : cb_(other.cb_), ctx_(other.ctx_) {
|
||||
// static_assert(std::is_same<Callback<TRetOther, TArgsOther...>, Callback>::value, "incompatible callback type");
|
||||
//}
|
||||
|
||||
Callback(const Callback& other) : cb_(other.cb_), ctx_(other.ctx_) {}
|
||||
|
||||
// If you get a compile error "[...] invokes a deleted function" that points
|
||||
// here then you're probably trying to assign a Callback with incompatible
|
||||
// template arguments to another Callback.
|
||||
template<typename TRetOther, typename ... TArgsOther>
|
||||
Callback(const Callback<TRetOther, TArgsOther...>& other) = delete;
|
||||
|
||||
/**
|
||||
* @brief Constructs a callback object from a functor. The functor must
|
||||
* remain allocated throughout the lifetime of the Callback.
|
||||
*/
|
||||
template<typename TFunc>
|
||||
Callback(const TFunc& func) :
|
||||
cb_([](void* ctx, TArgs...args){
|
||||
return (*(const TFunc*)ctx)(args...);
|
||||
}), ctx_((void*)&func) {}
|
||||
|
||||
operator bool() {
|
||||
return cb_;
|
||||
}
|
||||
|
||||
TRet invoke(TArgs ... arg) const {
|
||||
if (cb_) {
|
||||
return (*cb_)(ctx_, arg...);
|
||||
}
|
||||
return detail::get_default<TRet>::val();
|
||||
}
|
||||
|
||||
TRet invoke_and_clear(TArgs ... arg) {
|
||||
void* ctx = ctx_;
|
||||
auto cb = cb_;
|
||||
ctx_ = nullptr;
|
||||
cb_ = nullptr;
|
||||
if (cb) {
|
||||
return (*cb)(ctx, arg...);
|
||||
}
|
||||
return detail::get_default<TRet>::val();
|
||||
}
|
||||
|
||||
typedef TRet(*cb_t)(void*, TArgs...);
|
||||
cb_t get_ptr() { return cb_; }
|
||||
void* get_ctx() { return ctx_; }
|
||||
|
||||
private:
|
||||
TRet(*cb_)(void*, TArgs...);
|
||||
void* ctx_;
|
||||
};
|
||||
|
||||
template<typename _TRet, typename _TObj, typename ... _TArgs>
|
||||
struct function_traits {
|
||||
using TRet = _TRet;
|
||||
using TArgs = std::tuple<_TArgs...>;
|
||||
using TObj = _TObj;
|
||||
};
|
||||
|
||||
template<typename _TRet, typename _TObj, typename ... _TArgs>
|
||||
function_traits<_TRet, _TObj, _TArgs...> make_function_traits(_TRet (_TObj::*)(_TArgs...)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T1, T1 T2, typename T3, typename T4, typename T5>
|
||||
struct MemberCallback;
|
||||
|
||||
template<typename T, T func, typename TObj, typename TRes, typename ... TArgs>
|
||||
struct MemberCallback<T, func, TObj, TRes, std::tuple<TArgs...>> {
|
||||
using cb_t = Callback<TRes, TArgs...>;
|
||||
static cb_t with(TObj* obj) {
|
||||
return cb_t{[](void* obj, TArgs... arg) {
|
||||
return (((TObj*)obj)->*func)(arg...);
|
||||
}, obj};
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, T func,
|
||||
typename TTraits = decltype(make_function_traits(func)),
|
||||
typename MemCb = MemberCallback<T, func, typename TTraits::TObj, typename TTraits::TRet, typename TTraits::TArgs>>
|
||||
typename MemCb::cb_t make_callback(typename TTraits::TObj* obj) {
|
||||
return MemCb::with(obj);
|
||||
}
|
||||
|
||||
#define MEMBER_CB(obj, func) \
|
||||
fibre::make_callback< \
|
||||
decltype(&std::remove_reference_t<decltype(*obj)>::func), \
|
||||
&std::remove_reference_t<decltype(*obj)>::func \
|
||||
>(obj)
|
||||
|
||||
}
|
||||
|
||||
#endif // __CALLBACK_HPP
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef __FIBRE_CHANNEL_DISCOVERER
|
||||
#define __FIBRE_CHANNEL_DISCOVERER
|
||||
|
||||
#include "async_stream.hpp"
|
||||
#include <fibre/callback.hpp>
|
||||
#include <fibre/status.hpp>
|
||||
|
||||
namespace fibre {
|
||||
|
||||
struct ChannelDiscoveryResult {
|
||||
Status status;
|
||||
AsyncStreamSource* rx_channel;
|
||||
AsyncStreamSink* tx_channel;
|
||||
size_t mtu;
|
||||
};
|
||||
|
||||
struct ChannelDiscoveryContext {};
|
||||
|
||||
class Domain; // defined in fibre.hpp
|
||||
|
||||
class ChannelDiscoverer {
|
||||
public:
|
||||
// TODO: maybe we should remove "handle" because a discovery can also be
|
||||
// uniquely identified by domain.
|
||||
virtual void start_channel_discovery(
|
||||
Domain* domain,
|
||||
const char* specs, size_t specs_len,
|
||||
ChannelDiscoveryContext** handle) = 0;
|
||||
virtual int stop_channel_discovery(ChannelDiscoveryContext* handle) = 0;
|
||||
|
||||
protected:
|
||||
bool try_parse_key(const char* begin, const char* end, const char* key, const char** val_begin, const char** val_end);
|
||||
bool try_parse_key(const char* begin, const char* end, const char* key, int* val);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __FIBRE_CHANNEL_DISCOVERER
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
#ifndef __FIBRE_EVENT_LOOP_HPP
|
||||
#define __FIBRE_EVENT_LOOP_HPP
|
||||
|
||||
#include "callback.hpp"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fibre {
|
||||
|
||||
struct EventLoopTimer;
|
||||
|
||||
/**
|
||||
* @brief Base class for event loops.
|
||||
*
|
||||
* Thread-safety: The public functions of this class except for post() must not
|
||||
* be assumed to be thread-safe.
|
||||
* Generally the functions of an event loop are only safe to be called from the
|
||||
* event loop's thread itself.
|
||||
*/
|
||||
class EventLoop {
|
||||
public:
|
||||
/**
|
||||
* @brief Registers a callback for immediate execution on the event loop
|
||||
* thread.
|
||||
*
|
||||
* This function must be thread-safe.
|
||||
*/
|
||||
virtual bool post(Callback<void> callback) = 0;
|
||||
|
||||
/**
|
||||
* @brief Registers the given file descriptor on this event loop.
|
||||
*
|
||||
* This function is only implemented on Unix-like systems.
|
||||
*
|
||||
* @param fd: A waitable Unix file descriptor on which to listen for events.
|
||||
* @param events: A bitfield that specifies the events to listen for.
|
||||
* For instance EPOLLIN or EPOLLOUT.
|
||||
* @param callback: The callback to invoke every time the event triggers.
|
||||
* A bitfield is passed to the callback to indicate which events were
|
||||
* triggered. This callback must remain valid until
|
||||
* deregister_event() is called for the same file descriptor.
|
||||
*/
|
||||
virtual bool register_event(int fd, uint32_t events, Callback<void, uint32_t> callback) = 0;
|
||||
|
||||
/**
|
||||
* @brief Deregisters the given event.
|
||||
*
|
||||
* Once this function returns, the associated callback will no longer be
|
||||
* invoked and its resources can be freed.
|
||||
*/
|
||||
virtual bool deregister_event(int fd) = 0;
|
||||
|
||||
/**
|
||||
* @brief Registers a callback to be called at a later point in time.
|
||||
*
|
||||
* This returns an opaque handler which can be used to cancel the timer.
|
||||
*
|
||||
* @param delay: The delay from now in seconds.
|
||||
* TOOD: specify if OS sleep time is counted in.
|
||||
*/
|
||||
virtual struct EventLoopTimer* call_later(float delay, Callback<void> callback) = 0;
|
||||
|
||||
/**
|
||||
* @brief Cancels a timer which was previously started by call_later().
|
||||
*
|
||||
* Must not be called after invokation of the callback has started.
|
||||
* This also means that cancel_timer() must not be called from within the
|
||||
* callback of the timer itself.
|
||||
*/
|
||||
virtual bool cancel_timer(EventLoopTimer* timer) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __FIBRE_EVENT_LOOP_HPP
|
||||
@@ -0,0 +1,155 @@
|
||||
#ifndef __FIBRE_HPP
|
||||
#define __FIBRE_HPP
|
||||
|
||||
#include <fibre/callback.hpp>
|
||||
#include <fibre/bufptr.hpp>
|
||||
#include <fibre/cpp_utils.hpp>
|
||||
#include <fibre/event_loop.hpp>
|
||||
#include <fibre/channel_discoverer.hpp>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#if FIBRE_ENABLE_LIBUSB_BACKEND
|
||||
#include "../../platform_support/libusb_transport.hpp"
|
||||
#endif
|
||||
|
||||
#if FIBRE_ENABLE_TCP_CLIENT_BACKEND
|
||||
#include "../../platform_support/posix_tcp_backend.hpp"
|
||||
#endif
|
||||
|
||||
namespace fibre {
|
||||
|
||||
struct CallBuffers {
|
||||
Status status;
|
||||
cbufptr_t tx_buf;
|
||||
bufptr_t rx_buf;
|
||||
};
|
||||
|
||||
struct CallBufferRelease {
|
||||
Status status;
|
||||
const uint8_t* tx_end;
|
||||
uint8_t* rx_end;
|
||||
};
|
||||
|
||||
struct Function {
|
||||
virtual std::optional<CallBufferRelease>
|
||||
call(void**, CallBuffers, Callback<std::optional<CallBuffers>, CallBufferRelease>) = 0;
|
||||
};
|
||||
|
||||
struct Object;
|
||||
struct Interface;
|
||||
class Domain;
|
||||
|
||||
template<typename T>
|
||||
struct StaticBackend {
|
||||
std::string name;
|
||||
T impl;
|
||||
};
|
||||
|
||||
struct Context {
|
||||
size_t n_domains = 0;
|
||||
EventLoop* event_loop;
|
||||
|
||||
std::tuple<
|
||||
#if FIBRE_ENABLE_LIBUSB_BACKEND
|
||||
LibusbDiscoverer
|
||||
#endif
|
||||
#if FIBRE_ENABLE_LIBUSB_BACKEND && FIBRE_ENABLE_TCP_CLIENT_BACKEND
|
||||
, // TODO: find a less awkward way to do this
|
||||
#endif
|
||||
#if FIBRE_ENABLE_TCP_CLIENT_BACKEND
|
||||
PosixTcpClientBackend
|
||||
#endif
|
||||
#if FIBRE_ENABLE_TCP_CLIENT_BACKEND && FIBRE_ENABLE_TCP_SERVER_BACKEND
|
||||
, // TODO: find a less awkward way to do this
|
||||
#endif
|
||||
#if FIBRE_ENABLE_TCP_SERVER_BACKEND
|
||||
PosixTcpServerBackend
|
||||
#endif
|
||||
> static_backends;
|
||||
|
||||
#if FIBRE_ALLOW_HEAP
|
||||
std::unordered_map<std::string, ChannelDiscoverer*> discoverers;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Creates a domain on which objects can subsequently be published
|
||||
* and discovered.
|
||||
*
|
||||
* This potentially starts looking for channels on this domain.
|
||||
*/
|
||||
Domain* create_domain(std::string specs);
|
||||
void close_domain(Domain* domain);
|
||||
|
||||
void register_backend(std::string name, ChannelDiscoverer* backend);
|
||||
void deregister_backend(std::string name);
|
||||
};
|
||||
|
||||
// TODO: don't declare these types here
|
||||
struct LegacyProtocolPacketBased;
|
||||
class LegacyObjectClient;
|
||||
struct LegacyObject;
|
||||
|
||||
class Domain {
|
||||
friend struct Context;
|
||||
public:
|
||||
#if FIBRE_ENABLE_CLIENT
|
||||
// TODO: add interface argument
|
||||
// TODO: support multiple discovery instances
|
||||
void start_discovery(Callback<void, Object*, Interface*> on_found_object, Callback<void, Object*> on_lost_object);
|
||||
void stop_discovery();
|
||||
#endif
|
||||
|
||||
void add_channels(ChannelDiscoveryResult result);
|
||||
|
||||
Context* ctx;
|
||||
private:
|
||||
#if FIBRE_ENABLE_CLIENT
|
||||
void on_found_root_object(LegacyObjectClient* obj_client, std::shared_ptr<LegacyObject> obj);
|
||||
void on_lost_root_object(LegacyObjectClient* obj_client, std::shared_ptr<LegacyObject> obj);
|
||||
#endif
|
||||
void on_stopped(LegacyProtocolPacketBased* protocol, StreamStatus status);
|
||||
|
||||
#if FIBRE_ALLOW_HEAP
|
||||
std::unordered_map<std::string, fibre::ChannelDiscoveryContext*> channel_discovery_handles;
|
||||
#endif
|
||||
#if FIBRE_ENABLE_CLIENT
|
||||
Callback<void, Object*, Interface*> on_found_object_;
|
||||
Callback<void, Object*> on_lost_object_;
|
||||
std::unordered_map<Object*, Interface*> root_objects_;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Opens and initializes a Fibre context.
|
||||
*
|
||||
* If FIBRE_ALLOW_HEAP=0 only one Fibre context can be open at a time.
|
||||
*
|
||||
* @returns: A non-null pointer on success, null otherwise.
|
||||
*/
|
||||
Context* open(EventLoop* event_loop);
|
||||
|
||||
void close(Context*);
|
||||
|
||||
/**
|
||||
* @brief Launches an event loop on the current thread.
|
||||
*
|
||||
* This function returns when the event loop becomes empty.
|
||||
*
|
||||
* If FIBRE_ALLOW_HEAP=0 only one event loop can be running at a time.
|
||||
*
|
||||
* This function returns false if Fibre was compiled with
|
||||
* FIBRE_ENABLE_EVENT_LOOP=0.
|
||||
*
|
||||
* @param on_started: This function is the first event that is placed on the
|
||||
* event loop. This function usually creates further events, for instance
|
||||
* by calling open().
|
||||
* @returns: true if the event loop ran to completion. False if this function is
|
||||
* not implemented on this operating system or if another error
|
||||
* occurred.
|
||||
*/
|
||||
bool launch_event_loop(Callback<void, EventLoop*> on_started);
|
||||
|
||||
}
|
||||
|
||||
#endif // __FIBRE_HPP
|
||||
@@ -0,0 +1,219 @@
|
||||
#ifndef __FIBRE_INTROSPECTION_HPP
|
||||
#define __FIBRE_INTROSPECTION_HPP
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("s")
|
||||
|
||||
class TypeInfo;
|
||||
class Introspectable;
|
||||
using introspectable_storage_t = std::aligned_storage<4 * sizeof(uintptr_t), sizeof(uintptr_t)>::type;
|
||||
|
||||
struct PropertyInfo {
|
||||
const char * name;
|
||||
const TypeInfo* type_info;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Contains runtime accessible type information.
|
||||
*
|
||||
* Specifically, this information consists of a list of PropertyInfo items which
|
||||
* enable accessing attributes of an object by a runtime string.
|
||||
*
|
||||
* Typically, for each combination of C++ type and Fibre interface implemented
|
||||
* by this type, one (static constant) TypeInfo object will exist.
|
||||
*/
|
||||
class TypeInfo {
|
||||
friend class Introspectable;
|
||||
public:
|
||||
TypeInfo(const PropertyInfo* property_table, size_t property_table_length)
|
||||
: property_table_(property_table), property_table_length_(property_table_length) {}
|
||||
|
||||
virtual introspectable_storage_t get_child(introspectable_storage_t obj, size_t idx) const = 0;
|
||||
Introspectable get_child(const Introspectable& obj, const char * name, size_t length) const;
|
||||
|
||||
protected:
|
||||
|
||||
template<typename T> static T& as(Introspectable& obj);
|
||||
template<typename T> static const T& as(const Introspectable& obj);
|
||||
template<typename T> static Introspectable make_introspectable(T obj, const TypeInfo* type_info);
|
||||
|
||||
private:
|
||||
const PropertyInfo* property_table_;
|
||||
size_t property_table_length_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Wraps a reference to an application object by attaching runtime
|
||||
* accessible type information.
|
||||
*
|
||||
* The reference that is wrapped is typically a pointer but can also be a small
|
||||
* temporary, on-demand constructed object such as a fibre::Property<...> which
|
||||
* contains multiple pointers.
|
||||
*/
|
||||
class Introspectable {
|
||||
friend class TypeInfo;
|
||||
public:
|
||||
Introspectable() {}
|
||||
|
||||
/**
|
||||
* @brief Returns an Introspectable object for the attribute referenced by
|
||||
* the specified attribute name.
|
||||
*
|
||||
* The name can consist of multiple parts separated by dots.
|
||||
*
|
||||
* If the attribute does not exist, an invalid Introspectable is returned.
|
||||
*
|
||||
* @param path: The name or path of the attribute.
|
||||
* @param length: The maximum length of the name.
|
||||
*/
|
||||
Introspectable get_child(const char * path, size_t length) {
|
||||
Introspectable current = *this;
|
||||
|
||||
const char * begin = path;
|
||||
const char * end = std::find(begin, path + length, '\0');
|
||||
|
||||
while ((begin < end) && current.type_info_) {
|
||||
const char * end_of_token = std::find(begin, end, '.');
|
||||
current = current.get_direct_child(begin, end_of_token - begin);
|
||||
begin = std::min(end, end_of_token + 1);
|
||||
}
|
||||
|
||||
return current;
|
||||
};
|
||||
|
||||
bool is_valid() {
|
||||
return type_info_;
|
||||
}
|
||||
|
||||
const TypeInfo* get_type_info() {
|
||||
return type_info_;
|
||||
}
|
||||
|
||||
private:
|
||||
Introspectable get_direct_child(const char * name, size_t length) const {
|
||||
for (size_t i = 0; i < type_info_->property_table_length_; ++i) {
|
||||
if (!strncmp(name, type_info_->property_table_[i].name, length) && (length == strlen(type_info_->property_table_[i].name))) {
|
||||
Introspectable result;
|
||||
result.storage_ = type_info_->get_child(storage_, i);
|
||||
result.type_info_ = type_info_->property_table_[i].type_info;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
public: // these should technically be protected but are public for optimization reasons
|
||||
// We use this storage to hold generic small objects. Usually that's a pointer
|
||||
// but sometimes it's an on-demand constructed Property<...>.
|
||||
// Caution: only put objects in here which are trivially copyable, movable
|
||||
// and destructible as any custom operation wouldn't be called.
|
||||
introspectable_storage_t storage_;
|
||||
const TypeInfo* type_info_ = nullptr;
|
||||
};
|
||||
|
||||
template<typename T> T& TypeInfo::as(Introspectable& obj) {
|
||||
static_assert(sizeof(T) <= sizeof(obj.storage_), "invalid size");
|
||||
return *(T*)&obj.storage_;
|
||||
}
|
||||
template<typename T> const T& TypeInfo::as(const Introspectable& obj) {
|
||||
static_assert(sizeof(T) <= sizeof(obj.storage_), "invalid size");
|
||||
return *(const T*)&obj.storage_;
|
||||
}
|
||||
template<typename T> Introspectable TypeInfo::make_introspectable(T obj, const TypeInfo* type_info) {
|
||||
Introspectable introspectable;
|
||||
as<T>(introspectable) = obj;
|
||||
introspectable.type_info_ = type_info;
|
||||
return introspectable;
|
||||
}
|
||||
|
||||
|
||||
// maybe_underlying_type_t<T> resolves to the underlying type of T if T is an enum type or otherwise to T itself.
|
||||
template<typename T, bool = std::is_enum<T>::value> struct maybe_underlying_type;
|
||||
template<typename T> struct maybe_underlying_type<T, true> { typedef std::underlying_type_t<T> type; };
|
||||
template<typename T> struct maybe_underlying_type<T, false> { typedef T type; };
|
||||
template<typename T> using maybe_underlying_type_t = typename maybe_underlying_type<T>::type;
|
||||
|
||||
|
||||
struct StringConvertibleTypeInfo {
|
||||
virtual bool get_string(const Introspectable& obj, char* buffer, size_t length) const { return false; }
|
||||
virtual bool set_string(const Introspectable& obj, char* buffer, size_t length) const { return false; }
|
||||
};
|
||||
|
||||
struct FloatSettableTypeInfo {
|
||||
//virtual bool get_float(const Introspectable& obj, float* val) const { return false; }
|
||||
virtual bool set_float(const Introspectable& obj, float val) const { return false; }
|
||||
};
|
||||
|
||||
/* Built-in type infos ********************************************************/
|
||||
|
||||
template<typename T>
|
||||
struct FibrePropertyTypeInfo;
|
||||
|
||||
// readonly property
|
||||
template<typename T>
|
||||
struct FibrePropertyTypeInfo<Property<const T>> : StringConvertibleTypeInfo, TypeInfo {
|
||||
using TypeInfo::TypeInfo;
|
||||
static const PropertyInfo property_table[];
|
||||
static const FibrePropertyTypeInfo<Property<const T>> singleton;
|
||||
|
||||
introspectable_storage_t get_child(introspectable_storage_t obj, size_t idx) const override {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool get_string(const Introspectable& obj, char* buffer, size_t length) const override {
|
||||
return to_string(static_cast<maybe_underlying_type_t<T>>(as<const Property<const T>>(obj).read()), buffer, length, 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
const PropertyInfo FibrePropertyTypeInfo<Property<const T>>::property_table[] = {};
|
||||
template<typename T>
|
||||
const FibrePropertyTypeInfo<Property<const T>> FibrePropertyTypeInfo<Property<const T>>::singleton{FibrePropertyTypeInfo<Property<const T>>::property_table, sizeof(FibrePropertyTypeInfo<Property<const T>>::property_table) / sizeof(FibrePropertyTypeInfo<Property<const T>>::property_table[0])};
|
||||
|
||||
// readwrite property
|
||||
template<typename T>
|
||||
struct FibrePropertyTypeInfo<Property<T>> : FloatSettableTypeInfo, StringConvertibleTypeInfo, TypeInfo {
|
||||
using TypeInfo::TypeInfo;
|
||||
static const PropertyInfo property_table[];
|
||||
static const FibrePropertyTypeInfo<Property<T>> singleton;
|
||||
static const Introspectable make_introspectable(Property<T> obj) { return TypeInfo::make_introspectable(obj, &singleton); }
|
||||
|
||||
introspectable_storage_t get_child(introspectable_storage_t obj, size_t idx) const override {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool get_string(const Introspectable& obj, char* buffer, size_t length) const override {
|
||||
return to_string(static_cast<maybe_underlying_type_t<T>>(as<const Property<T>>(obj).read()), buffer, length, 0);
|
||||
}
|
||||
|
||||
bool set_string(const Introspectable& obj, char* buffer, size_t length) const override {
|
||||
maybe_underlying_type_t<T> value{};
|
||||
if (!from_string(buffer, length, &value, 0)) {
|
||||
return false;
|
||||
}
|
||||
as<const Property<T>>(obj).exchange(static_cast<T>(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_float(const Introspectable& obj, float val) const override {
|
||||
maybe_underlying_type_t<T> value{};
|
||||
if (!conversion::set_from_float(val, &value)) {
|
||||
return false;
|
||||
}
|
||||
as<const Property<T>>(obj).exchange(static_cast<T>(value));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
const PropertyInfo FibrePropertyTypeInfo<Property<T>>::property_table[] = {};
|
||||
template<typename T>
|
||||
const FibrePropertyTypeInfo<Property<T>> FibrePropertyTypeInfo<Property<T>>::singleton{FibrePropertyTypeInfo<Property<T>>::property_table, sizeof(FibrePropertyTypeInfo<Property<T>>::property_table) / sizeof(FibrePropertyTypeInfo<Property<T>>::property_table[0])};
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
#endif // __FIBRE_INTROSPECTION_HPP
|
||||
@@ -0,0 +1,603 @@
|
||||
/**
|
||||
* @brief Fibre C library
|
||||
*
|
||||
* The library is fully asynchronous and runs on an application-managed event
|
||||
* loop. This integration happens with the call to libfibre_open(), where the
|
||||
* application must pass a couple of functions that libfibre will use to put
|
||||
* tasks on the event loop.
|
||||
*
|
||||
* Some general things to note:
|
||||
* - None of the library's functions are blocking.
|
||||
* - None of the library's functions can be expected to be thread-safe, they
|
||||
* should not be invoked from any other thread than the one that runs the
|
||||
* event loop.
|
||||
* - Callbacks that the user passes to a libfibre function are always executed
|
||||
* on the event loop thread.
|
||||
* - All of the library's functions can be expected reentry-safe. That means
|
||||
* you can call into any libfibre function from any callback handler that
|
||||
* libfibre invokes.
|
||||
*/
|
||||
|
||||
#ifndef __LIBFIBRE_H
|
||||
#define __LIBFIBRE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define DLL_EXPORT __declspec(dllexport)
|
||||
# define DLL_IMPORT __declspec(dllimport)
|
||||
#elif defined(__GNUC__)
|
||||
# define DLL_EXPORT __attribute__((visibility("default")))
|
||||
# define DLL_IMPORT
|
||||
# if __GNUC__ > 4
|
||||
# define DLL_LOCAL __attribute__((visibility("hidden")))
|
||||
# else
|
||||
# define DLL_LOCAL
|
||||
# endif
|
||||
#else
|
||||
# error("Don't know how to export shared object libraries")
|
||||
#endif
|
||||
|
||||
#ifdef FIBRE_COMPILE
|
||||
# ifndef FIBRE_PUBLIC
|
||||
# define FIBRE_PUBLIC DLL_EXPORT
|
||||
# endif
|
||||
# define FIBRE_PRIVATE DLL_LOCAL
|
||||
#else
|
||||
# define FIBRE_PUBLIC DLL_IMPORT
|
||||
#endif
|
||||
|
||||
|
||||
#define FIBRE_PRIVATE DLL_LOCAL
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct LibFibreCtx;
|
||||
struct LibFibreDiscoveryCtx;
|
||||
struct LibFibreCallContext;
|
||||
struct LibFibreObject;
|
||||
struct LibFibreInterface;
|
||||
struct LibFibreFunction;
|
||||
struct LibFibreAttribute;
|
||||
struct LibFibreTxStream;
|
||||
struct LibFibreRxStream;
|
||||
struct LibFibreDomain;
|
||||
|
||||
// This enum must remain identical to fibre::Status.
|
||||
enum LibFibreStatus {
|
||||
kFibreOk,
|
||||
kFibreBusy, //<! The request will complete asynchronously
|
||||
kFibreCancelled, //!< The operation was cancelled due to a request by the application or the remote peer
|
||||
kFibreClosed, //!< The operation has finished orderly or shall be finished orderly
|
||||
kFibreInvalidArgument, //!< Bug in the application
|
||||
kFibreInternalError, //!< Bug in the local fibre implementation
|
||||
kFibreProtocolError, //!< A remote peer is misbehaving (indicates bug in the remote peer)
|
||||
kFibreHostUnreachable, //!< The remote peer can no longer be reached
|
||||
//kFibreInsufficientData, // maybe we will introduce this to tell the caller that the granularity of the data is too small
|
||||
};
|
||||
|
||||
struct LibFibreVersion {
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint16_t patch;
|
||||
};
|
||||
|
||||
typedef int (*post_cb_t)(void (*callback)(void*), void* cb_ctx);
|
||||
typedef int (*register_event_cb_t)(int fd, uint32_t events, void (*callback)(void*, uint32_t), void* cb_ctx);
|
||||
typedef int (*deregister_event_cb_t)(int fd);
|
||||
typedef struct EventLoopTimer* (*call_later_cb_t)(float delay, void (*callback)(void*), void* cb_ctx);
|
||||
typedef int (*cancel_timer_cb_t)(struct EventLoopTimer* timer);
|
||||
|
||||
struct LibFibreEventLoop {
|
||||
/**
|
||||
* @brief Called by libfibre when it wants the application to run a callback
|
||||
* on the application's event loop.
|
||||
*
|
||||
* This is the only callback that libfibre can invoke from a different
|
||||
* thread than the event loop thread itself. The application must ensure
|
||||
* that this callback is thread-safe.
|
||||
* This allows libfibre to run other threads internally while keeping
|
||||
* threading promises made to the application.
|
||||
*/
|
||||
post_cb_t post;
|
||||
|
||||
/**
|
||||
* @brief TODO: this is a Unix specific callback. Need to use IOCP on Windows.
|
||||
*/
|
||||
register_event_cb_t register_event;
|
||||
|
||||
/**
|
||||
* @brief TODO: this is a Unix specific callback. Need to use IOCP on Windows.
|
||||
*/
|
||||
deregister_event_cb_t deregister_event;
|
||||
|
||||
/**
|
||||
* @brief Called by libfibre to ask the application to call a certain
|
||||
* callback after a certain amount of time.
|
||||
*
|
||||
* The callback must be invoked on the same thread on which libfibre_open()
|
||||
* was called. The application should return an opaque handle that
|
||||
* libfibre can use to cancel the timer.
|
||||
*/
|
||||
call_later_cb_t call_later;
|
||||
|
||||
/**
|
||||
* @brief Called by libfibre to ask the application to cancel a callback
|
||||
* timer previously enqueued with call_later().
|
||||
*/
|
||||
cancel_timer_cb_t cancel_timer;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief on_start_discovery callback type for libfibre_register_backend().
|
||||
*
|
||||
* For every channel pair that the application finds that matches the filter of
|
||||
* this discoverer the application should call libfibre_add_channels().
|
||||
*
|
||||
* @param discovery_handle: An opaque handle that libfibre will pass to the
|
||||
* corresponding on_stop_discovery callback to stop the discovery.
|
||||
* @param specs, specs_length: The specs string that specifies discoverer-specific
|
||||
* filter parameters.
|
||||
*/
|
||||
typedef void (*on_start_discovery_cb_t)(void* ctx, LibFibreDomain* domain, const char* specs, size_t specs_length);
|
||||
typedef void (*on_stop_discovery_cb_t)(void* ctx, LibFibreDomain* domain);
|
||||
|
||||
/**
|
||||
* @brief on_found_object callback type for libfibre_start_discovery().
|
||||
* @param obj: The object handle.
|
||||
* @param intf: The interface handle. Valid for as long as any handle of an
|
||||
* object that implements it is valid.
|
||||
*/
|
||||
typedef void (*on_found_object_cb_t)(void*, LibFibreObject* obj, LibFibreInterface* intf);
|
||||
|
||||
/**
|
||||
* @brief on_lost_object callback type for libfibre_start_discovery().
|
||||
*/
|
||||
typedef void (*on_lost_object_cb_t)(void*, LibFibreObject* obj);
|
||||
|
||||
typedef void (*on_stopped_cb_t)(void*, LibFibreStatus);
|
||||
|
||||
typedef void (*on_attribute_added_cb_t)(void*, LibFibreAttribute*, const char* name, size_t name_length, LibFibreInterface*, const char* intf_name, size_t intf_name_length);
|
||||
typedef void (*on_attribute_removed_cb_t)(void*, LibFibreAttribute*);
|
||||
|
||||
/**
|
||||
* @brief on_function_added callback type for libfibre_subscribe_to_interface().
|
||||
*
|
||||
* @param ctx: The user data that was passed to libfibre_subscribe_to_interface().
|
||||
* @param func: A handle for the function. Remains valid until the corresponding
|
||||
* call to on_function_removed().
|
||||
* @param name: The ASCII-encoded name of the function.
|
||||
* @param name_length: Length in bytes of the name.
|
||||
* @param input_names: A null-terminated list of null-terminated ASCII-encoded
|
||||
* strings. Each string corresponds to the name of one input argument.
|
||||
* The list and the string buffers are only valid for the duration of the
|
||||
* callback. They must not be freed by the application.
|
||||
* @param input_codecs: A null-terminated list of null-terminated ASCII-encoded
|
||||
* strings. Each string names the codec of one input argument.
|
||||
* The list and the string buffers are only valid for the duration of the
|
||||
* callback. They must not be freed by the application.
|
||||
* @param output_names: Analogous to input_names.
|
||||
* @param output_codecs: Analogous to output names.
|
||||
*/
|
||||
typedef void (*on_function_added_cb_t)(void* ctx, LibFibreFunction* func, const char* name, size_t name_length, const char** input_names, const char** input_codecs, const char** output_names, const char** output_codecs);
|
||||
|
||||
typedef void (*on_function_removed_cb_t)(void*, LibFibreFunction*);
|
||||
|
||||
/**
|
||||
* @brief Callback type for libfibre_call().
|
||||
*
|
||||
* For an overview of the coroutine call control flow see libfibre_call().
|
||||
*
|
||||
* @param ctx: The context pointer that was passed to libfibre_call().
|
||||
* @param tx_end: End of the range of data that was accepted by libfibre. This
|
||||
* is always in the interval [tx_buf, tx_buf + tx_len] where `tx_buf` and
|
||||
* `tx_len` are the arguments of the corresponding libfibre_call() call.
|
||||
* @param tx_end: End of the range of data that was returned by libfibre. This
|
||||
* is always in the interval [rx_buf, rx_buf + rx_len] where `rx_buf` and
|
||||
* `rx_len` are the arguments of the corresponding libfibre_call() call.
|
||||
* @param tx_buf: The application should set this to the next buffer to
|
||||
* transmit. The buffer must remain valid until the next callback
|
||||
* invokation.
|
||||
* @param tx_len: The length of tx_buf. Must be zero if tx_buf is NULL.
|
||||
* @param rx_buf: The application should set this to the buffer into which data
|
||||
* should be written. The buffer must remain allocated until the next
|
||||
* callback invokation.
|
||||
* @param rx_len: The length of rx_buf. Must be zero if rx_buf is NULL.
|
||||
*
|
||||
* @retval kFibreOk: The application set tx_buf and rx_buf to valid or empty
|
||||
* buffers and libfibre should invoke the callback again when it has
|
||||
* made progress.
|
||||
* @retval kFibreBusy: The application cannot provide a new tx_buf or rx_buf at
|
||||
* the moment. The application will eventually call libfibre_call() for
|
||||
* this coroutine call again.
|
||||
* @retval kFibreClosed: The application may have returned non-empty buffers and
|
||||
* if libfibre manages to fully handle these buffers it shall consider
|
||||
* the call ended.
|
||||
* @retval kFibreCancelled: The application did not set valid tx and rx buffers
|
||||
* and libfibre should consider the call cancelled. Libfibre will not
|
||||
* invoke the callback anymore.
|
||||
*/
|
||||
typedef LibFibreStatus (*libfibre_call_cb_t)(void* ctx,
|
||||
LibFibreStatus status,
|
||||
const unsigned char* tx_end, unsigned char* rx_end,
|
||||
const unsigned char** tx_buf, size_t* tx_len,
|
||||
unsigned char** rx_buf, size_t* rx_len);
|
||||
|
||||
/**
|
||||
* @brief TX completion callback type for libfibre_start_tx().
|
||||
*
|
||||
* @param ctx: The user data that was passed to libfibre_start_tx().
|
||||
* @param tx_stream: The TX stream on which the TX operation completed.
|
||||
* @param status: The status of the last TX operation.
|
||||
* - kFibreOk: The indicated range of the TX buffer was successfully
|
||||
* transmitted and the stream might accept more data.
|
||||
* - kFibreClosed: The indicated range of the TX buffer was successfully
|
||||
* transmitted and the stream will no longer accept any data.
|
||||
* - Any other status: Successful transmission of the data cannot be
|
||||
* guaranteed and no more data can be sent on this stream.
|
||||
* @param tx_end: Points to the address after the last byte read from the
|
||||
* TX buffer. This pointer always points to a valid position in the
|
||||
* buffer (or the end of the buffer), even if the transmission failed.
|
||||
* However if the status is something other than kFibreOk and
|
||||
* kFibreClosed then the pointer may not precisely indicate the
|
||||
* transmitted data range.
|
||||
*/
|
||||
typedef void (*on_tx_completed_cb_t)(void* ctx, LibFibreTxStream* tx_stream, LibFibreStatus status, const uint8_t* tx_end);
|
||||
|
||||
/**
|
||||
* @brief RX completion callback type for libfibre_start_rx().
|
||||
*
|
||||
* @param ctx: The user data that was passed to libfibre_start_rx().
|
||||
* @param rx_stream: The RX stream on which the RX operation completed.
|
||||
* @param status: The status of the last RX operation.
|
||||
* - kFibreOk: The indicated range of the RX buffer was successfully
|
||||
* filled with received data and the stream might emit more data.
|
||||
* - kFibreClosed: The indicated range of the RX buffer was successfully
|
||||
* filled with received data and the stream will emit no more data.
|
||||
* - Any other status: Successful transmission of the data cannot be
|
||||
* guaranteed and no more data can be sent on this stream.
|
||||
* @param rx_end: Points to the address after the last byte written to the
|
||||
* RX buffer. This pointer always points to a valid position in the
|
||||
* buffer (or the end of the buffer), even if the reception failed.
|
||||
* However if the status is something other than kFibreOk and
|
||||
* kFibreClosed then the pointer may not precisely indicate the
|
||||
* received data range.
|
||||
*/
|
||||
typedef void (*on_rx_completed_cb_t)(void* ctx, LibFibreRxStream* rx_stream, LibFibreStatus status, uint8_t* rx_end);
|
||||
|
||||
/**
|
||||
* @brief Returns the version of the libfibre library.
|
||||
*
|
||||
* The returned struct must not be freed.
|
||||
*
|
||||
* The version adheres to Semantic Versioning, that means breaking changes of
|
||||
* the ABI can be detected by an increment of the major version number (unless
|
||||
* it's zero).
|
||||
*
|
||||
* Even if breaking changes are introduced, we promise to keep this function
|
||||
* backwards compatible.
|
||||
*/
|
||||
FIBRE_PUBLIC const struct LibFibreVersion* libfibre_get_version();
|
||||
|
||||
/**
|
||||
* @brief Opens and initializes a Fibre context.
|
||||
*
|
||||
* @param event_loop: The event loop on which libfibre will run. Some function
|
||||
of the event loop can be left unimplemented (set to NULL) depending on
|
||||
the platform and the backends used (TODO: elaborate).
|
||||
The event loop must be single threaded and all calls to libfibre must
|
||||
happen on the event loop thread.
|
||||
*/
|
||||
FIBRE_PUBLIC struct LibFibreCtx* libfibre_open(LibFibreEventLoop event_loop);
|
||||
|
||||
/**
|
||||
* @brief Closes a context that was previously opened with libfibre_open().
|
||||
*
|
||||
* This function must not be invoked before all ongoing discovery processes
|
||||
* are stopped and all channels are closed.
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_close(struct LibFibreCtx* ctx);
|
||||
|
||||
/**
|
||||
* @brief Registers an external channel provider.
|
||||
*
|
||||
* Libfibre starts and stops the discoverer on demand as a result of calls
|
||||
* to libfibre_start_discovery() and libfibre_stop_discovery().
|
||||
* This can be used by applications to implement transport providers which are
|
||||
* not supported natively in libfibre.
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_register_backend(LibFibreCtx* ctx, const char* name,
|
||||
size_t name_length, on_start_discovery_cb_t on_start_discovery,
|
||||
on_stop_discovery_cb_t on_stop_discovery, void* cb_ctx);
|
||||
|
||||
/**
|
||||
* @brief Creates a communication domain from the specified spec string.
|
||||
*
|
||||
* @param ctx: The libfibre context that was obtained from libfibre_open().
|
||||
* @param specs: Pointer to an ASCII string encoding the channel specifications.
|
||||
* Must remain valid for the life time of the discovery.
|
||||
* See README of the main Fibre repository for details.
|
||||
* (https://github.com/samuelsadok/fibre/tree/devel).
|
||||
* @returns: An opaque handle which can be passed to libfibre_start_discovery().
|
||||
*/
|
||||
FIBRE_PUBLIC LibFibreDomain* libfibre_open_domain(LibFibreCtx* ctx,
|
||||
const char* specs, size_t specs_len);
|
||||
|
||||
/**
|
||||
* @brief Closes a domain that was previously opened with libfibre_open_domain().
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_close_domain(LibFibreDomain* domain);
|
||||
|
||||
/**
|
||||
* @brief Adds new TX and RX channels to a domain.
|
||||
*
|
||||
* The channels can be closed with libfibre_close_tx() and libfibre_close_rx().
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_add_channels(LibFibreDomain* domain, LibFibreRxStream** tx_channel, LibFibreTxStream** rx_channel, size_t mtu);
|
||||
|
||||
/**
|
||||
* @brief Starts looking for Fibre objects that match the specifications.
|
||||
*
|
||||
* @param domain: The domain obtained from libfibre_open_domain() on which to
|
||||
* discover objects.
|
||||
* @param on_found_object: Invoked for every matching object that is found.
|
||||
* The application must expect the same object handle to appear more than
|
||||
* once.
|
||||
* libfibre increments the internal reference count of the object before
|
||||
* this call and decrements it after the corresponding call to
|
||||
* on_lost_object. When the reference count reaches zero the application
|
||||
* must no longer use it. The reference count is always non-negative.
|
||||
* @param on_lost_object: Invoked when an object is lost.
|
||||
* @param on_stopped: Invoked when the discovery stops for any reason, including
|
||||
* a corresponding call to libfibre_stop_discovery().
|
||||
* @param cb_ctx: Arbitrary user data passed to the callbacks.
|
||||
* @returns: An opaque handle which should be passed to libfibre_stop_discovery().
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_start_discovery(LibFibreDomain* domain,
|
||||
LibFibreDiscoveryCtx** handle, on_found_object_cb_t on_found_object,
|
||||
on_lost_object_cb_t on_lost_object,
|
||||
on_stopped_cb_t on_stopped, void* cb_ctx);
|
||||
|
||||
/**
|
||||
* @brief Stops an ongoing discovery process that was previously started with
|
||||
* libfibre_start_discovery().
|
||||
*
|
||||
* The discovery is stopped asynchronously. That means it must still be
|
||||
* considered ongoing until the on_stopped callback which was passed to
|
||||
* libfibre_start_discovery() is invoked. Once this callback is invoked,
|
||||
* libfibre_stop_discovery() must no longer be called.
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_stop_discovery(LibFibreDiscoveryCtx* handle);
|
||||
|
||||
/**
|
||||
* @brief Subscribes to changes on the interface.
|
||||
*
|
||||
* All functions and attributes which are already part of the interface by the
|
||||
* time this function is called are also announced to the subscriber.
|
||||
*
|
||||
* @param interface: An interface handle that was obtained in the callback of
|
||||
* libfibre_start_discovery().
|
||||
* @param on_attribute_added: Invoked when an attribute is added to the
|
||||
* interface.
|
||||
* @param on_attribute_removed: Invoked when an attribute is removed from the
|
||||
* interface, including when the interface is being torn down. This is
|
||||
* called exactly once for every call to on_attribute_added().
|
||||
* @param on_function_added: Invoked when a function is added to the
|
||||
* interface. The input_names, input_codecs, output_names and
|
||||
* output_codecs arguments are null terminated lists of null terminated
|
||||
* strings. The name buffer and the four lists are only valid for the
|
||||
* duration of the callback and must not be freed by the application.
|
||||
* The function handle remains valid until the corresponding call to
|
||||
* on_function_removed().
|
||||
* @param on_function_removed: Invoked when a function is removed from the
|
||||
* interface, including when the interface is being torn down. This is
|
||||
* called exactly once for every call to on_function_added().
|
||||
* @param cb_ctx: Arbitrary user data passed to the callbacks.
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_subscribe_to_interface(LibFibreInterface* interface,
|
||||
on_attribute_added_cb_t on_attribute_added,
|
||||
on_attribute_removed_cb_t on_attribute_removed,
|
||||
on_function_added_cb_t on_function_added,
|
||||
on_function_removed_cb_t on_function_removed,
|
||||
void* cb_ctx);
|
||||
|
||||
/**
|
||||
* @brief Returns the object that corresponds the the specified attribute of
|
||||
* another object.
|
||||
*
|
||||
* This function runs purely locally and therefore returns a result immediately.
|
||||
*
|
||||
* TODO: it might be useful to allow this operation to go through to the remote
|
||||
* device.
|
||||
* TODO: Specify whether the returned object handle must be identical for
|
||||
* repeated calls.
|
||||
*
|
||||
* @param parent_obj: An object handle that was obtained in the callback of
|
||||
* libfibre_start_discovery() or from a previous call to
|
||||
* libfibre_get_attribute().
|
||||
* @param attr: An attribute handle that was obtained in the on_attribute_added()
|
||||
* callback of libfibre_subscribe_to_interface().
|
||||
* @param child_obj_ptr: If and only if the function succeeds, the variable that
|
||||
* this argument points to is set to the requested subobject. The returned
|
||||
* object handle is only guaranteed to remain valid for as long as the
|
||||
* parent object handle is valid.
|
||||
* @returns: kFibreOk or kFibreInvalidArgument
|
||||
*/
|
||||
FIBRE_PUBLIC LibFibreStatus libfibre_get_attribute(LibFibreObject* parent_obj, LibFibreAttribute* attr, LibFibreObject** child_obj_ptr);
|
||||
|
||||
/**
|
||||
* @brief Starts a remote coroutine call or continues or cancels an ongoing call.
|
||||
*
|
||||
* A remote coroutine call can be considered a continuous exchange of the
|
||||
* following tuples:
|
||||
*
|
||||
* Client Application ===== (tx_buf, rx_buf, status) ====> libfibre
|
||||
* Client Application <==== (tx_end, rx_end, status) ===== libfibre
|
||||
*
|
||||
* These tuples are exchanged through the input/output arguments of
|
||||
* libfibre_call() or libfibre_call()'s callback.
|
||||
*
|
||||
* If during an ongoing call either of the two parties is unable to respond
|
||||
* immediately it responds with kFibreBusy and will thus get the responsibility
|
||||
* to resume the call when able. kFibreCancelled can be issued by either party
|
||||
* at any time.
|
||||
*
|
||||
* Each party must make progress during every control transfer to the other
|
||||
* party.
|
||||
*
|
||||
* For the application this means for every call to libfibre_call() and every
|
||||
* return from libfibre_call()'s callback the arguments passed from application
|
||||
* to libfibre must satisfy at least one of the following:
|
||||
*
|
||||
* - The call handle is NULL
|
||||
* - tx_len is non-zero
|
||||
* - rx_len is non-zero
|
||||
* - The status is different from kFibreOk
|
||||
*
|
||||
* For libfibre this means every for return from libfibre_call() and every
|
||||
* call to libfibre_call()'s callback the arguments passed from libfibre to
|
||||
* application satisfy at least one of the following:
|
||||
*
|
||||
* - tx_end is larger than the corresponding tx_buf
|
||||
* - rx_end is larger than the corresponding rx_buf
|
||||
* - The status is different from kFibreOk
|
||||
*
|
||||
* @param func: A function handle that was obtained in the on_function_added()
|
||||
* callback of libfibre_subscribe_to_interface().
|
||||
* @param handle: The variable being pointed to by this argument identifies the
|
||||
* coroutine call. If the variable is NULL it will be set to a new opaque
|
||||
* handle. If the variable is not NULL the active function call is
|
||||
* continued or cancelled (depending on status).
|
||||
* @param tx_buf: The buffer to transmit. If libfibre_call() returns kFibreBusy
|
||||
* then this buffer must remain valid until `callback` is invoked.
|
||||
* Otherwise it can be freed immediately after this call.
|
||||
* @param tx_len: Length of tx_buf. Must be zero if tx_buf is NULL.
|
||||
* @param rx_buf: The buffer into which the received data should be written. If
|
||||
* libfibre_call() returns kFibreBusy then this buffer must remain
|
||||
* allocated until `callback` is invoked. Otherwise it can be freed
|
||||
* immediately after this call.
|
||||
* @param rx_len: Length of rx_buf. Must be zero if rx_buf is NULL.
|
||||
* @param tx_end: End of the range of data that was accepted by libfibre. This
|
||||
* is always in the interval [tx_buf, tx_buf + tx_len] unless
|
||||
* libfibre_call() returns kFibreBusy, in which case this is NULL.
|
||||
* This value does not give any delivery guarantees.
|
||||
* @param rx_end: End of the range of data that was returned by libfibre. This
|
||||
* is always in the interval [rx_buf, rx_buf + rx_len] unless
|
||||
* libfibre_call() returns kFibreBusy, in which case this is NULL.
|
||||
* @param callback: Will be invoked eventually if and only if libfibre_call()
|
||||
* returns kFibreBusy. This callback is never invoked from inside
|
||||
* libfibre_call().
|
||||
* @param cb_ctx: An opaque application-defined handle that gets passed to
|
||||
* `callback`.
|
||||
*
|
||||
* @retval kFibreOk: libfibre accepted some or all of the tx_buf or filled some
|
||||
* or all of the rx_buf with data and can immediately accept more TX
|
||||
* data or provide more RX data.
|
||||
* @retval kFibreBusy: libfibre will complete the request asynchronously by
|
||||
* calling `callback`. If this value is returned, then the application
|
||||
* must not invoke libfibre_call() on the same call handle again until
|
||||
* `callback` is invoked except for cancelling the call with a status
|
||||
* of `kFibreCancelled`.
|
||||
* @retval kFibreClosed: the remote server completed the call and will not
|
||||
* accept or return any more data on this call. The application must not
|
||||
* pass the closed call context handle to libfibre_call() anymore.
|
||||
* @retval kFibreCancelled: the application's cancellation request was honored
|
||||
* or the remote server cancelled the call. The application must not
|
||||
* pass the cancelled call context handle to libfibre_call() anymore.
|
||||
*/
|
||||
FIBRE_PUBLIC LibFibreStatus libfibre_call(LibFibreFunction* func, LibFibreCallContext** handle,
|
||||
LibFibreStatus status,
|
||||
const unsigned char* tx_buf, size_t tx_len,
|
||||
unsigned char* rx_buf, size_t rx_len,
|
||||
const unsigned char** tx_end,
|
||||
unsigned char** rx_end,
|
||||
libfibre_call_cb_t callback, void* cb_ctx);
|
||||
|
||||
/**
|
||||
* @brief Starts sending data on the specified TX stream.
|
||||
*
|
||||
* The TX operation must be considered in progress until the on_completed
|
||||
* callback is called. Until then the application must not start another TX
|
||||
* operation on the same stream. In the meantime the application can call
|
||||
* libfibre_cancel_tx() at any time to abort the operation.
|
||||
*
|
||||
* @param tx_stream: The stream on which to send data.
|
||||
* @param tx_buf: The buffer to transmit. Must remain valid until the operation
|
||||
* completes.
|
||||
* @param tx_len: Length of tx_buf.
|
||||
* @param on_completed: Called when the operation completes, whether successful
|
||||
* or not.
|
||||
* @param ctx: Arbitrary user data passed to the on_completed callback.
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_start_tx(LibFibreTxStream* tx_stream, const uint8_t* tx_buf, size_t tx_len, on_tx_completed_cb_t on_completed, void* ctx);
|
||||
|
||||
/**
|
||||
* @brief Cancels an ongoing TX operation.
|
||||
*
|
||||
* Must only be called if there is actually a TX operation in progress for which
|
||||
* cancellation has not yet been requested.
|
||||
* The application must still wait for the on_complete callback to be called
|
||||
* before the operation can be considered finished. The completion callback may
|
||||
* be called with kFibreCancelled or any other status.
|
||||
*
|
||||
* TODO: specify if streams can be restarted (current doc of on_tx_completed_cb_t implies no)
|
||||
*
|
||||
* @param tx_stream: The TX stream on which to cancel the ongoing TX operation.
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_cancel_tx(LibFibreTxStream* tx_stream);
|
||||
|
||||
/**
|
||||
* @brief Permanently close TX stream.
|
||||
*
|
||||
* Must not be called while a transfer is ongoing.
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_close_tx(LibFibreTxStream* tx_stream, LibFibreStatus status);
|
||||
|
||||
/**
|
||||
* @brief Starts receiving data on the specified RX stream.
|
||||
*
|
||||
* The RX operation must be considered in progress until the on_completed
|
||||
* callback is called. Until then the application must not start another RX
|
||||
* operation on the same stream. In the meantime the application can call
|
||||
* libfibre_cancel_rx() at any time to abort the operation.
|
||||
*
|
||||
* @param rx_stream: The stream on which to receive data.
|
||||
* @param rx_buf: The buffer to receive to. Must remain valid until the
|
||||
* operation completes.
|
||||
* @param rx_len: Length of rx_buf.
|
||||
* @param on_completed: Called when the operation completes, whether successful
|
||||
* or not.
|
||||
* @param ctx: Arbitrary user data passed to the on_completed callback.
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_start_rx(LibFibreRxStream* rx_stream, uint8_t* rx_buf, size_t rx_len, on_rx_completed_cb_t on_completed, void* ctx);
|
||||
|
||||
/**
|
||||
* @brief Cancels an ongoing RX operation.
|
||||
*
|
||||
* Must only be called if there is actually a RX operation in progress for which
|
||||
* cancellation has not yet been requested.
|
||||
* The application must still wait for the on_complete callback to be called
|
||||
* before the operation can be considered finished. The completion callback may
|
||||
* be called with kFibreCancelled or any other status.
|
||||
*
|
||||
* TODO: specify if streams can be restarted (current doc of on_rx_completed_cb_t implies no)
|
||||
*
|
||||
* @param rx_stream: The RX stream on which to cancel the ongoing RX operation.
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_cancel_rx(LibFibreRxStream* rx_stream);
|
||||
|
||||
/**
|
||||
* @brief Permanently close RX stream.
|
||||
*
|
||||
* Must not be called while a transfer is ongoing.
|
||||
*/
|
||||
FIBRE_PUBLIC void libfibre_close_rx(LibFibreRxStream* rx_stream, LibFibreStatus status);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __LIBFIBRE_H
|
||||
@@ -0,0 +1,127 @@
|
||||
#ifndef __FIBRE_SIMPLE_SERDES
|
||||
#define __FIBRE_SIMPLE_SERDES
|
||||
|
||||
#include "cpp_utils.hpp"
|
||||
#include "limits.h"
|
||||
#include <optional> // TODO: make C++11 backport of this
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
|
||||
template<typename T, bool BigEndian, typename = void>
|
||||
struct SimpleSerializer;
|
||||
template<typename T>
|
||||
using LittleEndianSerializer = SimpleSerializer<T, false>;
|
||||
template<typename T>
|
||||
using BigEndianSerializer = SimpleSerializer<T, true>;
|
||||
|
||||
|
||||
/* @brief Serializer/deserializer for arbitrary integral number types */
|
||||
// TODO: allow reading an arbitrary number of bits
|
||||
template<typename T, bool BigEndian>
|
||||
struct SimpleSerializer<T, BigEndian, typename std::enable_if_t<std::is_integral<T>::value>> {
|
||||
static constexpr size_t BIT_WIDTH = std::numeric_limits<T>::digits;
|
||||
static constexpr size_t BYTE_WIDTH = (BIT_WIDTH + 7) / 8;
|
||||
|
||||
template<typename TIterator>
|
||||
static std::optional<T> read(TIterator* begin, TIterator end = nullptr) {
|
||||
T result = 0;
|
||||
if (BigEndian) {
|
||||
for (size_t i = BYTE_WIDTH; i > 0; (i++, (*begin)++)) {
|
||||
if (end && !(*begin < end))
|
||||
return std::nullopt;
|
||||
uint8_t byte = **begin;
|
||||
result |= static_cast<T>(byte) << ((i - 1) << 3);
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < BYTE_WIDTH; (i++, (*begin)++)) {
|
||||
if (end && !(*begin < end))
|
||||
return std::nullopt;
|
||||
uint8_t byte = **begin;
|
||||
result |= static_cast<T>(byte) << (i << 3);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename TIterator>
|
||||
static bool write(T value, TIterator* begin, TIterator end = nullptr) {
|
||||
if (BigEndian) {
|
||||
for (size_t i = BYTE_WIDTH; i > 0; (i--, (*begin)++)) {
|
||||
if (end && !(*begin < end))
|
||||
return false;
|
||||
uint8_t byte = static_cast<uint8_t>((value >> ((i - 1) << 3)) & 0xff);
|
||||
**begin = byte;
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < BYTE_WIDTH; (i++, (*begin)++)) {
|
||||
if (end && !(*begin < end))
|
||||
return false;
|
||||
uint8_t byte = static_cast<uint8_t>((value >> (i << 3)) & 0xff);
|
||||
**begin = byte;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline std::optional<T> read_le(fibre::cbufptr_t* buffer) {
|
||||
static_assert(is_complete<LittleEndianSerializer<T>>(), "no LittleEndianSerializer is defined for type T");
|
||||
return LittleEndianSerializer<T>::read(&buffer->begin(), buffer->end());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool write_le(T value, fibre::bufptr_t* buffer) {
|
||||
static_assert(is_complete<LittleEndianSerializer<T>>(), "no LittleEndianSerializer is defined for type T");
|
||||
return LittleEndianSerializer<T>::write(value, &buffer->begin(), buffer->end());
|
||||
}
|
||||
|
||||
template<typename T, typename = typename std::enable_if_t<!std::is_const<T>::value>>
|
||||
inline size_t write_le(T value, uint8_t* buffer){
|
||||
//TODO: add static_assert that this is still a little endian machine
|
||||
std::memcpy(&buffer[0], &value, sizeof(value));
|
||||
return sizeof(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if_t<std::is_const<T>::value, size_t>
|
||||
write_le(T value, uint8_t* buffer) {
|
||||
return write_le<std::remove_const_t<T>>(value, buffer);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline size_t write_le<float>(float value, uint8_t* buffer) {
|
||||
static_assert(CHAR_BIT * sizeof(float) == 32, "32 bit floating point expected");
|
||||
static_assert(std::numeric_limits<float>::is_iec559, "IEEE 754 floating point expected");
|
||||
uint32_t value_as_uint32;
|
||||
std::memcpy(&value_as_uint32, &value, sizeof(uint32_t));
|
||||
return write_le<uint32_t>(value_as_uint32, buffer);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline size_t read_le(T* value, const uint8_t* buffer){
|
||||
// TODO: add static_assert that this is still a little endian machine
|
||||
std::memcpy(value, buffer, sizeof(*value));
|
||||
return sizeof(*value);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline size_t read_le<float>(float* value, const uint8_t* buffer) {
|
||||
static_assert(CHAR_BIT * sizeof(float) == 32, "32 bit floating point expected");
|
||||
static_assert(std::numeric_limits<float>::is_iec559, "IEEE 754 floating point expected");
|
||||
return read_le(reinterpret_cast<uint32_t*>(value), buffer);
|
||||
}
|
||||
|
||||
// @brief Reads a value of type T from the buffer.
|
||||
// @param buffer Pointer to the buffer to be read. The pointer is updated by the number of bytes that were read.
|
||||
// @param length The number of available bytes in buffer. This value is updated to subtract the bytes that were read.
|
||||
template<typename T>
|
||||
static inline T read_le(const uint8_t** buffer, size_t* length) {
|
||||
T result;
|
||||
size_t cnt = read_le(&result, *buffer);
|
||||
*buffer += cnt;
|
||||
*length -= cnt;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef __FIBRE_STATUS_HPP
|
||||
#define __FIBRE_STATUS_HPP
|
||||
|
||||
namespace fibre {
|
||||
|
||||
enum Status {
|
||||
kFibreOk,
|
||||
kFibreBusy, //<! The request will complete asynchronously
|
||||
kFibreCancelled, //!< The operation was cancelled due to a request by the application or the remote peer
|
||||
kFibreClosed, //!< The operation has finished orderly or shall be finished orderly
|
||||
kFibreInvalidArgument, //!< Bug in the application
|
||||
kFibreInternalError, //!< Bug in the local fibre implementation
|
||||
kFibreProtocolError, //!< A remote peer is misbehaving (indicates bug in the remote peer)
|
||||
kFibreHostUnreachable, //!< The remote peer can no longer be reached
|
||||
//kFibreInsufficientData, // maybe we will introduce this to tell the caller that the granularity of the data is too small
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __FIBRE_STATUS_HPP
|
||||
Reference in New Issue
Block a user