d50c190957
* moving folders and files and adjust server demo build * Fix Makefile for apps/server on Linux * fix unit test source file folders * fix datetime convert UTC functions. Add Code::Blocks project for datetime testing * added some ignore extensions * disable parallel make option * fix build for abort, dcc, and epics apps * fix build for dcc, epics, error, and getevent apps. * Fixed building of all apps * fix the ipv4 to ipv6 router app build * Change indent style from Google to Webkit * make pretty to re-format style * removed common Makefile since we already had one and two was too many * remove scripts from root folder that are no longer maintained or used * remove mercurial EOL and ignore files for git repo * remove .vscodeconfig files from repo * tweak clang-format style * clang-format src and apps with tweaked style * added clang-tidy to fix readability if braces in src * result of make tidy for src and apps * fix clang-tidy mangling * Added code::blocks project for BACnet server simulation * added code::blocks linux project for WhoIs app * update text files for EOL * fix EOL in some files * fixed make win32 apps for older gcc * Removed Borland C++ Makefile in apps. Unable to maintain support for Borland C++ compiler. * created codeblocks project for apps/epics for Windows * fixing ports/xplained to work with new data structure. * fix ports/xplained example for Atmel Studio compile * fix ports/stm32f10x example for gcc Makefile compile * fix ports/stm32f10x example for IAR EWARM compile * fix ports/xplained timer callback * fix ports/bdk_atxx_mspt build with subdirs * fix ports/bdk_atxx_mspt build with subdirs * updated git ignore for IAR build artifacts * updated gitignore for non-tracked files and folders * fixed bdk-atxx4-mstp port for Rowley Crossworks project file * fixed bdk-atxx4-mstp port for GCC AVR Makefile * fixed atmega168 port for IAR AVR and GCC AVR Makefile * fixed at91sam7s port for IAR ARM and GCC ARM Makefile * removed unmaintainable DOS, RTOS32, and atmega8 ports. Updated rx62n (untested). * changed arm7 to uip port
824 lines
25 KiB
C
824 lines
25 KiB
C
/**
|
|
* @file
|
|
* @author Steve Karg
|
|
* @date 2004
|
|
* @brief Generic ring buffer library for deeply embedded system.
|
|
*
|
|
* @section LICENSE
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to:
|
|
* The Free Software Foundation, Inc.
|
|
* 59 Temple Place - Suite 330
|
|
* Boston, MA 02111-1307
|
|
* USA.
|
|
*
|
|
* As a special exception, if other files instantiate templates or
|
|
* use macros or inline functions from this file, or you compile
|
|
* this file and link it with other works to produce a work based
|
|
* on this file, this file does not by itself cause the resulting
|
|
* work to be covered by the GNU General Public License. However
|
|
* the source code for this file must still be made available in
|
|
* accordance with section (3) of the GNU General Public License.
|
|
*
|
|
* This exception does not invalidate any other reasons why a work
|
|
* based on this file might be covered by the GNU General Public
|
|
* License.
|
|
*
|
|
* @section DESCRIPTION
|
|
*
|
|
* Generic ring buffer library for deeply embedded system.
|
|
* It uses a data store whose size is a power of 2 (8, 16, 32, 64, ...)
|
|
* and doesn't waste any data bytes. It has very low overhead, and
|
|
* utilizes modulo for indexing the data in the data store.
|
|
* It uses separate variables for consumer and producer so it can
|
|
* be used in multithreaded environment.
|
|
*
|
|
* See the unit tests for usage examples.
|
|
*
|
|
*/
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include "bacnet/basic/sys/ringbuf.h"
|
|
|
|
/**
|
|
* Returns the number of elements in the ring buffer
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @return Number of elements in the ring buffer
|
|
*/
|
|
unsigned Ringbuf_Count(RING_BUFFER const *b)
|
|
{
|
|
unsigned head, tail; /* used to avoid volatile decision */
|
|
|
|
if (b) {
|
|
head = b->head;
|
|
tail = b->tail;
|
|
return head - tail;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the empty/full status of the ring buffer
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @return true if the ring buffer is full, false if it is not.
|
|
*/
|
|
bool Ringbuf_Full(RING_BUFFER const *b)
|
|
{
|
|
return (b ? (Ringbuf_Count(b) == b->element_count) : true);
|
|
}
|
|
|
|
/**
|
|
* Returns the empty/full status of the ring buffer
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @return true if the ring buffer is empty, false if it is not.
|
|
*/
|
|
bool Ringbuf_Empty(RING_BUFFER const *b)
|
|
{
|
|
return (b ? (Ringbuf_Count(b) == 0) : true);
|
|
}
|
|
|
|
/**
|
|
* Updates the depth tracking in the ring buffer
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
*/
|
|
static void Ringbuf_Depth_Update(RING_BUFFER *b)
|
|
{
|
|
unsigned count;
|
|
|
|
if (b) {
|
|
count = Ringbuf_Count(b);
|
|
if (count > b->depth) {
|
|
b->depth = count;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the depth tracking in the ring buffer
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @return largest number of items that have been in the ring buffer
|
|
*/
|
|
unsigned Ringbuf_Depth(RING_BUFFER const *b)
|
|
{
|
|
unsigned depth = 0;
|
|
|
|
if (b) {
|
|
depth = b->depth;
|
|
}
|
|
|
|
return depth;
|
|
}
|
|
|
|
/**
|
|
* Resets the depth tracking in the ring buffer
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @return largest number of items that have been in the ring buffer
|
|
*/
|
|
unsigned Ringbuf_Depth_Reset(RING_BUFFER *b)
|
|
{
|
|
unsigned depth = 0;
|
|
|
|
if (b) {
|
|
depth = b->depth;
|
|
b->depth = 0;
|
|
}
|
|
|
|
return depth;
|
|
}
|
|
|
|
/**
|
|
* Gets the capacity of the ring buffer (number of possible elements)
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @return largest number of items that have been in the ring buffer
|
|
*/
|
|
unsigned Ringbuf_Size(RING_BUFFER const *b)
|
|
{
|
|
unsigned count = 0;
|
|
|
|
if (b) {
|
|
count = b->element_count;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Looks at the data from the head of the list without removing it
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @return pointer to the data, or NULL if nothing in the list
|
|
*/
|
|
volatile uint8_t *Ringbuf_Peek(RING_BUFFER const *b)
|
|
{
|
|
volatile uint8_t *data_element = NULL; /* return value */
|
|
|
|
if (!Ringbuf_Empty(b)) {
|
|
data_element = b->buffer;
|
|
data_element += ((b->tail % b->element_count) * b->element_size);
|
|
}
|
|
|
|
return data_element;
|
|
}
|
|
|
|
/**
|
|
* Looks at the data from the next element of the list without removing it
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @param data_element - find the next element from this one
|
|
* @return pointer to the data, or NULL if nothing in the list
|
|
*/
|
|
volatile uint8_t *Ringbuf_Peek_Next(RING_BUFFER const *b, uint8_t *data_element)
|
|
{
|
|
unsigned index; /* list index */
|
|
volatile uint8_t *this_element;
|
|
volatile uint8_t *next_element = NULL; /* return value */
|
|
if (!Ringbuf_Empty(b) && data_element != NULL) {
|
|
/* Use (b->head-1) here to avoid walking off end of ring */
|
|
for (index = b->tail; index < b->head - 1; index++) {
|
|
/* Find the specified data_element */
|
|
this_element =
|
|
b->buffer + ((index % b->element_count) * b->element_size);
|
|
if (data_element == this_element) {
|
|
/* Found the current element, get the next one on the list */
|
|
next_element = b->buffer +
|
|
(((index + 1) % b->element_count) * b->element_size);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return next_element;
|
|
}
|
|
|
|
/**
|
|
* Copy the data from the front of the list, and removes it
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @param data_element - element of data that is loaded with data from ring
|
|
* @return true if data was copied, false if list is empty
|
|
*/
|
|
bool Ringbuf_Pop(RING_BUFFER *b, uint8_t *data_element)
|
|
{
|
|
bool status = false; /* return value */
|
|
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
|
|
unsigned i; /* loop counter */
|
|
|
|
if (!Ringbuf_Empty(b)) {
|
|
ring_data = b->buffer;
|
|
ring_data += ((b->tail % b->element_count) * b->element_size);
|
|
if (data_element) {
|
|
for (i = 0; i < b->element_size; i++) {
|
|
data_element[i] = ring_data[i];
|
|
}
|
|
}
|
|
b->tail++;
|
|
status = true;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Copy the data from the specified element, and removes it and moves other
|
|
* elements up the list
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @param this_element - element to find
|
|
* @param data_element - element of data that is loaded with data from ring
|
|
* @return true if data was copied, false if list is empty
|
|
*/
|
|
bool Ringbuf_Pop_Element(
|
|
RING_BUFFER *b, uint8_t *this_element, uint8_t *data_element)
|
|
{
|
|
bool status = false; /* return value */
|
|
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
|
|
volatile uint8_t *prev_data;
|
|
unsigned index; /* list index */
|
|
unsigned this_index = b->head; /* index of element to remove */
|
|
unsigned i; /* loop counter */
|
|
if (!Ringbuf_Empty(b) && this_element != NULL) {
|
|
for (index = b->tail; index < b->head; index++) {
|
|
/* Find the specified data_element */
|
|
ring_data =
|
|
b->buffer + ((index % b->element_count) * b->element_size);
|
|
if (this_element == ring_data) {
|
|
/* Found the specified element, copy the data if required */
|
|
this_index = index;
|
|
if (data_element) {
|
|
for (i = 0; i < b->element_size; i++) {
|
|
data_element[i] = ring_data[i];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (this_index < b->head) {
|
|
/* Found a match, move elements up the list to fill the gap */
|
|
for (index = this_index; index > b->tail; index--) {
|
|
/* Get pointers to current and previous data_elements */
|
|
ring_data =
|
|
b->buffer + ((index % b->element_count) * b->element_size);
|
|
prev_data = b->buffer +
|
|
(((index - 1) % b->element_count) * b->element_size);
|
|
for (i = 0; i < b->element_size; i++) {
|
|
ring_data[i] = prev_data[i];
|
|
}
|
|
}
|
|
}
|
|
b->tail++;
|
|
status = true;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Adds an element of data to the ring buffer
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @param data_element - one element that is copied to the ring buffer
|
|
* @return true on succesful add, false if not added
|
|
*/
|
|
bool Ringbuf_Put(RING_BUFFER *b, uint8_t *data_element)
|
|
{
|
|
bool status = false; /* return value */
|
|
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
|
|
unsigned i; /* loop counter */
|
|
|
|
if (b && data_element) {
|
|
/* limit the amount of elements that we accept */
|
|
if (!Ringbuf_Full(b)) {
|
|
ring_data = b->buffer;
|
|
ring_data += ((b->head % b->element_count) * b->element_size);
|
|
for (i = 0; i < b->element_size; i++) {
|
|
ring_data[i] = data_element[i];
|
|
}
|
|
b->head++;
|
|
Ringbuf_Depth_Update(b);
|
|
status = true;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Adds an element of data to the front of the ring buffer
|
|
*
|
|
* Note that this function moves the tail on add instead of head,
|
|
* so this function cannot be used if you are keeping producer and
|
|
* consumer as separate processes (i.e. interrupt handlers)
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @param data_element - one element to copy to the front of the ring
|
|
* @return true on succesful add, false if not added
|
|
*/
|
|
bool Ringbuf_Put_Front(RING_BUFFER *b, uint8_t *data_element)
|
|
{
|
|
bool status = false; /* return value */
|
|
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
|
|
unsigned i = 0; /* loop counter */
|
|
|
|
if (b && data_element) {
|
|
/* limit the amount of elements that we accept */
|
|
if (!Ringbuf_Full(b)) {
|
|
b->tail--;
|
|
ring_data = b->buffer;
|
|
ring_data += ((b->tail % b->element_count) * b->element_size);
|
|
/* copy the data to the ring data element */
|
|
for (i = 0; i < b->element_size; i++) {
|
|
ring_data[i] = data_element[i];
|
|
}
|
|
Ringbuf_Depth_Update(b);
|
|
status = true;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Gets a pointer to the next free data element of the buffer
|
|
* without adding it to the ring.
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @return pointer to the next data element, or NULL if the list is full
|
|
*/
|
|
volatile uint8_t *Ringbuf_Data_Peek(RING_BUFFER *b)
|
|
{
|
|
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
|
|
|
|
if (b) {
|
|
/* limit the amount of elements that we accept */
|
|
if (!Ringbuf_Full(b)) {
|
|
ring_data = b->buffer;
|
|
ring_data += ((b->head % b->element_count) * b->element_size);
|
|
}
|
|
}
|
|
|
|
return ring_data;
|
|
}
|
|
|
|
/**
|
|
* Adds the previously peeked element of data to the end of the
|
|
* ring buffer.
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @param data_element - pointer to the peeked data element
|
|
* @return true if the buffer has space and the data element points to the
|
|
* same memory previously peeked.
|
|
*/
|
|
bool Ringbuf_Data_Put(RING_BUFFER *b, volatile uint8_t *data_element)
|
|
{
|
|
bool status = false;
|
|
volatile uint8_t *ring_data = NULL; /* used to help point ring data */
|
|
|
|
if (b) {
|
|
/* limit the amount of elements that we accept */
|
|
if (!Ringbuf_Full(b)) {
|
|
ring_data = b->buffer;
|
|
ring_data += ((b->head % b->element_count) * b->element_size);
|
|
if (ring_data == data_element) {
|
|
/* same chunk of memory - okay to signal the head */
|
|
b->head++;
|
|
Ringbuf_Depth_Update(b);
|
|
status = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Test that the parameter is a power of two.
|
|
*
|
|
* @param x unsigned integer value to be tested
|
|
*
|
|
* @return true if the parameter is a power of 2
|
|
*/
|
|
static bool isPowerOfTwo(unsigned int x)
|
|
{
|
|
/* First x in the below expression is for the case when x is 0 */
|
|
return x && (!(x & (x - 1)));
|
|
}
|
|
|
|
/**
|
|
* Configures the ring buffer data buffer. Note that the element_count
|
|
* parameter must be a power of two.
|
|
*
|
|
* @param b - pointer to RING_BUFFER structure
|
|
* @param buffer - pointer to a data buffer that is used to store the ring data
|
|
* @param element_size - size of one element in the data block
|
|
* @param element_count - number elements in the data block
|
|
*
|
|
* @return true if ring buffer was initialized
|
|
*/
|
|
bool Ringbuf_Init(RING_BUFFER *b,
|
|
volatile uint8_t *buffer,
|
|
unsigned element_size,
|
|
unsigned element_count)
|
|
{
|
|
bool status = false;
|
|
|
|
if (b && isPowerOfTwo(element_count)) {
|
|
b->head = 0;
|
|
b->tail = 0;
|
|
b->buffer = buffer;
|
|
b->element_size = element_size;
|
|
b->element_count = element_count;
|
|
/* tuning diagnostics */
|
|
b->depth = 0;
|
|
status = true;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#ifdef TEST
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include "ctest.h"
|
|
|
|
/**
|
|
* Unit Test for the ring buffer
|
|
*
|
|
* @param pTest - test tracking pointer
|
|
* @param test_buffer - pointer to RING_BUFFER structure
|
|
* @param data_element - one data element
|
|
* @param element_size - size of one data element
|
|
* @param element_count - number of data elements in the store
|
|
*/
|
|
static void testRingAroundBuffer(Test *pTest,
|
|
RING_BUFFER *test_buffer,
|
|
uint8_t *data_element,
|
|
unsigned element_size,
|
|
unsigned element_count)
|
|
{
|
|
volatile uint8_t *test_data;
|
|
unsigned index;
|
|
unsigned data_index;
|
|
unsigned count;
|
|
uint8_t value;
|
|
bool status;
|
|
|
|
ct_test(pTest, Ringbuf_Empty(test_buffer));
|
|
/* test the ring around the buffer */
|
|
for (index = 0; index < element_count; index++) {
|
|
for (count = 1; count < 4; count++) {
|
|
value = (index * count) % 255;
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
data_element[data_index] = value;
|
|
}
|
|
status = Ringbuf_Put(test_buffer, data_element);
|
|
ct_test(pTest, status == true);
|
|
ct_test(pTest, Ringbuf_Count(test_buffer) == count);
|
|
}
|
|
for (count = 1; count < 4; count++) {
|
|
value = (index * count) % 255;
|
|
test_data = Ringbuf_Peek(test_buffer);
|
|
ct_test(pTest, test_data);
|
|
if (test_data) {
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
ct_test(pTest, test_data[data_index] == value);
|
|
}
|
|
}
|
|
status = Ringbuf_Pop(test_buffer, NULL);
|
|
ct_test(pTest, status == true);
|
|
}
|
|
}
|
|
ct_test(pTest, Ringbuf_Empty(test_buffer));
|
|
}
|
|
|
|
/**
|
|
* Unit Test for the ring buffer
|
|
*
|
|
* @param pTest - test tracking pointer
|
|
* @param data_store - buffer to store elements
|
|
* @param data_element - one data element
|
|
* @param element_size - size of one data element
|
|
* @param element_count - number of data elements in the store
|
|
*/
|
|
static bool testRingBuf(Test *pTest,
|
|
uint8_t *data_store,
|
|
uint8_t *data_element,
|
|
unsigned element_size,
|
|
unsigned element_count)
|
|
{
|
|
RING_BUFFER test_buffer;
|
|
volatile uint8_t *test_data;
|
|
unsigned index;
|
|
unsigned data_index;
|
|
bool status;
|
|
|
|
status =
|
|
Ringbuf_Init(&test_buffer, data_store, element_size, element_count);
|
|
if (!status) {
|
|
return false;
|
|
}
|
|
ct_test(pTest, Ringbuf_Empty(&test_buffer));
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 0);
|
|
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
data_element[data_index] = data_index;
|
|
}
|
|
status = Ringbuf_Put(&test_buffer, data_element);
|
|
ct_test(pTest, status == true);
|
|
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
|
|
|
|
test_data = Ringbuf_Peek(&test_buffer);
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
ct_test(pTest, test_data[data_index] == data_element[data_index]);
|
|
}
|
|
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
|
|
(void)Ringbuf_Pop(&test_buffer, NULL);
|
|
ct_test(pTest, Ringbuf_Empty(&test_buffer));
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
|
|
|
|
/* fill to max */
|
|
for (index = 0; index < element_count; index++) {
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
data_element[data_index] = index;
|
|
}
|
|
status = Ringbuf_Put(&test_buffer, data_element);
|
|
ct_test(pTest, status == true);
|
|
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == (index + 1));
|
|
}
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
|
|
/* verify actions on full buffer */
|
|
for (index = 0; index < element_count; index++) {
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
data_element[data_index] = index;
|
|
}
|
|
status = Ringbuf_Put(&test_buffer, data_element);
|
|
ct_test(pTest, status == false);
|
|
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
|
|
}
|
|
|
|
/* check buffer full */
|
|
for (index = 0; index < element_count; index++) {
|
|
test_data = Ringbuf_Peek(&test_buffer);
|
|
ct_test(pTest, test_data);
|
|
if (test_data) {
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
ct_test(pTest, test_data[data_index] == index);
|
|
}
|
|
}
|
|
(void)Ringbuf_Pop(&test_buffer, NULL);
|
|
}
|
|
ct_test(pTest, Ringbuf_Empty(&test_buffer));
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
|
|
Ringbuf_Depth_Reset(&test_buffer);
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 0);
|
|
|
|
testRingAroundBuffer(
|
|
pTest, &test_buffer, data_element, element_size, element_count);
|
|
|
|
/* adjust the internal index of Ringbuf to test unsigned wrapping */
|
|
test_buffer.head = UINT_MAX - 1;
|
|
test_buffer.tail = UINT_MAX - 1;
|
|
|
|
testRingAroundBuffer(
|
|
pTest, &test_buffer, data_element, element_size, element_count);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Unit Test for the ring buffer with 16 data elements
|
|
*
|
|
* @param pTest - test tracking pointer
|
|
*/
|
|
void testRingBufSizeSmall(Test *pTest)
|
|
{
|
|
bool status;
|
|
uint8_t data_element[5];
|
|
uint8_t data_store[sizeof(data_element) * NEXT_POWER_OF_2(16)];
|
|
|
|
status = testRingBuf(pTest, data_store, data_element, sizeof(data_element),
|
|
sizeof(data_store) / sizeof(data_element));
|
|
ct_test(pTest, status);
|
|
}
|
|
|
|
/**
|
|
* Unit Test for the ring buffer with 32 data elements
|
|
*
|
|
* @param pTest - test tracking pointer
|
|
*/
|
|
void testRingBufSizeLarge(Test *pTest)
|
|
{
|
|
bool status;
|
|
uint8_t data_element[16];
|
|
uint8_t data_store[sizeof(data_element) * NEXT_POWER_OF_2(99)];
|
|
|
|
status = testRingBuf(pTest, data_store, data_element, sizeof(data_element),
|
|
sizeof(data_store) / sizeof(data_element));
|
|
ct_test(pTest, status);
|
|
}
|
|
|
|
/**
|
|
* Unit Test for the ring buffer with 32 data elements
|
|
*
|
|
* @param pTest - test tracking pointer
|
|
*/
|
|
void testRingBufSizeInvalid(Test *pTest)
|
|
{
|
|
bool status;
|
|
uint8_t data_element[16];
|
|
uint8_t data_store[sizeof(data_element) * 99];
|
|
|
|
status = testRingBuf(pTest, data_store, data_element, sizeof(data_element),
|
|
sizeof(data_store) / sizeof(data_element));
|
|
ct_test(pTest, status == false);
|
|
}
|
|
|
|
void testRingBufPowerOfTwo(Test *pTest)
|
|
{
|
|
ct_test(pTest, NEXT_POWER_OF_2(3) == 4);
|
|
ct_test(pTest, NEXT_POWER_OF_2(100) == 128);
|
|
ct_test(pTest, NEXT_POWER_OF_2(127) == 128);
|
|
ct_test(pTest, NEXT_POWER_OF_2(128) == 128);
|
|
ct_test(pTest, NEXT_POWER_OF_2(129) == 256);
|
|
ct_test(pTest, NEXT_POWER_OF_2(300) == 512);
|
|
ct_test(pTest, NEXT_POWER_OF_2(500) == 512);
|
|
}
|
|
|
|
/**
|
|
* Unit Test for the ring buffer peek/pop next element
|
|
*
|
|
* @param pTest - test tracking pointer
|
|
* @param data_store - buffer to store elements
|
|
* @param data_element - one data element
|
|
* @param element_size - size of one data element
|
|
* @param element_count - number of data elements in the store
|
|
*/
|
|
static bool testRingBufNextElement(Test *pTest,
|
|
uint8_t *data_store,
|
|
uint8_t *data_element,
|
|
unsigned element_size,
|
|
unsigned element_count)
|
|
{
|
|
RING_BUFFER test_buffer;
|
|
volatile uint8_t *test_data;
|
|
unsigned index;
|
|
unsigned data_index;
|
|
bool status;
|
|
status =
|
|
Ringbuf_Init(&test_buffer, data_store, element_size, element_count);
|
|
if (!status) {
|
|
return false;
|
|
}
|
|
ct_test(pTest, Ringbuf_Empty(&test_buffer));
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 0);
|
|
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
data_element[data_index] = data_index;
|
|
}
|
|
status = Ringbuf_Put(&test_buffer, data_element);
|
|
ct_test(pTest, status == true);
|
|
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
|
|
|
|
test_data = Ringbuf_Peek(&test_buffer);
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
ct_test(pTest, test_data[data_index] == data_element[data_index]);
|
|
}
|
|
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
|
|
(void)Ringbuf_Pop(&test_buffer, NULL);
|
|
ct_test(pTest, Ringbuf_Empty(&test_buffer));
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == 1);
|
|
|
|
/* fill to max */
|
|
for (index = 0; index < element_count; index++) {
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
data_element[data_index] = index;
|
|
}
|
|
status = Ringbuf_Put(&test_buffer, data_element);
|
|
ct_test(pTest, status == true);
|
|
ct_test(pTest, !Ringbuf_Empty(&test_buffer));
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == (index + 1));
|
|
}
|
|
ct_test(pTest, Ringbuf_Depth(&test_buffer) == element_count);
|
|
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count);
|
|
|
|
/* Walk through ring buffer */
|
|
test_data = Ringbuf_Peek(&test_buffer);
|
|
ct_test(pTest, test_data);
|
|
for (index = 1; index < element_count; index++) {
|
|
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
|
|
ct_test(pTest, test_data);
|
|
if (test_data) {
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
ct_test(pTest, test_data[data_index] == index);
|
|
}
|
|
}
|
|
}
|
|
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count);
|
|
/* Try to walk off end of buffer - should return NULL */
|
|
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
|
|
ct_test(pTest, (test_data == NULL));
|
|
|
|
/* Walk through ring buffer and pop alternate elements */
|
|
test_data = Ringbuf_Peek(&test_buffer);
|
|
ct_test(pTest, test_data);
|
|
for (index = 1; index < element_count / 2; index++) {
|
|
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
|
|
ct_test(pTest, test_data);
|
|
(void)Ringbuf_Pop_Element(&test_buffer, (uint8_t *)test_data, NULL);
|
|
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
|
|
}
|
|
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count / 2 + 1);
|
|
|
|
/* Walk through ring buffer and check data */
|
|
test_data = Ringbuf_Peek(&test_buffer);
|
|
ct_test(pTest, test_data);
|
|
for (index = 0; index < element_count / 2; index++) {
|
|
if (test_data) {
|
|
for (data_index = 0; data_index < element_size; data_index++) {
|
|
ct_test(pTest, test_data[data_index] == index * 2);
|
|
}
|
|
}
|
|
test_data = Ringbuf_Peek_Next(&test_buffer, (uint8_t *)test_data);
|
|
ct_test(pTest, test_data);
|
|
}
|
|
ct_test(pTest, Ringbuf_Count(&test_buffer) == element_count / 2 + 1);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Unit Test for the ring buffer with 16 data elements
|
|
*
|
|
* @param pTest - test tracking pointer
|
|
*/
|
|
void testRingBufNextElementSizeSmall(Test *pTest)
|
|
{
|
|
bool status;
|
|
uint8_t data_element[5];
|
|
uint8_t data_store[sizeof(data_element) * NEXT_POWER_OF_2(16)];
|
|
|
|
status = testRingBufNextElement(pTest, data_store, data_element,
|
|
sizeof(data_element), sizeof(data_store) / sizeof(data_element));
|
|
ct_test(pTest, status);
|
|
}
|
|
|
|
#ifdef TEST_RING_BUFFER
|
|
/**
|
|
* Main program entry for Unit Test
|
|
*
|
|
* @return returns 0 on success, and non-zero on fail.
|
|
*/
|
|
int main(void)
|
|
{
|
|
Test *pTest;
|
|
bool rc;
|
|
|
|
pTest = ct_create("Ring Buffer", NULL);
|
|
|
|
/* individual tests */
|
|
rc = ct_addTestFunction(pTest, testRingBufPowerOfTwo);
|
|
assert(rc);
|
|
rc = ct_addTestFunction(pTest, testRingBufSizeSmall);
|
|
assert(rc);
|
|
rc = ct_addTestFunction(pTest, testRingBufSizeLarge);
|
|
assert(rc);
|
|
rc = ct_addTestFunction(pTest, testRingBufSizeInvalid);
|
|
assert(rc);
|
|
rc = ct_addTestFunction(pTest, testRingBufNextElementSizeSmall);
|
|
assert(rc);
|
|
|
|
ct_setStream(pTest, stdout);
|
|
ct_run(pTest);
|
|
(void)ct_report(pTest);
|
|
|
|
ct_destroy(pTest);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
#endif
|