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
31static inline int32
32space_left_in_buffer(struct ring_buffer *buffer)
33{
34	return buffer->size - buffer->in;
35}
36
37
38static ssize_t
39read_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
81static ssize_t
82write_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
126struct ring_buffer*
127create_ring_buffer(size_t size)
128{
129	return create_ring_buffer_etc(NULL, size, 0);
130}
131
132
133struct ring_buffer*
134create_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
163void
164delete_ring_buffer(struct ring_buffer *buffer)
165{
166	free(buffer);
167}
168
169
170void
171ring_buffer_clear(struct ring_buffer *buffer)
172{
173	buffer->in = 0;
174	buffer->first = 0;
175}
176
177
178size_t
179ring_buffer_readable(struct ring_buffer *buffer)
180{
181	return buffer->in;
182}
183
184
185size_t
186ring_buffer_writable(struct ring_buffer *buffer)
187{
188	return buffer->size - buffer->in;
189}
190
191
192void
193ring_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
204size_t
205ring_buffer_read(struct ring_buffer *buffer, uint8 *data, ssize_t length)
206{
207	return read_from_buffer(buffer, data, length, false);
208}
209
210
211size_t
212ring_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
218ssize_t
219ring_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
225ssize_t
226ring_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*/
240size_t
241ring_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*/
279int32
280ring_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*/
312size_t
313ring_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
351status_t
352ring_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