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