Annotation of libelwix/src/ring.c, revision 1.5.2.2

1.2       misho       1: /*************************************************************************
                      2: * (C) 2025 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
                      3: *  by Michael Pounov <misho@elwix.org>
                      4: *
                      5: * $Author: misho $
1.5.2.2 ! misho       6: * $Id: ring.c,v 1.5.2.1 2026/02/10 18:23:53 misho Exp $
1.2       misho       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: 
1.5       misho      15: Copyright 2004 - 2026
1.2       misho      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: 
1.3       misho      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);
1.2       misho      64: 
                     65:        rbuf->rb_buffer = e_calloc(num, sizeof(struct iovec));
                     66:        if (!rbuf->rb_buffer)
                     67:                return -1;
                     68:        else
                     69:                rbuf->rb_bufnum = num;
                     70:        memset(rbuf->rb_buffer, 0, num * sizeof(struct iovec));
                     71: 
                     72:        return 0;
                     73: }
                     74: 
                     75: /*
                     76:  * rbuf_free() - Free ring buffer
                     77:  *
                     78:  * @rbuf = Ring buffer
                     79:  * return: none
                     80:  */
                     81: void
                     82: rbuf_free(ringbuf_t *rbuf)
                     83: {
                     84:        if (!rbuf)
                     85:                return;
                     86: 
                     87:        if (rbuf->rb_buffer) {
                     88:                e_free(rbuf->rb_buffer);
                     89:                rbuf->rb_buffer = NULL;
                     90:                rbuf->rb_bufnum = 0;
                     91:        }
                     92: 
1.3       misho      93:        atomic_store_explicit((atomic_int*) &rbuf->rb_head, 0, memory_order_relaxed);
                     94:        atomic_store_explicit((atomic_int*) &rbuf->rb_tail, 0, memory_order_relaxed);
1.2       misho      95: }
                     96: 
                     97: /*
                     98:  * rbuf_purge() - Purge all buffer
                     99:  *
                    100:  * @rbuf = Ring buffer
                    101:  * return: none
                    102:  */
                    103: void
                    104: rbuf_purge(ringbuf_t *rbuf)
                    105: {
                    106:        if (!rbuf)
                    107:                return;
                    108: 
                    109:        if (rbuf->rb_buffer)
                    110:                memset(rbuf->rb_buffer, 0, rbuf->rb_bufnum * sizeof(struct iovec));
                    111: 
1.3       misho     112:        atomic_store_explicit((atomic_int*) &rbuf->rb_head, 0, memory_order_relaxed);
                    113:        atomic_store_explicit((atomic_int*) &rbuf->rb_tail, 0, memory_order_relaxed);
1.2       misho     114: }
                    115: 
                    116: /*
                    117:  * rbuf_isempty() - Check buffer is empty
                    118:  *
                    119:  * @rbuf = Ring buffer
                    120:  * return: -1 error, 0 it isn't empty
                    121:  */
                    122: int
                    123: rbuf_isempty(ringbuf_t *rbuf)
                    124: {
                    125:        if (!rbuf)
                    126:                return -1;
                    127: 
1.3       misho     128:        return (atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_acquire) ==
                    129:                atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire));
1.2       misho     130: }
                    131: 
                    132: /*
                    133:  * rbuf_isfull() - Check buffer is full
                    134:  *
                    135:  * @rbuf = Ring buffer
                    136:  * return: -1 error or 0 it isn't full
                    137:  */
                    138: int
                    139: rbuf_isfull(ringbuf_t *rbuf)
                    140: {
1.5       misho     141:        int h, t;
                    142: 
1.2       misho     143:        if (!rbuf)
                    144:                return -1;
                    145:        if (!rbuf->rb_bufnum)
                    146:                return 1;
                    147: 
1.5       misho     148:        t = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire);
                    149:        h = atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_relaxed) + 1;
                    150:        if (h >= rbuf->rb_bufnum)
                    151:                h ^= h;
                    152: 
                    153:        return (h == t);
