]>
Commit | Line | Data |
---|---|---|
e0dd9272 DH |
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> | |
e0dd9272 DH |
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 | ||
1ca5fd00 | 31 | void ring_flush(Ring *r) { |
e0dd9272 DH |
32 | assert(r); |
33 | ||
34 | r->start = 0; | |
35 | r->used = 0; | |
36 | } | |
37 | ||
1ca5fd00 DH |
38 | void ring_clear(Ring *r) { |
39 | assert(r); | |
40 | ||
e0dd9272 DH |
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 | */ | |
1ca5fd00 | 57 | size_t ring_peek(Ring *r, struct iovec *vec) { |
e0dd9272 DH |
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 | */ | |
1ca5fd00 | 84 | size_t ring_copy(Ring *r, void *buf, size_t size) { |
e0dd9272 DH |
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 | */ | |
1ca5fd00 | 110 | static int ring_resize(Ring *r, size_t nsize) { |
e0dd9272 DH |
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 | */ | |
1ca5fd00 | 144 | static int ring_grow(Ring *r, size_t add) { |
e0dd9272 DH |
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 | */ | |
1ca5fd00 | 169 | int ring_push(Ring *r, const void *u8, size_t size) { |
e0dd9272 DH |
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 | */ | |
1ca5fd00 | 201 | void ring_pull(Ring *r, size_t size) { |
e0dd9272 DH |
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 | } |