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