src/util/ring-fifo.c (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba-util/ring-fifo.h>
7
8#include <mgba-util/memory.h>
9
10void RingFIFOInit(struct RingFIFO* buffer, size_t capacity) {
11 buffer->data = anonymousMemoryMap(capacity);
12 buffer->capacity = capacity;
13 RingFIFOClear(buffer);
14}
15
16void RingFIFODeinit(struct RingFIFO* buffer) {
17 mappedMemoryFree(buffer->data, buffer->capacity);
18 buffer->data = 0;
19}
20
21size_t RingFIFOCapacity(const struct RingFIFO* buffer) {
22 return buffer->capacity;
23}
24
25size_t RingFIFOSize(const struct RingFIFO* buffer) {
26 const void* read;
27 const void* write;
28 ATOMIC_LOAD_PTR(read, buffer->readPtr);
29 ATOMIC_LOAD_PTR(write, buffer->writePtr);
30 if (read <= write) {
31 return (uintptr_t) write - (uintptr_t) read;
32 } else {
33 return buffer->capacity - (uintptr_t) read + (uintptr_t) write;
34 }
35}
36
37void RingFIFOClear(struct RingFIFO* buffer) {
38 ATOMIC_STORE_PTR(buffer->readPtr, buffer->data);
39 ATOMIC_STORE_PTR(buffer->writePtr, buffer->data);
40}
41
42size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length) {
43 void* data = buffer->writePtr;
44 void* end;
45 ATOMIC_LOAD_PTR(end, buffer->readPtr);
46
47 // Wrap around if we can't fit enough in here
48 if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) {
49 if (end == buffer->data || end > data) {
50 // Oops! If we wrap now, it'll appear empty
51 return 0;
52 }
53 data = buffer->data;
54 }
55
56 size_t remaining;
57 if (data >= end) {
58 uintptr_t bufferEnd = (uintptr_t) buffer->data + buffer->capacity;
59 remaining = bufferEnd - (uintptr_t) data;
60 } else {
61 remaining = (uintptr_t) end - (uintptr_t) data;
62 }
63 // Note that we can't hit the end pointer
64 if (remaining <= length) {
65 return 0;
66 }
67 if (value) {
68 memcpy(data, value, length);
69 }
70 ATOMIC_STORE_PTR(buffer->writePtr, (void*) ((intptr_t) data + length));
71 return length;
72}
73
74size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
75 void* data = buffer->readPtr;
76 void* end;
77 ATOMIC_LOAD_PTR(end, buffer->writePtr);
78
79 // Wrap around if we can't fit enough in here
80 if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) {
81 if (end >= data) {
82 // Oops! If we wrap now, it'll appear full
83 return 0;
84 }
85 data = buffer->data;
86 }
87
88 size_t remaining;
89 if (data > end) {
90 uintptr_t bufferEnd = (uintptr_t) buffer->data + buffer->capacity;
91 remaining = bufferEnd - (uintptr_t) data;
92 } else {
93 remaining = (uintptr_t) end - (uintptr_t) data;
94 }
95 // If the pointers touch, it's empty
96 if (remaining < length) {
97 return 0;
98 }
99 if (output) {
100 memcpy(output, data, length);
101 }
102 ATOMIC_STORE_PTR(buffer->readPtr, (void*) ((uintptr_t) data + length));
103 return length;
104}