Skip to content

Commit

Permalink
Document everything. Tweak use of lambdas to better enforce stack all…
Browse files Browse the repository at this point in the history
…ocation.
  • Loading branch information
eric-wieser committed Dec 31, 2016
1 parent 824752e commit edd1295
Show file tree
Hide file tree
Showing 20 changed files with 278 additions and 72 deletions.
2 changes: 1 addition & 1 deletion doc/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,7 @@ FILE_PATTERNS = *.c \
# be searched for input files as well.
# The default value is: NO.

RECURSIVE = NO
RECURSIVE = YES

# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
Expand Down
13 changes: 13 additions & 0 deletions doc/basics.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Basic interface
===============

The framing methods are exposed through the `PacketPrint` and `PacketStream` interfaces.

.. doxygenclass:: packetio::PacketPrint
:members:
:undoc-members:

.. doxygenclass:: packetio::PacketStream
:members:
:undoc-members:

2 changes: 1 addition & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
html_theme = 'default'

# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
Expand Down
32 changes: 12 additions & 20 deletions doc/index.rst
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
Welcome to PacketIO's documentation!
====================================
PacketIO
========

.. toctree::
:maxdepth: 2
:caption: Contents:

.. default-domain:: cpp
A C++ library distributed with PlatformIO_, for framing packets sent or received over an Arduino `Stream`, such as `Serial`.

Basic interface
=====================
The key feature of this library over other framing implementations it that it operates on streams. This means that if your application layer is able to produce or consume streams, you can push these streams right the way through your program. Put simply, this means you can send arbitrarily large packets, without having to worry about allocating buffers.

.. doxygenclass:: packetio::PacketPrint
:members:
:undoc-members:
.. toctree::
:maxdepth: 2

.. doxygenclass:: packetio::PacketStream
:members:
:undoc-members:
basics
protocols
listening
internals

Indices and tables
==================
.. default-domain:: cpp

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
.. _PlatformIO: https://platformio.org/
24 changes: 24 additions & 0 deletions doc/internals.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Internals
=========

Callback functions
------------------

The :class:`LambdaPointer` class is used to provide contextfull callback functions, using c++11 lambdas

.. doxygenclass:: packetio::LambdaPointer< Out(In...)>
:members:

Mirror of the Arduino interface
-------------------------------

These classes, found in :file:`_compat`, allow testing on the desktop, and potentially execution on
non-Arduino platforms

.. doxygenclass:: Print
:members:
:undoc-members:

.. doxygenclass:: Stream
:members:
:undoc-members:
33 changes: 33 additions & 0 deletions doc/listening.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Listening for packets
=====================

.. doxygenclass:: packetio::PacketListener_
:members:
:undoc-members:

Example usage:

.. code-block:: cpp
#include <packet_interface.h>
#include <PacketListener.h>
#include <cobs/Stream.h>
using namespace packetio;
void setup () {
COBSStream cobs_serial_in(Serial);
PacketListener handler(cobs_serial_in);
int message_count = 0;
auto message_handler = [&](const uint8_t* buffer, size_t len) {
message_count++;
};
handler.onMessage(&message_handler);
while(true) {
handler.update();
Serial.print("Message recieved: ");
Serial.print(message_count);
Serial.println();
}
}
31 changes: 31 additions & 0 deletions doc/protocols.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Framing Protocols
=================

COBS
----

.. include:: ../src/cobs/README.rst
:start-line: 2

.. doxygenclass:: packetio::COBSPrint
.. doxygenclass:: packetio::COBSStream

Escaped
-------

.. include:: ../src/escaped/README.rst
:start-line: 2

.. doxygenstruct:: packetio::EscapeCodes
.. doxygenclass:: packetio::EscapedPrint
.. doxygenclass:: packetio::EscapedStream

SLIP
----

.. include:: ../src/slip/README.rst
:start-line: 2

.. doxygentypedef:: packetio::SLIPEscapeCodes
.. doxygentypedef:: packetio::SLIPPrint
.. doxygentypedef:: packetio::SLIPStream
64 changes: 56 additions & 8 deletions src/PacketListener.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,50 @@

