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
20enum Wrap {
21  SAME_WRAP,
22  DIFF_WRAP
23};
24
25typedef 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.
39static 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
68int 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
96int 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
113int 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
126size_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
172size_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
209int 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
249size_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
263size_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