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