chiark / gitweb /
memfd: simplify API
[elogind.git] / src / shared / ring.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/uio.h>
27 #include "macro.h"
28 #include "ring.h"
29
30 #define RING_MASK(_r, _v) ((_v) & ((_r)->size - 1))
31
32 void ring_flush(Ring *r) {
33         assert(r);
34
35         r->start = 0;
36         r->used = 0;
37 }
38
39 void ring_clear(Ring *r) {
40         assert(r);
41
42         free(r->buf);
43         zero(*r);
44 }
45
46 /*
47  * Get data pointers for current ring-buffer data. @vec must be an array of 2
48  * iovec objects. They are filled according to the data available in the
49  * ring-buffer. 0, 1 or 2 is returned according to the number of iovec objects
50  * that were filled (0 meaning buffer is empty).
51  *
52  * Hint: "struct iovec" is defined in <sys/uio.h> and looks like this:
53  *     struct iovec {
54  *         void *iov_base;
55  *         size_t iov_len;
56  *     };
57  */
58 size_t ring_peek(Ring *r, struct iovec *vec) {
59         assert(r);
60
61         if (r->used == 0) {
62                 return 0;
63         } else if (r->start + r->used <= r->size) {
64                 if (vec) {
65                         vec[0].iov_base = &r->buf[r->start];
66                         vec[0].iov_len = r->used;
67                 }
68                 return 1;
69         } else {
70                 if (vec) {
71                         vec[0].iov_base = &r->buf[r->start];
72                         vec[0].iov_len = r->size - r->start;
73                         vec[1].iov_base = r->buf;
74                         vec[1].iov_len = r->used - (r->size - r->start);
75                 }
76                 return 2;
77         }
78 }
79
80 /*
81  * Copy data from the ring buffer into the linear external buffer @buf. Copy
82  * at most @size bytes. If the ring buffer size is smaller, copy less bytes and
83  * return the number of bytes copied.
84  */
85 size_t ring_copy(Ring *r, void *buf, size_t size) {
86         size_t l;
87
88         assert(r);
89         assert(buf);
90
91         if (size > r->used)
92                 size = r->used;
93
94         if (size > 0) {
95                 l = r->size - r->start;
96                 if (size <= l) {
97                         memcpy(buf, &r->buf[r->start], size);
98                 } else {
99                         memcpy(buf, &r->buf[r->start], l);
100                         memcpy((uint8_t*)buf + l, r->buf, size - l);
101                 }
102         }
103
104         return size;
105 }
106
107 /*
108  * Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise
109  * ring operations will behave incorrectly.
110  */
111 static int ring_resize(Ring *r, size_t nsize) {
112         uint8_t *buf;
113         size_t l;
114
115         assert(r);
116         assert(nsize > 0);
117
118         buf = malloc(nsize);
119         if (!buf)
120                 return -ENOMEM;
121
122         if (r->used > 0) {
123                 l = r->size - r->start;
124                 if (r->used <= l) {
125                         memcpy(buf, &r->buf[r->start], r->used);
126                 } else {
127                         memcpy(buf, &r->buf[r->start], l);
128                         memcpy(&buf[l], r->buf, r->used - l);
129                 }
130         }
131
132         free(r->buf);
133         r->buf = buf;
134         r->size = nsize;
135         r->start = 0;
136
137         return 0;
138 }
139
140 /*
141  * Resize ring-buffer to provide enough room for @add bytes of new data. This
142  * resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on
143  * success.
144  */
145 static int ring_grow(Ring *r, size_t add) {
146         size_t need;
147
148         assert(r);
149
150         if (r->size - r->used >= add)
151                 return 0;
152
153         need = r->used + add;
154         if (need <= r->used)
155                 return -ENOMEM;
156         else if (need < 4096)
157                 need = 4096;
158
159         need = ALIGN_POWER2(need);
160         if (need == 0)
161                 return -ENOMEM;
162
163         return ring_resize(r, need);
164 }
165
166 /*
167  * Push @len bytes from @u8 into the ring buffer. The buffer is resized if it
168  * is too small. -ENOMEM is returned on OOM, 0 on success.
169  */
170 int ring_push(Ring *r, const void *u8, size_t size) {
171         int err;
172         size_t pos, l;
173
174         assert(r);
175         assert(u8);
176
177         if (size == 0)
178                 return 0;
179
180         err = ring_grow(r, size);
181         if (err < 0)
182                 return err;
183
184         pos = RING_MASK(r, r->start + r->used);
185         l = r->size - pos;
186         if (l >= size) {
187                 memcpy(&r->buf[pos], u8, size);
188         } else {
189                 memcpy(&r->buf[pos], u8, l);
190                 memcpy(r->buf, (const uint8_t*)u8 + l, size - l);
191         }
192
193         r->used += size;
194
195         return 0;
196 }
197
198 /*
199  * Remove @len bytes from the start of the ring-buffer. Note that we protect
200  * against overflows so removing more bytes than available is safe.
201  */
202 void ring_pull(Ring *r, size_t size) {
203         assert(r);
204
205         if (size > r->used)
206                 size = r->used;
207
208         r->start = RING_MASK(r, r->start + size);
209         r->used -= size;
210 }