1/******************************************************************************
2 *
3 *  Copyright (C) 2015 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 "osi/include/allocator.h"
23#include "osi/include/ringbuffer.h"
24
25struct ringbuffer_t {
26  size_t total;
27  size_t available;
28  uint8_t *base;
29  uint8_t *head;
30  uint8_t *tail;
31};
32
33ringbuffer_t* ringbuffer_init(const size_t size) {
34  ringbuffer_t* p = osi_calloc(sizeof(ringbuffer_t));
35
36  p->base = osi_calloc(size);
37  p->head = p->tail = p->base;
38  p->total = p->available = size;
39
40  return p;
41}
42
43void ringbuffer_free(ringbuffer_t *rb) {
44  if (rb != NULL)
45    osi_free(rb->base);
46  osi_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))
64    length = ringbuffer_available(rb);
65
66  for (size_t i = 0; i != length; ++i) {
67    *rb->tail++ = *p++;
68    if (rb->tail >= (rb->base + rb->total))
69      rb->tail = rb->base;
70  }
71
72  rb->available -= length;
73  return length;
74}
75
76size_t ringbuffer_delete(ringbuffer_t *rb, size_t length) {
77  assert(rb);
78
79  if (length > ringbuffer_size(rb))
80    length = ringbuffer_size(rb);
81
82  rb->head += length;
83  if (rb->head >= (rb->base + rb->total))
84    rb->head -= rb->total;
85
86  rb->available += length;
87  return length;
88}
89
90size_t ringbuffer_peek(const ringbuffer_t *rb, off_t offset, uint8_t *p, size_t length) {
91  assert(rb);
92  assert(p);
93  assert(offset >= 0);
94  assert((size_t)offset <= ringbuffer_size(rb));
95
96  uint8_t *b = ((rb->head - rb->base + offset) % rb->total) + rb->base;
97  const size_t bytes_to_copy = (offset + length > ringbuffer_size(rb)) ? ringbuffer_size(rb) - offset : length;
98
99  for (size_t copied = 0; copied < bytes_to_copy; ++copied) {
100    *p++ = *b++;
101    if (b >= (rb->base + rb->total))
102      b = rb->base;
103  }
104
105  return bytes_to_copy;
106}
107
108size_t ringbuffer_pop(ringbuffer_t *rb, uint8_t *p, size_t length) {
109  assert(rb);
110  assert(p);
111
112  const size_t copied = ringbuffer_peek(rb, 0, p, length);
113  rb->head += copied;
114  if (rb->head >= (rb->base + rb->total))
115    rb->head -= rb->total;
116
117  rb->available += copied;
118  return copied;
119}
120