From 639bd30321bf55b4ea44f55ff2e753f3a270b51a Mon Sep 17 00:00:00 2001 From: Grissiom Date: Wed, 8 May 2013 15:45:31 +0800 Subject: [PATCH] reimplement ringbuffer The new implementation use the mirroring method to distinguish full buffer and the empty buffer. It also support buffers of arbitrary size. --- components/drivers/include/rtdevice.h | 58 ++++++++++-- components/drivers/src/ringbuffer.c | 122 +++++++++++++++----------- 2 files changed, 122 insertions(+), 58 deletions(-) diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index f3400953ff..a50ceb6b34 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -28,16 +28,64 @@ struct rt_completion rt_list_t suspended_list; }; -#define RT_RINGBUFFER_SIZE(rb) ((rb)->write_index - (rb)->read_index) -#define RT_RINGBUFFER_EMPTY(rb) ((rb)->buffer_size - RT_RINGBUFFER_SIZE(rb)) /* ring buffer */ struct rt_ringbuffer { - rt_uint16_t read_index, write_index; rt_uint8_t *buffer_ptr; - rt_uint16_t buffer_size; + /* use the msb of the {read,write}_index as mirror bit. You can see this as + * if the buffer adds a virtual mirror and the pointers point either to the + * normal or to the mirrored buffer. If the write_index has the same value + * with the read_index, but in differenct mirro, the buffer is full. While + * if the write_index and the read_index are the same and within the same + * mirror, the buffer is empty. The ASCII art of the ringbuffer is: + * + * mirror = 0 mirror = 1 + * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+ + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Full + * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+ + * read_idx-^ write_idx-^ + * + * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+ + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Empty + * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+ + * read_idx-^ ^-write_idx + * + * The tradeoff is we could only use 32KiB of buffer for 16 bit of index. + * But it should be enough for most of the cases. + * + * Ref: http://en.wikipedia.org/wiki/Circular_buffer#Mirroring */ + rt_uint16_t read_mirror : 1; + rt_uint16_t read_index : 15; + rt_uint16_t write_mirror : 1; + rt_uint16_t write_index : 15; + /* as we use msb of index as mirror bit, the size should be signed and + * could only be positive. */ + rt_int16_t buffer_size; }; +/** return the size of data in rb */ +rt_inline rt_uint16_t RT_RINGBUFFER_SIZE(struct rt_ringbuffer *rb) +{ + if (rb->read_index == rb->write_index) + { + if (rb->read_mirror == rb->write_mirror) + /* we are in the same side, the ringbuffer is empty. */ + return 0; + else + return rb->buffer_size; + } + else + { + if (rb->write_index > rb->read_index) + return rb->write_index - rb->read_index; + else + return rb->buffer_size - (rb->read_index - rb->write_index); + } +} + +/** return the size of empty space in rb */ +#define RT_RINGBUFFER_EMPTY(rb) ((rb)->buffer_size - RT_RINGBUFFER_SIZE(rb)) + /* pipe device */ #define PIPE_DEVICE(device) ((struct rt_pipe_device*)(device)) struct rt_pipe_device @@ -95,7 +143,7 @@ void rt_completion_done(struct rt_completion *completion); */ void rt_ringbuffer_init(struct rt_ringbuffer *rb, rt_uint8_t *pool, - rt_uint16_t size); + rt_int16_t size); rt_size_t rt_ringbuffer_put(struct rt_ringbuffer *rb, const rt_uint8_t *ptr, rt_uint16_t length); diff --git a/components/drivers/src/ringbuffer.c b/components/drivers/src/ringbuffer.c index 6ef8f5da63..efd72d3f84 100644 --- a/components/drivers/src/ringbuffer.c +++ b/components/drivers/src/ringbuffer.c @@ -18,12 +18,14 @@ void rt_ringbuffer_init(struct rt_ringbuffer *rb, rt_uint8_t *pool, - rt_uint16_t size) + rt_int16_t size) { RT_ASSERT(rb != RT_NULL); + RT_ASSERT(size > 0) /* initialize read and write index */ - rb->read_index = rb->write_index = 0; + rb->read_mirror = rb->read_index = 0; + rb->write_mirror = rb->write_index = 0; /* set buffer pool and size */ rb->buffer_ptr = pool; @@ -36,38 +38,40 @@ rt_size_t rt_ringbuffer_put(struct rt_ringbuffer *rb, rt_uint16_t length) { rt_uint16_t size; - rt_uint16_t mask; - rt_uint16_t write_position; RT_ASSERT(rb != RT_NULL); - mask = rb->buffer_size - 1; /* whether has enough space */ - size = rb->buffer_size - (rb->write_index - rb->read_index); + size = RT_RINGBUFFER_EMPTY(rb); /* no space */ if (size == 0) return 0; + /* drop some data */ if (size < length) length = size; - write_position = (rb->write_index & mask); - if (rb->buffer_size - write_position> length) + if (rb->buffer_size - rb->write_index > length) { /* read_index - write_index = empty space */ - memcpy(&rb->buffer_ptr[write_position], ptr, length); + memcpy(&rb->buffer_ptr[rb->write_index], ptr, length); + /* this should not cause overflow because there is enough space for + * length of data in current mirror */ + rb->write_index += length; + return length; } - else - { - memcpy(&rb->buffer_ptr[write_position], - ptr, - rb->buffer_size - write_position); - memcpy(&rb->buffer_ptr[0], - &ptr[rb->buffer_size - write_position], - length - (rb->buffer_size - write_position)); - } - rb->write_index += length; + + memcpy(&rb->buffer_ptr[rb->write_index], + &ptr[0], + rb->buffer_size - rb->write_index); + memcpy(&rb->buffer_ptr[0], + &ptr[rb->buffer_size - rb->write_index], + length - (rb->buffer_size - rb->write_index)); + + /* we are going into the other side of the mirror */ + rb->write_mirror = ~rb->write_mirror; + rb->write_index = length - (rb->buffer_size - rb->write_index); return length; } @@ -78,20 +82,24 @@ RTM_EXPORT(rt_ringbuffer_put); */ rt_size_t rt_ringbuffer_putchar(struct rt_ringbuffer *rb, const rt_uint8_t ch) { - rt_uint16_t mask; - RT_ASSERT(rb != RT_NULL); - /* whether has enough space */ - mask = rb->buffer_size - 1; /* whether has enough space */ - if (rb->write_index - rb->read_index == rb->buffer_size) + if (!RT_RINGBUFFER_EMPTY(rb)) return 0; - /* put character */ - rb->buffer_ptr[rb->write_index & mask] = ch; - rb->write_index += 1; + rb->buffer_ptr[rb->write_index] = ch; + /* flip mirror */ + if (rb->write_index == rb->buffer_size-1) + { + rb->write_mirror = ~rb->write_mirror; + rb->write_index = 0; + } + else + { + rb->write_index++; + } return 1; } RTM_EXPORT(rt_ringbuffer_putchar); @@ -104,38 +112,41 @@ rt_size_t rt_ringbuffer_get(struct rt_ringbuffer *rb, rt_uint16_t length) { rt_size_t size; - rt_uint16_t mask; - rt_uint16_t read_position; RT_ASSERT(rb != RT_NULL); + /* whether has enough data */ - mask = rb->buffer_size - 1; - size = rb->write_index - rb->read_index; + size = RT_RINGBUFFER_SIZE(rb); /* no data */ if (size == 0) return 0; + /* less data */ if (size < length) length = size; - read_position = rb->read_index & mask; - if (rb->buffer_size - read_position >= length) + if (rb->buffer_size - rb->read_index > length) { /* copy all of data */ - memcpy(ptr, &rb->buffer_ptr[read_position], length); + memcpy(ptr, &rb->buffer_ptr[rb->read_index], length); + /* this should not cause overflow because there is enough space for + * length of data in current mirror */ + rb->read_index += length; + return length; } - else - { - /* copy first and second */ - memcpy(ptr, - &rb->buffer_ptr[read_position], - rb->buffer_size - read_position); - memcpy(&ptr[rb->buffer_size - read_position], - &rb->buffer_ptr[0], - length - rb->buffer_size + read_position); - } - rb->read_index += length; + + /* copy first and second */ + memcpy(&ptr[0], + &rb->buffer_ptr[rb->read_index], + rb->buffer_size - rb->read_index); + memcpy(&ptr[rb->buffer_size - rb->read_index], + &rb->buffer_ptr[0], + length - (rb->buffer_size - rb->read_index)); + + /* we are going into the other side of the mirror */ + rb->read_mirror = ~rb->read_mirror; + rb->read_index = length - (rb->buffer_size - rb->read_index); return length; } @@ -146,19 +157,24 @@ RTM_EXPORT(rt_ringbuffer_get); */ rt_size_t rt_ringbuffer_getchar(struct rt_ringbuffer *rb, rt_uint8_t *ch) { - rt_uint16_t mask; - RT_ASSERT(rb != RT_NULL); - + /* ringbuffer is empty */ - if (rb->read_index == rb->write_index) + if (!RT_RINGBUFFER_SIZE(rb)) return 0; - mask = rb->buffer_size - 1; - /* put character */ - *ch = rb->buffer_ptr[rb->read_index & mask]; - rb->read_index += 1; + *ch = rb->buffer_ptr[rb->read_index]; + + if (rb->read_index == rb->buffer_size-1) + { + rb->read_mirror = ~rb->read_mirror; + rb->read_index = 0; + } + else + { + rb->read_index++; + } return 1; }