7_protocol
Store definition
// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers
//
// SPDX-License-Identifier: CC0-1.0
int32 a short int that fits in the response buffers
string:32 a long string that does not fit in the response buffers
uint32=32 MTU
double=1e-5 ber
uint32 injected errors
Application
// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers
//
// SPDX-License-Identifier: CC0-1.0
/*!
 * \file
 * \brief Example with a stack of all default supplied protocol layers.
 *
 * This example simulates a lossy channel by generating random bit errors.  The
 * bit error rate can be configured using the \c ber store variable.  Moreover,
 * the MTU can also dynamically changed.
 *
 * Start this example using the \c stdio wrapper and connect the GUI to it.
 */
#include "ExampleProtocol.h"
#include <stored>
#ifdef STORED_COMPILER_MSVC
#	include <io.h>
#	define read(fd, buffer, len) _read(fd, buffer, (unsigned int)(len))
#	define STDERR_FILENO	      _fileno(stderr)
#	define STDOUT_FILENO	      _fileno(stdout)
#	define STDIN_FILENO	      _fileno(stdin)
#else
#	include <unistd.h>
#endif
#ifdef STORED_OS_WINDOWS
#	include <malloc.h>
#	ifndef alloca
#		define alloca(x) _alloca(x)
#	endif
#else
#	include <alloca.h>
#endif
#include <cstdio>
#include <ctime>
#if STORED_cplusplus < 201103L
#	include <inttypes.h>
#else
#	include <cinttypes>
#endif
static stored::ExampleProtocol store;
/*!
 * \brief Print a buffer, for demonstration purposes.
 */
static void
printBuffer(void const* buffer, size_t len, char const* prefix = nullptr, FILE* f = stdout)
{
	std::string s;
	if(prefix)
		s += prefix;
	uint8_t const* b = static_cast<uint8_t const*>(buffer);
	// flawfinder: ignore
	char buf[16] = {};
	for(size_t i = 0; i < len; i++) {
		switch(b[i]) {
		case '\0':
			s += "\\0";
			break;
		case '\r':
			s += "\\r";
			break;
		case '\n':
			s += "\\n";
			break;
		case '\t':
			s += "\\t";
			break;
		case '\\':
			s += "\\\\";
			break;
		default:
			if(b[i] < 0x20 || b[i] >= 0x7f) {
				// flawfinder: ignore
				snprintf(buf, sizeof(buf) - 1, "\\x%02" PRIx8, b[i]);
				s += buf;
			} else {
				s += (char)b[i];
			}
		}
	}
	s += "\n";
	fputs(s.c_str(), f);
}
/*!
 * \brief Simulate a lossy channel.
 *
 * Depending on the bit error rate (ber) set in the store, bits are flipped.
 * Moreover, it allows to set an MTU via the store.
 */
class LossyChannel : public stored::ProtocolLayer {
	STORED_CLASS_NOCOPY(LossyChannel)
public:
	typedef stored::ProtocolLayer base;
	LossyChannel(ProtocolLayer* up = nullptr, ProtocolLayer* down = nullptr)
		: base(up, down)
	{}
	virtual ~LossyChannel() override is_default
	virtual void decode(void* buffer, size_t len) override
	{
		char* buffer_ = static_cast<char*>(buffer);
		for(size_t i = 0; i < len; i++)
			buffer_[i] = lossyByte(buffer_[i]);
		printBuffer(buffer, len, "> ");
		base::decode(buffer, len);
	}
	virtual void encode(void const* buffer, size_t len, bool last = true) override
	{
		// cppcheck-suppress allocaCalled
		char* buffer_ = (char*)alloca(len);
		for(size_t i = 0; i < len; i++)
			buffer_[i] = lossyByte(static_cast<char const*>(buffer)[i]);
		printBuffer(buffer_, len, "< ");
		base::encode(buffer_, len, last);
	}
	using base::encode;
	// Bit error rate
	double ber() const
	{
		return store.ber;
	}
	char lossyByte(char b)
	{
		for(int i = 0; i < 8; i++) {
			double p =
#ifdef STORED_OS_WINDOWS
				(double)::rand() / RAND_MAX;
#else
				// flawfinder: ignore
				drand48();
#endif
			if(p < ber()) {
				// Inject an error.
				b = (char)(b ^ (char)(1 << (rand() % 8)));
				store.injected_errors = store.injected_errors + 1;
			}
		}
		return b;
	}
	virtual size_t mtu() const override
	{
		return store.MTU.as<size_t>();
	}
};
int main()
{
	// Demonstrate a full stack assuming a lossy channel.
	// In this example, the lossy channel is simulated by LossyChannel,
	// which just flips bits, depending on the set bit error rate (BER).
	/*
	Consider the received string:
		\x1b_@Y?Ez\x7fI\x1b\\
	This is:
		\x1b_       TerminalLayer: start of message
		  @Y        DebugArqLayer: seq=89
		    ?       Debugger: capabilities
		  E         SegmentationLayer: last chunk
		  z\x7fI    AsciiEscapeLayer: z<tab>
			Crc16Layer: CRC=0x7a09
		\x1b\\      TerminalLayer: end of message
	To test, run in a shell:
	  echo -e -n '\x1b_\xc0X\xe4\x1c\x1b\\\x1b_@Y?Ez\x7fI\x1b\\' | 7_protocol
	*/
	printf("Demo of a lossy channel.\n");
	printf("Run this example using libstored.wrapper.stdio with the flag\n");
	printf("  -S segment,arq,crc16,ascii,term\n\n");
	stored::Debugger debugger("7_protocol");
	debugger.map(store);
	stored::SegmentationLayer segmentation;
	segmentation.wrap(debugger);
	stored::DebugArqLayer arq;
	arq.wrap(segmentation);
	stored::Crc16Layer crc;
	crc.wrap(arq);
	stored::AsciiEscapeLayer escape;
	escape.wrap(crc);
	stored::TerminalLayer terminal;
	terminal.wrap(escape);
	stored::BufferLayer buffer;
	buffer.wrap(terminal);
	LossyChannel lossy;
	lossy.wrap(buffer);
	stored::StdioLayer stdio;
	stdio.wrap(lossy);
	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);
#ifdef STORED_OS_WINDOWS
	// flawfinder: ignore
	srand((unsigned int)time(NULL));
#else
	// flawfinder: ignore
	srand48((long)time(NULL));
#endif
	stored::Poller poller;
	stored::PollableFileLayer pollable(stdio, stored::Pollable::PollIn);
	if((errno = poller.add(pollable))) {
		perror("Cannot add pollable");
		return 1;
	}
	while(stdio.isOpen()) {
		poller.poll();
		stdio.recv();
	}
	return 0;
}
Store reference
- 
template<typename Base_, typename Implementation_>
class ExampleProtocolObjects All ExampleProtocolBase’s objects.
Subclassed by stored::ExampleProtocolBase< ExampleProtocol >, stored::ExampleProtocolBase< QExampleProtocol >
Public Members
- 
impl::StoreVariantV<Base, Implementation, Type::String, 16u, 32u> a_long_string_that_does_not_fit_in_the_response_buffers
 a long string that does not fit in the response buffers
- 
impl::StoreVariable<Base, Implementation, int32_t, 56u, 4> a_short_int_that_fits_in_the_response_buffers
 a short int that fits in the response buffers
- 
impl::StoreVariable<Base, Implementation, double, 0u, 8> ber
 ber
- 
impl::StoreVariable<Base, Implementation, uint32_t, 60u, 4> injected_errors
 injected errors
- 
impl::StoreVariable<Base, Implementation, uint32_t, 8u, 4> MTU
 MTU.
- 
impl::StoreVariantV<Base, Implementation, Type::String, 16u, 32u> a_long_string_that_does_not_fit_in_the_response_buffers
 
