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