xref: /haiku/src/system/kernel/util/ring_buffer.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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 /*! Moves data from one ring buffer to another.
306 
307 	\param to The destination ring buffer.
308 	\param length The maximum number of bytes to move.
309 	\param from The source ring buffer.
310 	\return The number of bytes actually moved.
311 */
312 size_t
313 ring_buffer_move(struct ring_buffer *to, ssize_t length,
314 	struct ring_buffer *from)
315 {
316 	if (length > from->in)
317 		length = from->in;
318 
319 	if (length > (to->size - to->in))
320 		length = to->size - to->in;
321 
322 	size_t bytesMoved = 0;
323 
324 	if ((from->first + length) <= from->size) {
325 		// simple move
326 		bytesMoved = ring_buffer_write(to, from->buffer + from->first, length);
327 	} else {
328 		// need to move both ends
329 		size_t upper = from->size - from->first;
330 		size_t lower = length - upper;
331 
332 		bytesMoved = ring_buffer_write(to, from->buffer + from->first, upper);
333 		if (bytesMoved == upper) {
334 			// only continue writing if the first part was completely written
335 			bytesMoved += ring_buffer_write(to, from->buffer, lower);
336 		}
337 	}
338 
339 	from->first = (from->first + bytesMoved) % from->size;
340 	from->in -= bytesMoved;
341 
342 	return bytesMoved;
343 }
344 
345 
346 #if 0
347 /**	Sends the contents of the ring buffer to a port.
348  *	The buffer will be empty afterwards only if sending the data actually works.
349  */
350 
351 status_t
352 ring_buffer_write_to_port(struct ring_buffer *buffer, port_id port, int32 code,
353 	uint32 flags, bigtime_t timeout)
354 {
355 	int32 length = buffer->in;
356 	if (length == 0)
357 		return B_OK;
358 
359 	status_t status;
360 
361 	if (buffer->first + length <= buffer->size) {
362 		// simple write
363 		status = write_port_etc(port, code, buffer->buffer + buffer->first, length,
364 			flags, timeout);
365 	} else {
366 		// need to write both ends
367 		size_t upper = buffer->size - buffer->first;
368 		size_t lower = length - upper;
369 
370 		iovec vecs[2];
371 		vecs[0].iov_base = buffer->buffer + buffer->first;
372 		vecs[0].iov_len = upper;
373 		vecs[1].iov_base = buffer->buffer;
374 		vecs[1].iov_len = lower;
375 
376 		status = writev_port_etc(port, code, vecs, 2, length, flags, timeout);
377 	}
378 
379 	if (status < B_OK)
380 		return status;
381 
382 	buffer->first = (buffer->first + length) % buffer->size;
383 	buffer->in -= length;
384 
385 	return status;
386 }
387 #endif
388