- 
template<typename Implementation_>
class ExampleProtocolBase : public stored::ExampleProtocolObjects<ExampleProtocolBase<Implementation_>, Implementation_> Base class with default interface of all ExampleProtocol implementations.
Although there are no virtual functions in the base class, subclasses can override them. The (lowest) subclass must pass the
Implementation_template paramater to its base, such that all calls from the base class can be directed to the proper overridden implementation.The base class cannot be instantiated. If a default implementation is required, which does not have side effects to functions, instantiate stored::ExampleProtocol. This class contains all data of all variables, so it can be large. So, be aware when instantiating it on the stack. Heap is fine. Static allocations is fine too, as the constructor and destructor are trivial.
To inherit the base class, you can use the following template:
class ExampleProtocol : public stored::store<ExampleProtocol, ExampleProtocolBase>::type { STORE_CLASS(ExampleProtocol, ExampleProtocolBase) public: // Your class implementation, such as: ExampleProtocol() is_default // ... };Some compilers or tools may get confused by the inheritance using
stored::storeorstored::store_t. Alternatively, useSTORE_T(...)instead, providing the template parameters ofstored::storeas macro arguments.See also
See also
stored::ExampleProtocolData
Public Types
Values:
- 
enumerator ObjectCount
 Number of objects in the store.
- 
enumerator VariableCount
 Number of variables in the store.
- 
enumerator FunctionCount
 Number of functions in the store.
- 
enumerator BufferSize
 Buffer size.
- 
enumerator ObjectCount
 
- 
typedef Implementation_ Implementation
 Type of the actual implementation, which is the (lowest) subclass.
- 
typedef uintptr_t Key
 Type of a key.
See also
- 
typedef Map<String::type, Variant<Implementation>>::type ObjectMap
 Map as generated by map().
- 
typedef ExampleProtocolObjects<ExampleProtocolBase, Implementation_> Objects
 
- 
typedef ExampleProtocolBase root
 We are the root, as used by
STORE_CLASS.
- 
typedef ExampleProtocolBase self
 Define
selfforstored::store.
Public Functions
- 
inline ~ExampleProtocolBase()
 
- 
inline Key bufferToKey(void const *buffer) const noexcept
 Converts a variable’s buffer to a key.
A key is unique for all variables of the same store, but identical for the same variables across different instances of the same store class. Therefore, the key can be used to synchronize between instances of the same store. A key does not contain meta data, such as type or length. It is up to the synchronization library to make sure that these properties are handled well.
For synchronization, when hookEntryX() or hookEntryRO() is invoked, one can compute the key of the object that is accessed. The key can be used, for example, in a key-to-Variant map. When data arrives from another party, the key can be used to find the proper Variant in the map.
This way, data exchange is type-safe, as the Variant can check if the data format matches the expected type. However, one cannot process data if the key is not set yet in the map.
- 
inline Type::type bufferToType(void const *buffer) noexcept
 Return the type of the variable, given its buffer.
- 
inline Variant<Implementation> find(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept
 Finds an object with the given name.
- Returns:
 the object, or an invalid stored::Variant if not found.
- 
template<typename T>
inline Function<T, Implementation> function(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept Finds a function with the given name.
The function, when it exists, must have the given (fixed) type.
- 
inline Implementation const &implementation() const noexcept
 Returns the reference to the implementation.
- 
inline Implementation &implementation() noexcept
 Returns the reference to the implementation.
- 
template<typename F>
inline void list(F f, void *arg, char const *prefix, String::type *nameBuffer) noexcept Calls a callback for every object in the longDirectory().
See also
- 
template<typename F>
inline void list(F f, void *arg, char const *prefix = nullptr) noexcept Calls a callback for every object in the longDirectory().
See also
- 
inline uint8_t const *longDirectory() const noexcept
 Retuns the long directory.
When not available, the short directory is returned.
- 
inline ObjectMap map(char const *prefix = nullptr)
 Create a name to Variant map for the store.
Generating the map may be expensive and the result is not cached.
- 
inline char const *name() const noexcept
 Returns the name of store, which can be used as prefix for stored::Debugger.
- 
inline uint8_t const *shortDirectory() const noexcept
 Returns the short directory.
- 
template<typename T>
inline Variable<T, Implementation> variable(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept Finds a variable with the given name.
The variable, when it exists, must have the given (fixed) type.
Public Members
- 
impl::StoreVariantV<Base, Implementation, Type::String, 16u, 32u> a_long_string_that_does_not_fit_in_the_response_buffers
 a long string that does not fit in the response buffers
- 
impl::StoreVariable<Base, Implementation, int32_t, 56u, 4> a_short_int_that_fits_in_the_response_buffers
 a short int that fits in the response buffers
- 
impl::StoreVariable<Base, Implementation, double, 0u, 8> ber
 ber
- 
impl::StoreVariable<Base, Implementation, uint32_t, 60u, 4> injected_errors
 injected errors
- 
impl::StoreVariable<Base, Implementation, uint32_t, 8u, 4> MTU
 MTU.
Public Static Functions
- 
template<typename T>
static inline FreeFunction<T, Implementation> freeFunction(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept Finds a function with the given name.
The function, when it exists, must have the given (fixed) type. It is returned as a free function; it is not bound yet to a specific store instance. This function is constexpr for C++14.
- 
template<typename T>
static inline FreeVariable<T, Implementation> freeVariable(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept Finds a variable with the given name.
The variable, when it exists, must have the given (fixed) type. It is returned as a free variable; it is not bound yet to a specific store instance. This function is constexpr for C++14.
- 
static inline constexpr char const *hash() noexcept
 Returns a unique hash of the store.
Friends
- friend class impl::StoreFunction
 
- friend class impl::StoreVariable
 
- friend class impl::StoreVariantF
 
- friend class impl::StoreVariantV
 
- friend class stored::FreeVariable
 
- friend class stored::Variant< void >
 
- 
class ExampleProtocol : public stored::ExampleProtocolDefaultFunctions<ExampleProtocolBase<ExampleProtocol>>
 Default ExampleProtocolBase implementation.
Public Functions
- 
ExampleProtocol() = default
 Default constructor.
- 
ExampleProtocol() = default