all repos — mgba @ f6755a6e1b7b0cf2b944cd8ca842746f11d6bf82

mGBA Game Boy Advance Emulator

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}