ring_buffer.c (6617B)
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 "common_audio/ring_buffer.h" 15 16 #include <stddef.h> // size_t 17 #include <stdlib.h> 18 #include <string.h> 19 20 // Get address of region(s) from which we can read data. 21 // If the region is contiguous, `data_ptr_bytes_2` will be zero. 22 // If non-contiguous, `data_ptr_bytes_2` will be the size in bytes of the second 23 // region. Returns room available to be read or `element_count`, whichever is 24 // smaller. 25 static size_t GetBufferReadRegions(RingBuffer* buf, 26 size_t element_count, 27 void** data_ptr_1, 28 size_t* data_ptr_bytes_1, 29 void** data_ptr_2, 30 size_t* data_ptr_bytes_2) { 31 const size_t readable_elements = WebRtc_available_read(buf); 32 const size_t read_elements = 33 (readable_elements < element_count ? readable_elements : element_count); 34 const size_t margin = buf->element_count - buf->read_pos; 35 36 // Check to see if read is not contiguous. 37 if (read_elements > margin) { 38 // Write data in two blocks that wrap the buffer. 39 *data_ptr_1 = buf->data + buf->read_pos * buf->element_size; 40 *data_ptr_bytes_1 = margin * buf->element_size; 41 *data_ptr_2 = buf->data; 42 *data_ptr_bytes_2 = (read_elements - margin) * buf->element_size; 43 } else { 44 *data_ptr_1 = buf->data + buf->read_pos * buf->element_size; 45 *data_ptr_bytes_1 = read_elements * buf->element_size; 46 *data_ptr_2 = NULL; 47 *data_ptr_bytes_2 = 0; 48 } 49 50 return read_elements; 51 } 52 53 RingBuffer* WebRtc_CreateBuffer(size_t element_count, size_t element_size) { 54 RingBuffer* self = NULL; 55 if (element_count == 0 || element_size == 0) { 56 return NULL; 57 } 58 59 self = malloc(sizeof(RingBuffer)); 60 if (!self) { 61 return NULL; 62 } 63 64 self->data = malloc(element_count * element_size); 65 if (!self->data) { 66 free(self); 67 self = NULL; 68 return NULL; 69 } 70 71 self->element_count = element_count; 72 self->element_size = element_size; 73 WebRtc_InitBuffer(self); 74 75 return self; 76 } 77 78 void WebRtc_InitBuffer(RingBuffer* self) { 79 self->read_pos = 0; 80 self->write_pos = 0; 81 self->rw_wrap = SAME_WRAP; 82 83 // Initialize buffer to zeros 84 memset(self->data, 0, self->element_count * self->element_size); 85 } 86 87 void WebRtc_FreeBuffer(void* handle) { 88 RingBuffer* self = (RingBuffer*)handle; 89 if (!self) { 90 return; 91 } 92 93 free(self->data); 94 free(self); 95 } 96 97 size_t WebRtc_ReadBuffer(RingBuffer* self, 98 void** data_ptr, 99 void* data, 100 size_t element_count) { 101 if (self == NULL) { 102 return 0; 103 } 104 if (data == NULL) { 105 return 0; 106 } 107 108 { 109 void* buf_ptr_1 = NULL; 110 void* buf_ptr_2 = NULL; 111 size_t buf_ptr_bytes_1 = 0; 112 size_t buf_ptr_bytes_2 = 0; 113 const size_t read_count = 114 GetBufferReadRegions(self, element_count, &buf_ptr_1, &buf_ptr_bytes_1, 115 &buf_ptr_2, &buf_ptr_bytes_2); 116 if (buf_ptr_bytes_2 > 0) { 117 // We have a wrap around when reading the buffer. Copy the buffer data to 118 // `data` and point to it. 119 memcpy(data, buf_ptr_1, buf_ptr_bytes_1); 120 memcpy(((char*)data) + buf_ptr_bytes_1, buf_ptr_2, buf_ptr_bytes_2); 121 buf_ptr_1 = data; 122 } else if (!data_ptr) { 123 // No wrap, but a memcpy was requested. 124 memcpy(data, buf_ptr_1, buf_ptr_bytes_1); 125 } 126 if (data_ptr) { 127 // `buf_ptr_1` == `data` in the case of a wrap. 128 *data_ptr = read_count == 0 ? NULL : buf_ptr_1; 129 } 130 131 // Update read position 132 WebRtc_MoveReadPtr(self, (int)read_count); 133 134 return read_count; 135 } 136 } 137 138 size_t WebRtc_WriteBuffer(RingBuffer* self, 139 const void* data, 140 size_t element_count) { 141 if (!self) { 142 return 0; 143 } 144 if (!data) { 145 return 0; 146 } 147 148 { 149 const size_t free_elements = WebRtc_available_write(self); 150 const size_t write_elements = 151 (free_elements < element_count ? free_elements : element_count); 152 size_t n = write_elements; 153 const size_t margin = self->element_count - self->write_pos; 154 155 if (write_elements > margin) { 156 // Buffer wrap around when writing. 157 memcpy(self->data + self->write_pos * self->element_size, data, 158 margin * self->element_size); 159 self->write_pos = 0; 160 n -= margin; 161 self->rw_wrap = DIFF_WRAP; 162 } 163 memcpy(self->data + self->write_pos * self->element_size, 164 ((const char*)data) + ((write_elements - n) * self->element_size), 165 n * self->element_size); 166 self->write_pos += n; 167 168 return write_elements; 169 } 170 } 171 172 int WebRtc_MoveReadPtr(RingBuffer* self, int element_count) { 173 if (!self) { 174 return 0; 175 } 176 177 { 178 // We need to be able to take care of negative changes, hence use "int" 179 // instead of "size_t". 180 const int free_elements = (int)WebRtc_available_write(self); 181 const int readable_elements = (int)WebRtc_available_read(self); 182 int read_pos = (int)self->read_pos; 183 184 if (element_count > readable_elements) { 185 element_count = readable_elements; 186 } 187 if (element_count < -free_elements) { 188 element_count = -free_elements; 189 } 190 191 read_pos += element_count; 192 if (read_pos > (int)self->element_count) { 193 // Buffer wrap around. Restart read position and wrap indicator. 194 read_pos -= (int)self->element_count; 195 self->rw_wrap = SAME_WRAP; 196 } 197 if (read_pos < 0) { 198 // Buffer wrap around. Restart read position and wrap indicator. 199 read_pos += (int)self->element_count; 200 self->rw_wrap = DIFF_WRAP; 201 } 202 203 self->read_pos = (size_t)read_pos; 204 205 return element_count; 206 } 207 } 208 209 size_t WebRtc_available_read(const RingBuffer* self) { 210 if (!self) { 211 return 0; 212 } 213 214 if (self->rw_wrap == SAME_WRAP) { 215 return self->write_pos - self->read_pos; 216 } else { 217 return self->element_count - self->read_pos + self->write_pos; 218 } 219 } 220 221 size_t WebRtc_available_write(const RingBuffer* self) { 222 if (!self) { 223 return 0; 224 } 225 226 return self->element_count - WebRtc_available_read(self); 227 }