lib: ring_buffer: add raw byte access mode

Extended ring buffer to allow storing raw bytes in it. API has been
extended keeping 'data item' mode untouched.

Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
Krzysztof Chruscinski 2018-09-07 15:30:56 +02:00 committed by Anas Nashif
commit 26031f7bfd
5 changed files with 675 additions and 66 deletions

View file

@ -3,11 +3,15 @@
Ring Buffers
############
A :dfn:`ring buffer` is a circular buffer of 32-bit words, whose contents
are stored in first-in-first-out order. Data items can be enqueued and dequeued
from a ring buffer in chunks of up to 1020 bytes. Each data item also has
two associated metadata values: a type identifier and a 16-bit integer value,
both of which are application-specific.
A :dfn:`ring buffer` is a circular buffer, whose contents are stored in
first-in-first-out order. Two content data modes are supported:
* **Data item mode**: Multiple 32-bit word data items with metadata
can be enqueued and dequeued from the ring buffer in
chunks of up to 1020 bytes. Each data item also has two
associated metadata values: a type identifier and a 16-bit
integer value, both of which are application-specific.
* **Byte mode**: raw bytes can be enqueued and dequeued.
.. contents::
:local:
@ -21,27 +25,67 @@ by its memory address.
A ring buffer has the following key properties:
* A **data buffer** of 32-bit words. The data buffer contains the data items
that have been added to the ring buffer but not yet removed.
* A **data buffer** of 32-bit words or bytes. The data buffer contains the data
items or raw bytes that have been added to the ring buffer but not yet
removed.
* A **data buffer size**, measured in 32-bit words. This governs the maximum
amount of data (including metadata values) the ring buffer can hold.
* A **data buffer size**, measured in 32-bit words or bytes. This governs the
maximum amount of data (including metadata values) the ring buffer can hold.
A ring buffer must be initialized before it can be used. This sets its
data buffer to empty.
A ring buffer **data item** is an array of 32-bit words from 0 to 1020 bytes
in length. When a data item is **enqueued** its contents are copied
to the data buffer, along with its associated metadata values (which occupy
one additional 32-bit word).
If the ring buffer has insufficient space to hold the new data item
the enqueue operation fails.
Data item mode
==============
A data items is **dequeued** from a ring buffer by removing the oldest
enqueued item. The contents of the dequeued data item, as well as its
two metadata values, are copied to areas supplied by the retriever.
If the ring buffer is empty, or if the data array supplied by the retriever
is not large enough to hold the data item's data, the dequeue operation fails.
A **data item mode** ring buffer instance is declared using
:cpp:func:`RING_BUF_ITEM_DECLARE_POW2()` or
:cpp:func:`RING_BUF_ITEM_DECLARE_SIZE()` and accessed using
:cpp:func:`ring_buf_item_put()` and :cpp:func:`ring_buf_item_get()`.
A ring buffer **data item** is an array of 32-bit words from 0 to 1020 bytes
in length. When a data item is **enqueued** (:cpp:func:`ring_buf_item_put()`)
its contents are copied to the data buffer, along with its associated metadata
values (which occupy one additional 32-bit word). If the ring buffer has
insufficient space to hold the new data item the enqueue operation fails.
A data items is **dequeued** (:cpp:func:`ring_buf_item_get()`) from a ring
buffer by removing the oldest enqueued item. The contents of the dequeued data
item, as well as its two metadata values, are copied to areas supplied by the
retriever. If the ring buffer is empty, or if the data array supplied by the
retriever is not large enough to hold the data item's data, the dequeue
operation fails.
Byte mode
=========
A **byte mode** ring buffer instance is declared using
:cpp:func:`RING_BUF_DECLARE_SIZE()` and accessed using:
:cpp:func:`ring_buf_put_claim()`, :cpp:func:`ring_buf_put_finish()`,
:cpp:func:`ring_buf_get_claim()`, :cpp:func:`ring_buf_get_finish()`,
:cpp:func:`ring_buf_put()` and :cpp:func:`ring_buf_get()`.
Data can be copied into the ring buffer (see
:cpp:func:`ring_buf_put()`) or ring buffer memory can be used
directly by the user. In the latter case, the operation is split into three stages:
1. allocating the buffer (:cpp:func:`ring_buf_put_claim()`) when
user requests the destination location where data can be written.
#. writing the data by the user (e.g. buffer written by DMA).
#. indicating the amount of data written to the provided buffer
(:cpp:func:`ring_buf_put_finish()`). The amount
can be less than or equal to the allocated amount.
Data can be retrieved from a ring buffer through copying
(see :cpp:func:`ring_buf_get()`) or accessed directly by address. In the latter
case, the operation is split
into three stages:
1. retrieving source location with valid data written to a ring buffer
(see :cpp:func:`ring_buf_get_claim()`).
#. processing data
#. freeing processed data (see :cpp:func:`ring_buf_get_finish()`).
The amount freed can be less than or equal or to the retrieved amount.
Concurrency
===========
@ -58,12 +102,13 @@ shouldn't be needed.
Internal Operation
==================
The ring buffer always maintains an empty 32-bit word in its data buffer
to allow it to distinguish between empty and full states.
The ring buffer always maintains an empty 32-bit word (byte in bytes mode) in
its data buffer to allow it to distinguish between empty and full states.
If the size of the data buffer is a power of two, the ring buffer
uses efficient masking operations instead of expensive modulo operations
when enqueuing and dequeuing data items.
when enqueuing and dequeuing data items. This option is applicable only for
data item mode.
Implementation
**************
@ -74,8 +119,8 @@ Defining a Ring Buffer
A ring buffer is defined using a variable of type :c:type:`struct ring_buf`.
It must then be initialized by calling :cpp:func:`ring_buf_init()`.
The following code defines and initializes an empty ring buffer
(which is part of a larger data structure). The ring buffer's data buffer
The following code defines and initializes an empty **data item mode** ring
buffer (which is part of a larger data structure). The ring buffer's data buffer
is capable of holding 64 words of data and metadata information.
.. code-block:: c
@ -106,14 +151,23 @@ which can be accessed using efficient masking operations.
/* Buffer with 2^8 (or 256) words */
RING_BUF_ITEM_DECLARE_POW2(my_ring_buf, 8);
The following code defines a ring buffer with an arbitrary-sized data buffer,
which can be accessed using less efficient modulo operations.
The following code defines an application-specific sized **byte mode** ring
buffer enqueued and dequeued as raw bytes:
.. code-block:: c
#define MY_RING_BUF_WORDS 93
RING_BUF_ITEM_DECLARE_SIZE(my_ring_buf, MY_RING_BUF_WORDS);
The following code defines a ring buffer with an arbitrary-sized data buffer,
which can be accessed using less efficient modulo operations. Ring buffer is
intended to be used for raw bytes.
.. code-block:: c
#define MY_RING_BUF_BYTES 93
RING_BUF_DECLARE_SIZE(my_ring_buf, MY_RING_BUF_BYTES);
Enqueuing Data
==============
@ -145,6 +199,43 @@ can be specified.
...
}
Bytes are copied to a **byte mode** ring buffer by calling
:cpp:func:`ring_buf_put()`.
.. code-block:: c
u8_t my_data[MY_RING_BUF_BYTES];
u32_t ret;
ret = ring_buf_put(&ring_buf, my_data, SIZE_OF(my_data));
if (ret != SIZE_OF(my_data)) {
/* not enough room, partial copy. */
...
}
Data can be added to a **byte mode** ring buffer by directly accessing the
ring buffer's memory. For example:
.. code-block:: c
u32_t size;
u32_t rx_size;
u8_t *data;
int err;
/* Allocate buffer within a ring buffer memory. */
size = ring_buf_put_claim(&ring_buf, &data, MY_RING_BUF_BYTES);
/* Work directly on a ring buffer memory. */
rx_size = uart_rx(data, size);
/* Indicate amount of valid data. rx_size can be equal or less than size. */
err = ring_buf_put_finish(&ring_buf, rx_size);
if (err != 0) {
/* No space to put requested amount of data to ring buffer. */
...
}
Retrieving Data
===============
@ -171,6 +262,47 @@ A data item is removed from a ring buffer by calling
...
}
Data bytes are copied out from a **byte mode** ring buffer by calling
:cpp:func:`ring_buf_get()`. For example:
.. code-block:: c
u8_t my_data[MY_DATA_BYTES];
size_t ret;
ret = ring_buf_get(&ring_buf, my_data, sizeof(my_data));
if (ret != sizeof(my_size)) {
/* Less bytes copied. */
} else {
/* Requested amount of bytes retrieved. */
...
}
Data can be retrieved from a **byte mode** ring buffer by direct
operations on the ring buffer's memory. For example:
.. code-block:: c
u32_t size;
u32_t proc_size;
u8_t *data;
int err;
/* Get buffer within a ring buffer memory. */
size = ring_buf_get_claim(&ring_buf, &data, MY_RING_BUF_BYTES);
/* Work directly on a ring buffer memory. */
proc_size = process(data, size);
/* Indicate amount of data that can be freed. proc_size can be equal or less
* than size.
*/
err = ring_buf_get_finish(&ring_buf, proc_size);
if (err != 0) {
/* proc_size exceeds amount of valid data in a ring buffer. */
...
}
APIs
****
@ -178,8 +310,15 @@ The following ring buffer APIs are provided by :file:`include/ring_buffer.h`:
* :cpp:func:`RING_BUF_ITEM_DECLARE_POW2()`
* :cpp:func:`RING_BUF_ITEM_DECLARE_SIZE()`
* :cpp:func:`RING_BUF_DECLARE_SIZE()`
* :cpp:func:`ring_buf_init()`
* :cpp:func:`ring_buf_is_empty()`
* :cpp:func:`ring_buf_space_get()`
* :cpp:func:`ring_buf_item_put()`
* :cpp:func:`ring_buf_item_get()`
* :cpp:func:`ring_buf_put()`
* :cpp:func:`ring_buf_put_claim()`
* :cpp:func:`ring_buf_put_finish()`
* :cpp:func:`ring_buf_get()`
* :cpp:func:`ring_buf_get_claim()`
* :cpp:func:`ring_buf_get_finish()`

