First Commit

This commit is contained in:
2025-02-06 22:24:29 +08:00
parent ed7df4c81e
commit 7539e6a53c
18116 changed files with 6181499 additions and 0 deletions

155
externals/openal-soft/common/albit.h vendored Normal file
View File

@@ -0,0 +1,155 @@
#ifndef AL_BIT_H
#define AL_BIT_H
#include <cstdint>
#include <limits>
#include <type_traits>
#if !defined(__GNUC__) && (defined(_WIN32) || defined(_WIN64))
#include <intrin.h>
#endif
namespace al {
#ifdef __BYTE_ORDER__
enum class endian {
little = __ORDER_LITTLE_ENDIAN__,
big = __ORDER_BIG_ENDIAN__,
native = __BYTE_ORDER__
};
#else
/* This doesn't support mixed-endian. */
namespace detail_ {
constexpr bool IsLittleEndian() noexcept
{
static_assert(sizeof(char) < sizeof(int), "char is too big");
constexpr int test_val{1};
return static_cast<const char&>(test_val) ? true : false;
}
} // namespace detail_
enum class endian {
big = 0,
little = 1,
native = detail_::IsLittleEndian() ? little : big
};
#endif
/* Define popcount (population count/count 1 bits) and countr_zero (count
* trailing zero bits, starting from the lsb) methods, for various integer
* types.
*/
#ifdef __GNUC__
namespace detail_ {
inline int popcount(unsigned long long val) noexcept { return __builtin_popcountll(val); }
inline int popcount(unsigned long val) noexcept { return __builtin_popcountl(val); }
inline int popcount(unsigned int val) noexcept { return __builtin_popcount(val); }
inline int countr_zero(unsigned long long val) noexcept { return __builtin_ctzll(val); }
inline int countr_zero(unsigned long val) noexcept { return __builtin_ctzl(val); }
inline int countr_zero(unsigned int val) noexcept { return __builtin_ctz(val); }
} // namespace detail_
template<typename T>
inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
int> popcount(T v) noexcept { return detail_::popcount(v); }
template<typename T>
inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
int> countr_zero(T val) noexcept
{ return val ? detail_::countr_zero(val) : std::numeric_limits<T>::digits; }
#else
/* There be black magics here. The popcount method is derived from
* https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
* while the ctz-utilizing-popcount algorithm is shown here
* http://www.hackersdelight.org/hdcodetxt/ntz.c.txt
* as the ntz2 variant. These likely aren't the most efficient methods, but
* they're good enough if the GCC built-ins aren't available.
*/
namespace detail_ {
template<typename T, size_t = std::numeric_limits<T>::digits>
struct fast_utype { };
template<typename T>
struct fast_utype<T,8> { using type = std::uint_fast8_t; };
template<typename T>
struct fast_utype<T,16> { using type = std::uint_fast16_t; };
template<typename T>
struct fast_utype<T,32> { using type = std::uint_fast32_t; };
template<typename T>
struct fast_utype<T,64> { using type = std::uint_fast64_t; };
template<typename T>
constexpr T repbits(unsigned char bits) noexcept
{
T ret{bits};
for(size_t i{1};i < sizeof(T);++i)
ret = (ret<<8) | bits;
return ret;
}
} // namespace detail_
template<typename T>
constexpr std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
int> popcount(T val) noexcept
{
using fast_type = typename detail_::fast_utype<T>::type;
constexpr fast_type b01010101{detail_::repbits<fast_type>(0x55)};
constexpr fast_type b00110011{detail_::repbits<fast_type>(0x33)};
constexpr fast_type b00001111{detail_::repbits<fast_type>(0x0f)};
constexpr fast_type b00000001{detail_::repbits<fast_type>(0x01)};
fast_type v{fast_type{val} - ((fast_type{val} >> 1) & b01010101)};
v = (v & b00110011) + ((v >> 2) & b00110011);
v = (v + (v >> 4)) & b00001111;
return static_cast<int>(((v * b00000001) >> ((sizeof(T)-1)*8)) & 0xff);
}
#ifdef _WIN32
template<typename T>
inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value
&& std::numeric_limits<T>::digits <= 32,
int> countr_zero(T v)
{
unsigned long idx{std::numeric_limits<T>::digits};
_BitScanForward(&idx, static_cast<uint32_t>(v));
return static_cast<int>(idx);
}
template<typename T>
inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value
&& 32 < std::numeric_limits<T>::digits && std::numeric_limits<T>::digits <= 64,
int> countr_zero(T v)
{
unsigned long idx{std::numeric_limits<T>::digits};
#ifdef _WIN64
_BitScanForward64(&idx, v);
#else
if(!_BitScanForward(&idx, static_cast<uint32_t>(v)))
{
if(_BitScanForward(&idx, static_cast<uint32_t>(v>>32)))
idx += 32;
}
#endif /* _WIN64 */
return static_cast<int>(idx);
}
#else
template<typename T>
constexpr std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
int> countr_zero(T value)
{ return popcount(static_cast<T>(~value & (value - 1))); }
#endif
#endif
} // namespace al
#endif /* AL_BIT_H */

17
externals/openal-soft/common/albyte.h vendored Normal file
View File

@@ -0,0 +1,17 @@
#ifndef AL_BYTE_H
#define AL_BYTE_H
#include <cstddef>
#include <cstdint>
#include <limits>
#include <type_traits>
using uint = unsigned int;
namespace al {
using byte = unsigned char;
} // namespace al
#endif /* AL_BYTE_H */

View File

@@ -0,0 +1,171 @@
#include "config.h"
#include "alcomplex.h"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <functional>
#include <utility>
#include "albit.h"
#include "alnumbers.h"
#include "alnumeric.h"
#include "opthelpers.h"
namespace {
using ushort = unsigned short;
using ushort2 = std::pair<ushort,ushort>;
constexpr size_t BitReverseCounter(size_t log2_size) noexcept
{
/* Some magic math that calculates the number of swaps needed for a
* sequence of bit-reversed indices when index < reversed_index.
*/
return (1u<<(log2_size-1)) - (1u<<((log2_size-1u)/2u));
}
template<size_t N>
struct BitReverser {
static_assert(N <= sizeof(ushort)*8, "Too many bits for the bit-reversal table.");
ushort2 mData[BitReverseCounter(N)]{};
constexpr BitReverser()
{
const size_t fftsize{1u << N};
size_t ret_i{0};
/* Bit-reversal permutation applied to a sequence of fftsize items. */
for(size_t idx{1u};idx < fftsize-1;++idx)
{
size_t revidx{0u}, imask{idx};
for(size_t i{0};i < N;++i)
{
revidx = (revidx<<1) | (imask&1);
imask >>= 1;
}
if(idx < revidx)
{
mData[ret_i].first = static_cast<ushort>(idx);
mData[ret_i].second = static_cast<ushort>(revidx);
++ret_i;
}
}
assert(ret_i == al::size(mData));
}
};
/* These bit-reversal swap tables support up to 10-bit indices (1024 elements),
* which is the largest used by OpenAL Soft's filters and effects. Larger FFT
* requests, used by some utilities where performance is less important, will
* use a slower table-less path.
*/
constexpr BitReverser<2> BitReverser2{};
constexpr BitReverser<3> BitReverser3{};
constexpr BitReverser<4> BitReverser4{};
constexpr BitReverser<5> BitReverser5{};
constexpr BitReverser<6> BitReverser6{};
constexpr BitReverser<7> BitReverser7{};
constexpr BitReverser<8> BitReverser8{};
constexpr BitReverser<9> BitReverser9{};
constexpr BitReverser<10> BitReverser10{};
constexpr std::array<al::span<const ushort2>,11> gBitReverses{{
{}, {},
BitReverser2.mData,
BitReverser3.mData,
BitReverser4.mData,
BitReverser5.mData,
BitReverser6.mData,
BitReverser7.mData,
BitReverser8.mData,
BitReverser9.mData,
BitReverser10.mData
}};
} // namespace
template<typename Real>
std::enable_if_t<std::is_floating_point<Real>::value>
complex_fft(const al::span<std::complex<Real>> buffer, const al::type_identity_t<Real> sign)
{
const size_t fftsize{buffer.size()};
/* Get the number of bits used for indexing. Simplifies bit-reversal and
* the main loop count.
*/
const size_t log2_size{static_cast<size_t>(al::countr_zero(fftsize))};
if(log2_size >= gBitReverses.size()) UNLIKELY
{
for(size_t idx{1u};idx < fftsize-1;++idx)
{
size_t revidx{0u}, imask{idx};
for(size_t i{0};i < log2_size;++i)
{
revidx = (revidx<<1) | (imask&1);
imask >>= 1;
}
if(idx < revidx)
std::swap(buffer[idx], buffer[revidx]);
}
}
else for(auto &rev : gBitReverses[log2_size])
std::swap(buffer[rev.first], buffer[rev.second]);
/* Iterative form of Danielson-Lanczos lemma */
const Real pi{al::numbers::pi_v<Real> * sign};
size_t step2{1u};
for(size_t i{0};i < log2_size;++i)
{
const Real arg{pi / static_cast<Real>(step2)};
/* TODO: Would std::polar(1.0, arg) be any better? */
const std::complex<Real> w{std::cos(arg), std::sin(arg)};
std::complex<Real> u{1.0, 0.0};
const size_t step{step2 << 1};
for(size_t j{0};j < step2;j++)
{
for(size_t k{j};k < fftsize;k+=step)
{
std::complex<Real> temp{buffer[k+step2] * u};
buffer[k+step2] = buffer[k] - temp;
buffer[k] += temp;
}
u *= w;
}
step2 <<= 1;
}
}
void complex_hilbert(const al::span<std::complex<double>> buffer)
{
using namespace std::placeholders;
inverse_fft(buffer);
const double inverse_size = 1.0/static_cast<double>(buffer.size());
auto bufiter = buffer.begin();
const auto halfiter = bufiter + (buffer.size()>>1);
*bufiter *= inverse_size; ++bufiter;
bufiter = std::transform(bufiter, halfiter, bufiter,
[scale=inverse_size*2.0](std::complex<double> d){ return d * scale; });
*bufiter *= inverse_size; ++bufiter;
std::fill(bufiter, buffer.end(), std::complex<double>{});
forward_fft(buffer);
}
template void complex_fft<>(const al::span<std::complex<float>> buffer, const float sign);
template void complex_fft<>(const al::span<std::complex<double>> buffer, const double sign);

View File

@@ -0,0 +1,45 @@
#ifndef ALCOMPLEX_H
#define ALCOMPLEX_H
#include <complex>
#include <type_traits>
#include "alspan.h"
/**
* Iterative implementation of 2-radix FFT (In-place algorithm). Sign = -1 is
* FFT and 1 is inverse FFT. Applies the Discrete Fourier Transform (DFT) to
* the data supplied in the buffer, which MUST BE power of two.
*/
template<typename Real>
std::enable_if_t<std::is_floating_point<Real>::value>
complex_fft(const al::span<std::complex<Real>> buffer, const al::type_identity_t<Real> sign);
/**
* Calculate the frequency-domain response of the time-domain signal in the
* provided buffer, which MUST BE power of two.
*/
template<typename Real, size_t N>
std::enable_if_t<std::is_floating_point<Real>::value>
forward_fft(const al::span<std::complex<Real>,N> buffer)
{ complex_fft(buffer.subspan(0), -1); }
/**
* Calculate the time-domain signal of the frequency-domain response in the
* provided buffer, which MUST BE power of two.
*/
template<typename Real, size_t N>
std::enable_if_t<std::is_floating_point<Real>::value>
inverse_fft(const al::span<std::complex<Real>,N> buffer)
{ complex_fft(buffer.subspan(0), 1); }
/**
* Calculate the complex helical sequence (discrete-time analytical signal) of
* the given input using the discrete Hilbert transform (In-place algorithm).
* Fills the buffer with the discrete-time analytical signal stored in the
* buffer. The buffer is an array of complex numbers and MUST BE power of two,
* and the imaginary components should be cleared to 0.
*/
void complex_hilbert(const al::span<std::complex<double>> buffer);
#endif /* ALCOMPLEX_H */

16
externals/openal-soft/common/aldeque.h vendored Normal file
View File

@@ -0,0 +1,16 @@
#ifndef ALDEQUE_H
#define ALDEQUE_H
#include <deque>
#include "almalloc.h"
namespace al {
template<typename T>
using deque = std::deque<T, al::allocator<T>>;
} // namespace al
#endif /* ALDEQUE_H */

View File

@@ -0,0 +1,26 @@
#include "config.h"
#include "alfstream.h"
#include "strutils.h"
#ifdef _WIN32
namespace al {
ifstream::ifstream(const char *filename, std::ios_base::openmode mode)
: std::ifstream{utf8_to_wstr(filename).c_str(), mode}
{ }
void ifstream::open(const char *filename, std::ios_base::openmode mode)
{
std::wstring wstr{utf8_to_wstr(filename)};
std::ifstream::open(wstr.c_str(), mode);
}
ifstream::~ifstream() = default;
} // namespace al
#endif

View File

@@ -0,0 +1,45 @@
#ifndef AL_FSTREAM_H
#define AL_FSTREAM_H
#ifdef _WIN32
#include <string>
#include <fstream>
namespace al {
// Inherit from std::ifstream to accept UTF-8 filenames
class ifstream final : public std::ifstream {
public:
explicit ifstream(const char *filename, std::ios_base::openmode mode=std::ios_base::in);
explicit ifstream(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in)
: ifstream{filename.c_str(), mode} { }
explicit ifstream(const wchar_t *filename, std::ios_base::openmode mode=std::ios_base::in)
: std::ifstream{filename, mode} { }
explicit ifstream(const std::wstring &filename, std::ios_base::openmode mode=std::ios_base::in)
: ifstream{filename.c_str(), mode} { }
void open(const char *filename, std::ios_base::openmode mode=std::ios_base::in);
void open(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in)
{ open(filename.c_str(), mode); }
~ifstream() override;
};
} // namespace al
#else /* _WIN32 */
#include <fstream>
namespace al {
using ifstream = std::ifstream;
} // namespace al
#endif /* _WIN32 */
#endif /* AL_FSTREAM_H */

