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