1 /*
2  * Copyright (c) 2015  Machine Zone, Inc.
3  *
4  * Original author: Lev Walkin <lwalkin@machinezone.com>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14 
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 #ifndef TCPKALI_RING_H
28 #define TCPKALI_RING_H
29 
30 struct ring_buffer {
31     void *ptr;
32     void *left;
33     void *right;
34     size_t size;      /* Buffer size, bytes */
35     size_t unit_size; /* Single unit size, bytes */
36 };
37 
38 struct ring_buffer *ring_buffer_new(size_t unit_size);
39 void ring_buffer_init(struct ring_buffer *, size_t unit_size);
40 
41 #define ring_buffer_free(rb) \
42     do {                     \
43         if(rb) {             \
44             free(rb->ptr);   \
45             free(rb);        \
46         }                    \
47     } while(0)
48 
49 /*
50  * Add a specified element to the ring.
51  * Returns non-zero value if the ring has grown because of it.
52  */
53 #define ring_buffer_add(rb, datum)             \
54     ({                                         \
55         typeof(datum) d = datum;               \
56         void *np = ring_buffer_next_right(rb); \
57         int grown = 0;                         \
58         if(!np) {                              \
59             ring_buffer_grow(rb);              \
60             grown = 1;                         \
61             np = ring_buffer_next_right(rb);   \
62             assert(np);                        \
63         }                                      \
64         assert(rb->unit_size == sizeof(d));    \
65         typeof(d) *p = rb->right;              \
66         rb->right = np;                        \
67         *p = d;                                \
68         grown;                                 \
69     })
70 
71 #define ring_buffer_get(rb, datump)                             \
72     ({                                                          \
73         typeof(datump) p = rb->ptr;                             \
74         typeof(datump) l = rb->left;                            \
75         typeof(datump) r = rb->right;                           \
76         assert(rb->unit_size == sizeof(*p));                    \
77         int got;                                                \
78         if(l < r) {                                             \
79             *(datump) = *l;                                     \
80             rb->left = ++l;                                     \
81             got = 1;                                            \
82         } else if(l == r) {                                     \
83             got = 0;                                            \
84         } else {                                                \
85             if((((char *)l - (char *)p)) < (ssize_t)rb->size) { \
86                 *(datump) = *l;                                 \
87                 rb->left = ++l;                                 \
88                 got = 1;                                        \
89             } else {                                            \
90                 l = p;                                          \
91                 if(l < r) {                                     \
92                     *(datump) = *l;                             \
93                     rb->left = ++l;                             \
94                     got = 1;                                    \
95                 } else {                                        \
96                     got = 0;                                    \
97                 }                                               \
98             }                                                   \
99         }                                                       \
100         got;                                                    \
101     })
102 
103 static inline void *__attribute__((unused))
ring_buffer_next_right(struct ring_buffer * rb)104 ring_buffer_next_right(struct ring_buffer *rb) {
105     char *p = rb->ptr, *l = rb->left, *r = rb->right;
106     if(r < l) {
107         r += rb->unit_size;
108         if(r >= l)
109             return 0;
110         else
111             return r;
112     } else {
113         if((r - p) < (ssize_t)(rb->size - rb->unit_size)) {
114             r += rb->unit_size;
115             assert((r - p) <= (ssize_t)rb->size);
116             return r;
117         } else {
118             if((l - p) <= (ssize_t)rb->unit_size)
119                 return 0;
120             else
121                 return p;
122         }
123     }
124 }
125 
126 
127 void ring_buffer_grow(struct ring_buffer *);
128 
129 #endif /* TCPKALI_RING_H */
130