View File

@@ -0,0 +1,61 @@
#include "config.h"
#include "almalloc.h"
#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <memory>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
void *al_malloc(size_t alignment, size_t size)
{
assert((alignment & (alignment-1)) == 0);
alignment = std::max(alignment, alignof(std::max_align_t));
#if defined(HAVE_POSIX_MEMALIGN)
void *ret{};
if(posix_memalign(&ret, alignment, size) == 0)
return ret;
return nullptr;
#elif defined(HAVE__ALIGNED_MALLOC)
return _aligned_malloc(size, alignment);
#else
size_t total_size{size + alignment-1 + sizeof(void*)};
void *base{std::malloc(total_size)};
if(base != nullptr)
{
void *aligned_ptr{static_cast<char*>(base) + sizeof(void*)};
total_size -= sizeof(void*);
std::align(alignment, size, aligned_ptr, total_size);
*(static_cast<void**>(aligned_ptr)-1) = base;
base = aligned_ptr;
}
return base;
#endif
}
void *al_calloc(size_t alignment, size_t size)
{
void *ret{al_malloc(alignment, size)};
if(ret) std::memset(ret, 0, size);
return ret;
}
void al_free(void *ptr) noexcept
{
#if defined(HAVE_POSIX_MEMALIGN)
std::free(ptr);
#elif defined(HAVE__ALIGNED_MALLOC)
_aligned_free(ptr);
#else
if(ptr != nullptr)
std::free(*(static_cast<void**>(ptr) - 1));
#endif
}

311
externals/openal-soft/common/almalloc.h vendored Normal file
View File

@@ -0,0 +1,311 @@
#ifndef AL_MALLOC_H
#define AL_MALLOC_H
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <limits>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
#include "pragmadefs.h"
void al_free(void *ptr) noexcept;
[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]]
void *al_malloc(size_t alignment, size_t size);
[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]]
void *al_calloc(size_t alignment, size_t size);
#define DISABLE_ALLOC() \
void *operator new(size_t) = delete; \
void *operator new[](size_t) = delete; \
void operator delete(void*) noexcept = delete; \
void operator delete[](void*) noexcept = delete;
#define DEF_NEWDEL(T) \
void *operator new(size_t size) \
{ \
static_assert(&operator new == &T::operator new, \
"Incorrect container type specified"); \
if(void *ret{al_malloc(alignof(T), size)}) \
return ret; \
throw std::bad_alloc(); \
} \
void *operator new[](size_t size) { return operator new(size); } \
void operator delete(void *block) noexcept { al_free(block); } \
void operator delete[](void *block) noexcept { operator delete(block); }
#define DEF_PLACE_NEWDEL() \
void *operator new(size_t /*size*/, void *ptr) noexcept { return ptr; } \
void *operator new[](size_t /*size*/, void *ptr) noexcept { return ptr; } \
void operator delete(void *block, void*) noexcept { al_free(block); } \
void operator delete(void *block) noexcept { al_free(block); } \
void operator delete[](void *block, void*) noexcept { al_free(block); } \
void operator delete[](void *block) noexcept { al_free(block); }
enum FamCount : size_t { };
#define DEF_FAM_NEWDEL(T, FamMem) \
static constexpr size_t Sizeof(size_t count) noexcept \
{ \
static_assert(&Sizeof == &T::Sizeof, \
"Incorrect container type specified"); \
return std::max(decltype(FamMem)::Sizeof(count, offsetof(T, FamMem)), \
sizeof(T)); \
} \
\
void *operator new(size_t /*size*/, FamCount count) \
{ \
if(void *ret{al_malloc(alignof(T), T::Sizeof(count))}) \
return ret; \
throw std::bad_alloc(); \
} \
void *operator new[](size_t /*size*/) = delete; \
void operator delete(void *block, FamCount) { al_free(block); } \
void operator delete(void *block) noexcept { al_free(block); } \
void operator delete[](void* /*block*/) = delete;
namespace al {
template<typename T, std::size_t Align=alignof(T)>
struct allocator {
static constexpr std::size_t alignment{std::max(Align, alignof(T))};
using value_type = T;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using is_always_equal = std::true_type;
template<typename U>
struct rebind {
using other = allocator<U, Align>;
};
constexpr explicit allocator() noexcept = default;
template<typename U, std::size_t N>
constexpr explicit allocator(const allocator<U,N>&) noexcept { }
T *allocate(std::size_t n)
{
if(n > std::numeric_limits<std::size_t>::max()/sizeof(T)) throw std::bad_alloc();
if(auto p = al_malloc(alignment, n*sizeof(T))) return static_cast<T*>(p);
throw std::bad_alloc();
}
void deallocate(T *p, std::size_t) noexcept { al_free(p); }
};
template<typename T, std::size_t N, typename U, std::size_t M>
constexpr bool operator==(const allocator<T,N>&, const allocator<U,M>&) noexcept { return true; }
template<typename T, std::size_t N, typename U, std::size_t M>
constexpr bool operator!=(const allocator<T,N>&, const allocator<U,M>&) noexcept { return false; }
template<typename T>
constexpr T *to_address(T *p) noexcept
{
static_assert(!std::is_function<T>::value, "Can't be a function type");
return p;
}
template<typename T>
constexpr auto to_address(const T &p) noexcept
{ return to_address(p.operator->()); }
template<typename T, typename ...Args>
constexpr T* construct_at(T *ptr, Args&& ...args)
noexcept(std::is_nothrow_constructible<T, Args...>::value)
{ return ::new(static_cast<void*>(ptr)) T{std::forward<Args>(args)...}; }
/* At least VS 2015 complains that 'ptr' is unused when the given type's
* destructor is trivial (a no-op). So disable that warning for this call.
*/
DIAGNOSTIC_PUSH
msc_pragma(warning(disable : 4100))
template<typename T>
constexpr std::enable_if_t<!std::is_array<T>::value>
destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<T>::value)
{ ptr->~T(); }
DIAGNOSTIC_POP
template<typename T>
constexpr std::enable_if_t<std::is_array<T>::value>
destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<std::remove_all_extents_t<T>>::value)
{
for(auto &elem : *ptr)
al::destroy_at(std::addressof(elem));
}
template<typename T>
constexpr void destroy(T first, T end) noexcept(noexcept(al::destroy_at(std::addressof(*first))))
{
while(first != end)
{
al::destroy_at(std::addressof(*first));
++first;
}
}
template<typename T, typename N>
constexpr std::enable_if_t<std::is_integral<N>::value,T>
destroy_n(T first, N count) noexcept(noexcept(al::destroy_at(std::addressof(*first))))
{
if(count != 0)
{
do {
al::destroy_at(std::addressof(*first));
++first;
} while(--count);
}
return first;
}
template<typename T, typename N>
inline std::enable_if_t<std::is_integral<N>::value,
T> uninitialized_default_construct_n(T first, N count)
{
using ValueT = typename std::iterator_traits<T>::value_type;
T current{first};
if(count != 0)
{
try {
do {
::new(static_cast<void*>(std::addressof(*current))) ValueT;
++current;
} while(--count);
}
catch(...) {
al::destroy(first, current);
throw;
}
}
return current;
}
/* Storage for flexible array data. This is trivially destructible if type T is
* trivially destructible.
*/
template<typename T, size_t alignment, bool = std::is_trivially_destructible<T>::value>
struct FlexArrayStorage {
const size_t mSize;
union {
char mDummy;
alignas(alignment) T mArray[1];
};
static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept
{
const size_t len{sizeof(T)*count};
return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base;
}
FlexArrayStorage(size_t size) : mSize{size}
{ al::uninitialized_default_construct_n(mArray, mSize); }
~FlexArrayStorage() = default;
FlexArrayStorage(const FlexArrayStorage&) = delete;
FlexArrayStorage& operator=(const FlexArrayStorage&) = delete;
};
template<typename T, size_t alignment>
struct FlexArrayStorage<T,alignment,false> {
const size_t mSize;
union {
char mDummy;
alignas(alignment) T mArray[1];
};
static constexpr size_t Sizeof(size_t count, size_t base) noexcept
{
const size_t len{sizeof(T)*count};
return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base;
}
FlexArrayStorage(size_t size) : mSize{size}
{ al::uninitialized_default_construct_n(mArray, mSize); }
~FlexArrayStorage() { al::destroy_n(mArray, mSize); }
FlexArrayStorage(const FlexArrayStorage&) = delete;
FlexArrayStorage& operator=(const FlexArrayStorage&) = delete;
};
/* A flexible array type. Used either standalone or at the end of a parent
* struct, with placement new, to have a run-time-sized array that's embedded
* with its size.
*/
template<typename T, size_t alignment=alignof(T)>
struct FlexArray {
using element_type = T;
using value_type = std::remove_cv_t<T>;
using index_type = size_t;
using difference_type = ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using Storage_t_ = FlexArrayStorage<element_type,alignment>;
Storage_t_ mStore;
static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept
{ return Storage_t_::Sizeof(count, base); }
static std::unique_ptr<FlexArray> Create(index_type count)
{
void *ptr{al_calloc(alignof(FlexArray), Sizeof(count))};
return std::unique_ptr<FlexArray>{al::construct_at(static_cast<FlexArray*>(ptr), count)};
}
FlexArray(index_type size) : mStore{size} { }
~FlexArray() = default;
index_type size() const noexcept { return mStore.mSize; }
bool empty() const noexcept { return mStore.mSize == 0; }
pointer data() noexcept { return mStore.mArray; }
const_pointer data() const noexcept { return mStore.mArray; }
reference operator[](index_type i) noexcept { return mStore.mArray[i]; }
const_reference operator[](index_type i) const noexcept { return mStore.mArray[i]; }
reference front() noexcept { return mStore.mArray[0]; }
const_reference front() const noexcept { return mStore.mArray[0]; }
reference back() noexcept { return mStore.mArray[mStore.mSize-1]; }
const_reference back() const noexcept { return mStore.mArray[mStore.mSize-1]; }
iterator begin() noexcept { return mStore.mArray; }
const_iterator begin() const noexcept { return mStore.mArray; }
const_iterator cbegin() const noexcept { return mStore.mArray; }
iterator end() noexcept { return mStore.mArray + mStore.mSize; }
const_iterator end() const noexcept { return mStore.mArray + mStore.mSize; }
const_iterator cend() const noexcept { return mStore.mArray + mStore.mSize; }
reverse_iterator rbegin() noexcept { return end(); }
const_reverse_iterator rbegin() const noexcept { return end(); }
const_reverse_iterator crbegin() const noexcept { return cend(); }
reverse_iterator rend() noexcept { return begin(); }
const_reverse_iterator rend() const noexcept { return begin(); }
const_reverse_iterator crend() const noexcept { return cbegin(); }
DEF_PLACE_NEWDEL()
};
} // namespace al
#endif /* AL_MALLOC_H */

View File

@@ -0,0 +1,36 @@
#ifndef COMMON_ALNUMBERS_H
#define COMMON_ALNUMBERS_H
#include <utility>
namespace al {
namespace numbers {
namespace detail_ {
template<typename T>
using as_fp = std::enable_if_t<std::is_floating_point<T>::value, T>;
} // detail_
template<typename T>
static constexpr auto pi_v = detail_::as_fp<T>(3.141592653589793238462643383279502884L);
template<typename T>
static constexpr auto inv_pi_v = detail_::as_fp<T>(0.318309886183790671537767526745028724L);
template<typename T>
static constexpr auto sqrt2_v = detail_::as_fp<T>(1.414213562373095048801688724209698079L);
template<typename T>
static constexpr auto sqrt3_v = detail_::as_fp<T>(1.732050807568877293527446341505872367L);
static constexpr auto pi = pi_v<double>;
static constexpr auto inv_pi = inv_pi_v<double>;
static constexpr auto sqrt2 = sqrt2_v<double>;
static constexpr auto sqrt3 = sqrt3_v<double>;
} // namespace numbers
} // namespace al
#endif /* COMMON_ALNUMBERS_H */

308
externals/openal-soft/common/alnumeric.h vendored Normal file
View File