1.2       misho     154: }
                    155: 
                    156: /*
                    157:  * rbuf_enqueue() - Enqueue data to buffer
                    158:  *
                    159:  * @rbuf = Ring buffer
                    160:  * @data = Data
                    161:  * @len = Length
                    162:  * return: -1 error, 1 buffer is full or 0 ok
                    163:  */
                    164: int
                    165: rbuf_enqueue(ringbuf_t *rbuf, void *data, size_t len)
                    166: {
                    167:        int h, t, n;
                    168:        struct iovec *iov;
                    169: 
                    170:        if (!rbuf || !rbuf->rb_buffer)
                    171:                return -1;
                    172:        if (!rbuf->rb_bufnum)
                    173:                return 1;
                    174: 
1.3       misho     175:        h = atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_relaxed);
                    176:        t = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire);
1.2       misho     177:        n = (h + 1) % rbuf->rb_bufnum;
                    178: 
                    179:        if (n == t)
                    180:                return 1;
                    181: 
                    182:        iov = rbuf->rb_buffer + h;
                    183:        iov->iov_len = len;
                    184:        iov->iov_base = data;
                    185: 
1.3       misho     186:        atomic_store_explicit((atomic_int*) &rbuf->rb_head, n, memory_order_release);
1.2       misho     187:        return 0;
                    188: }
                    189: 
                    190: /*
                    191:  * rbuf_dequeue() - Dequeue data from buffer
                    192:  *
                    193:  * @rbuf = Ring buffer
                    194:  * @out = Data, if =NULL, just dequeue data
                    195:  * return: -1 error, 1 buffer is empty or 0 ok
                    196:  */
                    197: int
1.4       misho     198: rbuf_dequeue(ringbuf_t *rbuf, struct iovec **out)
1.2       misho     199: {
                    200:        int h, t, n;
                    201: 
                    202:        if (!rbuf || !rbuf->rb_buffer)
                    203:                return -1;
                    204:        if (!rbuf->rb_bufnum)
                    205:                return 1;
                    206: 
1.3       misho     207:        h = atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_acquire);
                    208:        t = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_relaxed);
1.2       misho     209:        n = (t + 1) % rbuf->rb_bufnum;
                    210: 
                    211:        if (h == t)
                    212:                return 1;
                    213: 
                    214:        if (out)
1.4       misho     215:                *out = rbuf->rb_buffer + t;
1.2       misho     216: 
1.3       misho     217:        atomic_store_explicit((atomic_int*) &rbuf->rb_tail, n, memory_order_release);
1.2       misho     218:        return 0;
                    219: }
