chiark / gitweb /
tmpfiles: add new "L+" command as stronger version of "L", that removes the destinati...
[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(struct ring *r) {
33         assert(r);
34
35         r->start = 0;
36         r->used = 0;
37 }
38
39 void ring_clear(struct ring *r) {
40         free(r->buf);
41         zero(*r);
42 }
43
44 /*
45  * Get data pointers for current ring-buffer data. @vec must be an array of 2
46  * iovec objects. They are filled according to the data available in the
47  * ring-buffer. 0, 1 or 2 is returned according to the number of iovec objects
48  * that were filled (0 meaning buffer is empty).
49  *
50  * Hint: "struct iovec" is defined in <sys/uio.h> and looks like this:
51  *     struct iovec {
52  *         void *iov_base;
53  *         size_t iov_len;
54  *     };
55  */
56 size_t ring_peek(struct ring *r, struct iovec *vec) {
57         assert(r);
58
59         if (r->used == 0) {
60                 return 0;
61         } else if (r->start + r->used <= r->size) {
62                 if (vec) {
63                         vec[0].iov_base = &r->buf[r->start];
64                         vec[0].iov_len = r->used;
65                 }
66                 return 1;
67         } else {
68                 if (vec) {
69                         vec[0].iov_base = &r->buf[r->start];
70                         vec[0].iov_len = r->size - r->start;
71                         vec[1].iov_base = r->buf;
72                         vec[1].iov_len = r->used - (r->size - r->start);
73                 }
74                 return 2;
75         }
76 }
77
78 /*
79  * Copy data from the ring buffer into the linear external buffer @buf. Copy
80  * at most @size bytes. If the ring buffer size is smaller, copy less bytes and
81  * return the number of bytes copied.
82  */
83 size_t ring_copy(struct ring *r, void *buf, size_t size) {
84         size_t l;
85
86         assert(r);
87         assert(buf);
88
89         if (size > r->used)
90                 size = r->used;
91
92         if (size > 0) {
93                 l = r->size - r->start;
94                 if (size <= l) {
95                         memcpy(buf, &r->buf[r->start], size);
96                 } else {
97                         memcpy(buf, &r->buf[r->start], l);
98                         memcpy((uint8_t*)buf + l, r->buf, size - l);
99                 }
100         }
101
102         return size;
103 }
104
105 /*
106  * Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise
107  * ring operations will behave incorrectly.
108  */
109 static int ring_resize(struct ring *r, size_t nsize) {
110         uint8_t *buf;
111         size_t l;
112
113         assert(r);
114         assert(nsize > 0);
115
116         buf = malloc(nsize);
117         if (!buf)
118                 return -ENOMEM;
119
120         if (r->used > 0) {
121                 l = r->size - r->start;
122                 if (r->used <= l) {
123                         memcpy(buf, &r->buf[r->start], r->used);
124                 } else {
125                         memcpy(buf, &r->buf[r->start], l);
126                         memcpy(&buf[l], r->buf, r->used - l);
127                 }
128         }
129
130         free(r->buf);
131         r->buf = buf;
132         r->size = nsize;
133         r->start = 0;
134
135         return 0;
136 }
137
138 /*
139  * Resize ring-buffer to provide enough room for @add bytes of new data. This
140  * resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on
141  * success.
142  */
143 static int ring_grow(struct ring *r, size_t add) {
144         size_t need;
145
146         assert(r);
147
148         if (r->size - r->used >= add)
149                 return 0;
150
151         need = r->used + add;
152         if (need <= r->used)
153                 return -ENOMEM;
154         else if (need < 4096)
155                 need = 4096;
156
157         need = ALIGN_POWER2(need);
158         if (need == 0)
159                 return -ENOMEM;
160
161         return ring_resize(r, need);
162 }
163
164 /*
165  * Push @len bytes from @u8 into the ring buffer. The buffer is resized if it
166  * is too small. -ENOMEM is returned on OOM, 0 on success.
167  */
168 int ring_push(struct ring *r, const void *u8, size_t size) {
169         int err;
170         size_t pos, l;
171
172         assert(r);
173         assert(u8);
174
175         if (size == 0)
176                 return 0;
177
178         err = ring_grow(r, size);
179         if (err < 0)
180                 return err;
181
182         pos = RING_MASK(r, r->start + r->used);
183         l = r->size - pos;
184         if (l >= size) {
185                 memcpy(&r->buf[pos], u8, size);
186         } else {
187                 memcpy(&r->buf[pos], u8, l);
188                 memcpy(r->buf, (const uint8_t*)u8 + l, size - l);
189         }
190
191         r->used += size;
192
193         return 0;
194 }
195
196 /*
197  * Remove @len bytes from the start of the ring-buffer. Note that we protect
198  * against overflows so removing more bytes than available is safe.
199  */
200 void ring_pull(struct ring *r, size_t size) {
201         assert(r);
202
203         if (size > r->used)
204                 size = r->used;
205
206         r->start = RING_MASK(r, r->start + size);
207         r->used -= size;
208 }