1: /*************************************************************************
2: * (C) 2025 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
3: * by Michael Pounov <misho@elwix.org>
4: *
5: * $Author: misho $
6: * $Id: ring.c,v 1.8 2026/02/18 11:41:47 misho Exp $
7: *
8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
15: Copyright 2004 - 2026
16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #include "global.h"
47:
48:
49: /*
50: * rbuf_init() - Init ring buffer
51: *
52: * @rbuf = Ring buffer
53: * @num = Number of elements in buffer
54: * return: -1 error or 0 ok
55: */
56: int
57: rbuf_init(ringbuf_t *rbuf, int num)
58: {
59: if (!rbuf)
60: return -1;
61:
62: atomic_store_explicit((atomic_int*) &rbuf->rb_head, 0, memory_order_relaxed);
63: atomic_store_explicit((atomic_int*) &rbuf->rb_tail, 0, memory_order_relaxed);
64: atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_relaxed);
65:
66: rbuf->rb_buffer = e_calloc(num, sizeof(struct iovec));
67: if (!rbuf->rb_buffer)
68: return -1;
69: else
70: rbuf->rb_bufnum = num;
71: memset(rbuf->rb_buffer, 0, num * sizeof(struct iovec));
72:
73: return 0;
74: }
75:
76: /*
77: * rbuf_free() - Free ring buffer
78: *
79: * @rbuf = Ring buffer
80: * return: none
81: */
82: void
83: rbuf_free(ringbuf_t *rbuf)
84: {
85: if (!rbuf)
86: return;
87:
88: if (rbuf->rb_buffer) {
89: e_free(rbuf->rb_buffer);
90: rbuf->rb_buffer = NULL;
91: rbuf->rb_bufnum = 0;
92: }
93:
94: atomic_store_explicit((atomic_int*) &rbuf->rb_head, 0, memory_order_relaxed);
95: atomic_store_explicit((atomic_int*) &rbuf->rb_tail, 0, memory_order_relaxed);
96: atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_relaxed);
97: }
98:
99: /*
100: * rbuf_purge() - Purge all buffer
101: *
102: * @rbuf = Ring buffer
103: * return: none
104: */
105: void
106: rbuf_purge(ringbuf_t *rbuf)
107: {
108: if (!rbuf)
109: return;
110:
111: if (rbuf->rb_buffer)
112: memset(rbuf->rb_buffer, 0, rbuf->rb_bufnum * sizeof(struct iovec));
113:
114: atomic_store_explicit((atomic_int*) &rbuf->rb_head, 0, memory_order_relaxed);
115: atomic_store_explicit((atomic_int*) &rbuf->rb_tail, 0, memory_order_relaxed);
116: atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_relaxed);
117: }
118:
119: /*
120: * rbuf_isempty() - Check buffer is empty
121: *
122: * @rbuf = Ring buffer
123: * return: -1 error, 0 it isn't empty
124: */
125: int
126: rbuf_isempty(ringbuf_t *rbuf)
127: {
128: if (!rbuf || !rbuf->rb_bufnum)
129: return -1;
130:
131: if (atomic_load_explicit((atomic_int*) &rbuf->rb_full, memory_order_acquire))
132: return 0;
133:
134: return (atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_acquire) ==
135: atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire));
136: }
137:
138: /*
139: * rbuf_isfull() - Check buffer is full
140: *
141: * @rbuf = Ring buffer
142: * return: -1 error or 0 it isn't full
143: */
144: int
145: rbuf_isfull(ringbuf_t *rbuf)
146: {
147: int h, t;
148:
149: if (!rbuf || !rbuf->rb_bufnum)
150: return -1;
151:
152: if (!atomic_load_explicit((atomic_int*) &rbuf->rb_full, memory_order_acquire))
153: return 0;
154:
155: t = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire);
156: h = atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_acquire);
157: return (h == t);
158: }
159:
160: /*
161: * rbuf_enqueue() - Enqueue data to buffer
162: *
163: * @rbuf = Ring buffer
164: * @data = Data
165: * @len = Length
166: * @lost = Permit to lost data
167: * return: -1 error, 1 buffer is full or 0 ok
168: */
169: int
170: rbuf_enqueue(ringbuf_t *rbuf, void *data, size_t len, int lost)
171: {
172: int h, t, f, n, t2, drop = 0;
173: struct iovec *iov;
174:
175: if (!rbuf || !rbuf->rb_buffer || !rbuf->rb_bufnum)
176: return -1;
177:
178: f = atomic_load_explicit((atomic_int*) &rbuf->rb_full, memory_order_acquire);
179: t = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire);
180: h = atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_acquire);
181:
182: if (f && h == t) {
183: if (!lost)
184: return 1;
185: else
186: drop = 1;
187: }
188:
189: n = (h + 1) % rbuf->rb_bufnum;
190:
191: iov = rbuf->rb_buffer + h;
192: iov->iov_len = len;
193: iov->iov_base = data;
194:
195: atomic_store_explicit((atomic_int*) &rbuf->rb_head, n, memory_order_release);
196: if (drop) {
197: t2 = (t + 1) % rbuf->rb_bufnum;
198: while (42) {
199: drop = t;
200: if (atomic_compare_exchange_weak_explicit((atomic_int*) &rbuf->rb_tail,
201: &drop, t2, memory_order_release, memory_order_relaxed))
202: break;
203: t = drop;
204: t2 = (t + 1) % rbuf->rb_bufnum;
205: }
206: } else
207: t2 = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire);
208: atomic_store_explicit((atomic_int*) &rbuf->rb_full, (n == t2), memory_order_release);
209: return 0;
210: }
211:
212: /*
213: * rbuf_dequeue() - Dequeue data from buffer
214: *
215: * @rbuf = Ring buffer
216: * @out = Data, if =NULL, just dequeue data
217: * return: -1 error, 1 buffer is empty or 0 ok
218: */
219: int
220: rbuf_dequeue(ringbuf_t *rbuf, struct iovec **out)
221: {
222: int h, t, n, f;
223:
224: if (!rbuf || !rbuf->rb_buffer || !rbuf->rb_bufnum)
225: return -1;
226:
227: while (42) {
228: h = atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_acquire);
229: f = atomic_load_explicit((atomic_int*) &rbuf->rb_full, memory_order_acquire);
230: t = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire);
231:
232: if (!f && h == t)
233: return 1;
234:
235: n = (t + 1) % rbuf->rb_bufnum;
236:
237: f = t;
238: if (atomic_compare_exchange_weak_explicit((atomic_int*) &rbuf->rb_tail,
239: &f, n, memory_order_release, memory_order_relaxed)) {
240: if (out)
241: *out = rbuf->rb_buffer + t;
242:
243: atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_release);
244: break;
245: }
246: }
247:
248: return 0;
249: }
250:
251:
252: /*
253: * lrb_init() - Init linear ring buffer
254: *
255: * @lrb = Linear ring buffer
256: * @size = Size of ring buffer
257: * return: -1 error or 0 ok
258: */
259: int
260: lrb_init(lrbuf_t *lrb, u_int size)
261: {
262: if (!lrb)
263: return -1;
264:
265: atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_relaxed);
266: atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_relaxed);
267: atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_relaxed);
268:
269: lrb->lrb_data = e_malloc(size);
270: if (!lrb->lrb_data)
271: return -1;
272: else
273: lrb->lrb_size = size;
274: memset(lrb->lrb_data, 0, lrb->lrb_size);
275:
276: return 0;
277: }
278:
279: /*
280: * lrb_free() - Free linear ring buffer
281: *
282: * @lrb = Linear ring buffer
283: * return: none
284: */
285: void
286: lrb_free(lrbuf_t *lrb)
287: {
288: if (!lrb)
289: return;
290:
291: if (lrb->lrb_data) {
292: e_free(lrb->lrb_data);
293: lrb->lrb_data = NULL;
294: lrb->lrb_size = 0;
295: }
296:
297: atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_relaxed);
298: atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_relaxed);
299: atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_relaxed);
300: }
301:
302: /*
303: * lrb_purge() - Purge all buffer
304: *
305: * @lrb = Linear ring buffer
306: * return: none
307: */
308: void
309: lrb_purge(lrbuf_t *lrb)
310: {
311: if (!lrb)
312: return;
313:
314: if (lrb->lrb_data)
315: memset(lrb->lrb_data, 0, lrb->lrb_size);
316:
317: atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_relaxed);
318: atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_relaxed);
319: atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_relaxed);
320: }
321:
322: /*
323: * lrb_isempty() - Check buffer is empty
324: *
325: * @lrb = Linear ring buffer
326: * return: -1 error, 0 it isn't empty
327: */
328: int
329: lrb_isempty(lrbuf_t *lrb)
330: {
331: if (!lrb || !lrb->lrb_size)
332: return -1;
333:
334: if (atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire))
335: return 0;
336:
337: return (atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire) ==
338: atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire));
339: }
340:
341: /*
342: * lrb_isfull() - Check buffer is full
343: *
344: * @lrb = Linear ring buffer
345: * return: -1 error or 0 it isn't full
346: */
347: int
348: lrb_isfull(lrbuf_t *lrb)
349: {
350: int h, t;
351:
352: if (!lrb || !lrb->lrb_size)
353: return -1;
354:
355: if (!atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire))
356: return 0;
357:
358: t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
359: h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
360: return (h == t);
361: }
362:
363: /*
364: * lrb_getw() - Get address for write
365: *
366: * @lrb = Linear ring buffer
367: * @len = Return available buffer length for write
368: * return: NULL error or !=NULL pointer for write
369: * remark: After use of lrb_getw() and write to pointer.
370: * You should update ring buffer with lrb_enqueue(,NULL,wrote_len,)
371: */
372: void *
373: lrb_getw(lrbuf_t *lrb, size_t *len)
374: {
375: int h;
376:
377: if (!lrb || !lrb->lrb_data || !lrb->lrb_size)
378: return NULL;
379:
380: h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_relaxed);
381: if (len)
382: *len = lrb->lrb_size - h;
383:
384: return (lrb->lrb_data + h);
385: }
386:
387: /*
388: * lrb_enqueue() - Enqueue data to buffer
389: *
390: * @lrb = Linear ring buffer
391: * @data = Data
392: * @len = Length
393: * @lost = Permit to lost data
394: * return: -1 error, 1 buffer is full or 0 ok
395: */
396: int
397: lrb_enqueue(lrbuf_t *lrb, void *data, size_t len, int lost)
398: {
399: int h, t = 0, n, t2 = 0, unused, drop = 0;
400:
401: if (!lrb || !lrb->lrb_data || !lrb->lrb_size)
402: return -1;
403: if (lrb->lrb_size <= len)
404: return 1;
405:
406: lrb_unused(lrb, unused);
407: if (!lost) {
408: if (len > unused)
409: return 1;
410: } else {
411: drop = len - unused;
412: if(drop < 0)
413: drop ^= drop;
414: }
415:
416: if (drop > 0) {
417: t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
418: t2 = (t + drop) % lrb->lrb_size;
419: }
420: h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_relaxed);
421: n = lrb->lrb_size - h;
422: if (len < n) {
423: if (data)
424: memcpy(lrb->lrb_data + h, data, len);
425: n = h + len;
426: } else {
427: if (data) {
428: memcpy(lrb->lrb_data + h, data, n);
429: memcpy(lrb->lrb_data, data + n, len - n);
430: }
431: n = len - n;
432: }
433:
434: h = n;
435: atomic_store_explicit((atomic_int*) &lrb->lrb_head, h, memory_order_release);
436: if (drop > 0)
437: while (42) {
438: n = t;
439: if (atomic_compare_exchange_weak_explicit((atomic_int*) &lrb->lrb_tail,
440: &n, t2, memory_order_release, memory_order_relaxed))
441: break;
442: t = n;
443: t2 = (t + drop) % lrb->lrb_size;
444: }
445: else
446: t2 = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
447: atomic_store_explicit((atomic_int*) &lrb->lrb_full, (h == t2), memory_order_release);
448: return 0;
449: }
450:
451: /*
452: * lrb_getr() - Get address for read
453: *
454: * @lrb = Linear ring buffer
455: * @len = Return available data length for read
456: * return: NULL error or !=NULL pointer for read
457: * remark: After use of lrb_getr() and read from pointer.
458: * You could update ring buffer with lrb_dequeue(,NULL,read_len)
459: */
460: void *
461: lrb_getr(lrbuf_t *lrb, size_t *len)
462: {
463: int t;
464:
465: if (!lrb || !lrb->lrb_data || !lrb->lrb_size)
466: return NULL;
467:
468: t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
469: if (len)
470: lrb_queued(lrb, *len);
471:
472: return (lrb->lrb_data + t);
473: }
474:
475: /*
476: * lrb_dequeue() - Dequeue data from buffer
477: *
478: * @lrb = Linear ring buffer
479: * @data = Data, if =NULL, just dequeue data
480: * @len = Length of data
481: * return: -1 error, 0 buffer is empty or >0 stored data bytes
482: */
483: int
484: lrb_dequeue(lrbuf_t *lrb, void *data, size_t len)
485: {
486: int h, t, t2, n, l, f;
487:
488: if (!lrb || !lrb->lrb_size)
489: return -1;
490: if (!len || lrb_isempty(lrb))
491: return 0;
492: if (lrb->lrb_size <= len)
493: len = lrb->lrb_size - 1;
494:
495: while (42) {
496: t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
497: h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
498: f = atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire);
499:
500: l = h - t;
501: if (l < 0)
502: l += lrb->lrb_size;
503: if (!l) {
504: if (!f)
505: return 0;
506: l = lrb->lrb_size;
507: }
508: if (l > len)
509: l = len;
510:
511: n = lrb->lrb_size - t;
512: if (l < n) {
513: if (data)
514: memcpy(data, lrb->lrb_data + t, l);
515: t2 = t + l;
516: } else {
517: if (data) {
518: memcpy(data, lrb->lrb_data + t, n);
519: memcpy(((u_char*) data) + n, lrb->lrb_data, l - n);
520: }
521: t2 = l - n;
522: }
523:
524: n = t;
525: if (atomic_compare_exchange_weak_explicit((atomic_int*) &lrb->lrb_tail,
526: &n, t2, memory_order_release, memory_order_relaxed)) {
527: atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_release);
528: return l;
529: }
530: }
531:
532: return 0;
533: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>