1.5       misho     220: 
                    221: 
                    222: /*
                    223:  * lrb_init() - Init linear ring buffer
                    224:  *
                    225:  * @lrb = Linear ring buffer
                    226:  * @size = Size of ring buffer
                    227:  * return: -1 error or 0 ok
                    228:  */
                    229: int
                    230: lrb_init(lrbuf_t *lrb, u_int size)
                    231: {
                    232:        if (!lrb)
                    233:                return -1;
                    234: 
                    235:        atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_relaxed);
                    236:        atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_relaxed);
                    237: 
                    238:        lrb->lrb_data = e_malloc(size);
                    239:        if (!lrb->lrb_data)
                    240:                return -1;
                    241:        else
                    242:                lrb->lrb_size = size;
                    243:        memset(lrb->lrb_data, 0, lrb->lrb_size);
                    244: 
                    245:        return 0;
                    246: }
                    247: 
                    248: /*
                    249:  * lrb_free() - Free linear ring buffer
                    250:  *
                    251:  * @lrb = Linear ring buffer
                    252:  * return: none
                    253:  */
                    254: void
                    255: lrb_free(lrbuf_t *lrb)
                    256: {
                    257:        if (!lrb)
                    258:                return;
                    259: 
                    260:        if (lrb->lrb_data) {
                    261:                e_free(lrb->lrb_data);
                    262:                lrb->lrb_data = NULL;
                    263:                lrb->lrb_size = 0;
                    264:        }
                    265: 
                    266:        atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_relaxed);
                    267:        atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_relaxed);
                    268: }
                    269: 
                    270: /*
                    271:  * lrb_purge() - Purge all buffer
                    272:  *
                    273:  * @lrb = Linear ring buffer
                    274:  * return: none
                    275:  */
                    276: void
                    277: lrb_purge(lrbuf_t *lrb)
                    278: {
                    279:        if (!lrb)
                    280:                return;
                    281: 
                    282:        if (lrb->lrb_data)
                    283:                memset(lrb->lrb_data, 0, lrb->lrb_size);
                    284: 
                    285:        atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_relaxed);
                    286:        atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_relaxed);
                    287:        atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_relaxed);
                    288: }
                    289: 
                    290: /*
                    291:  * lrb_isempty() - Check buffer is empty
                    292:  *
                    293:  * @lrb = Linear ring buffer
                    294:  * return: -1 error, 0 it isn't empty
                    295:  */
                    296: int
                    297: lrb_isempty(lrbuf_t *lrb)
                    298: {
                    299:        if (!lrb)
                    300:                return -1;
                    301: 
                    302:        return (!atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire) &&
                    303:                        (atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire) ==
                    304:                        atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire)));
                    305: }
                    306: 
                    307: /*
                    308:  * lrb_isfull() - Check buffer is full
                    309:  *
                    310:  * @lrb = Linear ring buffer
                    311:  * return: -1 error or 0 it isn't full
                    312:  */
                    313: int
                    314: lrb_isfull(lrbuf_t *lrb)
                    315: {
                    316:        int h, t;
                    317: 
                    318:        if (!lrb)
                    319:                return -1;
                    320:        if (!lrb->lrb_size)
                    321:                return 1;
                    322: 
                    323:        if (!atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire))
                    324:                return 0;
                    325: 
                    326:        t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
                    327:        h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
                    328:        return (h == t);
                    329: }
                    330: 
                    331: /*
1.5.2.2 ! misho     332:  * lrb_getw() - Get address for write
        !           333:  *
        !           334:  * @lrb = Linear ring buffer
        !           335:  * @len = Return available buffer length for write
        !           336:  * return: NULL error or !=NULL pointer for write
        !           337:  * remark: After use of lrb_getw() and write to pointer.
        !           338:  *             You should update ring buffer with lrb_enqueue(,NULL,wrote_len,)
        !           339:  */
        !           340: void *
        !           341: lrb_getw(lrbuf_t *lrb, size_t *len)
        !           342: {
        !           343:        int h;
        !           344: 
        !           345:        if (!lrb || !lrb->lrb_data || !lrb->lrb_size)
        !           346:                return NULL;
        !           347: 
        !           348:        h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_relaxed);
        !           349:        if (len)
        !           350:                *len = lrb->lrb_size - h;
        !           351: 
        !           352:        return (lrb->lrb_data + h);
        !           353: }
        !           354: 
        !           355: /*
1.5       misho     356:  * lrb_enqueue() - Enqueue data to buffer
                    357:  *
                    358:  * @lrb = Linear ring buffer
                    359:  * @data = Data
                    360:  * @len = Length
                    361:  * @lost = Permit to lost data
                    362:  * return: -1 error, 1 buffer is full or 0 ok
                    363:  */
                    364: int
                    365: lrb_enqueue(lrbuf_t *lrb, void *data, size_t len, int lost)
                    366: {
1.5.2.1   misho     367:        int h, t = 0, n, t2 = 0, unused, drop = 0;
1.5       misho     368: 
                    369:        if (!lrb || !lrb->lrb_data)
                    370:                return -1;
                    371:        if (!lrb->lrb_size || lrb->lrb_size <= len)
                    372:                return 1;
                    373: 
                    374:        lrb_unused(lrb, unused);
                    375:        if (!lost) {
                    376:                if (len > unused)
                    377:                        return 1;
                    378:        } else {
                    379:                drop = len - unused;
                    380:                if(drop < 0)
                    381:                        drop ^= drop;
                    382:        }
                    383: 
                    384:        if (drop > 0) {
                    385:                t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
                    386:                t2 = (t + drop) % lrb->lrb_size;
                    387:        }
                    388:        h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_relaxed);
                    389:        n = lrb->lrb_size - h;
                    390:        if (len < n) {
1.5.2.2 ! misho     391:                if (data)
        !           392:                        memcpy(lrb->lrb_data + h, data, len);
1.5       misho     393:                n = h + len;
                    394:        } else {
1.5.2.2 ! misho     395:                if (data) {
        !           396:                        memcpy(lrb->lrb_data + h, data, n);
        !           397:                        memcpy(lrb->lrb_data, data + n, len - n);
        !           398:                }
1.5       misho     399:                n = len - n;
                    400:        }
                    401: 
                    402:        h = n;
                    403:        atomic_store_explicit((atomic_int*) &lrb->lrb_head, h, memory_order_release);
                    404:        if (drop > 0)
                    405:                while (42) {
                    406:                        n = t;
                    407:                        if (atomic_compare_exchange_weak_explicit((atomic_int*) &lrb->lrb_tail,
                    408:                                                &n, t2, memory_order_release, memory_order_relaxed))
                    409:                                break;
                    410:                        t = n;
                    411:                        t2 = (t + drop) % lrb->lrb_size;
                    412:                }
                    413:        else
                    414:                t2 = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
                    415:        atomic_store_explicit((atomic_int*) &lrb->lrb_full, (h == t2), memory_order_release);
                    416:        return 0;
                    417: }
                    418: 
                    419: /*
1.5.2.2 ! misho     420:  * lrb_getr() - Get address for read
        !           421:  *
        !           422:  * @lrb = Linear ring buffer
        !           423:  * @len = Return available data length for read
        !           424:  * return: NULL error or !=NULL pointer for read
        !           425:  * remark: After use of lrb_getr() and read from pointer.
        !           426:  *             You could update ring buffer with lrb_dequeue(,NULL,read_len)
        !           427:  */
        !           428: void *
        !           429: lrb_getr(lrbuf_t *lrb, size_t *len)
        !           430: {
        !           431:        int t;
        !           432: 
        !           433:        if (!lrb || !lrb->lrb_data || !lrb->lrb_size)
        !           434:                return NULL;
        !           435: 
        !           436:        t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
        !           437:        if (len)
        !           438:                lrb_queued(lrb, *len);
        !           439: 
        !           440:        return (lrb->lrb_data + t);
        !           441: }
        !           442: 
        !           443: /*
1.5       misho     444:  * lrb_dequeue() - Dequeue data from buffer
                    445:  *
                    446:  * @lrb = Linear ring buffer
                    447:  * @data = Data, if =NULL, just dequeue data
                    448:  * @len = Length of data
                    449:  * return: -1 error, 0 buffer is empty or >0 stored data bytes
                    450:  */
                    451: int
                    452: lrb_dequeue(lrbuf_t *lrb, void *data, size_t len)
                    453: {
                    454:        int h, t, t2, n, l, f;
                    455: 
                    456:        if (!lrb)
                    457:                return -1;
                    458:        if (!lrb->lrb_size || !len || lrb_isempty(lrb))
                    459:                return 0;
                    460:        if (lrb->lrb_size <= len)
                    461:                len = lrb->lrb_size - 1;
                    462: 
                    463:        while (42) {
                    464:                t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
                    465:                h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
                    466:                f = atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire);
                    467: 
                    468:                l = h - t;
                    469:                if (l < 0)
                    470:                        l += lrb->lrb_size;
                    471:                if (!l) {
                    472:                        if (!f)
                    473:                                return 0;
                    474:                        l = lrb->lrb_size;
                    475:                }
                    476:                if (l > len)
                    477:                        l = len;
                    478: 
                    479:                n = lrb->lrb_size - t;
                    480:                if (l < n) {
                    481:                        if (data)
                    482:                                memcpy(data, lrb->lrb_data + t, l);
                    483:                        t2 = t + l;
                    484:                } else {
                    485:                        if (data) {
                    486:                                memcpy(data, lrb->lrb_data + t, n);
                    487:                                memcpy(data + n, lrb->lrb_data, l - n);
                    488:                        }
                    489:                        t2 = l - n;
                    490:                }
                    491: 
                    492:                n = t;
                    493:                if (atomic_compare_exchange_weak_explicit((atomic_int*) &lrb->lrb_tail,
                    494:                                        &n, t2, memory_order_release, memory_order_relaxed)) {
                    495:                        atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_release);
                    496:                        return l;
                    497:                }
                    498:        }
                    499: 
                    500:        return 0;
                    501: }

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