1#ifndef _GNU_SOURCE
2# define _GNU_SOURCE
3#endif
4
5#include <errno.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/mman.h>
10
11#include "alignment.h"
12#include "allocator.h"
13#include "ringbuffer.h"
14
15bool init_ringbuffer(RingBuffer* ringbuf, unsigned size)
16{
17 ringbuf->size = align_unsigned_to_page(size);
18 if (ringbuf->size == 0) {
19 ringbuf->size = sys_page_size();
20 }
21 ringbuf->data = mmap(nullptr, ringbuf->size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
22 if (ringbuf->data == MAP_FAILED) {
23 fprintf(stderr, "%s mmap: %s\n", __func__, strerror(errno));
24 return false;
25 }
26 ringbuf->head = 0;
27 ringbuf->tail = 0;
28 return true;
29}
30
31void fini_ringbuffer(RingBuffer* ringbuf)
32{
33 if (ringbuf->data) {
34 munmap(ringbuf->data, ringbuf->size);
35 ringbuf->data = nullptr;
36 }
37}
38
39RingBuffer* create_ringbuffer(unsigned size)
40{
41 RingBuffer* ringbuf = allocate(sizeof(RingBuffer), false);
42 if (ringbuf) {
43 if (init_ringbuffer(ringbuf, size)) {
44 return ringbuf;
45 }
46 release((void**) &ringbuf, sizeof(RingBuffer));
47 }
48 return nullptr;
49}
50
51void delete_ringbuffer(RingBuffer** ringbuf_ptr)
52{
53 RingBuffer* ringbuf = *ringbuf_ptr;
54 if (ringbuf) {
55 fini_ringbuffer(ringbuf);
56 release((void**) &ringbuf, sizeof(RingBuffer));
57 }
58 *ringbuf_ptr = nullptr;
59}
60
61bool grow_ringbuffer(RingBuffer* ringbuf, unsigned new_size)
62{
63 new_size = align_unsigned_to_page(new_size);
64 if (new_size == 0) {
65 new_size = sys_page_size();
66 }
67 if (new_size <= ringbuf->size) {
68 return true;
69 }
70 uint8_t* new_addr = mremap(ringbuf->data, ringbuf->size, new_size, MREMAP_MAYMOVE);
71 if (new_addr == MAP_FAILED) {
72 return false;
73 }
74 ringbuf->data = new_addr;
75
76 if (ringbuf->head > ringbuf->tail) {
77 // shift upper data up to the end of buffer
78 uint8_t* upper_data = ringbuf->data + ringbuf->head;
79 unsigned upper_data_size = ringbuf->size - ringbuf->head;
80 unsigned offset = new_size - ringbuf->size;
81 memmove(upper_data + offset, upper_data, upper_data_size);
82 ringbuf->head += offset;
83 }
84 ringbuf->size = new_size;
85 return true;
86}
87
88void shrink_ringbuffer(RingBuffer* ringbuf, unsigned new_size)
89{
90 new_size = align_unsigned_to_page(new_size);
91 if (new_size == 0) {
92 new_size = sys_page_size();
93 }
94 if (new_size >= ringbuf->size) {
95 return;
96 }
97 unsigned shrinkable_bytes;
98 if (ringbuf->head == ringbuf->tail) {
99 // buffer is empty
100 ringbuf->head = ringbuf->tail = 0;
101 shrinkable_bytes = ringbuf->size - sys_page_size();
102 if (shrinkable_bytes == 0) {
103 return;
104 }
105 } else {
106 // buffer is not empty, check if we can shrink
107 if (ringbuf->head > ringbuf->tail) {
108 shrinkable_bytes = (ringbuf->head - ringbuf->tail - 1) & ~(sys_page_size() - 1);
109 if (shrinkable_bytes == 0) {
110 return;
111 }
112 // shift upper data down
113 uint8_t* upper_data = ringbuf->data + ringbuf->head;
114 unsigned upper_data_size = ringbuf->size - ringbuf->head;
115 memmove(upper_data - shrinkable_bytes, upper_data, upper_data_size);
116 ringbuf->head -= shrinkable_bytes;
117 } else {
118 shrinkable_bytes = (ringbuf->head + ringbuf->size - ringbuf->tail - 1) & ~(sys_page_size() - 1);
119 if (shrinkable_bytes == 0) {
120 return;
121 }
122 // shift data down
123 if (ringbuf->head) {
124 memmove(ringbuf->data, ringbuf->data + ringbuf->head, ringbuf->tail - ringbuf->head);
125 ringbuf->tail -= ringbuf->head;
126 ringbuf->head = 0;
127 }
128 }
129 }
130 if (new_size < ringbuf->size - shrinkable_bytes) {
131 new_size = ringbuf->size - shrinkable_bytes;
132 }
133 uint8_t* new_addr = mremap(ringbuf->data, ringbuf->size, new_size, MREMAP_MAYMOVE);
134 if (new_addr == MAP_FAILED) {
135 fprintf(stderr, "%s mremap(%p, %u, %u): %s\n", __func__, (void*) ringbuf->data, ringbuf->size, new_size, strerror(errno));
136 abort();
137 }
138 ringbuf->data = new_addr;
139 ringbuf->size = new_size;
140 return;
141}
142
143bool write_ringbuffer(RingBuffer* ringbuf, uint8_t* data, unsigned size)
144{
145 unsigned bytes_avail;
146 unsigned tail_len;
147 if (ringbuf->head > ringbuf->tail) {
148 tail_len = ringbuf->head - ringbuf->tail;
149 bytes_avail = tail_len;
150 } else {
151 tail_len = ringbuf->size - ringbuf->tail;
152 bytes_avail = ringbuf->head + tail_len;
153 }
154
155 if (size >= bytes_avail) {
156 return false;
157 }
158
159 if (size < tail_len) {
160 // tail does not wrap
161 memcpy(&ringbuf->data[ringbuf->tail], data, size);
162 ringbuf->tail += size;
163 } else {
164 // tail wraps around
165 memcpy(&ringbuf->data[ringbuf->tail], data, tail_len);
166 unsigned head_len = size - tail_len;
167 if (head_len) {
168 memcpy(ringbuf->data, data + tail_len, head_len);
169 }
170 ringbuf->tail = head_len;
171 }
172 return true;
173}
174
175unsigned read_ringbuffer(RingBuffer* ringbuf, uint8_t* buffer, unsigned buffer_size)
176{
177 if (ringbuf->head == ringbuf->tail) {
178 return 0;
179 }
180 if (ringbuf->head < ringbuf->tail) {
181 unsigned data_size = ringbuf->tail - ringbuf->head;
182 unsigned len = (data_size <= buffer_size)? data_size : buffer_size;
183 memcpy(buffer, &ringbuf->data[ringbuf->head], len);
184 ringbuf->head += len;
185 return len;
186 }
187 unsigned upper_len = ringbuf->size - ringbuf->tail;
188 if (buffer_size <= upper_len) {
189 memcpy(buffer, &ringbuf->data[ringbuf->head], buffer_size);
190 if (buffer_size == upper_len) {
191 ringbuf->head = 0;
192 } else {
193 ringbuf->head += upper_len;
194 }
195 return buffer_size;
196 }
197 memcpy(buffer, &ringbuf->data[ringbuf->head], upper_len);
198 buffer_size -= upper_len;
199 unsigned lower_len = (ringbuf->tail < buffer_size)? ringbuf->tail : buffer_size;
200 memcpy(buffer + upper_len, ringbuf->data, lower_len);
201 ringbuf->head = lower_len;
202 return upper_len + lower_len;
203}