19 Commits
Author SHA1 Message Date
MightyPork b74bda7cf5 added logging support for better debug 2018-01-12 10:09:52 +01:00
MightyPorkandGitHub 530577958d Update README.md 2017-12-28 15:23:57 +01:00
MightyPorkandGitHub 2d121ddce9 Update README.md 2017-12-28 15:17:56 +01:00
MightyPork 1e6cdf22f0 remove lies from readme 2017-12-27 19:26:08 +01:00
MightyPork 148b8a0fe6 added better comment to config example 2017-12-27 19:22:43 +01:00
MightyPorkandGitHub 8205ab93b0 Update README.md 2017-12-27 17:25:19 +01:00
MightyPork 88d6a5cd23 v bump 2017-12-27 17:15:43 +01:00
MightyPork c4b169cb80 fix call to undefined function if SOF byte is not used 2017-12-27 17:15:29 +01:00
MightyPorkandGitHub 03377b4b9f Update README.md 2017-12-27 00:37:52 +01:00
MightyPork 1a62d78443 fix wrongly disallowed rx of max possible size 2017-12-23 22:55:46 +01:00
MightyPork 1488abb517 bumped version 2017-12-23 16:24:33 +01:00
MightyPork 1f52a17fc2 Fix empty payload checksum being appended even if payload is empty. 2017-12-23 16:23:59 +01:00
MightyPork 7f6f21cdb7 fixed some indentation 2017-12-23 12:25:48 +01:00
MightyPork 38952f8405 version bump 2017-12-23 09:57:07 +01:00
MightyPork 68ea3bb3a4 Fix the ID listener removal bug 2017-12-23 09:56:33 +01:00
MightyPork ce06abfdbf fix for a compile bug & added TF_DeInit() 2017-12-14 11:40:22 +01:00
MightyPorkandGitHub db0ae30f41 Add re-entrancy support / multiple instances (#9)
* done with changes but now segfaulting

* fixed the bug

* fix some bad indents

* some cleanings

* updated readme
2017-12-14 00:28:06 +01:00
MightyPork 77f60e1876 removed no longer used TF_OVERHEAD_BYTES 2017-12-12 21:09:07 +01:00
MightyPork 978eabfb92 Added utilities 2017-12-12 20:54:47 +01:00
23 changed files with 1896 additions and 1210 deletions
+37 -17
View File
@@ -1,8 +1,8 @@
# TinyFrame # TinyFrame
TinyFrame is a simple library for building and parsing frames to be sent TinyFrame is a simple library for building and parsing data frames to be sent
over a serial interface (e.g. UART, telnet etc.). The code is written over a serial interface (e.g. UART, telnet etc.). The code is written to build with
to build with `--std=gnu89` and later. `--std=gnu89` and later.
TinyFrame is suitable for a wide range of applications, including inter-microcontroller TinyFrame is suitable for a wide range of applications, including inter-microcontroller
communication, as a protocol for FTDI-based PC applications or for messaging through communication, as a protocol for FTDI-based PC applications or for messaging through
@@ -10,15 +10,32 @@ UDP packets. If you find a good use for it, please let me know so I can add it h
Frames can be protected by a checksum (~XOR, CRC16 or CRC32) and contain Frames can be protected by a checksum (~XOR, CRC16 or CRC32) and contain
a unique ID field which can be used for chaining related messages. The highest bit a unique ID field which can be used for chaining related messages. The highest bit
of the generated IDs is different in each peer to avoid collisions. of the generated frame IDs is different in each peer to avoid collisions.
Peers are functionally equivalent and can send messages to each other Peers are functionally equivalent and can send messages to each other
(the names "master" and "slave" are used only for convenience and have special meaning (the names "master" and "slave" are used only for convenience).
in the demos).
The library lets you register listeners (callback functions) to wait for (1) any frame, (2) The library lets you register listeners (callback functions) to wait for (1) any frame, (2)
a particular frame Type, or (3) a specific message ID. This high-level API lets you a particular frame Type, or (3) a specific message ID. This high-level API lets the user
easily implement various async communication patterns. easily implement various async communication patterns.
TinyFrame is re-entrant and supports creating multiple instances with the limitation
that their structure (field sizes and checksum type) must be the same. There is a support
for adding multi-threaded access to a shared instance using a mutex (via a callback stub).
TinyFrame also comes with (optional) helper functions for building and parsing message
payloads, those are provided in the `utils/` folder.
## Ports
TinyFrame has been ported to mutiple languages:
- The reference C implementation is in this repo
- Python port - [MightyPork/PonyFrame](https://github.com/MightyPork/PonyFrame)
- Rust port - [cpsdqs/tinyframe-rs](https://github.com/cpsdqs/tinyframe-rs)
- JavaScript port - [cpsdqs/tinyframe-js](https://github.com/cpsdqs/tinyframe-js)
Please note most of the ports are experimental and may exhibit various bugs or missing features. Testers are welcome :)
## Frame structure ## Frame structure
All fields in the message frame have a configurable size (see the top of the header file). All fields in the message frame have a configurable size (see the top of the header file).
@@ -28,28 +45,31 @@ best suit your application needs.
For example, you don't need 4 bytes (`uint32_t`) for the For example, you don't need 4 bytes (`uint32_t`) for the
length field if your payloads are 20 bytes long, using a 1-byte field (`uint8_t`) will save length field if your payloads are 20 bytes long, using a 1-byte field (`uint8_t`) will save
3 bytes. This may be significant if you high throughput. 3 bytes. This may be significant if you need high throughput.
``` ```
,-----+----+-----+------+------------+- - - -+------------, ,-----+-----+-----+------+------------+- - - -+-------------,
| SOF | ID | LEN | TYPE | HEAD_CKSUM | DATA | PLD_CKSUM | | SOF | ID | LEN | TYPE | HEAD_CKSUM | DATA | DATA_CKSUM |
| 1 | ? | ? | ? | ? | ... | ? | <- size (bytes) | 0-1 | 1-4 | 1-4 | 1-4 | 0-4 | ... | 0-4 | <- size (bytes)
'-----+----+-----+------+------------+- - - -+------------' '-----+-----+-----+------+------------+- - - -+-------------'
SOF ......... start of frame, 0x01 SOF ......... start of frame, usually 0x01 (optional, configurable)
ID ......... the frame ID (MSb is the peer bit) ID ......... the frame ID (MSb is the peer bit)
LEN ......... nr of data bytes in the frame LEN ......... number of data bytes in the frame
TYPE ........ message type (used to run Type Listeners, pick any values you like) TYPE ........ message type (used to run Type Listeners, pick any values you like)
HEAD_CKSUM .. header checksum HEAD_CKSUM .. header checksum
DATA ........ LEN bytes of data
DATA_CKSUM .. checksum, implemented as XOR of all preceding bytes in the message DATA ........ LEN bytes of data (can be 0, in which case DATA_CKSUM is omitted as well)
DATA_CKSUM .. data checksum
``` ```
## Usage Hints ## Usage Hints
- All TinyFrame functions, typedefs and macros start with the `TF_` prefix. - All TinyFrame functions, typedefs and macros start with the `TF_` prefix.
- Both peers must include the library with the same parameters (configured at the top of the header file) - Both peers must include the library with the same parameters (configured at the top of the header file)
- Start by calling `TF_Init()` with `TF_MASTER` or `TF_SLAVE` as the argument - Start by calling `TF_Init()` with `TF_MASTER` or `TF_SLAVE` as the argument. This creates a handle.
Use `TF_InitStatic()` to avoid the use of malloc(). If multiple instances are used, you can tag them
using the `tf.userdata` / `tf.usertag` field.
- Implement `TF_WriteImpl()` - declared at the bottom of the header file as `extern`. - Implement `TF_WriteImpl()` - declared at the bottom of the header file as `extern`.
This function is used by `TF_Send()` and others to write bytes to your UART (or other physical layer). This function is used by `TF_Send()` and others to write bytes to your UART (or other physical layer).
A frame can be sent in it's entirety, or in multiple parts, depending on its size. A frame can be sent in it's entirety, or in multiple parts, depending on its size.
+7 -4
View File
@@ -16,10 +16,10 @@
// If the connection is reliable, you can disable the SOF byte and checksums. // If the connection is reliable, you can disable the SOF byte and checksums.
// That can save up to 9 bytes of overhead. // That can save up to 9 bytes of overhead.
// ,-----+----+-----+------+------------+- - - -+------------, // ,-----+-----+-----+------+------------+- - - -+-------------,
// | SOF | ID | LEN | TYPE | HEAD_CKSUM | DATA | PLD_CKSUM | // | SOF | ID | LEN | TYPE | HEAD_CKSUM | DATA | DATA_CKSUM |
// | 1 | ? | ? | ? | ? | ... | ? | <- size (bytes) // | 0-1 | 1-4 | 1-4 | 1-4 | 0-4 | ... | 0-4 | <- size (bytes)
// '-----+----+-----+------+------------+- - - -+------------' // '-----+-----+-----+------+------------+- - - -+-------------'
// !!! BOTH PEERS MUST USE THE SAME SETTINGS !!! // !!! BOTH PEERS MUST USE THE SAME SETTINGS !!!
@@ -69,6 +69,9 @@ typedef uint8_t TF_COUNT;
// ticks = number of calls to TF_Tick() // ticks = number of calls to TF_Tick()
#define TF_PARSER_TIMEOUT_TICKS 10 #define TF_PARSER_TIMEOUT_TICKS 10
// Error reporting function. To disable debug, change to empty define
#define TF_Error(format, ...) printf("[TF] " format "\n", ##__VA_ARGS__)
//------------------------- End of user config ------------------------------ //------------------------- End of user config ------------------------------
#endif //TF_CONFIG_H #endif //TF_CONFIG_H
+3 -3
View File
@@ -10,19 +10,19 @@
* listener timeout feature. * listener timeout feature.
*/ */
void TF_WriteImpl(const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{ {
// send to UART // send to UART
} }
/** Claim the TX interface before composing and sending a frame */ /** Claim the TX interface before composing and sending a frame */
void TF_ClaimTx(void) void TF_ClaimTx(TinyFrame *tf)
{ {
// take mutex // take mutex
} }
/** Free the TX interface after composing and sending a frame */ /** Free the TX interface after composing and sending a frame */
void TF_ReleaseTx(void) void TF_ReleaseTx(TinyFrame *tf)
{ {
// release mutex // release mutex
} }
+327 -283
View File
File diff suppressed because it is too large Load Diff
+148 -55
View File
@@ -10,12 +10,13 @@
* Upstream URL: https://github.com/MightyPork/TinyFrame * Upstream URL: https://github.com/MightyPork/TinyFrame
*/ */
#define TF_VERSION "1.2.0" #define TF_VERSION "2.1.0"
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include <stdint.h> // for uint8_t etc #include <stdint.h> // for uint8_t etc
#include <stdbool.h> // for bool #include <stdbool.h> // for bool
#include <stdlib.h> // for NULL #include <stddef.h> // for NULL
#include <string.h> // for memset()
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Select checksum type (0 = none, 8 = ~XOR, 16 = CRC16 0x8005, 32 = CRC32) // Select checksum type (0 = none, 8 = ~XOR, 16 = CRC16 0x8005, 32 = CRC32)
@@ -24,7 +25,7 @@
#define TF_CKSUM_CRC16 16 #define TF_CKSUM_CRC16 16
#define TF_CKSUM_CRC32 32 #define TF_CKSUM_CRC32 32
#include <TF_Config.h> #include "TF_Config.h"
//region Resolve data types //region Resolve data types
@@ -74,17 +75,6 @@
#error Bad value for TF_CKSUM_TYPE, must be 8, 16 or 32 #error Bad value for TF_CKSUM_TYPE, must be 8, 16 or 32
#endif #endif
// Bytes added to TF_MAX_PAYLOAD for the send buffer size.
#define TF_OVERHEAD_BYTES \
(1*TF_USE_SOF_BYTE + \
sizeof(TF_ID) + \
sizeof(TF_LEN) + \
sizeof(TF_CKSUM) + \
sizeof(TF_TYPE) + \
sizeof(TF_CKSUM) \
)
//endregion //endregion
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@@ -98,9 +88,10 @@
/** Peer bit enum (used for init) */ /** Peer bit enum (used for init) */
typedef enum { typedef enum {
TF_SLAVE = 0, TF_SLAVE = 0,
TF_MASTER, TF_MASTER = 1,
} TF_Peer; } TF_Peer;
/** Response from listeners */
typedef enum { typedef enum {
TF_NEXT = 0, //!< Not handled, let other listeners handle it TF_NEXT = 0, //!< Not handled, let other listeners handle it
TF_STAY = 1, //!< Handled, stay TF_STAY = 1, //!< Handled, stay
@@ -124,15 +115,11 @@ typedef struct _TF_MSG_STRUCT_ {
*/ */
static inline void TF_ClearMsg(TF_Msg *msg) static inline void TF_ClearMsg(TF_Msg *msg)
{ {
msg->frame_id = 0; memset(msg, 0, sizeof(TF_Msg));
msg->is_response = false;
msg->type = 0;
msg->data = NULL;
msg->len = 0;
msg->userdata = NULL;
msg->userdata2 = NULL;
} }
typedef struct TinyFrame_ TinyFrame;
/** /**
* TinyFrame Type Listener callback * TinyFrame Type Listener callback
* *
@@ -142,21 +129,124 @@ static inline void TF_ClearMsg(TF_Msg *msg)
* @param len - number of bytes in the buffer * @param len - number of bytes in the buffer
* @return listener result * @return listener result
*/ */
typedef TF_Result (*TF_Listener)(TF_Msg *msg); typedef TF_Result (*TF_Listener)(TinyFrame *tf, TF_Msg *msg);
// -------------------------------------------------------------------
// region Internal
enum TFState_ {
TFState_SOF = 0, //!< Wait for SOF
TFState_LEN, //!< Wait for Number Of Bytes
TFState_HEAD_CKSUM, //!< Wait for header Checksum
TFState_ID, //!< Wait for ID
TFState_TYPE, //!< Wait for message type
TFState_DATA, //!< Receive payload
TFState_DATA_CKSUM //!< Wait for Checksum
};
struct TF_IdListener_ {
TF_ID id;
TF_Listener fn;
TF_TICKS timeout; // nr of ticks remaining to disable this listener
TF_TICKS timeout_max; // the original timeout is stored here
void *userdata;
void *userdata2;
};
struct TF_TypeListener_ {
TF_TYPE type;
TF_Listener fn;
};
struct TF_GenericListener_ {
TF_Listener fn;
};
/**
* Frame parser internal state.
*/
struct TinyFrame_ {
/* Public user data */
void *userdata;
uint32_t usertag;
// --- the rest of the struct is internal, do not access directly ---
/* Own state */
TF_Peer peer_bit; //!< Own peer bit (unqiue to avoid msg ID clash)
TF_ID next_id; //!< Next frame / frame chain ID
/* Parser state */
enum TFState_ state;
TF_TICKS parser_timeout_ticks;
TF_ID id; //!< Incoming packet ID
TF_LEN len; //!< Payload length
uint8_t data[TF_MAX_PAYLOAD_RX]; //!< Data byte buffer
TF_LEN rxi; //!< Field size byte counter
TF_CKSUM cksum; //!< Checksum calculated of the data stream
TF_CKSUM ref_cksum; //!< Reference checksum read from the message
TF_TYPE type; //!< Collected message type number
bool discard_data; //!< Set if (len > TF_MAX_PAYLOAD) to read the frame, but ignore the data.
/* --- Callbacks --- */
/* Transaction callbacks */
struct TF_IdListener_ id_listeners[TF_MAX_ID_LST];
struct TF_TypeListener_ type_listeners[TF_MAX_TYPE_LST];
struct TF_GenericListener_ generic_listeners[TF_MAX_GEN_LST];
// Those counters are used to optimize look-up times.
// They point to the highest used slot number,
// or close to it, depending on the removal order.
TF_COUNT count_id_lst;
TF_COUNT count_type_lst;
TF_COUNT count_generic_lst;
// Buffer for building frames
uint8_t sendbuf[TF_SENDBUF_LEN];
};
// endregion
// -------------------------------------------------------------------
/** /**
* Initialize the TinyFrame engine. * Initialize the TinyFrame engine.
* This can also be used to completely reset it (removing all listeners etc) * This can also be used to completely reset it (removing all listeners etc).
*
* The field .userdata (or .usertag) can be used to identify different instances
* in the TF_WriteImpl() function etc. Set this field after the init.
*
* This function is a wrapper around TF_InitStatic that calls malloc() to obtain
* the instance.
* *
* @param peer_bit - peer bit to use for self * @param peer_bit - peer bit to use for self
*/ */
void TF_Init(TF_Peer peer_bit); TinyFrame *TF_Init(TF_Peer peer_bit);
/**
* Initialize the TinyFrame engine using a statically allocated instance struct.
*
* The .userdata / .usertag field is preserved when TF_InitStatic is called.
*
* @param peer_bit - peer bit to use for self
*/
void TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit);
/**
* De-init the dynamically allocated TF instance
*
* @param tf
*/
void TF_DeInit(TinyFrame *tf);
/** /**
* Reset the frame parser state machine. * Reset the frame parser state machine.
* This does not affect registered listeners. * This does not affect registered listeners.
*/ */
void TF_ResetParser(void); void TF_ResetParser(TinyFrame *tf);
/** /**
* Register a frame type listener. * Register a frame type listener.
@@ -166,14 +256,14 @@ void TF_ResetParser(void);
* @param timeout - timeout in ticks to auto-remove the listener (0 = keep forever) * @param timeout - timeout in ticks to auto-remove the listener (0 = keep forever)
* @return slot index (for removing), or TF_ERROR (-1) * @return slot index (for removing), or TF_ERROR (-1)
*/ */
bool TF_AddIdListener(TF_Msg *msg, TF_Listener cb, TF_TICKS timeout); bool TF_AddIdListener(TinyFrame *tf, TF_Msg *msg, TF_Listener cb, TF_TICKS timeout);
/** /**
* Remove a listener by the message ID it's registered for * Remove a listener by the message ID it's registered for
* *
* @param frame_id - the frame we're listening for * @param frame_id - the frame we're listening for
*/ */
bool TF_RemoveIdListener(TF_ID frame_id); bool TF_RemoveIdListener(TinyFrame *tf, TF_ID frame_id);
/** /**
* Register a frame type listener. * Register a frame type listener.
@@ -182,14 +272,14 @@ bool TF_RemoveIdListener(TF_ID frame_id);
* @param cb - callback * @param cb - callback
* @return slot index (for removing), or TF_ERROR (-1) * @return slot index (for removing), or TF_ERROR (-1)
*/ */
bool TF_AddTypeListener(TF_TYPE frame_type, TF_Listener cb); bool TF_AddTypeListener(TinyFrame *tf, TF_TYPE frame_type, TF_Listener cb);
/** /**
* Remove a listener by type. * Remove a listener by type.
* *
* @param type - the type it's registered for * @param type - the type it's registered for
*/ */
bool TF_RemoveTypeListener(TF_TYPE type); bool TF_RemoveTypeListener(TinyFrame *tf, TF_TYPE type);
/** /**
* Register a generic listener. * Register a generic listener.
@@ -197,14 +287,14 @@ bool TF_RemoveTypeListener(TF_TYPE type);
* @param cb - callback * @param cb - callback
* @return slot index (for removing), or TF_ERROR (-1) * @return slot index (for removing), or TF_ERROR (-1)
*/ */
bool TF_AddGenericListener(TF_Listener cb); bool TF_AddGenericListener(TinyFrame *tf, TF_Listener cb);
/** /**
* Remove a generic listener by function pointer * Remove a generic listener by function pointer
* *
* @param cb - callback function to remove * @param cb - callback function to remove
*/ */
bool TF_RemoveGenericListener(TF_Listener cb); bool TF_RemoveGenericListener(TinyFrame *tf, TF_Listener cb);
/** /**
* Send a frame, no listener * Send a frame, no listener
@@ -212,17 +302,12 @@ bool TF_RemoveGenericListener(TF_Listener cb);
* @param msg - message struct. ID is stored in the frame_id field * @param msg - message struct. ID is stored in the frame_id field
* @return success * @return success
*/ */
bool TF_Send(TF_Msg *msg); bool TF_Send(TinyFrame *tf, TF_Msg *msg);
/** /**
* Like TF_Send, but without the struct * Like TF_Send, but without the struct
*/ */
bool TF_SendSimple(TF_TYPE type, const uint8_t *data, TF_LEN len); bool TF_SendSimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len);
/**
* Like TF_Query, but without the struct
*/
bool TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener listener, TF_TICKS timeout, void *userdata);
/** /**
* Send a frame, and optionally attach an ID listener. * Send a frame, and optionally attach an ID listener.
@@ -232,7 +317,13 @@ bool TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener l
* @param timeout - listener expiry time in ticks * @param timeout - listener expiry time in ticks
* @return success * @return success
*/ */
bool TF_Query(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout); bool TF_Query(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout);
/**
* Like TF_Query, but without the struct
*/
bool TF_QuerySimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len,
TF_Listener listener, TF_TICKS timeout);
/** /**
* Send a response to a received message. * Send a response to a received message.
@@ -240,15 +331,15 @@ bool TF_Query(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout);
* @param msg - message struct. ID is read from frame_id. set ->renew to reset listener timeout * @param msg - message struct. ID is read from frame_id. set ->renew to reset listener timeout
* @return success * @return success
*/ */
bool TF_Respond(TF_Msg *msg); bool TF_Respond(TinyFrame *tf, TF_Msg *msg);
/** /**
* Renew ID listener timeout * Renew an ID listener timeout externally (as opposed to by returning TF_RENEW from the ID listener)
* *
* @param id - listener ID to renew * @param id - listener ID to renew
* @return true if listener was found and renewed * @return true if listener was found and renewed
*/ */
bool TF_RenewIdListener(TF_ID id); bool TF_RenewIdListener(TinyFrame *tf, TF_ID id);
/** /**
* Accept incoming bytes & parse frames * Accept incoming bytes & parse frames
@@ -256,21 +347,14 @@ bool TF_RenewIdListener(TF_ID id);
* @param buffer - byte buffer to process * @param buffer - byte buffer to process
* @param count - nr of bytes in the buffer * @param count - nr of bytes in the buffer
*/ */
void TF_Accept(const uint8_t *buffer, size_t count); void TF_Accept(TinyFrame *tf, const uint8_t *buffer, size_t count);
/** /**
* Accept a single incoming byte * Accept a single incoming byte
* *
* @param c - a received char * @param c - a received char
*/ */
void TF_AcceptChar(uint8_t c); void TF_AcceptChar(TinyFrame *tf, uint8_t c);
/**
* 'Write bytes' function that sends data to UART
*
* ! Implement this in your application code !
*/
extern void TF_WriteImpl(const uint8_t *buff, size_t len);
/** /**
* This function should be called periodically. * This function should be called periodically.
@@ -280,12 +364,21 @@ extern void TF_WriteImpl(const uint8_t *buff, size_t len);
* *
* (suggestion - call this in a SysTick handler) * (suggestion - call this in a SysTick handler)
*/ */
void TF_Tick(void); void TF_Tick(TinyFrame *tf);
// --- TO BE IMPLEMENTED BY USER ---
/**
* 'Write bytes' function that sends data to UART
*
* ! Implement this in your application code !
*/
extern void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len);
/** Claim the TX interface before composing and sending a frame */ /** Claim the TX interface before composing and sending a frame */
extern void TF_ClaimTx(void); extern void TF_ClaimTx(TinyFrame *tf);
/** Free the TX interface after composing and sending a frame */ /** Free the TX interface after composing and sending a frame */
extern void TF_ReleaseTx(void); extern void TF_ReleaseTx(TinyFrame *tf);
#endif #endif
+12 -5
View File
@@ -7,6 +7,7 @@
// those magic defines are needed so we can use clone() // those magic defines are needed so we can use clone()
#define _GNU_SOURCE #define _GNU_SOURCE
#define __USE_GNU #define __USE_GNU
#include <sched.h> #include <sched.h>
#include <unistd.h> #include <unistd.h>
@@ -15,10 +16,14 @@
#include <string.h> #include <string.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <signal.h> #include <signal.h>
#include <malloc.h>
#include <stdlib.h>
volatile int sockfd = -1; volatile int sockfd = -1;
volatile bool conn_disband = false; volatile bool conn_disband = false;
TinyFrame *demo_tf;
/** /**
* Close socket * Close socket
*/ */
@@ -34,7 +39,7 @@ void demo_disconn(void)
* @param buff * @param buff
* @param len * @param len
*/ */
void TF_WriteImpl(const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{ {
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n"); printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len); dumpFrame(buff, len);
@@ -42,7 +47,8 @@ void TF_WriteImpl(const uint8_t *buff, size_t len)
if (sockfd != -1) { if (sockfd != -1) {
write(sockfd, buff, len); write(sockfd, buff, len);
} else { }
else {
printf("\nNo peer!\n"); printf("\nNo peer!\n");
} }
} }
@@ -91,7 +97,7 @@ static int demo_client(void* unused)
while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0) { while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0) {
printf("\033[36m--- RX %ld bytes ---\033[0m\n", n); printf("\033[36m--- RX %ld bytes ---\033[0m\n", n);
dumpFrame(recvBuff, (size_t) n); dumpFrame(recvBuff, (size_t) n);
TF_Accept(recvBuff, (size_t) n); TF_Accept(demo_tf, recvBuff, (size_t) n);
} }
return 0; return 0;
} }
@@ -143,7 +149,7 @@ static int demo_server(void* unused)
while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0 && !conn_disband) { while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0 && !conn_disband) {
printf("\033[36m--- RX %ld bytes ---\033[0m\n", n); printf("\033[36m--- RX %ld bytes ---\033[0m\n", n);
dumpFrame(recvBuff, n); dumpFrame(recvBuff, n);
TF_Accept(recvBuff, (size_t) n); TF_Accept(demo_tf, recvBuff, (size_t) n);
} }
if (n < 0) { if (n < 0) {
@@ -205,7 +211,8 @@ void demo_init(TF_Peer peer)
// CLONE_FILES --- share stdout and stderr // CLONE_FILES --- share stdout and stderr
if (peer == TF_MASTER) { if (peer == TF_MASTER) {
retc = clone(&demo_client, (char *) stack + 8192, CLONE_VM | CLONE_FILES, 0); retc = clone(&demo_client, (char *) stack + 8192, CLONE_VM | CLONE_FILES, 0);
} else { }
else {
retc = clone(&demo_server, (char *) stack + 8192, CLONE_VM | CLONE_FILES, 0); retc = clone(&demo_server, (char *) stack + 8192, CLONE_VM | CLONE_FILES, 0);
} }
+2
View File
@@ -11,6 +11,8 @@
#define PORT 9798 #define PORT 9798
extern TinyFrame *demo_tf;
/** Sleep and wait for ^C */ /** Sleep and wait for ^C */
void demo_sleep(void); void demo_sleep(void);
+4 -3
View File
@@ -1,11 +1,12 @@
CFILES=../utils.c ../../TinyFrame.c CFILES=../utils.c ../../TinyFrame.c
INCLDIRS=-I. -I.. -I../.. INCLDIRS=-I. -I.. -I../..
CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wextra $(CFILES) $(INCLDIRS) CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wno-unused -Wextra $(CFILES) $(INCLDIRS)
build: test.bin
run: test.bin run: test.bin
./test.bin ./test.bin
build: test.bin
test.bin: test.c $(CFILES) test.bin: test.c $(CFILES)
gcc test.c $(CFLAGS) -o test.bin gcc test.c $(CFLAGS) -o test.bin
+2
View File
@@ -22,4 +22,6 @@ typedef uint8_t TF_COUNT;
#define TF_MAX_GEN_LST 5 #define TF_MAX_GEN_LST 5
#define TF_PARSER_TIMEOUT_TICKS 10 #define TF_PARSER_TIMEOUT_TICKS 10
#define TF_Error(format, ...) printf("[TF] " format "\n", ##__VA_ARGS__)
#endif //TF_CONFIG_H #endif //TF_CONFIG_H
+12 -9
View File
@@ -3,29 +3,32 @@
#include "../../TinyFrame.h" #include "../../TinyFrame.h"
#include "../utils.h" #include "../utils.h"
TinyFrame *demo_tf;
extern const char *romeo; extern const char *romeo;
/** /**
* This function should be defined in the application code. * This function should be defined in the application code.
* It implements the lowest layer - sending bytes to UART (or other) * It implements the lowest layer - sending bytes to UART (or other)
*/ */
void TF_WriteImpl(const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{ {
printf("--------------------\n"); printf("--------------------\n");
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n"); printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len); dumpFrame(buff, len);
// Send it back as if we received it // Send it back as if we received it
TF_Accept(buff, len); TF_Accept(tf, buff, len);
} }
/** An example listener function */ /** An example listener function */
TF_Result myListener(TF_Msg *msg) TF_Result myListener(TinyFrame *tf, TF_Msg *msg)
{ {
dumpFrameInfo(msg); dumpFrameInfo(msg);
if (strcmp(msg->data, romeo) == 0) { if (strcmp((const char *) msg->data, romeo) == 0) {
printf("FILE TRANSFERRED OK!\r\n"); printf("FILE TRANSFERRED OK!\r\n");
} else { }
else {
printf("FAIL!!!!\r\n"); printf("FAIL!!!!\r\n");
} }
return TF_STAY; return TF_STAY;
@@ -36,16 +39,16 @@ void main(void)
TF_Msg msg; TF_Msg msg;
// Set up the TinyFrame library // Set up the TinyFrame library
TF_Init(TF_MASTER); // 1 = master, 0 = slave demo_tf = TF_Init(TF_MASTER); // 1 = master, 0 = slave
TF_AddGenericListener(myListener); TF_AddGenericListener(demo_tf, myListener);
printf("------ Simulate sending a LOOONG message --------\n"); printf("------ Simulate sending a LOOONG message --------\n");
TF_ClearMsg(&msg); TF_ClearMsg(&msg);
msg.type = 0x22; msg.type = 0x22;
msg.data = (pu8) romeo; msg.data = (pu8) romeo;
msg.len = strlen(romeo); msg.len = (TF_LEN) strlen(romeo);
TF_Send(&msg); TF_Send(demo_tf, &msg);
} }
const char *romeo = "THE TRAGEDY OF ROMEO AND JULIET\n" const char *romeo = "THE TRAGEDY OF ROMEO AND JULIET\n"
+1 -1
View File
@@ -1,6 +1,6 @@
CFILES=../utils.c ../../TinyFrame.c CFILES=../utils.c ../../TinyFrame.c
INCLDIRS=-I. -I.. -I../.. INCLDIRS=-I. -I.. -I../..
CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wextra $(CFILES) $(INCLDIRS) CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wno-unused -Wall -Wextra $(CFILES) $(INCLDIRS)
run: test.bin run: test.bin
./test.bin ./test.bin
+2
View File
@@ -22,4 +22,6 @@ typedef uint8_t TF_COUNT;
#define TF_MAX_GEN_LST 5 #define TF_MAX_GEN_LST 5
#define TF_PARSER_TIMEOUT_TICKS 10 #define TF_PARSER_TIMEOUT_TICKS 10
#define TF_Error(format, ...) printf("[TF] " format "\n", ##__VA_ARGS__)
#endif //TF_CONFIG_H #endif //TF_CONFIG_H
+11 -10
View File
@@ -3,29 +3,30 @@
#include "../../TinyFrame.h" #include "../../TinyFrame.h"
#include "../utils.h" #include "../utils.h"
TinyFrame *demo_tf;
/** /**
* This function should be defined in the application code. * This function should be defined in the application code.
* It implements the lowest layer - sending bytes to UART (or other) * It implements the lowest layer - sending bytes to UART (or other)
*/ */
void TF_WriteImpl(const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{ {
printf("--------------------\n"); printf("--------------------\n");
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n"); printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len); dumpFrame(buff, len);
// Send it back as if we received it // Send it back as if we received it
TF_Accept(buff, len); TF_Accept(tf, buff, len);
} }
/** An example listener function */ /** An example listener function */
TF_Result myListener(TF_Msg *msg) TF_Result myListener(TinyFrame *tf, TF_Msg *msg)
{ {
dumpFrameInfo(msg); dumpFrameInfo(msg);
return TF_STAY; return TF_STAY;
} }
TF_Result testIdListener(TF_Msg *msg) TF_Result testIdListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("OK - ID Listener triggered for msg!\n"); printf("OK - ID Listener triggered for msg!\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
@@ -38,8 +39,8 @@ void main(void)
const char *longstr = "Lorem ipsum dolor sit amet."; const char *longstr = "Lorem ipsum dolor sit amet.";
// Set up the TinyFrame library // Set up the TinyFrame library
TF_Init(TF_MASTER); // 1 = master, 0 = slave demo_tf = TF_Init(TF_MASTER); // 1 = master, 0 = slave
TF_AddGenericListener(myListener); TF_AddGenericListener(demo_tf, myListener);
printf("------ Simulate sending a message --------\n"); printf("------ Simulate sending a message --------\n");
@@ -47,19 +48,19 @@ void main(void)
msg.type = 0x22; msg.type = 0x22;
msg.data = (pu8) "Hello TinyFrame"; msg.data = (pu8) "Hello TinyFrame";
msg.len = 16; msg.len = 16;
TF_Send(&msg); TF_Send(demo_tf, &msg);
msg.type = 0x33; msg.type = 0x33;
msg.data = (pu8) longstr; msg.data = (pu8) longstr;
msg.len = (TF_LEN) (strlen(longstr) + 1); // add the null byte msg.len = (TF_LEN) (strlen(longstr) + 1); // add the null byte
TF_Send(&msg); TF_Send(demo_tf, &msg);
msg.type = 0x44; msg.type = 0x44;
msg.data = (pu8) "Hello2"; msg.data = (pu8) "Hello2";
msg.len = 7; msg.len = 7;
TF_Send(&msg); TF_Send(demo_tf, &msg);
msg.len = 0; msg.len = 0;
msg.type = 0x77; msg.type = 0x77;
TF_Query(&msg, testIdListener, 0); TF_Query(demo_tf, &msg, testIdListener, 0);
} }
+1 -1
View File
@@ -1,6 +1,6 @@
CFILES=../demo.c ../utils.c ../../TinyFrame.c CFILES=../demo.c ../utils.c ../../TinyFrame.c
INCLDIRS=-I. -I.. -I../.. INCLDIRS=-I. -I.. -I../..
CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wextra $(CFILES) $(INCLDIRS) CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wno-unused -Wextra $(CFILES) $(INCLDIRS)
build: master.bin slave.bin build: master.bin slave.bin
+2
View File
@@ -22,4 +22,6 @@ typedef uint8_t TF_COUNT;
#define TF_MAX_GEN_LST 5 #define TF_MAX_GEN_LST 5
#define TF_PARSER_TIMEOUT_TICKS 10 #define TF_PARSER_TIMEOUT_TICKS 10
#define TF_Error(format, ...) printf("[TF] " format "\n", ##__VA_ARGS__)
#endif //TF_CONFIG_H #endif //TF_CONFIG_H
+7 -7
View File
@@ -5,14 +5,14 @@
#include <stdio.h> #include <stdio.h>
#include "../demo.h" #include "../demo.h"
TF_Result testIdListener(TF_Msg *msg) TF_Result testIdListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("testIdListener()\n"); printf("testIdListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
return TF_CLOSE; return TF_CLOSE;
} }
TF_Result testGenericListener(TF_Msg *msg) TF_Result testGenericListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("testGenericListener()\n"); printf("testGenericListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
@@ -21,15 +21,15 @@ TF_Result testGenericListener(TF_Msg *msg)
int main(void) int main(void)
{ {
TF_Init(TF_MASTER); demo_tf = TF_Init(TF_MASTER);
TF_AddGenericListener(testGenericListener); TF_AddGenericListener(demo_tf, testGenericListener);
demo_init(TF_MASTER); demo_init(TF_MASTER);
TF_SendSimple(1, (pu8)"Ahoj", 5); TF_SendSimple(demo_tf, 1, (pu8) "Ahoj", 5);
TF_SendSimple(1, (pu8)"Hello", 6); TF_SendSimple(demo_tf, 1, (pu8) "Hello", 6);
TF_QuerySimple(2, (pu8)"Query!", 6, testIdListener, 0, NULL); TF_QuerySimple(demo_tf, 2, (pu8) "Query!", 6, testIdListener, 0);
demo_sleep(); demo_sleep();
} }
+8 -9
View File
@@ -4,38 +4,37 @@
#include <stdio.h> #include <stdio.h>
#include "../demo.h" #include "../demo.h"
#include <memory.h>
TF_Result helloListener(TF_Msg *msg) TF_Result helloListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("helloListener()\n"); printf("helloListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
return TF_STAY; return TF_STAY;
} }
TF_Result replyListener(TF_Msg *msg) TF_Result replyListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("replyListener()\n"); printf("replyListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
msg->data = (const uint8_t *) "response to query"; msg->data = (const uint8_t *) "response to query";
msg->len = (TF_LEN) strlen((const char *) msg->data); msg->len = (TF_LEN) strlen((const char *) msg->data);
TF_Respond(msg); TF_Respond(tf, msg);
// unsolicited reply - will not be handled by the ID listener, which is already gone // unsolicited reply - will not be handled by the ID listener, which is already gone
msg->data = (const uint8_t *) "SPAM"; msg->data = (const uint8_t *) "SPAM";
msg->len = 5; msg->len = 5;
TF_Respond(msg); TF_Respond(tf, msg);
// unrelated message // unrelated message
TF_SendSimple(77, (const uint8_t *) "NAZDAR", 7); TF_SendSimple(tf, 77, (const uint8_t *) "NAZDAR", 7);
return TF_STAY; return TF_STAY;
} }
int main(void) int main(void)
{ {
TF_Init(TF_SLAVE); demo_tf = TF_Init(TF_SLAVE);
TF_AddTypeListener(1, helloListener); TF_AddTypeListener(demo_tf, 1, helloListener);
TF_AddTypeListener(2, replyListener); TF_AddTypeListener(demo_tf, 2, replyListener);
demo_init(TF_SLAVE); demo_init(TF_SLAVE);
demo_sleep(); demo_sleep();
+2 -1
View File
@@ -13,7 +13,8 @@ void dumpFrame(const uint8_t *buff, size_t len)
printf("%3u \033[94m%02X\033[0m", buff[i], buff[i]); printf("%3u \033[94m%02X\033[0m", buff[i], buff[i]);
if (buff[i] >= 0x20 && buff[i] < 127) { if (buff[i] >= 0x20 && buff[i] < 127) {
printf(" %c", buff[i]); printf(" %c", buff[i]);
} else { }
else {
printf(" \033[31m.\033[0m"); printf(" \033[31m.\033[0m");
} }
printf("\n"); printf("\n");
+100
View File
@@ -0,0 +1,100 @@
#include <string.h>
#include "payload_builder.h"
#define pb_check_capacity(pb, needed) \
if ((pb)->current + (needed) > (pb)->end) { \
if ((pb)->full_handler == NULL || !(pb)->full_handler(pb, needed)) (pb)->ok = 0; \
}
/** Write from a buffer */
bool pb_buf(PayloadBuilder *pb, const uint8_t *buf, uint32_t len)
{
pb_check_capacity(pb, len);
if (!pb->ok) return false;
memcpy(pb->current, buf, len);
pb->current += len;
return true;
}
/** Write s zero terminated string */
bool pb_string(PayloadBuilder *pb, const char *str)
{
uint32_t len = (uint32_t) strlen(str);
pb_check_capacity(pb, len+1);
if (!pb->ok) return false;
memcpy(pb->current, str, len+1);
pb->current += len+1;
return true;
}
/** Write uint8_t to the buffer */
bool pb_u8(PayloadBuilder *pb, uint8_t byte)
{
pb_check_capacity(pb, 1);
if (!pb->ok) return false;
*pb->current++ = byte;
return true;
}
/** Write uint16_t to the buffer. */
bool pb_u16(PayloadBuilder *pb, uint16_t word)
{
pb_check_capacity(pb, 2);
if (!pb->ok) return false;
if (pb->bigendian) {
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
*pb->current++ = (uint8_t) (word & 0xFF);
} else {
*pb->current++ = (uint8_t) (word & 0xFF);
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
}
return true;
}
/** Write uint32_t to the buffer. */
bool pb_u32(PayloadBuilder *pb, uint32_t word)
{
pb_check_capacity(pb, 4);
if (!pb->ok) return false;
if (pb->bigendian) {
*pb->current++ = (uint8_t) ((word >> 24) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 16) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
*pb->current++ = (uint8_t) (word & 0xFF);
} else {
*pb->current++ = (uint8_t) (word & 0xFF);
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 16) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 24) & 0xFF);
}
return true;
}
/** Write int8_t to the buffer. */
bool pb_i8(PayloadBuilder *pb, int8_t byte)
{
return pb_u8(pb, ((union conv8){.i8 = byte}).u8);
}
/** Write int16_t to the buffer. */
bool pb_i16(PayloadBuilder *pb, int16_t word)
{
return pb_u16(pb, ((union conv16){.i16 = word}).u16);
}
/** Write int32_t to the buffer. */
bool pb_i32(PayloadBuilder *pb, int32_t word)
{
return pb_u32(pb, ((union conv32){.i32 = word}).u32);
}
/** Write 4-byte float to the buffer. */
bool pb_float(PayloadBuilder *pb, float f)
{
return pb_u32(pb, ((union conv32){.f32 = f}).u32);
}
+110
View File
@@ -0,0 +1,110 @@
#ifndef PAYLOAD_BUILDER_H
#define PAYLOAD_BUILDER_H
/**
* PayloadBuilder, part of the TinyFrame utilities collection
*
* (c) Ondřej Hruška, 2014-2017. MIT license.
*
* The builder supports big and little endian which is selected when
* initializing it or by accessing the bigendian struct field.
*
* This module helps you with building payloads (not only for TinyFrame)
*
* The builder performs bounds checking and calls the provided handler when
* the requested write wouldn't fit. Use the handler to realloc / flush the buffer
* or report an error.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "type_coerce.h"
typedef struct PayloadBuilder_ PayloadBuilder;
/**
* Full buffer handler.
*
* 'needed' more bytes should be written but the end of the buffer was reached.
*
* Return true if the problem was solved (e.g. buffer was flushed and the
* 'current' pointer moved to the beginning).
*
* If false is returned, the 'ok' flag on the struct is set to false
* and all following writes are discarded.
*/
typedef bool (*pb_full_handler)(PayloadBuilder *pb, uint32_t needed);
struct PayloadBuilder_ {
uint8_t *start; //!< Pointer to the beginning of the buffer
uint8_t *current; //!< Pointer to the next byte to be read
uint8_t *end; //!< Pointer to the end of the buffer (start + length)
pb_full_handler full_handler; //!< Callback for buffer overrun
bool bigendian; //!< Flag to use big-endian parsing
bool ok; //!< Indicates that all reads were successful
};
// --- initializer helper macros ---
/** Start the builder. */
#define pb_start_e(buf, capacity, bigendian, full_handler) ((PayloadBuilder){buf, buf, (buf)+(capacity), full_handler, bigendian, 1})
/** Start the builder in big-endian mode */
#define pb_start_be(buf, capacity, full_handler) pb_start_e(buf, capacity, 1, full_handler)
/** Start the builder in little-endian mode */
#define pb_start_le(buf, capacity, full_handler) pb_start_e(buf, capacity, 0, full_handler)
/** Start the parser in little-endian mode (default) */
#define pb_start(buf, capacity, full_handler) pb_start_le(buf, capacity, full_handler)
// --- utilities ---
/** Get already used bytes count */
#define pb_length(pb) ((pb)->current - (pb)->start)
/** Reset the current pointer to start */
#define pb_rewind(pb) do { pb->current = pb->start; } while (0)
/** Write from a buffer */
bool pb_buf(PayloadBuilder *pb, const uint8_t *buf, uint32_t len);
/** Write a zero terminated string */
bool pb_string(PayloadBuilder *pb, const char *str);
/** Write uint8_t to the buffer */
bool pb_u8(PayloadBuilder *pb, uint8_t byte);
/** Write boolean to the buffer. */
static inline bool pb_bool(PayloadBuilder *pb, bool b)
{
return pb_u8(pb, (uint8_t) b);
}
/** Write uint16_t to the buffer. */
bool pb_u16(PayloadBuilder *pb, uint16_t word);
/** Write uint32_t to the buffer. */
bool pb_u32(PayloadBuilder *pb, uint32_t word);
/** Write int8_t to the buffer. */
bool pb_i8(PayloadBuilder *pb, int8_t byte);
/** Write char (int8_t) to the buffer. */
static inline bool pb_char(PayloadBuilder *pb, char c)
{
return pb_i8(pb, c);
}
/** Write int16_t to the buffer. */
bool pb_i16(PayloadBuilder *pb, int16_t word);
/** Write int32_t to the buffer. */
bool pb_i32(PayloadBuilder *pb, int32_t word);
/** Write 4-byte float to the buffer. */
bool pb_float(PayloadBuilder *pb, float f);
#endif // PAYLOAD_BUILDER_H
+121
View File
@@ -0,0 +1,121 @@
#include "payload_parser.h"
#define pp_check_capacity(pp, needed) \
if ((pp)->current + (needed) > (pp)->end) { \
if ((pp)->empty_handler == NULL || !(pp)->empty_handler(pp, needed)) {(pp)->ok = 0;} ; \
}
void pp_skip(PayloadParser *pp, uint32_t num)
{
pp->current += num;
}
uint8_t pp_u8(PayloadParser *pp)
{
pp_check_capacity(pp, 1);
if (!pp->ok) return 0;
return *pp->current++;
}
uint16_t pp_u16(PayloadParser *pp)
{
pp_check_capacity(pp, 2);
if (!pp->ok) return 0;
uint16_t x = 0;
if (pp->bigendian) {
x |= *pp->current++ << 8;
x |= *pp->current++;
} else {
x |= *pp->current++;
x |= *pp->current++ << 8;
}
return x;
}
uint32_t pp_u32(PayloadParser *pp)
{
pp_check_capacity(pp, 4);
if (!pp->ok) return 0;
uint32_t x = 0;
if (pp->bigendian) {
x |= (uint32_t) (*pp->current++ << 24);
x |= (uint32_t) (*pp->current++ << 16);
x |= (uint32_t) (*pp->current++ << 8);
x |= *pp->current++;
} else {
x |= *pp->current++;
x |= (uint32_t) (*pp->current++ << 8);
x |= (uint32_t) (*pp->current++ << 16);
x |= (uint32_t) (*pp->current++ << 24);
}
return x;
}
const uint8_t *pp_tail(PayloadParser *pp, uint32_t *length)
{
int32_t len = (int) (pp->end - pp->current);
if (!pp->ok || len <= 0) {
if (length != NULL) *length = 0;
return NULL;
}
if (length != NULL) {
*length = (uint32_t) len;
}
return pp->current;
}
/** Read int8_t from the payload. */
int8_t pp_i8(PayloadParser *pp)
{
return ((union conv8) {.u8 = pp_u8(pp)}).i8;
}
/** Read int16_t from the payload. */
int16_t pp_i16(PayloadParser *pp)
{
return ((union conv16) {.u16 = pp_u16(pp)}).i16;
}
/** Read int32_t from the payload. */
int32_t pp_i32(PayloadParser *pp)
{
return ((union conv32) {.u32 = pp_u32(pp)}).i32;
}
/** Read 4-byte float from the payload. */
float pp_float(PayloadParser *pp)
{
return ((union conv32) {.u32 = pp_u32(pp)}).f32;
}
/** Read a zstring */
uint32_t pp_string(PayloadParser *pp, char *buffer, uint32_t maxlen)
{
pp_check_capacity(pp, 1);
uint32_t len = 0;
while (len < maxlen-1 && pp->current != pp->end) {
char c = *buffer++ = *pp->current++;
if (c == 0) break;
len++;
}
*buffer = 0;
return len;
}
/** Read a buffer */
uint32_t pp_buf(PayloadParser *pp, uint8_t *buffer, uint32_t maxlen)
{
uint32_t len = 0;
while (len < maxlen && pp->current != pp->end) {
*buffer++ = *pp->current++;
len++;
}
return len;
}
+143
View File
@@ -0,0 +1,143 @@
#ifndef PAYLOAD_PARSER_H
#define PAYLOAD_PARSER_H
/**
* PayloadParser, part of the TinyFrame utilities collection
*
* (c) Ondřej Hruška, 2016-2017. MIT license.
*
* This module helps you with parsing payloads (not only from TinyFrame).
*
* The parser supports big and little-endian which is selected when
* initializing it or by accessing the bigendian struct field.
*
* The parser performs bounds checking and calls the provided handler when
* the requested read doesn't have enough data. Use the callback to take
* appropriate action, e.g. report an error.
*
* If the handler function is not defined, the pb->ok flag is set to false
* (use this to check for success), and further reads won't have any effect
* and always result in 0 or empty array.
*/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "type_coerce.h"
typedef struct PayloadParser_ PayloadParser;
/**
* Empty buffer handler.
*
* 'needed' more bytes should be read but the end was reached.
*
* Return true if the problem was solved (e.g. new data loaded into
* the buffer and the 'current' pointer moved to the beginning).
*
* If false is returned, the 'ok' flag on the struct is set to false
* and all following reads will fail / return 0.
*/
typedef bool (*pp_empty_handler)(PayloadParser *pp, uint32_t needed);
struct PayloadParser_ {
uint8_t *start; //!< Pointer to the beginning of the buffer
uint8_t *current; //!< Pointer to the next byte to be read
uint8_t *end; //!< Pointer to the end of the buffer (start + length)
pp_empty_handler empty_handler; //!< Callback for buffer underrun
bool bigendian; //!< Flag to use big-endian parsing
bool ok; //!< Indicates that all reads were successful
};
// --- initializer helper macros ---
/** Start the parser. */
#define pp_start_e(buf, length, bigendian, empty_handler) ((PayloadParser){buf, buf, (buf)+(length), empty_handler, bigendian, 1})
/** Start the parser in big-endian mode */
#define pp_start_be(buf, length, empty_handler) pp_start_e(buf, length, 1, empty_handler)
/** Start the parser in little-endian mode */
#define pp_start_le(buf, length, empty_handler) pp_start_e(buf, length, 0, empty_handler)
/** Start the parser in little-endian mode (default) */
#define pp_start(buf, length, empty_handler) pp_start_le(buf, length, empty_handler)
// --- utilities ---
/** Get remaining length */
#define pp_length(pp) ((pp)->end - (pp)->current)
/** Reset the current pointer to start */
#define pp_rewind(pp) do { pp->current = pp->start; } while (0)
/**
* @brief Get the remainder of the buffer.
*
* Returns NULL and sets 'length' to 0 if there are no bytes left.
*
* @param pp
* @param length : here the buffer length will be stored. NULL to do not store.
* @return the remaining portion of the input buffer
*/
const uint8_t *pp_tail(PayloadParser *pp, uint32_t *length);
/** Read uint8_t from the payload. */
uint8_t pp_u8(PayloadParser *pp);
/** Read bool from the payload. */
static inline int8_t pp_bool(PayloadParser *pp)
{
return pp_u8(pp) != 0;
}
/** Skip bytes */
void pp_skip(PayloadParser *pp, uint32_t num);
/** Read uint16_t from the payload. */
uint16_t pp_u16(PayloadParser *pp);
/** Read uint32_t from the payload. */
uint32_t pp_u32(PayloadParser *pp);
/** Read int8_t from the payload. */
int8_t pp_i8(PayloadParser *pp);
/** Read char (int8_t) from the payload. */
static inline int8_t pp_char(PayloadParser *pp)
{
return pp_i8(pp);
}
/** Read int16_t from the payload. */
int16_t pp_i16(PayloadParser *pp);
/** Read int32_t from the payload. */
int32_t pp_i32(PayloadParser *pp);
/** Read 4-byte float from the payload. */
float pp_float(PayloadParser *pp);
/**
* Parse a zero-terminated string
*
* @param pp - parser
* @param buffer - target buffer
* @param maxlen - buffer size
* @return actual number of bytes, excluding terminator
*/
uint32_t pp_string(PayloadParser *pp, char *buffer, uint32_t maxlen);
/**
* Parse a buffer
*
* @param pp - parser
* @param buffer - target buffer
* @param maxlen - buffer size
* @return actual number of bytes, excluding terminator
*/
uint32_t pp_buf(PayloadParser *pp, uint8_t *buffer, uint32_t maxlen);
#endif // PAYLOAD_PARSER_H
+32
View File
@@ -0,0 +1,32 @@
#ifndef TYPE_COERCE_H
#define TYPE_COERCE_H
/**
* Structs for conversion between types,
* part of the TinyFrame utilities collection
*
* (c) Ondřej Hruška, 2016-2017. MIT license.
*
* This is a support header file for PayloadParser and PayloadBuilder.
*/
#include <stdint.h>
#include <stddef.h>
union conv8 {
uint8_t u8;
int8_t i8;
};
union conv16 {
uint16_t u16;
int16_t i16;
};
union conv32 {
uint32_t u32;
int32_t i32;
float f32;
};
#endif // TYPE_COERCE_H