migrating to the latest JUCE version
This commit is contained in:
1787
deps/juce/modules/juce_dsp/containers/juce_AudioBlock.h
vendored
1787
deps/juce/modules/juce_dsp/containers/juce_AudioBlock.h
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,242 +1,242 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename Ret, typename... Args>
|
||||
struct Vtable
|
||||
{
|
||||
using Storage = void*;
|
||||
|
||||
using Move = void (*) (Storage, Storage);
|
||||
using Call = Ret (*) (Storage, Args...);
|
||||
using Clear = void (*) (Storage);
|
||||
|
||||
constexpr Vtable (Move moveIn, Call callIn, Clear clearIn) noexcept
|
||||
: move (moveIn), call (callIn), clear (clearIn) {}
|
||||
|
||||
Move move = nullptr;
|
||||
Call call = nullptr;
|
||||
Clear clear = nullptr;
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
void move (void* from, void* to)
|
||||
{
|
||||
new (to) Fn (std::move (*reinterpret_cast<Fn*> (from)));
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
typename std::enable_if<std::is_same<Ret, void>::value, Ret>::type call (void* s, Args... args)
|
||||
{
|
||||
(*reinterpret_cast<Fn*> (s)) (args...);
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
typename std::enable_if<! std::is_same<Ret, void>::value, Ret>::type call (void* s, Args... args)
|
||||
{
|
||||
return (*reinterpret_cast<Fn*> (s)) (std::forward<Args> (args)...);
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void clear (void* s)
|
||||
{
|
||||
auto& fn = *reinterpret_cast<Fn*> (s);
|
||||
fn.~Fn();
|
||||
// I know this looks insane, for some reason MSVC 14 sometimes thinks fn is unreferenced
|
||||
ignoreUnused (fn);
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
constexpr Vtable<Ret, Args...> makeVtable()
|
||||
{
|
||||
return { move <Fn>, call <Fn, Ret, Args...>, clear<Fn> };
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <size_t len, typename T>
|
||||
class FixedSizeFunction;
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
A type similar to `std::function` that holds a callable object.
|
||||
|
||||
Unlike `std::function`, the callable object will always be stored in
|
||||
a buffer of size `len` that is internal to the FixedSizeFunction instance.
|
||||
This in turn means that creating a FixedSizeFunction instance will never allocate,
|
||||
making FixedSizeFunctions suitable for use in realtime contexts.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <size_t len, typename Ret, typename... Args>
|
||||
class FixedSizeFunction<len, Ret (Args...)>
|
||||
{
|
||||
private:
|
||||
using Storage = typename std::aligned_storage<len>::type;
|
||||
|
||||
template <typename Item>
|
||||
using Decay = typename std::decay<Item>::type;
|
||||
|
||||
template <typename Item, typename Fn = Decay<Item>>
|
||||
using IntIfValidConversion = typename std::enable_if<sizeof (Fn) <= len
|
||||
&& alignof (Fn) <= alignof (Storage)
|
||||
&& ! std::is_same<FixedSizeFunction, Fn>::value,
|
||||
int>::type;
|
||||
|
||||
public:
|
||||
/** Create an empty function. */
|
||||
FixedSizeFunction() noexcept = default;
|
||||
|
||||
/** Create an empty function. */
|
||||
FixedSizeFunction (std::nullptr_t) noexcept
|
||||
: FixedSizeFunction() {}
|
||||
|
||||
FixedSizeFunction (const FixedSizeFunction&) = delete;
|
||||
|
||||
/** Forwards the passed Callable into the internal storage buffer. */
|
||||
template <typename Callable,
|
||||
typename Fn = Decay<Callable>,
|
||||
IntIfValidConversion<Callable> = 0>
|
||||
FixedSizeFunction (Callable&& callable)
|
||||
{
|
||||
static_assert (sizeof (Fn) <= len,
|
||||
"The requested function cannot fit in this FixedSizeFunction");
|
||||
static_assert (alignof (Fn) <= alignof (Storage),
|
||||
"FixedSizeFunction cannot accommodate the requested alignment requirements");
|
||||
|
||||
static constexpr auto vtableForCallable = detail::makeVtable<Fn, Ret, Args...>();
|
||||
vtable = &vtableForCallable;
|
||||
|
||||
auto* ptr = new (&storage) Fn (std::forward<Callable> (callable));
|
||||
jassertquiet ((void*) ptr == (void*) &storage);
|
||||
}
|
||||
|
||||
/** Move constructor. */
|
||||
FixedSizeFunction (FixedSizeFunction&& other) noexcept
|
||||
: vtable (other.vtable)
|
||||
{
|
||||
move (std::move (other));
|
||||
}
|
||||
|
||||
/** Converting constructor from smaller FixedSizeFunctions. */
|
||||
template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
|
||||
FixedSizeFunction (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
|
||||
: vtable (other.vtable)
|
||||
{
|
||||
move (std::move (other));
|
||||
}
|
||||
|
||||
/** Nulls this instance. */
|
||||
FixedSizeFunction& operator= (std::nullptr_t) noexcept
|
||||
{
|
||||
return *this = FixedSizeFunction();
|
||||
}
|
||||
|
||||
FixedSizeFunction& operator= (const FixedSizeFunction&) = delete;
|
||||
|
||||
/** Assigns a new callable to this instance. */
|
||||
template <typename Callable, IntIfValidConversion<Callable> = 0>
|
||||
FixedSizeFunction& operator= (Callable&& callable)
|
||||
{
|
||||
return *this = FixedSizeFunction (std::forward<Callable> (callable));
|
||||
}
|
||||
|
||||
/** Move assignment from smaller FixedSizeFunctions. */
|
||||
template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
|
||||
FixedSizeFunction& operator= (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
|
||||
{
|
||||
return *this = FixedSizeFunction (std::move (other));
|
||||
}
|
||||
|
||||
/** Move assignment operator. */
|
||||
FixedSizeFunction& operator= (FixedSizeFunction&& other) noexcept
|
||||
{
|
||||
clear();
|
||||
vtable = other.vtable;
|
||||
move (std::move (other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~FixedSizeFunction() noexcept { clear(); }
|
||||
|
||||
/** If this instance is currently storing a callable object, calls that object,
|
||||
otherwise throws `std::bad_function_call`.
|
||||
*/
|
||||
Ret operator() (Args... args) const
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
return vtable->call (&storage, std::forward<Args> (args)...);
|
||||
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
/** Returns true if this instance currently holds a callable. */
|
||||
explicit operator bool() const noexcept { return vtable != nullptr; }
|
||||
|
||||
private:
|
||||
template <size_t, typename>
|
||||
friend class FixedSizeFunction;
|
||||
|
||||
void clear() noexcept
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
vtable->clear (&storage);
|
||||
}
|
||||
|
||||
template <size_t otherLen, typename T>
|
||||
void move (FixedSizeFunction<otherLen, T>&& other) noexcept
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
vtable->move (&other.storage, &storage);
|
||||
}
|
||||
|
||||
const detail::Vtable<Ret, Args...>* vtable = nullptr;
|
||||
mutable Storage storage;
|
||||
};
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator!= (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return bool (fn); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator!= (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return bool (fn); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator== (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return ! (fn != nullptr); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator== (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return ! (fn != nullptr); }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename Ret, typename... Args>
|
||||
struct Vtable
|
||||
{
|
||||
using Storage = void*;
|
||||
|
||||
using Move = void (*) (Storage, Storage);
|
||||
using Call = Ret (*) (Storage, Args...);
|
||||
using Clear = void (*) (Storage);
|
||||
|
||||
constexpr Vtable (Move moveIn, Call callIn, Clear clearIn) noexcept
|
||||
: move (moveIn), call (callIn), clear (clearIn) {}
|
||||
|
||||
Move move = nullptr;
|
||||
Call call = nullptr;
|
||||
Clear clear = nullptr;
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
void move (void* from, void* to)
|
||||
{
|
||||
new (to) Fn (std::move (*reinterpret_cast<Fn*> (from)));
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
typename std::enable_if<std::is_same<Ret, void>::value, Ret>::type call (void* s, Args... args)
|
||||
{
|
||||
(*reinterpret_cast<Fn*> (s)) (args...);
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
typename std::enable_if<! std::is_same<Ret, void>::value, Ret>::type call (void* s, Args... args)
|
||||
{
|
||||
return (*reinterpret_cast<Fn*> (s)) (std::forward<Args> (args)...);
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void clear (void* s)
|
||||
{
|
||||
auto& fn = *reinterpret_cast<Fn*> (s);
|
||||
fn.~Fn();
|
||||
// I know this looks insane, for some reason MSVC 14 sometimes thinks fn is unreferenced
|
||||
ignoreUnused (fn);
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
constexpr Vtable<Ret, Args...> makeVtable()
|
||||
{
|
||||
return { move <Fn>, call <Fn, Ret, Args...>, clear<Fn> };
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <size_t len, typename T>
|
||||
class FixedSizeFunction;
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
A type similar to `std::function` that holds a callable object.
|
||||
|
||||
Unlike `std::function`, the callable object will always be stored in
|
||||
a buffer of size `len` that is internal to the FixedSizeFunction instance.
|
||||
This in turn means that creating a FixedSizeFunction instance will never allocate,
|
||||
making FixedSizeFunctions suitable for use in realtime contexts.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <size_t len, typename Ret, typename... Args>
|
||||
class FixedSizeFunction<len, Ret (Args...)>
|
||||
{
|
||||
private:
|
||||
using Storage = typename std::aligned_storage<len>::type;
|
||||
|
||||
template <typename Item>
|
||||
using Decay = typename std::decay<Item>::type;
|
||||
|
||||
template <typename Item, typename Fn = Decay<Item>>
|
||||
using IntIfValidConversion = typename std::enable_if<sizeof (Fn) <= len
|
||||
&& alignof (Fn) <= alignof (Storage)
|
||||
&& ! std::is_same<FixedSizeFunction, Fn>::value,
|
||||
int>::type;
|
||||
|
||||
public:
|
||||
/** Create an empty function. */
|
||||
FixedSizeFunction() noexcept = default;
|
||||
|
||||
/** Create an empty function. */
|
||||
FixedSizeFunction (std::nullptr_t) noexcept
|
||||
: FixedSizeFunction() {}
|
||||
|
||||
FixedSizeFunction (const FixedSizeFunction&) = delete;
|
||||
|
||||
/** Forwards the passed Callable into the internal storage buffer. */
|
||||
template <typename Callable,
|
||||
typename Fn = Decay<Callable>,
|
||||
IntIfValidConversion<Callable> = 0>
|
||||
FixedSizeFunction (Callable&& callable)
|
||||
{
|
||||
static_assert (sizeof (Fn) <= len,
|
||||
"The requested function cannot fit in this FixedSizeFunction");
|
||||
static_assert (alignof (Fn) <= alignof (Storage),
|
||||
"FixedSizeFunction cannot accommodate the requested alignment requirements");
|
||||
|
||||
static constexpr auto vtableForCallable = detail::makeVtable<Fn, Ret, Args...>();
|
||||
vtable = &vtableForCallable;
|
||||
|
||||
auto* ptr = new (&storage) Fn (std::forward<Callable> (callable));
|
||||
jassertquiet ((void*) ptr == (void*) &storage);
|
||||
}
|
||||
|
||||
/** Move constructor. */
|
||||
FixedSizeFunction (FixedSizeFunction&& other) noexcept
|
||||
: vtable (other.vtable)
|
||||
{
|
||||
move (std::move (other));
|
||||
}
|
||||
|
||||
/** Converting constructor from smaller FixedSizeFunctions. */
|
||||
template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
|
||||
FixedSizeFunction (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
|
||||
: vtable (other.vtable)
|
||||
{
|
||||
move (std::move (other));
|
||||
}
|
||||
|
||||
/** Nulls this instance. */
|
||||
FixedSizeFunction& operator= (std::nullptr_t) noexcept
|
||||
{
|
||||
return *this = FixedSizeFunction();
|
||||
}
|
||||
|
||||
FixedSizeFunction& operator= (const FixedSizeFunction&) = delete;
|
||||
|
||||
/** Assigns a new callable to this instance. */
|
||||
template <typename Callable, IntIfValidConversion<Callable> = 0>
|
||||
FixedSizeFunction& operator= (Callable&& callable)
|
||||
{
|
||||
return *this = FixedSizeFunction (std::forward<Callable> (callable));
|
||||
}
|
||||
|
||||
/** Move assignment from smaller FixedSizeFunctions. */
|
||||
template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
|
||||
FixedSizeFunction& operator= (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
|
||||
{
|
||||
return *this = FixedSizeFunction (std::move (other));
|
||||
}
|
||||
|
||||
/** Move assignment operator. */
|
||||
FixedSizeFunction& operator= (FixedSizeFunction&& other) noexcept
|
||||
{
|
||||
clear();
|
||||
vtable = other.vtable;
|
||||
move (std::move (other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~FixedSizeFunction() noexcept { clear(); }
|
||||
|
||||
/** If this instance is currently storing a callable object, calls that object,
|
||||
otherwise throws `std::bad_function_call`.
|
||||
*/
|
||||
Ret operator() (Args... args) const
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
return vtable->call (&storage, std::forward<Args> (args)...);
|
||||
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
/** Returns true if this instance currently holds a callable. */
|
||||
explicit operator bool() const noexcept { return vtable != nullptr; }
|
||||
|
||||
private:
|
||||
template <size_t, typename>
|
||||
friend class FixedSizeFunction;
|
||||
|
||||
void clear() noexcept
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
vtable->clear (&storage);
|
||||
}
|
||||
|
||||
template <size_t otherLen, typename T>
|
||||
void move (FixedSizeFunction<otherLen, T>&& other) noexcept
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
vtable->move (&other.storage, &storage);
|
||||
}
|
||||
|
||||
const detail::Vtable<Ret, Args...>* vtable = nullptr;
|
||||
mutable Storage storage;
|
||||
};
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator!= (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return bool (fn); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator!= (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return bool (fn); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator== (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return ! (fn != nullptr); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator== (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return ! (fn != nullptr); }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,352 +1,355 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_ENABLE_ALLOCATION_HOOKS
|
||||
#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE const UnitTestAllocationChecker checker (*this)
|
||||
#else
|
||||
#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
|
||||
#endif
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
class ConstructCounts
|
||||
{
|
||||
auto tie() const noexcept { return std::tie (constructions, copies, moves, calls, destructions); }
|
||||
|
||||
public:
|
||||
int constructions = 0;
|
||||
int copies = 0;
|
||||
int moves = 0;
|
||||
int calls = 0;
|
||||
int destructions = 0;
|
||||
|
||||
ConstructCounts withConstructions (int i) const noexcept { auto c = *this; c.constructions = i; return c; }
|
||||
ConstructCounts withCopies (int i) const noexcept { auto c = *this; c.copies = i; return c; }
|
||||
ConstructCounts withMoves (int i) const noexcept { auto c = *this; c.moves = i; return c; }
|
||||
ConstructCounts withCalls (int i) const noexcept { auto c = *this; c.calls = i; return c; }
|
||||
ConstructCounts withDestructions (int i) const noexcept { auto c = *this; c.destructions = i; return c; }
|
||||
|
||||
bool operator== (const ConstructCounts& other) const noexcept { return tie() == other.tie(); }
|
||||
bool operator!= (const ConstructCounts& other) const noexcept { return tie() != other.tie(); }
|
||||
};
|
||||
|
||||
String& operator<< (String& str, const ConstructCounts& c)
|
||||
{
|
||||
return str << "{ constructions: " << c.constructions
|
||||
<< ", copies: " << c.copies
|
||||
<< ", moves: " << c.moves
|
||||
<< ", calls: " << c.calls
|
||||
<< ", destructions: " << c.destructions
|
||||
<< " }";
|
||||
}
|
||||
|
||||
class FixedSizeFunctionTest : public UnitTest
|
||||
{
|
||||
static void toggleBool (bool& b) { b = ! b; }
|
||||
|
||||
struct ConstructCounter
|
||||
{
|
||||
explicit ConstructCounter (ConstructCounts& countsIn)
|
||||
: counts (countsIn) {}
|
||||
|
||||
ConstructCounter (const ConstructCounter& c)
|
||||
: counts (c.counts)
|
||||
{
|
||||
counts.copies += 1;
|
||||
}
|
||||
|
||||
ConstructCounter (ConstructCounter&& c) noexcept
|
||||
: counts (c.counts)
|
||||
{
|
||||
counts.moves += 1;
|
||||
}
|
||||
|
||||
~ConstructCounter() noexcept { counts.destructions += 1; }
|
||||
|
||||
void operator()() const noexcept { counts.calls += 1; }
|
||||
|
||||
ConstructCounts& counts;
|
||||
};
|
||||
|
||||
public:
|
||||
FixedSizeFunctionTest()
|
||||
: UnitTest ("Fixed Size Function", UnitTestCategories::dsp)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Can be constructed and called from a lambda");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
const auto result = 5;
|
||||
bool wasCalled = false;
|
||||
const auto lambda = [&] { wasCalled = true; return result; };
|
||||
|
||||
const FixedSizeFunction<sizeof (lambda), int()> fn (lambda);
|
||||
const auto out = fn();
|
||||
|
||||
expect (wasCalled);
|
||||
expectEquals (result, out);
|
||||
}
|
||||
|
||||
beginTest ("void fn can be constructed from function with return value");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
bool wasCalled = false;
|
||||
const auto lambda = [&] { wasCalled = true; return 5; };
|
||||
const FixedSizeFunction<sizeof (lambda), void()> fn (lambda);
|
||||
|
||||
fn();
|
||||
expect (wasCalled);
|
||||
}
|
||||
|
||||
beginTest ("Can be constructed and called from a function pointer");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
bool state = false;
|
||||
|
||||
const FixedSizeFunction<sizeof (void*), void (bool&)> fn (toggleBool);
|
||||
|
||||
fn (state);
|
||||
expect (state);
|
||||
|
||||
fn (state);
|
||||
expect (! state);
|
||||
|
||||
fn (state);
|
||||
expect (state);
|
||||
}
|
||||
|
||||
beginTest ("Default constructed functions throw if called");
|
||||
{
|
||||
const auto a = FixedSizeFunction<8, void()>();
|
||||
expectThrowsType (a(), std::bad_function_call)
|
||||
|
||||
const auto b = FixedSizeFunction<8, void()> (nullptr);
|
||||
expectThrowsType (b(), std::bad_function_call)
|
||||
}
|
||||
|
||||
beginTest ("Functions can be moved");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
ConstructCounts counts;
|
||||
|
||||
auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (ConstructCounter { counts });
|
||||
expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1)); // The temporary gets destroyed
|
||||
|
||||
a();
|
||||
expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1).withCalls (1));
|
||||
|
||||
const auto b = std::move (a);
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (1));
|
||||
|
||||
b();
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (2));
|
||||
|
||||
b();
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (3));
|
||||
}
|
||||
|
||||
beginTest ("Functions are destructed properly");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
ConstructCounts counts;
|
||||
const ConstructCounter toCopy { counts };
|
||||
|
||||
{
|
||||
auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (toCopy);
|
||||
expectEquals (counts, ConstructCounts().withCopies (1));
|
||||
}
|
||||
|
||||
expectEquals (counts, ConstructCounts().withCopies (1).withDestructions (1));
|
||||
}
|
||||
|
||||
beginTest ("Avoid destructing functions that fail to construct");
|
||||
{
|
||||
struct BadConstructor
|
||||
{
|
||||
explicit BadConstructor (ConstructCounts& c)
|
||||
: counts (c)
|
||||
{
|
||||
counts.constructions += 1;
|
||||
throw std::runtime_error { "this was meant to happen" };
|
||||
}
|
||||
|
||||
~BadConstructor() noexcept { counts.destructions += 1; }
|
||||
|
||||
void operator()() const noexcept { counts.calls += 1; }
|
||||
|
||||
ConstructCounts& counts;
|
||||
};
|
||||
|
||||
ConstructCounts counts;
|
||||
|
||||
expectThrowsType ((FixedSizeFunction<sizeof (BadConstructor), void()> (BadConstructor { counts })),
|
||||
std::runtime_error)
|
||||
|
||||
expectEquals (counts, ConstructCounts().withConstructions (1));
|
||||
}
|
||||
|
||||
beginTest ("Equality checks work");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
FixedSizeFunction<8, void()> a;
|
||||
expect (! bool (a));
|
||||
expect (a == nullptr);
|
||||
expect (nullptr == a);
|
||||
expect (! (a != nullptr));
|
||||
expect (! (nullptr != a));
|
||||
|
||||
FixedSizeFunction<8, void()> b ([] {});
|
||||
expect (bool (b));
|
||||
expect (b != nullptr);
|
||||
expect (nullptr != b);
|
||||
expect (! (b == nullptr));
|
||||
expect (! (nullptr == b));
|
||||
}
|
||||
|
||||
beginTest ("Functions can be cleared");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
FixedSizeFunction<8, void()> fn ([] {});
|
||||
expect (bool (fn));
|
||||
|
||||
fn = nullptr;
|
||||
expect (! bool (fn));
|
||||
}
|
||||
|
||||
beginTest ("Functions can be assigned");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<8, void()>;
|
||||
|
||||
int numCallsA = 0;
|
||||
int numCallsB = 0;
|
||||
|
||||
Fn x;
|
||||
Fn y;
|
||||
expect (! bool (x));
|
||||
expect (! bool (y));
|
||||
|
||||
x = [&] { numCallsA += 1; };
|
||||
y = [&] { numCallsB += 1; };
|
||||
expect (bool (x));
|
||||
expect (bool (y));
|
||||
|
||||
x();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 0);
|
||||
|
||||
y();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 1);
|
||||
|
||||
x = std::move (y);
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 1);
|
||||
|
||||
x();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 2);
|
||||
}
|
||||
|
||||
beginTest ("Functions may mutate internal state");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<64, void()>;
|
||||
|
||||
Fn x;
|
||||
expect (! bool (x));
|
||||
|
||||
int numCalls = 0;
|
||||
x = [&numCalls, counter = 0]() mutable { counter += 1; numCalls = counter; };
|
||||
expect (bool (x));
|
||||
|
||||
expectEquals (numCalls, 0);
|
||||
|
||||
x();
|
||||
expectEquals (numCalls, 1);
|
||||
|
||||
x();
|
||||
expectEquals (numCalls, 2);
|
||||
}
|
||||
|
||||
beginTest ("Functions can sink move-only parameters");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<64, int (std::unique_ptr<int>)>;
|
||||
|
||||
auto value = 5;
|
||||
auto ptr = std::make_unique<int> (value);
|
||||
|
||||
Fn fn = [] (std::unique_ptr<int> p) { return *p; };
|
||||
|
||||
expect (value == fn (std::move (ptr)));
|
||||
}
|
||||
|
||||
beginTest ("Functions be converted from smaller functions");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using SmallFn = FixedSizeFunction<20, void()>;
|
||||
using LargeFn = FixedSizeFunction<21, void()>;
|
||||
|
||||
bool smallCalled = false;
|
||||
bool largeCalled = false;
|
||||
|
||||
SmallFn small = [&smallCalled, a = std::array<char, 8>{}] { smallCalled = true; juce::ignoreUnused (a); };
|
||||
LargeFn large = [&largeCalled, a = std::array<char, 8>{}] { largeCalled = true; juce::ignoreUnused (a); };
|
||||
|
||||
large = std::move (small);
|
||||
|
||||
large();
|
||||
|
||||
expect (smallCalled);
|
||||
expect (! largeCalled);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FixedSizeFunctionTest fixedSizedFunctionTest;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_ENABLE_ALLOCATION_HOOKS
|
||||
#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE const UnitTestAllocationChecker checker (*this)
|
||||
#else
|
||||
#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
|
||||
#endif
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
class ConstructCounts
|
||||
{
|
||||
auto tie() const noexcept { return std::tie (constructions, copies, moves, calls, destructions); }
|
||||
|
||||
public:
|
||||
int constructions = 0;
|
||||
int copies = 0;
|
||||
int moves = 0;
|
||||
int calls = 0;
|
||||
int destructions = 0;
|
||||
|
||||
ConstructCounts withConstructions (int i) const noexcept { auto c = *this; c.constructions = i; return c; }
|
||||
ConstructCounts withCopies (int i) const noexcept { auto c = *this; c.copies = i; return c; }
|
||||
ConstructCounts withMoves (int i) const noexcept { auto c = *this; c.moves = i; return c; }
|
||||
ConstructCounts withCalls (int i) const noexcept { auto c = *this; c.calls = i; return c; }
|
||||
ConstructCounts withDestructions (int i) const noexcept { auto c = *this; c.destructions = i; return c; }
|
||||
|
||||
bool operator== (const ConstructCounts& other) const noexcept { return tie() == other.tie(); }
|
||||
bool operator!= (const ConstructCounts& other) const noexcept { return tie() != other.tie(); }
|
||||
};
|
||||
|
||||
String& operator<< (String& str, const ConstructCounts& c)
|
||||
{
|
||||
return str << "{ constructions: " << c.constructions
|
||||
<< ", copies: " << c.copies
|
||||
<< ", moves: " << c.moves
|
||||
<< ", calls: " << c.calls
|
||||
<< ", destructions: " << c.destructions
|
||||
<< " }";
|
||||
}
|
||||
|
||||
class FixedSizeFunctionTest : public UnitTest
|
||||
{
|
||||
static void toggleBool (bool& b) { b = ! b; }
|
||||
|
||||
struct ConstructCounter
|
||||
{
|
||||
explicit ConstructCounter (ConstructCounts& countsIn)
|
||||
: counts (countsIn) {}
|
||||
|
||||
ConstructCounter (const ConstructCounter& c)
|
||||
: counts (c.counts)
|
||||
{
|
||||
counts.copies += 1;
|
||||
}
|
||||
|
||||
ConstructCounter (ConstructCounter&& c) noexcept
|
||||
: counts (c.counts)
|
||||
{
|
||||
counts.moves += 1;
|
||||
}
|
||||
|
||||
~ConstructCounter() noexcept { counts.destructions += 1; }
|
||||
|
||||
void operator()() const noexcept { counts.calls += 1; }
|
||||
|
||||
ConstructCounts& counts;
|
||||
};
|
||||
|
||||
public:
|
||||
FixedSizeFunctionTest()
|
||||
: UnitTest ("Fixed Size Function", UnitTestCategories::dsp)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Can be constructed and called from a lambda");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
const auto result = 5;
|
||||
bool wasCalled = false;
|
||||
const auto lambda = [&] { wasCalled = true; return result; };
|
||||
|
||||
const FixedSizeFunction<sizeof (lambda), int()> fn (lambda);
|
||||
const auto out = fn();
|
||||
|
||||
expect (wasCalled);
|
||||
expectEquals (result, out);
|
||||
}
|
||||
|
||||
beginTest ("void fn can be constructed from function with return value");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
bool wasCalled = false;
|
||||
const auto lambda = [&] { wasCalled = true; return 5; };
|
||||
const FixedSizeFunction<sizeof (lambda), void()> fn (lambda);
|
||||
|
||||
fn();
|
||||
expect (wasCalled);
|
||||
}
|
||||
|
||||
beginTest ("Can be constructed and called from a function pointer");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
bool state = false;
|
||||
|
||||
const FixedSizeFunction<sizeof (void*), void (bool&)> fn (toggleBool);
|
||||
|
||||
fn (state);
|
||||
expect (state);
|
||||
|
||||
fn (state);
|
||||
expect (! state);
|
||||
|
||||
fn (state);
|
||||
expect (state);
|
||||
}
|
||||
|
||||
beginTest ("Default constructed functions throw if called");
|
||||
{
|
||||
const auto a = FixedSizeFunction<8, void()>();
|
||||
expectThrowsType (a(), std::bad_function_call)
|
||||
|
||||
const auto b = FixedSizeFunction<8, void()> (nullptr);
|
||||
expectThrowsType (b(), std::bad_function_call)
|
||||
}
|
||||
|
||||
beginTest ("Functions can be moved");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
ConstructCounts counts;
|
||||
|
||||
auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (ConstructCounter { counts });
|
||||
expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1)); // The temporary gets destroyed
|
||||
|
||||
a();
|
||||
expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1).withCalls (1));
|
||||
|
||||
const auto b = std::move (a);
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (1));
|
||||
|
||||
b();
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (2));
|
||||
|
||||
b();
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (3));
|
||||
}
|
||||
|
||||
beginTest ("Functions are destructed properly");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
ConstructCounts counts;
|
||||
const ConstructCounter toCopy { counts };
|
||||
|
||||
{
|
||||
auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (toCopy);
|
||||
expectEquals (counts, ConstructCounts().withCopies (1));
|
||||
}
|
||||
|
||||
expectEquals (counts, ConstructCounts().withCopies (1).withDestructions (1));
|
||||
}
|
||||
|
||||
beginTest ("Avoid destructing functions that fail to construct");
|
||||
{
|
||||
struct BadConstructor
|
||||
{
|
||||
explicit BadConstructor (ConstructCounts& c)
|
||||
: counts (c)
|
||||
{
|
||||
counts.constructions += 1;
|
||||
throw std::runtime_error { "this was meant to happen" };
|
||||
}
|
||||
|
||||
BadConstructor (const BadConstructor&) = default;
|
||||
BadConstructor& operator= (const BadConstructor&) = delete;
|
||||
|
||||
~BadConstructor() noexcept { counts.destructions += 1; }
|
||||
|
||||
void operator()() const noexcept { counts.calls += 1; }
|
||||
|
||||
ConstructCounts& counts;
|
||||
};
|
||||
|
||||
ConstructCounts counts;
|
||||
|
||||
expectThrowsType ((FixedSizeFunction<sizeof (BadConstructor), void()> (BadConstructor { counts })),
|
||||
std::runtime_error)
|
||||
|
||||
expectEquals (counts, ConstructCounts().withConstructions (1));
|
||||
}
|
||||
|
||||
beginTest ("Equality checks work");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
FixedSizeFunction<8, void()> a;
|
||||
expect (! bool (a));
|
||||
expect (a == nullptr);
|
||||
expect (nullptr == a);
|
||||
expect (! (a != nullptr));
|
||||
expect (! (nullptr != a));
|
||||
|
||||
FixedSizeFunction<8, void()> b ([] {});
|
||||
expect (bool (b));
|
||||
expect (b != nullptr);
|
||||
expect (nullptr != b);
|
||||
expect (! (b == nullptr));
|
||||
expect (! (nullptr == b));
|
||||
}
|
||||
|
||||
beginTest ("Functions can be cleared");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
FixedSizeFunction<8, void()> fn ([] {});
|
||||
expect (bool (fn));
|
||||
|
||||
fn = nullptr;
|
||||
expect (! bool (fn));
|
||||
}
|
||||
|
||||
beginTest ("Functions can be assigned");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<8, void()>;
|
||||
|
||||
int numCallsA = 0;
|
||||
int numCallsB = 0;
|
||||
|
||||
Fn x;
|
||||
Fn y;
|
||||
expect (! bool (x));
|
||||
expect (! bool (y));
|
||||
|
||||
x = [&] { numCallsA += 1; };
|
||||
y = [&] { numCallsB += 1; };
|
||||
expect (bool (x));
|
||||
expect (bool (y));
|
||||
|
||||
x();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 0);
|
||||
|
||||
y();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 1);
|
||||
|
||||
x = std::move (y);
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 1);
|
||||
|
||||
x();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 2);
|
||||
}
|
||||
|
||||
beginTest ("Functions may mutate internal state");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<64, void()>;
|
||||
|
||||
Fn x;
|
||||
expect (! bool (x));
|
||||
|
||||
int numCalls = 0;
|
||||
x = [&numCalls, counter = 0]() mutable { counter += 1; numCalls = counter; };
|
||||
expect (bool (x));
|
||||
|
||||
expectEquals (numCalls, 0);
|
||||
|
||||
x();
|
||||
expectEquals (numCalls, 1);
|
||||
|
||||
x();
|
||||
expectEquals (numCalls, 2);
|
||||
}
|
||||
|
||||
beginTest ("Functions can sink move-only parameters");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<64, int (std::unique_ptr<int>)>;
|
||||
|
||||
auto value = 5;
|
||||
auto ptr = std::make_unique<int> (value);
|
||||
|
||||
Fn fn = [] (std::unique_ptr<int> p) { return *p; };
|
||||
|
||||
expect (value == fn (std::move (ptr)));
|
||||
}
|
||||
|
||||
beginTest ("Functions be converted from smaller functions");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using SmallFn = FixedSizeFunction<20, void()>;
|
||||
using LargeFn = FixedSizeFunction<21, void()>;
|
||||
|
||||
bool smallCalled = false;
|
||||
bool largeCalled = false;
|
||||
|
||||
SmallFn small = [&smallCalled, a = std::array<char, 8>{}] { smallCalled = true; juce::ignoreUnused (a); };
|
||||
LargeFn large = [&largeCalled, a = std::array<char, 8>{}] { largeCalled = true; juce::ignoreUnused (a); };
|
||||
|
||||
large = std::move (small);
|
||||
|
||||
large();
|
||||
|
||||
expect (smallCalled);
|
||||
expect (! largeCalled);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FixedSizeFunctionTest fixedSizedFunctionTest;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
|
||||
|
@ -1,403 +1,400 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// This class is needed internally.
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A wrapper around the platform's native SIMD register type.
|
||||
|
||||
This class is only available on SIMD machines. Use JUCE_USE_SIMD to query
|
||||
if SIMD is available for your system.
|
||||
|
||||
SIMDRegister<Type> is a templated class representing the native
|
||||
vectorized version of FloatingType. SIMDRegister supports all numerical
|
||||
primitive types and std:complex<float> and std::complex<double> supports
|
||||
and most operations of the corresponding primitive
|
||||
type. Additionally, SIMDRegister can be accessed like an array to extract
|
||||
the individual elements.
|
||||
|
||||
If you are using SIMDRegister as a pointer, then you must ensure that the
|
||||
memory is sufficiently aligned for SIMD vector operations. Failing to do so
|
||||
will result in crashes or very slow code. Use SIMDRegister::isSIMDAligned
|
||||
to query if a pointer is sufficiently aligned for SIMD vector operations.
|
||||
|
||||
Note that using SIMDRegister without enabling optimizations will result
|
||||
in code with very poor performance.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename Type>
|
||||
struct SIMDRegister
|
||||
{
|
||||
//==============================================================================
|
||||
/** The type that represents the individual constituents of the SIMD Register */
|
||||
using ElementType = Type;
|
||||
|
||||
/** STL compatible value_type definition (same as ElementType). */
|
||||
using value_type = ElementType;
|
||||
|
||||
/** The corresponding primitive integer type, for example, this will be int32_t
|
||||
if type is a float. */
|
||||
using MaskType = typename SIMDInternal::MaskTypeFor<ElementType>::type;
|
||||
|
||||
//==============================================================================
|
||||
// Here are some types which are needed internally
|
||||
|
||||
/** The native primitive type (used internally). */
|
||||
using PrimitiveType = typename SIMDInternal::PrimitiveType<ElementType>::type;
|
||||
|
||||
/** The native operations for this platform and type combination (used internally) */
|
||||
using NativeOps = SIMDNativeOps<PrimitiveType>;
|
||||
|
||||
/** The native type (used internally). */
|
||||
using vSIMDType = typename NativeOps::vSIMDType;
|
||||
|
||||
/** The corresponding integer SIMDRegister type (used internally). */
|
||||
using vMaskType = SIMDRegister<MaskType>;
|
||||
|
||||
/** The internal native type for the corresponding mask type (used internally). */
|
||||
using vMaskSIMDType = typename vMaskType::vSIMDType;
|
||||
|
||||
/** Wrapper for operations which need to be handled differently for complex
|
||||
and scalar types (used internally). */
|
||||
using CmplxOps = CmplxSIMDOps<ElementType>;
|
||||
|
||||
/** Type which is returned when using the subscript operator. The returned type
|
||||
should be used just like the type ElementType. */
|
||||
struct ElementAccess;
|
||||
|
||||
//==============================================================================
|
||||
/** The size in bytes of this register. */
|
||||
static constexpr size_t SIMDRegisterSize = sizeof (vSIMDType);
|
||||
|
||||
/** The number of elements that this vector can hold. */
|
||||
static constexpr size_t SIMDNumElements = SIMDRegisterSize / sizeof (ElementType);
|
||||
|
||||
vSIMDType value;
|
||||
|
||||
/** Default constructor. */
|
||||
inline SIMDRegister() noexcept = default;
|
||||
|
||||
/** Constructs an object from the native SIMD type. */
|
||||
inline SIMDRegister (vSIMDType a) noexcept : value (a) {}
|
||||
|
||||
/** Constructs an object from a scalar type by broadcasting it to all elements. */
|
||||
inline SIMDRegister (Type s) noexcept { *this = s; }
|
||||
|
||||
/** Destructor. */
|
||||
inline ~SIMDRegister() noexcept = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of elements in this vector. */
|
||||
static constexpr size_t size() noexcept { return SIMDNumElements; }
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a new SIMDRegister from the corresponding scalar primitive.
|
||||
The scalar is extended to all elements of the vector. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE expand (ElementType s) noexcept { return {CmplxOps::expand (s)}; }
|
||||
|
||||
/** Creates a new SIMDRegister from the internal SIMD type (for example
|
||||
__mm128 for single-precision floating point on SSE architectures). */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE fromNative (vSIMDType a) noexcept { return {a}; }
|
||||
|
||||
/** Creates a new SIMDRegister from the first SIMDNumElements of a scalar array. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE fromRawArray (const ElementType* a) noexcept
|
||||
{
|
||||
jassert (isSIMDAligned (a));
|
||||
return {CmplxOps::load (a)};
|
||||
}
|
||||
|
||||
/** Copies the elements of the SIMDRegister to a scalar array in memory. */
|
||||
inline void JUCE_VECTOR_CALLTYPE copyToRawArray (ElementType* a) const noexcept
|
||||
{
|
||||
jassert (isSIMDAligned (a));
|
||||
CmplxOps::store (value, a);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementType JUCE_VECTOR_CALLTYPE get (size_t idx) const noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
return CmplxOps::get (value, idx);
|
||||
}
|
||||
|
||||
/** Sets the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline void JUCE_VECTOR_CALLTYPE set (size_t idx, ElementType v) noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
value = CmplxOps::set (value, idx, v);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementType JUCE_VECTOR_CALLTYPE operator[] (size_t idx) const noexcept
|
||||
{
|
||||
return get (idx);
|
||||
}
|
||||
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementAccess JUCE_VECTOR_CALLTYPE operator[] (size_t idx) noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
return ElementAccess (*this, idx);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator+= (SIMDRegister v) noexcept { value = NativeOps::add (value, v.value); return *this; }
|
||||
|
||||
/** Subtracts another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator-= (SIMDRegister v) noexcept { value = NativeOps::sub (value, v.value); return *this; }
|
||||
|
||||
/** Multiplies another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator*= (SIMDRegister v) noexcept { value = CmplxOps::mul (value, v.value); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Broadcasts the scalar to all elements of the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator= (ElementType s) noexcept { value = CmplxOps::expand (s); return *this; }
|
||||
|
||||
/** Adds a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator+= (ElementType s) noexcept { value = NativeOps::add (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
/** Subtracts a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator-= (ElementType s) noexcept { value = NativeOps::sub (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
/** Multiplies a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator*= (ElementType s) noexcept { value = CmplxOps::mul (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Bit-and the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator&= (vMaskType v) noexcept { value = NativeOps::bit_and (value, toVecType (v.value)); return *this; }
|
||||
|
||||
/** Bit-or the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator|= (vMaskType v) noexcept { value = NativeOps::bit_or (value, toVecType (v.value)); return *this; }
|
||||
|
||||
/** Bit-xor the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator^= (vMaskType v) noexcept { value = NativeOps::bit_xor (value, toVecType (v.value)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Bit-and each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator&= (MaskType s) noexcept { value = NativeOps::bit_and (value, toVecType (s)); return *this; }
|
||||
|
||||
/** Bit-or each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator|= (MaskType s) noexcept { value = NativeOps::bit_or (value, toVecType (s)); return *this; }
|
||||
|
||||
/** Bit-xor each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator^= (MaskType s) noexcept { value = NativeOps::bit_xor (value, toVecType (s)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the sum of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator+ (SIMDRegister v) const noexcept { return { NativeOps::add (value, v.value) }; }
|
||||
|
||||
/** Returns the difference of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator- (SIMDRegister v) const noexcept { return { NativeOps::sub (value, v.value) }; }
|
||||
|
||||
/** Returns the product of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator* (SIMDRegister v) const noexcept { return { CmplxOps::mul (value, v.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a vector where each element is the sum of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator+ (ElementType s) const noexcept { return { NativeOps::add (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the difference of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator- (ElementType s) const noexcept { return { NativeOps::sub (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the product of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator* (ElementType s) const noexcept { return { CmplxOps::mul (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the bit-and of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator& (vMaskType v) const noexcept { return { NativeOps::bit_and (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns the bit-or of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator| (vMaskType v) const noexcept { return { NativeOps::bit_or (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns the bit-xor of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator^ (vMaskType v) const noexcept { return { NativeOps::bit_xor (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-inverted value of the corresponding element in the receiver.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator~() const noexcept { return { NativeOps::bit_not (value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a vector where each element is the bit-and'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator& (MaskType s) const noexcept { return { NativeOps::bit_and (value, toVecType (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-or'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator| (MaskType s) const noexcept { return { NativeOps::bit_or (value, toVecType (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-xor'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator^ (MaskType s) const noexcept { return { NativeOps::bit_xor (value, toVecType (s)) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if all element-wise comparisons return true. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator== (SIMDRegister other) const noexcept { return NativeOps::allEqual (value, other.value); }
|
||||
|
||||
/** Returns true if any element-wise comparisons return false. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator!= (SIMDRegister other) const noexcept { return ! (*this == other); }
|
||||
|
||||
/** Returns true if all elements are equal to the scalar. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator== (Type s) const noexcept { return *this == SIMDRegister::expand (s); }
|
||||
|
||||
/** Returns true if any elements are not equal to the scalar. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator!= (Type s) const noexcept { return ! (*this == s); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE equal (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::equal (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is not equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE notEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::notEqual (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is less than to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE lessThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (b.value, a.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is than or equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE lessThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (b.value, a.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is greater than to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE greaterThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is greater than or equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE greaterThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (a.value, b.value)); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a new vector where each element is the minimum of the corresponding element of a and b. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE min (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::min (a.value, b.value) }; }
|
||||
|
||||
/** Returns a new vector where each element is the maximum of the corresponding element of a and b. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE max (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::max (a.value, b.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Multiplies b and c and adds the result to a. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE multiplyAdd (SIMDRegister a, const SIMDRegister b, SIMDRegister c) noexcept
|
||||
{
|
||||
return { CmplxOps::muladd (a.value, b.value, c.value) };
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a scalar which is the sum of all elements of the receiver. */
|
||||
inline ElementType sum() const noexcept { return CmplxOps::sum (value); }
|
||||
|
||||
//==============================================================================
|
||||
/** Truncates each element to its integer part.
|
||||
Effectively discards the fractional part of each element. A.k.a. round to zero. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE truncate (SIMDRegister a) noexcept { return { NativeOps::truncate (a.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the absolute value of each element. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE abs (SIMDRegister a) noexcept
|
||||
{
|
||||
return a - (a * (expand (ElementType (2)) & lessThan (a, expand (ElementType (0)))));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Checks if the given pointer is sufficiently aligned for using SIMD operations. */
|
||||
static bool isSIMDAligned (const ElementType* ptr) noexcept
|
||||
{
|
||||
uintptr_t bitmask = SIMDRegisterSize - 1;
|
||||
return (reinterpret_cast<uintptr_t> (ptr) & bitmask) == 0;
|
||||
}
|
||||
|
||||
/** Returns the next position in memory where isSIMDAligned returns true.
|
||||
|
||||
If the current position in memory is already aligned then this method
|
||||
will simply return the pointer.
|
||||
*/
|
||||
static ElementType* getNextSIMDAlignedPtr (ElementType* ptr) noexcept
|
||||
{
|
||||
return snapPointerToAlignment (ptr, SIMDRegisterSize);
|
||||
}
|
||||
|
||||
private:
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE toMaskType (vSIMDType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vSIMDType in;
|
||||
vMaskSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = a;
|
||||
return vMaskType::fromNative (u.out);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE toVecType (vMaskSIMDType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vMaskSIMDType in;
|
||||
vSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = a;
|
||||
return u.out;
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE toVecType (MaskType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vMaskSIMDType in;
|
||||
vSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = CmplxSIMDOps<MaskType>::expand (a);
|
||||
return u.out;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
|
||||
#ifndef DOXYGEN
|
||||
#include "juce_SIMDRegister_Impl.h"
|
||||
#endif
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// This class is needed internally.
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A wrapper around the platform's native SIMD register type.
|
||||
|
||||
This class is only available on SIMD machines. Use JUCE_USE_SIMD to query
|
||||
if SIMD is available for your system.
|
||||
|
||||
SIMDRegister<Type> is a templated class representing the native
|
||||
vectorized version of FloatingType. SIMDRegister supports all numerical
|
||||
primitive types and std:complex<float> and std::complex<double> supports
|
||||
and most operations of the corresponding primitive
|
||||
type. Additionally, SIMDRegister can be accessed like an array to extract
|
||||
the individual elements.
|
||||
|
||||
If you are using SIMDRegister as a pointer, then you must ensure that the
|
||||
memory is sufficiently aligned for SIMD vector operations. Failing to do so
|
||||
will result in crashes or very slow code. Use SIMDRegister::isSIMDAligned
|
||||
to query if a pointer is sufficiently aligned for SIMD vector operations.
|
||||
|
||||
Note that using SIMDRegister without enabling optimizations will result
|
||||
in code with very poor performance.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename Type>
|
||||
struct SIMDRegister
|
||||
{
|
||||
//==============================================================================
|
||||
/** The type that represents the individual constituents of the SIMD Register */
|
||||
using ElementType = Type;
|
||||
|
||||
/** STL compatible value_type definition (same as ElementType). */
|
||||
using value_type = ElementType;
|
||||
|
||||
/** The corresponding primitive integer type, for example, this will be int32_t
|
||||
if type is a float. */
|
||||
using MaskType = typename SIMDInternal::MaskTypeFor<ElementType>::type;
|
||||
|
||||
//==============================================================================
|
||||
// Here are some types which are needed internally
|
||||
|
||||
/** The native primitive type (used internally). */
|
||||
using PrimitiveType = typename SIMDInternal::PrimitiveType<ElementType>::type;
|
||||
|
||||
/** The native operations for this platform and type combination (used internally) */
|
||||
using NativeOps = SIMDNativeOps<PrimitiveType>;
|
||||
|
||||
/** The native type (used internally). */
|
||||
using vSIMDType = typename NativeOps::vSIMDType;
|
||||
|
||||
/** The corresponding integer SIMDRegister type (used internally). */
|
||||
using vMaskType = SIMDRegister<MaskType>;
|
||||
|
||||
/** The internal native type for the corresponding mask type (used internally). */
|
||||
using vMaskSIMDType = typename vMaskType::vSIMDType;
|
||||
|
||||
/** Wrapper for operations which need to be handled differently for complex
|
||||
and scalar types (used internally). */
|
||||
using CmplxOps = CmplxSIMDOps<ElementType>;
|
||||
|
||||
/** Type which is returned when using the subscript operator. The returned type
|
||||
should be used just like the type ElementType. */
|
||||
struct ElementAccess;
|
||||
|
||||
//==============================================================================
|
||||
/** The size in bytes of this register. */
|
||||
static constexpr size_t SIMDRegisterSize = sizeof (vSIMDType);
|
||||
|
||||
/** The number of elements that this vector can hold. */
|
||||
static constexpr size_t SIMDNumElements = SIMDRegisterSize / sizeof (ElementType);
|
||||
|
||||
vSIMDType value;
|
||||
|
||||
/** Default constructor. */
|
||||
inline SIMDRegister() noexcept = default;
|
||||
|
||||
/** Constructs an object from the native SIMD type. */
|
||||
inline SIMDRegister (vSIMDType a) noexcept : value (a) {}
|
||||
|
||||
/** Constructs an object from a scalar type by broadcasting it to all elements. */
|
||||
inline SIMDRegister (Type s) noexcept { *this = s; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of elements in this vector. */
|
||||
static constexpr size_t size() noexcept { return SIMDNumElements; }
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a new SIMDRegister from the corresponding scalar primitive.
|
||||
The scalar is extended to all elements of the vector. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE expand (ElementType s) noexcept { return {CmplxOps::expand (s)}; }
|
||||
|
||||
/** Creates a new SIMDRegister from the internal SIMD type (for example
|
||||
__mm128 for single-precision floating point on SSE architectures). */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE fromNative (vSIMDType a) noexcept { return {a}; }
|
||||
|
||||
/** Creates a new SIMDRegister from the first SIMDNumElements of a scalar array. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE fromRawArray (const ElementType* a) noexcept
|
||||
{
|
||||
jassert (isSIMDAligned (a));
|
||||
return {CmplxOps::load (a)};
|
||||
}
|
||||
|
||||
/** Copies the elements of the SIMDRegister to a scalar array in memory. */
|
||||
inline void JUCE_VECTOR_CALLTYPE copyToRawArray (ElementType* a) const noexcept
|
||||
{
|
||||
jassert (isSIMDAligned (a));
|
||||
CmplxOps::store (value, a);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementType JUCE_VECTOR_CALLTYPE get (size_t idx) const noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
return CmplxOps::get (value, idx);
|
||||
}
|
||||
|
||||
/** Sets the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline void JUCE_VECTOR_CALLTYPE set (size_t idx, ElementType v) noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
value = CmplxOps::set (value, idx, v);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementType JUCE_VECTOR_CALLTYPE operator[] (size_t idx) const noexcept
|
||||
{
|
||||
return get (idx);
|
||||
}
|
||||
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementAccess JUCE_VECTOR_CALLTYPE operator[] (size_t idx) noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
return ElementAccess (*this, idx);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator+= (SIMDRegister v) noexcept { value = NativeOps::add (value, v.value); return *this; }
|
||||
|
||||
/** Subtracts another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator-= (SIMDRegister v) noexcept { value = NativeOps::sub (value, v.value); return *this; }
|
||||
|
||||
/** Multiplies another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator*= (SIMDRegister v) noexcept { value = CmplxOps::mul (value, v.value); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Broadcasts the scalar to all elements of the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator= (ElementType s) noexcept { value = CmplxOps::expand (s); return *this; }
|
||||
|
||||
/** Adds a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator+= (ElementType s) noexcept { value = NativeOps::add (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
/** Subtracts a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator-= (ElementType s) noexcept { value = NativeOps::sub (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
/** Multiplies a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator*= (ElementType s) noexcept { value = CmplxOps::mul (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Bit-and the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator&= (vMaskType v) noexcept { value = NativeOps::bit_and (value, toVecType (v.value)); return *this; }
|
||||
|
||||
/** Bit-or the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator|= (vMaskType v) noexcept { value = NativeOps::bit_or (value, toVecType (v.value)); return *this; }
|
||||
|
||||
/** Bit-xor the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator^= (vMaskType v) noexcept { value = NativeOps::bit_xor (value, toVecType (v.value)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Bit-and each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator&= (MaskType s) noexcept { value = NativeOps::bit_and (value, toVecType (s)); return *this; }
|
||||
|
||||
/** Bit-or each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator|= (MaskType s) noexcept { value = NativeOps::bit_or (value, toVecType (s)); return *this; }
|
||||
|
||||
/** Bit-xor each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator^= (MaskType s) noexcept { value = NativeOps::bit_xor (value, toVecType (s)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the sum of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator+ (SIMDRegister v) const noexcept { return { NativeOps::add (value, v.value) }; }
|
||||
|
||||
/** Returns the difference of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator- (SIMDRegister v) const noexcept { return { NativeOps::sub (value, v.value) }; }
|
||||
|
||||
/** Returns the product of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator* (SIMDRegister v) const noexcept { return { CmplxOps::mul (value, v.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a vector where each element is the sum of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator+ (ElementType s) const noexcept { return { NativeOps::add (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the difference of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator- (ElementType s) const noexcept { return { NativeOps::sub (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the product of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator* (ElementType s) const noexcept { return { CmplxOps::mul (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the bit-and of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator& (vMaskType v) const noexcept { return { NativeOps::bit_and (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns the bit-or of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator| (vMaskType v) const noexcept { return { NativeOps::bit_or (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns the bit-xor of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator^ (vMaskType v) const noexcept { return { NativeOps::bit_xor (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-inverted value of the corresponding element in the receiver.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator~() const noexcept { return { NativeOps::bit_not (value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a vector where each element is the bit-and'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator& (MaskType s) const noexcept { return { NativeOps::bit_and (value, toVecType (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-or'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator| (MaskType s) const noexcept { return { NativeOps::bit_or (value, toVecType (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-xor'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator^ (MaskType s) const noexcept { return { NativeOps::bit_xor (value, toVecType (s)) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if all element-wise comparisons return true. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator== (SIMDRegister other) const noexcept { return NativeOps::allEqual (value, other.value); }
|
||||
|
||||
/** Returns true if any element-wise comparisons return false. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator!= (SIMDRegister other) const noexcept { return ! (*this == other); }
|
||||
|
||||
/** Returns true if all elements are equal to the scalar. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator== (Type s) const noexcept { return *this == SIMDRegister::expand (s); }
|
||||
|
||||
/** Returns true if any elements are not equal to the scalar. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator!= (Type s) const noexcept { return ! (*this == s); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE equal (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::equal (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is not equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE notEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::notEqual (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is less than to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE lessThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (b.value, a.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is than or equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE lessThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (b.value, a.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is greater than to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE greaterThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is greater than or equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE greaterThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (a.value, b.value)); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a new vector where each element is the minimum of the corresponding element of a and b. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE min (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::min (a.value, b.value) }; }
|
||||
|
||||
/** Returns a new vector where each element is the maximum of the corresponding element of a and b. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE max (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::max (a.value, b.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Multiplies b and c and adds the result to a. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE multiplyAdd (SIMDRegister a, const SIMDRegister b, SIMDRegister c) noexcept
|
||||
{
|
||||
return { CmplxOps::muladd (a.value, b.value, c.value) };
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a scalar which is the sum of all elements of the receiver. */
|
||||
inline ElementType sum() const noexcept { return CmplxOps::sum (value); }
|
||||
|
||||
//==============================================================================
|
||||
/** Truncates each element to its integer part.
|
||||
Effectively discards the fractional part of each element. A.k.a. round to zero. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE truncate (SIMDRegister a) noexcept { return { NativeOps::truncate (a.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the absolute value of each element. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE abs (SIMDRegister a) noexcept
|
||||
{
|
||||
return a - (a * (expand (ElementType (2)) & lessThan (a, expand (ElementType (0)))));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Checks if the given pointer is sufficiently aligned for using SIMD operations. */
|
||||
static bool isSIMDAligned (const ElementType* ptr) noexcept
|
||||
{
|
||||
uintptr_t bitmask = SIMDRegisterSize - 1;
|
||||
return (reinterpret_cast<uintptr_t> (ptr) & bitmask) == 0;
|
||||
}
|
||||
|
||||
/** Returns the next position in memory where isSIMDAligned returns true.
|
||||
|
||||
If the current position in memory is already aligned then this method
|
||||
will simply return the pointer.
|
||||
*/
|
||||
static ElementType* getNextSIMDAlignedPtr (ElementType* ptr) noexcept
|
||||
{
|
||||
return snapPointerToAlignment (ptr, SIMDRegisterSize);
|
||||
}
|
||||
|
||||
private:
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE toMaskType (vSIMDType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vSIMDType in;
|
||||
vMaskSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = a;
|
||||
return vMaskType::fromNative (u.out);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE toVecType (vMaskSIMDType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vMaskSIMDType in;
|
||||
vSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = a;
|
||||
return u.out;
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE toVecType (MaskType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vMaskSIMDType in;
|
||||
vSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = CmplxSIMDOps<MaskType>::expand (a);
|
||||
return u.out;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
|
||||
#ifndef DOXYGEN
|
||||
#include "juce_SIMDRegister_Impl.h"
|
||||
#endif
|
||||
|
@ -1,177 +1,178 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
struct SIMDRegister<Type>::ElementAccess
|
||||
{
|
||||
operator Type() const { return simd.get (idx); }
|
||||
ElementAccess& operator= (Type scalar) noexcept { simd.set (idx, scalar); return *this; }
|
||||
ElementAccess& operator= (ElementAccess& o) noexcept { return operator= ((Type) o); }
|
||||
|
||||
private:
|
||||
friend struct SIMDRegister;
|
||||
ElementAccess (SIMDRegister& owner, size_t index) noexcept : simd (owner), idx (index) {}
|
||||
SIMDRegister& simd;
|
||||
size_t idx;
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
//==============================================================================
|
||||
/* This class is used internally by SIMDRegister to abstract away differences
|
||||
in operations which are different for complex and pure floating point types. */
|
||||
|
||||
// the pure floating-point version
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps
|
||||
{
|
||||
using vSIMDType = typename SIMDNativeOps<Scalar>::vSIMDType;
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE load (const Scalar* a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::load (a);
|
||||
}
|
||||
|
||||
static void JUCE_VECTOR_CALLTYPE store (vSIMDType value, Scalar* dest) noexcept
|
||||
{
|
||||
SIMDNativeOps<Scalar>::store (value, dest);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE expand (Scalar s) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::expand (s);
|
||||
}
|
||||
|
||||
static Scalar JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::get (v, i);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, Scalar s) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::set (v, i, s);
|
||||
}
|
||||
|
||||
static Scalar JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::sum (a);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::mul (a, b);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::multiplyAdd (a, b, c);
|
||||
}
|
||||
};
|
||||
|
||||
// The pure complex version
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps<std::complex<Scalar>>
|
||||
{
|
||||
using vSIMDType = typename SIMDNativeOps<Scalar>::vSIMDType;
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE load (const std::complex<Scalar>* a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::load (reinterpret_cast<const Scalar*> (a));
|
||||
}
|
||||
|
||||
static void JUCE_VECTOR_CALLTYPE store (vSIMDType value, std::complex<Scalar>* dest) noexcept
|
||||
{
|
||||
SIMDNativeOps<Scalar>::store (value, reinterpret_cast<Scalar*> (dest));
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE expand (std::complex<Scalar> s) noexcept
|
||||
{
|
||||
const int n = sizeof (vSIMDType) / sizeof (Scalar);
|
||||
|
||||
union
|
||||
{
|
||||
vSIMDType v;
|
||||
Scalar floats[(size_t) n];
|
||||
} u;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
u.floats[i] = (i & 1) == 0 ? s.real() : s.imag();
|
||||
|
||||
return u.v;
|
||||
}
|
||||
|
||||
static std::complex<Scalar> JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept
|
||||
{
|
||||
auto j = i << 1;
|
||||
return std::complex<Scalar> (SIMDNativeOps<Scalar>::get (v, j), SIMDNativeOps<Scalar>::get (v, j + 1));
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, std::complex<Scalar> s) noexcept
|
||||
{
|
||||
auto j = i << 1;
|
||||
return SIMDNativeOps<Scalar>::set (SIMDNativeOps<Scalar>::set (v, j, s.real()), j + 1, s.imag());
|
||||
}
|
||||
|
||||
static std::complex<Scalar> JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept
|
||||
{
|
||||
vSIMDType result = SIMDNativeOps<Scalar>::oddevensum (a);
|
||||
auto* ptr = reinterpret_cast<const Scalar*> (&result);
|
||||
return std::complex<Scalar> (ptr[0], ptr[1]);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::cmplxmul (a, b);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::add (a, SIMDNativeOps<Scalar>::cmplxmul (b, c));
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
namespace util
|
||||
{
|
||||
template <typename Type>
|
||||
inline void snapToZero (SIMDRegister<Type>&) noexcept {}
|
||||
}
|
||||
|
||||
} // namespace dsp
|
||||
|
||||
// Extend some common used global functions to SIMDRegister types
|
||||
template <typename Type>
|
||||
inline dsp::SIMDRegister<Type> JUCE_VECTOR_CALLTYPE jmin (dsp::SIMDRegister<Type> a, dsp::SIMDRegister<Type> b) { return dsp::SIMDRegister<Type>::min (a, b); }
|
||||
template <typename Type>
|
||||
inline dsp::SIMDRegister<Type> JUCE_VECTOR_CALLTYPE jmax (dsp::SIMDRegister<Type> a, dsp::SIMDRegister<Type> b) { return dsp::SIMDRegister<Type>::max (a, b); }
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
struct SIMDRegister<Type>::ElementAccess
|
||||
{
|
||||
ElementAccess (const ElementAccess&) = default;
|
||||
operator Type() const { return simd.get (idx); }
|
||||
ElementAccess& operator= (Type scalar) noexcept { simd.set (idx, scalar); return *this; }
|
||||
ElementAccess& operator= (const ElementAccess& o) noexcept { return operator= ((Type) o); }
|
||||
|
||||
private:
|
||||
friend struct SIMDRegister;
|
||||
ElementAccess (SIMDRegister& owner, size_t index) noexcept : simd (owner), idx (index) {}
|
||||
SIMDRegister& simd;
|
||||
size_t idx;
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
//==============================================================================
|
||||
/* This class is used internally by SIMDRegister to abstract away differences
|
||||
in operations which are different for complex and pure floating point types. */
|
||||
|
||||
// the pure floating-point version
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps
|
||||
{
|
||||
using vSIMDType = typename SIMDNativeOps<Scalar>::vSIMDType;
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE load (const Scalar* a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::load (a);
|
||||
}
|
||||
|
||||
static void JUCE_VECTOR_CALLTYPE store (vSIMDType value, Scalar* dest) noexcept
|
||||
{
|
||||
SIMDNativeOps<Scalar>::store (value, dest);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE expand (Scalar s) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::expand (s);
|
||||
}
|
||||
|
||||
static Scalar JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::get (v, i);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, Scalar s) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::set (v, i, s);
|
||||
}
|
||||
|
||||
static Scalar JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::sum (a);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::mul (a, b);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::multiplyAdd (a, b, c);
|
||||
}
|
||||
};
|
||||
|
||||
// The pure complex version
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps<std::complex<Scalar>>
|
||||
{
|
||||
using vSIMDType = typename SIMDNativeOps<Scalar>::vSIMDType;
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE load (const std::complex<Scalar>* a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::load (reinterpret_cast<const Scalar*> (a));
|
||||
}
|
||||
|
||||
static void JUCE_VECTOR_CALLTYPE store (vSIMDType value, std::complex<Scalar>* dest) noexcept
|
||||
{
|
||||
SIMDNativeOps<Scalar>::store (value, reinterpret_cast<Scalar*> (dest));
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE expand (std::complex<Scalar> s) noexcept
|
||||
{
|
||||
const int n = sizeof (vSIMDType) / sizeof (Scalar);
|
||||
|
||||
union
|
||||
{
|
||||
vSIMDType v;
|
||||
Scalar floats[(size_t) n];
|
||||
} u;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
u.floats[i] = (i & 1) == 0 ? s.real() : s.imag();
|
||||
|
||||
return u.v;
|
||||
}
|
||||
|
||||
static std::complex<Scalar> JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept
|
||||
{
|
||||
auto j = i << 1;
|
||||
return std::complex<Scalar> (SIMDNativeOps<Scalar>::get (v, j), SIMDNativeOps<Scalar>::get (v, j + 1));
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, std::complex<Scalar> s) noexcept
|
||||
{
|
||||
auto j = i << 1;
|
||||
return SIMDNativeOps<Scalar>::set (SIMDNativeOps<Scalar>::set (v, j, s.real()), j + 1, s.imag());
|
||||
}
|
||||
|
||||
static std::complex<Scalar> JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept
|
||||
{
|
||||
vSIMDType result = SIMDNativeOps<Scalar>::oddevensum (a);
|
||||
auto* ptr = reinterpret_cast<const Scalar*> (&result);
|
||||
return std::complex<Scalar> (ptr[0], ptr[1]);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::cmplxmul (a, b);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::add (a, SIMDNativeOps<Scalar>::cmplxmul (b, c));
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
namespace util
|
||||
{
|
||||
template <typename Type>
|
||||
inline void snapToZero (SIMDRegister<Type>&) noexcept {}
|
||||
}
|
||||
|
||||
} // namespace dsp
|
||||
|
||||
// Extend some common used global functions to SIMDRegister types
|
||||
template <typename Type>
|
||||
inline dsp::SIMDRegister<Type> JUCE_VECTOR_CALLTYPE jmin (dsp::SIMDRegister<Type> a, dsp::SIMDRegister<Type> b) { return dsp::SIMDRegister<Type>::min (a, b); }
|
||||
template <typename Type>
|
||||
inline dsp::SIMDRegister<Type> JUCE_VECTOR_CALLTYPE jmax (dsp::SIMDRegister<Type> a, dsp::SIMDRegister<Type> b) { return dsp::SIMDRegister<Type>::max (a, b); }
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user