1 /* 2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 // A ring buffer to hold arbitrary data. Provides no thread safety. Unless 12 // otherwise specified, functions return 0 on success and -1 on error. 13 14 #include "ring_buffer.h" 15 16 #include <stddef.h> // size_t 17 #include <stdlib.h> 18 #include <string.h> 19 20 enum Wrap { 21 SAME_WRAP, 22 DIFF_WRAP 23 }; 24 25 typedef struct { 26 size_t read_pos; 27 size_t write_pos; 28 size_t element_count; 29 size_t element_size; 30 enum Wrap rw_wrap; 31 char* data; 32 } buf_t; 33 34 // Get address of region(s) from which we can read data. 35 // If the region is contiguous, |data_ptr_bytes_2| will be zero. 36 // If non-contiguous, |data_ptr_bytes_2| will be the size in bytes of the second 37 // region. Returns room available to be read or |element_count|, whichever is 38 // smaller. 39 static size_t GetBufferReadRegions(buf_t* buf, 40 size_t element_count, 41 void** data_ptr_1, 42 size_t* data_ptr_bytes_1, 43 void** data_ptr_2, 44 size_t* data_ptr_bytes_2) { 45 46 const size_t readable_elements = WebRtc_available_read(buf); 47 const size_t read_elements = (readable_elements < element_count ? 48 readable_elements : element_count); 49 const size_t margin = buf->element_count - buf->read_pos; 50 51 // Check to see if read is not contiguous. 52 if (read_elements > margin) { 53 // Write data in two blocks that wrap the buffer. 54 *data_ptr_1 = buf->data + buf->read_pos * buf->element_size; 55 *data_ptr_bytes_1 = margin * buf->element_size; 56 *data_ptr_2 = buf->data; 57 *data_ptr_bytes_2 = (read_elements - margin) * buf->element_size; 58 } else { 59 *data_ptr_1 = buf->data + buf->read_pos * buf->element_size; 60 *data_ptr_bytes_1 = read_elements * buf->element_size; 61 *data_ptr_2 = NULL; 62 *data_ptr_bytes_2 = 0; 63 } 64 65 return read_elements; 66 } 67 68 int WebRtc_CreateBuffer(void** handle, 69 size_t element_count, 70 size_t element_size) { 71 buf_t* self = NULL; 72 73 if (handle == NULL) { 74 return -1; 75 } 76 77 self = malloc(sizeof(buf_t)); 78 if (self == NULL) { 79 return -1; 80 } 81 *handle = self; 82 83 self->data = malloc(element_count * element_size); 84 if (self->data == NULL) { 85 free(self); 86 self = NULL; 87 return -1; 88 } 89 90 self->element_count = element_count; 91 self->element_size = element_size; 92 93 return 0; 94 } 95 96 int WebRtc_InitBuffer(void* handle) { 97 buf_t* self = (buf_t*) handle; 98 99 if (self == NULL) { 100 return -1; 101 } 102 103 self->read_pos = 0; 104 self->write_pos = 0; 105 self->rw_wrap = SAME_WRAP; 106 107 // Initialize buffer to zeros 108 memset(self->data, 0, self->element_count * self->element_size); 109 110 return 0; 111 } 112 113 int WebRtc_FreeBuffer(void* handle) { 114 buf_t* self = (buf_t*) handle; 115 116 if (self == NULL) { 117 return -1; 118 } 119 120 free(self->data); 121 free(self); 122 123 return 0; 124 } 125 126 size_t WebRtc_ReadBuffer(void* handle, 127 void** data_ptr, 128 void* data, 129 size_t element_count) { 130 131 buf_t* self = (buf_t*) handle; 132 133 if (self == NULL) { 134 return 0; 135 } 136 if (data == NULL) { 137 return 0; 138 } 139 if (data_ptr == NULL) { 140 return 0; 141 } 142 143 { 144 void* buf_ptr_1 = NULL; 145 void* buf_ptr_2 = NULL; 146 size_t buf_ptr_bytes_1 = 0; 147 size_t buf_ptr_bytes_2 = 0; 148 const size_t read_count = GetBufferReadRegions(self, 149 element_count, 150 &buf_ptr_1, 151 &buf_ptr_bytes_1, 152 &buf_ptr_2, 153 &buf_ptr_bytes_2); 154 155 if (buf_ptr_bytes_2 > 0) { 156 // We have a wrap around when reading the buffer. Copy the buffer data to 157 // |data| and point to it. 158 memcpy(data, buf_ptr_1, buf_ptr_bytes_1); 159 memcpy(((char*) data) + buf_ptr_bytes_1, buf_ptr_2, buf_ptr_bytes_2); 160 *data_ptr = data; 161 } else { 162 *data_ptr = buf_ptr_1; 163 } 164 165 // Update read position 166 WebRtc_MoveReadPtr(handle, (int) read_count); 167 168 return read_count; 169 } 170 } 171 172 size_t WebRtc_WriteBuffer(void* handle, 173 const void* data, 174 size_t element_count) { 175 176 buf_t* self = (buf_t*) handle; 177 178 if (self == NULL) { 179 return 0; 180 } 181 if (data == NULL) { 182 return 0; 183 } 184 185 { 186 const size_t free_elements = WebRtc_available_write(handle); 187 const size_t write_elements = (free_elements < element_count ? free_elements 188 : element_count); 189 size_t n = write_elements; 190 const size_t margin = self->element_count - self->write_pos; 191 192 if (write_elements > margin) { 193 // Buffer wrap around when writing. 194 memcpy(self->data + self->write_pos * self->element_size, 195 data, margin * self->element_size); 196 self->write_pos = 0; 197 n -= margin; 198 self->rw_wrap = DIFF_WRAP; 199 } 200 memcpy(self->data + self->write_pos * self->element_size, 201 ((const char*) data) + ((write_elements - n) * self->element_size), 202 n * self->element_size); 203 self->write_pos += n; 204 205 return write_elements; 206 } 207 } 208 209 int WebRtc_MoveReadPtr(void* handle, int element_count) { 210 211 buf_t* self = (buf_t*) handle; 212 213 if (self == NULL) { 214 return 0; 215 } 216 217 { 218 // We need to be able to take care of negative changes, hence use "int" 219 // instead of "size_t". 220 const int free_elements = (int) WebRtc_available_write(handle); 221 const int readable_elements = (int) WebRtc_available_read(handle); 222 int read_pos = (int) self->read_pos; 223 224 if (element_count > readable_elements) { 225 element_count = readable_elements; 226 } 227 if (element_count < -free_elements) { 228 element_count = -free_elements; 229 } 230 231 read_pos += element_count; 232 if (read_pos > (int) self->element_count) { 233 // Buffer wrap around. Restart read position and wrap indicator. 234 read_pos -= (int) self->element_count; 235 self->rw_wrap = SAME_WRAP; 236 } 237 if (read_pos < 0) { 238 // Buffer wrap around. Restart read position and wrap indicator. 239 read_pos += (int) self->element_count; 240 self->rw_wrap = DIFF_WRAP; 241 } 242 243 self->read_pos = (size_t) read_pos; 244 245 return element_count; 246 } 247 } 248 249 size_t WebRtc_available_read(const void* handle) { 250 const buf_t* self = (buf_t*) handle; 251 252 if (self == NULL) { 253 return 0; 254 } 255 256 if (self->rw_wrap == SAME_WRAP) { 257 return self->write_pos - self->read_pos; 258 } else { 259 return self->element_count - self->read_pos + self->write_pos; 260 } 261 } 262 263 size_t WebRtc_available_write(const void* handle) { 264 const buf_t* self = (buf_t*) handle; 265 266 if (self == NULL) { 267 return 0; 268 } 269 270 return self->element_count - WebRtc_available_read(handle); 271 } 272