1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2012-09-30     Bernard      first version.
9  * 2013-05-08     Grissiom     reimplement
10  * 2016-08-18     heyuanjie    add interface
11  * 2021-07-20     arminker     fix write_index bug in function rt_ringbuffer_put_force
12  * 2021-08-14     Jackistang   add comments for function interface.
13  */
14 
15 #include <rtdevice.h>
16 #include <string.h>
17 
rt_ringbuffer_status(struct rt_ringbuffer * rb)18 rt_inline enum rt_ringbuffer_state rt_ringbuffer_status(struct rt_ringbuffer *rb)
19 {
20     if (rb->read_index == rb->write_index)
21     {
22         if (rb->read_mirror == rb->write_mirror)
23             return RT_RINGBUFFER_EMPTY;
24         else
25             return RT_RINGBUFFER_FULL;
26     }
27     return RT_RINGBUFFER_HALFFULL;
28 }
29 
30 /**
31  * @brief Initialize the ring buffer object.
32  *
33  * @param rb        A pointer to the ring buffer object.
34  * @param pool      A pointer to the buffer.
35  * @param size      The size of the buffer in bytes.
36  */
rt_ringbuffer_init(struct rt_ringbuffer * rb,rt_uint8_t * pool,rt_int32_t size)37 void rt_ringbuffer_init(struct rt_ringbuffer *rb,
38                         rt_uint8_t           *pool,
39                         rt_int32_t            size)
40 {
41     RT_ASSERT(rb != RT_NULL);
42     RT_ASSERT(size > 0);
43 
44     /* initialize read and write index */
45     rb->read_mirror = rb->read_index = 0;
46     rb->write_mirror = rb->write_index = 0;
47 
48     /* set buffer pool and size */
49     rb->buffer_ptr = pool;
50     rb->buffer_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
51 }
52 RTM_EXPORT(rt_ringbuffer_init);
53 
54 /**
55  * @brief Put a block of data into the ring buffer. If the capacity of ring buffer is insufficient, it will discard out-of-range data.
56  *
57  * @param rb            A pointer to the ring buffer object.
58  * @param ptr           A pointer to the data buffer.
59  * @param length        The size of data in bytes.
60  *
61  * @return Return the data size we put into the ring buffer.
62  */
rt_ringbuffer_put(struct rt_ringbuffer * rb,const rt_uint8_t * ptr,rt_uint32_t length)63 rt_size_t rt_ringbuffer_put(struct rt_ringbuffer *rb,
64                             const rt_uint8_t     *ptr,
65                             rt_uint32_t           length)
66 {
67     rt_uint32_t size;
68 
69     RT_ASSERT(rb != RT_NULL);
70 
71     /* whether has enough space */
72     size = rt_ringbuffer_space_len(rb);
73 
74     /* no space */
75     if (size == 0)
76         return 0;
77 
78     /* drop some data */
79     if (size < length)
80         length = size;
81 
82     if (rb->buffer_size - rb->write_index > length)
83     {
84         /* read_index - write_index = empty space */
85         rt_memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
86         /* this should not cause overflow because there is enough space for
87          * length of data in current mirror */
88         rb->write_index += length;
89         return length;
90     }
91 
92     rt_memcpy(&rb->buffer_ptr[rb->write_index],
93               &ptr[0],
94               rb->buffer_size - rb->write_index);
95     rt_memcpy(&rb->buffer_ptr[0],
96               &ptr[rb->buffer_size - rb->write_index],
97               length - (rb->buffer_size - rb->write_index));
98 
99     /* we are going into the other side of the mirror */
100     rb->write_mirror = ~rb->write_mirror;
101     rb->write_index = length - (rb->buffer_size - rb->write_index);
102 
103     return length;
104 }
105 RTM_EXPORT(rt_ringbuffer_put);
106 
107 /**
108  * @brief Put a block of data into the ring buffer. If the capacity of ring buffer is insufficient, it will overwrite the existing data in the ring buffer.
109  *
110  * @param rb            A pointer to the ring buffer object.
111  * @param ptr           A pointer to the data buffer.
112  * @param length        The size of data in bytes.
113  *
114  * @return Return the data size we put into the ring buffer.
115  */
rt_ringbuffer_put_force(struct rt_ringbuffer * rb,const rt_uint8_t * ptr,rt_uint32_t length)116 rt_size_t rt_ringbuffer_put_force(struct rt_ringbuffer *rb,
117                                   const rt_uint8_t     *ptr,
118                                   rt_uint32_t           length)
119 {
120     rt_uint32_t space_length;
121 
122     RT_ASSERT(rb != RT_NULL);
123 
124     space_length = rt_ringbuffer_space_len(rb);
125 
126     if (length > rb->buffer_size)
127     {
128         ptr = &ptr[length - rb->buffer_size];
129         length = rb->buffer_size;
130     }
131 
132     if (rb->buffer_size - rb->write_index > length)
133     {
134         /* read_index - write_index = empty space */
135         rt_memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
136         /* this should not cause overflow because there is enough space for
137          * length of data in current mirror */
138         rb->write_index += length;
139 
140         if (length > space_length)
141             rb->read_index = rb->write_index;
142 
143         return length;
144     }
145 
146     rt_memcpy(&rb->buffer_ptr[rb->write_index],
147               &ptr[0],
148               rb->buffer_size - rb->write_index);
149     rt_memcpy(&rb->buffer_ptr[0],
150               &ptr[rb->buffer_size - rb->write_index],
151               length - (rb->buffer_size - rb->write_index));
152 
153     /* we are going into the other side of the mirror */
154     rb->write_mirror = ~rb->write_mirror;
155     rb->write_index = length - (rb->buffer_size - rb->write_index);
156 
157     if (length > space_length)
158     {
159         if (rb->write_index <= rb->read_index)
160             rb->read_mirror = ~rb->read_mirror;
161         rb->read_index = rb->write_index;
162     }
163 
164     return length;
165 }
166 RTM_EXPORT(rt_ringbuffer_put_force);
167 
168 /**
169  * @brief Get data from the ring buffer.
170  *
171  * @param rb            A pointer to the ring buffer.
172  * @param ptr           A pointer to the data buffer.
173  * @param length        The size of the data we want to read from the ring buffer.
174  *
175  * @return Return the data size we read from the ring buffer.
176  */
rt_ringbuffer_get(struct rt_ringbuffer * rb,rt_uint8_t * ptr,rt_uint32_t length)177 rt_size_t rt_ringbuffer_get(struct rt_ringbuffer *rb,
178                             rt_uint8_t           *ptr,
179                             rt_uint32_t           length)
180 {
181     rt_size_t size;
182 
183     RT_ASSERT(rb != RT_NULL);
184 
185     /* whether has enough data  */
186     size = rt_ringbuffer_data_len(rb);
187 
188     /* no data */
189     if (size == 0)
190         return 0;
191 
192     /* less data */
193     if (size < length)
194         length = size;
195 
196     if (rb->buffer_size - rb->read_index > length)
197     {
198         /* copy all of data */
199         rt_memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);
200         /* this should not cause overflow because there is enough space for
201          * length of data in current mirror */
202         rb->read_index += length;
203         return length;
204     }
205 
206     rt_memcpy(&ptr[0],
207               &rb->buffer_ptr[rb->read_index],
208               rb->buffer_size - rb->read_index);
209     rt_memcpy(&ptr[rb->buffer_size - rb->read_index],
210               &rb->buffer_ptr[0],
211               length - (rb->buffer_size - rb->read_index));
212 
213     /* we are going into the other side of the mirror */
214     rb->read_mirror = ~rb->read_mirror;
215     rb->read_index = length - (rb->buffer_size - rb->read_index);
216 
217     return length;
218 }
219 RTM_EXPORT(rt_ringbuffer_get);
220 
221 /**
222  * @brief Get the first readable byte of the ring buffer.
223  *
224  * @param rb        A pointer to the ringbuffer.
225  * @param ptr       When this function return, *ptr is a pointer to the first readable byte of the ring buffer.
226  *
227  * @note It is recommended to read only one byte, otherwise it may cause buffer overflow.
228  *
229  * @return Return the size of the ring buffer.
230  */
rt_ringbuffer_peek(struct rt_ringbuffer * rb,rt_uint8_t ** ptr)231 rt_size_t rt_ringbuffer_peek(struct rt_ringbuffer *rb, rt_uint8_t **ptr)
232 {
233     rt_size_t size;
234 
235     RT_ASSERT(rb != RT_NULL);
236 
237     *ptr = RT_NULL;
238 
239     /* whether has enough data  */
240     size = rt_ringbuffer_data_len(rb);
241 
242     /* no data */
243     if (size == 0)
244         return 0;
245 
246     *ptr = &rb->buffer_ptr[rb->read_index];
247 
248     if ((rt_size_t)(rb->buffer_size - rb->read_index) > size)
249     {
250         rb->read_index += size;
251         return size;
252     }
253 
254     size = rb->buffer_size - rb->read_index;
255 
256     /* we are going into the other side of the mirror */
257     rb->read_mirror = ~rb->read_mirror;
258     rb->read_index = 0;
259 
260     return size;
261 }
262 RTM_EXPORT(rt_ringbuffer_peek);
263 
264 /**
265  * @brief Put a byte into the ring buffer. If ring buffer is full, this operation will fail.
266  *
267  * @param rb        A pointer to the ring buffer object.
268  * @param ch        A byte put into the ring buffer.
269  *
270  * @return Return the data size we put into the ring buffer. The ring buffer is full if returns 0. Otherwise, it will return 1.
271  */
rt_ringbuffer_putchar(struct rt_ringbuffer * rb,const rt_uint8_t ch)272 rt_size_t rt_ringbuffer_putchar(struct rt_ringbuffer *rb, const rt_uint8_t ch)
273 {
274     RT_ASSERT(rb != RT_NULL);
275 
276     /* whether has enough space */
277     if (!rt_ringbuffer_space_len(rb))
278         return 0;
279 
280     rb->buffer_ptr[rb->write_index] = ch;
281 
282     /* flip mirror */
283     if (rb->write_index == rb->buffer_size - 1)
284     {
285         rb->write_mirror = ~rb->write_mirror;
286         rb->write_index = 0;
287     }
288     else
289     {
290         rb->write_index++;
291     }
292 
293     return 1;
294 }
295 RTM_EXPORT(rt_ringbuffer_putchar);
296 
297 /**
298  * @brief Put a byte into the ring buffer. If ring buffer is full, it will discard an old data and put into a new data.
299  *
300  * @param rb        A pointer to the ring buffer object.
301  * @param ch        A byte put into the ring buffer.
302  *
303  * @return Return the data size we put into the ring buffer. Always return 1.
304  */
rt_ringbuffer_putchar_force(struct rt_ringbuffer * rb,const rt_uint8_t ch)305 rt_size_t rt_ringbuffer_putchar_force(struct rt_ringbuffer *rb, const rt_uint8_t ch)
306 {
307     enum rt_ringbuffer_state old_state;
308 
309     RT_ASSERT(rb != RT_NULL);
310 
311     old_state = rt_ringbuffer_status(rb);
312 
313     rb->buffer_ptr[rb->write_index] = ch;
314 
315     /* flip mirror */
316     if (rb->write_index == rb->buffer_size - 1)
317     {
318         rb->write_mirror = ~rb->write_mirror;
319         rb->write_index = 0;
320         if (old_state == RT_RINGBUFFER_FULL)
321         {
322             rb->read_mirror = ~rb->read_mirror;
323             rb->read_index = rb->write_index;
324         }
325     }
326     else
327     {
328         rb->write_index++;
329         if (old_state == RT_RINGBUFFER_FULL)
330             rb->read_index = rb->write_index;
331     }
332 
333     return 1;
334 }
335 RTM_EXPORT(rt_ringbuffer_putchar_force);
336 
337 /**
338  * @brief Get a byte from the ring buffer.
339  *
340  * @param rb        The pointer to the ring buffer object.
341  * @param ch        A pointer to the buffer, used to store one byte.
342  *
343  * @return 0    The ring buffer is empty.
344  * @return 1    Success
345  */
rt_ringbuffer_getchar(struct rt_ringbuffer * rb,rt_uint8_t * ch)346 rt_size_t rt_ringbuffer_getchar(struct rt_ringbuffer *rb, rt_uint8_t *ch)
347 {
348     RT_ASSERT(rb != RT_NULL);
349 
350     /* ringbuffer is empty */
351     if (!rt_ringbuffer_data_len(rb))
352         return 0;
353 
354     /* put byte */
355     *ch = rb->buffer_ptr[rb->read_index];
356 
357     if (rb->read_index == rb->buffer_size - 1)
358     {
359         rb->read_mirror = ~rb->read_mirror;
360         rb->read_index = 0;
361     }
362     else
363     {
364         rb->read_index++;
365     }
366 
367     return 1;
368 }
369 RTM_EXPORT(rt_ringbuffer_getchar);
370 
371 /**
372  * @brief Get the size of data in the ring buffer in bytes.
373  *
374  * @param rb        The pointer to the ring buffer object.
375  *
376  * @return Return the size of data in the ring buffer in bytes.
377  */
rt_ringbuffer_data_len(struct rt_ringbuffer * rb)378 rt_size_t rt_ringbuffer_data_len(struct rt_ringbuffer *rb)
379 {
380     switch (rt_ringbuffer_status(rb))
381     {
382     case RT_RINGBUFFER_EMPTY:
383         return 0;
384     case RT_RINGBUFFER_FULL:
385         return rb->buffer_size;
386     case RT_RINGBUFFER_HALFFULL:
387     default:
388     {
389         rt_size_t wi = rb->write_index, ri = rb->read_index;
390 
391         if (wi > ri)
392             return wi - ri;
393         else
394             return rb->buffer_size - (ri - wi);
395     }
396     }
397 }
398 RTM_EXPORT(rt_ringbuffer_data_len);
399 
400 /**
401  * @brief Reset the ring buffer object, and clear all contents in the buffer.
402  *
403  * @param rb        A pointer to the ring buffer object.
404  */
rt_ringbuffer_reset(struct rt_ringbuffer * rb)405 void rt_ringbuffer_reset(struct rt_ringbuffer *rb)
406 {
407     RT_ASSERT(rb != RT_NULL);
408 
409     rb->read_mirror = 0;
410     rb->read_index = 0;
411     rb->write_mirror = 0;
412     rb->write_index = 0;
413 }
414 RTM_EXPORT(rt_ringbuffer_reset);
415 
416 #ifdef RT_USING_HEAP
417 
418 /**
419  * @brief Create a ring buffer object with a given size.
420  *
421  * @param size      The size of the buffer in bytes.
422  *
423  * @return Return a pointer to ring buffer object. When the return value is RT_NULL, it means this creation failed.
424  */
rt_ringbuffer_create(rt_uint32_t size)425 struct rt_ringbuffer *rt_ringbuffer_create(rt_uint32_t size)
426 {
427     struct rt_ringbuffer *rb;
428     rt_uint8_t *pool;
429 
430     RT_ASSERT(size > 0);
431 
432     size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
433 
434     rb = (struct rt_ringbuffer *)rt_malloc(sizeof(struct rt_ringbuffer));
435     if (rb == RT_NULL)
436         goto exit;
437 
438     pool = (rt_uint8_t *)rt_malloc(size);
439     if (pool == RT_NULL)
440     {
441         rt_free(rb);
442         rb = RT_NULL;
443         goto exit;
444     }
445     rt_ringbuffer_init(rb, pool, size);
446 
447 exit:
448     return rb;
449 }
450 RTM_EXPORT(rt_ringbuffer_create);
451 
452 /**
453  * @brief Destroy the ring buffer object, which is created by rt_ringbuffer_create() .
454  *
455  * @param rb        A pointer to the ring buffer object.
456  */
rt_ringbuffer_destroy(struct rt_ringbuffer * rb)457 void rt_ringbuffer_destroy(struct rt_ringbuffer *rb)
458 {
459     RT_ASSERT(rb != RT_NULL);
460 
461     rt_free(rb->buffer_ptr);
462     rt_free(rb);
463 }
464 RTM_EXPORT(rt_ringbuffer_destroy);
465 
466 #endif
467