xref: /haiku/src/system/kernel/util/ring_buffer.cpp (revision 3cb015b1ee509d69c643506e8ff573808c86dcfc)
1 /*
2  * Copyright 2005-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
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 struct ring_buffer {
30 	int32		first;
31 	int32		in;
32 	int32		size;
33 	uint8		buffer[0];
34 };
35 
36 
37 static inline int32
38 space_left_in_buffer(struct ring_buffer *buffer)
39 {
40 	return buffer->size - buffer->in;
41 }
42 
43 
44 static ssize_t
45 read_from_buffer(struct ring_buffer *buffer, uint8 *data, ssize_t length,
46 	bool user)
47 {
48 	int32 available = buffer->in;
49 
50 	if (length > available)
51 		length = available;
52 
53 	if (length == 0)
54 		return 0;
55 
56 	ssize_t bytesRead = length;
57 
58 	if (buffer->first + length <= buffer->size) {
59 		// simple copy
60 		if (user) {
61 			if (user_memcpy(data, buffer->buffer + buffer->first, length) < B_OK)
62 				return B_BAD_ADDRESS;
63 		} else
64 			memcpy(data, buffer->buffer + buffer->first, length);
65 	} else {
66 		// need to copy both ends
67 		size_t upper = buffer->size - buffer->first;
68 		size_t lower = length - upper;
69 
70 		if (user) {
71 			if (user_memcpy(data, buffer->buffer + buffer->first, upper) < B_OK
72 				|| user_memcpy(data + upper, buffer->buffer, lower) < B_OK)
73 				return B_BAD_ADDRESS;
74 		} else {
75 			memcpy(data, buffer->buffer + buffer->first, upper);
76 			memcpy(data + upper, buffer->buffer, lower);
77 		}
78 	}
79 
80 	buffer->first = (buffer->first + bytesRead) % buffer->size;
81 	buffer->in -= bytesRead;
82 
83 	return bytesRead;
84 }
85 
86 
87 static ssize_t
88 write_to_buffer(struct ring_buffer *buffer, const uint8 *data, ssize_t length,
89 	bool user)
90 {
91 	int32 left = space_left_in_buffer(buffer);
92 	if (length > left)
93 		length = left;
94 
95 	if (length == 0)
96 		return 0;
97 
98 	ssize_t bytesWritten = length;
99 	int32 position = (buffer->first + buffer->in) % buffer->size;
100 
101 	if (position + length <= buffer->size) {
102 		// simple copy
103 		if (user) {
104 			if (user_memcpy(buffer->buffer + position, data, length) < B_OK)
105 				return B_BAD_ADDRESS;
106 		} else
107 			memcpy(buffer->buffer + position, data, length);
108 	} else {
109 		// need to copy both ends
110 		size_t upper = buffer->size - position;
111 		size_t lower = length - upper;
112 
113 		if (user) {
114 			if (user_memcpy(buffer->buffer + position, data, upper) < B_OK
115 				|| user_memcpy(buffer->buffer, data + upper, lower) < B_OK)
116 				return B_BAD_ADDRESS;
117 		} else {
118 			memcpy(buffer->buffer + position, data, upper);
119 			memcpy(buffer->buffer, data + upper, lower);
120 		}
121 	}
122 
123 	buffer->in += bytesWritten;
124 
125 	return bytesWritten;
126 }
127 
128 
129 //	#pragma mark -
130 
131 
132 struct ring_buffer *
133 create_ring_buffer(size_t size)
134 {
135 	struct ring_buffer *buffer = (ring_buffer *)malloc(sizeof(ring_buffer) + size);
136 	if (buffer == NULL)
137 		return NULL;
138 
139 	buffer->size = size;
140 	ring_buffer_clear(buffer);
141 
142 	return buffer;
143 }
144 
145 
146 void
147 delete_ring_buffer(struct ring_buffer *buffer)
148 {
149 	free(buffer);
150 }
151 
152 
153 void
154 ring_buffer_clear(struct ring_buffer *buffer)
155 {
156 	buffer->in = 0;
157 	buffer->first = 0;
158 }
159 
160 
161 size_t
162 ring_buffer_readable(struct ring_buffer *buffer)
163 {
164 	return buffer->in;
165 }
166 
167 
168 size_t
169 ring_buffer_writable(struct ring_buffer *buffer)
170 {
171 	return buffer->size - buffer->in;
172 }
173 
174 
175 void
176 ring_buffer_flush(struct ring_buffer *buffer, size_t length)
177 {
178 	// we can't flush more bytes than there are
179 	if (length > (size_t)buffer->in)
180 		length = buffer->in;
181 
182 	buffer->in -= length;
183 	buffer->first = (buffer->first + length) % buffer->size;
184 }
185 
186 
187 size_t
188 ring_buffer_read(struct ring_buffer *buffer, uint8 *data, ssize_t length)
189 {
190 	return read_from_buffer(buffer, data, length, false);
191 }
192 
193 
194 size_t
195 ring_buffer_write(struct ring_buffer *buffer, const uint8 *data, ssize_t length)
196 {
197 	return write_to_buffer(buffer, data, length, false);
198 }
199 
200 
201 ssize_t
202 ring_buffer_user_read(struct ring_buffer *buffer, uint8 *data, ssize_t length)
203 {
204 	return read_from_buffer(buffer, data, length, true);
205 }
206 
207 
208 ssize_t
209 ring_buffer_user_write(struct ring_buffer *buffer, const uint8 *data, ssize_t length)
210 {
211 	return write_to_buffer(buffer, data, length, true);
212 }
213 
214 
215 #if 0
216 /**	Sends the contents of the ring buffer to a port.
217  *	The buffer will be empty afterwards only if sending the data actually works.
218  */
219 
220 status_t
221 ring_buffer_write_to_port(struct ring_buffer *buffer, port_id port, int32 code,
222 	uint32 flags, bigtime_t timeout)
223 {
224 	int32 length = buffer->in;
225 	if (length == 0)
226 		return B_OK;
227 
228 	status_t status;
229 
230 	if (buffer->first + length <= buffer->size) {
231 		// simple write
232 		status = write_port_etc(port, code, buffer->buffer + buffer->first, length,
233 			flags, timeout);
234 	} else {
235 		// need to write both ends
236 		size_t upper = buffer->size - buffer->first;
237 		size_t lower = length - upper;
238 
239 		iovec vecs[2];
240 		vecs[0].iov_base = buffer->buffer + buffer->first;
241 		vecs[0].iov_len = upper;
242 		vecs[1].iov_base = buffer->buffer;
243 		vecs[1].iov_len = lower;
244 
245 		status = writev_port_etc(port, code, vecs, 2, length, flags, timeout);
246 	}
247 
248 	if (status < B_OK)
249 		return status;
250 
251 	buffer->first = (buffer->first + length) % buffer->size;
252 	buffer->in -= length;
253 
254 	return status;
255 }
256 #endif
257