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}