@@ -0,0 +1,308 @@
#ifndef AL_NUMERIC_H
#define AL_NUMERIC_H
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#ifdef HAVE_INTRIN_H
#include <intrin.h>
#endif
#ifdef HAVE_SSE_INTRINSICS
#include <xmmintrin.h>
#endif
#include "altraits.h"
#include "opthelpers.h"
inline constexpr int64_t operator "" _i64(unsigned long long int n) noexcept { return static_cast<int64_t>(n); }
inline constexpr uint64_t operator "" _u64(unsigned long long int n) noexcept { return static_cast<uint64_t>(n); }
constexpr inline float minf(float a, float b) noexcept
{ return ((a > b) ? b : a); }
constexpr inline float maxf(float a, float b) noexcept
{ return ((a > b) ? a : b); }
constexpr inline float clampf(float val, float min, float max) noexcept
{ return minf(max, maxf(min, val)); }
constexpr inline double mind(double a, double b) noexcept
{ return ((a > b) ? b : a); }
constexpr inline double maxd(double a, double b) noexcept
{ return ((a > b) ? a : b); }
constexpr inline double clampd(double val, double min, double max) noexcept
{ return mind(max, maxd(min, val)); }
constexpr inline unsigned int minu(unsigned int a, unsigned int b) noexcept
{ return ((a > b) ? b : a); }
constexpr inline unsigned int maxu(unsigned int a, unsigned int b) noexcept
{ return ((a > b) ? a : b); }
constexpr inline unsigned int clampu(unsigned int val, unsigned int min, unsigned int max) noexcept
{ return minu(max, maxu(min, val)); }
constexpr inline int mini(int a, int b) noexcept
{ return ((a > b) ? b : a); }
constexpr inline int maxi(int a, int b) noexcept
{ return ((a > b) ? a : b); }
constexpr inline int clampi(int val, int min, int max) noexcept
{ return mini(max, maxi(min, val)); }
constexpr inline int64_t mini64(int64_t a, int64_t b) noexcept
{ return ((a > b) ? b : a); }
constexpr inline int64_t maxi64(int64_t a, int64_t b) noexcept
{ return ((a > b) ? a : b); }
constexpr inline int64_t clampi64(int64_t val, int64_t min, int64_t max) noexcept
{ return mini64(max, maxi64(min, val)); }
constexpr inline uint64_t minu64(uint64_t a, uint64_t b) noexcept
{ return ((a > b) ? b : a); }
constexpr inline uint64_t maxu64(uint64_t a, uint64_t b) noexcept
{ return ((a > b) ? a : b); }
constexpr inline uint64_t clampu64(uint64_t val, uint64_t min, uint64_t max) noexcept
{ return minu64(max, maxu64(min, val)); }
constexpr inline size_t minz(size_t a, size_t b) noexcept
{ return ((a > b) ? b : a); }
constexpr inline size_t maxz(size_t a, size_t b) noexcept
{ return ((a > b) ? a : b); }
constexpr inline size_t clampz(size_t val, size_t min, size_t max) noexcept
{ return minz(max, maxz(min, val)); }
constexpr inline float lerpf(float val1, float val2, float mu) noexcept
{ return val1 + (val2-val1)*mu; }
constexpr inline float cubic(float val1, float val2, float val3, float val4, float mu) noexcept
{
const float mu2{mu*mu}, mu3{mu2*mu};
const float a0{-0.5f*mu3 + mu2 + -0.5f*mu};
const float a1{ 1.5f*mu3 + -2.5f*mu2 + 1.0f};
const float a2{-1.5f*mu3 + 2.0f*mu2 + 0.5f*mu};
const float a3{ 0.5f*mu3 + -0.5f*mu2};
return val1*a0 + val2*a1 + val3*a2 + val4*a3;
}
/** Find the next power-of-2 for non-power-of-2 numbers. */
inline uint32_t NextPowerOf2(uint32_t value) noexcept
{
if(value > 0)
{
value--;
value |= value>>1;
value |= value>>2;
value |= value>>4;
value |= value>>8;
value |= value>>16;
}
return value+1;
}
/**
* If the value is not already a multiple of r, round down to the next
* multiple.
*/
template<typename T>
constexpr T RoundDown(T value, al::type_identity_t<T> r) noexcept
{ return value - (value%r); }
/**
* If the value is not already a multiple of r, round up to the next multiple.
*/
template<typename T>
constexpr T RoundUp(T value, al::type_identity_t<T> r) noexcept
{ return RoundDown(value + r-1, r); }
/**
* Fast float-to-int conversion. No particular rounding mode is assumed; the
* IEEE-754 default is round-to-nearest with ties-to-even, though an app could
* change it on its own threads. On some systems, a truncating conversion may
* always be the fastest method.
*/
inline int fastf2i(float f) noexcept
{
#if defined(HAVE_SSE_INTRINSICS)
return _mm_cvt_ss2si(_mm_set_ss(f));
#elif defined(_MSC_VER) && defined(_M_IX86_FP)
int i;
__asm fld f
__asm fistp i
return i;
#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
int i;
#ifdef __SSE_MATH__
__asm__("cvtss2si %1, %0" : "=r"(i) : "x"(f));
#else
__asm__ __volatile__("fistpl %0" : "=m"(i) : "t"(f) : "st");
#endif
return i;
#else
return static_cast<int>(f);
#endif
}
inline unsigned int fastf2u(float f) noexcept
{ return static_cast<unsigned int>(fastf2i(f)); }
/** Converts float-to-int using standard behavior (truncation). */
inline int float2int(float f) noexcept
{
#if defined(HAVE_SSE_INTRINSICS)
return _mm_cvtt_ss2si(_mm_set_ss(f));
#elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0) \
|| ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \
&& !defined(__SSE_MATH__))
int sign, shift, mant;
union {
float f;
int i;
} conv;
conv.f = f;
sign = (conv.i>>31) | 1;
shift = ((conv.i>>23)&0xff) - (127+23);
/* Over/underflow */
if(shift >= 31 || shift < -23) UNLIKELY
return 0;
mant = (conv.i&0x7fffff) | 0x800000;
if(shift < 0) LIKELY
return (mant >> -shift) * sign;
return (mant << shift) * sign;
#else
return static_cast<int>(f);
#endif
}
inline unsigned int float2uint(float f) noexcept
{ return static_cast<unsigned int>(float2int(f)); }
/** Converts double-to-int using standard behavior (truncation). */
inline int double2int(double d) noexcept
{
#if defined(HAVE_SSE_INTRINSICS)
return _mm_cvttsd_si32(_mm_set_sd(d));
#elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2) \
|| ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \
&& !defined(__SSE2_MATH__))
int sign, shift;
int64_t mant;
union {
double d;
int64_t i64;
} conv;
conv.d = d;
sign = (conv.i64 >> 63) | 1;
shift = ((conv.i64 >> 52) & 0x7ff) - (1023 + 52);
/* Over/underflow */
if(shift >= 63 || shift < -52) UNLIKELY
return 0;
mant = (conv.i64 & 0xfffffffffffff_i64) | 0x10000000000000_i64;
if(shift < 0) LIKELY
return (int)(mant >> -shift) * sign;
return (int)(mant << shift) * sign;
#else
return static_cast<int>(d);
#endif
}
/**
* Rounds a float to the nearest integral value, according to the current
* rounding mode. This is essentially an inlined version of rintf, although
* makes fewer promises (e.g. -0 or -0.25 rounded to 0 may result in +0).
*/
inline float fast_roundf(float f) noexcept
{
#if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \
&& !defined(__SSE_MATH__)
float out;
__asm__ __volatile__("frndint" : "=t"(out) : "0"(f));
return out;
#elif (defined(__GNUC__) || defined(__clang__)) && defined(__aarch64__)
float out;
__asm__ volatile("frintx %s0, %s1" : "=w"(out) : "w"(f));
return out;
#else
/* Integral limit, where sub-integral precision is not available for
* floats.
*/
static const float ilim[2]{
8388608.0f /* 0x1.0p+23 */,
-8388608.0f /* -0x1.0p+23 */
};
unsigned int sign, expo;
union {
float f;
unsigned int i;
} conv;
conv.f = f;
sign = (conv.i>>31)&0x01;
expo = (conv.i>>23)&0xff;
if(expo >= 150/*+23*/) UNLIKELY
{
/* An exponent (base-2) of 23 or higher is incapable of sub-integral
* precision, so it's already an integral value. We don't need to worry
* about infinity or NaN here.
*/
return f;
}
/* Adding the integral limit to the value (with a matching sign) forces a
* result that has no sub-integral precision, and is consequently forced to
* round to an integral value. Removing the integral limit then restores
* the initial value rounded to the integral. The compiler should not
* optimize this out because of non-associative rules on floating-point
* math (as long as you don't use -fassociative-math,
* -funsafe-math-optimizations, -ffast-math, or -Ofast, in which case this
* may break).
*/
f += ilim[sign];
return f - ilim[sign];
#endif
}
template<typename T>
constexpr const T& clamp(const T& value, const T& min_value, const T& max_value) noexcept
{
return std::min(std::max(value, min_value), max_value);
}
// Converts level (mB) to gain.
inline float level_mb_to_gain(float x)
{
if(x <= -10'000.0f)
return 0.0f;
return std::pow(10.0f, x / 2'000.0f);
}
// Converts gain to level (mB).
inline float gain_to_level_mb(float x)
{
if (x <= 0.0f)
return -10'000.0f;
return maxf(std::log10(x) * 2'000.0f, -10'000.0f);
}
#endif /* AL_NUMERIC_H */

View File

@@ -0,0 +1,353 @@
#ifndef AL_OPTIONAL_H
#define AL_OPTIONAL_H
#include <initializer_list>
#include <type_traits>
#include <utility>
#include "almalloc.h"
namespace al {
struct nullopt_t { };
struct in_place_t { };
constexpr nullopt_t nullopt{};
constexpr in_place_t in_place{};
#define NOEXCEPT_AS(...) noexcept(noexcept(__VA_ARGS__))
namespace detail_ {
/* Base storage struct for an optional. Defines a trivial destructor, for types
* that can be trivially destructed.
*/
template<typename T, bool = std::is_trivially_destructible<T>::value>
struct optstore_base {
bool mHasValue{false};
union {
char mDummy{};
T mValue;
};
constexpr optstore_base() noexcept { }
template<typename ...Args>
constexpr explicit optstore_base(in_place_t, Args&& ...args)
noexcept(std::is_nothrow_constructible<T, Args...>::value)
: mHasValue{true}, mValue{std::forward<Args>(args)...}
{ }
~optstore_base() = default;
};
/* Specialization needing a non-trivial destructor. */
template<typename T>
struct optstore_base<T, false> {
bool mHasValue{false};
union {
char mDummy{};
T mValue;
};
constexpr optstore_base() noexcept { }
template<typename ...Args>
constexpr explicit optstore_base(in_place_t, Args&& ...args)
noexcept(std::is_nothrow_constructible<T, Args...>::value)
: mHasValue{true}, mValue{std::forward<Args>(args)...}
{ }
~optstore_base() { if(mHasValue) al::destroy_at(std::addressof(mValue)); }
};
/* Next level of storage, which defines helpers to construct and destruct the
* stored object.
*/
template<typename T>
struct optstore_helper : public optstore_base<T> {
using optstore_base<T>::optstore_base;
template<typename... Args>
constexpr void construct(Args&& ...args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
{
al::construct_at(std::addressof(this->mValue), std::forward<Args>(args)...);
this->mHasValue = true;
}
constexpr void reset() noexcept
{
if(this->mHasValue)
al::destroy_at(std::addressof(this->mValue));
this->mHasValue = false;
}
constexpr void assign(const optstore_helper &rhs)
noexcept(std::is_nothrow_copy_constructible<T>::value
&& std::is_nothrow_copy_assignable<T>::value)
{
if(!rhs.mHasValue)
this->reset();
else if(this->mHasValue)
this->mValue = rhs.mValue;
else
this->construct(rhs.mValue);
}
constexpr void assign(optstore_helper&& rhs)
noexcept(std::is_nothrow_move_constructible<T>::value
&& std::is_nothrow_move_assignable<T>::value)
{
if(!rhs.mHasValue)
this->reset();
else if(this->mHasValue)
this->mValue = std::move(rhs.mValue);
else
this->construct(std::move(rhs.mValue));
}
};
/* Define copy and move constructors and assignment operators, which may or may
* not be trivial.
*/
template<typename T, bool trivial_copy = std::is_trivially_copy_constructible<T>::value,
bool trivial_move = std::is_trivially_move_constructible<T>::value,
/* Trivial assignment is dependent on trivial construction+destruction. */
bool = trivial_copy && std::is_trivially_copy_assignable<T>::value
&& std::is_trivially_destructible<T>::value,
bool = trivial_move && std::is_trivially_move_assignable<T>::value
&& std::is_trivially_destructible<T>::value>
struct optional_storage;
/* Some versions of GCC have issues with 'this' in the following noexcept(...)
* statements, so this macro is a workaround.
*/
#define _this std::declval<optional_storage*>()
/* Completely trivial. */
template<typename T>
struct optional_storage<T, true, true, true, true> : public optstore_helper<T> {
using optstore_helper<T>::optstore_helper;
constexpr optional_storage() noexcept = default;
constexpr optional_storage(const optional_storage&) = default;
constexpr optional_storage(optional_storage&&) = default;
constexpr optional_storage& operator=(const optional_storage&) = default;
constexpr optional_storage& operator=(optional_storage&&) = default;
};
/* Non-trivial move assignment. */
template<typename T>
struct optional_storage<T, true, true, true, false> : public optstore_helper<T> {
using optstore_helper<T>::optstore_helper;
constexpr optional_storage() noexcept = default;
constexpr optional_storage(const optional_storage&) = default;
constexpr optional_storage(optional_storage&&) = default;
constexpr optional_storage& operator=(const optional_storage&) = default;
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
{ this->assign(std::move(rhs)); return *this; }
};
/* Non-trivial move construction. */
template<typename T>
struct optional_storage<T, true, false, true, false> : public optstore_helper<T> {
using optstore_helper<T>::optstore_helper;
constexpr optional_storage() noexcept = default;
constexpr optional_storage(const optional_storage&) = default;
constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
{ if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
constexpr optional_storage& operator=(const optional_storage&) = default;
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
{ this->assign(std::move(rhs)); return *this; }
};
/* Non-trivial copy assignment. */
template<typename T>
struct optional_storage<T, true, true, false, true> : public optstore_helper<T> {
using optstore_helper<T>::optstore_helper;
constexpr optional_storage() noexcept = default;
constexpr optional_storage(const optional_storage&) = default;
constexpr optional_storage(optional_storage&&) = default;
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
{ this->assign(rhs); return *this; }
constexpr optional_storage& operator=(optional_storage&&) = default;
};
/* Non-trivial copy construction. */
template<typename T>
struct optional_storage<T, false, true, false, true> : public optstore_helper<T> {
using optstore_helper<T>::optstore_helper;
constexpr optional_storage() noexcept = default;
constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
{ if(rhs.mHasValue) this->construct(rhs.mValue); }
constexpr optional_storage(optional_storage&&) = default;
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
{ this->assign(rhs); return *this; }
constexpr optional_storage& operator=(optional_storage&&) = default;
};
/* Non-trivial assignment. */
template<typename T>
struct optional_storage<T, true, true, false, false> : public optstore_helper<T> {
using optstore_helper<T>::optstore_helper;
constexpr optional_storage() noexcept = default;
constexpr optional_storage(const optional_storage&) = default;
constexpr optional_storage(optional_storage&&) = default;
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
{ this->assign(rhs); return *this; }
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
{ this->assign(std::move(rhs)); return *this; }
};
/* Non-trivial assignment, non-trivial move construction. */
template<typename T>
struct optional_storage<T, true, false, false, false> : public optstore_helper<T> {
using optstore_helper<T>::optstore_helper;
constexpr optional_storage() noexcept = default;
constexpr optional_storage(const optional_storage&) = default;
constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
{ if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
{ this->assign(rhs); return *this; }
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
{ this->assign(std::move(rhs)); return *this; }
};
/* Non-trivial assignment, non-trivial copy construction. */
template<typename T>
struct optional_storage<T, false, true, false, false> : public optstore_helper<T> {
using optstore_helper<T>::optstore_helper;
constexpr optional_storage() noexcept = default;
constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
{ if(rhs.mHasValue) this->construct(rhs.mValue); }
constexpr optional_storage(optional_storage&&) = default;
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
{ this->assign(rhs); return *this; }
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
{ this->assign(std::move(rhs)); return *this; }
};
/* Completely non-trivial. */
template<typename T>
struct optional_storage<T, false, false, false, false> : public optstore_helper<T> {
using optstore_helper<T>::optstore_helper;
constexpr optional_storage() noexcept = default;
constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
{ if(rhs.mHasValue) this->construct(rhs.mValue); }
constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
{ if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
{ this->assign(rhs); return *this; }
constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
{ this->assign(std::move(rhs)); return *this; }
};
#undef _this
} // namespace detail_
#define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true
template<typename T>
class optional {
using storage_t = detail_::optional_storage<T>;
storage_t mStore{};
public:
using value_type = T;
constexpr optional() = default;
constexpr optional(const optional&) = default;
constexpr optional(optional&&) = default;
constexpr optional(nullopt_t) noexcept { }
template<typename ...Args>
constexpr explicit optional(in_place_t, Args&& ...args)
NOEXCEPT_AS(storage_t{al::in_place, std::forward<Args>(args)...})
: mStore{al::in_place, std::forward<Args>(args)...}
{ }
template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
&& !std::is_same<std::decay_t<U>, al::in_place_t>::value
&& !std::is_same<std::decay_t<U>, optional<T>>::value
&& std::is_convertible<U&&, T>::value)>
constexpr optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
: mStore{al::in_place, std::forward<U>(rhs)}
{ }
template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
&& !std::is_same<std::decay_t<U>, al::in_place_t>::value
&& !std::is_same<std::decay_t<U>, optional<T>>::value
&& !std::is_convertible<U&&, T>::value)>
constexpr explicit optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
: mStore{al::in_place, std::forward<U>(rhs)}
{ }
~optional() = default;
constexpr optional& operator=(const optional&) = default;
constexpr optional& operator=(optional&&) = default;
constexpr optional& operator=(nullopt_t) noexcept { mStore.reset(); return *this; }
template<typename U=T>
constexpr std::enable_if_t<std::is_constructible<T, U>::value
&& std::is_assignable<T&, U>::value
&& !std::is_same<std::decay_t<U>, optional<T>>::value
&& (!std::is_same<std::decay_t<U>, T>::value || !std::is_scalar<U>::value),
optional&> operator=(U&& rhs)
{
if(mStore.mHasValue)
mStore.mValue = std::forward<U>(rhs);
else
mStore.construct(std::forward<U>(rhs));
return *this;
}
constexpr const T* operator->() const { return std::addressof(mStore.mValue); }
constexpr T* operator->() { return std::addressof(mStore.mValue); }
constexpr const T& operator*() const& { return mStore.mValue; }
constexpr T& operator*() & { return mStore.mValue; }
constexpr const T&& operator*() const&& { return std::move(mStore.mValue); }
constexpr T&& operator*() && { return std::move(mStore.mValue); }
constexpr explicit operator bool() const noexcept { return mStore.mHasValue; }
constexpr bool has_value() const noexcept { return mStore.mHasValue; }
constexpr T& value() & { return mStore.mValue; }
constexpr const T& value() const& { return mStore.mValue; }
constexpr T&& value() && { return std::move(mStore.mValue); }
constexpr const T&& value() const&& { return std::move(mStore.mValue); }
template<typename U>
constexpr T value_or(U&& defval) const&
{ return bool(*this) ? **this : static_cast<T>(std::forward<U>(defval)); }
template<typename U>
constexpr T value_or(U&& defval) &&
{ return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }
template<typename ...Args>
constexpr T& emplace(Args&& ...args)
{
mStore.reset();
mStore.construct(std::forward<Args>(args)...);
return mStore.mValue;
}
template<typename U, typename ...Args>
constexpr std::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
T&> emplace(std::initializer_list<U> il, Args&& ...args)
{
mStore.reset();
mStore.construct(il, std::forward<Args>(args)...);
return mStore.mValue;
}
constexpr void reset() noexcept { mStore.reset(); }
};
template<typename T>
constexpr optional<std::decay_t<T>> make_optional(T&& arg)
{ return optional<std::decay_t<T>>{in_place, std::forward<T>(arg)}; }
template<typename T, typename... Args>
constexpr optional<T> make_optional(Args&& ...args)
{ return optional<T>{in_place, std::forward<Args>(args)...}; }
template<typename T, typename U, typename... Args>
constexpr optional<T> make_optional(std::initializer_list<U> il, Args&& ...args)
{ return optional<T>{in_place, il, std::forward<Args>(args)...}; }
#undef REQUIRES
#undef NOEXCEPT_AS
} // namespace al
#endif /* AL_OPTIONAL_H */

354
externals/openal-soft/common/alspan.h vendored Normal file
View File

@@ -0,0 +1,354 @@
#ifndef AL_SPAN_H
#define AL_SPAN_H
#include <array>
#include <cstddef>
#include <initializer_list>
#include <iterator>
#include <type_traits>
#include "almalloc.h"
#include "altraits.h"
namespace al {
template<typename T>
constexpr auto size(const T &cont) noexcept(noexcept(cont.size())) -> decltype(cont.size())
{ return cont.size(); }
template<typename T, size_t N>
constexpr size_t size(const T (&)[N]) noexcept
{ return N; }
template<typename T>
constexpr auto data(T &cont) noexcept(noexcept(cont.data())) -> decltype(cont.data())
{ return cont.data(); }
template<typename T>
constexpr auto data(const T &cont) noexcept(noexcept(cont.data())) -> decltype(cont.data())
{ return cont.data(); }
template<typename T, size_t N>
constexpr T* data(T (&arr)[N]) noexcept
{ return arr; }
template<typename T>
constexpr const T* data(std::initializer_list<T> list) noexcept
{ return list.begin(); }
constexpr size_t dynamic_extent{static_cast<size_t>(-1)};
template<typename T, size_t E=dynamic_extent>
class span;
namespace detail_ {
template<typename... Ts>
using void_t = void;
template<typename T>
struct is_span_ : std::false_type { };
template<typename T, size_t E>
struct is_span_<span<T,E>> : std::true_type { };
template<typename T>
constexpr bool is_span_v = is_span_<std::remove_cv_t<T>>::value;
template<typename T>
struct is_std_array_ : std::false_type { };
template<typename T, size_t N>
struct is_std_array_<std::array<T,N>> : std::true_type { };
template<typename T>
constexpr bool is_std_array_v = is_std_array_<std::remove_cv_t<T>>::value;
template<typename T, typename = void>
constexpr bool has_size_and_data = false;
template<typename T>
constexpr bool has_size_and_data<T,
void_t<decltype(al::size(std::declval<T>())), decltype(al::data(std::declval<T>()))>>
= true;
template<typename T, typename U>
constexpr bool is_array_compatible = std::is_convertible<T(*)[],U(*)[]>::value;
template<typename C, typename T>
constexpr bool is_valid_container = !is_span_v<C> && !is_std_array_v<C>
&& !std::is_array<C>::value && has_size_and_data<C>
&& is_array_compatible<std::remove_pointer_t<decltype(al::data(std::declval<C&>()))>,T>;
} // namespace detail_
#define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true
template<typename T, size_t E>
class span {
public:
using element_type = T;
using value_type = std::remove_cv_t<T>;
using index_type = size_t;
using difference_type = ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr size_t extent{E};
template<bool is0=(extent == 0), REQUIRES(is0)>
constexpr span() noexcept { }
template<typename U>
constexpr explicit span(U iter, index_type) : mData{to_address(iter)} { }
template<typename U, typename V, REQUIRES(!std::is_convertible<V,size_t>::value)>
constexpr explicit span(U first, V) : mData{to_address(first)} { }
constexpr span(type_identity_t<element_type> (&arr)[E]) noexcept
: span{al::data(arr), al::size(arr)}
{ }
constexpr span(std::array<value_type,E> &arr) noexcept : span{al::data(arr), al::size(arr)} { }
template<typename U=T, REQUIRES(std::is_const<U>::value)>
constexpr span(const std::array<value_type,E> &arr) noexcept
: span{al::data(arr), al::size(arr)}
{ }
template<typename U, REQUIRES(detail_::is_valid_container<U, element_type>)>
constexpr explicit span(U&& cont) : span{al::data(cont), al::size(cont)} { }
template<typename U, index_type N, REQUIRES(!std::is_same<element_type,U>::value
&& detail_::is_array_compatible<U,element_type> && N == dynamic_extent)>
constexpr explicit span(const span<U,N> &span_) noexcept
: span{al::data(span_), al::size(span_)}
{ }
template<typename U, index_type N, REQUIRES(!std::is_same<element_type,U>::value
&& detail_::is_array_compatible<U,element_type> && N == extent)>
constexpr span(const span<U,N> &span_) noexcept : span{al::data(span_), al::size(span_)} { }
constexpr span(const span&) noexcept = default;
constexpr span& operator=(const span &rhs) noexcept = default;
constexpr reference front() const { return *mData; }
constexpr reference back() const { return *(mData+E-1); }
constexpr reference operator[](index_type idx) const { return mData[idx]; }
constexpr pointer data() const noexcept { return mData; }
constexpr index_type size() const noexcept { return E; }
constexpr index_type size_bytes() const noexcept { return E * sizeof(value_type); }
constexpr bool empty() const noexcept { return E == 0; }
constexpr iterator begin() const noexcept { return mData; }
constexpr iterator end() const noexcept { return mData+E; }
constexpr const_iterator cbegin() const noexcept { return mData; }
constexpr const_iterator cend() const noexcept { return mData+E; }
constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
constexpr const_reverse_iterator crbegin() const noexcept
{ return const_reverse_iterator{cend()}; }
constexpr const_reverse_iterator crend() const noexcept
{ return const_reverse_iterator{cbegin()}; }
template<size_t C>
constexpr span<element_type,C> first() const
{
static_assert(E >= C, "New size exceeds original capacity");
return span<element_type,C>{mData, C};
}
template<size_t C>
constexpr span<element_type,C> last() const
{
static_assert(E >= C, "New size exceeds original capacity");
return span<element_type,C>{mData+(E-C), C};
}
template<size_t O, size_t C>
constexpr auto subspan() const -> std::enable_if_t<C!=dynamic_extent,span<element_type,C>>
{
static_assert(E >= O, "Offset exceeds extent");
static_assert(E-O >= C, "New size exceeds original capacity");
return span<element_type,C>{mData+O, C};
}
template<size_t O, size_t C=dynamic_extent>
constexpr auto subspan() const -> std::enable_if_t<C==dynamic_extent,span<element_type,E-O>>
{
static_assert(E >= O, "Offset exceeds extent");
return span<element_type,E-O>{mData+O, E-O};
}
/* NOTE: Can't declare objects of a specialized template class prior to
* defining the specialization. As a result, these methods need to be
* defined later.
*/
constexpr span<element_type,dynamic_extent> first(size_t count) const;
constexpr span<element_type,dynamic_extent> last(size_t count) const;
constexpr span<element_type,dynamic_extent> subspan(size_t offset,
size_t count=dynamic_extent) const;
private:
pointer mData{nullptr};
};
template<typename T>
class span<T,dynamic_extent> {
public:
using element_type = T;
using value_type = std::remove_cv_t<T>;
using index_type = size_t;
using difference_type = ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr size_t extent{dynamic_extent};
constexpr span() noexcept = default;
template<typename U>
constexpr span(U iter, index_type count)
: mData{to_address(iter)}, mDataEnd{to_address(iter)+count}
{ }
template<typename U, typename V, REQUIRES(!std::is_convertible<V,size_t>::value)>
constexpr span(U first, V last) : span{to_address(first), static_cast<size_t>(last-first)}
{ }
template<size_t N>
constexpr span(type_identity_t<element_type> (&arr)[N]) noexcept
: span{al::data(arr), al::size(arr)}
{ }
template<size_t N>
constexpr span(std::array<value_type,N> &arr) noexcept : span{al::data(arr), al::size(arr)} { }
template<size_t N, typename U=T, REQUIRES(std::is_const<U>::value)>
constexpr span(const std::array<value_type,N> &arr) noexcept
: span{al::data(arr), al::size(arr)}
{ }
template<typename U, REQUIRES(detail_::is_valid_container<U, element_type>)>
constexpr span(U&& cont) : span{al::data(cont), al::size(cont)} { }
template<typename U, size_t N, REQUIRES((!std::is_same<element_type,U>::value || extent != N)
&& detail_::is_array_compatible<U,element_type>)>
constexpr span(const span<U,N> &span_) noexcept : span{al::data(span_), al::size(span_)} { }
constexpr span(const span&) noexcept = default;
constexpr span& operator=(const span &rhs) noexcept = default;
constexpr reference front() const { return *mData; }
constexpr reference back() const { return *(mDataEnd-1); }
constexpr reference operator[](index_type idx) const { return mData[idx]; }
constexpr pointer data() const noexcept { return mData; }
constexpr index_type size() const noexcept { return static_cast<index_type>(mDataEnd-mData); }
constexpr index_type size_bytes() const noexcept
{ return static_cast<index_type>(mDataEnd-mData) * sizeof(value_type); }
constexpr bool empty() const noexcept { return mData == mDataEnd; }
constexpr iterator begin() const noexcept { return mData; }
constexpr iterator end() const noexcept { return mDataEnd; }
constexpr const_iterator cbegin() const noexcept { return mData; }
constexpr const_iterator cend() const noexcept { return mDataEnd; }
constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
constexpr const_reverse_iterator crbegin() const noexcept
{ return const_reverse_iterator{cend()}; }
constexpr const_reverse_iterator crend() const noexcept
{ return const_reverse_iterator{cbegin()}; }
template<size_t C>
constexpr span<element_type,C> first() const
{ return span<element_type,C>{mData, C}; }
constexpr span first(size_t count) const
{ return (count >= size()) ? *this : span{mData, mData+count}; }
template<size_t C>
constexpr span<element_type,C> last() const
{ return span<element_type,C>{mDataEnd-C, C}; }
constexpr span last(size_t count) const
{ return (count >= size()) ? *this : span{mDataEnd-count, mDataEnd}; }
template<size_t O, size_t C>
constexpr auto subspan() const -> std::enable_if_t<C!=dynamic_extent,span<element_type,C>>
{ return span<element_type,C>{mData+O, C}; }
template<size_t O, size_t C=dynamic_extent>
constexpr auto subspan() const -> std::enable_if_t<C==dynamic_extent,span<element_type,C>>
{ return span<element_type,C>{mData+O, mDataEnd}; }
constexpr span subspan(size_t offset, size_t count=dynamic_extent) const
{
return (offset > size()) ? span{} :
(count >= size()-offset) ? span{mData+offset, mDataEnd} :
span{mData+offset, mData+offset+count};
}
private:
pointer mData{nullptr};
pointer mDataEnd{nullptr};
};
template<typename T, size_t E>
constexpr inline auto span<T,E>::first(size_t count) const -> span<element_type,dynamic_extent>
{
return (count >= size()) ? span<element_type>{mData, extent} :
span<element_type>{mData, count};
}
template<typename T, size_t E>
constexpr inline auto span<T,E>::last(size_t count) const -> span<element_type,dynamic_extent>
{
return (count >= size()) ? span<element_type>{mData, extent} :
span<element_type>{mData+extent-count, count};
}
template<typename T, size_t E>
constexpr inline auto span<T,E>::subspan(size_t offset, size_t count) const
-> span<element_type,dynamic_extent>
{
return (offset > size()) ? span<element_type>{} :
(count >= size()-offset) ? span<element_type>{mData+offset, mData+extent} :
span<element_type>{mData+offset, mData+offset+count};
}
/* Helpers to deal with the lack of user-defined deduction guides (C++17). */
template<typename T, typename U>
constexpr auto as_span(T ptr, U count_or_end)
{
using value_type = typename std::pointer_traits<T>::element_type;
return span<value_type>{ptr, count_or_end};
}
template<typename T, size_t N>
constexpr auto as_span(T (&arr)[N]) noexcept { return span<T,N>{al::data(arr), al::size(arr)}; }
template<typename T, size_t N>
constexpr auto as_span(std::array<T,N> &arr) noexcept
{ return span<T,N>{al::data(arr), al::size(arr)}; }
template<typename T, size_t N>
constexpr auto as_span(const std::array<T,N> &arr) noexcept
{ return span<std::add_const_t<T>,N>{al::data(arr), al::size(arr)}; }
template<typename U, REQUIRES(!detail_::is_span_v<U> && !detail_::is_std_array_v<U>
&& !std::is_array<U>::value && detail_::has_size_and_data<U>)>
constexpr auto as_span(U&& cont)
{
using value_type = std::remove_pointer_t<decltype(al::data(std::declval<U&>()))>;
return span<value_type>{al::data(cont), al::size(cont)};
}
template<typename T, size_t N>
constexpr auto as_span(span<T,N> span_) noexcept { return span_; }
#undef REQUIRES
} // namespace al
#endif /* AL_SPAN_H */

View File

@@ -0,0 +1,45 @@
#include "config.h"
#include "alstring.h"
#include <cctype>
#include <string>
namespace {
int to_upper(const char ch)
{
using char8_traits = std::char_traits<char>;
return std::toupper(char8_traits::to_int_type(ch));
}
} // namespace
namespace al {
int strcasecmp(const char *str0, const char *str1) noexcept
{
do {
const int diff{to_upper(*str0) - to_upper(*str1)};
if(diff < 0) return -1;
if(diff > 0) return 1;
} while(*(str0++) && *(str1++));
return 0;
}
int strncasecmp(const char *str0, const char *str1, std::size_t len) noexcept
{
if(len > 0)
{
do {
const int diff{to_upper(*str0) - to_upper(*str1)};
if(diff < 0) return -1;
if(diff > 0) return 1;
} while(--len && *(str0++) && *(str1++));
}
return 0;
}
} // namespace al

18
externals/openal-soft/common/alstring.h vendored Normal file
View File

@@ -0,0 +1,18 @@
#ifndef AL_STRING_H
#define AL_STRING_H
#include <cstddef>
#include <cstring>
namespace al {
/* These would be better served by using a string_view-like span/view with
* case-insensitive char traits.
*/
int strcasecmp(const char *str0, const char *str1) noexcept;
int strncasecmp(const char *str0, const char *str1, std::size_t len) noexcept;
} // namespace al
#endif /* AL_STRING_H */

14
externals/openal-soft/common/altraits.h vendored Normal file
View File

@@ -0,0 +1,14 @@
#ifndef COMMON_ALTRAITS_H
#define COMMON_ALTRAITS_H
namespace al {
template<typename T>
struct type_identity { using type = T; };
template<typename T>
using type_identity_t = typename type_identity<T>::type;
} // namespace al
#endif /* COMMON_ALTRAITS_H */

33
externals/openal-soft/common/atomic.h vendored Normal file
View File

@@ -0,0 +1,33 @@
#ifndef AL_ATOMIC_H
#define AL_ATOMIC_H
#include <atomic>
using RefCount = std::atomic<unsigned int>;
inline void InitRef(RefCount &ref, unsigned int value)
{ ref.store(value, std::memory_order_relaxed); }
inline unsigned int ReadRef(RefCount &ref)
{ return ref.load(std::memory_order_acquire); }
inline unsigned int IncrementRef(RefCount &ref)
{ return ref.fetch_add(1u, std::memory_order_acq_rel)+1u; }
inline unsigned int DecrementRef(RefCount &ref)
{ return ref.fetch_sub(1u, std::memory_order_acq_rel)-1u; }
/* WARNING: A livelock is theoretically possible if another thread keeps
* changing the head without giving this a chance to actually swap in the new
* one (practically impossible with this little code, but...).
*/
template<typename T>
inline void AtomicReplaceHead(std::atomic<T> &head, T newhead)
{
T first_ = head.load(std::memory_order_acquire);
do {
newhead->next.store(first_, std::memory_order_relaxed);
} while(!head.compare_exchange_weak(first_, newhead,
std::memory_order_acq_rel, std::memory_order_acquire));
}
#endif /* AL_ATOMIC_H */

68
externals/openal-soft/common/comptr.h vendored Normal file
View File

@@ -0,0 +1,68 @@
#ifndef COMMON_COMPTR_H
#define COMMON_COMPTR_H
#include <cstddef>
#include <utility>
#include "opthelpers.h"
template<typename T>
class ComPtr {
T *mPtr{nullptr};
public:
ComPtr() noexcept = default;
ComPtr(const ComPtr &rhs) : mPtr{rhs.mPtr} { if(mPtr) mPtr->AddRef(); }
ComPtr(ComPtr&& rhs) noexcept : mPtr{rhs.mPtr} { rhs.mPtr = nullptr; }
ComPtr(std::nullptr_t) noexcept { }
explicit ComPtr(T *ptr) noexcept : mPtr{ptr} { }
~ComPtr() { if(mPtr) mPtr->Release(); }
ComPtr& operator=(const ComPtr &rhs)
{
if(!rhs.mPtr)
{
if(mPtr)
mPtr->Release();
mPtr = nullptr;
}
else
{
rhs.mPtr->AddRef();
try {
if(mPtr)
mPtr->Release();
mPtr = rhs.mPtr;
}
catch(...) {
rhs.mPtr->Release();
throw;
}
}
return *this;
}
ComPtr& operator=(ComPtr&& rhs)
{
if(&rhs != this) LIKELY
{
if(mPtr) mPtr->Release();
mPtr = std::exchange(rhs.mPtr, nullptr);
}
return *this;
}
explicit operator bool() const noexcept { return mPtr != nullptr; }
T& operator*() const noexcept { return *mPtr; }
T* operator->() const noexcept { return mPtr; }
T* get() const noexcept { return mPtr; }
T** getPtr() noexcept { return &mPtr; }
T* release() noexcept { return std::exchange(mPtr, nullptr); }
void swap(ComPtr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
void swap(ComPtr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
};
#endif

View File

@@ -0,0 +1,44 @@
#include "config.h"
#include "dynload.h"
#include "strutils.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
void *LoadLib(const char *name)
{
std::wstring wname{utf8_to_wstr(name)};
return LoadLibraryW(wname.c_str());
}
void CloseLib(void *handle)
{ FreeLibrary(static_cast<HMODULE>(handle)); }
void *GetSymbol(void *handle, const char *name)
{ return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name)); }
#elif defined(HAVE_DLFCN_H)
#include <dlfcn.h>
void *LoadLib(const char *name)
{
dlerror();
void *handle{dlopen(name, RTLD_NOW)};
const char *err{dlerror()};
if(err) handle = nullptr;
return handle;
}
void CloseLib(void *handle)
{ dlclose(handle); }
void *GetSymbol(void *handle, const char *name)
{
dlerror();
void *sym{dlsym(handle, name)};
const char *err{dlerror()};
if(err) sym = nullptr;
return sym;
}
#endif

14
externals/openal-soft/common/dynload.h vendored Normal file
View File

@@ -0,0 +1,14 @@
#ifndef AL_DYNLOAD_H
#define AL_DYNLOAD_H
#if defined(_WIN32) || defined(HAVE_DLFCN_H)
#define HAVE_DYNLOAD
void *LoadLib(const char *name);
void CloseLib(void *handle);
void *GetSymbol(void *handle, const char *name);
#endif
#endif /* AL_DYNLOAD_H */

View File

@@ -0,0 +1,120 @@
#ifndef INTRUSIVE_PTR_H
#define INTRUSIVE_PTR_H
#include <utility>
#include "atomic.h"
#include "opthelpers.h"
namespace al {
template<typename T>
class intrusive_ref {
RefCount mRef{1u};
public:
unsigned int add_ref() noexcept { return IncrementRef(mRef); }
unsigned int dec_ref() noexcept
{
auto ref = DecrementRef(mRef);
if(ref == 0) UNLIKELY
delete static_cast<T*>(this);
return ref;
}
/**
* Release only if doing so would not bring the object to 0 references and
* delete it. Returns false if the object could not be released.
*
* NOTE: The caller is responsible for handling a failed release, as it
* means the object has no other references and needs to be be deleted
* somehow.
*/
bool releaseIfNoDelete() noexcept
{
auto val = mRef.load(std::memory_order_acquire);
while(val > 1 && !mRef.compare_exchange_strong(val, val-1, std::memory_order_acq_rel))
{
/* val was updated with the current value on failure, so just try
* again.
*/
}
return val >= 2;
}
};
template<typename T>
class intrusive_ptr {
T *mPtr{nullptr};
public:
intrusive_ptr() noexcept = default;
intrusive_ptr(const intrusive_ptr &rhs) noexcept : mPtr{rhs.mPtr}
{ if(mPtr) mPtr->add_ref(); }
intrusive_ptr(intrusive_ptr&& rhs) noexcept : mPtr{rhs.mPtr}
{ rhs.mPtr = nullptr; }
intrusive_ptr(std::nullptr_t) noexcept { }
explicit intrusive_ptr(T *ptr) noexcept : mPtr{ptr} { }
~intrusive_ptr() { if(mPtr) mPtr->dec_ref(); }
intrusive_ptr& operator=(const intrusive_ptr &rhs) noexcept
{
static_assert(noexcept(std::declval<T*>()->dec_ref()), "dec_ref must be noexcept");
if(rhs.mPtr) rhs.mPtr->add_ref();
if(mPtr) mPtr->dec_ref();
mPtr = rhs.mPtr;
return *this;
}
intrusive_ptr& operator=(intrusive_ptr&& rhs) noexcept
{
if(&rhs != this) LIKELY
{
if(mPtr) mPtr->dec_ref();
mPtr = std::exchange(rhs.mPtr, nullptr);
}
return *this;
}
explicit operator bool() const noexcept { return mPtr != nullptr; }
T& operator*() const noexcept { return *mPtr; }
T* operator->() const noexcept { return mPtr; }
T* get() const noexcept { return mPtr; }
void reset(T *ptr=nullptr) noexcept
{
if(mPtr)
mPtr->dec_ref();
mPtr = ptr;
}
T* release() noexcept { return std::exchange(mPtr, nullptr); }
void swap(intrusive_ptr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
void swap(intrusive_ptr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
};
#define AL_DECL_OP(op) \
template<typename T> \
inline bool operator op(const intrusive_ptr<T> &lhs, const T *rhs) noexcept \
{ return lhs.get() op rhs; } \
template<typename T> \
inline bool operator op(const T *lhs, const intrusive_ptr<T> &rhs) noexcept \
{ return lhs op rhs.get(); }
AL_DECL_OP(==)
AL_DECL_OP(!=)
AL_DECL_OP(<=)
AL_DECL_OP(>=)
AL_DECL_OP(<)
AL_DECL_OP(>)
#undef AL_DECL_OP
} // namespace al
#endif /* INTRUSIVE_PTR_H */

View File

@@ -0,0 +1,93 @@
#ifndef OPTHELPERS_H
#define OPTHELPERS_H
#include <cstdint>
#include <utility>
#include <memory>
#ifdef __has_builtin
#define HAS_BUILTIN __has_builtin
#else
#define HAS_BUILTIN(x) (0)
#endif
#ifdef __has_cpp_attribute
#define HAS_ATTRIBUTE __has_cpp_attribute
#else
#define HAS_ATTRIBUTE(x) (0)
#endif
#ifdef __GNUC__
#define force_inline [[gnu::always_inline]] inline
#elif defined(_MSC_VER)
#define force_inline __forceinline
#else
#define force_inline inline
#endif
/* Unlike the likely attribute, ASSUME requires the condition to be true or
* else it invokes undefined behavior. It's essentially an assert without
* actually checking the condition at run-time, allowing for stronger
* optimizations than the likely attribute.
*/
#if HAS_BUILTIN(__builtin_assume)
#define ASSUME __builtin_assume
#elif defined(_MSC_VER)
#define ASSUME __assume
#elif __has_attribute(assume)
#define ASSUME(x) [[assume(x)]]
#elif HAS_BUILTIN(__builtin_unreachable)
#define ASSUME(x) do { if(x) break; __builtin_unreachable(); } while(0)
#else
#define ASSUME(x) ((void)0)
#endif
/* This shouldn't be needed since unknown attributes are ignored, but older
* versions of GCC choke on the attribute syntax in certain situations.
*/
#if HAS_ATTRIBUTE(likely)
#define LIKELY [[likely]]
#define UNLIKELY [[unlikely]]
#else
#define LIKELY
#define UNLIKELY
#endif
namespace al {
template<typename T>
constexpr std::underlying_type_t<T> to_underlying(T e) noexcept
{ return static_cast<std::underlying_type_t<T>>(e); }
[[noreturn]] inline void unreachable()
{
#if HAS_BUILTIN(__builtin_unreachable)
__builtin_unreachable();
#else
ASSUME(false);
#endif
}
template<std::size_t alignment, typename T>
force_inline constexpr auto assume_aligned(T *ptr) noexcept
{
#ifdef __cpp_lib_assume_aligned
return std::assume_aligned<alignment,T>(ptr);
#elif HAS_BUILTIN(__builtin_assume_aligned)
return static_cast<T*>(__builtin_assume_aligned(ptr, alignment));
#elif defined(_MSC_VER)
constexpr std::size_t alignment_mask{(1<<alignment) - 1};
if((reinterpret_cast<std::uintptr_t>(ptr)&alignment_mask) == 0)
return ptr;
__assume(0);
#elif defined(__ICC)
__assume_aligned(ptr, alignment);
return ptr;
#else
return ptr;
#endif
}
} // namespace al
#endif /* OPTHELPERS_H */

View File

@@ -0,0 +1,214 @@
#ifndef PHASE_SHIFTER_H
#define PHASE_SHIFTER_H
#ifdef HAVE_SSE_INTRINSICS
#include <xmmintrin.h>
#elif defined(HAVE_NEON)
#include <arm_neon.h>
#endif
#include <array>
#include <stddef.h>
#include "alcomplex.h"
#include "alspan.h"
/* Implements a wide-band +90 degree phase-shift. Note that this should be
* given one sample less of a delay (FilterSize/2 - 1) compared to the direct
* signal delay (FilterSize/2) to properly align.
*/
template<size_t FilterSize>
struct PhaseShifterT {
static_assert(FilterSize >= 16, "FilterSize needs to be at least 16");
static_assert((FilterSize&(FilterSize-1)) == 0, "FilterSize needs to be power-of-two");
alignas(16) std::array<float,FilterSize/2> mCoeffs{};
/* Some notes on this filter construction.
*
* A wide-band phase-shift filter needs a delay to maintain linearity. A
* dirac impulse in the center of a time-domain buffer represents a filter
* passing all frequencies through as-is with a pure delay. Converting that
* to the frequency domain, adjusting the phase of each frequency bin by
* +90 degrees, then converting back to the time domain, results in a FIR
* filter that applies a +90 degree wide-band phase-shift.
*
* A particularly notable aspect of the time-domain filter response is that
* every other coefficient is 0. This allows doubling the effective size of
* the filter, by storing only the non-0 coefficients and double-stepping
* over the input to apply it.
*
* Additionally, the resulting filter is independent of the sample rate.
* The same filter can be applied regardless of the device's sample rate
* and achieve the same effect.
*/
PhaseShifterT()
{
using complex_d = std::complex<double>;
constexpr size_t fft_size{FilterSize};
constexpr size_t half_size{fft_size / 2};
auto fftBuffer = std::make_unique<complex_d[]>(fft_size);
std::fill_n(fftBuffer.get(), fft_size, complex_d{});
fftBuffer[half_size] = 1.0;
forward_fft(al::as_span(fftBuffer.get(), fft_size));
for(size_t i{0};i < half_size+1;++i)
fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()};
for(size_t i{half_size+1};i < fft_size;++i)
fftBuffer[i] = std::conj(fftBuffer[fft_size - i]);
inverse_fft(al::as_span(fftBuffer.get(), fft_size));
auto fftiter = fftBuffer.get() + half_size + (FilterSize/2 - 1);
for(float &coeff : mCoeffs)
{
coeff = static_cast<float>(fftiter->real() / double{fft_size});
fftiter -= 2;
}
}
void process(al::span<float> dst, const float *RESTRICT src) const;
private:
#if defined(HAVE_NEON)
/* There doesn't seem to be NEON intrinsics to do this kind of stipple
* shuffling, so there's two custom methods for it.
*/
static auto shuffle_2020(float32x4_t a, float32x4_t b)
{
float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))};
ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1);
ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2);
ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3);
return ret;
}
static auto shuffle_3131(float32x4_t a, float32x4_t b)
{
float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))};
ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1);
ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2);
ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3);
return ret;
}
static auto unpacklo(float32x4_t a, float32x4_t b)
{
float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))};
return vcombine_f32(result.val[0], result.val[1]);
}
static auto unpackhi(float32x4_t a, float32x4_t b)
{
float32x2x2_t result{vzip_f32(vget_high_f32(a), vget_high_f32(b))};
return vcombine_f32(result.val[0], result.val[1]);
}
static auto load4(float32_t a, float32_t b, float32_t c, float32_t d)
{
float32x4_t ret{vmovq_n_f32(a)};
ret = vsetq_lane_f32(b, ret, 1);
ret = vsetq_lane_f32(c, ret, 2);
ret = vsetq_lane_f32(d, ret, 3);
return ret;
}
#endif
};
template<size_t S>
inline void PhaseShifterT<S>::process(al::span<float> dst, const float *RESTRICT src) const
{
#ifdef HAVE_SSE_INTRINSICS
if(size_t todo{dst.size()>>1})
{
auto *out = reinterpret_cast<__m64*>(dst.data());
do {
__m128 r04{_mm_setzero_ps()};
__m128 r14{_mm_setzero_ps()};
for(size_t j{0};j < mCoeffs.size();j+=4)
{
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
const __m128 s0{_mm_loadu_ps(&src[j*2])};
const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])};
__m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))};
r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs));
s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1));
r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs));
}
src += 2;
__m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))};
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
_mm_storel_pi(out, r4);
++out;
} while(--todo);
}
if((dst.size()&1))
{
__m128 r4{_mm_setzero_ps()};
for(size_t j{0};j < mCoeffs.size();j+=4)
{
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs));
}
r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
dst.back() = _mm_cvtss_f32(r4);
}
#elif defined(HAVE_NEON)
size_t pos{0};
if(size_t todo{dst.size()>>1})
{
do {
float32x4_t r04{vdupq_n_f32(0.0f)};
float32x4_t r14{vdupq_n_f32(0.0f)};
for(size_t j{0};j < mCoeffs.size();j+=4)
{
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
const float32x4_t s0{vld1q_f32(&src[j*2])};
const float32x4_t s1{vld1q_f32(&src[j*2 + 4])};
r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs);
r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs);
}
src += 2;
float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))};
float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))};
vst1_f32(&dst[pos], r2);
pos += 2;
} while(--todo);
}
if((dst.size()&1))
{
float32x4_t r4{vdupq_n_f32(0.0f)};
for(size_t j{0};j < mCoeffs.size();j+=4)
{
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
r4 = vmlaq_f32(r4, s, coeffs);
}
r4 = vaddq_f32(r4, vrev64q_f32(r4));
dst[pos] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
}
#else
for(float &output : dst)
{
float ret{0.0f};
for(size_t j{0};j < mCoeffs.size();++j)
ret += src[j*2] * mCoeffs[j];
output = ret;
++src;
}
#endif
}
#endif /* PHASE_SHIFTER_H */

View File

@@ -0,0 +1,222 @@
#include "polyphase_resampler.h"
#include <algorithm>
#include <cmath>
#include "alnumbers.h"
#include "opthelpers.h"
namespace {
constexpr double Epsilon{1e-9};
using uint = unsigned int;
/* This is the normalized cardinal sine (sinc) function.
*
* sinc(x) = { 1, x = 0
* { sin(pi x) / (pi x), otherwise.
*/
double Sinc(const double x)
{
if(std::abs(x) < Epsilon) UNLIKELY
return 1.0;
return std::sin(al::numbers::pi*x) / (al::numbers::pi*x);
}
/* The zero-order modified Bessel function of the first kind, used for the
* Kaiser window.
*
* I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
* = sum_{k=0}^inf ((x / 2)^k / k!)^2
*/
constexpr double BesselI_0(const double x)
{
// Start at k=1 since k=0 is trivial.
const double x2{x/2.0};
double term{1.0};
double sum{1.0};
int k{1};
// Let the integration converge until the term of the sum is no longer
// significant.
double last_sum{};
do {
const double y{x2 / k};
++k;
last_sum = sum;
term *= y * y;
sum += term;
} while(sum != last_sum);
return sum;
}
/* Calculate a Kaiser window from the given beta value and a normalized k
* [-1, 1].
*
* w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1
* { 0, elsewhere.
*
* Where k can be calculated as:
*
* k = i / l, where -l <= i <= l.
*
* or:
*
* k = 2 i / M - 1, where 0 <= i <= M.
*/
double Kaiser(const double b, const double k)
{
if(!(k >= -1.0 && k <= 1.0))
return 0.0;
return BesselI_0(b * std::sqrt(1.0 - k*k)) / BesselI_0(b);
}
// Calculates the greatest common divisor of a and b.
constexpr uint Gcd(uint x, uint y)
{
while(y > 0)
{
const uint z{y};
y = x % y;
x = z;
}
return x;
}
/* Calculates the size (order) of the Kaiser window. Rejection is in dB and
* the transition width is normalized frequency (0.5 is nyquist).
*
* M = { ceil((r - 7.95) / (2.285 2 pi f_t)), r > 21
* { ceil(5.79 / 2 pi f_t), r <= 21.
*
*/
constexpr uint CalcKaiserOrder(const double rejection, const double transition)
{
const double w_t{2.0 * al::numbers::pi * transition};
if(rejection > 21.0) LIKELY
return static_cast<uint>(std::ceil((rejection - 7.95) / (2.285 * w_t)));
return static_cast<uint>(std::ceil(5.79 / w_t));
}
// Calculates the beta value of the Kaiser window. Rejection is in dB.
constexpr double CalcKaiserBeta(const double rejection)
{
if(rejection > 50.0) LIKELY
return 0.1102 * (rejection - 8.7);
if(rejection >= 21.0)
return (0.5842 * std::pow(rejection - 21.0, 0.4)) +
(0.07886 * (rejection - 21.0));
return 0.0;
}
/* Calculates a point on the Kaiser-windowed sinc filter for the given half-
* width, beta, gain, and cutoff. The point is specified in non-normalized
* samples, from 0 to M, where M = (2 l + 1).
*
* w(k) 2 p f_t sinc(2 f_t x)
*
* x -- centered sample index (i - l)
* k -- normalized and centered window index (x / l)
* w(k) -- window function (Kaiser)
* p -- gain compensation factor when sampling
* f_t -- normalized center frequency (or cutoff; 0.5 is nyquist)
*/
double SincFilter(const uint l, const double b, const double gain, const double cutoff,
const uint i)
{
const double x{static_cast<double>(i) - l};
return Kaiser(b, x / l) * 2.0 * gain * cutoff * Sinc(2.0 * cutoff * x);
}
} // namespace
// Calculate the resampling metrics and build the Kaiser-windowed sinc filter
// that's used to cut frequencies above the destination nyquist.
void PPhaseResampler::init(const uint srcRate, const uint dstRate)
{
const uint gcd{Gcd(srcRate, dstRate)};
mP = dstRate / gcd;
mQ = srcRate / gcd;
/* The cutoff is adjusted by half the transition width, so the transition
* ends before the nyquist (0.5). Both are scaled by the downsampling
* factor.
*/
double cutoff, width;
if(mP > mQ)
{
cutoff = 0.475 / mP;
width = 0.05 / mP;
}
else
{
cutoff = 0.475 / mQ;
width = 0.05 / mQ;
}
// A rejection of -180 dB is used for the stop band. Round up when
// calculating the left offset to avoid increasing the transition width.
const uint l{(CalcKaiserOrder(180.0, width)+1) / 2};
const double beta{CalcKaiserBeta(180.0)};
mM = l*2 + 1;
mL = l;
mF.resize(mM);
for(uint i{0};i < mM;i++)
mF[i] = SincFilter(l, beta, mP, cutoff, i);
}
// Perform the upsample-filter-downsample resampling operation using a
// polyphase filter implementation.
void PPhaseResampler::process(const uint inN, const double *in, const uint outN, double *out)
{
if(outN == 0) UNLIKELY
return;
// Handle in-place operation.
std::vector<double> workspace;
double *work{out};
if(work == in) UNLIKELY
{
workspace.resize(outN);
work = workspace.data();
}
// Resample the input.
const uint p{mP}, q{mQ}, m{mM}, l{mL};
const double *f{mF.data()};
for(uint i{0};i < outN;i++)
{
// Input starts at l to compensate for the filter delay. This will
// drop any build-up from the first half of the filter.
size_t j_f{(l + q*i) % p};
size_t j_s{(l + q*i) / p};
// Only take input when 0 <= j_s < inN.
double r{0.0};
if(j_f < m) LIKELY
{
size_t filt_len{(m-j_f+p-1) / p};
if(j_s+1 > inN) LIKELY
{
size_t skip{std::min<size_t>(j_s+1 - inN, filt_len)};
j_f += p*skip;
j_s -= skip;
filt_len -= skip;
}
if(size_t todo{std::min<size_t>(j_s+1, filt_len)}) LIKELY
{
do {
r += f[j_f] * in[j_s];
j_f += p;
--j_s;
} while(--todo);
}
}
work[i] = r;
}
// Clean up after in-place operation.
if(work != out)
std::copy_n(work, outN, out);
}

View File

@@ -0,0 +1,47 @@
#ifndef POLYPHASE_RESAMPLER_H
#define POLYPHASE_RESAMPLER_H
#include <vector>
using uint = unsigned int;
/* This is a polyphase sinc-filtered resampler. It is built for very high
* quality results, rather than real-time performance.
*
* Upsample Downsample
*
* p/q = 3/2 p/q = 3/5
*
* M-+-+-+-> M-+-+-+->
* -------------------+ ---------------------+
* p s * f f f f|f| | p s * f f f f f |
* | 0 * 0 0 0|0|0 | | 0 * 0 0 0 0|0| |
* v 0 * 0 0|0|0 0 | v 0 * 0 0 0|0|0 |
* s * f|f|f f f | s * f f|f|f f |
* 0 * |0|0 0 0 0 | 0 * 0|0|0 0 0 |
* --------+=+--------+ 0 * |0|0 0 0 0 |
* d . d .|d|. d . d ----------+=+--------+
* d . . . .|d|. . . .
* q->
* q-+-+-+->
*
* P_f(i,j) = q i mod p + pj
* P_s(i,j) = floor(q i / p) - j
* d[i=0..N-1] = sum_{j=0}^{floor((M - 1) / p)} {
* { f[P_f(i,j)] s[P_s(i,j)], P_f(i,j) < M
* { 0, P_f(i,j) >= M. }
*/
struct PPhaseResampler {
void init(const uint srcRate, const uint dstRate);
void process(const uint inN, const double *in, const uint outN, double *out);
explicit operator bool() const noexcept { return !mF.empty(); }
private:
uint mP, mQ, mM, mL;
std::vector<double> mF;
};
#endif /* POLYPHASE_RESAMPLER_H */

View File

@@ -0,0 +1,21 @@
#ifndef PRAGMADEFS_H
#define PRAGMADEFS_H
#if defined(_MSC_VER)
#define DIAGNOSTIC_PUSH __pragma(warning(push))
#define DIAGNOSTIC_POP __pragma(warning(pop))
#define std_pragma(...)
#define msc_pragma __pragma
#else
#if defined(__GNUC__) || defined(__clang__)
#define DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
#define DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
#else
#define DIAGNOSTIC_PUSH
#define DIAGNOSTIC_POP
#endif
#define std_pragma _Pragma
#define msc_pragma(...)
#endif
#endif /* PRAGMADEFS_H */

View File

@@ -0,0 +1,224 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include "ringbuffer.h"
#include <algorithm>
#include <climits>
#include <stdexcept>
#include "almalloc.h"
RingBufferPtr RingBuffer::Create(size_t sz, size_t elem_sz, int limit_writes)
{
size_t power_of_two{0u};
if(sz > 0)
{
power_of_two = sz;
power_of_two |= power_of_two>>1;
power_of_two |= power_of_two>>2;
power_of_two |= power_of_two>>4;
power_of_two |= power_of_two>>8;
power_of_two |= power_of_two>>16;
#if SIZE_MAX > UINT_MAX
power_of_two |= power_of_two>>32;
#endif
}
++power_of_two;
if(power_of_two <= sz || power_of_two > std::numeric_limits<size_t>::max()/elem_sz)
throw std::overflow_error{"Ring buffer size overflow"};
const size_t bufbytes{power_of_two * elem_sz};
RingBufferPtr rb{new(FamCount(bufbytes)) RingBuffer{bufbytes}};
rb->mWriteSize = limit_writes ? sz : (power_of_two-1);
rb->mSizeMask = power_of_two - 1;
rb->mElemSize = elem_sz;
return rb;
}
void RingBuffer::reset() noexcept
{
mWritePtr.store(0, std::memory_order_relaxed);
mReadPtr.store(0, std::memory_order_relaxed);
std::fill_n(mBuffer.begin(), (mSizeMask+1)*mElemSize, al::byte{});
}
size_t RingBuffer::read(void *dest, size_t cnt) noexcept
{
const size_t free_cnt{readSpace()};
if(free_cnt == 0) return 0;
const size_t to_read{std::min(cnt, free_cnt)};
size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask};
size_t n1, n2;
const size_t cnt2{read_ptr + to_read};
if(cnt2 > mSizeMask+1)
{
n1 = mSizeMask+1 - read_ptr;
n2 = cnt2 & mSizeMask;
}
else
{
n1 = to_read;
n2 = 0;
}
auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize,
static_cast<al::byte*>(dest));
read_ptr += n1;
if(n2 > 0)
{
std::copy_n(mBuffer.begin(), n2*mElemSize, outiter);
read_ptr += n2;
}
mReadPtr.store(read_ptr, std::memory_order_release);
return to_read;
}
size_t RingBuffer::peek(void *dest, size_t cnt) const noexcept
{
const size_t free_cnt{readSpace()};
if(free_cnt == 0) return 0;
const size_t to_read{std::min(cnt, free_cnt)};
size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask};
size_t n1, n2;
const size_t cnt2{read_ptr + to_read};
if(cnt2 > mSizeMask+1)
{
n1 = mSizeMask+1 - read_ptr;
n2 = cnt2 & mSizeMask;
}
else
{
n1 = to_read;
n2 = 0;
}
auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize,
static_cast<al::byte*>(dest));
if(n2 > 0)
std::copy_n(mBuffer.begin(), n2*mElemSize, outiter);
return to_read;
}
size_t RingBuffer::write(const void *src, size_t cnt) noexcept
{
const size_t free_cnt{writeSpace()};
if(free_cnt == 0) return 0;
const size_t to_write{std::min(cnt, free_cnt)};
size_t write_ptr{mWritePtr.load(std::memory_order_relaxed) & mSizeMask};
size_t n1, n2;
const size_t cnt2{write_ptr + to_write};
if(cnt2 > mSizeMask+1)
{
n1 = mSizeMask+1 - write_ptr;
n2 = cnt2 & mSizeMask;
}
else
{
n1 = to_write;
n2 = 0;
}
auto srcbytes = static_cast<const al::byte*>(src);
std::copy_n(srcbytes, n1*mElemSize, mBuffer.begin() + write_ptr*mElemSize);
write_ptr += n1;
if(n2 > 0)
{
std::copy_n(srcbytes + n1*mElemSize, n2*mElemSize, mBuffer.begin());
write_ptr += n2;
}
mWritePtr.store(write_ptr, std::memory_order_release);
return to_write;
}
auto RingBuffer::getReadVector() const noexcept -> DataPair
{
DataPair ret;
size_t w{mWritePtr.load(std::memory_order_acquire)};
size_t r{mReadPtr.load(std::memory_order_acquire)};
w &= mSizeMask;
r &= mSizeMask;
const size_t free_cnt{(w-r) & mSizeMask};
const size_t cnt2{r + free_cnt};
if(cnt2 > mSizeMask+1)
{
/* Two part vector: the rest of the buffer after the current read ptr,
* plus some from the start of the buffer. */
ret.first.buf = const_cast<al::byte*>(mBuffer.data() + r*mElemSize);
ret.first.len = mSizeMask+1 - r;
ret.second.buf = const_cast<al::byte*>(mBuffer.data());
ret.second.len = cnt2 & mSizeMask;
}
else
{
/* Single part vector: just the rest of the buffer */
ret.first.buf = const_cast<al::byte*>(mBuffer.data() + r*mElemSize);
ret.first.len = free_cnt;
ret.second.buf = nullptr;
ret.second.len = 0;
}
return ret;
}
auto RingBuffer::getWriteVector() const noexcept -> DataPair
{
DataPair ret;
size_t w{mWritePtr.load(std::memory_order_acquire)};
size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask};
w &= mSizeMask;
r &= mSizeMask;
const size_t free_cnt{(r-w-1) & mSizeMask};
const size_t cnt2{w + free_cnt};
if(cnt2 > mSizeMask+1)
{
/* Two part vector: the rest of the buffer after the current write ptr,
* plus some from the start of the buffer. */
ret.first.buf = const_cast<al::byte*>(mBuffer.data() + w*mElemSize);
ret.first.len = mSizeMask+1 - w;
ret.second.buf = const_cast<al::byte*>(mBuffer.data());
ret.second.len = cnt2 & mSizeMask;
}
else
{
ret.first.buf = const_cast<al::byte*>(mBuffer.data() + w*mElemSize);
ret.first.len = free_cnt;
ret.second.buf = nullptr;
ret.second.len = 0;
}
return ret;
}

View File

@@ -0,0 +1,115 @@
#ifndef RINGBUFFER_H
#define RINGBUFFER_H
#include <atomic>
#include <memory>
#include <stddef.h>
#include <utility>
#include "albyte.h"
#include "almalloc.h"
/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
* to include an element size. Consequently, parameters and return values for a
* size or count is in 'elements', not bytes. Additionally, it only supports
* single-consumer/single-provider operation.
*/
struct RingBuffer {
private:
std::atomic<size_t> mWritePtr{0u};
std::atomic<size_t> mReadPtr{0u};
size_t mWriteSize{0u};
size_t mSizeMask{0u};
size_t mElemSize{0u};
al::FlexArray<al::byte, 16> mBuffer;
public:
struct Data {
al::byte *buf;
size_t len;
};
using DataPair = std::pair<Data,Data>;
RingBuffer(const size_t count) : mBuffer{count} { }
/** Reset the read and write pointers to zero. This is not thread safe. */
void reset() noexcept;
/**
* The non-copying data reader. Returns two ringbuffer data pointers that
* hold the current readable data. If the readable data is in one segment
* the second segment has zero length.
*/
DataPair getReadVector() const noexcept;
/**
* The non-copying data writer. Returns two ringbuffer data pointers that
* hold the current writeable data. If the writeable data is in one segment
* the second segment has zero length.
*/
DataPair getWriteVector() const noexcept;
/**
* Return the number of elements available for reading. This is the number
* of elements in front of the read pointer and behind the write pointer.
*/
size_t readSpace() const noexcept
{
const size_t w{mWritePtr.load(std::memory_order_acquire)};
const size_t r{mReadPtr.load(std::memory_order_acquire)};
return (w-r) & mSizeMask;
}
/**
* The copying data reader. Copy at most `cnt' elements into `dest'.
* Returns the actual number of elements copied.
*/
size_t read(void *dest, size_t cnt) noexcept;
/**
* The copying data reader w/o read pointer advance. Copy at most `cnt'
* elements into `dest'. Returns the actual number of elements copied.
*/
size_t peek(void *dest, size_t cnt) const noexcept;
/** Advance the read pointer `cnt' places. */
void readAdvance(size_t cnt) noexcept
{ mReadPtr.fetch_add(cnt, std::memory_order_acq_rel); }
/**
* Return the number of elements available for writing. This is the number
* of elements in front of the write pointer and behind the read pointer.
*/
size_t writeSpace() const noexcept
{
const size_t w{mWritePtr.load(std::memory_order_acquire)};
const size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask};
return (r-w-1) & mSizeMask;
}
/**
* The copying data writer. Copy at most `cnt' elements from `src'. Returns
* the actual number of elements copied.
*/
size_t write(const void *src, size_t cnt) noexcept;
/** Advance the write pointer `cnt' places. */
void writeAdvance(size_t cnt) noexcept
{ mWritePtr.fetch_add(cnt, std::memory_order_acq_rel); }
size_t getElemSize() const noexcept { return mElemSize; }
/**
* Create a new ringbuffer to hold at least `sz' elements of `elem_sz'
* bytes. The number of elements is rounded up to the next power of two
* (even if it is already a power of two, to ensure the requested amount
* can be written).
*/
static std::unique_ptr<RingBuffer> Create(size_t sz, size_t elem_sz, int limit_writes);
DEF_FAM_NEWDEL(RingBuffer, mBuffer)
};
using RingBufferPtr = std::unique_ptr<RingBuffer>;
#endif /* RINGBUFFER_H */

View File

@@ -0,0 +1,64 @@
#include "config.h"
#include "strutils.h"
#include <cstdlib>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
std::string wstr_to_utf8(const WCHAR *wstr)
{
std::string ret;
int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
if(len > 0)
{
ret.resize(len);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &ret[0], len, nullptr, nullptr);
ret.pop_back();
}
return ret;
}
std::wstring utf8_to_wstr(const char *str)
{
std::wstring ret;
int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0);
if(len > 0)
{
ret.resize(len);
MultiByteToWideChar(CP_UTF8, 0, str, -1, &ret[0], len);
ret.pop_back();
}
return ret;
}
#endif
namespace al {
al::optional<std::string> getenv(const char *envname)
{
const char *str{std::getenv(envname)};
if(str && str[0] != '\0')
return str;
return al::nullopt;
}
#ifdef _WIN32
al::optional<std::wstring> getenv(const WCHAR *envname)
{
const WCHAR *str{_wgetenv(envname)};
if(str && str[0] != L'\0')
return str;
return al::nullopt;
}
#endif
} // namespace al

24
externals/openal-soft/common/strutils.h vendored Normal file
View File

@@ -0,0 +1,24 @@
#ifndef AL_STRUTILS_H
#define AL_STRUTILS_H
#include <string>
#include "aloptional.h"
#ifdef _WIN32
#include <wchar.h>
std::string wstr_to_utf8(const wchar_t *wstr);
std::wstring utf8_to_wstr(const char *str);
#endif
namespace al {
al::optional<std::string> getenv(const char *envname);
#ifdef _WIN32
al::optional<std::wstring> getenv(const wchar_t *envname);
#endif
} // namespace al
#endif /* AL_STRUTILS_H */

