Browse Source

reimplement ringbuffer

The new implementation use the mirroring method to distinguish full
buffer and the empty buffer. It also support buffers of arbitrary size.
Grissiom 12 years ago
parent
commit
639bd30321
2 changed files with 122 additions and 58 deletions
  1. 53 5
      components/drivers/include/rtdevice.h
  2. 69 53
      components/drivers/src/ringbuffer.c

+ 53 - 5
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);

+ 69 - 53
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;
 }