Last active
August 8, 2019 01:56
-
-
Save hosackm/cb3ad68f9275764b9b7a0a9398fbf801 to your computer and use it in GitHub Desktop.
Dumb ringbuffer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CC=clang | |
CXX=clang++ | |
SOURCES=ringbuffer.c | |
INCLUDES=-I. | |
test:test.cpp $(SOURCES) | |
$(CXX) -std=c++11 -o runtests $(INCLUDES) `pkg-config --cflags --libs gtest` $^ | |
./runtests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ringbuffer.h" | |
#include <string.h> | |
ringbuffer ringbuffer_create(size_t max_buffer_size) | |
{ | |
if(max_buffer_size == 0) | |
{ | |
return NULL; | |
} | |
else | |
{ | |
ringbuffer r; | |
const size_t buffer_size = max_buffer_size * 2; | |
r = (ringbuffer) malloc(sizeof(ringbuffer_s)); | |
r->read = r->write = r->buffer = (char *) malloc(max_buffer_size * 2); | |
r->max_buffer_size = buffer_size; | |
return r; | |
} | |
} | |
ringbuffer_result ringbuffer_get_size(ringbuffer r, size_t *size) | |
{ | |
size_t diff; | |
if(r == NULL || size == NULL) | |
{ | |
return ringbuffer_result_bad_pointer; | |
} | |
if(r->read > r->write) | |
{ | |
return ringbuffer_result_underflow; | |
} | |
*size = r->write - r->read; | |
return ringbuffer_result_ok; | |
} | |
ringbuffer_result ringbuffer_write(ringbuffer r, char const *bytes, size_t n) | |
{ | |
if(r == NULL || bytes == NULL) | |
{ | |
return ringbuffer_result_bad_pointer; | |
} | |
if(r->write + n > r->buffer + r->max_buffer_size) | |
{ | |
return ringbuffer_result_overflow; | |
} | |
// copy the bytes | |
memcpy(r->write, bytes, n); | |
r->write += n; | |
return ringbuffer_result_ok; | |
} | |
ringbuffer_result ringbuffer_read(ringbuffer r, size_t n, char *output) | |
{ | |
if(r == NULL || output == NULL) | |
{ | |
return ringbuffer_result_bad_pointer; | |
} | |
if(r->read + n > r->write) | |
{ | |
return ringbuffer_result_underflow; | |
} | |
// copy the bytes | |
memcpy(output, r->read, n); | |
r->read += n; | |
return ringbuffer_result_ok; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _RINGBUFFER_H_ | |
#define _RINGBUFFER_H_ | |
#include <stdlib.h> | |
#ifdef _cplusplus | |
extern "C" { | |
#endif | |
typedef enum | |
{ | |
ringbuffer_result_ok = 0, | |
ringbuffer_result_overflow = 1, | |
ringbuffer_result_underflow = 2, | |
ringbuffer_result_bad_pointer = 3 | |
} ringbuffer_result; | |
typedef struct | |
{ | |
char *buffer; | |
size_t max_buffer_size; | |
char *read; | |
char *write; | |
} ringbuffer_s; | |
typedef ringbuffer_s *ringbuffer; | |
ringbuffer ringbuffer_create(size_t max_buffer_size); | |
ringbuffer_result ringbuffer_destroy(ringbuffer r); | |
ringbuffer_result ringbuffer_get_size(ringbuffer r, size_t *sz); | |
ringbuffer_result ringbuffer_write(ringbuffer r, char const * bytes, size_t n); | |
ringbuffer_result ringbuffer_read(ringbuffer r, size_t n, char *output); | |
#ifdef _cplusplus | |
} | |
#endif | |
#endif // _RINGBUFFER_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "gtest/gtest.h" | |
#include "ringbuffer.h" | |
TEST(RingbufferTests, CreateWithZeroReturnsNull) | |
{ | |
EXPECT_EQ(ringbuffer_create(0), nullptr); | |
} | |
TEST(RingbufferTests, CreateReturnsNonNull) | |
{ | |
EXPECT_NE(ringbuffer_create(10), nullptr); | |
} | |
TEST(RingbufferTests, NullPointersRejected) | |
{ | |
ringbuffer_result res; | |
size_t size; | |
const size_t nbytes = 16; | |
char bytes[nbytes]; | |
ringbuffer r = ringbuffer_create(10); | |
EXPECT_EQ(ringbuffer_get_size(NULL, NULL), ringbuffer_result_bad_pointer); | |
EXPECT_EQ(ringbuffer_get_size(NULL, &size), ringbuffer_result_bad_pointer); | |
EXPECT_EQ(ringbuffer_get_size(r, NULL), ringbuffer_result_bad_pointer); | |
EXPECT_EQ(ringbuffer_get_size(NULL, NULL), ringbuffer_result_bad_pointer); | |
EXPECT_EQ(ringbuffer_write(NULL, bytes, nbytes), ringbuffer_result_bad_pointer); | |
EXPECT_EQ(ringbuffer_write(r, NULL, nbytes), ringbuffer_result_bad_pointer); | |
EXPECT_EQ(ringbuffer_write(NULL, NULL, nbytes), ringbuffer_result_bad_pointer); | |
EXPECT_EQ(ringbuffer_read(NULL, nbytes, bytes), ringbuffer_result_bad_pointer); | |
EXPECT_EQ(ringbuffer_read(r, nbytes, NULL), ringbuffer_result_bad_pointer); | |
} | |
TEST(RingbufferTests, CreateReturnsSizeZero) | |
{ | |
ringbuffer r = ringbuffer_create(10); | |
size_t size; | |
const ringbuffer_result res = ringbuffer_get_size(r, &size); | |
EXPECT_EQ(res, ringbuffer_result_ok); | |
EXPECT_EQ(size, 0); | |
} | |
TEST(RingbufferTests, WriteIncreasesSize) | |
{ | |
const size_t size = 16; | |
char bytes[size] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; | |
ringbuffer r = ringbuffer_create(size); | |
ringbuffer_result res; | |
size_t new_size; | |
res = ringbuffer_write(r, bytes, size); | |
EXPECT_EQ(res, ringbuffer_result_ok); | |
res = ringbuffer_get_size(r, &new_size); | |
EXPECT_EQ(res, ringbuffer_result_ok); | |
EXPECT_EQ(new_size, size); | |
} | |
TEST(RingbufferTests, WriteGreaterThanSizeReturnsOverflowError) | |
{ | |
const size_t size = 16; | |
char bytes[size] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; | |
const size_t under_alloc_size = 16/2-1; // this will create a max_buffer_size of 14 which will overflow | |
ringbuffer r = ringbuffer_create(under_alloc_size); | |
ringbuffer_result res; | |
size_t new_size; | |
res = ringbuffer_write(r, bytes, size); | |
EXPECT_EQ(res, ringbuffer_result_overflow); | |
} | |
TEST(RingbufferTests, WriteOfTwiceTheAskedSizeReturnsNoError) | |
{ | |
const size_t size = 16; | |
char bytes[size] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; | |
const size_t exact_alloc_size = 16/2; // this will create a max_buffer_size of 14 which will overflow | |
ringbuffer r = ringbuffer_create(exact_alloc_size); | |
ringbuffer_result res; | |
size_t new_size; | |
res = ringbuffer_write(r, bytes, size); | |
EXPECT_EQ(res, ringbuffer_result_ok); | |
} | |
TEST(RingbufferTests, ReadOfLargerThanWhatsInBufferReturnsUnderflow) | |
{ | |
ringbuffer_result res; | |
const size_t size = 16; | |
char bytes[size] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; | |
char outputbytes[size+1]; | |
ringbuffer r = ringbuffer_create(size); | |
EXPECT_EQ(ringbuffer_read(r, size, bytes), ringbuffer_result_underflow); | |
res = ringbuffer_write(r, bytes, size); | |
EXPECT_EQ(res, ringbuffer_result_ok); | |
EXPECT_EQ(ringbuffer_read(r, size+1, bytes), ringbuffer_result_underflow); | |
} | |
TEST(RingbufferTests, ReadReturnsLastWrittenBytes) | |
{ | |
ringbuffer_result res; | |
const size_t size = 4; | |
char bytes[size] = {0, 1, 2, 3}; | |
char returned[size]; | |
ringbuffer r = ringbuffer_create(size); | |
ringbuffer_write(r, bytes, size); | |
ringbuffer_read(r, size, returned); | |
for(int i = 0; i < (int)size; ++i) | |
{ | |
EXPECT_EQ(bytes[i], returned[i]); | |
} | |
} | |
int main(int argc, char **argv) | |
{ | |
::testing::InitGoogleTest(&argc, argv); | |
return RUN_ALL_TESTS(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment