TeaSpeak-Client/native/serverconnection/src/ring_buffer.cpp

212 lines
6.1 KiB
C++

//
// Created by wolverindev on 07.02.20.
//
#include "ring_buffer.h"
#ifdef HAVE_SOUNDIO
using namespace tc;
#include <soundio/soundio.h>
ring_buffer::ring_buffer(size_t cap) {
this->handle = soundio_ring_buffer_create(nullptr, (int) cap);
}
ring_buffer::~ring_buffer() {
soundio_ring_buffer_destroy((SoundIoRingBuffer*) this->handle);
}
size_t ring_buffer::capacity() const {
return soundio_ring_buffer_capacity((SoundIoRingBuffer*) this->handle);
}
size_t ring_buffer::free_count() const {
return soundio_ring_buffer_free_count((SoundIoRingBuffer*) this->handle);
}
size_t ring_buffer::fill_count() const {
return soundio_ring_buffer_fill_count((SoundIoRingBuffer*) this->handle);
}
char* ring_buffer::write_ptr() {
return soundio_ring_buffer_write_ptr((SoundIoRingBuffer*) this->handle);
}
void ring_buffer::advance_write_ptr(size_t bytes) {
soundio_ring_buffer_advance_write_ptr((SoundIoRingBuffer*) this->handle, (int) bytes);
}
const void* ring_buffer::read_ptr() const {
return soundio_ring_buffer_read_ptr((SoundIoRingBuffer*) this->handle);
}
void ring_buffer::advance_read_ptr(size_t bytes) {
soundio_ring_buffer_advance_read_ptr((SoundIoRingBuffer*) this->handle, (int) bytes);
}
void ring_buffer::clear() {
soundio_ring_buffer_clear((SoundIoRingBuffer*) this->handle);
}
#else
#include <windows.h>
#include <objbase.h>
#include <cassert>
namespace tc {
bool sysinfo_initialized{false};
static SYSTEM_INFO win32_system_info;
int soundio_os_page_size() {
if(!sysinfo_initialized) {
GetSystemInfo(&win32_system_info);
sysinfo_initialized = true;
}
return win32_system_info.dwAllocationGranularity;
}
static inline size_t ceil_dbl_to_size_t(double x) {
const auto truncation = (double) x;
return (size_t) (truncation + (truncation < x));
}
ring_buffer::ring_buffer(size_t min_capacity) {
this->allocate_memory(min_capacity);
}
ring_buffer::~ring_buffer() {
this->free_memory();
}
bool ring_buffer::allocate_memory(size_t requested_capacity) {
size_t actual_capacity = ceil_dbl_to_size_t(requested_capacity / (double) soundio_os_page_size()) * soundio_os_page_size();
this->memory.address = nullptr;
#ifdef WIN32
BOOL ok;
HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, (DWORD) (actual_capacity * 2), nullptr);
if (!hMapFile) return false;
for (;;) {
// find a free address space with the correct size
char *address = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, actual_capacity * 2);
if (!address) {
ok = CloseHandle(hMapFile);
assert(ok);
return false;
}
// found a big enough address space. hopefully it will remain free
// while we map to it. if not, we'll try again.
ok = UnmapViewOfFile(address);
assert(ok);
char *addr1 = (char*)MapViewOfFileEx(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, actual_capacity, address);
if (addr1 != address) {
DWORD err = GetLastError();
if (err == ERROR_INVALID_ADDRESS) {
continue;
} else {
ok = CloseHandle(hMapFile);
assert(ok);
return false;
}
}
char *addr2 = (char*)MapViewOfFileEx(hMapFile, FILE_MAP_WRITE, 0, 0,
actual_capacity, address + actual_capacity);
if (addr2 != address + actual_capacity) {
ok = UnmapViewOfFile(addr1);
assert(ok);
DWORD err = GetLastError();
if (err == ERROR_INVALID_ADDRESS) {
continue;
} else {
ok = CloseHandle(hMapFile);
assert(ok);
return false;
}
}
this->memory.priv = hMapFile;
this->memory.address = address;
break;
}
#else
#error "Implement me!"
#endif
this->memory.capacity = actual_capacity;
this->_capacity = actual_capacity;
return true;
}
void ring_buffer::free_memory() {
if(!this->memory.address) return;
#ifdef WIN32
BOOL ok;
ok = UnmapViewOfFile(this->memory.address);
assert(ok);
ok = UnmapViewOfFile(this->memory.address + this->memory.capacity);
assert(ok);
ok = CloseHandle((HANDLE) this->memory.priv);
assert(ok);
#else
#error "Implement me!"
#endif
this->memory.address = nullptr;
}
size_t ring_buffer::capacity() const {
return this->_capacity;
}
char* ring_buffer::write_ptr() {
auto offset{this->write_offset.load()};
return this->memory.address + (offset % this->memory.capacity);
}
void ring_buffer::advance_write_ptr(size_t bytes) {
this->write_offset.fetch_add((long) bytes);
assert(this->fill_count() >= 0);
}
const void* ring_buffer::read_ptr() const {
auto offset{this->read_offset.load()};
return this->memory.address + (offset % this->memory.capacity);
}
void ring_buffer::advance_read_ptr(size_t bytes) {
this->read_offset.fetch_add((long) bytes);
assert(this->fill_count() >= 0);
}
size_t ring_buffer::fill_count() const {
// Whichever offset we load first might have a smaller value. So we load
// the read_offset first.
auto roffset{this->read_offset.load()};
auto woffset{this->write_offset.load()};
int count = woffset - roffset;
assert(count >= 0);
assert(count <= this->memory.capacity);
return count;
}
size_t ring_buffer::free_count() const {
return this->memory.capacity - this->fill_count();
}
void ring_buffer::clear() {
this->write_offset.store(this->read_offset.load());
}
bool ring_buffer::valid() const {
return this->memory.address != nullptr;
}
}
#endif