xref: /haiku/src/system/kernel/util/ring_buffer.cpp (revision 61ce1824dfdea15a581a1e54b92dc06aadf418c5)
1 /*
2  * Copyright 2005-2008, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ring_buffer.h"
8 
9 #include <KernelExport.h>
10 #if 0
11 #include <port.h>
12 #endif
13 
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/uio.h>
17 
18 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
19 #define user_memcpy(x...) (memcpy(x), B_OK)
20 #endif
21 
22 /*!	This is a light-weight ring_buffer implementation.
23  *	It does not provide any locking - you are supposed to ensure thread-safety
24  *	with the restrictions you choose. Unless you are passing in unsafe buffers,
25  *	the functions are safe to be called with interrupts turned off as well (not
26  *	the user functions).
27  *	They also don't use malloc() or any kind of locking after initialization.
28  */
29 
30 
31 static inline int32
32 space_left_in_buffer(struct ring_buffer *buffer)
33 {
34 	return buffer->size - buffer->in;
35 }
36 
37 
38 static ssize_t
39 read_from_buffer(struct ring_buffer *buffer, uint8 *data, ssize_t length,
40 	bool user)
41 {
42 	int32 available = buffer->in;
43 
44 	if (length > available)
45 		length = available;
46 
47 	if (length == 0)
48 		return 0;
49 
50 	ssize_t bytesRead = length;
51 
52 	if (buffer->first + length <= buffer->size) {
53 		// simple copy
54 		if (user) {
55 			if (user_memcpy(data, buffer->buffer + buffer->first, length) < B_OK)
56 				return B_BAD_ADDRESS;
57 		} else
58 			memcpy(data, buffer->buffer + buffer->first, length);
59 	} else {
60 		// need to copy both ends
61 		size_t upper = buffer->size - buffer->first;
62 		size_t lower = length - upper;
63 
64 		if (user) {
65 			if (user_memcpy(data, buffer->buffer + buffer->first, upper) < B_OK
66 				|| user_memcpy(data + upper, buffer->buffer, lower) < B_OK)
67 				return B_BAD_ADDRESS;
68 		} else {
69 			memcpy(data, buffer->buffer + buffer->first, upper);
70 			memcpy(data + upper, buffer->buffer, lower);
71 		}
72 	}
73 
74 	buffer->first = (buffer->first + bytesRead) % buffer->size;
75 	buffer->in -= bytesRead;
76 
77 	return bytesRead;
78 }
79 
80 
81 static ssize_t
82 write_to_buffer(struct ring_buffer *buffer, const uint8 *data, ssize_t length,
83 	bool user)
84 {
85 	int32 left = space_left_in_buffer(buffer);
86 	if (length > left)
87 		length = left;
88 
89 	if (length == 0)
90 		return 0;
91 
92 	ssize_t bytesWritten = length;
93 	int32 position = (buffer->first + buffer->in) % buffer->size;
94 
95 	if (position + length <= buffer->size) {
96 		// simple copy
97 		if (user) {
98 			if (user_memcpy(buffer->buffer + position, data, length) < B_OK)
99 				return B_BAD_ADDRESS;
100 		} else
101 			memcpy(buffer->buffer + position, data, length);
102 	} else {
103 		// need to copy both ends
104 		size_t upper = buffer->size - position;
105 		size_t lower = length - upper;
106 
107 		if (user) {
108 			if (user_memcpy(buffer->buffer + position, data, upper) < B_OK
109 				|| user_memcpy(buffer->buffer, data + upper, lower) < B_OK)
110 				return B_BAD_ADDRESS;
111 		} else {
112 			memcpy(buffer->buffer + position, data, upper);
113 			memcpy(buffer->buffer, data + upper, lower);
114 		}
115 	}
116 
117 	buffer->in += bytesWritten;
118 
119 	return bytesWritten;
120 }
121 
122 
123 //	#pragma mark -
124 
125 
126 struct ring_buffer*
127 create_ring_buffer(size_t size)
128 {
129 	return create_ring_buffer_etc(NULL, size, 0);
130 }
131 
132 
133 struct ring_buffer*
134 create_ring_buffer_etc(void* memory, size_t size, uint32 flags)
135 {
136 	if (memory == NULL) {
137 		ring_buffer* buffer = (ring_buffer*)malloc(sizeof(ring_buffer) + size);
138 		if (buffer == NULL)
139 			return NULL;
140 
141 		buffer->size = size;
142 		ring_buffer_clear(buffer);
143 
144 		return buffer;
145 	}
146 
147 	size -= sizeof(ring_buffer);
148 	ring_buffer* buffer = (ring_buffer*)memory;
149 
150 	buffer->size = size;
151 	if ((flags & RING_BUFFER_INIT_FROM_BUFFER) != 0
152 		&& (size_t)buffer->size == size
153 		&& buffer->in >= 0 && (size_t)buffer->in <= size
154 		&& buffer->first >= 0 && (size_t)buffer->first < size) {
155 		// structure looks valid
156 	} else
157 		ring_buffer_clear(buffer);
158 
159 	return buffer;
160 }
161 
162 
163 void
164 delete_ring_buffer(struct ring_buffer *buffer)
165 {
166 	free(buffer);
167 }
168 
169 
170 void
171 ring_buffer_clear(struct ring_buffer *buffer)
172 {
173 	buffer->in = 0;
174 	buffer->first = 0;
175 }
176 
177 
178 size_t
179 ring_buffer_readable(struct ring_buffer *buffer)
180 {
181 	return buffer->in;
182 }
183 
184 
185 size_t
186 ring_buffer_writable(struct ring_buffer *buffer)
187 {
188 	return buffer->size - buffer->in;
189 }
190 
191 
192 void
193 ring_buffer_flush(struct ring_buffer *buffer, size_t length)
194 {
195 	// we can't flush more bytes than there are
196 	if (length > (size_t)buffer->in)
197 		length = buffer->in;
198 
199 	buffer->in -= length;
200 	buffer->first = (buffer->first + length) % buffer->size;
201 }
202 
203 
204 size_t
205 ring_buffer_read(struct ring_buffer *buffer, uint8 *data, ssize_t length)
206 {
207 	return read_from_buffer(buffer, data, length, false);
208 }
209 
210 
211 size_t
212 ring_buffer_write(struct ring_buffer *buffer, const uint8 *data, ssize_t length)
213 {
214 	return write_to_buffer(buffer, data, length, false);
215 }
216 
217 
218 ssize_t
219 ring_buffer_user_read(struct ring_buffer *buffer, uint8 *data, ssize_t length)
220 {
221 	return read_from_buffer(buffer, data, length, true);
222 }
223 
224 
225 ssize_t
226 ring_buffer_user_write(struct ring_buffer *buffer, const uint8 *data, ssize_t length)
227 {
228 	return write_to_buffer(buffer, data, length, true);
229 }
230 
231 
232 /*!	Reads data from the ring buffer, but doesn't remove the data from it.
233 	\param buffer The ring buffer.
234 	\param offset The offset relative to the beginning of the data in the ring
235 		buffer at which to start reading.
236 	\param data The buffer to which to copy the data.
237 	\param length The number of bytes to read at maximum.
238 	\return The number of bytes actually read from the buffer.
239 */
240 size_t
241 ring_buffer_peek(struct ring_buffer* buffer, size_t offset, void* data,
242 	size_t length)
243 {
244 	size_t available = buffer->in;
245 
246 	if (offset >= available || length == 0)
247 		return 0;
248 
249 	if (offset + length > available)
250 		length = available - offset;
251 
252 	if ((offset += buffer->first) >= (size_t)buffer->size)
253 		offset -= buffer->size;
254 
255 	if (offset + length <= (size_t)buffer->size) {
256 		// simple copy
257 		memcpy(data, buffer->buffer + offset, length);
258 	} else {
259 		// need to copy both ends
260 		size_t upper = buffer->size - offset;
261 		size_t lower = length - upper;
262 
263 		memcpy(data, buffer->buffer + offset, upper);
264 		memcpy((uint8*)data + upper, buffer->buffer, lower);
265 	}
266 
267 	return length;
268 }
269 
270 
271 /*!	Returns iovecs describing the contents of the ring buffer.
272 
273 	\param buffer The ring buffer.
274 	\param vecs Pointer to an iovec array with at least 2 elements to be filled
275 		in by the function.
276 	\return The number of iovecs the function has filled in to describe the
277 		contents of the ring buffer. \c 0, if empty, \c 2 at maximum.
278 */
279 int32
280 ring_buffer_get_vecs(struct ring_buffer* buffer, struct iovec* vecs)
281 {
282 	if (buffer->in == 0)
283 		return 0;
284 
285 	if (buffer->first + buffer->in <= buffer->size) {
286 		// one element
287 		vecs[0].iov_base = buffer->buffer + buffer->first;
288 		vecs[0].iov_len = buffer->in;
289 		return 1;
290 	}
291 
292 	// two elements
293 	size_t upper = buffer->size - buffer->first;
294 	size_t lower = buffer->in - upper;
295 
296 	vecs[0].iov_base = buffer->buffer + buffer->first;
297 	vecs[0].iov_len = upper;
298 	vecs[1].iov_base = buffer->buffer;
299 	vecs[1].iov_len = lower;
300 
301 	return 2;
302 }
303 
304 
305 #if 0
306 /**	Sends the contents of the ring buffer to a port.
307  *	The buffer will be empty afterwards only if sending the data actually works.
308  */
309 
310 status_t
311 ring_buffer_write_to_port(struct ring_buffer *buffer, port_id port, int32 code,
312 	uint32 flags, bigtime_t timeout)
313 {
314 	int32 length = buffer->in;
315 	if (length == 0)
316 		return B_OK;
317 
318 	status_t status;
319 
320 	if (buffer->first + length <= buffer->size) {
321 		// simple write
322 		status = write_port_etc(port, code, buffer->buffer + buffer->first, length,
323 			flags, timeout);
324 	} else {
325 		// need to write both ends
326 		size_t upper = buffer->size - buffer->first;
327 		size_t lower = length - upper;
328 
329 		iovec vecs[2];
330 		vecs[0].iov_base = buffer->buffer + buffer->first;
331 		vecs[0].iov_len = upper;
332 		vecs[1].iov_base = buffer->buffer;
333 		vecs[1].iov_len = lower;
334 
335 		status = writev_port_etc(port, code, vecs, 2, length, flags, timeout);
336 	}
337 
338 	if (status < B_OK)
339 		return status;
340 
341 	buffer->first = (buffer->first + length) % buffer->size;
342 	buffer->in -= length;
343 
344 	return status;
345 }
346 #endif
347