forked from electro/esp-irblaster
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
4.8 KiB
167 lines
4.8 KiB
#ifndef PAYLOAD_PARSER_H
|
|
#define PAYLOAD_PARSER_H
|
|
|
|
/**
|
|
* PayloadParser, part of the TinyFrame utilities collection
|
|
*
|
|
* (c) Ondřej Hruška, 2016-2022. 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_ {
|
|
const uint8_t *start; //!< Pointer to the beginning of the buffer
|
|
const uint8_t *current; //!< Pointer to the next byte to be read
|
|
const 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_remains(pp) ((pp)->end - (pp)->current)
|
|
|
|
/** Reset the current pointer to start */
|
|
#define pp_rewind(pp) do { (pp)->current = (pp)->start; } while (0)
|
|
|
|
/** save & restore position */
|
|
typedef const uint8_t* pp_mark_t;
|
|
#define pp_save(pp) ((pp)->current)
|
|
#define pp_restore(pp, mark) do { (pp)->current = (mark); } while (0)
|
|
|
|
/** Returns true if the parser is still in OK state (not overrun) */
|
|
#define pp_ok(pp) ((pp)->ok)
|
|
/** Returns true if end was reached */
|
|
#define pp_end(pp) ((pp)->end == (pp)->current)
|
|
|
|
/**
|
|
* @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 bool pp_bool(PayloadParser *pp)
|
|
{
|
|
return pp_u8(pp) != 0;
|
|
}
|
|
|
|
/** Skip bytes */
|
|
static inline void pp_skip(PayloadParser *pp, uint32_t num)
|
|
{
|
|
pp->current += 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. */
|
|
static inline int8_t pp_i8(PayloadParser *pp)
|
|
{
|
|
return ((union conv8) {.u8 = pp_u8(pp)}).i8;
|
|
}
|
|
|
|
/** 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. */
|
|
static inline int16_t pp_i16(PayloadParser *pp)
|
|
{
|
|
return ((union conv16) {.u16 = pp_u16(pp)}).i16;
|
|
}
|
|
|
|
/** Read int32_t from the payload. */
|
|
static inline int32_t pp_i32(PayloadParser *pp)
|
|
{
|
|
return ((union conv32) {.u32 = pp_u32(pp)}).i32;
|
|
}
|
|
|
|
/** Read 4-byte float from the payload. */
|
|
static inline float pp_float(PayloadParser *pp)
|
|
{
|
|
return ((union conv32) {.u32 = pp_u32(pp)}).f32;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
|