Skip to content

Commit

Permalink
encoder: Add polymorphic stream interface
Browse files Browse the repository at this point in the history
  • Loading branch information
bergzand committed Oct 13, 2023
1 parent 0261bc5 commit de08b00
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 22 deletions.
72 changes: 67 additions & 5 deletions include/nanocbor/nanocbor.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,65 @@ typedef struct nanocbor_value {
uint8_t flags; /**< Flags for decoding hints */
} nanocbor_value_t;

/**
* @brief Encoder context forward declaration
*/
typedef struct nanocbor_encoder nanocbor_encoder_t;

/**
* @name encoder streaming functions
* @{
*/

/**
* @brief Fits function for streaming encoder data
*
* This function is called to check whether the data that will be supplied can
* be consumed by the interface.
*
* The append function will only be called if this function returns true. The
* amount of bytes checked by this call can be used by multiple successive
* @ref nanocbor_encoder_append calls
*
* @param enc The encoder context struct
* @param ctx The context ptr supplied in the
* @ref nanocbor_encoder_stream_init call
* @param len Length of the data in bytes that will be supplied
*/
typedef bool (*nanocbor_encoder_fits)(nanocbor_encoder_t *enc, void *ctx, size_t len);

/**
* @brief Append function for streaming encoder data
*
* This is a user supplied function for the polymorphic encoder interface. It
* will only be called if a previous call to @ref nanocbor_encoder_fits returned
* true.
*
* @param enc The encoder context struct
* @param ctx The context ptr supplied in the
* @ref nanocbor_encoder_stream_init call
* @param data Data emitted by the encoder
* @param len Length of the data in bytes
*/
typedef void (*nanocbor_encoder_append)(nanocbor_encoder_t *enc, void *ctx, const uint8_t *data, size_t len);

/** @} */

/**
* @brief encoder context
*/
typedef struct nanocbor_encoder {
uint8_t *cur; /**< Current position in the buffer */
uint8_t *end; /**< end of the buffer */
struct nanocbor_encoder {
size_t len; /**< Length in bytes of supplied cbor data. Incremented
* separate from the buffer check */
} nanocbor_encoder_t;
nanocbor_encoder_append append; /**< Function used to append encoded data */
nanocbor_encoder_fits fits; /** Function used to check encoded length */
union {
uint8_t *cur; /**< Current position in the buffer, unioned to keep
* compatibility */
void *context; /**< Context ptr supplied to the custom functions */
};
uint8_t *end; /**< end of the buffer */
};

/**
* @name decoder flags
Expand Down Expand Up @@ -621,7 +671,7 @@ static inline bool nanocbor_in_container(const nanocbor_value_t *container)
*/

/**
* @brief Initializes an encoder context with a buffer.
* @brief Initializes an encoder context with a memory buffer.
*
* It is safe to pass `NULL` to @p buf with @p len is `0` to determine the size
* of a CBOR structure.
Expand All @@ -632,6 +682,18 @@ static inline bool nanocbor_in_container(const nanocbor_value_t *container)
*/
void nanocbor_encoder_init(nanocbor_encoder_t *enc, uint8_t *buf, size_t len);

/**
* @brief Initializes an encoder context with custom append and fits functions.
*
* @param[in] enc Encoder context
* @param[in] ctx Context pointer
* @param[in] append_func Called to append emitted encoder data
* @param[in] fits_func Called to check if data can be consumed
*/
void nanocbor_encoder_stream_init(nanocbor_encoder_t *enc, void *ctx,
nanocbor_encoder_append append_func,
nanocbor_encoder_fits fits_func);

/**
* @brief Retrieve the encoded length of the CBOR structure
*
Expand Down
77 changes: 60 additions & 17 deletions src/encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,67 @@

#include NANOCBOR_BYTEORDER_HEADER

/* memarray functions */
static bool _encoder_mem_fits(nanocbor_encoder_t *enc, void *ctx, size_t len)
{
(void)ctx;
return ((size_t)(enc->end - enc->cur) >= len);
}

static void _encoder_mem_append(nanocbor_encoder_t *enc, void *ctx, const uint8_t *data, size_t len)
{
(void)ctx;
memcpy(enc->cur, data, len);
enc->cur += len;
}

void nanocbor_encoder_init(nanocbor_encoder_t *enc, uint8_t *buf, size_t len)
{
enc->len = 0;
enc->cur = buf;
enc->end = buf + len;
enc->append = _encoder_mem_append;
enc->fits = _encoder_mem_fits;
}

void nanocbor_encoder_stream_init(nanocbor_encoder_t *enc, void *ctx,
nanocbor_encoder_append append_func,
nanocbor_encoder_fits fits_func)
{
enc->len = 0;
enc->append = append_func;
enc->fits = fits_func;
enc->context = ctx;
}