197
externals/openal-soft/common/threads.cpp vendored Normal file
View File

@@ -0,0 +1,197 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include "opthelpers.h"
#include "threads.h"
#include <system_error>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <limits>
void althrd_setname(const char *name)
{
#if defined(_MSC_VER) && !defined(_M_ARM)
#define MS_VC_EXCEPTION 0x406D1388
#pragma pack(push,8)
struct {
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} info;
#pragma pack(pop)
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = ~DWORD{0};
info.dwFlags = 0;
__try {
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
}
__except(EXCEPTION_CONTINUE_EXECUTION) {
}
#undef MS_VC_EXCEPTION
#else
(void)name;
#endif
}
namespace al {
semaphore::semaphore(unsigned int initial)
{
if(initial > static_cast<unsigned int>(std::numeric_limits<int>::max()))
throw std::system_error(std::make_error_code(std::errc::value_too_large));
mSem = CreateSemaphore(nullptr, initial, std::numeric_limits<int>::max(), nullptr);
if(mSem == nullptr)
throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again));
}
semaphore::~semaphore()
{ CloseHandle(mSem); }
void semaphore::post()
{
if(!ReleaseSemaphore(static_cast<HANDLE>(mSem), 1, nullptr))
throw std::system_error(std::make_error_code(std::errc::value_too_large));
}
void semaphore::wait() noexcept
{ WaitForSingleObject(static_cast<HANDLE>(mSem), INFINITE); }
bool semaphore::try_wait() noexcept
{ return WaitForSingleObject(static_cast<HANDLE>(mSem), 0) == WAIT_OBJECT_0; }
} // namespace al
#else
#include <pthread.h>
#ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h>
#endif
#include <tuple>
namespace {
using setname_t1 = int(*)(const char*);
using setname_t2 = int(*)(pthread_t, const char*);
using setname_t3 = void(*)(pthread_t, const char*);
using setname_t4 = int(*)(pthread_t, const char*, void*);
void setname_caller(setname_t1 func, const char *name)
{ func(name); }
void setname_caller(setname_t2 func, const char *name)
{ func(pthread_self(), name); }
void setname_caller(setname_t3 func, const char *name)
{ func(pthread_self(), name); }
void setname_caller(setname_t4 func, const char *name)
{ func(pthread_self(), "%s", static_cast<void*>(const_cast<char*>(name))); }
} // namespace
void althrd_setname(const char *name)
{
#if defined(HAVE_PTHREAD_SET_NAME_NP)
setname_caller(pthread_set_name_np, name);
#elif defined(HAVE_PTHREAD_SETNAME_NP)
setname_caller(pthread_setname_np, name);
#endif
/* Avoid unused function/parameter warnings. */
std::ignore = name;
std::ignore = static_cast<void(*)(setname_t1,const char*)>(&setname_caller);
std::ignore = static_cast<void(*)(setname_t2,const char*)>(&setname_caller);
std::ignore = static_cast<void(*)(setname_t3,const char*)>(&setname_caller);
std::ignore = static_cast<void(*)(setname_t4,const char*)>(&setname_caller);
}
#ifdef __APPLE__
namespace al {
semaphore::semaphore(unsigned int initial)
{
mSem = dispatch_semaphore_create(initial);
if(!mSem)
throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again));
}
semaphore::~semaphore()
{ dispatch_release(mSem); }
void semaphore::post()
{ dispatch_semaphore_signal(mSem); }
void semaphore::wait() noexcept
{ dispatch_semaphore_wait(mSem, DISPATCH_TIME_FOREVER); }
bool semaphore::try_wait() noexcept
{ return dispatch_semaphore_wait(mSem, DISPATCH_TIME_NOW) == 0; }
} // namespace al
#else /* !__APPLE__ */
#include <cerrno>
namespace al {
semaphore::semaphore(unsigned int initial)
{
if(sem_init(&mSem, 0, initial) != 0)
throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again));
}
semaphore::~semaphore()
{ sem_destroy(&mSem); }
void semaphore::post()
{
if(sem_post(&mSem) != 0)
throw std::system_error(std::make_error_code(std::errc::value_too_large));
}
void semaphore::wait() noexcept
{
while(sem_wait(&mSem) == -1 && errno == EINTR) {
}
}
bool semaphore::try_wait() noexcept
{ return sem_trywait(&mSem) == 0; }
} // namespace al
#endif /* __APPLE__ */
#endif /* _WIN32 */

48
externals/openal-soft/common/threads.h vendored Normal file
View File

@@ -0,0 +1,48 @@
#ifndef AL_THREADS_H
#define AL_THREADS_H
#if defined(__GNUC__) && defined(__i386__)
/* force_align_arg_pointer is required for proper function arguments aligning
* when SSE code is used. Some systems (Windows, QNX) do not guarantee our
* thread functions will be properly aligned on the stack, even though GCC may
* generate code with the assumption that it is. */
#define FORCE_ALIGN __attribute__((force_align_arg_pointer))
#else
#define FORCE_ALIGN
#endif
#if defined(__APPLE__)
#include <dispatch/dispatch.h>
#elif !defined(_WIN32)
#include <semaphore.h>
#endif
void althrd_setname(const char *name);
namespace al {
class semaphore {
#ifdef _WIN32
using native_type = void*;
#elif defined(__APPLE__)
using native_type = dispatch_semaphore_t;
#else
using native_type = sem_t;
#endif
native_type mSem;
public:
semaphore(unsigned int initial=0);
semaphore(const semaphore&) = delete;
~semaphore();
semaphore& operator=(const semaphore&) = delete;
void post();
void wait() noexcept;
bool try_wait() noexcept;
};
} // namespace al
#endif /* AL_THREADS_H */

120
externals/openal-soft/common/vecmat.h vendored Normal file
View File

@@ -0,0 +1,120 @@
#ifndef COMMON_VECMAT_H
#define COMMON_VECMAT_H
#include <array>
#include <cmath>
#include <cstddef>
#include <limits>
#include "alspan.h"
namespace alu {
template<typename T>
class VectorR {
static_assert(std::is_floating_point<T>::value, "Must use floating-point types");
alignas(16) T mVals[4];
public:
constexpr VectorR() noexcept = default;
constexpr VectorR(const VectorR&) noexcept = default;
constexpr explicit VectorR(T a, T b, T c, T d) noexcept : mVals{a, b, c, d} { }
constexpr VectorR& operator=(const VectorR&) noexcept = default;
constexpr T& operator[](size_t idx) noexcept { return mVals[idx]; }
constexpr const T& operator[](size_t idx) const noexcept { return mVals[idx]; }
constexpr VectorR& operator+=(const VectorR &rhs) noexcept
{
mVals[0] += rhs.mVals[0];
mVals[1] += rhs.mVals[1];
mVals[2] += rhs.mVals[2];
mVals[3] += rhs.mVals[3];
return *this;
}
constexpr VectorR operator-(const VectorR &rhs) const noexcept
{
return VectorR{mVals[0] - rhs.mVals[0], mVals[1] - rhs.mVals[1],
mVals[2] - rhs.mVals[2], mVals[3] - rhs.mVals[3]};
}
constexpr T normalize(T limit = std::numeric_limits<T>::epsilon())
{
limit = std::max(limit, std::numeric_limits<T>::epsilon());
const T length_sqr{mVals[0]*mVals[0] + mVals[1]*mVals[1] + mVals[2]*mVals[2]};
if(length_sqr > limit*limit)
{
const T length{std::sqrt(length_sqr)};
T inv_length{T{1}/length};
mVals[0] *= inv_length;
mVals[1] *= inv_length;
mVals[2] *= inv_length;
return length;
}
mVals[0] = mVals[1] = mVals[2] = T{0};
return T{0};
}
constexpr VectorR cross_product(const alu::VectorR<T> &rhs) const noexcept
{
return VectorR{
mVals[1]*rhs.mVals[2] - mVals[2]*rhs.mVals[1],
mVals[2]*rhs.mVals[0] - mVals[0]*rhs.mVals[2],
mVals[0]*rhs.mVals[1] - mVals[1]*rhs.mVals[0],
T{0}};
}
constexpr T dot_product(const alu::VectorR<T> &rhs) const noexcept
{ return mVals[0]*rhs.mVals[0] + mVals[1]*rhs.mVals[1] + mVals[2]*rhs.mVals[2]; }
};
using Vector = VectorR<float>;
template<typename T>
class MatrixR {
static_assert(std::is_floating_point<T>::value, "Must use floating-point types");
alignas(16) T mVals[16];
public:
constexpr MatrixR() noexcept = default;
constexpr MatrixR(const MatrixR&) noexcept = default;
constexpr explicit MatrixR(
T aa, T ab, T ac, T ad,
T ba, T bb, T bc, T bd,
T ca, T cb, T cc, T cd,
T da, T db, T dc, T dd) noexcept
: mVals{aa,ab,ac,ad, ba,bb,bc,bd, ca,cb,cc,cd, da,db,dc,dd}
{ }
constexpr MatrixR& operator=(const MatrixR&) noexcept = default;
constexpr auto operator[](size_t idx) noexcept { return al::span<T,4>{&mVals[idx*4], 4}; }
constexpr auto operator[](size_t idx) const noexcept
{ return al::span<const T,4>{&mVals[idx*4], 4}; }
static constexpr MatrixR Identity() noexcept
{
return MatrixR{
T{1}, T{0}, T{0}, T{0},
T{0}, T{1}, T{0}, T{0},
T{0}, T{0}, T{1}, T{0},
T{0}, T{0}, T{0}, T{1}};
}
};
using Matrix = MatrixR<float>;
template<typename T>
constexpr VectorR<T> operator*(const MatrixR<T> &mtx, const VectorR<T> &vec) noexcept
{
return VectorR<T>{
vec[0]*mtx[0][0] + vec[1]*mtx[1][0] + vec[2]*mtx[2][0] + vec[3]*mtx[3][0],
vec[0]*mtx[0][1] + vec[1]*mtx[1][1] + vec[2]*mtx[2][1] + vec[3]*mtx[3][1],
vec[0]*mtx[0][2] + vec[1]*mtx[1][2] + vec[2]*mtx[2][2] + vec[3]*mtx[3][2],
vec[0]*mtx[0][3] + vec[1]*mtx[1][3] + vec[2]*mtx[2][3] + vec[3]*mtx[3][3]};
}
} // namespace alu
#endif /* COMMON_VECMAT_H */

15
externals/openal-soft/common/vector.h vendored Normal file
View File

@@ -0,0 +1,15 @@
#ifndef AL_VECTOR_H
#define AL_VECTOR_H
#include <vector>
#include "almalloc.h"
namespace al {
template<typename T, size_t alignment=alignof(T)>
using vector = std::vector<T, al::allocator<T, alignment>>;
} // namespace al
#endif /* AL_VECTOR_H */

View File

@@ -0,0 +1,117 @@
#ifndef WIN_MAIN_UTF8_H
#define WIN_MAIN_UTF8_H
/* For Windows systems this provides a way to get UTF-8 encoded argv strings,
* and also overrides fopen to accept UTF-8 filenames. Working with wmain
* directly complicates cross-platform compatibility, while normal main() in
* Windows uses the current codepage (which has limited availability of
* characters).
*
* For MinGW, you must link with -municode
*/
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
#include <wchar.h>
#ifdef __cplusplus
#include <memory>
#define STATIC_CAST(...) static_cast<__VA_ARGS__>
#define REINTERPRET_CAST(...) reinterpret_cast<__VA_ARGS__>
#else
#define STATIC_CAST(...) (__VA_ARGS__)
#define REINTERPRET_CAST(...) (__VA_ARGS__)
#endif
static FILE *my_fopen(const char *fname, const char *mode)
{
wchar_t *wname=NULL, *wmode=NULL;
int namelen, modelen;
FILE *file = NULL;
errno_t err;
namelen = MultiByteToWideChar(CP_UTF8, 0, fname, -1, NULL, 0);
modelen = MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
if(namelen <= 0 || modelen <= 0)
{
fprintf(stderr, "Failed to convert UTF-8 fname \"%s\", mode \"%s\"\n", fname, mode);
return NULL;
}
#ifdef __cplusplus
auto strbuf = std::make_unique<wchar_t[]>(static_cast<size_t>(namelen)+modelen);
wname = strbuf.get();
#else
wname = (wchar_t*)calloc(sizeof(wchar_t), (size_t)namelen + modelen);
#endif
wmode = wname + namelen;
MultiByteToWideChar(CP_UTF8, 0, fname, -1, wname, namelen);
MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, modelen);
err = _wfopen_s(&file, wname, wmode);
if(err)
{
errno = err;
file = NULL;
}
#ifndef __cplusplus
free(wname);
#endif
return file;
}
#define fopen my_fopen
/* SDL overrides main and provides UTF-8 args for us. */
#if !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE)
int my_main(int, char**);
#define main my_main
#ifdef __cplusplus
extern "C"
#endif
int wmain(int argc, wchar_t **wargv)
{
char **argv;
size_t total;
int i;
total = sizeof(*argv) * STATIC_CAST(size_t)(argc);
for(i = 0;i < argc;i++)
total += STATIC_CAST(size_t)(WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL,
NULL));
#ifdef __cplusplus
auto argbuf = std::make_unique<char[]>(total);
argv = reinterpret_cast<char**>(argbuf.get());
#else
argv = (char**)calloc(1, total);
#endif
argv[0] = REINTERPRET_CAST(char*)(argv + argc);
for(i = 0;i < argc-1;i++)
{
int len = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], 65535, NULL, NULL);
argv[i+1] = argv[i] + len;
}
WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], 65535, NULL, NULL);
#ifdef __cplusplus
return main(argc, argv);
#else
i = main(argc, argv);
free(argv);
return i;
#endif
}
#endif /* !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE) */
#endif /* _WIN32 */
#endif /* WIN_MAIN_UTF8_H */