View file

@ -13,6 +13,7 @@
#include <kernel.h>
#include <misc/util.h>
#include <errno.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
@ -26,11 +27,24 @@ extern "C" {
struct ring_buf {
u32_t head; /**< Index in buf for the head element */
u32_t tail; /**< Index in buf for the tail element */
u32_t dropped_put_count; /**< Running tally of the number of failed
* put attempts
*/
union ring_buf_misc {
struct ring_buf_misc_item_mode {
u32_t dropped_put_count; /**< Running tally of the
* number of failed put
* attempts.
*/
} item_mode;
struct ring_buf_misc_byte_mode {
u32_t tmp_tail;
u32_t tmp_head;
} byte_mode;
} misc;
u32_t size; /**< Size of buf in 32-bit chunks */
u32_t *buf; /**< Memory region for stored entries */
union ring_buf_buffer {
u32_t *buf32; /**< Memory region for stored entries */
u8_t *buf8;
} buf;
u32_t mask; /**< Modulo mask if size is a power of 2 */
};
@ -61,8 +75,8 @@ struct ring_buf {
struct ring_buf name = { \
.size = (1 << (pow)), \
.mask = (1 << (pow)) - 1, \
.buf = _ring_buffer_data_##name \
};
.buf = { .buf32 = _ring_buffer_data_##name } \
}
/** @deprecated Renamed to RING_BUF_ITEM_DECLARE_POW2. */
#define SYS_RING_BUF_DECLARE_POW2(name, pow) \
@ -86,35 +100,55 @@ struct ring_buf {
static u32_t _ring_buffer_data_##name[size32]; \
struct ring_buf name = { \
.size = size32, \
.buf = _ring_buffer_data_##name \
};
.buf = { .buf32 = _ring_buffer_data_##name} \
}
/** @deprecated Renamed to RING_BUF_ITEM_DECLARE_SIZE. */
#define SYS_RING_BUF_DECLARE_SIZE(name, size32) \
__DEPRECATED_MACRO RING_BUF_ITEM_DECLARE_SIZE(name, size32)
/**
* @brief Statically define and initialize a ring buffer for byte data.
*
* This macro establishes a ring buffer of an arbitrary size.
*
* The ring buffer can be accessed outside the module where it is defined
* using:
*
* @code extern struct ring_buf <name>; @endcode
*
* @param name Name of the ring buffer.
* @param size8 Size of ring buffer (in bytes).
*/
#define RING_BUF_DECLARE(name, size8) \
static u8_t _ring_buffer_data_##name[size8]; \
struct ring_buf name = { \
.size = size8, \
.buf = { .buf8 = _ring_buffer_data_##name} \
}
/**
* @brief Initialize a ring buffer.
*
* This routine initializes a ring buffer, prior to its first use. It is only
* used for ring buffers not defined using RING_BUF_ITEM_DECLARE_POW2 or
* RING_BUF_ITEM_DECLARE_SIZE.
* used for ring buffers not defined using RING_BUF_DECLARE,
* RING_BUF_ITEM_DECLARE_POW2 or RING_BUF_ITEM_DECLARE_SIZE.
*
* Setting @a size to a power of 2 establishes a high performance ring buffer
* that doesn't require the use of modulo arithmetic operations to maintain
* itself.
*
* @param buf Address of ring buffer.
* @param size Ring buffer size (in 32-bit words).
* @param data Ring buffer data area (typically u32_t data[size]).
* @param size Ring buffer size (in 32-bit words or bytes).
* @param data Ring buffer data area (u32_t data[size] or u8_t data[size] for
* bytes mode).
*/
static inline void ring_buf_init(struct ring_buf *buf, u32_t size, u32_t *data)
static inline void ring_buf_init(struct ring_buf *buf, u32_t size, void *data)
{
buf->head = 0;
buf->tail = 0;
buf->dropped_put_count = 0;
memset(buf, 0, sizeof(struct ring_buf));
buf->size = size;
buf->buf = data;
buf->buf.buf32 = data;
if (is_power_of_two(size)) {
buf->mask = size - 1;
} else {
@ -129,6 +163,27 @@ __deprecated static inline void sys_ring_buf_init(struct ring_buf *buf,
ring_buf_init(buf, size, data);
}
/** @brief Determine free space based on ring buffer parameters.
*
* @note Function for internal use.
*
* @param size Ring buffer size.
* @param head Ring buffer head.
* @param tail Ring buffer tail.
*
* @return Ring buffer free space (in 32-bit words or bytes).
*/
static inline int z_ring_buf_custom_space_get(u32_t size, u32_t head,
u32_t tail)
{
if (tail < head) {
return head - tail - 1;
}
/* buf->tail > buf->head */
return (size - tail) + head - 1;
}
/**
* @brief Determine if a ring buffer is empty.
*
@ -140,7 +195,6 @@ static inline int ring_buf_is_empty(struct ring_buf *buf)
{
return (buf->head == buf->tail);
}
/** @deprecated Renamed to ring_buf_is_empty. */
__deprecated static inline int sys_ring_buf_is_empty(struct ring_buf *buf)
{
@ -152,20 +206,11 @@ __deprecated static inline int sys_ring_buf_is_empty(struct ring_buf *buf)
*
* @param buf Address of ring buffer.
*
* @return Ring buffer free space (in 32-bit words).
* @return Ring buffer free space (in 32-bit words or bytes).
*/
static inline int ring_buf_space_get(struct ring_buf *buf)
{
if (ring_buf_is_empty(buf)) {
return buf->size - 1;
}
if (buf->tail < buf->head) {
return buf->head - buf->tail - 1;
}
/* buf->tail > buf->head */
return (buf->size - buf->tail) + buf->head - 1;
return z_ring_buf_custom_space_get(buf->size, buf->head, buf->tail);
}
/** @deprecated Renamed to ring_buf_space_get. */
@ -242,6 +287,142 @@ __deprecated static inline int sys_ring_buf_get(struct ring_buf *buf,
return ring_buf_item_get(buf, type, value, data, size32);
}
/**
* @brief Allocate buffer for writing data to a ring buffer.
*
* With this routine, memory copying can be reduced since internal ring buffer
* can be used directly by the user. Once data is written to allocated area
* number of bytes written can be confirmed (see @ref ring_buf_put_finish).
*
* @warning
* Use cases involving multiple writers to the ring buffer must prevent
* concurrent write operations, either by preventing all writers from
* being preempted or by using a mutex to govern writes to the ring buffer.
*
* @warning
* Ring buffer instance should not mix byte access and item access
* (calls prefixed with ring_buf_item_).
*
* @param[in] buf Address of ring buffer.
* @param[out] data Pointer to the address. It is set to a location within
* ring buffer.
* @param[in] size Requested allocation size (in bytes).
*
* @return Size of allocated buffer which can be smaller than requested if
* there is not enough free space or buffer wraps.
*/
u32_t ring_buf_put_claim(struct ring_buf *buf, u8_t **data, u32_t size);
/**
* @brief Indicate number of bytes written to allocated buffers.
*
* @warning
* Use cases involving multiple writers to the ring buffer must prevent
* concurrent write operations, either by preventing all writers from
* being preempted or by using a mutex to govern writes to the ring buffer.
*
* @warning
* Ring buffer instance should not mix byte access and item access
* (calls prefixed with ring_buf_item_).
*
* @param buf Address of ring buffer.
* @param size Number of valid bytes in the allocated buffers.
*
* @retval 0 Successful operation.
* @retval -EINVAL Provided @a size exceeds free space in the ring buffer.
*/
int ring_buf_put_finish(struct ring_buf *buf, u32_t size);
/**
* @brief Write (copy) data to a ring buffer.
*
* This routine writes data to a ring buffer @a buf.
*
* @warning
* Use cases involving multiple writers to the ring buffer must prevent
* concurrent write operations, either by preventing all writers from
* being preempted or by using a mutex to govern writes to the ring buffer.
*
* @warning
* Ring buffer instance should not mix byte access and item access
* (calls prefixed with ring_buf_item_).
*
* @param buf Address of ring buffer.
* @param data Address of data.
* @param size Data size (in bytes).
*
* @retval Number of bytes written.
*/
u32_t ring_buf_put(struct ring_buf *buf, const u8_t *data, u32_t size);
/**
* @brief Get address of a valid data in a ring buffer.
*
* With this routine, memory copying can be reduced since internal ring buffer
* can be used directly by the user. Once data is processed it can be freed
* using @ref ring_buf_get_finish.
*
* @warning
* Use cases involving multiple reads of the ring buffer must prevent
* concurrent read operations, either by preventing all readers from
* being preempted or by using a mutex to govern reads to the ring buffer.
*
* @warning
* Ring buffer instance should not mix byte access and item access
* (calls prefixed with ring_buf_item_).
*
* @param[in] buf Address of ring buffer.
* @param[out] data Pointer to the address. It is set to a location within
* ring buffer.
* @param[in] size Requested size (in bytes).
*
* @return Number of valid bytes in the provided buffer which can be smaller
* than requested if there is not enough free space or buffer wraps.
*/
u32_t ring_buf_get_claim(struct ring_buf *buf, u8_t **data, u32_t size);
/**
* @brief Indicate number of bytes read from claimed buffer.
*
* @warning
* Use cases involving multiple reads of the ring buffer must prevent
* concurrent read operations, either by preventing all readers from
* being preempted or by using a mutex to govern reads to the ring buffer.
*
* @warning
* Ring buffer instance should not mix byte access and item mode
* (calls prefixed with ring_buf_item_).
*
* @param buf Address of ring buffer.
* @param size Number of bytes that can be freed.
*
* @retval 0 Successful operation.
* @retval -EINVAL Provided @a size exceeds valid bytes in the ring buffer.
*/
int ring_buf_get_finish(struct ring_buf *buf, u32_t size);
/**
* @brief Read data from a ring buffer.
*
* This routine reads data from a ring buffer @a buf.
*
* @warning
* Use cases involving multiple reads of the ring buffer must prevent
* concurrent read operations, either by preventing all readers from
* being preempted or by using a mutex to govern reads to the ring buffer.
*
* @warning
* Ring buffer instance should not mix byte access and item mode
* (calls prefixed with ring_buf_item_).
*
* @param buf Address of ring buffer.
* @param data Address of the output buffer.
* @param size Data size (in bytes).
*
* @retval Number of bytes written to the output buffer.
*/
u32_t ring_buf_get(struct ring_buf *buf, u8_t *data, u32_t size);
/**
* @}
*/

View file

@ -7,6 +7,7 @@
*/
#include <ring_buffer.h>
#include <string.h>
/**
* Internal data structure for a buffer header.
@ -28,7 +29,7 @@ int ring_buf_item_put(struct ring_buf *buf, u16_t type, u8_t value,
space = ring_buf_space_get(buf);
if (space >= (size32 + 1)) {
struct ring_element *header =
(struct ring_element *)&buf->buf[buf->tail];
(struct ring_element *)&buf->buf.buf32[buf->tail];
header->type = type;
header->length = size32;
header->value = value;
@ -36,19 +37,19 @@ int ring_buf_item_put(struct ring_buf *buf, u16_t type, u8_t value,
if (likely(buf->mask)) {
for (i = 0; i < size32; ++i) {
index = (i + buf->tail + 1) & buf->mask;
buf->buf[index] = data[i];
buf->buf.buf32[index] = data[i];
}
buf->tail = (buf->tail + size32 + 1) & buf->mask;
} else {
for (i = 0; i < size32; ++i) {
index = (i + buf->tail + 1) % buf->size;
buf->buf[index] = data[i];
buf->buf.buf32[index] = data[i];
}
buf->tail = (buf->tail + size32 + 1) % buf->size;
}
rc = 0;
} else {
buf->dropped_put_count++;
buf->misc.item_mode.dropped_put_count++;
rc = -EMSGSIZE;
}
@ -65,7 +66,7 @@ int ring_buf_item_get(struct ring_buf *buf, u16_t *type, u8_t *value,
return -EAGAIN;
}
header = (struct ring_element *) &buf->buf[buf->head];
header = (struct ring_element *) &buf->buf.buf32[buf->head];
if (header->length > *size32) {
*size32 = header->length;
@ -79,16 +80,136 @@ int ring_buf_item_get(struct ring_buf *buf, u16_t *type, u8_t *value,
if (likely(buf->mask)) {
for (i = 0; i < header->length; ++i) {
index = (i + buf->head + 1) & buf->mask;
data[i] = buf->buf[index];
data[i] = buf->buf.buf32[index];
}
buf->head = (buf->head + header->length + 1) & buf->mask;
} else {
for (i = 0; i < header->length; ++i) {
index = (i + buf->head + 1) % buf->size;
data[i] = buf->buf[index];
data[i] = buf->buf.buf32[index];
}
buf->head = (buf->head + header->length + 1) % buf->size;
}
return 0;
}
/** @brief Wraps index if it exceeds the limit.
*
* @param val Value
* @param max Max.
*
* @return value % max.
*/
static inline u32_t wrap(u32_t val, u32_t max)
{
return val >= max ? (val - max) : val;
}
u32_t ring_buf_put_claim(struct ring_buf *buf, u8_t **data, u32_t size)
{
u32_t space, trail_size, allocated;
space = z_ring_buf_custom_space_get(buf->size, buf->head,
buf->misc.byte_mode.tmp_tail);
/* Limit requested size to available size. */
size = min(size, space);
trail_size = buf->size - buf->misc.byte_mode.tmp_tail;
/* Limit allocated size to trail size. */
allocated = min(trail_size, size);
*data = &buf->buf.buf8[buf->misc.byte_mode.tmp_tail];
buf->misc.byte_mode.tmp_tail =
wrap(buf->misc.byte_mode.tmp_tail + allocated, buf->size);
return allocated;
}
int ring_buf_put_finish(struct ring_buf *buf, u32_t size)
{
if (size > ring_buf_space_get(buf)) {
return -EINVAL;
}
buf->tail = wrap(buf->tail + size, buf->size);
buf->misc.byte_mode.tmp_tail = buf->tail;
return 0;
}
u32_t ring_buf_put(struct ring_buf *buf, const u8_t *data, u32_t size)
{
u8_t *dst;
u32_t partial_size;
u32_t total_size = 0;
do {
partial_size = ring_buf_put_claim(buf, &dst, size);
memcpy(dst, data, partial_size);
total_size += partial_size;
size -= partial_size;
data += partial_size;
} while (size && partial_size);
ring_buf_put_finish(buf, total_size);
return total_size;
}
u32_t ring_buf_get_claim(struct ring_buf *buf, u8_t **data, u32_t size)
{
u32_t space, granted_size, trail_size;
space = (buf->size - 1) -
z_ring_buf_custom_space_get(buf->size,
buf->misc.byte_mode.tmp_head,
buf->tail);
trail_size = buf->size - buf->misc.byte_mode.tmp_head;
/* Limit requested size to available size. */
granted_size = min(size, space);
/* Limit allocated size to trail size. */
granted_size = min(trail_size, granted_size);
*data = &buf->buf.buf8[buf->misc.byte_mode.tmp_head];
buf->misc.byte_mode.tmp_head =
wrap(buf->misc.byte_mode.tmp_head + granted_size, buf->size);
return granted_size;
}
int ring_buf_get_finish(struct ring_buf *buf, u32_t size)
{
u32_t allocated = (buf->size - 1) - ring_buf_space_get(buf);
if (size > allocated) {
return -EINVAL;
}
buf->head = wrap(buf->head + size, buf->size);
buf->misc.byte_mode.tmp_head = buf->head;
return 0;
}
u32_t ring_buf_get(struct ring_buf *buf, u8_t *data, u32_t size)
{
u8_t *src;
u32_t partial_size;
u32_t total_size = 0;
do {
partial_size = ring_buf_get_claim(buf, &src, size);
memcpy(data, src, partial_size);
total_size += partial_size;
size -= partial_size;
data += partial_size;
} while (size && partial_size);
ring_buf_get_finish(buf, total_size);
return total_size;
}

View file

@ -71,7 +71,8 @@ void test_ring_buffer_main(void)
}
getsize = INITIAL_SIZE - 1;
ret = ring_buf_item_get(&ring_buf1, &gettype, &getval, getdata, &getsize);
ret = ring_buf_item_get(&ring_buf1, &gettype, &getval,
getdata, &getsize);
if (ret != -EMSGSIZE) {
SYS_LOG_DBG("Allowed retreival with insufficient destination buffer space");
zassert_true((getsize == INITIAL_SIZE), "Correct size wasn't reported back to the caller");
@ -104,6 +105,8 @@ RING_BUF_ITEM_DECLARE_POW2(ringbuf_pow2, POW);
/**TESTPOINT: init via RING_BUF_ITEM_DECLARE_SIZE*/
RING_BUF_ITEM_DECLARE_SIZE(ringbuf_size, RINGBUFFER_SIZE);
RING_BUF_DECLARE(ringbuf_raw, RINGBUFFER_SIZE);
static struct ring_buf ringbuf, *pbuf;
static u32_t buffer[RINGBUFFER_SIZE];
@ -227,6 +230,167 @@ void test_ringbuffer_size_put_get_thread_isr(void)
irq_offload(tringbuf_get, (void *)2);
}
void test_ringbuffer_raw(void)
{
int i;
u8_t inbuf[RINGBUFFER_SIZE];
u8_t outbuf[RINGBUFFER_SIZE];
size_t in_size;
size_t out_size;
/* Initialize test buffer. */
for (i = 0; i < RINGBUFFER_SIZE; i++) {
inbuf[i] = i;
}
for (i = 0; i < 10; i++) {
memset(outbuf, 0, sizeof(outbuf));
in_size = ring_buf_put(&ringbuf_raw, inbuf,
RINGBUFFER_SIZE - 2);
out_size = ring_buf_get(&ringbuf_raw, outbuf,
RINGBUFFER_SIZE - 2);
zassert_true(in_size == RINGBUFFER_SIZE - 2, NULL);
zassert_true(in_size == out_size, NULL);
zassert_true(memcmp(inbuf, outbuf, RINGBUFFER_SIZE - 2) == 0,
NULL);
}
in_size = ring_buf_put(&ringbuf_raw, inbuf,
RINGBUFFER_SIZE);
zassert_equal(in_size, RINGBUFFER_SIZE - 1, NULL);
in_size = ring_buf_put(&ringbuf_raw, inbuf,
1);
zassert_equal(in_size, 0, NULL);
out_size = ring_buf_get(&ringbuf_raw, outbuf,
RINGBUFFER_SIZE);
zassert_true(out_size == RINGBUFFER_SIZE - 1, NULL);
out_size = ring_buf_get(&ringbuf_raw, outbuf,
RINGBUFFER_SIZE + 1);
zassert_true(out_size == 0, NULL);
}
void test_ringbuffer_alloc_put(void)
{
u8_t outputbuf[RINGBUFFER_SIZE];
u8_t inputbuf[] = {1, 2, 3, 4};
u32_t read_size;
u32_t allocated;
u32_t sum_allocated;
u8_t *data;
int err;
ring_buf_init(&ringbuf_raw, RINGBUFFER_SIZE, ringbuf_raw.buf.buf8);
allocated = ring_buf_put_claim(&ringbuf_raw, &data, 1);
sum_allocated = allocated;
zassert_true(allocated == 1, NULL);
allocated = ring_buf_put_claim(&ringbuf_raw, &data,
RINGBUFFER_SIZE - 1);
sum_allocated += allocated;
zassert_true(allocated == RINGBUFFER_SIZE - 2, NULL);
/* Putting too much returns error */
err = ring_buf_put_finish(&ringbuf_raw, RINGBUFFER_SIZE);
zassert_true(err != 0, NULL);
err = ring_buf_put_finish(&ringbuf_raw, 1);
zassert_true(err == 0, NULL);
err = ring_buf_put_finish(&ringbuf_raw, RINGBUFFER_SIZE - 2);
zassert_true(err == 0, NULL);
read_size = ring_buf_get(&ringbuf_raw, outputbuf,
RINGBUFFER_SIZE - 1);
zassert_true(read_size == (RINGBUFFER_SIZE - 1), NULL);
for (int i = 0; i < 10; i++) {
allocated = ring_buf_put_claim(&ringbuf_raw, &data, 2);
if (allocated == 2) {
data[0] = inputbuf[0];
data[1] = inputbuf[1];
} else {
data[0] = inputbuf[0];
ring_buf_put_claim(&ringbuf_raw, &data, 1);
data[0] = inputbuf[1];
}
allocated = ring_buf_put_claim(&ringbuf_raw, &data, 2);
if (allocated == 2) {
data[0] = inputbuf[2];
data[1] = inputbuf[3];
} else {
data[0] = inputbuf[2];
ring_buf_put_claim(&ringbuf_raw, &data, 1);
data[0] = inputbuf[3];
}
err = ring_buf_put_finish(&ringbuf_raw, 4);
zassert_true(err == 0, NULL);
read_size = ring_buf_get(&ringbuf_raw,
outputbuf, 4);
zassert_true(read_size == 4, NULL);
zassert_true(memcmp(outputbuf, inputbuf, 4) == 0, NULL);
}
}
void test_byte_put_free(void)
{
u8_t indata[] = {1, 2, 3, 4, 5};
int err;
u32_t granted;
u8_t *data;
ring_buf_init(&ringbuf_raw, RINGBUFFER_SIZE, ringbuf_raw.buf.buf8);
/* Ring buffer is empty */
granted = ring_buf_get_claim(&ringbuf_raw, &data, RINGBUFFER_SIZE);
zassert_true(granted == 0, NULL);
for (int i = 0; i < 10; i++) {
ring_buf_put(&ringbuf_raw, indata,
RINGBUFFER_SIZE-2);
granted = ring_buf_get_claim(&ringbuf_raw, &data,
RINGBUFFER_SIZE);
if (granted == (RINGBUFFER_SIZE-2)) {
zassert_true(memcmp(indata, data, granted) == 0, NULL);
} else if (granted < (RINGBUFFER_SIZE-2)) {
/* When buffer wraps, operation is split. */
u32_t granted_1 = granted;
zassert_true(memcmp(indata, data, granted) == 0, NULL);
granted = ring_buf_get_claim(&ringbuf_raw, &data,
RINGBUFFER_SIZE);
zassert_true((granted + granted_1) ==
RINGBUFFER_SIZE - 2, NULL);
zassert_true(memcmp(&indata[granted_1], data, granted)
== 0, NULL);
} else {
zassert_true(false, NULL);
}
/* Freeing more than possible case. */
err = ring_buf_get_finish(&ringbuf_raw, RINGBUFFER_SIZE-1);
zassert_true(err != 0, NULL);
err = ring_buf_get_finish(&ringbuf_raw, RINGBUFFER_SIZE-2);
zassert_true(err == 0, NULL);
}
}
/*test case main entry*/
void test_main(void)
{
@ -239,6 +403,10 @@ void test_main(void)
ztest_unit_test(test_ringbuffer_put_get_thread_isr),
ztest_unit_test(test_ringbuffer_pow2_put_get_thread_isr),
ztest_unit_test(test_ringbuffer_size_put_get_thread_isr),
ztest_unit_test(test_ring_buffer_main));
ztest_unit_test(test_ring_buffer_main),
ztest_unit_test(test_ringbuffer_raw),
ztest_unit_test(test_ringbuffer_alloc_put),
ztest_unit_test(test_byte_put_free)
);
ztest_run_test_suite(test_ringbuffer_api);
}

View file

@ -38,7 +38,7 @@ int logger_put(struct log_cbuffer *logger, char *data, u32_t data_size)
key = irq_lock();
ret = ring_buf_item_put(&logger->ring_buffer, 0, 0,
(u32_t *)data, size32);
(u32_t *)data, size32);
irq_unlock(key);
return ret;