File:  [ELWIX - Embedded LightWeight unIX -] / libelwix / src / ring.c
Revision 1.10: download - view: text, annotated - select for diffs - revision graph
Mon Jul 27 21:11:07 2026 UTC (6 weeks, 2 days ago) by misho
Branches: MAIN
CVS tags: elwix7_2, elwix7_1, HEAD, ELWIX7_1, ELWIX7_0
Version 7.0

Changelog:
 - adds feature to ring buffers to read the data w/o move tail
 - adds new string function

    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.10 2026/07/27 21:11:07 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_release);
   63: 	atomic_store_explicit((atomic_int*) &rbuf->rb_tail, 0, memory_order_release);
   64: 	atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_release);
   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_release);
   95: 	atomic_store_explicit((atomic_int*) &rbuf->rb_tail, 0, memory_order_release);
   96: 	atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_release);
   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_release);
  115: 	atomic_store_explicit((atomic_int*) &rbuf->rb_tail, 0, memory_order_release);
  116: 	atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_release);
  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:  * @peek = Peek only data, keep the same tail position
  218:  * return: -1 error, 1 buffer is empty or 0 ok
  219:  */
  220: int
  221: rbuf_dequeue(ringbuf_t *rbuf, struct iovec **out, int peek)
  222: {
  223: 	int h, t, n, f;
  224: 
  225: 	if (!rbuf || !rbuf->rb_buffer || !rbuf->rb_bufnum)
  226: 		return -1;
  227: 
  228: 	while (42) {
  229: 		h = atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_acquire);
  230: 		f = atomic_load_explicit((atomic_int*) &rbuf->rb_full, memory_order_acquire);
  231: 		t = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire);
  232: 
  233: 		if (!f && h == t)
  234: 			return 1;
  235: 
  236: 		n = (t + 1) % rbuf->rb_bufnum;
  237: 
  238: 		if (out)
  239: 			*out = rbuf->rb_buffer + t;
  240: 
  241: 		if (peek)
  242: 			break;
  243: 
  244: 		f = t;
  245: 		if (atomic_compare_exchange_weak_explicit((atomic_int*) &rbuf->rb_tail,
  246: 					&f, n, memory_order_release, memory_order_relaxed)) {
  247: 			atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_release);
  248: 			break;
  249: 		}
  250: 	}
  251: 
  252: 	return 0;
  253: }
  254: 
  255: 
  256: /*
  257:  * lrb_init() - Init linear ring buffer
  258:  *
  259:  * @lrb = Linear ring buffer
  260:  * @size = Size of ring buffer
  261:  * return: -1 error or 0 ok
  262:  */
  263: int
  264: lrb_init(lrbuf_t *lrb, u_int size)
  265: {
  266: 	if (!lrb)
  267: 		return -1;
  268: 
  269: 	atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_release);
  270: 	atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_release);
  271: 	atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_release);
  272: 
  273: 	lrb->lrb_data = e_malloc(size);
  274: 	if (!lrb->lrb_data)
  275: 		return -1;
  276: 	else
  277: 		lrb->lrb_size = size;
  278: 	memset(lrb->lrb_data, 0, lrb->lrb_size);
  279: 
  280: 	return 0;
  281: }
  282: 
  283: /*
  284:  * lrb_free() - Free linear ring buffer
  285:  *
  286:  * @lrb = Linear ring buffer
  287:  * return: none
  288:  */
  289: void
  290: lrb_free(lrbuf_t *lrb)
  291: {
  292: 	if (!lrb)
  293: 		return;
  294: 
  295: 	if (lrb->lrb_data) {
  296: 		e_free(lrb->lrb_data);
  297: 		lrb->lrb_data = NULL;
  298: 		lrb->lrb_size = 0;
  299: 	}
  300: 
  301: 	atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_release);
  302: 	atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_release);
  303: 	atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_release);
  304: }
  305: 
  306: /*
  307:  * lrb_purge() - Purge all buffer
  308:  *
  309:  * @lrb = Linear ring buffer
  310:  * return: none
  311:  */
  312: void
  313: lrb_purge(lrbuf_t *lrb)
  314: {
  315: 	if (!lrb)
  316: 		return;
  317: 
  318: 	if (lrb->lrb_data)
  319: 		memset(lrb->lrb_data, 0, lrb->lrb_size);
  320: 
  321: 	atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_release);
  322: 	atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_release);
  323: 	atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_release);
  324: }
  325: 
  326: /*
  327:  * lrb_isempty() - Check buffer is empty
  328:  *
  329:  * @lrb = Linear ring buffer
  330:  * return: -1 error, 0 it isn't empty
  331:  */
  332: int
  333: lrb_isempty(lrbuf_t *lrb)
  334: {
  335: 	if (!lrb || !lrb->lrb_size)
  336: 		return -1;
  337: 
  338: 	if (atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire))
  339: 		return 0;
  340: 
  341: 	return (atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire) ==
  342: 			atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire));
  343: }
  344: 
  345: /*
  346:  * lrb_isfull() - Check buffer is full
  347:  *
  348:  * @lrb = Linear ring buffer
  349:  * return: -1 error or 0 it isn't full
  350:  */
  351: int
  352: lrb_isfull(lrbuf_t *lrb)
  353: {
  354: 	int h, t;
  355: 
  356: 	if (!lrb || !lrb->lrb_size)
  357: 		return -1;
  358: 
  359: 	if (!atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire))
  360: 		return 0;
  361: 
  362: 	t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
  363: 	h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
  364: 	return (h == t);
  365: }
  366: 
  367: /*
  368:  * lrb_getw() - Get address for write
  369:  *
  370:  * @lrb = Linear ring buffer
  371:  * @len = Return available buffer length for write
  372:  * return: NULL error or !=NULL pointer for write
  373:  * remark: After use of lrb_getw() and write to pointer.
  374:  * 		You should update ring buffer with lrb_enqueue(,NULL,wrote_len,)
  375:  */
  376: void *
  377: lrb_getw(lrbuf_t *lrb, size_t *len)
  378: {
  379: 	int h;
  380: 
  381: 	if (!lrb || !lrb->lrb_data || !lrb->lrb_size)
  382: 		return NULL;
  383: 
  384: 	h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
  385: 	if (len)
  386: 		*len = lrb->lrb_size - h;
  387: 
  388: 	return (lrb->lrb_data + h);
  389: }
  390: 
  391: /*
  392:  * lrb_enqueue() - Enqueue data to buffer
  393:  *
  394:  * @lrb = Linear ring buffer
  395:  * @data = Data
  396:  * @len = Length
  397:  * @lost = Permit to lost data
  398:  * return: -1 error, 1 buffer is full or 0 ok
  399:  */
  400: int
  401: lrb_enqueue(lrbuf_t *lrb, void *data, size_t len, int lost)
  402: {
  403: 	int h, t = 0, n, t2 = 0, unused, drop = 0;
  404: 
  405: 	if (!lrb || !lrb->lrb_data || !lrb->lrb_size)
  406: 		return -1;
  407: 	if (lrb->lrb_size <= len)
  408: 		return 1;
  409: 
  410: 	lrb_unused(lrb, unused);
  411: 	if (!lost) {
  412: 		if (len > unused)
  413: 			return 1;
  414: 	} else {
  415: 		drop = len - unused;
  416: 		if(drop < 0)
  417: 			drop ^= drop;
  418: 	}
  419: 
  420: 	if (drop > 0) {
  421: 		t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
  422: 		t2 = (t + drop) % lrb->lrb_size;
  423: 	}
  424: 	h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
  425: 	n = lrb->lrb_size - h;
  426: 	if (len < n) {
  427: 		if (data)
  428: 			memcpy(lrb->lrb_data + h, data, len);
  429: 		n = h + len;
  430: 	} else {
  431: 		if (data) {
  432: 			memcpy(lrb->lrb_data + h, data, n);
  433: 			memcpy(lrb->lrb_data, data + n, len - n);
  434: 		}
  435: 		n = len - n;
  436: 	}
  437: 
  438: 	h = n;
  439: 	atomic_store_explicit((atomic_int*) &lrb->lrb_head, h, memory_order_release);
  440: 	if (drop > 0)
  441: 		while (42) {
  442: 			n = t;
  443: 			if (atomic_compare_exchange_weak_explicit((atomic_int*) &lrb->lrb_tail,
  444: 						&n, t2, memory_order_release, memory_order_relaxed))
  445: 				break;
  446: 			t = n;
  447: 			t2 = (t + drop) % lrb->lrb_size;
  448: 		}
  449: 	else
  450: 		t2 = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
  451: 	atomic_store_explicit((atomic_int*) &lrb->lrb_full, (h == t2), memory_order_release);
  452: 	return 0;
  453: }
  454: 
  455: /*
  456:  * lrb_getr() - Get address for read
  457:  *
  458:  * @lrb = Linear ring buffer
  459:  * @len = Return available data length for read
  460:  * return: NULL error or !=NULL pointer for read
  461:  * remark: After use of lrb_getr() and read from pointer.
  462:  * 		You could update ring buffer with lrb_dequeue(,NULL,read_len)
  463:  */
  464: void *
  465: lrb_getr(lrbuf_t *lrb, size_t *len)
  466: {
  467: 	int t;
  468: 
  469: 	if (!lrb || !lrb->lrb_data || !lrb->lrb_size)
  470: 		return NULL;
  471: 
  472: 	t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
  473: 	if (len)
  474: 		lrb_queued(lrb, *len);
  475: 
  476: 	return (lrb->lrb_data + t);
  477: }
  478: 
  479: /*
  480:  * lrb_dequeue() - Dequeue data from buffer
  481:  *
  482:  * @lrb = Linear ring buffer
  483:  * @data = Data, if =NULL, just dequeue data
  484:  * @len = Length of data
  485:  * @peek = Peek only data, keep the same tail position
  486:  * return: -1 error, 0 buffer is empty or >0 stored data bytes
  487:  */
  488: int
  489: lrb_dequeue(lrbuf_t *lrb, void *data, size_t len, int peek)
  490: {
  491: 	int h, t, t2, n, l, f;
  492: 
  493: 	if (!lrb || !lrb->lrb_size)
  494: 		return -1;
  495: 	if (!len || lrb_isempty(lrb))
  496: 		return 0;
  497: 	if (lrb->lrb_size <= len)
  498: 		len = lrb->lrb_size - 1;
  499: 
  500: 	while (42) {
  501: 		t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
  502: 		h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
  503: 		f = atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire);
  504: 
  505: 		l = h - t;
  506: 		if (l < 0)
  507: 			l += lrb->lrb_size;
  508: 		if (!l) {
  509: 			if (!f)
  510: 				return 0;
  511: 			l = lrb->lrb_size;
  512: 		}
  513: 		if (l > len)
  514: 			l = len;
  515: 
  516: 		n = lrb->lrb_size - t;
  517: 		if (l < n) {
  518: 			if (data)
  519: 				memcpy(data, lrb->lrb_data + t, l);
  520: 			t2 = t + l;
  521: 		} else {
  522: 			if (data) {
  523: 				memcpy(data, lrb->lrb_data + t, n);
  524: 				memcpy(((u_char*) data) + n, lrb->lrb_data, l - n);
  525: 			}
  526: 			t2 = l - n;
  527: 		}
  528: 
  529: 		if (peek)
  530: 			return l;
  531: 
  532: 		n = t;
  533: 		if (atomic_compare_exchange_weak_explicit((atomic_int*) &lrb->lrb_tail,
  534: 					&n, t2, memory_order_release, memory_order_relaxed)) {
  535: 			atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_release);
  536: 			return l;
  537: 		}
  538: 	}
  539: 
  540: 	return 0;
  541: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>