1/******************************************************************************
2 *
3 *  Copyright (C) 2017 Google Inc.
4 *
5 *  Licensed under the Apache License, Version 2.0 (the "License");
6 *  you may not use this file except in compliance with the License.
7 *  You may obtain a copy of the License at:
8 *
9 *  http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 ******************************************************************************/
18
19#include <assert.h>
20#include <stdlib.h>
21
22#include "ringbuffer.h"
23
24struct ringbuffer_t {
25  size_t total;
26  size_t available;
27  uint8_t* base;
28  uint8_t* head;
29  uint8_t* tail;
30};
31
32ringbuffer_t* ringbuffer_init(const size_t size) {
33  ringbuffer_t* p = static_cast<ringbuffer_t*>(calloc(1, sizeof(ringbuffer_t)));
34
35  if (p == NULL) return p;
36
37  p->base = static_cast<uint8_t*>(calloc(size, sizeof(uint8_t)));
38  p->head = p->tail = p->base;
39  p->total = p->available = size;
40
41  return p;
42}
43
44void ringbuffer_free(ringbuffer_t* rb) {
45  if (rb != NULL) free(rb->base);
46  free(rb);
47}
48
49size_t ringbuffer_available(const ringbuffer_t* rb) {
50  assert(rb);
51  return rb->available;
52}
53
54size_t ringbuffer_size(const ringbuffer_t* rb) {
55  assert(rb);
56  return rb->total - rb->available;
57}
58
59size_t ringbuffer_insert(ringbuffer_t* rb, const uint8_t* p, size_t length) {
60  assert(rb);
61  assert(p);
62
63  if (length > ringbuffer_available(rb)) length = ringbuffer_available(rb);
64
65  for (size_t i = 0; i != length; ++i) {
66    *rb->tail++ = *p++;
67    if (rb->tail >= (rb->base + rb->total)) rb->tail = rb->base;
68  }
69
70  rb->available -= length;
71  return length;
72}
73
74size_t ringbuffer_delete(ringbuffer_t* rb, size_t length) {
75  assert(rb);
76
77  if (length > ringbuffer_size(rb)) length = ringbuffer_size(rb);
78
79  rb->head += length;
80  if (rb->head >= (rb->base + rb->total)) rb->head -= rb->total;
81
82  rb->available += length;
83  return length;
84}
85
86size_t ringbuffer_peek(const ringbuffer_t* rb, off_t offset, uint8_t* p,
87                       size_t length) {
88  assert(rb);
89  assert(p);
90  assert(offset >= 0);
91  assert((size_t)offset <= ringbuffer_size(rb));
92
93  uint8_t* b = ((rb->head - rb->base + offset) % rb->total) + rb->base;
94  const size_t bytes_to_copy = (offset + length > ringbuffer_size(rb))
95                                   ? ringbuffer_size(rb) - offset
96                                   : length;
97
98  for (size_t copied = 0; copied < bytes_to_copy; ++copied) {
99    *p++ = *b++;
100    if (b >= (rb->base + rb->total)) b = rb->base;
101  }
102
103  return bytes_to_copy;
104}
105
106size_t ringbuffer_pop(ringbuffer_t* rb, uint8_t* p, size_t length) {
107  assert(rb);
108  assert(p);
109
110  const size_t copied = ringbuffer_peek(rb, 0, p, length);
111  rb->head += copied;
112  if (rb->head >= (rb->base + rb->total)) rb->head -= rb->total;
113
114  rb->available += copied;
115  return copied;
116}
117