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