summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorGrissiom <[email protected]>2013-05-08 15:45:31 +0800
committerGrissiom <[email protected]>2013-05-08 15:45:31 +0800
commit639bd30321bf55b4ea44f55ff2e753f3a270b51a (patch)
treeecf365f2e7816d402339dae48b11a905ea8ceaa6 /components
parent1161e7b3f87914f941900e08807a70c99b36f1a5 (diff)
reimplement ringbuffer
The new implementation use the mirroring method to distinguish full buffer and the empty buffer. It also support buffers of arbitrary size.
Diffstat (limited to 'components')
-rw-r--r--components/drivers/include/rtdevice.h58
-rw-r--r--components/drivers/src/ringbuffer.c122
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);
- }
- 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));
+ 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;
}
- 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);
- }
- 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);
+ 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;
}
- 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;
}