From bc37f7b0d0a3eaa5763a873c5730bc14b849aaa0 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Mon, 17 Oct 2016 23:54:12 +0200 Subject: [PATCH] add ring buffer implementation --- src/btstack_ring_buffer.c | 115 ++++++++++++++++++++ src/btstack_ring_buffer.h | 80 ++++++++++++++ test/ring_buffer/.gitignore | 1 + test/ring_buffer/Makefile | 28 +++++ test/ring_buffer/btstack_ring_buffer_test.c | 75 +++++++++++++ 5 files changed, 299 insertions(+) create mode 100644 src/btstack_ring_buffer.c create mode 100644 src/btstack_ring_buffer.h create mode 100644 test/ring_buffer/.gitignore create mode 100644 test/ring_buffer/Makefile create mode 100644 test/ring_buffer/btstack_ring_buffer_test.c diff --git a/src/btstack_ring_buffer.c b/src/btstack_ring_buffer.c new file mode 100644 index 000000000..29f8d7ea0 --- /dev/null +++ b/src/btstack_ring_buffer.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * btstack_ring_buffer.c + * + */ + +#include +#include + +#include "btstack_ring_buffer.h" + +#define ERROR_CODE_MEMORY_CAPACITY_EXCEEDED 0x07 + +// init ring buffer +void btstack_ring_buffer_init(btstack_ring_buffer_t * ring_buffer, uint8_t * storage, uint16_t storage_size){ + ring_buffer->storage = storage; + ring_buffer->size = storage_size; + ring_buffer->last_read_index = 0; + ring_buffer->last_written_index = 0; + ring_buffer->full = 0; +} + +int btstack_ring_buffer_bytes_available(btstack_ring_buffer_t * ring_buffer){ + if (ring_buffer->full) return ring_buffer->size; + int diff = ring_buffer->last_written_index - ring_buffer->last_read_index; + if (diff >= 0) return diff; + return diff + ring_buffer->size; +} + +// test if ring buffer is empty +int btstack_ring_buffer_empty(btstack_ring_buffer_t * ring_buffer){ + return btstack_ring_buffer_bytes_available(ring_buffer) == 0; +} + +// +int btstack_ring_buffer_bytes_free(btstack_ring_buffer_t * ring_buffer){ + return ring_buffer->size - btstack_ring_buffer_bytes_available(ring_buffer); +} + +// add byte block to ring buffer, +int btstack_ring_buffer_write(btstack_ring_buffer_t * ring_buffer, uint8_t * data, uint16_t data_length){ + if (btstack_ring_buffer_bytes_free(ring_buffer) < data_length){ + return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; + } + // printf("\n\nbtstack_ring_buffer_write %d, ring_buffer->size %d\n", data_length, ring_buffer->size); + int count = 0; + while (count < data_length){ + if (ring_buffer->last_written_index < ring_buffer->size - 1){ + ring_buffer->last_written_index++; + } else { + ring_buffer->last_written_index = 0; + } + ring_buffer->storage[ring_buffer->last_written_index] = data[count++]; + } + if (ring_buffer->last_written_index == ring_buffer->last_read_index){ + ring_buffer->full = 1; + } + return 0; +} + +// fetch data_length bytes from ring buffer +void btstack_ring_buffer_read(btstack_ring_buffer_t * ring_buffer, uint8_t * data, uint16_t data_length, uint16_t * number_of_bytes_read){ + uint32_t count = 0; + while (*number_of_bytes_read < data_length && btstack_ring_buffer_bytes_available(ring_buffer)){ + if (ring_buffer->last_read_index < ring_buffer->last_written_index ) { + ring_buffer->last_read_index++; + } else { + if (ring_buffer->last_read_index < ring_buffer->size - 1){ + ring_buffer->last_read_index++; + } else { + ring_buffer->last_read_index = 0; + } + } + ring_buffer->full = 0; + data[count++] = ring_buffer->storage[ring_buffer->last_read_index]; + } + *number_of_bytes_read = count; +} + diff --git a/src/btstack_ring_buffer.h b/src/btstack_ring_buffer.h new file mode 100644 index 000000000..ad76b6210 --- /dev/null +++ b/src/btstack_ring_buffer.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * btstack_ring_buffer.h + */ + +#ifndef __BTSTACK_RING_BUFFER_H +#define __BTSTACK_RING_BUFFER_H + +#if defined __cplusplus +extern "C" { +#endif + +typedef struct btstack_ring_buffer { + uint8_t * storage; + uint16_t size; + uint16_t last_read_index; + uint16_t last_written_index; + uint8_t full; +} btstack_ring_buffer_t; + +// init ring buffer +void btstack_ring_buffer_init(btstack_ring_buffer_t * ring_buffer, uint8_t * storage, uint16_t storage_size); + +// test if ring buffer is empty +int btstack_ring_buffer_empty(btstack_ring_buffer_t * ring_buffer); + +// number of bytes available for read +int btstack_ring_buffer_bytes_available(btstack_ring_buffer_t * ring_buffer); + +// free space for write given in number of bytes +int btstack_ring_buffer_bytes_free(btstack_ring_buffer_t * ring_buffer); + +// add byte block to ring buffer, +int btstack_ring_buffer_write(btstack_ring_buffer_t * ring_buffer, uint8_t * data, uint16_t data_length); + +// fetch data_length bytes from ring buffer +void btstack_ring_buffer_read(btstack_ring_buffer_t * ring_buffer, uint8_t * data, uint16_t data_length, uint16_t * number_of_bytes_read); + + +#if defined __cplusplus +} +#endif + +#endif // __BTSTACK_RING_BUFFER_H diff --git a/test/ring_buffer/.gitignore b/test/ring_buffer/.gitignore new file mode 100644 index 000000000..c3866d23f --- /dev/null +++ b/test/ring_buffer/.gitignore @@ -0,0 +1 @@ +btstack_ring_buffer_test diff --git a/test/ring_buffer/Makefile b/test/ring_buffer/Makefile new file mode 100644 index 000000000..d1e8f074a --- /dev/null +++ b/test/ring_buffer/Makefile @@ -0,0 +1,28 @@ +CC=g++ + +# Requirements: cpputest.github.io + +BTSTACK_ROOT = ../.. +CPPUTEST_HOME = ${BTSTACK_ROOT}/test/cpputest + +CFLAGS = -g -Wall -I. -I../ -I${BTSTACK_ROOT}/src +LDFLAGS += -lCppUTest -lCppUTestExt + +VPATH += ${BTSTACK_ROOT}/src + +COMMON = \ + btstack_ring_buffer.c \ + +COMMON_OBJ = $(COMMON:.c=.o) + +all: btstack_ring_buffer_test + +btstack_ring_buffer_test: ${COMMON_OBJ} btstack_ring_buffer_test.c + ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ + +test: all + ./btstack_ring_buffer_test + +clean: + rm -fr btstack_ring_buffer_test *.dSYM *.o ../src/*.o + diff --git a/test/ring_buffer/btstack_ring_buffer_test.c b/test/ring_buffer/btstack_ring_buffer_test.c new file mode 100644 index 000000000..289d93af4 --- /dev/null +++ b/test/ring_buffer/btstack_ring_buffer_test.c @@ -0,0 +1,75 @@ +#include "CppUTest/TestHarness.h" +#include "CppUTest/CommandLineTestRunner.h" +#include "btstack_ring_buffer.h" + +static uint8_t storage[10]; + +TEST_GROUP(RingBuffer){ + btstack_ring_buffer_t ring_buffer; + int storage_size; + + void setup(void){ + storage_size = sizeof(storage); + memset(storage, 0, storage_size); + btstack_ring_buffer_init(&ring_buffer, storage, storage_size); + } +}; + +TEST(RingBuffer, EmptyBuffer){ + CHECK_TRUE(btstack_ring_buffer_empty(&ring_buffer)); + CHECK_EQUAL(0, btstack_ring_buffer_bytes_available(&ring_buffer)); + CHECK_EQUAL(storage_size, btstack_ring_buffer_bytes_free(&ring_buffer)); +} + +TEST(RingBuffer, WriteBuffer){ + uint8_t test_write_data[] = {1,2,3,4, 5}; + int test_data_size = sizeof(test_write_data); + uint8_t test_read_data[test_data_size]; + + btstack_ring_buffer_write(&ring_buffer, test_write_data, test_data_size); + CHECK_EQUAL(test_data_size, btstack_ring_buffer_bytes_available(&ring_buffer)); + + uint16_t number_of_bytes_read = 0; + memset(test_read_data, 0, test_data_size); + btstack_ring_buffer_read(&ring_buffer, test_read_data, test_data_size, &number_of_bytes_read); + + CHECK_EQUAL(0, memcmp(test_write_data, test_read_data, test_data_size)); + +} + +TEST(RingBuffer, WriteFullBuffer){ + uint8_t test_write_data[] = {1,2,3,4,5,6,7,8,9,10}; + int test_data_size = sizeof(test_write_data); + uint8_t test_read_data[test_data_size]; + + btstack_ring_buffer_write(&ring_buffer, test_write_data, test_data_size); + CHECK_EQUAL(test_data_size, btstack_ring_buffer_bytes_available(&ring_buffer)); + + memset(test_read_data, 0, test_data_size); + uint16_t number_of_bytes_read = 0; + btstack_ring_buffer_read(&ring_buffer, test_read_data, test_data_size, &number_of_bytes_read); + + CHECK_EQUAL(0, memcmp(test_write_data, test_read_data, test_data_size)); +} + + +TEST(RingBuffer, ReadWrite){ + uint8_t test_write_data[] = {1,2,3,4}; + int test_data_size = sizeof(test_write_data); + uint8_t test_read_data[test_data_size]; + + int i; + for (i=0;i<30;i++){ + btstack_ring_buffer_write(&ring_buffer, test_write_data, test_data_size); + CHECK_EQUAL(test_data_size, btstack_ring_buffer_bytes_available(&ring_buffer)); + + memset(test_read_data, 0, test_data_size); + uint16_t number_of_bytes_read = 0; + btstack_ring_buffer_read(&ring_buffer, test_read_data, test_data_size, &number_of_bytes_read); + CHECK_EQUAL(0, memcmp(test_write_data, test_read_data, test_data_size)); + } +} + +int main (int argc, const char * argv[]){ + return CommandLineTestRunner::RunAllTests(argc, argv); +} \ No newline at end of file