diff --git a/sys/include/tsrb.h b/sys/include/tsrb.h new file mode 100644 index 000000000000..989acbbe3123 --- /dev/null +++ b/sys/include/tsrb.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2015 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_tsrb Thread safe ringbuffer + * @ingroup sys + * @{ + */ + +/** + * @file + * @brief Thread-safe ringbuffer implementation + * + * This ringbuffer implementation can be used without locking if + * there's only one producer and one consumer. + * + * @note Buffer size must be a power of two! + * + * @author Kaspar Schleiser + */ + +#ifndef TSRB_H +#define TSRB_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief thread-safe ringbuffer struct + */ +typedef struct tsrb { + char *buf; /**< Buffer to operate on. */ + unsigned int size; /**< Size of buf. */ + volatile unsigned reads; /**< total number of reads */ + volatile unsigned writes; /**< total number of writes */ +} tsrb_t; + +/** + * @brief Static initializer + */ +#define TSRB_INIT(BUF) { (BUF), sizeof (BUF), 0, 0 } + +/** + * @brief Initialize a tsrb. + * @param[out] rb Datum to initialize. + * @param[in] buffer Buffer to use by tsrb. + * @param[in] bufsize `sizeof (buffer)` + */ +static inline void tsrb_init(tsrb_t *rb, char *buffer, unsigned bufsize) +{ + /* make sure bufsize is a power of two. + * http://www.exploringbinary.com/ten-ways-to-check-if-an-integer-is-a-power-of-two-in-c/ + */ + assert((bufsize != 0) && ((bufsize & (~bufsize + 1)) == bufsize)); + + rb->buf = buffer; + rb->size = bufsize; + rb->reads = 0; + rb->writes = 0; +} + +/** + * @brief Test if the tsrb is empty. + * @param[in] rb Ringbuffer to operate on + * @return 0 if not empty + * @return 1 otherwise + */ +static inline int tsrb_empty(const tsrb_t *rb) +{ + return (rb->reads == rb->writes); +} + + +/** + * @brief Get number of bytes available for reading + * @param[in] rb Ringbuffer to operate on + * @return nr of available bytes + */ +static inline unsigned int tsrb_avail(const tsrb_t *rb) +{ + return (rb->writes - rb->reads); +} + +/** + * @brief Test if the tsrb is full + * @param[in] rb Ringbuffer to operate on + * @return 0 if not full + * @return 1 otherwise + */ +static inline int tsrb_full(const tsrb_t *rb) +{ + return (rb->writes - rb->reads) == rb->size; +} + +/** + * @brief Get free space in ringbuffer + * @param[in] rb Ringbuffer to operate on + * @return nr of available bytes + */ +static inline unsigned int tsrb_free(const tsrb_t *rb) +{ + return (rb->size - rb->writes + rb->reads); +} + +/** + * @brief Get a byte from ringbuffer + * @param[in] rb Ringbuffer to operate on + * @return >=0 byte that has been read + * @return -1 if no byte available + */ +int tsrb_get_one(tsrb_t *rb); + +/** + * @brief Get bytes from ringbuffer + * @param[in] rb Ringbuffer to operate on + * @param[out] dst buffer to write to + * @param[in] n max number of bytes to write to @p dst + * @return nr of bytes written to @p dst + */ +int tsrb_get(tsrb_t *rb, char *dst, size_t n); + +/** + * @brief Add a byte to ringbuffer + * @param[in] rb Ringbuffer to operate on + * @param[in] c Character to add to ringbuffer + * @return 0 on success + * @return -1 if no space available + */ +int tsrb_add_one(tsrb_t *rb, char c); + +/** + * @brief Add bytes to ringbuffer + * @param[in] rb Ringbuffer to operate on + * @param[in] src buffer to read from + * @param[in] n max number of bytes to read from @p src + * @return nr of bytes read from @p src + */ +int tsrb_add(tsrb_t *rb, const char *src, size_t n); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* TSRB_H */ diff --git a/sys/tsrb/Makefile b/sys/tsrb/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/sys/tsrb/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/tsrb/tsrb.c b/sys/tsrb/tsrb.c new file mode 100644 index 000000000000..334550fa4af6 --- /dev/null +++ b/sys/tsrb/tsrb.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys + * @{ + * @file + * @brief thread-safe ringbuffer implementation + * + * @author Kaspar Schleiser + * + * @} + */ + +#include "tsrb.h" + +static void _push(tsrb_t *rb, char c) +{ + rb->buf[rb->writes++ & (rb->size - 1)] = c; +} + +static char _pop(tsrb_t *rb) +{ + return rb->buf[rb->reads++ & (rb->size - 1)]; +} + +int tsrb_get_one(tsrb_t *rb) +{ + if (!tsrb_empty(rb)) { + return _pop(rb); + } + else { + return -1; + } +} + +int tsrb_get(tsrb_t *rb, char *dst, size_t n) +{ + size_t tmp = n; + while (tmp && !tsrb_empty(rb)) { + *dst++ = _pop(rb); + tmp--; + } + return (n - tmp); +} + +int tsrb_add_one(tsrb_t *rb, char c) +{ + if (!tsrb_full(rb)) { + _push(rb, c); + return 0; + } + else { + return -1; + } +} + +int tsrb_add(tsrb_t *rb, const char *src, size_t n) +{ + size_t tmp = n; + while (tmp && !tsrb_full(rb)) { + _push(rb, *src++); + tmp--; + } + return (n - tmp); +}