Line data Source code
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 0 : void ring_flush(Ring *r) {
32 0 : assert(r);
33 :
34 0 : r->start = 0;
35 0 : r->used = 0;
36 0 : }
37 :
38 1001 : void ring_clear(Ring *r) {
39 1001 : assert(r);
40 :
41 1001 : free(r->buf);
42 1001 : zero(*r);
43 1001 : }
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 5182 : size_t ring_peek(Ring *r, struct iovec *vec) {
58 5182 : assert(r);
59 :
60 5182 : if (r->used == 0) {
61 4176 : return 0;
62 1006 : } else if (r->start + r->used <= r->size) {
63 1004 : if (vec) {
64 1004 : vec[0].iov_base = &r->buf[r->start];
65 1004 : vec[0].iov_len = r->used;
66 : }
67 1004 : return 1;
68 : } else {
69 2 : if (vec) {
70 2 : vec[0].iov_base = &r->buf[r->start];
71 2 : vec[0].iov_len = r->size - r->start;
72 2 : vec[1].iov_base = r->buf;
73 2 : vec[1].iov_len = r->used - (r->size - r->start);
74 : }
75 2 : 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 0 : size_t ring_copy(Ring *r, void *buf, size_t size) {
85 : size_t l;
86 :
87 0 : assert(r);
88 0 : assert(buf);
89 :
90 0 : if (size > r->used)
91 0 : size = r->used;
92 :
93 0 : if (size > 0) {
94 0 : l = r->size - r->start;
95 0 : if (size <= l) {
96 0 : memcpy(buf, &r->buf[r->start], size);
97 : } else {
98 0 : memcpy(buf, &r->buf[r->start], l);
99 0 : memcpy((uint8_t*)buf + l, r->buf, size - l);
100 : }
101 : }
102 :
103 0 : 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 1002 : static int ring_resize(Ring *r, size_t nsize) {
111 : uint8_t *buf;
112 : size_t l;
113 :
114 1002 : assert(r);
115 1002 : assert(nsize > 0);
116 :
117 1002 : buf = malloc(nsize);
118 1002 : if (!buf)
119 0 : return -ENOMEM;
120 :
121 1002 : if (r->used > 0) {
122 1 : l = r->size - r->start;
123 1 : if (r->used <= l) {
124 1 : memcpy(buf, &r->buf[r->start], r->used);
125 : } else {
126 0 : memcpy(buf, &r->buf[r->start], l);
127 0 : memcpy(&buf[l], r->buf, r->used - l);
128 : }
129 : }
130 :
131 1002 : free(r->buf);
132 1002 : r->buf = buf;
133 1002 : r->size = nsize;
134 1002 : r->start = 0;
135 :
136 1002 : 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 1006 : static int ring_grow(Ring *r, size_t add) {
145 : size_t need;
146 :
147 1006 : assert(r);
148 :
149 1006 : if (r->size - r->used >= add)
150 4 : return 0;
151 :
152 1002 : need = r->used + add;
153 1002 : if (need <= r->used)
154 0 : return -ENOMEM;
155 1002 : else if (need < 4096)
156 1001 : need = 4096;
157 :
158 1002 : need = ALIGN_POWER2(need);
159 1002 : if (need == 0)
160 0 : return -ENOMEM;
161 :
162 1002 : 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 1006 : int ring_push(Ring *r, const void *u8, size_t size) {
170 : int err;
171 : size_t pos, l;
172 :
173 1006 : assert(r);
174 1006 : assert(u8);
175 :
176 1006 : if (size == 0)
177 0 : return 0;
178 :
179 1006 : err = ring_grow(r, size);
180 1006 : if (err < 0)
181 0 : return err;
182 :
183 1006 : pos = RING_MASK(r, r->start + r->used);
184 1006 : l = r->size - pos;
185 1006 : if (l >= size) {
186 1005 : memcpy(&r->buf[pos], u8, size);
187 : } else {
188 1 : memcpy(&r->buf[pos], u8, l);
189 1 : memcpy(r->buf, (const uint8_t*)u8 + l, size - l);
190 : }
191 :
192 1006 : r->used += size;
193 :
194 1006 : 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 1004 : void ring_pull(Ring *r, size_t size) {
202 1004 : assert(r);
203 :
204 1004 : if (size > r->used)
205 0 : size = r->used;
206 :
207 1004 : r->start = RING_MASK(r, r->start + size);
208 1004 : r->used -= size;
209 1004 : }
|