Bläddra i källkod

Merge pull request #84 from grissiom/ringbuffer

Ringbuffer
Bernard Xiong 12 år sedan
förälder
incheckning
c8d85a5c35
2 ändrade filer med 138 tillägg och 72 borttagningar
  1. 53 5
      components/drivers/include/rtdevice.h
  2. 85 67
      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);

+ 85 - 67
components/drivers/src/ringbuffer.c

@@ -10,6 +10,7 @@
  * Change Logs:
  * Date           Author       Notes
  * 2012-09-30     Bernard      first version.
+ * 2013-05-08     Grissiom     reimplement
  */
 
 #include <rtthread.h>
@@ -18,12 +19,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,65 +39,44 @@ 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;
-
-    return length;
-}
-RTM_EXPORT(rt_ringbuffer_put);
-
-/**
- * put a character into ring buffer
- */
-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)
-        return 0;
+    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));
 
-    /* put character */
-    rb->buffer_ptr[rb->write_index & mask] = ch;
-    rb->write_index += 1;
+    /* 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 1;
+    return length;
 }
-RTM_EXPORT(rt_ringbuffer_putchar);
+RTM_EXPORT(rt_ringbuffer_put);
 
 /**
  *  get data from ring buffer
@@ -104,62 +86,98 @@ 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;
+    }
+
+    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;
+}
+RTM_EXPORT(rt_ringbuffer_get);
+
+/**
+ * put a character into ring buffer
+ */
+rt_size_t rt_ringbuffer_putchar(struct rt_ringbuffer *rb, const rt_uint8_t ch)
+{
+    RT_ASSERT(rb != RT_NULL);
+
+    /* whether has enough space */
+    if (!RT_RINGBUFFER_EMPTY(rb))
+        return 0;
+
+    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
     {
-        /* 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->write_index++;
     }
-    rb->read_index += length;
 
-    return length;
+    return 1;
 }
-RTM_EXPORT(rt_ringbuffer_get);
+RTM_EXPORT(rt_ringbuffer_putchar);
 
 /**
  * get a character from a ringbuffer
  */
 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;
 }
 RTM_EXPORT(rt_ringbuffer_getchar);
+