xref: /haiku/src/system/kernel/util/ring_buffer.cpp (revision 6a6c43798f9205239189404501b7bc4afd3283bf)
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