size_t nanocbor_encoded_len(nanocbor_encoder_t *enc)
{
return enc->len;
}

static int _fits(nanocbor_encoder_t *enc, size_t len)
static inline void _incr_len(nanocbor_encoder_t *enc, size_t len)
{
enc->len += len;
return ((size_t)(enc->end - enc->cur) >= len) ? (int)len : NANOCBOR_ERR_END;
}

static inline void _append(nanocbor_encoder_t *enc, const uint8_t *data, size_t len)
{
enc->append(enc, enc->context, data, len);
}

static inline int _fits(nanocbor_encoder_t *enc, size_t len)
{
return enc->fits(enc, enc->context, len) ? (int)len : NANOCBOR_ERR_END;
}

static int _fmt_single(nanocbor_encoder_t *enc, uint8_t single)
{
_incr_len(enc, 1);
int res = _fits(enc, 1);

if (res == 1) {
*enc->cur++ = single;
_append(enc, &single, 1);

}
return res;
}
Expand Down Expand Up @@ -85,16 +122,17 @@ static int _fmt_uint64(nanocbor_encoder_t *enc, uint64_t num, uint8_t type)
extrabytes = sizeof(uint8_t);
}
}
_incr_len(enc, extrabytes + 1);
int res = _fits(enc, extrabytes + 1);
if (res > 0) {
*enc->cur++ = type;
_append(enc, &type, 1);

/* NOLINTNEXTLINE: user supplied function */
uint64_t benum = NANOCBOR_HTOBE64_FUNC(num);

memcpy(enc->cur, (uint8_t *)&benum + sizeof(benum) - extrabytes,

_append(enc, (uint8_t *)&benum + sizeof(benum) - extrabytes,
extrabytes);
enc->cur += extrabytes;
}
return res;
}
Expand Down Expand Up @@ -131,11 +169,11 @@ int nanocbor_fmt_tstr(nanocbor_encoder_t *enc, size_t len)

static int _put_bytes(nanocbor_encoder_t *enc, const uint8_t *str, size_t len)
{
_incr_len(enc, len);
int res = _fits(enc, len);

if (res >= 0) {
memcpy(enc->cur, str, len);
enc->cur += len;
_append(enc, str, len);
return NANOCBOR_OK;
}
return res;
Expand Down Expand Up @@ -251,11 +289,14 @@ static bool _single_in_range(uint8_t exp, uint32_t num)

static int _fmt_halffloat(nanocbor_encoder_t *enc, uint16_t half)
{
_incr_len(enc, sizeof(uint16_t) + 1);
int res = _fits(enc, sizeof(uint16_t) + 1);
if (res > 0) {
*enc->cur++ = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_SHORT;
*enc->cur++ = (half >> HALF_SIZE / 2);
*enc->cur++ = half & HALF_MASK_HALF;
uint8_t tmp[3] = {
NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_SHORT,
(half >> HALF_SIZE / 2),
half & HALF_MASK_HALF };
_append(enc, tmp, sizeof(tmp));
res = sizeof(uint16_t) + 1;
}
return res;
Expand Down Expand Up @@ -307,13 +348,14 @@ int nanocbor_fmt_float(nanocbor_encoder_t *enc, float num)
return _fmt_halffloat(enc, half);
}
/* normal float */
_incr_len(enc, sizeof(float) + 1);
int res = _fits(enc, 1 + sizeof(float));
if (res > 0) {
*enc->cur++ = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_WORD;
const uint8_t tmp = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_WORD;
_append(enc, &tmp, sizeof(tmp));
/* NOLINTNEXTLINE: user supplied function */
uint32_t bnum = NANOCBOR_HTOBE32_FUNC(*unum);
memcpy(enc->cur, &bnum, sizeof(bnum));
enc->cur += sizeof(float);
_append(enc, (uint8_t*)&bnum, sizeof(bnum));
}
return res;
}
Expand All @@ -339,13 +381,14 @@ int nanocbor_fmt_double(nanocbor_encoder_t *enc, double num)
float *fsingle = (float *)&single;
return nanocbor_fmt_float(enc, *fsingle);
}
_incr_len(enc, sizeof(double) + 1);
int res = _fits(enc, 1 + sizeof(double));
if (res > 0) {
*enc->cur++ = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_LONG;
const uint8_t tmp = NANOCBOR_MASK_FLOAT | NANOCBOR_SIZE_LONG;
_append(enc, &tmp, 1);
/* NOLINTNEXTLINE: user supplied function */
uint64_t bnum = NANOCBOR_HTOBE64_FUNC(*unum);
memcpy(enc->cur, &bnum, sizeof(bnum));
enc->cur += sizeof(double);
_append(enc, (uint8_t*)&bnum, sizeof(bnum));
}
return res;
#endif
Expand Down

0 comments on commit de08b00

Please sign in to comment.