xref: /haiku/src/system/kernel/util/ring_buffer.cpp (revision 0b46f622d157d4313672dcd6220fbec0749e268b)
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 	struct ring_buffer *buffer = (ring_buffer *)malloc(sizeof(ring_buffer) + size);
129 	if (buffer == NULL)
130 		return NULL;
131 
132 	buffer->size = size;
133 	ring_buffer_clear(buffer);
134 
135 	return buffer;
136 }
137 
138 
139 void
140 delete_ring_buffer(struct ring_buffer *buffer)
141 {
142 	free(buffer);
143 }
144 
145 
146 void
147 ring_buffer_clear(struct ring_buffer *buffer)
148 {
149 	buffer->in = 0;
150 	buffer->first = 0;
151 }
152 
153 
154 size_t
155 ring_buffer_readable(struct ring_buffer *buffer)
156 {
157 	return buffer->in;
158 }
159 
160 
161 size_t
162 ring_buffer_writable(struct ring_buffer *buffer)
163 {
164 	return buffer->size - buffer->in;
165 }
166 
167 
168 void
169 ring_buffer_flush(struct ring_buffer *buffer, size_t length)
170 {
171 	// we can't flush more bytes than there are
172 	if (length > (size_t)buffer->in)
173 		length = buffer->in;
174 
175 	buffer->in -= length;
176 	buffer->first = (buffer->first + length) % buffer->size;
177 }
178 
179 
180 size_t
181 ring_buffer_read(struct ring_buffer *buffer, uint8 *data, ssize_t length)
182 {
183 	return read_from_buffer(buffer, data, length, false);
184 }
185 
186 
187 size_t
188 ring_buffer_write(struct ring_buffer *buffer, const uint8 *data, ssize_t length)
189 {
190 	return write_to_buffer(buffer, data, length, false);
191 }
192 
193 
194 ssize_t
195 ring_buffer_user_read(struct ring_buffer *buffer, uint8 *data, ssize_t length)
196 {
197 	return read_from_buffer(buffer, data, length, true);
198 }
199 
200 
201 ssize_t
202 ring_buffer_user_write(struct ring_buffer *buffer, const uint8 *data, ssize_t length)
203 {
204 	return write_to_buffer(buffer, data, length, true);
205 }
206 
207 
208 #if 0
209 /**	Sends the contents of the ring buffer to a port.
210  *	The buffer will be empty afterwards only if sending the data actually works.
211  */
212 
213 status_t
214 ring_buffer_write_to_port(struct ring_buffer *buffer, port_id port, int32 code,
215 	uint32 flags, bigtime_t timeout)
216 {
217 	int32 length = buffer->in;
218 	if (length == 0)
219 		return B_OK;
220 
221 	status_t status;
222 
223 	if (buffer->first + length <= buffer->size) {
224 		// simple write
225 		status = write_port_etc(port, code, buffer->buffer + buffer->first, length,
226 			flags, timeout);
227 	} else {
228 		// need to write both ends
229 		size_t upper = buffer->size - buffer->first;
230 		size_t lower = length - upper;
231 
232 		iovec vecs[2];
233 		vecs[0].iov_base = buffer->buffer + buffer->first;
234 		vecs[0].iov_len = upper;
235 		vecs[1].iov_base = buffer->buffer;
236 		vecs[1].iov_len = lower;
237 
238 		status = writev_port_etc(port, code, vecs, 2, length, flags, timeout);
239 	}
240 
241 	if (status < B_OK)
242 		return status;
243 
244 	buffer->first = (buffer->first + length) % buffer->size;
245 	buffer->in -= length;
246 
247 	return status;
248 }
249 #endif
250