]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/ring.c
build-sys: split internal basic/ library from shared/
[thirdparty/systemd.git] / src / basic / ring.c
CommitLineData
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 31void ring_flush(Ring *r) {
e0dd9272
DH
32 assert(r);
33
34 r->start = 0;
35 r->used = 0;
36}
37
1ca5fd00
DH
38void 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 57size_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 84size_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 110static 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 144static 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 169int 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 201void 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}