namespace packetio {

// A wrapper for a lambda function, that works with captured variables
template<typename LambdaType> class Handler {};
template<typename Out, typename ...In> class Handler<Out(In...)> {
template<typename LambdaType> class LambdaPointer {};
/**
* @brief A wrapper for storing a pointer to a lambda function, that works with captured variables
*
* @tparam Out lambda return type
* @tparam In lambda argument types
*/
template<typename Out, typename ...In> class LambdaPointer<Out(In...)> {
Out (*function)(void *, In...);
void *context;

static Out _no_context_handler(void* context, In... arguments) {
return ((Out (*)(In...))context)(arguments...);
}
public:
Handler(Out (*fptr)(In...)) {
/**
* @brief Create a LambdaPointer from a raw function pointer
*
* @param[in] fptr The context-less function
*/
LambdaPointer(Out (*fptr)(In...)) {
context = reinterpret_cast<void*>(fptr);
function = _no_context_handler;
}
/**
* @brief Create a LambdaPointer from a lambda, possibly with captures
*
* @param lambda A pointer to the lambda function. Because this a
* pointer, this must be allocated on the stack.
*/
template <typename T>
Handler(T &lambda) {
context = static_cast<void*>(&lambda);
LambdaPointer(T *lambda) {
context = static_cast<void*>(lambda);
function = [](void *context, In... arguments) {
return ((T *)context)->operator()(arguments...);
};
}
//! Invoke the underlying function
Out operator()(In ... in)
{
return function(context, in...);
}

//! Determine if the reference has been initialized
operator bool() {
return context != nullptr;
}
Expand All @@ -43,8 +62,8 @@ class PacketListener_ {
Overflow,
Framing
};
typedef Handler<void(uint8_t*, size_t, Error)> ErrorHandler;
typedef Handler<void(uint8_t*, size_t)> MessageHandler;
typedef LambdaPointer<void(uint8_t*, size_t, Error)> ErrorHandler;
typedef LambdaPointer<void(uint8_t*, size_t)> MessageHandler;

private:
PacketStream& _base;
Expand All @@ -61,8 +80,16 @@ class PacketListener_ {
if(_errorHandler) _errorHandler(message, len, e);
}
public:
/**
* @brief Construct a listener for the given packet stream
*/
PacketListener_(PacketStream& base) : _base(base) {}

/**
* @brief Read as much as possible from the underlying stream.
* If new packets are completed or errors occur, invoke the
* appropriate handler
*/
void update() {
int a = 0;
while((a = _base.available()) > 0) {
Expand All @@ -88,15 +115,36 @@ class PacketListener_ {
}
}

/**
* @brief Set the handler to invoke when a message is recieved
*
* @param[in] handler The handler to invoke. This will be called with the
* pointer to the start of the message, and its length.
*
* Note that because this is a LambdaPointer, the lambda function must have
* been allocated on the stack, such that its lifetime exceeds that of this
* object.
*/
void onMessage(MessageHandler handler) {
_messageHandler = handler;
}

/**
* @brief Set the handler to invoke when an error occurs
*
* @param[in] handler The handler to invoke. This will be called with the
* pointer to the start of the message, and its length.
*
* Note that because this is a LambdaPointer, the lambda function must have
* been allocated on the stack, such that its lifetime exceeds that of this
* object.
*/
void onError(ErrorHandler handler) {
_errorHandler = handler;
}
};

//! Convenience typedef for a PacketListener with a buffer size of 256
typedef PacketListener_<> PacketListener;

}
3 changes: 3 additions & 0 deletions src/_compat/Print.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#ifndef Print_H
#define Print_H
#include <string.h>
/**
* @brief Lightweight mirror of the (undocumented) arduino Print class, for use on other platforms
*/
class Print
{
public:
Expand Down
File renamed without changes.
5 changes: 5 additions & 0 deletions src/_compat/Stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@

#include <string.h>
#include "Print.h"
/**
* @brief Lightweight mirror of the arduino
* <a href="https://www.arduino.cc/en/Reference/Stream">Stream</a>
* class, for use on other platforms
*/
class Stream : Print
{
protected:
Expand Down
Empty file removed src/cobs/README.md
Empty file.
10 changes: 10 additions & 0 deletions src/cobs/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Consistent-Overhead Byte Stuffing
=================================

This is an implementation of `Consistent-Overhead Byte Stuffing`_.

This uses a null byte as an end of packet marker, and uses a clever technique
to encode null bytes within the packet with minimal overhead. See the link
above for more information.

.. _Consistent-Overhead Byte Stuffing: http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
32 changes: 0 additions & 32 deletions src/escaped/README.md

This file was deleted.

31 changes: 31 additions & 0 deletions src/escaped/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Escaping packet framer
======================

This packet framer uses a special character to indicate end-of-frame, and an
escape character to allow this to appear within a message. In the worst-case,
this causes the packet to be twice the data size.

The choice of these special characters is parameterizable through template
arguments. :file:`encoded/codes.h` contains some example choices of values, including
an implementation of SLIP_. To define your own, you can use code like the
following::

#include <escaped/Print.h>
#include <escaped/Stream.h>
#include <escaped/codes.h>

using namespace packetio;

// end, escape, escaped end, escaped escape
typedef EscapeCodes<'A','/','a','\\'> MyCodes;

EscapedPrint<MyCodes> printer(Serial);
EscapedStream<MyCodes> reader(Serial);


In this example, we use ``A`` to end a packet. So the packet ``ABCD/EFGH`` is
encoded to ``/aBCD/\EFGHA``. Here, the first `A` is replaced by the escape
sequence ``/a``, and the ``/`` is replaced with ``/\``. Finally, an ``A`` is
appended to end the packet.

.. _SLIP: https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol

0 comments on commit edd1295

Please sign in to comment.