all repos — mgba @ 6519c4e465f42d58c297fc92e497766bfbbd5e0a

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 "ring-fifo.h"
  7
  8#include "util/memory.h"
  9
 10#ifndef _MSC_VER
 11#define ATOMIC_STORE(DST, SRC) __atomic_store_n(&DST, SRC, __ATOMIC_RELEASE)
 12#define ATOMIC_LOAD(DST, SRC) DST = __atomic_load_n(&SRC, __ATOMIC_ACQUIRE)
 13#else
 14// TODO
 15#define ATOMIC_STORE(DST, SRC) DST = SRC
 16#define ATOMIC_LOAD(DST, SRC) DST = SRC
 17#endif
 18
 19void RingFIFOInit(struct RingFIFO* buffer, size_t capacity) {
 20	buffer->data = anonymousMemoryMap(capacity);
 21	buffer->capacity = capacity;
 22	RingFIFOClear(buffer);
 23}
 24
 25void RingFIFODeinit(struct RingFIFO* buffer) {
 26	mappedMemoryFree(buffer->data, buffer->capacity);
 27	buffer->data = 0;
 28}
 29
 30size_t RingFIFOCapacity(const struct RingFIFO* buffer) {
 31	return buffer->capacity;
 32}
 33
 34void RingFIFOClear(struct RingFIFO* buffer) {
 35	ATOMIC_STORE(buffer->readPtr, buffer->data);
 36	ATOMIC_STORE(buffer->writePtr, buffer->data);
 37}
 38
 39size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length) {
 40	void* data = buffer->writePtr;
 41	void* end;
 42	ATOMIC_LOAD(end, buffer->readPtr);
 43
 44	// Wrap around if we can't fit enough in here
 45	if ((intptr_t) data - (intptr_t) buffer->data + length >= buffer->capacity) {
 46		if (end == buffer->data) {
 47			// Oops! If we wrap now, it'll appear empty
 48			return 0;
 49		}
 50		data = buffer->data;
 51	}
 52
 53	size_t remaining;
 54	if (data >= end) {
 55		uintptr_t bufferEnd = (uintptr_t) buffer->data + buffer->capacity;
 56		remaining = bufferEnd - (uintptr_t) data;
 57	} else {
 58		remaining = (uintptr_t) end - (uintptr_t) data;
 59	}
 60	// Note that we can't hit the end pointer
 61	if (remaining <= length) {
 62		return 0;
 63	}
 64	if (value) {
 65		memcpy(data, value, length);
 66	}
 67	ATOMIC_STORE(buffer->writePtr, (void*) ((intptr_t) data + length));
 68	return length;
 69}
 70
 71size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
 72	void* data = buffer->readPtr;
 73	void* end;
 74	ATOMIC_LOAD(end, buffer->writePtr);
 75
 76	// Wrap around if we can't fit enough in here
 77	if ((intptr_t) data - (intptr_t) buffer->data + length >= buffer->capacity) {
 78		if (end == data) {
 79			// Oops! If we wrap now, it'll appear full
 80			return 0;
 81		}
 82		data = buffer->data;
 83	}
 84
 85	size_t remaining;
 86	if (data > end) {
 87		uintptr_t bufferEnd = (uintptr_t) buffer->data + buffer->capacity;
 88		remaining = bufferEnd - (uintptr_t) data;
 89	} else {
 90		remaining = (intptr_t) end - (intptr_t) data;
 91	}
 92	// If the pointers touch, it's empty
 93	if (remaining < length) {
 94		return 0;
 95	}
 96	if (output) {
 97		memcpy(output, data, length);
 98	}
 99	ATOMIC_STORE(buffer->readPtr, (void*) ((intptr_t) data + length));
100	return length;
101}