Add Chromium-only Blender WebEngine parity work
This commit is contained in:
19
blender-5.2.0/source/blender/blenlib/BLI_alloca.h
Normal file
19
blender-5.2.0/source/blender/blenlib/BLI_alloca.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* Defines alloca and utility macro BLI_array_alloca
|
||||
*/
|
||||
|
||||
/* BLI_array_alloca / alloca */
|
||||
|
||||
#include <cstdlib>
|
||||
#include <type_traits> /* IWYU pragma: keep */
|
||||
|
||||
#define BLI_array_alloca(arr, realsize) \
|
||||
(std::remove_reference_t<decltype(arr)>)alloca(sizeof(*arr) * (realsize))
|
||||
135
blender-5.2.0/source/blender/blenlib/BLI_allocator.hh
Normal file
135
blender-5.2.0/source/blender/blenlib/BLI_allocator.hh
Normal file
@@ -0,0 +1,135 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* An `Allocator` can allocate and deallocate memory. It is used by containers such as
|
||||
* Vector. The allocators defined in this file do not work with standard library
|
||||
* containers such as std::vector.
|
||||
*
|
||||
* Every allocator has to implement two methods:
|
||||
* void *allocate(size_t size, size_t alignment, const char *name);
|
||||
* void deallocate(void *ptr);
|
||||
*
|
||||
* We don't use the std::allocator interface, because it does more than is really necessary for an
|
||||
* allocator and has some other quirks. It mixes the concepts of allocation and construction. It is
|
||||
* essentially forced to be a template, even though the allocator should not care about the type.
|
||||
* Also see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2271.html#std_allocator. Some
|
||||
* of these aspects have been improved in new versions of C++, so we might have to reevaluate the
|
||||
* strategy later on.
|
||||
*
|
||||
* The allocator interface dictated by this file is very simplistic, but for now that is all we
|
||||
* need. More complexity can be added when it seems necessary.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Use Blender's guarded allocator (aka MEM_*). This should always be used except there is a
|
||||
* good reason not to use it.
|
||||
*/
|
||||
class GuardedAllocator {
|
||||
public:
|
||||
void *allocate(size_t size, size_t alignment, const char *name)
|
||||
{
|
||||
/* Should we use MEM_new_uninitialized, when alignment is small? If yes, how small must
|
||||
* alignment be? */
|
||||
return MEM_new_uninitialized_aligned(size, alignment, name);
|
||||
}
|
||||
void *allocate_zero(size_t size, size_t alignment, const char *name)
|
||||
{
|
||||
if (alignment > MEM_MIN_CPP_ALIGNMENT) {
|
||||
/* There is no version of calloc with a specific alignment argument. */
|
||||
void *ptr = this->allocate(size, alignment, name);
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
return MEM_new_zeroed(size, name);
|
||||
}
|
||||
|
||||
void deallocate(void *ptr)
|
||||
{
|
||||
MEM_delete_void(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Like #GuardedAllocator, but makes sure each allocation has a minimum alignment. One use case is
|
||||
* reusing an allocation between multiple types that have different alignment requirements. The
|
||||
* default alignment template parameter should be large enough for any type in practice.
|
||||
*/
|
||||
template<size_t Alignment = 64ul> class GuardedAlignedAllocator {
|
||||
public:
|
||||
static constexpr size_t min_alignment = Alignment;
|
||||
|
||||
void *allocate(size_t size, size_t alignment, const char *name)
|
||||
{
|
||||
return MEM_new_uninitialized_aligned(size, std::max(alignment, min_alignment), name);
|
||||
}
|
||||
void *allocate_zero(size_t size, size_t alignment, const char *name)
|
||||
{
|
||||
if (std::max(alignment, Alignment) > MEM_MIN_CPP_ALIGNMENT) {
|
||||
/* There is no version of calloc with a specific alignment argument. */
|
||||
void *ptr = this->allocate(size, alignment, name);
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
return MEM_new_zeroed(size, name);
|
||||
}
|
||||
|
||||
void deallocate(void *ptr)
|
||||
{
|
||||
MEM_delete_void(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a wrapper around malloc/free. Only use this when the GuardedAllocator cannot be
|
||||
* used. This can be the case when the allocated memory might live longer than Blender's
|
||||
* allocator. For example, when the memory is owned by a static variable.
|
||||
*/
|
||||
class RawAllocator {
|
||||
private:
|
||||
struct MemHead {
|
||||
int offset;
|
||||
};
|
||||
|
||||
public:
|
||||
void *allocate(size_t size, size_t alignment, const char * /*name*/)
|
||||
{
|
||||
BLI_assert(is_power_of_2(int(alignment)));
|
||||
void *ptr = malloc(size + alignment + sizeof(MemHead));
|
||||
void *used_ptr = reinterpret_cast<void *>(
|
||||
uintptr_t(POINTER_OFFSET(ptr, alignment + sizeof(MemHead))) & ~(uintptr_t(alignment) - 1));
|
||||
int offset = int(intptr_t(used_ptr) - intptr_t(ptr));
|
||||
BLI_assert(offset >= int(sizeof(MemHead)));
|
||||
(static_cast<MemHead *>(used_ptr) - 1)->offset = offset;
|
||||
return used_ptr;
|
||||
}
|
||||
void *allocate_zero(size_t size, size_t alignment, const char *name)
|
||||
{
|
||||
void *ptr = this->allocate(size, alignment, name);
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(void *ptr)
|
||||
{
|
||||
MemHead *head = static_cast<MemHead *>(ptr) - 1;
|
||||
int offset = -head->offset;
|
||||
void *actual_pointer = POINTER_OFFSET(ptr, offset);
|
||||
free(actual_pointer);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
397
blender-5.2.0/source/blender/blenlib/BLI_any.hh
Normal file
397
blender-5.2.0/source/blender/blenlib/BLI_any.hh
Normal file
@@ -0,0 +1,397 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A #Any is a type-safe container for single values of any copy constructible type.
|
||||
* It is similar to #std::any but provides the following two additional features:
|
||||
* - Adjustable inline buffer capacity and alignment. #std::any has a small inline buffer in most
|
||||
* implementations as well, but its size is not guaranteed.
|
||||
* - Can store additional user-defined type information without increasing the stack size of #Any.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "BLI_memory_utils.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace blenlib_detail {
|
||||
|
||||
/**
|
||||
* Contains function pointers that manage the memory in an #Any.
|
||||
* Additional type specific #ExtraInfo can be embedded here as well.
|
||||
*/
|
||||
template<typename ExtraInfo> struct AnyTypeInfo {
|
||||
/* The pointers are allowed to be null, which means that the implementation is trivial. */
|
||||
void (*copy_construct)(void *dst, const void *src);
|
||||
void (*move_construct)(void *dst, void *src);
|
||||
void (*destruct)(void *src);
|
||||
const void *(*get)(const void *src);
|
||||
ExtraInfo extra_info;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used when #T is stored directly in the inline buffer of the #Any.
|
||||
*/
|
||||
template<typename ExtraInfo, typename T>
|
||||
inline constexpr AnyTypeInfo<ExtraInfo> info_for_inline = {
|
||||
is_trivially_copy_constructible_extended_v<T> ?
|
||||
nullptr :
|
||||
+[](void *dst, const void *src) { new (dst) T(*static_cast<const T *>(src)); },
|
||||
is_trivially_move_constructible_extended_v<T> ?
|
||||
nullptr :
|
||||
+[](void *dst, void *src) { new (dst) T(std::move(*static_cast<T *>(src))); },
|
||||
is_trivially_destructible_extended_v<T> ?
|
||||
nullptr :
|
||||
+[](void *src) { std::destroy_at((static_cast<T *>(src))); },
|
||||
nullptr,
|
||||
ExtraInfo::template get<T>()};
|
||||
|
||||
/**
|
||||
* Used when #T can't be stored directly in the inline buffer and is stored in a #std::unique_ptr
|
||||
* instead. In this scenario, the #std::unique_ptr is stored in the inline buffer.
|
||||
*/
|
||||
template<typename T> using Ptr = std::unique_ptr<T>;
|
||||
template<typename ExtraInfo, typename T>
|
||||
inline constexpr AnyTypeInfo<ExtraInfo> info_for_unique_ptr = {
|
||||
[](void *dst, const void *src) {
|
||||
new (dst) Ptr<T>(new T(**static_cast<const Ptr<T> *>(src)));
|
||||
},
|
||||
[](void *dst, void *src) { new (dst) Ptr<T>(new T(std::move(**static_cast<Ptr<T> *>(src)))); },
|
||||
[](void *src) { std::destroy_at(static_cast<Ptr<T> *>(src)); },
|
||||
[](const void *src) -> const void * { return &**static_cast<const Ptr<T> *>(src); },
|
||||
ExtraInfo::template get<T>()};
|
||||
|
||||
/**
|
||||
* Dummy extra info that is used when no additional type information should be stored in the #Any.
|
||||
*/
|
||||
struct NoExtraInfo {
|
||||
template<typename T> static constexpr NoExtraInfo get()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
struct EmptyType {};
|
||||
|
||||
} // namespace blenlib_detail
|
||||
|
||||
template<
|
||||
/**
|
||||
* Either void or a struct that contains data members for additional type information.
|
||||
* The struct has to have a static `ExtraInfo get<T>()` method that initializes the struct
|
||||
* based on a type.
|
||||
*/
|
||||
typename ExtraInfo = void,
|
||||
/**
|
||||
* Size of the inline buffer. This allows types that are small enough to be stored directly
|
||||
* inside the #Any without an additional allocation.
|
||||
*/
|
||||
size_t InlineBufferCapacity = 8,
|
||||
/**
|
||||
* Required minimum alignment of the inline buffer. If this is smaller than the alignment
|
||||
* requirement of a used type, a separate allocation is necessary.
|
||||
*/
|
||||
size_t Alignment = 8,
|
||||
/**
|
||||
* Depending on the Alignment template parameter, there may be extra padding space that's
|
||||
* available to store some data. This type is stored after the buffer to use that space.
|
||||
*/
|
||||
typename ExtraData = blenlib_detail::EmptyType>
|
||||
class Any {
|
||||
private:
|
||||
/* Makes it possible to use void in the template parameters. */
|
||||
using RealExtraInfo =
|
||||
std::conditional_t<std::is_void_v<ExtraInfo>, blenlib_detail::NoExtraInfo, ExtraInfo>;
|
||||
using Info = blenlib_detail::AnyTypeInfo<RealExtraInfo>;
|
||||
static constexpr size_t RealInlineBufferCapacity = std::max(InlineBufferCapacity,
|
||||
sizeof(std::unique_ptr<int>));
|
||||
|
||||
/**
|
||||
* Inline buffer that either contains nothing, the stored value directly, or a #std::unique_ptr
|
||||
* to the value.
|
||||
*/
|
||||
AlignedBuffer<RealInlineBufferCapacity, Alignment> buffer_{};
|
||||
|
||||
public:
|
||||
/** Extra data potentially stored within padding required by the buffer. */
|
||||
BLI_NO_UNIQUE_ADDRESS ExtraData extra = {};
|
||||
|
||||
private:
|
||||
/**
|
||||
* Information about the type that is currently stored.
|
||||
* This is null when the #Any does not contain a value.
|
||||
*/
|
||||
const Info *info_ = nullptr;
|
||||
|
||||
public:
|
||||
/** Only copy constructible types can be stored in #Any. */
|
||||
template<typename T> static constexpr bool is_allowed_v = std::is_copy_constructible_v<T>;
|
||||
|
||||
/**
|
||||
* Checks if the type will be stored in the inline buffer or if it requires a separate
|
||||
* allocation.
|
||||
*/
|
||||
template<typename T>
|
||||
static constexpr bool is_inline_v = std::is_nothrow_move_constructible_v<T> &&
|
||||
sizeof(T) <= InlineBufferCapacity && alignof(T) <= Alignment;
|
||||
|
||||
/**
|
||||
* Checks if #T is the same type as this #Any, because in this case the behavior of e.g. the
|
||||
* assignment operator is different.
|
||||
*/
|
||||
template<typename T> static constexpr bool is_same_any_v = std::is_same_v<std::decay_t<T>, Any>;
|
||||
|
||||
private:
|
||||
template<typename T> const Info &get_info() const
|
||||
{
|
||||
using DecayT = std::decay_t<T>;
|
||||
static_assert(is_allowed_v<DecayT>);
|
||||
if constexpr (is_inline_v<DecayT>) {
|
||||
return blenlib_detail::template info_for_inline<RealExtraInfo, DecayT>;
|
||||
}
|
||||
else {
|
||||
return blenlib_detail::template info_for_unique_ptr<RealExtraInfo, DecayT>;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Any() = default;
|
||||
|
||||
Any(const Any &other) : extra(other.extra), info_(other.info_)
|
||||
{
|
||||
if (info_ != nullptr) {
|
||||
if (info_->copy_construct != nullptr) {
|
||||
info_->copy_construct(&buffer_, &other.buffer_);
|
||||
}
|
||||
else {
|
||||
std::copy_n(static_cast<const std::byte *>(other.buffer_.ptr()),
|
||||
RealInlineBufferCapacity,
|
||||
static_cast<std::byte *>(buffer_.ptr()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \note The #other #Any will not be empty afterwards if it was not before. Just its value is in
|
||||
* a moved-from state.
|
||||
*/
|
||||
Any(Any &&other) noexcept : extra(std::move(other.extra)), info_(other.info_)
|
||||
{
|
||||
if (info_ != nullptr) {
|
||||
if (info_->move_construct != nullptr) {
|
||||
info_->move_construct(&buffer_, &other.buffer_);
|
||||
}
|
||||
else {
|
||||
std::copy_n(static_cast<const std::byte *>(other.buffer_.ptr()),
|
||||
RealInlineBufferCapacity,
|
||||
static_cast<std::byte *>(buffer_.ptr()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new #Any that contains the given type #T from #args. The #std::in_place_type_t is
|
||||
* used to disambiguate this and the copy/move constructors.
|
||||
*/
|
||||
template<typename T, typename... Args>
|
||||
explicit Any(std::in_place_type_t<T> /*tag*/, Args &&...args)
|
||||
{
|
||||
this->emplace_on_empty<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new #Any that contains the given value.
|
||||
*/
|
||||
template<typename T>
|
||||
Any(T &&value)
|
||||
requires(!is_same_any_v<T>)
|
||||
: Any(std::in_place_type<T>, std::forward<T>(value))
|
||||
{
|
||||
}
|
||||
|
||||
~Any()
|
||||
{
|
||||
if (info_ != nullptr) {
|
||||
if (info_->destruct != nullptr) {
|
||||
info_->destruct(&buffer_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \note Only needed because the template below does not count as copy assignment operator.
|
||||
*/
|
||||
Any &operator=(const Any &other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->~Any();
|
||||
new (this) Any(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Assign any value to the #Any. */
|
||||
template<typename T> Any &operator=(T &&other)
|
||||
{
|
||||
if constexpr (is_same_any_v<T>) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
this->~Any();
|
||||
new (this) Any(std::forward<T>(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Destruct any existing value to make it empty. */
|
||||
void reset()
|
||||
{
|
||||
if (info_ != nullptr) {
|
||||
if (info_->destruct != nullptr) {
|
||||
info_->destruct(&buffer_);
|
||||
}
|
||||
}
|
||||
info_ = nullptr;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->has_value();
|
||||
}
|
||||
|
||||
bool has_value() const
|
||||
{
|
||||
return info_ != nullptr;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args> std::decay_t<T> &emplace(Args &&...args)
|
||||
{
|
||||
this->~Any();
|
||||
new (this) Any(std::in_place_type<T>, std::forward<Args>(args)...);
|
||||
return this->get<T>();
|
||||
}
|
||||
|
||||
template<typename T, typename... Args> std::decay_t<T> &emplace_on_empty(Args &&...args)
|
||||
{
|
||||
BLI_assert(!this->has_value());
|
||||
using DecayT = std::decay_t<T>;
|
||||
static_assert(is_allowed_v<DecayT>);
|
||||
info_ = &this->template get_info<DecayT>();
|
||||
if constexpr (is_inline_v<DecayT>) {
|
||||
/* Construct the value directly in the inline buffer. */
|
||||
DecayT *stored_value = new (&buffer_) DecayT(std::forward<Args>(args)...);
|
||||
return *stored_value;
|
||||
}
|
||||
else {
|
||||
/* Construct the value in a new allocation and store a #std::unique_ptr to it in the inline
|
||||
* buffer. */
|
||||
std::unique_ptr<DecayT> *stored_value = new (&buffer_)
|
||||
std::unique_ptr<DecayT>(new DecayT(std::forward<Args>(args)...));
|
||||
return **stored_value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like #emplace but does *not* actually construct the value. The caller is responsible for
|
||||
* calling the constructor before the value is used.
|
||||
*/
|
||||
template<typename T> void *allocate()
|
||||
{
|
||||
this->reset();
|
||||
return this->allocate_on_empty<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Like #emplace_on_empty but does *not* actually construct the value. The caller is responsible
|
||||
* for calling the constructor before the value is used.
|
||||
*/
|
||||
template<typename T> void *allocate_on_empty()
|
||||
{
|
||||
BLI_assert(!this->has_value());
|
||||
static_assert(is_allowed_v<T>);
|
||||
info_ = &this->template get_info<T>();
|
||||
if constexpr (is_inline_v<T>) {
|
||||
return buffer_.ptr();
|
||||
}
|
||||
else {
|
||||
/* Using raw allocation here. The caller is responsible for constructing the value. */
|
||||
T *value = static_cast<T *>(::operator new(sizeof(T)));
|
||||
new (&buffer_) std::unique_ptr<T>(value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true when the value that is currently stored is a #T. */
|
||||
template<typename T> bool is() const
|
||||
{
|
||||
return info_ == &this->template get_info<T>();
|
||||
}
|
||||
|
||||
/** Get a pointer to the stored value. */
|
||||
void *get()
|
||||
{
|
||||
BLI_assert(info_ != nullptr);
|
||||
if (info_->get != nullptr) {
|
||||
return const_cast<void *>(info_->get(&buffer_));
|
||||
}
|
||||
return &buffer_;
|
||||
}
|
||||
|
||||
/** Get a pointer to the stored value. */
|
||||
const void *get() const
|
||||
{
|
||||
BLI_assert(info_ != nullptr);
|
||||
if (info_->get != nullptr) {
|
||||
return info_->get(&buffer_);
|
||||
}
|
||||
return &buffer_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to the stored value. This invokes undefined behavior when #T does not have the
|
||||
* correct type.
|
||||
*/
|
||||
template<typename T> T &get()
|
||||
{
|
||||
/* Use const-cast to be able to reuse the const method above. */
|
||||
return const_cast<T &>(const_cast<const Any *>(this)->get<T>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to the stored value. This invokes undefined behavior when #T does not have the
|
||||
* correct type.
|
||||
*/
|
||||
template<typename T> const T &get() const
|
||||
{
|
||||
BLI_assert(this->is<T>());
|
||||
const void *buffer;
|
||||
/* Can avoid the `info_->get == nullptr` check because the result is known statically. */
|
||||
if constexpr (is_inline_v<T>) {
|
||||
buffer = &buffer_;
|
||||
}
|
||||
else {
|
||||
BLI_assert(info_->get != nullptr);
|
||||
buffer = info_->get(&buffer_);
|
||||
}
|
||||
return *static_cast<const T *>(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra information that has been stored for the contained type.
|
||||
*/
|
||||
const RealExtraInfo &extra_info() const
|
||||
{
|
||||
BLI_assert(info_ != nullptr);
|
||||
return info_->extra_info;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
178
blender-5.2.0/source/blender/blenlib/BLI_any_derived.hh
Normal file
178
blender-5.2.0/source/blender/blenlib/BLI_any_derived.hh
Normal file
@@ -0,0 +1,178 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_any.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename Base> struct AnyDerivedExtraInfo {
|
||||
|
||||
Base *(*get_impl)(const void *buffer);
|
||||
|
||||
template<typename StorageT> static constexpr AnyDerivedExtraInfo get()
|
||||
{
|
||||
/* These are the only allowed types in the #Any. */
|
||||
static_assert(std::is_base_of_v<Base, StorageT> ||
|
||||
is_same_any_v<StorageT, Base *, std::shared_ptr<Base>>);
|
||||
|
||||
/* Depending on how the implementation is stored in the #Any, a different #get_impl function
|
||||
* is required. */
|
||||
if constexpr (std::is_base_of_v<Base, StorageT>) {
|
||||
return {[](const void *buffer) {
|
||||
return static_cast<Base *>(const_cast<StorageT *>(static_cast<const StorageT *>(buffer)));
|
||||
}};
|
||||
}
|
||||
else if constexpr (std::is_same_v<StorageT, Base *>) {
|
||||
return {[](const void *buffer) {
|
||||
return *const_cast<StorageT *>(static_cast<const StorageT *>(buffer));
|
||||
}};
|
||||
}
|
||||
else if constexpr (std::is_same_v<StorageT, std::shared_ptr<Base>>) {
|
||||
return {[](const void *buffer) {
|
||||
return (const_cast<StorageT *>(static_cast<const StorageT *>(buffer)))->get();
|
||||
}};
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* This allows storing or passing around derived classes of a common base class. Typically, this
|
||||
* always requires allocating the value on the heap and passing it around e.g. as unique_ptr.
|
||||
* #AnyDerived has small buffer optimization. So if the type is small, it can be stored directly
|
||||
* without an additional allocation.
|
||||
*
|
||||
* If all derived types are known where the type is used, it can be more efficient to use
|
||||
* std::variant<Derived1, Derived2, ...> instead. #AnyDerived uses type erasure through the use of
|
||||
* #Any and therefore works even when not all used derived types are known.
|
||||
*
|
||||
* This is used extensively for virtual arrays. Each type of virtual array is implemented as
|
||||
* subclass of #VArrayImpl while #VArray actually stores a specific implementation.
|
||||
*
|
||||
* Note: If the value is not stored inline, it's currently stored as a shared_ptr which is shared
|
||||
* when the AnyDerived is copied. This behavior is fine and efficient for all current uses but it
|
||||
* may need to be generalized if #AnyDerived is supposed to be used in more places.
|
||||
*/
|
||||
template<typename Base, int64_t InlineBufferCapacity = 24> struct AnyDerived {
|
||||
private:
|
||||
using Storage = Any<detail::AnyDerivedExtraInfo<Base>, InlineBufferCapacity, alignof(Base)>;
|
||||
|
||||
/** Pointer to the currently contained implementation. This may be null. */
|
||||
Base *impl_ = nullptr;
|
||||
|
||||
/**
|
||||
* Does the memory management for the implementation. It contains one of the following:
|
||||
* - Inline subclass of T.
|
||||
* - Non-owning pointer to a T.
|
||||
* - Shared pointer to a T.
|
||||
*/
|
||||
Storage storage_;
|
||||
|
||||
public:
|
||||
AnyDerived() = default;
|
||||
|
||||
AnyDerived(const AnyDerived &other) : storage_(other.storage_)
|
||||
{
|
||||
impl_ = this->impl_from_storage();
|
||||
}
|
||||
|
||||
AnyDerived(AnyDerived &&other) noexcept : storage_(std::move(other.storage_))
|
||||
{
|
||||
impl_ = this->impl_from_storage();
|
||||
other.storage_.reset();
|
||||
other.impl_ = nullptr;
|
||||
}
|
||||
|
||||
explicit AnyDerived(Base *impl) : impl_(impl)
|
||||
{
|
||||
storage_ = impl_;
|
||||
}
|
||||
|
||||
explicit AnyDerived(std::shared_ptr<Base> impl) : impl_(impl.get())
|
||||
{
|
||||
if (impl_) {
|
||||
storage_ = std::move(impl);
|
||||
}
|
||||
}
|
||||
|
||||
AnyDerived &operator=(const AnyDerived &other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
storage_ = other.storage_;
|
||||
impl_ = this->impl_from_storage();
|
||||
return *this;
|
||||
}
|
||||
|
||||
AnyDerived &operator=(AnyDerived &&other) noexcept
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
storage_ = std::move(other.storage_);
|
||||
impl_ = this->impl_from_storage();
|
||||
other.storage_.reset();
|
||||
other.impl_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ImplT, typename... Args>
|
||||
requires std::is_base_of_v<Base, ImplT>
|
||||
void emplace(Args &&...args)
|
||||
{
|
||||
if constexpr (std::is_copy_constructible_v<ImplT> && Storage::template is_inline_v<ImplT>) {
|
||||
/* Only inline the implementation when it is copyable and when it fits into the inline
|
||||
* buffer of the storage. */
|
||||
impl_ = &storage_.template emplace<ImplT>(std::forward<Args>(args)...);
|
||||
}
|
||||
else {
|
||||
/* If it can't be inlined, create a new #std::shared_ptr instead and store that in the
|
||||
* storage. */
|
||||
std::shared_ptr<Base> ptr = std::make_shared<ImplT>(std::forward<Args>(args)...);
|
||||
impl_ = &*ptr;
|
||||
storage_ = std::move(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return impl_ != nullptr;
|
||||
}
|
||||
|
||||
Base &operator*() const
|
||||
{
|
||||
return *impl_;
|
||||
}
|
||||
|
||||
Base *operator->() const
|
||||
{
|
||||
return impl_;
|
||||
}
|
||||
|
||||
Base *get() const
|
||||
{
|
||||
return impl_;
|
||||
}
|
||||
|
||||
protected:
|
||||
Base *impl_from_storage() const
|
||||
{
|
||||
if (!storage_.has_value()) {
|
||||
return nullptr;
|
||||
}
|
||||
return storage_.extra_info().get_impl(storage_.get());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
72
blender-5.2.0/source/blender/blenlib/BLI_args.h
Normal file
72
blender-5.2.0/source/blender/blenlib/BLI_args.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief A general argument parsing module.
|
||||
*/
|
||||
|
||||
#include <stdarg.h> /* For `va_list`. */
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bArgs;
|
||||
|
||||
/**
|
||||
* Returns the number of extra arguments consumed by the function.
|
||||
* - 0 is normal value,
|
||||
* - -1 stops parsing arguments, other negative indicates skip
|
||||
*/
|
||||
using BA_ArgCallback = int (*)(int argc, const char **argv, void *data);
|
||||
|
||||
struct bArgs *BLI_args_create(int argc, const char **argv);
|
||||
void BLI_args_destroy(struct bArgs *ba);
|
||||
|
||||
using bArgPrintFn = void (*)(void *user_data, const char *format, va_list args);
|
||||
void BLI_args_printf(struct bArgs *ba, const char *format, ...);
|
||||
void BLI_args_print_fn_set(struct bArgs *ba,
|
||||
ATTR_PRINTF_FORMAT(2, 0) bArgPrintFn print_fn,
|
||||
void *user_data);
|
||||
|
||||
/** The pass to use for #BLI_args_add. */
|
||||
void BLI_args_pass_set(struct bArgs *ba, int current_pass);
|
||||
|
||||
/**
|
||||
* Pass starts at 1, -1 means valid all the time
|
||||
* short_arg or long_arg can be null to specify no short or long versions
|
||||
*/
|
||||
void BLI_args_add(struct bArgs *ba,
|
||||
const char *short_arg,
|
||||
const char *long_arg,
|
||||
const char *doc,
|
||||
BA_ArgCallback cb,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Short_case and long_case specify if those arguments are case specific
|
||||
*/
|
||||
void BLI_args_add_case(struct bArgs *ba,
|
||||
const char *short_arg,
|
||||
int short_case,
|
||||
const char *long_arg,
|
||||
int long_case,
|
||||
const char *doc,
|
||||
BA_ArgCallback cb,
|
||||
void *data);
|
||||
|
||||
void BLI_args_parse(struct bArgs *ba, int pass, BA_ArgCallback default_cb, void *default_data);
|
||||
|
||||
void BLI_args_print_arg_doc(struct bArgs *ba, const char *arg);
|
||||
void BLI_args_print_other_doc(struct bArgs *ba);
|
||||
|
||||
bool BLI_args_has_other_doc(const struct bArgs *ba);
|
||||
|
||||
void BLI_args_print(const struct bArgs *ba);
|
||||
|
||||
} // namespace blender
|
||||
487
blender-5.2.0/source/blender/blenlib/BLI_array.hh
Normal file
487
blender-5.2.0/source/blender/blenlib/BLI_array.hh
Normal file
@@ -0,0 +1,487 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A `Array<T>` is a container for a fixed size array the size of which is NOT known at
|
||||
* compile time.
|
||||
*
|
||||
* If the size is known at compile time, `std::array<T, N>` should be used instead.
|
||||
*
|
||||
* Array should usually be used instead of Vector whenever the number of elements
|
||||
* is known at construction time. Note however, that Array will default construct all
|
||||
* elements when initialized with the size-constructor. For trivial types, this does nothing. In
|
||||
* all other cases, this adds overhead.
|
||||
*
|
||||
* A main benefit of using Array over Vector is that it expresses the intent of the developer
|
||||
* better. It indicates that the size of the data structure is not expected to change. Furthermore,
|
||||
* you can be more certain that an array does not over-allocate.
|
||||
*
|
||||
* Array supports small object optimization to improve performance when the size turns out
|
||||
* to be small at run-time.
|
||||
*/
|
||||
|
||||
#include "BLI_allocator.hh"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_memory_utils.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
template<
|
||||
/**
|
||||
* The type of the values stored in the array.
|
||||
*/
|
||||
typename T,
|
||||
/**
|
||||
* The number of values that can be stored in the array, without doing a heap allocation.
|
||||
*/
|
||||
int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T)),
|
||||
/**
|
||||
* The allocator used by this array. Should rarely be changed, except when you don't want that
|
||||
* MEM_* functions are used internally.
|
||||
*/
|
||||
typename Allocator = GuardedAllocator>
|
||||
class Array {
|
||||
public:
|
||||
using value_type = T;
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using reference = T &;
|
||||
using const_reference = const T &;
|
||||
using iterator = T *;
|
||||
using const_iterator = const T *;
|
||||
using size_type = int64_t;
|
||||
|
||||
private:
|
||||
/** The beginning of the array. It might point into the inline buffer. */
|
||||
T *data_;
|
||||
|
||||
/** Number of elements in the array. */
|
||||
int64_t size_;
|
||||
|
||||
/** Used for allocations when the inline buffer is too small. */
|
||||
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
|
||||
|
||||
/** A placeholder buffer that will remain uninitialized until it is used. */
|
||||
BLI_NO_UNIQUE_ADDRESS TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* By default an empty array is created.
|
||||
*/
|
||||
Array(Allocator allocator = {}) noexcept : allocator_(allocator)
|
||||
{
|
||||
data_ = inline_buffer_;
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
Array(NoExceptConstructor, Allocator allocator = {}) noexcept : Array(allocator) {}
|
||||
|
||||
/**
|
||||
* Create a new array that contains copies of all values.
|
||||
*/
|
||||
template<typename U>
|
||||
Array(Span<U> values, Allocator allocator = {})
|
||||
requires(std::is_convertible_v<U, T>)
|
||||
: Array(NoExceptConstructor(), allocator)
|
||||
{
|
||||
const int64_t size = values.size();
|
||||
data_ = this->get_buffer_for_size(size);
|
||||
uninitialized_convert_n<U, T>(values.data(), size, data_);
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new array that contains copies of all values.
|
||||
*/
|
||||
template<typename U>
|
||||
Array(const std::initializer_list<U> &values, Allocator allocator = {})
|
||||
requires(std::is_convertible_v<U, T>)
|
||||
: Array(Span<U>(values), allocator)
|
||||
{
|
||||
}
|
||||
|
||||
Array(const std::initializer_list<T> &values, Allocator allocator = {})
|
||||
: Array(Span<T>(values), allocator)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new array with the given size. All values will be default constructed. For trivial
|
||||
* types like int, default construction does nothing.
|
||||
*
|
||||
* We might want another version of this in the future, that does not do default construction
|
||||
* even for non-trivial types. This should not be the default though, because one can easily mess
|
||||
* up when dealing with uninitialized memory.
|
||||
*/
|
||||
explicit Array(int64_t size, Allocator allocator = {}) : Array(NoExceptConstructor(), allocator)
|
||||
{
|
||||
BLI_assert(size >= 0);
|
||||
data_ = this->get_buffer_for_size(size);
|
||||
default_construct_n(data_, size);
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new array with the given size. All values will be initialized by copying the given
|
||||
* default.
|
||||
*/
|
||||
Array(int64_t size, const T &value, Allocator allocator = {})
|
||||
: Array(NoExceptConstructor(), allocator)
|
||||
{
|
||||
BLI_assert(size >= 0);
|
||||
if (std::is_trivially_copyable_v<T> && value_is_zero(value)) {
|
||||
data_ = this->get_buffer_for_size(size, true);
|
||||
}
|
||||
else {
|
||||
data_ = this->get_buffer_for_size(size);
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
#endif
|
||||
uninitialized_fill_n(data_, size, value);
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new array with uninitialized elements. The caller is responsible for constructing the
|
||||
* elements. Moving, copying or destructing an Array with uninitialized elements invokes
|
||||
* undefined behavior.
|
||||
*
|
||||
* This should be used very rarely. Note, that the normal size-constructor also does not
|
||||
* initialize the elements when T is trivially constructible. Therefore, it only makes sense to
|
||||
* use this with non trivially constructible types.
|
||||
*
|
||||
* Usage:
|
||||
* Array<std::string> my_strings(10, NoInitialization());
|
||||
*/
|
||||
Array(int64_t size, NoInitialization, Allocator allocator = {})
|
||||
: Array(NoExceptConstructor(), allocator)
|
||||
{
|
||||
BLI_assert(size >= 0);
|
||||
data_ = this->get_buffer_for_size(size);
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
Array(const Array &other) : Array(other.as_span(), other.allocator_) {}
|
||||
|
||||
Array(Array &&other) noexcept(std::is_nothrow_move_constructible_v<T>)
|
||||
: Array(NoExceptConstructor(), other.allocator_)
|
||||
{
|
||||
if (other.data_ == other.inline_buffer_) {
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
#endif
|
||||
uninitialized_relocate_n(other.data_, other.size_, data_);
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
data_ = other.data_;
|
||||
}
|
||||
size_ = other.size_;
|
||||
|
||||
other.data_ = other.inline_buffer_;
|
||||
other.size_ = 0;
|
||||
}
|
||||
|
||||
~Array()
|
||||
{
|
||||
destruct_n(data_, size_);
|
||||
this->deallocate_if_not_inline(data_);
|
||||
}
|
||||
|
||||
Array &operator=(const Array &other)
|
||||
{
|
||||
return copy_assign_container(*this, other);
|
||||
}
|
||||
|
||||
Array &operator=(Array &&other) noexcept(std::is_nothrow_move_constructible_v<T>)
|
||||
{
|
||||
return move_assign_container(*this, std::move(other));
|
||||
}
|
||||
|
||||
T &operator[](int64_t index)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < size_);
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
const T &operator[](int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < size_);
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
operator Span<T>() const
|
||||
{
|
||||
return Span<T>(data_, size_);
|
||||
}
|
||||
|
||||
operator MutableSpan<T>()
|
||||
{
|
||||
return MutableSpan<T>(data_, size_);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
operator Span<U>() const
|
||||
requires(is_span_convertible_pointer_v<T, U>)
|
||||
{
|
||||
return Span<U>(data_, size_);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
operator MutableSpan<U>()
|
||||
requires(is_span_convertible_pointer_v<T, U>)
|
||||
{
|
||||
return MutableSpan<U>(data_, size_);
|
||||
}
|
||||
|
||||
Span<T> as_span() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
MutableSpan<T> as_mutable_span()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of elements in the array.
|
||||
*/
|
||||
int64_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the number of elements in the array is zero.
|
||||
*/
|
||||
bool is_empty() const
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the given value to every element in the array.
|
||||
*/
|
||||
void fill(const T &value) const
|
||||
{
|
||||
initialized_fill_n(data_, size_, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a reference to the first element in the array.
|
||||
* This invokes undefined behavior when the array is empty.
|
||||
*/
|
||||
const T &first() const
|
||||
{
|
||||
BLI_assert(size_ > 0);
|
||||
return *data_;
|
||||
}
|
||||
T &first()
|
||||
{
|
||||
BLI_assert(size_ > 0);
|
||||
return *data_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a reference to the nth last element.
|
||||
* This invokes undefined behavior when the array is too short.
|
||||
*/
|
||||
const T &last(const int64_t n = 0) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
BLI_assert(n < size_);
|
||||
return *(data_ + size_ - 1 - n);
|
||||
}
|
||||
T &last(const int64_t n = 0)
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
BLI_assert(n < size_);
|
||||
return *(data_ + size_ - 1 - n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer to the beginning of the array.
|
||||
*/
|
||||
const T *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
T *data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const T *begin() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
const T *end() const
|
||||
{
|
||||
return data_ + size_;
|
||||
}
|
||||
|
||||
T *begin()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
T *end()
|
||||
{
|
||||
return data_ + size_;
|
||||
}
|
||||
|
||||
std::reverse_iterator<T *> rbegin()
|
||||
{
|
||||
return std::reverse_iterator<T *>(this->end());
|
||||
}
|
||||
std::reverse_iterator<T *> rend()
|
||||
{
|
||||
return std::reverse_iterator<T *>(this->begin());
|
||||
}
|
||||
|
||||
std::reverse_iterator<const T *> rbegin() const
|
||||
{
|
||||
return std::reverse_iterator<T *>(this->end());
|
||||
}
|
||||
std::reverse_iterator<const T *> rend() const
|
||||
{
|
||||
return std::reverse_iterator<T *>(this->begin());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an index range containing all valid indices for this array.
|
||||
*/
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(size_);
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
return this->as_span().hash();
|
||||
}
|
||||
|
||||
static uint64_t hash_as(const Span<T> values)
|
||||
{
|
||||
return values.hash();
|
||||
}
|
||||
|
||||
friend bool operator==(const Array &a, const Array &b)
|
||||
{
|
||||
return a.as_span() == b.as_span();
|
||||
}
|
||||
|
||||
friend bool operator!=(const Array &a, const Array &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size to zero. This should only be used when you have manually destructed all elements
|
||||
* in the array beforehand. Use with care.
|
||||
*/
|
||||
void clear_without_destruct()
|
||||
{
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the allocator used by this array.
|
||||
*/
|
||||
Allocator &allocator()
|
||||
{
|
||||
return allocator_;
|
||||
}
|
||||
const Allocator &allocator() const
|
||||
{
|
||||
return allocator_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the InlineBufferCapacity template argument. This is the number of elements
|
||||
* that can be stored without doing an allocation.
|
||||
*/
|
||||
static int64_t inline_buffer_capacity()
|
||||
{
|
||||
return InlineBufferCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destruct values and create a new array of the given size. The values in the new array are
|
||||
* default constructed.
|
||||
*/
|
||||
void reinitialize(const int64_t new_size)
|
||||
{
|
||||
BLI_assert(new_size >= 0);
|
||||
int64_t old_size = size_;
|
||||
|
||||
destruct_n(data_, size_);
|
||||
size_ = 0;
|
||||
|
||||
if (new_size <= old_size) {
|
||||
default_construct_n(data_, new_size);
|
||||
}
|
||||
else {
|
||||
T *new_data = this->get_buffer_for_size(new_size, false);
|
||||
try {
|
||||
default_construct_n(new_data, new_size);
|
||||
}
|
||||
catch (...) {
|
||||
this->deallocate_if_not_inline(new_data);
|
||||
throw;
|
||||
}
|
||||
this->deallocate_if_not_inline(data_);
|
||||
data_ = new_data;
|
||||
}
|
||||
|
||||
size_ = new_size;
|
||||
}
|
||||
|
||||
private:
|
||||
T *get_buffer_for_size(int64_t size, const bool zero = false)
|
||||
{
|
||||
if (size <= InlineBufferCapacity) {
|
||||
if (zero) {
|
||||
if constexpr (InlineBufferCapacity > 0) {
|
||||
memset(static_cast<void *>(inline_buffer_), 0, size * sizeof(T));
|
||||
}
|
||||
}
|
||||
return inline_buffer_;
|
||||
}
|
||||
return this->allocate(size, zero);
|
||||
}
|
||||
|
||||
T *allocate(int64_t size, const bool zero)
|
||||
{
|
||||
if (zero) {
|
||||
return static_cast<T *>(allocator_.allocate_zero(size_t(size) * sizeof(T), alignof(T), AT));
|
||||
}
|
||||
return static_cast<T *>(allocator_.allocate(size_t(size) * sizeof(T), alignof(T), AT));
|
||||
}
|
||||
|
||||
void deallocate_if_not_inline(T *ptr)
|
||||
{
|
||||
if (ptr != inline_buffer_) {
|
||||
allocator_.deallocate(ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
89
blender-5.2.0/source/blender/blenlib/BLI_array_state.hh
Normal file
89
blender-5.2.0/source/blender/blenlib/BLI_array_state.hh
Normal file
@@ -0,0 +1,89 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_implicit_sharing_ptr.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Remembers the values in an array and allows checking if another array in the future has exactly
|
||||
* the same values. This is useful for checking if the topology of a mesh has changed.
|
||||
*
|
||||
* If possible, this class makes use of implicit-sharing to avoid creating unnecessary copies of
|
||||
* the data. This also allows detecting that the array is not changed in constant time in common
|
||||
* cases.
|
||||
*/
|
||||
template<typename T> class ArrayState {
|
||||
private:
|
||||
/**
|
||||
* The actual values in the remembered array. This may point to data owned by #sharing_info_ or
|
||||
* #cached_values_.
|
||||
*/
|
||||
Span<T> values_;
|
||||
/** (Shared) ownership of the array in case it supports implicit-sharing. */
|
||||
ImplicitSharingPtr<> sharing_info_;
|
||||
/** Fallback-copy in the case when the array could not be shared. */
|
||||
std::optional<Array<T, 0>> cached_values_;
|
||||
|
||||
public:
|
||||
ArrayState() = default;
|
||||
|
||||
ArrayState(const VArray<T> &values, const ImplicitSharingInfo *sharing_info)
|
||||
{
|
||||
if (values.is_span() && sharing_info) {
|
||||
/* Don't create a copy of the array and just take shared ownership. */
|
||||
values_ = values.get_internal_span();
|
||||
sharing_info->add_user();
|
||||
sharing_info_ = ImplicitSharingPtr(sharing_info);
|
||||
return;
|
||||
}
|
||||
/* Create a copy of the array because sharing is not possible. */
|
||||
cached_values_.emplace(values.size(), NoInitialization{});
|
||||
values.materialize_to_uninitialized(*cached_values_);
|
||||
values_ = *cached_values_;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the remembered array does not contain any values.
|
||||
*/
|
||||
bool is_empty() const
|
||||
{
|
||||
return values_.is_empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the remembered array contains the same values as the given array.
|
||||
* This is O(1) in the case when the array was shared and has not been modified.
|
||||
* If determining equality in constant time is not possible, the method falls back to comparing
|
||||
* the values individually which will take O(n) time.
|
||||
*/
|
||||
bool same_as(const VArray<T> &other_values, const ImplicitSharingInfo *other_sharing_info) const
|
||||
{
|
||||
if (sharing_info_ && other_sharing_info) {
|
||||
if (sharing_info_ == other_sharing_info) {
|
||||
/* The data is still shared. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (values_.size() != other_values.size()) {
|
||||
/* The arrays can't be the same if their sizes differ. */
|
||||
return false;
|
||||
}
|
||||
/* Need to actually compare all elements. */
|
||||
VArraySpan<T> other_values_span(other_values);
|
||||
return values_ == other_values_span;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
131
blender-5.2.0/source/blender/blenlib/BLI_array_store.h
Normal file
131
blender-5.2.0/source/blender/blenlib/BLI_array_store.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief Efficient in-memory storage of multiple similar arrays.
|
||||
*/
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BArrayState;
|
||||
struct BArrayStore;
|
||||
|
||||
/**
|
||||
* Create a new array store, which can store any number of arrays
|
||||
* as long as their stride matches.
|
||||
*
|
||||
* \param stride: `sizeof()` each element,
|
||||
*
|
||||
* \note while a stride of `1` will always work,
|
||||
* its less efficient since duplicate chunks of memory will be searched
|
||||
* at positions unaligned with the array data.
|
||||
*
|
||||
* \param chunk_count: Number of elements to split each chunk into.
|
||||
* - A small value increases the ability to de-duplicate chunks,
|
||||
* but adds overhead by increasing the number of chunks to look up when searching for duplicates,
|
||||
* as well as some overhead constructing the original array again, with more calls to `memcpy`.
|
||||
* - Larger values reduce the *book keeping* overhead,
|
||||
* but increase the chance a small,
|
||||
* isolated change will cause a larger amount of data to be duplicated.
|
||||
*
|
||||
* \return A new array store, to be freed with #BLI_array_store_destroy.
|
||||
*/
|
||||
BArrayStore *BLI_array_store_create(unsigned int stride, unsigned int chunk_count);
|
||||
/**
|
||||
* Free the #BArrayStore, including all states and chunks.
|
||||
*/
|
||||
void BLI_array_store_destroy(BArrayStore *bs);
|
||||
/**
|
||||
* Clear all contents, allowing reuse of \a bs.
|
||||
*/
|
||||
void BLI_array_store_clear(BArrayStore *bs);
|
||||
|
||||
/**
|
||||
* Find the memory used by all states (expanded & real).
|
||||
*
|
||||
* \return the total amount of memory that would be used by getting the arrays for all states.
|
||||
*/
|
||||
size_t BLI_array_store_calc_size_expanded_get(const BArrayStore *bs);
|
||||
/**
|
||||
* \return the amount of memory used by all #BChunk.data
|
||||
* (duplicate chunks are only counted once).
|
||||
*/
|
||||
size_t BLI_array_store_calc_size_compacted_get(const BArrayStore *bs);
|
||||
|
||||
/**
|
||||
* \param data: Data used to create
|
||||
* \param state_reference: The state to use as a reference when adding the new state,
|
||||
* typically this is the previous state,
|
||||
* however it can be any previously created state from this \a bs.
|
||||
*
|
||||
* \return The new state,
|
||||
* which is used by the caller as a handle to get back the contents of \a data.
|
||||
* This may be removed using #BLI_array_store_state_remove,
|
||||
* otherwise it will be removed with #BLI_array_store_destroy.
|
||||
*/
|
||||
BArrayState *BLI_array_store_state_add(BArrayStore *bs,
|
||||
const void *data,
|
||||
size_t data_len,
|
||||
const BArrayState *state_reference);
|
||||
/**
|
||||
* Remove a state and free any unused #BChunk data.
|
||||
*
|
||||
* The states can be freed in any order.
|
||||
*/
|
||||
void BLI_array_store_state_remove(BArrayStore *bs, BArrayState *state);
|
||||
|
||||
/**
|
||||
* \return the expanded size of the array,
|
||||
* use this to know how much memory to allocate #BLI_array_store_state_data_get's argument.
|
||||
*/
|
||||
size_t BLI_array_store_state_size_get(const BArrayState *state);
|
||||
/**
|
||||
* Fill in existing allocated memory with the contents of \a state.
|
||||
*/
|
||||
void BLI_array_store_state_data_get(const BArrayState *state, void *data);
|
||||
/**
|
||||
* Allocate an array for \a state and return it.
|
||||
*/
|
||||
void *BLI_array_store_state_data_get_alloc(const BArrayState *state, size_t *r_data_len);
|
||||
|
||||
/**
|
||||
* \note Only for tests.
|
||||
*/
|
||||
bool BLI_array_store_is_valid(BArrayStore *bs);
|
||||
|
||||
/* `array_store_rle.cc` */
|
||||
|
||||
/**
|
||||
* Return a run-length encoded copy of `data_dec`.
|
||||
*
|
||||
* \param data_dec: The data to encode.
|
||||
* \param data_dec_len: The size of the data to encode.
|
||||
* \param data_enc_extra_size: Allocate extra memory at the beginning of the array.
|
||||
* - This doesn't impact the value of `r_data_enc_len`.
|
||||
* - This must be skipped when decoding.
|
||||
* \param r_data_enc_len: The size of the resulting RLE encoded data.
|
||||
*/
|
||||
uint8_t *BLI_array_store_rle_encode(const uint8_t *data_dec,
|
||||
size_t data_dec_len,
|
||||
size_t data_enc_extra_size,
|
||||
size_t *r_data_enc_len);
|
||||
/**
|
||||
* Decode a run-length encoded array, writing the result into `data_dec_v`.
|
||||
*
|
||||
* \param data_enc: The data to encode (returned by #BLI_array_store_rle_encode).
|
||||
* \param data_enc_len: The size of `data_enc`.
|
||||
* \param data_dec_v: The destination for the decoded data to be written to.
|
||||
* \param data_dec_len: The size of the destination (as passed to #BLI_array_store_rle_encode).
|
||||
*/
|
||||
void BLI_array_store_rle_decode(const uint8_t *data_enc,
|
||||
const size_t data_enc_len,
|
||||
void *data_dec_v,
|
||||
const size_t data_dec_len);
|
||||
|
||||
} // namespace blender
|
||||
34
blender-5.2.0/source/blender/blenlib/BLI_array_store_utils.h
Normal file
34
blender-5.2.0/source/blender/blenlib/BLI_array_store_utils.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BArrayStore;
|
||||
|
||||
struct BArrayStore_AtSize {
|
||||
struct BArrayStore **stride_table;
|
||||
int stride_table_len;
|
||||
};
|
||||
|
||||
struct BArrayStore *BLI_array_store_at_size_ensure(struct BArrayStore_AtSize *bs_stride,
|
||||
int stride,
|
||||
int chunk_size);
|
||||
|
||||
struct BArrayStore *BLI_array_store_at_size_get(struct BArrayStore_AtSize *bs_stride, int stride);
|
||||
|
||||
void BLI_array_store_at_size_clear(struct BArrayStore_AtSize *bs_stride);
|
||||
|
||||
void BLI_array_store_at_size_calc_memory_usage(const struct BArrayStore_AtSize *bs_stride,
|
||||
size_t *r_size_expanded,
|
||||
size_t *r_size_compacted);
|
||||
|
||||
} // namespace blender
|
||||
148
blender-5.2.0/source/blender/blenlib/BLI_array_utils.h
Normal file
148
blender-5.2.0/source/blender/blenlib/BLI_array_utils.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief Generic array manipulation API.
|
||||
*/
|
||||
|
||||
#include "BLI_compiler_typecheck.h"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* In-place array reverse.
|
||||
*
|
||||
* Access via #BLI_array_reverse
|
||||
*/
|
||||
void _bli_array_reverse(void *arr_v, uint arr_len, size_t arr_stride);
|
||||
#define BLI_array_reverse(arr, arr_len) _bli_array_reverse(arr, arr_len, sizeof(*(arr)))
|
||||
|
||||
/**
|
||||
* In-place array wrap.
|
||||
* (rotate the array one step forward or backwards).
|
||||
*
|
||||
* Access via #BLI_array_wrap
|
||||
*/
|
||||
void _bli_array_wrap(void *arr_v, uint arr_len, size_t arr_stride, int dir);
|
||||
#define BLI_array_wrap(arr, arr_len, dir) _bli_array_wrap(arr, arr_len, sizeof(*(arr)), dir)
|
||||
|
||||
/**
|
||||
* In-place array permute.
|
||||
* (re-arrange elements based on an array of indices).
|
||||
*
|
||||
* Access via #BLI_array_wrap
|
||||
*/
|
||||
void _bli_array_permute(
|
||||
void *arr, uint arr_len, size_t arr_stride, const uint *order, void *arr_temp);
|
||||
#define BLI_array_permute(arr, arr_len, order) \
|
||||
_bli_array_permute(arr, arr_len, sizeof(*(arr)), order, NULL)
|
||||
#define BLI_array_permute_ex(arr, arr_len, order, arr_temp) \
|
||||
_bli_array_permute(arr, arr_len, sizeof(*(arr)), order, arr_temp)
|
||||
|
||||
/**
|
||||
* In-place array de-duplication of an ordered array.
|
||||
*
|
||||
* \return The new length of the array.
|
||||
*
|
||||
* Access via #BLI_array_deduplicate_ordered
|
||||
*/
|
||||
uint _bli_array_deduplicate_ordered(void *arr, uint arr_len, size_t arr_stride);
|
||||
#define BLI_array_deduplicate_ordered(arr, arr_len) \
|
||||
_bli_array_deduplicate_ordered(arr, arr_len, sizeof(*(arr)))
|
||||
|
||||
/**
|
||||
* Find the first index of an item in an array.
|
||||
*
|
||||
* Access via #BLI_array_findindex
|
||||
*
|
||||
* \note Not efficient, use for error checks/asserts.
|
||||
*/
|
||||
int _bli_array_findindex(const void *arr, uint arr_len, size_t arr_stride, const void *p);
|
||||
#define BLI_array_findindex(arr, arr_len, p) _bli_array_findindex(arr, arr_len, sizeof(*(arr)), p)
|
||||
|
||||
/**
|
||||
* A version of #BLI_array_findindex that searches from the end of the list.
|
||||
*/
|
||||
int _bli_array_rfindindex(const void *arr, uint arr_len, size_t arr_stride, const void *p);
|
||||
#define BLI_array_rfindindex(arr, arr_len, p) \
|
||||
_bli_array_rfindindex(arr, arr_len, sizeof(*(arr)), p)
|
||||
|
||||
void _bli_array_binary_and(
|
||||
void *arr, const void *arr_a, const void *arr_b, uint arr_len, size_t arr_stride);
|
||||
#define BLI_array_binary_and(arr, arr_a, arr_b, arr_len) \
|
||||
(CHECK_TYPE_PAIR_INLINE(*(arr), *(arr_a)), \
|
||||
CHECK_TYPE_PAIR_INLINE(*(arr), *(arr_b)), \
|
||||
_bli_array_binary_and(arr, arr_a, arr_b, arr_len, sizeof(*(arr))))
|
||||
|
||||
void _bli_array_binary_or(
|
||||
void *arr, const void *arr_a, const void *arr_b, uint arr_len, size_t arr_stride);
|
||||
#define BLI_array_binary_or(arr, arr_a, arr_b, arr_len) \
|
||||
(CHECK_TYPE_PAIR_INLINE(*(arr), *(arr_a)), \
|
||||
CHECK_TYPE_PAIR_INLINE(*(arr), *(arr_b)), \
|
||||
_bli_array_binary_or(arr, arr_a, arr_b, arr_len, sizeof(*(arr))))
|
||||
|
||||
/**
|
||||
* Utility function to iterate over contiguous items in an array.
|
||||
*
|
||||
* \param use_wrap: Detect contiguous ranges across the first/last points.
|
||||
* In this case the second index of \a span_step may be lower than the first,
|
||||
* which indicates the values are wrapped.
|
||||
* \param use_delimit_bounds: When false,
|
||||
* ranges that defined by the start/end indices are excluded.
|
||||
* This option has no effect when \a use_wrap is enabled.
|
||||
* \param test_fn: Function to test if the item should be included in the range.
|
||||
* \param user_data: User data for \a test_fn.
|
||||
* \param span_step: Indices to iterate over,
|
||||
* initialize both values to the array length to initialize iteration.
|
||||
* \param r_span_len: The length of the span, useful when \a use_wrap is enabled,
|
||||
* where calculating the length isn't a simple subtraction.
|
||||
*/
|
||||
bool _bli_array_iter_span(const void *arr,
|
||||
uint arr_len,
|
||||
size_t arr_stride,
|
||||
bool use_wrap,
|
||||
bool use_delimit_bounds,
|
||||
bool (*test_fn)(const void *arr_item, void *user_data),
|
||||
void *user_data,
|
||||
uint span_step[2],
|
||||
uint *r_span_len);
|
||||
#define BLI_array_iter_span( \
|
||||
arr, arr_len, use_wrap, use_delimit_bounds, test_fn, user_data, span_step, r_span_len) \
|
||||
_bli_array_iter_span(arr, \
|
||||
arr_len, \
|
||||
sizeof(*(arr)), \
|
||||
use_wrap, \
|
||||
use_delimit_bounds, \
|
||||
test_fn, \
|
||||
user_data, \
|
||||
span_step, \
|
||||
r_span_len)
|
||||
|
||||
/**
|
||||
* Simple utility to check memory is zeroed.
|
||||
*/
|
||||
bool _bli_array_is_zeroed(const void *arr_v, uint arr_len, size_t arr_stride);
|
||||
#define BLI_array_is_zeroed(arr, arr_len) _bli_array_is_zeroed(arr, arr_len, sizeof(*(arr)))
|
||||
|
||||
/**
|
||||
* Smart function to sample a rectangle spiraling outside.
|
||||
* Nice for selection ID.
|
||||
*
|
||||
* \param arr_shape: dimensions [w, h].
|
||||
* \param center: coordinates [x, y] indicating where to start traversing.
|
||||
*/
|
||||
bool _bli_array_iter_spiral_square(const void *arr_v,
|
||||
const int arr_shape[2],
|
||||
size_t elem_size,
|
||||
const int center[2],
|
||||
bool (*test_fn)(const void *arr_item, void *user_data),
|
||||
void *user_data);
|
||||
#define BLI_array_iter_spiral_square(arr, arr_shape, center, test_fn, user_data) \
|
||||
_bli_array_iter_spiral_square(arr, arr_shape, sizeof(*(arr)), center, test_fn, user_data)
|
||||
|
||||
} // namespace blender
|
||||
509
blender-5.2.0/source/blender/blenlib/BLI_array_utils.hh
Normal file
509
blender-5.2.0/source/blender/blenlib/BLI_array_utils.hh
Normal file
@@ -0,0 +1,509 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "BLI_execution_mode.hh"
|
||||
#include "BLI_generic_span.hh"
|
||||
#include "BLI_generic_virtual_array.hh"
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_offset_indices.hh"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
namespace blender::array_utils {
|
||||
|
||||
constexpr int64_t calc_copy_grain_size(const exec_mode::Tag auto mode, const int64_t type_size)
|
||||
{
|
||||
/* The grain size should roughly depend on the work being done per index, which roughly
|
||||
* corresponds with the size of the type being copied. */
|
||||
return mode.grain_size(std::max<int64_t>(1, 32768 / type_size));
|
||||
}
|
||||
|
||||
constexpr int64_t calc_copy_grain_size(const exec_mode::Mode mode, const int64_t type_size)
|
||||
{
|
||||
return mode.grain_size(std::max<int64_t>(1, 32768 / type_size));
|
||||
}
|
||||
|
||||
/** Similar to #Mode::grain_size(int), but also returns a tag type. */
|
||||
template<exec_mode::Tag Mode>
|
||||
constexpr auto exec_mode_tag_for_copy(const Mode mode, const int64_t type_size)
|
||||
{
|
||||
if constexpr (mode.is_parallel) {
|
||||
return exec_mode::ParallelGrainSize{mode.grain_size(std::max<int64_t>(1, 32768 / type_size))};
|
||||
}
|
||||
else {
|
||||
return Mode{mode};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by copying all values from the `src` array.
|
||||
*/
|
||||
void copy(const GVArray &src, GMutableSpan dst, exec_mode::Mode mode = exec_mode::parallel);
|
||||
template<typename T, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void copy(const VArray<T> &src, MutableSpan<T> dst, const Mode mode = {})
|
||||
{
|
||||
BLI_assert(src.size() == dst.size());
|
||||
if constexpr (!mode.is_parallel) {
|
||||
src.materialize_compressed(dst.index_range(), dst);
|
||||
}
|
||||
else {
|
||||
const int64_t grain_size = calc_copy_grain_size(mode, sizeof(T));
|
||||
threading::parallel_for(src.index_range(), grain_size, [&](const IndexRange range) {
|
||||
src.materialize(range, dst);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by copying all values from the `src` array.
|
||||
*/
|
||||
template<typename T, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void copy(const Span<T> src, MutableSpan<T> dst, const Mode mode = {})
|
||||
{
|
||||
BLI_assert(src.size() == dst.size());
|
||||
if constexpr (!mode.is_parallel) {
|
||||
dst.copy_from(src);
|
||||
}
|
||||
else {
|
||||
const int64_t grain_size = calc_copy_grain_size(mode, sizeof(T));
|
||||
threading::parallel_for(src.index_range(), grain_size, [&](const IndexRange range) {
|
||||
copy(src.slice(range), dst.slice(range), exec_mode::serial);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by copying masked values from the `src` array.
|
||||
*/
|
||||
void copy(const GVArray &src,
|
||||
const IndexMask &selection,
|
||||
GMutableSpan dst,
|
||||
exec_mode::Mode mode = exec_mode::parallel);
|
||||
|
||||
/**
|
||||
* Fill the destination span by copying values from the `src` array.
|
||||
*/
|
||||
template<typename T, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void copy(const Span<T> src,
|
||||
const IndexMask &selection,
|
||||
MutableSpan<T> dst,
|
||||
const Mode mode = {})
|
||||
{
|
||||
BLI_assert(src.size() == dst.size());
|
||||
selection.foreach_index_optimized<int64_t>([&](const int64_t i) { dst[i] = src[i]; },
|
||||
exec_mode_tag_for_copy(mode, sizeof(T)));
|
||||
}
|
||||
|
||||
template<typename T> T compute_sum(const Span<T> data)
|
||||
{
|
||||
/* Explicitly splitting work into chunks for a couple of reasons:
|
||||
* - Improve numerical stability. While there are even more stable algorithms (e.g. Kahan
|
||||
* summation), they also add more complexity to the hot code path. So far, this simple approach
|
||||
* seems to solve the common issues people run into.
|
||||
* - Support computing the sum using multiple threads.
|
||||
* - Ensure deterministic results even with floating point numbers.
|
||||
*/
|
||||
constexpr int64_t chunk_size = 1024;
|
||||
const int64_t chunks_num = divide_ceil_ul(data.size(), chunk_size);
|
||||
Array<T> partial_sums(chunks_num);
|
||||
threading::parallel_for(partial_sums.index_range(), 1, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
const int64_t start = i * chunk_size;
|
||||
const Span<T> chunk = data.slice_safe(start, chunk_size);
|
||||
const T partial_sum = std::accumulate(chunk.begin(), chunk.end(), T());
|
||||
partial_sums[i] = partial_sum;
|
||||
}
|
||||
});
|
||||
return std::accumulate(partial_sums.begin(), partial_sums.end(), T());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the specified indices of the destination with the values in the source span.
|
||||
*/
|
||||
template<typename T, typename IndexT, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void scatter(const Span<T> src,
|
||||
const Span<IndexT> indices,
|
||||
MutableSpan<T> dst,
|
||||
const Mode mode = {})
|
||||
{
|
||||
BLI_assert(indices.size() == src.size());
|
||||
if constexpr (!mode.is_parallel) {
|
||||
for (const int64_t i : indices.index_range()) {
|
||||
dst[indices[i]] = src[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
const int64_t grain_size = calc_copy_grain_size(mode, sizeof(T));
|
||||
threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
|
||||
scatter(src.slice(range), indices.slice(range), dst, exec_mode::serial);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void scatter(const Span<T> src,
|
||||
const IndexMask &indices,
|
||||
MutableSpan<T> dst,
|
||||
const Mode mode = {})
|
||||
{
|
||||
BLI_assert(indices.size() == src.size());
|
||||
BLI_assert(indices.min_array_size() <= dst.size());
|
||||
indices.foreach_index_optimized<int64_t>(
|
||||
[&](const int64_t index, const int64_t pos) { dst[index] = src[pos]; },
|
||||
exec_mode_tag_for_copy(mode, sizeof(T)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by gathering indexed values from the `src` array.
|
||||
*/
|
||||
void gather(const GVArray &src,
|
||||
const IndexMask &indices,
|
||||
GMutableSpan dst,
|
||||
exec_mode::Mode mode = exec_mode::parallel);
|
||||
|
||||
/**
|
||||
* Fill the destination span by gathering indexed values from the `src` array.
|
||||
*/
|
||||
void gather(GSpan src,
|
||||
const IndexMask &indices,
|
||||
GMutableSpan dst,
|
||||
exec_mode::Mode mode = exec_mode::parallel);
|
||||
|
||||
/**
|
||||
* Fill the destination span by gathering indexed values from the `src` array.
|
||||
*/
|
||||
template<typename T, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void gather(const VArray<T> &src,
|
||||
const IndexMask &indices,
|
||||
MutableSpan<T> dst,
|
||||
const Mode mode = {})
|
||||
{
|
||||
PRF_scope_with_name("array_utils::gather", ProfileCategory::Default);
|
||||
BLI_assert(indices.size() >= dst.size());
|
||||
if constexpr (!mode.is_parallel) {
|
||||
src.materialize_compressed(indices, dst);
|
||||
}
|
||||
else {
|
||||
const int64_t grain_size = calc_copy_grain_size(mode, sizeof(T));
|
||||
threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
|
||||
src.materialize_compressed(indices.slice(range), dst.slice(range));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by gathering indexed values from the `src` array.
|
||||
*/
|
||||
template<typename T, typename IndexT, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void gather(const Span<T> src,
|
||||
const Span<IndexT> indices,
|
||||
const IndexMask &dst_mask,
|
||||
MutableSpan<T> dst,
|
||||
const Mode mode = {})
|
||||
{
|
||||
PRF_scope_with_name("array_utils::gather", ProfileCategory::Default);
|
||||
BLI_assert(indices.size() >= dst.size());
|
||||
dst_mask.foreach_index_optimized<int64_t>([&](const int64_t i) { dst[i] = src[indices[i]]; },
|
||||
exec_mode_tag_for_copy(mode, sizeof(T)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by gathering indexed values from the `src` array.
|
||||
*/
|
||||
template<typename T, typename IndexT, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void gather(const Span<T> src,
|
||||
const Span<IndexT> indices,
|
||||
MutableSpan<T> dst,
|
||||
const Mode mode = {})
|
||||
{
|
||||
gather(src, indices, IndexMask(dst.size()), dst, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by gathering indexed values from the `src` array.
|
||||
*/
|
||||
template<typename T, typename IndexT, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void gather(const VArray<T> &src,
|
||||
const Span<IndexT> indices,
|
||||
const IndexMask &dst_mask,
|
||||
MutableSpan<T> dst,
|
||||
const Mode mode = {})
|
||||
{
|
||||
PRF_scope_with_name("array_utils::gather", ProfileCategory::Default);
|
||||
BLI_assert(indices.size() >= dst_mask.min_array_size());
|
||||
const CommonVArrayInfo info = src.common_info();
|
||||
switch (info.type) {
|
||||
case CommonVArrayInfo::Type::Any: {
|
||||
dst_mask.foreach_index_optimized<int64_t>([&](const int64_t i) { dst[i] = src[indices[i]]; },
|
||||
exec_mode_tag_for_copy(mode, sizeof(T)));
|
||||
break;
|
||||
}
|
||||
case CommonVArrayInfo::Type::Span: {
|
||||
const Span span(static_cast<const T *>(info.data), src.size());
|
||||
gather(span, indices, dst_mask, dst, mode);
|
||||
break;
|
||||
}
|
||||
case CommonVArrayInfo::Type::Single: {
|
||||
index_mask::masked_fill(dst, *static_cast<const T *>(info.data), dst_mask);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by gathering indexed values from the `src` array.
|
||||
*/
|
||||
template<typename T, typename IndexT, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void gather(const VArray<T> &src,
|
||||
const Span<IndexT> indices,
|
||||
MutableSpan<T> dst,
|
||||
const Mode mode = {})
|
||||
{
|
||||
gather(src, indices, dst.index_range(), dst, mode);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void gather_group_to_group(const OffsetIndices<int> src_offsets,
|
||||
const OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &selection,
|
||||
const Span<T> src,
|
||||
MutableSpan<T> dst)
|
||||
{
|
||||
selection.foreach_index(
|
||||
[&](const int64_t src_i, const int64_t dst_i) {
|
||||
dst.slice(dst_offsets[dst_i]).copy_from(src.slice(src_offsets[src_i]));
|
||||
},
|
||||
exec_mode::grain_size(512));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void gather_group_to_group(const OffsetIndices<int> src_offsets,
|
||||
const OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &selection,
|
||||
const VArray<T> src,
|
||||
MutableSpan<T> dst)
|
||||
{
|
||||
selection.foreach_index(
|
||||
[&](const int64_t src_i, const int64_t dst_i) {
|
||||
src.materialize_compressed(src_offsets[src_i], dst.slice(dst_offsets[dst_i]));
|
||||
},
|
||||
exec_mode::grain_size(512));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void gather_to_groups(const OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &src_selection,
|
||||
const Span<T> src,
|
||||
MutableSpan<T> dst)
|
||||
{
|
||||
src_selection.foreach_index(
|
||||
[&](const int src_i, const int dst_i) { dst.slice(dst_offsets[dst_i]).fill(src[src_i]); },
|
||||
exec_mode::grain_size(1024));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the \a src data from the groups defined by \a src_offsets to the groups in \a dst defined
|
||||
* by \a dst_offsets. Groups to use are masked by \a selection, and it is assumed that the
|
||||
* corresponding groups have the same size.
|
||||
*/
|
||||
void copy_group_to_group(OffsetIndices<int> src_offsets,
|
||||
OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &selection,
|
||||
GSpan src,
|
||||
GMutableSpan dst);
|
||||
template<typename T>
|
||||
void copy_group_to_group(OffsetIndices<int> src_offsets,
|
||||
OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &selection,
|
||||
Span<T> src,
|
||||
MutableSpan<T> dst)
|
||||
{
|
||||
copy_group_to_group(src_offsets, dst_offsets, selection, GSpan(src), GMutableSpan(dst));
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of occurrences of each index.
|
||||
* \param indices: The indices to count.
|
||||
* \param counts: The number of occurrences of each index. Typically initialized to zero.
|
||||
* Must be large enough to contain the maximum index.
|
||||
*
|
||||
* \note The memory referenced by the two spans must not overlap.
|
||||
*/
|
||||
void count_indices(Span<int> indices, MutableSpan<int> counts);
|
||||
|
||||
void invert_booleans(MutableSpan<bool> span);
|
||||
void invert_booleans(MutableSpan<bool> span, const IndexMask &mask);
|
||||
|
||||
int64_t count_booleans(const VArray<bool> &varray);
|
||||
int64_t count_booleans(const VArray<bool> &varray, const IndexMask &mask);
|
||||
|
||||
enum class BooleanMix {
|
||||
None,
|
||||
AllFalse,
|
||||
AllTrue,
|
||||
Mixed,
|
||||
};
|
||||
BooleanMix booleans_mix_calc(const VArray<bool> &varray, IndexRange range_to_check);
|
||||
inline BooleanMix booleans_mix_calc(const VArray<bool> &varray)
|
||||
{
|
||||
return booleans_mix_calc(varray, varray.index_range());
|
||||
}
|
||||
|
||||
/** Check if the value exists in the array. */
|
||||
bool contains(const VArray<bool> &varray, const IndexMask &indices_to_check, bool value);
|
||||
|
||||
/** Return indices in the mask that are non-negative. */
|
||||
IndexMask indices_non_negative(const IndexMask &universe,
|
||||
Span<int> values,
|
||||
LinearAllocator<> &memory);
|
||||
/** Return indices in the mask that are not negative and less than the given size. */
|
||||
IndexMask indices_in_range(const IndexMask &universe,
|
||||
Span<int> values,
|
||||
IndexRange range,
|
||||
LinearAllocator<> &memory);
|
||||
|
||||
/**
|
||||
* Finds all the index ranges for which consecutive values in \a span equal \a value.
|
||||
*/
|
||||
template<typename T> inline Vector<IndexRange> find_all_ranges(const Span<T> span, const T &value)
|
||||
{
|
||||
if (span.is_empty()) {
|
||||
return Vector<IndexRange>();
|
||||
}
|
||||
Vector<IndexRange> ranges;
|
||||
int64_t length = (span.first() == value) ? 1 : 0;
|
||||
for (const int64_t i : span.index_range().drop_front(1)) {
|
||||
if (span[i - 1] == value && span[i] != value) {
|
||||
ranges.append(IndexRange::from_end_size(i, length));
|
||||
length = 0;
|
||||
}
|
||||
else if (span[i] == value) {
|
||||
length++;
|
||||
}
|
||||
}
|
||||
if (length > 0) {
|
||||
ranges.append(IndexRange::from_end_size(span.size(), length));
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the span with increasing indices: 0, 1, 2, ...
|
||||
* Optionally, the start value can be provided.
|
||||
*/
|
||||
template<typename T> inline void fill_index_range(MutableSpan<T> span, const T start = 0)
|
||||
{
|
||||
std::iota(span.begin(), span.end(), start);
|
||||
}
|
||||
|
||||
template<typename T, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void fill_index_range(const IndexMask &mask, MutableSpan<T> span, const Mode mode = {})
|
||||
{
|
||||
mask.foreach_index_optimized<T>([&](const T index) { span[index] = index; }, mode);
|
||||
}
|
||||
|
||||
template<typename T, exec_mode::Tag Mode = exec_mode::Parallel>
|
||||
inline void fill_index_range(const IndexMask &mask,
|
||||
MutableSpan<T> span,
|
||||
const T start,
|
||||
const Mode mode = {})
|
||||
{
|
||||
if (start == 0) {
|
||||
fill_index_range(mask, span, mode);
|
||||
}
|
||||
else {
|
||||
mask.foreach_index_optimized<T>([&](const T index) { span[index] = start + index; }, mode);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool indexed_data_equal(const Span<T> all_values, const Span<int> indices, const Span<T> values)
|
||||
{
|
||||
BLI_assert(indices.size() == values.size());
|
||||
for (const int i : indices.index_range()) {
|
||||
if (all_values[indices[i]] != values[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool indices_are_range(Span<int> indices, IndexRange range);
|
||||
|
||||
/**
|
||||
* Returns the index of the (first) maximum element in the virtual array or std::nullopt if the
|
||||
* array is empty.
|
||||
*/
|
||||
template<typename T>
|
||||
inline std::optional<int64_t> max_element_index(const Span<T> &span,
|
||||
const int64_t grain_size = 8192)
|
||||
{
|
||||
const T *max_it = threading::parallel_reduce(
|
||||
span.index_range(),
|
||||
grain_size,
|
||||
span.begin(),
|
||||
[&](const IndexRange range, const T *init_max) {
|
||||
const Span<T> sub_span = span.slice(range);
|
||||
const T *max_elem = std::max_element(sub_span.begin(), sub_span.end());
|
||||
if (*max_elem < *init_max) {
|
||||
return init_max;
|
||||
}
|
||||
return max_elem;
|
||||
},
|
||||
[&](const T *a, const T *b) {
|
||||
if (*a < *b) {
|
||||
return a;
|
||||
}
|
||||
return b;
|
||||
});
|
||||
return std::distance(span.begin(), max_it);
|
||||
}
|
||||
template<typename T>
|
||||
inline std::optional<int64_t> max_element_index(const VArray<T> &array,
|
||||
const int64_t grain_size = 8192)
|
||||
{
|
||||
if (!array || array.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (array.is_single()) {
|
||||
return array.first();
|
||||
}
|
||||
if (array.is_span()) {
|
||||
return max_element_index(array.get_internal_span(), grain_size);
|
||||
}
|
||||
return threading::parallel_reduce(
|
||||
array.index_range(),
|
||||
grain_size,
|
||||
array.first(),
|
||||
[&](const IndexRange range, const int64_t init_i) {
|
||||
int64_t max_index = init_i;
|
||||
T max_elem = array[max_index];
|
||||
for (const int i : range) {
|
||||
if (max_elem < array[i]) {
|
||||
max_index = i;
|
||||
max_elem = array[i];
|
||||
}
|
||||
}
|
||||
return max_index;
|
||||
},
|
||||
[&](const int64_t index_a, const int64_t index_b) {
|
||||
if (array[index_a] < array[index_b]) {
|
||||
return index_b;
|
||||
}
|
||||
return index_a;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace blender::array_utils
|
||||
36
blender-5.2.0/source/blender/blenlib/BLI_asan.h
Normal file
36
blender-5.2.0/source/blender/blenlib/BLI_asan.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Clang defines this. */
|
||||
#ifndef __has_feature
|
||||
# define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) && \
|
||||
(!defined(_MSC_VER) || _MSC_VER > 1929) /* MSVC 2019 and below doesn't ship ASAN headers. */
|
||||
# include "sanitizer/asan_interface.h"
|
||||
# define WITH_ASAN
|
||||
#else
|
||||
/* Ensure return value is used. Just using UNUSED_VARS results in a warning. */
|
||||
# define ASAN_POISON_MEMORY_REGION(addr, size) (void)(0 && ((size) != 0 && (addr) != NULL))
|
||||
# define ASAN_UNPOISON_MEMORY_REGION(addr, size) (void)(0 && ((size) != 0 && (addr) != NULL))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Mark a region of memory as "freed". When using address sanitizer, accessing the given memory
|
||||
* region will cause an use-after-poison error. This can be used to find errors when dealing with
|
||||
* uninitialized memory in custom containers.
|
||||
*/
|
||||
#define BLI_asan_poison(addr, size) ASAN_POISON_MEMORY_REGION(addr, size)
|
||||
|
||||
/**
|
||||
* Mark a region of memory as usable again.
|
||||
*/
|
||||
#define BLI_asan_unpoison(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size)
|
||||
120
blender-5.2.0/source/blender/blenlib/BLI_assert.h
Normal file
120
blender-5.2.0/source/blender/blenlib/BLI_assert.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* Defines:
|
||||
* - #BLI_assert
|
||||
* - #BLI_STATIC_ASSERT
|
||||
* - #BLI_STATIC_ASSERT_ALIGN
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* Utility functions. */
|
||||
|
||||
void _BLI_assert_print_pos(const char *file, int line, const char *function, const char *id);
|
||||
void _BLI_assert_print_extra(const char *str);
|
||||
void _BLI_assert_print_backtrace();
|
||||
void _BLI_assert_abort();
|
||||
void _BLI_assert_unreachable_print(const char *file, int line, const char *function);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# include <crtdbg.h> /* for _STATIC_ASSERT */
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* _BLI_ASSERT_PRINT_POS */
|
||||
# if defined(__GNUC__)
|
||||
# define _BLI_ASSERT_PRINT_POS(a) _BLI_assert_print_pos(__FILE__, __LINE__, __func__, #a)
|
||||
# elif defined(_MSC_VER)
|
||||
# define _BLI_ASSERT_PRINT_POS(a) _BLI_assert_print_pos(__FILE__, __LINE__, __func__, #a)
|
||||
# else
|
||||
# define _BLI_ASSERT_PRINT_POS(a) _BLI_assert_print_pos(__FILE__, __LINE__, "<?>", #a)
|
||||
# endif
|
||||
/* _BLI_ASSERT_ABORT */
|
||||
# ifdef WITH_ASSERT_ABORT
|
||||
# define _BLI_ASSERT_ABORT _BLI_assert_abort
|
||||
# else
|
||||
# define _BLI_ASSERT_ABORT() (void)0
|
||||
# endif
|
||||
/* BLI_assert */
|
||||
# define BLI_assert(a) \
|
||||
(void)((!(a)) ? ((_BLI_assert_print_backtrace(), \
|
||||
_BLI_ASSERT_PRINT_POS(a), \
|
||||
_BLI_ASSERT_ABORT(), \
|
||||
NULL)) : \
|
||||
NULL)
|
||||
/** A version of #BLI_assert() to pass an additional message to be printed on failure. */
|
||||
# define BLI_assert_msg(a, msg) \
|
||||
(void)((!(a)) ? ((_BLI_assert_print_backtrace(), \
|
||||
_BLI_ASSERT_PRINT_POS(a), \
|
||||
_BLI_assert_print_extra(msg), \
|
||||
_BLI_ASSERT_ABORT(), \
|
||||
NULL)) : \
|
||||
NULL)
|
||||
#else
|
||||
# define BLI_assert(a) ((void)0)
|
||||
# define BLI_assert_msg(a, msg) ((void)0)
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
/* C++11 */
|
||||
# define BLI_STATIC_ASSERT(a, msg) static_assert(a, msg);
|
||||
#elif defined(_MSC_VER)
|
||||
/* Visual Studio */
|
||||
# if !defined(__clang__)
|
||||
# define BLI_STATIC_ASSERT(a, msg) static_assert(a, msg);
|
||||
# else
|
||||
# define BLI_STATIC_ASSERT(a, msg) _STATIC_ASSERT(a);
|
||||
# endif
|
||||
#elif defined(__COVERITY__)
|
||||
/* Workaround error with COVERITY. */
|
||||
# define BLI_STATIC_ASSERT(a, msg)
|
||||
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
|
||||
/* C11 */
|
||||
# define BLI_STATIC_ASSERT(a, msg) _Static_assert(a, msg);
|
||||
#else
|
||||
/* Old unsupported compiler */
|
||||
# define BLI_STATIC_ASSERT(a, msg)
|
||||
#endif
|
||||
|
||||
#define BLI_STATIC_ASSERT_ALIGN(st, align) \
|
||||
BLI_STATIC_ASSERT((sizeof(st) % (align) == 0), "Structure must be strictly aligned")
|
||||
|
||||
/**
|
||||
* Indicates that this line of code should never be executed. If it is reached, it will abort in
|
||||
* debug builds and print an error in release builds.
|
||||
*/
|
||||
#define BLI_assert_unreachable() \
|
||||
{ \
|
||||
_BLI_assert_unreachable_print(__FILE__, __LINE__, __func__); \
|
||||
BLI_assert_msg(0, "This line of code is marked to be unreachable."); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/**
|
||||
* Indicates that this line should never be reached, even at compile-time. Just doing
|
||||
* `static_assert(false)` doesn't work in C++20 for this use-case. BLI_assert_unreachable is
|
||||
* similar but does not catch issues at compile-time.
|
||||
*/
|
||||
# define BLI_assert_unreachable_static() \
|
||||
static_assert([]<bool flag = false>() { return flag; }(), \
|
||||
"Unreachable code path reached at compile-time!")
|
||||
|
||||
template<class T> inline constexpr bool _bli_always_false = false;
|
||||
|
||||
/**
|
||||
* Like #BLI_assert_unreachable_static but allows passing in a type. This results in a more useful
|
||||
* error message containing the type name.
|
||||
*/
|
||||
# define BLI_assert_unreachable_static_t(T) \
|
||||
static_assert(_bli_always_false<T>, "Unreachable code path reached at compile-time!")
|
||||
|
||||
#endif
|
||||
150
blender-5.2.0/source/blender/blenlib/BLI_astar.h
Normal file
150
blender-5.2.0/source/blender/blenlib/BLI_astar.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief An implementation of the A* (AStar) algorithm to solve shortest path problem.
|
||||
*/
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
struct BLI_AStarGNLink {
|
||||
int nodes[2];
|
||||
float cost;
|
||||
|
||||
void *custom_data;
|
||||
};
|
||||
|
||||
struct BLI_AStarGNode {
|
||||
ListBaseT<LinkData> neighbor_links;
|
||||
|
||||
void *custom_data;
|
||||
};
|
||||
|
||||
struct BLI_AStarSolution {
|
||||
/* Final 'most useful' data. */
|
||||
/** Number of steps (i.e. walked links) in path
|
||||
* (nodes num, including start and end, is steps + 1). */
|
||||
int steps;
|
||||
/** Store the path, in reversed order (from destination to source node), as indices. */
|
||||
int *prev_nodes;
|
||||
/** Indices are nodes' ones, as prev_nodes, but they map to relevant link. */
|
||||
BLI_AStarGNLink **prev_links;
|
||||
|
||||
void *custom_data;
|
||||
|
||||
/* Mostly runtime data. */
|
||||
BLI_bitmap *done_nodes;
|
||||
float *g_costs;
|
||||
int *g_steps;
|
||||
|
||||
struct MemArena *mem; /* Memory arena. */
|
||||
};
|
||||
|
||||
struct BLI_AStarGraph {
|
||||
int node_num;
|
||||
BLI_AStarGNode *nodes;
|
||||
|
||||
void *custom_data;
|
||||
|
||||
struct MemArena *mem; /* Memory arena. */
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a node in A* graph.
|
||||
*
|
||||
* \param custom_data: an opaque pointer attached to this link,
|
||||
* available e.g. to cost callback function.
|
||||
*/
|
||||
void BLI_astar_node_init(BLI_AStarGraph *as_graph, int node_index, void *custom_data);
|
||||
/**
|
||||
* Add a link between two nodes of our A* graph.
|
||||
*
|
||||
* \param cost: The 'length' of the link
|
||||
* (actual distance between two vertices or face centers e.g.).
|
||||
* \param custom_data: An opaque pointer attached to this link,
|
||||
* available e.g. to cost callback function.
|
||||
*/
|
||||
void BLI_astar_node_link_add(
|
||||
BLI_AStarGraph *as_graph, int node1_index, int node2_index, float cost, void *custom_data);
|
||||
/**
|
||||
* \return The index of the other node of given link.
|
||||
*/
|
||||
int BLI_astar_node_link_other_node(BLI_AStarGNLink *lnk, int idx);
|
||||
|
||||
/**
|
||||
* Initialize a solution data for given A* graph. Does not compute anything!
|
||||
*
|
||||
* \param custom_data: an opaque pointer attached to this link, available e.g.
|
||||
* to cost callback function.
|
||||
*
|
||||
* \note BLI_AStarSolution stores nearly all data needed during solution compute.
|
||||
*/
|
||||
void BLI_astar_solution_init(BLI_AStarGraph *as_graph,
|
||||
BLI_AStarSolution *as_solution,
|
||||
void *custom_data);
|
||||
/**
|
||||
* Clear given solution's data, but does not release its memory.
|
||||
* Avoids having to recreate/allocate a memory-arena in loops, e.g.
|
||||
*
|
||||
* \note This *has to be called* between each path solving.
|
||||
*/
|
||||
void BLI_astar_solution_clear(BLI_AStarSolution *as_solution);
|
||||
/**
|
||||
* Release the memory allocated for this solution.
|
||||
*/
|
||||
void BLI_astar_solution_free(BLI_AStarSolution *as_solution);
|
||||
|
||||
/**
|
||||
* Callback computing the current cost (distance) to next node,
|
||||
* and the estimated overall cost to destination node
|
||||
* (A* expects this estimation to always be less or equal than actual shortest path
|
||||
* from next node to destination one).
|
||||
*
|
||||
* \param link: the graph link between current node and next one.
|
||||
* \param node_idx_curr: current node index.
|
||||
* \param node_idx_next: next node index.
|
||||
* \param node_idx_dst: destination node index.
|
||||
*/
|
||||
using astar_f_cost = float (*)(BLI_AStarGraph *as_graph,
|
||||
BLI_AStarSolution *as_solution,
|
||||
BLI_AStarGNLink *link,
|
||||
int node_idx_curr,
|
||||
int node_idx_next,
|
||||
int node_idx_dst);
|
||||
|
||||
/**
|
||||
* Initialize an A* graph. Total number of nodes must be known.
|
||||
*
|
||||
* Nodes might be e.g. vertices, faces, ... etc.
|
||||
*
|
||||
* \param custom_data: an opaque pointer attached to this link,
|
||||
* available e.g. to cost callback function.
|
||||
*/
|
||||
void BLI_astar_graph_init(BLI_AStarGraph *as_graph, int node_num, void *custom_data);
|
||||
void BLI_astar_graph_free(BLI_AStarGraph *as_graph);
|
||||
/**
|
||||
* Solve a path in given graph, using given 'cost' callback function.
|
||||
*
|
||||
* \param max_steps: maximum number of nodes the found path may have.
|
||||
* Useful in performance-critical usages.
|
||||
* If no path is found within given steps, returns false too.
|
||||
* \return true if a path was found, false otherwise.
|
||||
*/
|
||||
bool BLI_astar_graph_solve(BLI_AStarGraph *as_graph,
|
||||
int node_index_src,
|
||||
int node_index_dst,
|
||||
astar_f_cost f_cost_cb,
|
||||
BLI_AStarSolution *r_solution,
|
||||
int max_steps);
|
||||
|
||||
} // namespace blender
|
||||
158
blender-5.2.0/source/blender/blenlib/BLI_atomic_disjoint_set.hh
Normal file
158
blender-5.2.0/source/blender/blenlib/BLI_atomic_disjoint_set.hh
Normal file
@@ -0,0 +1,158 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "BLI_array.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Same as `DisjointSet` but is thread safe (at slightly higher cost for the single threaded case).
|
||||
*
|
||||
* The implementation is based on the following paper:
|
||||
* "Wait-free Parallel Algorithms for the Union-Find Problem"
|
||||
* by Richard J. Anderson and Heather Woll.
|
||||
*
|
||||
* It's also inspired by this implementation: https://github.com/wjakob/dset.
|
||||
*/
|
||||
class AtomicDisjointSet {
|
||||
private:
|
||||
/* Can generally used relaxed memory order with this algorithm. */
|
||||
static constexpr auto relaxed = std::memory_order_relaxed;
|
||||
|
||||
struct Item {
|
||||
int parent;
|
||||
int rank;
|
||||
};
|
||||
|
||||
/**
|
||||
* An #Item per element. It's important that the entire item is in a single atomic, so that it
|
||||
* can be updated atomically. */
|
||||
mutable Array<std::atomic<Item>> items_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new disjoing set with the given set. Initially, every element is in a separate set.
|
||||
*/
|
||||
AtomicDisjointSet(const int size);
|
||||
|
||||
/**
|
||||
* Join the sets containing elements x and y. Nothing happens when they were in the same set
|
||||
* before.
|
||||
*/
|
||||
void join(int x, int y)
|
||||
{
|
||||
while (true) {
|
||||
x = this->find_root(x);
|
||||
y = this->find_root(y);
|
||||
|
||||
if (x == y) {
|
||||
/* They are in the same set already. */
|
||||
return;
|
||||
}
|
||||
|
||||
int x_rank = items_[x].load(relaxed).rank;
|
||||
int y_rank = items_[y].load(relaxed).rank;
|
||||
|
||||
if (
|
||||
/* Implement union by rank heuristic. */
|
||||
x_rank > y_rank
|
||||
/* If the rank is the same, make a consistent decision. */
|
||||
|| (x_rank == y_rank && x < y))
|
||||
{
|
||||
std::swap(x_rank, y_rank);
|
||||
std::swap(x, y);
|
||||
}
|
||||
|
||||
/* Update parent of item x. */
|
||||
Item x_item_old = {x, x_rank};
|
||||
const Item x_item_new{y, x_rank};
|
||||
if (!items_[x].compare_exchange_strong(x_item_old, x_item_new, relaxed)) {
|
||||
/* Another thread has updated item x, start again. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (x_rank == y_rank) {
|
||||
/* Increase rank of item y. This may fail when another thread has updated item y in the
|
||||
* meantime. That may lead to worse behavior with the union by rank heurist, but seems to
|
||||
* be ok in practice. */
|
||||
Item y_item_old{y, y_rank};
|
||||
const Item y_item_new{y, y_rank + 1};
|
||||
items_[y].compare_exchange_weak(y_item_old, y_item_new, relaxed);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true when x and y are in the same set.
|
||||
*/
|
||||
bool in_same_set(int x, int y) const
|
||||
{
|
||||
while (true) {
|
||||
x = this->find_root(x);
|
||||
y = this->find_root(y);
|
||||
if (x == y) {
|
||||
return true;
|
||||
}
|
||||
if (items_[x].load(relaxed).parent == x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the element that represents the set containing x currently.
|
||||
*/
|
||||
int find_root(int x) const
|
||||
{
|
||||
while (true) {
|
||||
const Item item = items_[x].load(relaxed);
|
||||
if (x == item.parent) {
|
||||
return x;
|
||||
}
|
||||
const int new_parent = items_[item.parent].load(relaxed).parent;
|
||||
if (item.parent != new_parent) {
|
||||
/* This halves the path for faster future lookups. That fail but that does not change
|
||||
* correctness. */
|
||||
Item expected = item;
|
||||
const Item desired{new_parent, item.rank};
|
||||
items_[x].compare_exchange_weak(expected, desired, relaxed);
|
||||
}
|
||||
x = new_parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* True when x represents a set.
|
||||
*/
|
||||
bool is_root(const int x) const
|
||||
{
|
||||
const Item item = items_[x].load(relaxed);
|
||||
return item.parent == x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an identifier for each id. This is deterministic and does not depend on the order of
|
||||
* joins. The ids are ordered by their first occurrence. Consequently, `result[0]` is always zero
|
||||
* (unless there are no elements).
|
||||
* \return The total number of unique IDs.
|
||||
*/
|
||||
int calc_reduced_ids(MutableSpan<int> result) const;
|
||||
|
||||
/**
|
||||
* Count the number of disjoint sets.
|
||||
*/
|
||||
int count_sets() const;
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
56
blender-5.2.0/source/blender/blenlib/BLI_binary_search.hh
Normal file
56
blender-5.2.0/source/blender/blenlib/BLI_binary_search.hh
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace blender::binary_search {
|
||||
|
||||
/**
|
||||
* Find the index of the first element where the predicate is true. The predicate must also be
|
||||
* true for all following elements. If the predicate is false for all elements, the size of the
|
||||
* range is returned.
|
||||
*/
|
||||
template<typename Iterator, typename Predicate>
|
||||
static int64_t first_if(Iterator begin, Iterator end, Predicate &&predicate)
|
||||
{
|
||||
return std::lower_bound(begin,
|
||||
end,
|
||||
nullptr,
|
||||
[&](const auto &value, void * /*dummy*/) { return !predicate(value); }) -
|
||||
begin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the last element where the predicate is true. The predicate must also be
|
||||
* true for all previous elements. If the predicate is false for all elements, the -1 is returned.
|
||||
*/
|
||||
template<typename Iterator, typename Predicate>
|
||||
static int64_t last_if(Iterator begin, Iterator end, Predicate &&predicate)
|
||||
{
|
||||
return std::upper_bound(begin,
|
||||
end,
|
||||
nullptr,
|
||||
[&](void * /*dummy*/, const auto &value) { return !predicate(value); }) -
|
||||
begin - 1;
|
||||
}
|
||||
|
||||
template<typename Range, typename Predicate>
|
||||
int64_t first_if(const Range &range, Predicate &&predicate)
|
||||
{
|
||||
return first_if(range.begin(), range.end(), predicate);
|
||||
}
|
||||
|
||||
template<typename Range, typename Predicate>
|
||||
int64_t last_if(const Range &range, Predicate &&predicate)
|
||||
{
|
||||
return last_if(range.begin(), range.end(), predicate);
|
||||
}
|
||||
|
||||
} // namespace blender::binary_search
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_bit_span.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender::bits {
|
||||
|
||||
/**
|
||||
* Converts the bools to bits and `or`s them into the given bits. For pure conversion, the bits
|
||||
* should therefore be zero initialized before they are passed into this function.
|
||||
*
|
||||
* \param allowed_overshoot: How many bools/bits can be read/written after the end of the given
|
||||
* spans. This can help with performance because the internal algorithm can process many elements
|
||||
* at once.
|
||||
*
|
||||
* \return True if any of the checked bools were true (this also includes the bools in the
|
||||
* overshoot).
|
||||
*/
|
||||
bool or_bools_into_bits(Span<bool> bools, MutableBitSpan r_bits, int64_t allowed_overshoot = 0);
|
||||
|
||||
} // namespace blender::bits
|
||||
166
blender-5.2.0/source/blender/blenlib/BLI_bit_group_vector.hh
Normal file
166
blender-5.2.0/source/blender/blenlib/BLI_bit_group_vector.hh
Normal file
@@ -0,0 +1,166 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_bit_span_ops.hh"
|
||||
#include "BLI_bit_vector.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace bits {
|
||||
|
||||
/**
|
||||
* A #BitGroupVector is a compact data structure that allows storing an arbitrary but fixed number
|
||||
* of bits per element. For example, it could be used to compactly store 5 bits per vertex in a
|
||||
* mesh. The data structure stores the bits in a way so that the #BitSpan for every element is
|
||||
* bounded according to #is_bounded_span. The makes sure that operations on entire groups can be
|
||||
* implemented efficiently. For example, one can easy `or` one group into another.
|
||||
*/
|
||||
template<int64_t InlineBufferCapacity = 64, typename Allocator = GuardedAllocator>
|
||||
class BitGroupVector {
|
||||
private:
|
||||
/**
|
||||
* Number of bits per group.
|
||||
*/
|
||||
int64_t group_size_ = 0;
|
||||
/**
|
||||
* Actually stored number of bits per group so that individual groups are bounded according to
|
||||
* #is_bounded_span.
|
||||
*/
|
||||
int64_t aligned_group_size_ = 0;
|
||||
BitVector<InlineBufferCapacity, Allocator> data_;
|
||||
|
||||
static int64_t align_group_size(const int64_t group_size)
|
||||
{
|
||||
if (group_size < 64) {
|
||||
/* Align to next power of two so that a single group never spans across two ints. */
|
||||
return power_of_2_max(group_size);
|
||||
}
|
||||
/* Align to multiple of BitsPerInt. */
|
||||
return (group_size + BitsPerInt - 1) & ~(BitsPerInt - 1);
|
||||
}
|
||||
|
||||
public:
|
||||
BitGroupVector(Allocator allocator = {}) noexcept : data_(allocator) {}
|
||||
|
||||
BitGroupVector(NoExceptConstructor, Allocator allocator = {}) noexcept
|
||||
: BitGroupVector(allocator)
|
||||
{
|
||||
}
|
||||
|
||||
BitGroupVector(const int64_t size_in_groups,
|
||||
const int64_t group_size,
|
||||
const bool value = false,
|
||||
Allocator allocator = {})
|
||||
: group_size_(group_size),
|
||||
aligned_group_size_(align_group_size(group_size)),
|
||||
data_(size_in_groups * aligned_group_size_, value, allocator)
|
||||
{
|
||||
BLI_assert(group_size >= 0);
|
||||
BLI_assert(size_in_groups >= 0);
|
||||
}
|
||||
|
||||
BitGroupVector(const BitGroupVector &other)
|
||||
: group_size_(other.group_size_),
|
||||
aligned_group_size_(other.aligned_group_size_),
|
||||
data_(other.data_)
|
||||
{
|
||||
}
|
||||
|
||||
BitGroupVector(BitGroupVector &&other)
|
||||
: group_size_(other.group_size_),
|
||||
aligned_group_size_(other.aligned_group_size_),
|
||||
data_(std::move(other.data_))
|
||||
{
|
||||
}
|
||||
|
||||
BitGroupVector &operator=(const BitGroupVector &other)
|
||||
{
|
||||
return copy_assign_container(*this, other);
|
||||
}
|
||||
|
||||
BitGroupVector &operator=(BitGroupVector &&other)
|
||||
{
|
||||
return move_assign_container(*this, std::move(other));
|
||||
}
|
||||
|
||||
/** Get all the bits at an index. */
|
||||
BoundedBitSpan operator[](const int64_t i) const
|
||||
{
|
||||
BLI_assert(this->index_range().contains(i));
|
||||
const int64_t offset = aligned_group_size_ * i;
|
||||
return {data_.data() + (offset >> BitToIntIndexShift),
|
||||
IndexRange(offset & BitIndexMask, group_size_)};
|
||||
}
|
||||
|
||||
/** Get all the bits at an index. */
|
||||
MutableBoundedBitSpan operator[](const int64_t i)
|
||||
{
|
||||
BLI_assert(this->index_range().contains(i));
|
||||
const int64_t offset = aligned_group_size_ * i;
|
||||
return {data_.data() + (offset >> BitToIntIndexShift),
|
||||
IndexRange(offset & BitIndexMask, group_size_)};
|
||||
}
|
||||
|
||||
/** Number of groups. */
|
||||
int64_t size() const
|
||||
{
|
||||
return aligned_group_size_ == 0 ? 0 : data_.size() / aligned_group_size_;
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return this->size() == 0;
|
||||
}
|
||||
|
||||
/** Number of bits per group. */
|
||||
int64_t group_size() const
|
||||
{
|
||||
return group_size_;
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange{this->size()};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all stored bits. Note that this may also contain padding bits. This can be used to e.g.
|
||||
* mix multiple #BitGroupVector.
|
||||
*/
|
||||
BoundedBitSpan all_bits() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
MutableBoundedBitSpan all_bits()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates each group by computing the bitwise-and with the given bits.
|
||||
*/
|
||||
void foreach_and(const BoundedBitSpan bits)
|
||||
{
|
||||
/* This can still be optimized due to the additional knowledge we have how consecutive groups
|
||||
* are laid out in memory. It is possible to updated multiple small groups at once. */
|
||||
BLI_assert(bits.size() == group_size_);
|
||||
for (const int64_t i : this->index_range()) {
|
||||
MutableBoundedBitSpan group = (*this)[i];
|
||||
group &= bits;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace bits
|
||||
|
||||
using bits::BitGroupVector;
|
||||
|
||||
} // namespace blender
|
||||
246
blender-5.2.0/source/blender/blenlib/BLI_bit_ref.hh
Normal file
246
blender-5.2.0/source/blender/blenlib/BLI_bit_ref.hh
Normal file
@@ -0,0 +1,246 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* This file provides the basis for processing "indexed bits" (i.e. every bit has an index).
|
||||
* The main purpose of this file is to define how bits are indexed within a memory buffer.
|
||||
* For example, one has to define whether the first bit is the least or most significant bit and
|
||||
* how endianness affect the bit order.
|
||||
*
|
||||
* The order is defined as follows:
|
||||
* - Every indexed bit is part of an #BitInt. These ints are ordered by their address as usual.
|
||||
* - Within each #BitInt, the bits are ordered from least to most significant.
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace bits {
|
||||
|
||||
/** Using a large integer type is better because then it's easier to process many bits at once. */
|
||||
using BitInt = uint64_t;
|
||||
/** Number of bits that fit into #BitInt. */
|
||||
static constexpr int64_t BitsPerInt = int64_t(sizeof(BitInt) * 8);
|
||||
/** Shift amount to get from a bit index to an int index. Equivalent to `log(BitsPerInt, 2)`. */
|
||||
static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(BitInt) >= 2) + (sizeof(BitInt) >= 4) +
|
||||
(sizeof(BitInt) >= 8);
|
||||
/** Bit mask containing a 1 for the last few bits that index a bit inside of an #BitInt. */
|
||||
static constexpr BitInt BitIndexMask = (BitInt(1) << BitToIntIndexShift) - 1;
|
||||
|
||||
inline BitInt mask_first_n_bits(const int64_t n)
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
BLI_assert(n <= BitsPerInt);
|
||||
if (n == BitsPerInt) {
|
||||
return BitInt(-1);
|
||||
}
|
||||
return (BitInt(1) << n) - 1;
|
||||
}
|
||||
|
||||
inline BitInt mask_last_n_bits(const int64_t n)
|
||||
{
|
||||
return ~mask_first_n_bits(BitsPerInt - n);
|
||||
}
|
||||
|
||||
inline BitInt mask_range_bits(const int64_t start, const int64_t size)
|
||||
{
|
||||
BLI_assert(start >= 0);
|
||||
BLI_assert(size >= 0);
|
||||
const int64_t end = start + size;
|
||||
BLI_assert(end <= BitsPerInt);
|
||||
if (end == BitsPerInt) {
|
||||
return mask_last_n_bits(size);
|
||||
}
|
||||
return ((BitInt(1) << end) - 1) & ~((BitInt(1) << start) - 1);
|
||||
}
|
||||
|
||||
inline BitInt mask_single_bit(const int64_t bit_index)
|
||||
{
|
||||
BLI_assert(bit_index >= 0);
|
||||
BLI_assert(bit_index < BitsPerInt);
|
||||
return BitInt(1) << bit_index;
|
||||
}
|
||||
|
||||
inline BitInt *int_containing_bit(BitInt *data, const int64_t bit_index)
|
||||
{
|
||||
return data + (bit_index >> BitToIntIndexShift);
|
||||
}
|
||||
|
||||
inline const BitInt *int_containing_bit(const BitInt *data, const int64_t bit_index)
|
||||
{
|
||||
return data + (bit_index >> BitToIntIndexShift);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but
|
||||
* not changed.
|
||||
*/
|
||||
class BitRef {
|
||||
private:
|
||||
/** Points to the exact integer that the bit is in. */
|
||||
const BitInt *int_;
|
||||
/** All zeros except for a single one at the bit that is referenced. */
|
||||
BitInt mask_;
|
||||
|
||||
friend class MutableBitRef;
|
||||
|
||||
public:
|
||||
BitRef() = default;
|
||||
|
||||
/**
|
||||
* Reference a specific bit in an array. Note that #data does *not* have to point to the
|
||||
* exact integer the bit is in.
|
||||
*/
|
||||
BitRef(const BitInt *data, const int64_t bit_index)
|
||||
{
|
||||
int_ = int_containing_bit(data, bit_index);
|
||||
mask_ = mask_single_bit(bit_index & BitIndexMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true when the bit is currently 1 and false otherwise.
|
||||
*/
|
||||
bool test() const
|
||||
{
|
||||
const BitInt value = *int_;
|
||||
const BitInt masked_value = value & mask_;
|
||||
return masked_value != 0;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->test();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to #BitRef, but also allows changing the referenced bit.
|
||||
*/
|
||||
class MutableBitRef {
|
||||
private:
|
||||
/** Points to the integer that the bit is in. */
|
||||
BitInt *int_;
|
||||
/** All zeros except for a single one at the bit that is referenced. */
|
||||
BitInt mask_;
|
||||
|
||||
public:
|
||||
MutableBitRef() = default;
|
||||
|
||||
/**
|
||||
* Reference a specific bit in an array. Note that #data does *not* have to point to the
|
||||
* exact int the bit is in.
|
||||
*/
|
||||
MutableBitRef(BitInt *data, const int64_t bit_index)
|
||||
{
|
||||
int_ = int_containing_bit(data, bit_index);
|
||||
mask_ = mask_single_bit(bit_index & BitIndexMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Support implicitly casting to a read-only #BitRef.
|
||||
*/
|
||||
operator BitRef() const
|
||||
{
|
||||
BitRef bit_ref;
|
||||
bit_ref.int_ = int_;
|
||||
bit_ref.mask_ = mask_;
|
||||
return bit_ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true when the bit is currently 1 and false otherwise.
|
||||
*/
|
||||
bool test() const
|
||||
{
|
||||
const BitInt value = *int_;
|
||||
const BitInt masked_value = value & mask_;
|
||||
return masked_value != 0;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the bit to a 1.
|
||||
*/
|
||||
void set()
|
||||
{
|
||||
*int_ |= mask_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the bit to a 0.
|
||||
*/
|
||||
void reset()
|
||||
{
|
||||
*int_ &= ~mask_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the bit to a 1 if #value is true and 0 otherwise. If the value is highly unpredictable
|
||||
* by the CPU branch predictor, it can be faster to use #set_branchless instead.
|
||||
*/
|
||||
void set(const bool value)
|
||||
{
|
||||
if (value) {
|
||||
this->set();
|
||||
}
|
||||
else {
|
||||
this->reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the same as #set, but does not use a branch. This is faster when the input value is
|
||||
* unpredictable for the CPU branch predictor (best case for this function is a uniform random
|
||||
* distribution with 50% probability for true and false). If the value is predictable, this is
|
||||
* likely slower than #set.
|
||||
*/
|
||||
void set_branchless(const bool value)
|
||||
{
|
||||
const BitInt value_int = BitInt(value);
|
||||
BLI_assert(ELEM(value_int, 0, 1));
|
||||
const BitInt old = *int_;
|
||||
*int_ =
|
||||
/* Unset bit. */
|
||||
(~mask_ & old)
|
||||
/* Optionally set it again. The -1 turns a 1 into `0x00...` and a 0 into `0xff...`. */
|
||||
| (mask_ & ~(value_int - 1));
|
||||
}
|
||||
|
||||
MutableBitRef &operator|=(const bool value)
|
||||
{
|
||||
if (value) {
|
||||
this->set();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
MutableBitRef &operator&=(const bool value)
|
||||
{
|
||||
if (!value) {
|
||||
this->reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, const BitRef &bit);
|
||||
std::ostream &operator<<(std::ostream &stream, const MutableBitRef &bit);
|
||||
|
||||
} // namespace bits
|
||||
|
||||
using bits::BitRef;
|
||||
using bits::MutableBitRef;
|
||||
|
||||
} // namespace blender
|
||||
486
blender-5.2.0/source/blender/blenlib/BLI_bit_span.hh
Normal file
486
blender-5.2.0/source/blender/blenlib/BLI_bit_span.hh
Normal file
@@ -0,0 +1,486 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_bit_ref.hh"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_math_bits.h"
|
||||
#include "BLI_memory_utils.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace bits {
|
||||
|
||||
/** Base class for a const and non-const bit-iterator. */
|
||||
class BitIteratorBase {
|
||||
protected:
|
||||
const BitInt *data_;
|
||||
int64_t bit_index_;
|
||||
|
||||
public:
|
||||
BitIteratorBase(const BitInt *data, const int64_t bit_index) : data_(data), bit_index_(bit_index)
|
||||
{
|
||||
}
|
||||
|
||||
BitIteratorBase &operator++()
|
||||
{
|
||||
bit_index_++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator!=(const BitIteratorBase &a, const BitIteratorBase &b)
|
||||
{
|
||||
BLI_assert(a.data_ == b.data_);
|
||||
return a.bit_index_ != b.bit_index_;
|
||||
}
|
||||
};
|
||||
|
||||
/** Allows iterating over the bits in a memory buffer. */
|
||||
class BitIterator : public BitIteratorBase {
|
||||
public:
|
||||
BitIterator(const BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index) {}
|
||||
|
||||
BitRef operator*() const
|
||||
{
|
||||
return BitRef(data_, bit_index_);
|
||||
}
|
||||
};
|
||||
|
||||
/** Allows iterating over the bits in a memory buffer. */
|
||||
class MutableBitIterator : public BitIteratorBase {
|
||||
public:
|
||||
MutableBitIterator(BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index) {}
|
||||
|
||||
MutableBitRef operator*() const
|
||||
{
|
||||
return MutableBitRef(const_cast<BitInt *>(data_), bit_index_);
|
||||
}
|
||||
};
|
||||
|
||||
class OneBitIterator {
|
||||
private:
|
||||
BitInt data_;
|
||||
int current_bit_;
|
||||
|
||||
public:
|
||||
explicit OneBitIterator(BitInt data) : data_(data)
|
||||
{
|
||||
this->operator++();
|
||||
}
|
||||
|
||||
int operator*() const
|
||||
{
|
||||
return current_bit_;
|
||||
}
|
||||
|
||||
OneBitIterator &operator++()
|
||||
{
|
||||
if (data_ > 0) {
|
||||
current_bit_ = bitscan_forward_clear_uint64(&data_);
|
||||
}
|
||||
else {
|
||||
current_bit_ = -1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator!=(const OneBitIterator &a, const OneBitIterator &b)
|
||||
{
|
||||
return a.current_bit_ != b.current_bit_;
|
||||
}
|
||||
};
|
||||
|
||||
class OneBitIteratorRange {
|
||||
private:
|
||||
const BitInt data_;
|
||||
|
||||
public:
|
||||
OneBitIteratorRange(BitInt data) : data_(data) {}
|
||||
|
||||
OneBitIterator begin() const
|
||||
{
|
||||
return OneBitIterator(data_);
|
||||
}
|
||||
OneBitIterator end() const
|
||||
{
|
||||
return OneBitIterator(0);
|
||||
}
|
||||
};
|
||||
|
||||
inline OneBitIteratorRange iter_1_indices(BitInt value)
|
||||
{
|
||||
return OneBitIteratorRange(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to #Span, but references a range of bits instead of normal C++ types (which must be at
|
||||
* least one byte large). Use #MutableBitSpan if the values are supposed to be modified.
|
||||
*
|
||||
* The beginning and end of a #BitSpan does *not* have to be at byte/int boundaries. It can start
|
||||
* and end at any bit.
|
||||
*/
|
||||
class BitSpan {
|
||||
protected:
|
||||
/** Base pointer to the integers containing the bits. The actual bit span might start at a much
|
||||
* higher address when `bit_range_.start()` is large. */
|
||||
const BitInt *data_ = nullptr;
|
||||
/** The range of referenced bits. */
|
||||
IndexRange bit_range_ = {0, 0};
|
||||
|
||||
public:
|
||||
/** Construct an empty span. */
|
||||
BitSpan() = default;
|
||||
|
||||
BitSpan(const BitInt *data, const int64_t size_in_bits) : data_(data), bit_range_(size_in_bits)
|
||||
{
|
||||
}
|
||||
|
||||
BitSpan(const BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) {}
|
||||
|
||||
/** Number of bits referenced by the span. */
|
||||
int64_t size() const
|
||||
{
|
||||
return bit_range_.size();
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return bit_range_.is_empty();
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(bit_range_.size());
|
||||
}
|
||||
|
||||
[[nodiscard]] BitRef operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < bit_range_.size());
|
||||
return {data_, bit_range_.start() + index};
|
||||
}
|
||||
|
||||
[[nodiscard]] BitSpan slice(const IndexRange range) const
|
||||
{
|
||||
return {data_, bit_range_.slice(range)};
|
||||
}
|
||||
|
||||
BitSpan take_front(const int64_t n) const
|
||||
{
|
||||
return {data_, bit_range_.take_front(n)};
|
||||
}
|
||||
|
||||
BitSpan take_back(const int64_t n) const
|
||||
{
|
||||
return {data_, bit_range_.take_back(n)};
|
||||
}
|
||||
|
||||
BitSpan drop_front(const int64_t n) const
|
||||
{
|
||||
return {data_, bit_range_.drop_front(n)};
|
||||
}
|
||||
|
||||
BitSpan drop_back(const int64_t n) const
|
||||
{
|
||||
return {data_, bit_range_.drop_back(n)};
|
||||
}
|
||||
|
||||
const BitInt *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const IndexRange &bit_range() const
|
||||
{
|
||||
return bit_range_;
|
||||
}
|
||||
|
||||
BitIterator begin() const
|
||||
{
|
||||
return {data_, bit_range_.start()};
|
||||
}
|
||||
|
||||
BitIterator end() const
|
||||
{
|
||||
return {data_, bit_range_.one_after_last()};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the span fulfills the requirements for a bounded span. Bounded spans can often be
|
||||
* processed more efficiently, because fewer cases have to be considered when aligning multiple
|
||||
* such spans.
|
||||
*
|
||||
* See comments in the function for the exact requirements.
|
||||
*/
|
||||
inline bool is_bounded_span(const BitSpan span)
|
||||
{
|
||||
const int64_t offset = span.bit_range().start();
|
||||
const int64_t size = span.size();
|
||||
if (offset >= BitsPerInt) {
|
||||
/* The data pointer must point at the first int already. If the offset is a multiple of
|
||||
* #BitsPerInt, the bit span could theoretically become bounded as well if the data pointer is
|
||||
* adjusted. But that is not handled here. */
|
||||
return false;
|
||||
}
|
||||
if (size < BitsPerInt) {
|
||||
/** Don't allow small sized spans to cross `BitInt` boundaries. */
|
||||
return offset + size <= 64;
|
||||
}
|
||||
if (offset != 0) {
|
||||
/* Start of larger spans must be aligned to `BitInt` boundaries. */
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as #BitSpan but fulfills the requirements mentioned on #is_bounded_span.
|
||||
*/
|
||||
class BoundedBitSpan : public BitSpan {
|
||||
public:
|
||||
BoundedBitSpan() = default;
|
||||
|
||||
BoundedBitSpan(const BitInt *data, const int64_t size_in_bits) : BitSpan(data, size_in_bits)
|
||||
{
|
||||
BLI_assert(is_bounded_span(*this));
|
||||
}
|
||||
|
||||
BoundedBitSpan(const BitInt *data, const IndexRange bit_range) : BitSpan(data, bit_range)
|
||||
{
|
||||
BLI_assert(is_bounded_span(*this));
|
||||
}
|
||||
|
||||
explicit BoundedBitSpan(const BitSpan other) : BitSpan(other)
|
||||
{
|
||||
BLI_assert(is_bounded_span(*this));
|
||||
}
|
||||
|
||||
int64_t offset() const
|
||||
{
|
||||
return bit_range_.start();
|
||||
}
|
||||
|
||||
int64_t full_ints_num() const
|
||||
{
|
||||
return bit_range_.size() >> BitToIntIndexShift;
|
||||
}
|
||||
|
||||
int64_t final_bits_num() const
|
||||
{
|
||||
return bit_range_.size() & BitIndexMask;
|
||||
}
|
||||
|
||||
BoundedBitSpan take_front(const int64_t n) const
|
||||
{
|
||||
return {data_, bit_range_.take_front(n)};
|
||||
}
|
||||
};
|
||||
|
||||
/** Same as #BitSpan, but also allows modifying the referenced bits. */
|
||||
class MutableBitSpan {
|
||||
protected:
|
||||
BitInt *data_ = nullptr;
|
||||
IndexRange bit_range_ = {0, 0};
|
||||
|
||||
public:
|
||||
MutableBitSpan() = default;
|
||||
|
||||
MutableBitSpan(BitInt *data, const int64_t size) : data_(data), bit_range_(size) {}
|
||||
|
||||
MutableBitSpan(BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) {}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return bit_range_.size();
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return bit_range_.is_empty();
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(bit_range_.size());
|
||||
}
|
||||
|
||||
MutableBitRef operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < bit_range_.size());
|
||||
return {data_, bit_range_.start() + index};
|
||||
}
|
||||
|
||||
MutableBitSpan slice(const IndexRange range) const
|
||||
{
|
||||
return {data_, bit_range_.slice(range)};
|
||||
}
|
||||
|
||||
MutableBitSpan take_front(const int64_t n) const
|
||||
{
|
||||
return {data_, bit_range_.take_front(n)};
|
||||
}
|
||||
|
||||
MutableBitSpan take_back(const int64_t n) const
|
||||
{
|
||||
return {data_, bit_range_.take_back(n)};
|
||||
}
|
||||
|
||||
BitInt *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const IndexRange &bit_range() const
|
||||
{
|
||||
return bit_range_;
|
||||
}
|
||||
|
||||
MutableBitIterator begin() const
|
||||
{
|
||||
return {data_, bit_range_.start()};
|
||||
}
|
||||
|
||||
MutableBitIterator end() const
|
||||
{
|
||||
return {data_, bit_range_.one_after_last()};
|
||||
}
|
||||
|
||||
operator BitSpan() const
|
||||
{
|
||||
return {data_, bit_range_};
|
||||
}
|
||||
|
||||
/** Sets all referenced bits to 1. */
|
||||
void set_all();
|
||||
|
||||
/** Sets all referenced bits to 0. */
|
||||
void reset_all();
|
||||
|
||||
void copy_from(const BitSpan other);
|
||||
void copy_from(const BoundedBitSpan other);
|
||||
|
||||
/** Sets all referenced bits to either 0 or 1. */
|
||||
void set_all(const bool value)
|
||||
{
|
||||
if (value) {
|
||||
this->set_all();
|
||||
}
|
||||
else {
|
||||
this->reset_all();
|
||||
}
|
||||
}
|
||||
|
||||
/** Same as #set_all to mirror #MutableSpan. */
|
||||
void fill(const bool value)
|
||||
{
|
||||
this->set_all(value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Same as #MutableBitSpan but fulfills the requirements mentioned on #is_bounded_span.
|
||||
*/
|
||||
class MutableBoundedBitSpan : public MutableBitSpan {
|
||||
public:
|
||||
MutableBoundedBitSpan() = default;
|
||||
|
||||
MutableBoundedBitSpan(BitInt *data, const int64_t size) : MutableBitSpan(data, size)
|
||||
{
|
||||
BLI_assert(is_bounded_span(*this));
|
||||
}
|
||||
|
||||
MutableBoundedBitSpan(BitInt *data, const IndexRange bit_range) : MutableBitSpan(data, bit_range)
|
||||
{
|
||||
BLI_assert(is_bounded_span(*this));
|
||||
}
|
||||
|
||||
explicit MutableBoundedBitSpan(const MutableBitSpan other) : MutableBitSpan(other)
|
||||
{
|
||||
BLI_assert(is_bounded_span(*this));
|
||||
}
|
||||
|
||||
operator BoundedBitSpan() const
|
||||
{
|
||||
return BoundedBitSpan{BitSpan(*this)};
|
||||
}
|
||||
|
||||
int64_t offset() const
|
||||
{
|
||||
return bit_range_.start();
|
||||
}
|
||||
|
||||
int64_t full_ints_num() const
|
||||
{
|
||||
return bit_range_.size() >> BitToIntIndexShift;
|
||||
}
|
||||
|
||||
int64_t final_bits_num() const
|
||||
{
|
||||
return bit_range_.size() & BitIndexMask;
|
||||
}
|
||||
|
||||
MutableBoundedBitSpan take_front(const int64_t n) const
|
||||
{
|
||||
return {data_, bit_range_.take_front(n)};
|
||||
}
|
||||
|
||||
BoundedBitSpan as_span() const
|
||||
{
|
||||
return BoundedBitSpan(data_, bit_range_);
|
||||
}
|
||||
|
||||
void copy_from(const BitSpan other);
|
||||
void copy_from(const BoundedBitSpan other);
|
||||
};
|
||||
|
||||
inline std::optional<BoundedBitSpan> try_get_bounded_span(const BitSpan span)
|
||||
{
|
||||
if (is_bounded_span(span)) {
|
||||
return BoundedBitSpan(span);
|
||||
}
|
||||
if (span.bit_range().start() % BitsPerInt == 0) {
|
||||
return BoundedBitSpan(span.data() + (span.bit_range().start() >> BitToIntIndexShift),
|
||||
span.size());
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloaded in BLI_bit_vector.hh. The purpose is to make passing #BitVector into bit span
|
||||
* operations more efficient (interpreting it as `BoundedBitSpan` instead of just `BitSpan`).
|
||||
*/
|
||||
template<typename T> inline T to_best_bit_span(const T &data)
|
||||
{
|
||||
static_assert(is_same_any_v<std::decay_t<T>,
|
||||
BitSpan,
|
||||
MutableBitSpan,
|
||||
BoundedBitSpan,
|
||||
MutableBoundedBitSpan>);
|
||||
return data;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
constexpr bool all_bounded_spans =
|
||||
(is_same_any_v<std::decay_t<Args>, BoundedBitSpan, MutableBoundedBitSpan> && ...);
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, const BitSpan &span);
|
||||
std::ostream &operator<<(std::ostream &stream, const MutableBitSpan &span);
|
||||
|
||||
} // namespace bits
|
||||
|
||||
using bits::BitSpan;
|
||||
using bits::BoundedBitSpan;
|
||||
using bits::MutableBitSpan;
|
||||
using bits::MutableBoundedBitSpan;
|
||||
|
||||
} // namespace blender
|
||||
369
blender-5.2.0/source/blender/blenlib/BLI_bit_span_ops.hh
Normal file
369
blender-5.2.0/source/blender/blenlib/BLI_bit_span_ops.hh
Normal file
@@ -0,0 +1,369 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_bit_span.hh"
|
||||
#include "BLI_math_bits.h"
|
||||
|
||||
namespace blender::bits {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Evaluates the expression on one or more bit spans and stores the result in the first.
|
||||
*
|
||||
* The expected type for the expression is:
|
||||
* (BitInt ...one_or_more_args) -> BitInt
|
||||
*/
|
||||
template<typename ExprFn, typename FirstBitSpanT, typename... BitSpanT>
|
||||
inline void mix_into_first_expr(ExprFn &&expr,
|
||||
const FirstBitSpanT &first_arg,
|
||||
const BitSpanT &...args)
|
||||
{
|
||||
const int64_t size = first_arg.size();
|
||||
BLI_assert(((size == args.size()) && ...));
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (all_bounded_spans<FirstBitSpanT, BitSpanT...>) {
|
||||
BitInt *first_data = first_arg.data();
|
||||
const int64_t first_offset = first_arg.offset();
|
||||
const int64_t full_ints_num = first_arg.full_ints_num();
|
||||
/* Compute expression without any masking, all the spans are expected to be aligned to the
|
||||
* beginning of a #BitInt. */
|
||||
for (const int64_t i : IndexRange(full_ints_num)) {
|
||||
first_data[i] = expr(first_data[i], args.data()[i]...);
|
||||
}
|
||||
/* Compute expression for the remaining bits. */
|
||||
if (const int64_t final_bits = first_arg.final_bits_num()) {
|
||||
const BitInt result = expr(first_data[full_ints_num] >> first_offset,
|
||||
(args.data()[full_ints_num] >> args.offset())...);
|
||||
const BitInt mask = mask_range_bits(first_offset, final_bits);
|
||||
first_data[full_ints_num] = ((result << first_offset) & mask) |
|
||||
(first_data[full_ints_num] & ~mask);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Fallback for arbitrary bit spans. This could be implemented more efficiently but adds more
|
||||
* complexity and is not necessary yet. */
|
||||
for (const int64_t i : IndexRange(size)) {
|
||||
const bool result = expr(BitInt(first_arg[i].test()), BitInt(args[i].test())...) != 0;
|
||||
first_arg[i].set(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the expression on one or more bit spans and returns true when the result contains a 1
|
||||
* anywhere.
|
||||
*
|
||||
* The expected type for the expression is:
|
||||
* (BitInt ...one_or_more_args) -> BitInt
|
||||
*/
|
||||
template<typename ExprFn, typename FirstBitSpanT, typename... BitSpanT>
|
||||
inline bool any_set_expr(ExprFn &&expr, const FirstBitSpanT &first_arg, const BitSpanT &...args)
|
||||
{
|
||||
const int64_t size = first_arg.size();
|
||||
BLI_assert(((size == args.size()) && ...));
|
||||
if (size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (all_bounded_spans<FirstBitSpanT, BitSpanT...>) {
|
||||
const BitInt *first_data = first_arg.data();
|
||||
const int64_t full_ints_num = first_arg.full_ints_num();
|
||||
/* Compute expression without any masking, all the spans are expected to be aligned to the
|
||||
* beginning of a #BitInt. */
|
||||
for (const int64_t i : IndexRange(full_ints_num)) {
|
||||
if (expr(first_data[i], args.data()[i]...) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/* Compute expression for the remaining bits. */
|
||||
if (const int64_t final_bits = first_arg.final_bits_num()) {
|
||||
const BitInt result = expr(first_data[full_ints_num] >> first_arg.offset(),
|
||||
(args.data()[full_ints_num] >> args.offset())...);
|
||||
const BitInt mask = mask_first_n_bits(final_bits);
|
||||
if ((result & mask) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
/* Fallback for arbitrary bit spans. This could be implemented more efficiently but adds more
|
||||
* complexity and is not necessary yet. */
|
||||
for (const int64_t i : IndexRange(size)) {
|
||||
const BitInt result = expr(BitInt(first_arg[i].test()), BitInt(args[i].test())...);
|
||||
if (result & 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the expression on one or more bit spans and calls the `handle` function for each bit
|
||||
* index where the result is 1.
|
||||
*
|
||||
* The expected type for the expression is:
|
||||
* (BitInt ...one_or_more_args) -> BitInt
|
||||
*/
|
||||
template<typename ExprFn, typename HandleFn, typename FirstBitSpanT, typename... BitSpanT>
|
||||
inline void foreach_1_index_expr(ExprFn &&expr,
|
||||
HandleFn &&handle,
|
||||
const FirstBitSpanT &first_arg,
|
||||
const BitSpanT &...args)
|
||||
{
|
||||
static_assert(std::is_invocable_v<HandleFn, int64_t>);
|
||||
constexpr bool is_cancellable = std::is_invocable_r_v<bool, HandleFn, int64_t>;
|
||||
|
||||
const int64_t size = first_arg.size();
|
||||
BLI_assert(((size == args.size()) && ...));
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (all_bounded_spans<FirstBitSpanT, BitSpanT...>) {
|
||||
const BitInt *first_data = first_arg.data();
|
||||
const int64_t full_ints_num = first_arg.full_ints_num();
|
||||
/* Iterate over full ints without any bit masks. */
|
||||
for (const int64_t int_i : IndexRange(full_ints_num)) {
|
||||
BitInt tmp = expr(first_data[int_i], args.data()[int_i]...);
|
||||
const int64_t offset = int_i << BitToIntIndexShift;
|
||||
while (tmp != 0) {
|
||||
static_assert(std::is_same_v<BitInt, uint64_t>);
|
||||
const int index_in_int = bitscan_forward_uint64(tmp);
|
||||
const int64_t index_in_span = index_in_int + offset;
|
||||
if constexpr (is_cancellable) {
|
||||
if (!handle(index_in_span)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
handle(index_in_span);
|
||||
}
|
||||
tmp &= ~mask_single_bit(index_in_int);
|
||||
}
|
||||
}
|
||||
/* Iterate over remaining bits. */
|
||||
if (const int64_t final_bits = first_arg.final_bits_num()) {
|
||||
BitInt tmp = expr(first_data[full_ints_num] >> first_arg.offset(),
|
||||
(args.data()[full_ints_num] >> args.offset())...) &
|
||||
mask_first_n_bits(final_bits);
|
||||
const int64_t offset = full_ints_num << BitToIntIndexShift;
|
||||
while (tmp != 0) {
|
||||
static_assert(std::is_same_v<BitInt, uint64_t>);
|
||||
const int index_in_int = bitscan_forward_uint64(tmp);
|
||||
const int64_t index_in_span = index_in_int + offset;
|
||||
if constexpr (is_cancellable) {
|
||||
if (!handle(index_in_span)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
handle(index_in_span);
|
||||
}
|
||||
tmp &= ~mask_single_bit(index_in_int);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Fallback for arbitrary bit spans. This could be implemented more efficiently but adds more
|
||||
* complexity and is not necessary yet. */
|
||||
for (const int64_t i : IndexRange(size)) {
|
||||
const BitInt result = expr(BitInt(first_arg[i].test()), BitInt(args[i].test())...);
|
||||
if (result & 1) {
|
||||
if constexpr (is_cancellable) {
|
||||
if (!handle(i)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
handle(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ExprFn, typename FirstBitSpanT, typename... BitSpanT>
|
||||
inline std::optional<int64_t> find_first_1_index_expr(ExprFn &&expr,
|
||||
const FirstBitSpanT &first_arg,
|
||||
const BitSpanT &...args)
|
||||
{
|
||||
std::optional<int64_t> result;
|
||||
detail::foreach_1_index_expr(
|
||||
expr,
|
||||
[&](const int64_t i) {
|
||||
result = i;
|
||||
return false;
|
||||
},
|
||||
first_arg,
|
||||
args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename ExprFn, typename FirstBitSpanT, typename... BitSpanT>
|
||||
inline void mix_into_first_expr(ExprFn &&expr, FirstBitSpanT &&first_arg, const BitSpanT &...args)
|
||||
{
|
||||
detail::mix_into_first_expr(expr, to_best_bit_span(first_arg), to_best_bit_span(args)...);
|
||||
}
|
||||
|
||||
template<typename ExprFn, typename FirstBitSpanT, typename... BitSpanT>
|
||||
inline bool any_set_expr(ExprFn &&expr, const FirstBitSpanT &first_arg, const BitSpanT &...args)
|
||||
{
|
||||
return detail::any_set_expr(expr, to_best_bit_span(first_arg), to_best_bit_span(args)...);
|
||||
}
|
||||
|
||||
template<typename ExprFn, typename HandleFn, typename FirstBitSpanT, typename... BitSpanT>
|
||||
inline void foreach_1_index_expr(ExprFn &&expr,
|
||||
HandleFn &&handle,
|
||||
const FirstBitSpanT &first_arg,
|
||||
const BitSpanT &...args)
|
||||
{
|
||||
detail::foreach_1_index_expr(
|
||||
expr, handle, to_best_bit_span(first_arg), to_best_bit_span(args)...);
|
||||
}
|
||||
|
||||
template<typename BitSpanT> inline void invert(BitSpanT &&data)
|
||||
{
|
||||
mix_into_first_expr([](const BitInt x) { return ~x; }, data);
|
||||
}
|
||||
|
||||
template<typename FirstBitSpanT, typename... BitSpanT>
|
||||
inline void inplace_or(FirstBitSpanT &first_arg, const BitSpanT &...args)
|
||||
{
|
||||
mix_into_first_expr([](const auto... x) { return (x | ...); }, first_arg, args...);
|
||||
}
|
||||
|
||||
template<typename FirstBitSpanT, typename MaskBitSpanT, typename... BitSpanT>
|
||||
inline void inplace_or_masked(FirstBitSpanT &&first_arg,
|
||||
const MaskBitSpanT &mask,
|
||||
const BitSpanT &...args)
|
||||
{
|
||||
mix_into_first_expr(
|
||||
[](const BitInt a, const BitInt mask, const auto... x) { return a | ((x | ...) & mask); },
|
||||
first_arg,
|
||||
mask,
|
||||
args...);
|
||||
}
|
||||
|
||||
template<typename FirstBitSpanT, typename... BitSpanT>
|
||||
inline void copy_from_or(FirstBitSpanT &first_arg, const BitSpanT &...args)
|
||||
{
|
||||
mix_into_first_expr(
|
||||
[](auto /*first*/, auto... rest) { return (rest | ...); }, first_arg, args...);
|
||||
}
|
||||
|
||||
template<typename FirstBitSpanT, typename... BitSpanT>
|
||||
inline void inplace_and(FirstBitSpanT &first_arg, const BitSpanT &...args)
|
||||
{
|
||||
mix_into_first_expr([](const auto... x) { return (x & ...); }, first_arg, args...);
|
||||
}
|
||||
|
||||
template<typename... BitSpanT>
|
||||
inline void operator|=(MutableBitSpan first_arg, const BitSpanT &...args)
|
||||
{
|
||||
inplace_or(first_arg, args...);
|
||||
}
|
||||
|
||||
template<typename... BitSpanT>
|
||||
inline void operator|=(MutableBoundedBitSpan first_arg, const BitSpanT &...args)
|
||||
{
|
||||
inplace_or(first_arg, args...);
|
||||
}
|
||||
|
||||
template<typename... BitSpanT>
|
||||
inline void operator&=(MutableBitSpan first_arg, const BitSpanT &...args)
|
||||
{
|
||||
inplace_and(first_arg, args...);
|
||||
}
|
||||
|
||||
template<typename... BitSpanT>
|
||||
inline void operator&=(MutableBoundedBitSpan first_arg, const BitSpanT &...args)
|
||||
{
|
||||
inplace_and(first_arg, args...);
|
||||
}
|
||||
|
||||
template<typename... BitSpanT> inline bool has_common_set_bits(const BitSpanT &...args)
|
||||
{
|
||||
return any_set_expr([](const auto... x) { return (x & ...); }, args...);
|
||||
}
|
||||
|
||||
template<typename BitSpanT> inline bool any_bit_set(const BitSpanT &arg)
|
||||
{
|
||||
return has_common_set_bits(arg);
|
||||
}
|
||||
|
||||
template<typename... BitSpanT> inline bool has_common_unset_bits(const BitSpanT &...args)
|
||||
{
|
||||
return any_set_expr([](const auto... x) { return ~(x | ...); }, args...);
|
||||
}
|
||||
|
||||
template<typename BitSpanT> inline bool any_bit_unset(const BitSpanT &arg)
|
||||
{
|
||||
return has_common_unset_bits(arg);
|
||||
}
|
||||
|
||||
template<typename BitSpanT, typename Fn> inline void foreach_1_index(const BitSpanT &data, Fn &&fn)
|
||||
{
|
||||
foreach_1_index_expr([](const BitInt x) { return x; }, fn, data);
|
||||
}
|
||||
|
||||
template<typename BitSpanT, typename Fn> inline void foreach_0_index(const BitSpanT &data, Fn &&fn)
|
||||
{
|
||||
foreach_1_index_expr([](const BitInt x) { return ~x; }, fn, data);
|
||||
}
|
||||
|
||||
template<typename ExprFn, typename FirstBitSpanT, typename... BitSpanT>
|
||||
inline std::optional<int64_t> find_first_1_index_expr(ExprFn &&Expr,
|
||||
const FirstBitSpanT &first_arg,
|
||||
const BitSpanT &...args)
|
||||
{
|
||||
return detail::find_first_1_index_expr(
|
||||
Expr, to_best_bit_span(first_arg), to_best_bit_span(args)...);
|
||||
}
|
||||
|
||||
template<typename BitSpanT> inline std::optional<int64_t> find_first_1_index(const BitSpanT &data)
|
||||
{
|
||||
return find_first_1_index_expr([](const BitInt x) { return x; }, data);
|
||||
}
|
||||
|
||||
template<typename BitSpanT> inline std::optional<int64_t> find_first_0_index(const BitSpanT &data)
|
||||
{
|
||||
return find_first_1_index_expr([](const BitInt x) { return ~x; }, data);
|
||||
}
|
||||
|
||||
template<typename BitSpanT1, typename BitSpanT2>
|
||||
inline bool spans_equal(const BitSpanT1 &a, const BitSpanT2 &b)
|
||||
{
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
return !any_set_expr([](const BitInt a, const BitInt b) { return a ^ b; }, a, b);
|
||||
}
|
||||
|
||||
template<typename BitSpanT1, typename BitSpanT2, typename BitSpanT3>
|
||||
inline bool spans_equal_masked(const BitSpanT1 &a, const BitSpanT2 &b, const BitSpanT3 &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == a.size());
|
||||
BLI_assert(mask.size() == b.size());
|
||||
return !bits::any_set_expr(
|
||||
[](const BitInt a, const BitInt b, const BitInt mask) { return (a ^ b) & mask; },
|
||||
a,
|
||||
b,
|
||||
mask);
|
||||
}
|
||||
|
||||
} // namespace blender::bits
|
||||
@@ -0,0 +1,140 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "BLI_bit_span.hh"
|
||||
#include "BLI_index_ranges_builder.hh"
|
||||
#include "BLI_math_bits.h"
|
||||
#include "BLI_simd.hh"
|
||||
|
||||
namespace blender::bits {
|
||||
|
||||
/**
|
||||
* Extracts index ranges from the given bits.
|
||||
* For example 00111011 would result in two ranges: [2-4], [6-7].
|
||||
*
|
||||
* It's especially optimized to handle cases where there are very many or very few set bits.
|
||||
*/
|
||||
template<typename IntT>
|
||||
inline void bits_to_index_ranges(const BitSpan bits, IndexRangesBuilder<IntT> &builder)
|
||||
{
|
||||
if (bits.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* -1 because we also need to store the end of the last range. */
|
||||
constexpr int64_t max_index = std::numeric_limits<IntT>::max() - 1;
|
||||
UNUSED_VARS_NDEBUG(max_index);
|
||||
|
||||
auto append_range = [&](const IndexRange range) {
|
||||
BLI_assert(range.last() <= max_index);
|
||||
builder.add_range(IntT(range.start()), IntT(range.one_after_last()));
|
||||
};
|
||||
|
||||
auto process_bit_int = [&](const BitInt value,
|
||||
const int64_t start_bit,
|
||||
const int64_t bits_num,
|
||||
const int64_t start) {
|
||||
/* The bits in the mask are the ones we should look at. */
|
||||
const BitInt mask = mask_range_bits(start_bit, bits_num);
|
||||
const BitInt masked_value = mask & value;
|
||||
if (masked_value == 0) {
|
||||
/* Do nothing. */
|
||||
return;
|
||||
}
|
||||
if (masked_value == mask) {
|
||||
/* All bits are set. */
|
||||
append_range(IndexRange::from_begin_size(start, bits_num));
|
||||
return;
|
||||
}
|
||||
const int64_t bit_i_to_output_offset = start - start_bit;
|
||||
|
||||
/* Iterate over ranges of 1s. For example, if the bits are 0b000111110001111000, the loop
|
||||
* below requires two iterations. The worst case for this is when there are very many small
|
||||
* ranges of 1s (e.g. 0b10101010101). So far it seems like the overhead of detecting such
|
||||
* cases is higher than the potential benefit of using another algorithm. */
|
||||
BitInt current_value = masked_value;
|
||||
while (current_value != 0) {
|
||||
/* Find start of next range of 1s. */
|
||||
const int64_t first_set_bit_i = int64_t(bitscan_forward_uint64(current_value));
|
||||
/* This mask is used to find the end of the 1s range. */
|
||||
const BitInt find_unset_value = ~(current_value | mask_first_n_bits(first_set_bit_i) |
|
||||
~mask);
|
||||
if (find_unset_value == 0) {
|
||||
/* In this case, the range one 1s extends to the end of the current integer. */
|
||||
const IndexRange range = IndexRange::from_begin_end(first_set_bit_i, start_bit + bits_num);
|
||||
append_range(range.shift(bit_i_to_output_offset));
|
||||
break;
|
||||
}
|
||||
/* Find the index of the first 0 after the range of 1s. */
|
||||
const int64_t next_unset_bit_i = int64_t(bitscan_forward_uint64(find_unset_value));
|
||||
/* Store the range of 1s. */
|
||||
const IndexRange range = IndexRange::from_begin_end(first_set_bit_i, next_unset_bit_i);
|
||||
append_range(range.shift(bit_i_to_output_offset));
|
||||
/* Remove the processed range of 1s so that it is ignored in the next iteration. */
|
||||
current_value &= ~mask_first_n_bits(next_unset_bit_i);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
const BitInt *data = bits.data();
|
||||
const IndexRange bit_range = bits.bit_range();
|
||||
|
||||
/* As much as possible we want to process full 64-bit integers at once. However, the bit-span may
|
||||
* not be aligned, so it's first split up into aligned and unaligned sections. */
|
||||
const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range, bits::BitsPerInt);
|
||||
|
||||
/* Process the first (partial) integer in the bit-span. */
|
||||
if (!ranges.prefix.is_empty()) {
|
||||
const BitInt first_int = *int_containing_bit(data, bit_range.start());
|
||||
process_bit_int(
|
||||
first_int, BitInt(ranges.prefix.start()) & BitIndexMask, ranges.prefix.size(), 0);
|
||||
}
|
||||
|
||||
/* Process all the full integers in the bit-span. */
|
||||
if (!ranges.aligned.is_empty()) {
|
||||
const BitInt *start = int_containing_bit(data, ranges.aligned.start());
|
||||
const int64_t ints_to_check = ranges.aligned.size() / BitsPerInt;
|
||||
int64_t int_i = 0;
|
||||
|
||||
/* Checking for chunks of 0 bits can be speedup using intrinsics quite significantly. */
|
||||
#if BLI_HAVE_SSE4
|
||||
for (; int_i + 1 < ints_to_check; int_i += 2) {
|
||||
/* Loads the next 128 bit. */
|
||||
const __m128i group = _mm_loadu_si128(reinterpret_cast<const __m128i *>(start + int_i));
|
||||
/* Checks if all the 128 bits are zero. */
|
||||
const bool group_is_zero = _mm_testz_si128(group, group);
|
||||
if (group_is_zero) {
|
||||
continue;
|
||||
}
|
||||
/* If at least one of them is not zero, process the two integers separately. */
|
||||
for (int j = 0; j < 2; j++) {
|
||||
process_bit_int(
|
||||
start[int_i + j], 0, BitsPerInt, ranges.prefix.size() + (int_i + j) * BitsPerInt);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Process the remaining integers. */
|
||||
for (; int_i < ints_to_check; int_i++) {
|
||||
process_bit_int(start[int_i], 0, BitsPerInt, ranges.prefix.size() + int_i * BitsPerInt);
|
||||
}
|
||||
}
|
||||
|
||||
/* Process the final few bits that don't fill up a full integer. */
|
||||
if (!ranges.suffix.is_empty()) {
|
||||
const BitInt last_int = *int_containing_bit(data, bit_range.last());
|
||||
process_bit_int(
|
||||
last_int, 0, ranges.suffix.size(), ranges.prefix.size() + ranges.aligned.size());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::bits
|
||||
406
blender-5.2.0/source/blender/blenlib/BLI_bit_vector.hh
Normal file
406
blender-5.2.0/source/blender/blenlib/BLI_bit_vector.hh
Normal file
@@ -0,0 +1,406 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A `BitVector` is a dynamically growing contiguous arrays of bits. Its main purpose is
|
||||
* to provide a compact way to map indices to bools. It requires 8 times less memory compared to a
|
||||
* `Vector<bool>`.
|
||||
*
|
||||
* Advantages of using a bit- instead of byte-vector are:
|
||||
* - Uses less memory.
|
||||
* - Allows checking the state of many elements at the same time (8 times more bits than bytes fit
|
||||
* into a CPU register). This can improve performance.
|
||||
*
|
||||
* The compact nature of storing bools in individual bits has some downsides that have to be kept
|
||||
* in mind:
|
||||
* - Writing to separate bits in the same int is not thread-safe. Therefore, an existing vector of
|
||||
* bool can't easily be replaced with a bit vector, if it is written to from multiple threads.
|
||||
* Read-only access from multiple threads is fine though.
|
||||
* - Writing individual elements is more expensive when the array is in cache already. That is
|
||||
* because changing a bit is always a read-modify-write operation on the int the bit resides in.
|
||||
* - Reading individual elements is more expensive when the array is in cache already. That is
|
||||
* because additional bit-wise operations have to be applied after the corresponding int is
|
||||
* read.
|
||||
*
|
||||
* Comparison to `std::vector<bool>`:
|
||||
* - `BitVector` has an interface that is more optimized for dealing with bits.
|
||||
* - `BitVector` has an inline buffer that is used to avoid allocations when the vector is
|
||||
* small.
|
||||
*
|
||||
* Comparison to `BLI_bitmap`:
|
||||
* - `BitVector` offers a more C++ friendly interface.
|
||||
* - `BLI_bitmap` should only be used in C code that can not use `BitVector`.
|
||||
*/
|
||||
|
||||
#include "BLI_allocator.hh"
|
||||
#include "BLI_bit_bool_conversion.hh"
|
||||
#include "BLI_bit_span.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace bits {
|
||||
|
||||
template<
|
||||
/**
|
||||
* Number of bits that can be stored in the vector without doing an allocation.
|
||||
*/
|
||||
int64_t InlineBufferCapacity = 64,
|
||||
/**
|
||||
* The allocator used by this vector. Should rarely be changed, except when you don't want that
|
||||
* MEM_* is used internally.
|
||||
*/
|
||||
typename Allocator = GuardedAllocator>
|
||||
class BitVector {
|
||||
private:
|
||||
static constexpr int64_t required_ints_for_bits(const int64_t number_of_bits)
|
||||
{
|
||||
return (number_of_bits + BitsPerInt - 1) / BitsPerInt;
|
||||
}
|
||||
|
||||
static constexpr int64_t IntsInInlineBuffer = required_ints_for_bits(InlineBufferCapacity);
|
||||
static constexpr int64_t BitsInInlineBuffer = IntsInInlineBuffer * BitsPerInt;
|
||||
static constexpr int64_t AllocationAlignment = alignof(BitInt);
|
||||
|
||||
/**
|
||||
* Points to the first integer used by the vector. It might point to the memory in the inline
|
||||
* buffer.
|
||||
*/
|
||||
BitInt *data_;
|
||||
|
||||
/** Current size of the vector in bits. */
|
||||
int64_t size_in_bits_;
|
||||
|
||||
/** Number of bits that fit into the vector until a reallocation has to occur. */
|
||||
int64_t capacity_in_bits_;
|
||||
|
||||
/** Used for allocations when the inline buffer is too small. */
|
||||
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
|
||||
|
||||
/** Contains the bits as long as the vector is small enough. */
|
||||
BLI_NO_UNIQUE_ADDRESS TypedBuffer<BitInt, IntsInInlineBuffer> inline_buffer_;
|
||||
|
||||
public:
|
||||
BitVector(Allocator allocator = {}) noexcept : allocator_(allocator)
|
||||
{
|
||||
data_ = inline_buffer_;
|
||||
size_in_bits_ = 0;
|
||||
capacity_in_bits_ = BitsInInlineBuffer;
|
||||
uninitialized_fill_n(data_, IntsInInlineBuffer, BitInt(0));
|
||||
}
|
||||
|
||||
BitVector(NoExceptConstructor, Allocator allocator = {}) noexcept : BitVector(allocator) {}
|
||||
|
||||
BitVector(const BoundedBitSpan span) : BitVector(NoExceptConstructor())
|
||||
{
|
||||
const int64_t ints_to_copy = required_ints_for_bits(span.size());
|
||||
if (span.size() <= BitsInInlineBuffer) {
|
||||
/* The data is copied into the owned inline buffer. */
|
||||
data_ = inline_buffer_;
|
||||
capacity_in_bits_ = BitsInInlineBuffer;
|
||||
}
|
||||
else {
|
||||
/* Allocate a new array because the inline buffer is too small. */
|
||||
data_ = static_cast<BitInt *>(
|
||||
allocator_.allocate(ints_to_copy * sizeof(BitInt), AllocationAlignment, __func__));
|
||||
capacity_in_bits_ = ints_to_copy * BitsPerInt;
|
||||
}
|
||||
size_in_bits_ = span.size();
|
||||
uninitialized_copy_n(span.data(), ints_to_copy, data_);
|
||||
}
|
||||
|
||||
BitVector(const BitVector &other) : BitVector(BoundedBitSpan(other))
|
||||
{
|
||||
allocator_ = other.allocator_;
|
||||
}
|
||||
|
||||
BitVector(BitVector &&other) noexcept : BitVector(NoExceptConstructor(), other.allocator_)
|
||||
{
|
||||
if (other.is_inline()) {
|
||||
/* Copy the data into the inline buffer. */
|
||||
/* For small inline buffers, always copy all the bits because checking how many bits to copy
|
||||
* would add additional overhead. */
|
||||
int64_t ints_to_copy = IntsInInlineBuffer;
|
||||
if constexpr (IntsInInlineBuffer > 8) {
|
||||
/* Avoid copying too much unnecessary data in case the inline buffer is large. */
|
||||
ints_to_copy = other.used_ints_amount();
|
||||
}
|
||||
data_ = inline_buffer_;
|
||||
uninitialized_copy_n(other.data_, ints_to_copy, data_);
|
||||
}
|
||||
else {
|
||||
/* Steal the pointer. */
|
||||
data_ = other.data_;
|
||||
}
|
||||
size_in_bits_ = other.size_in_bits_;
|
||||
capacity_in_bits_ = other.capacity_in_bits_;
|
||||
|
||||
/* Clear the other vector because it has been moved from. */
|
||||
other.data_ = other.inline_buffer_;
|
||||
other.size_in_bits_ = 0;
|
||||
other.capacity_in_bits_ = BitsInInlineBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new vector with the given size and fill it with #value.
|
||||
*/
|
||||
BitVector(const int64_t size_in_bits, const bool value = false, Allocator allocator = {})
|
||||
: BitVector(NoExceptConstructor(), allocator)
|
||||
{
|
||||
this->resize(size_in_bits, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bit vector based on an array of bools. Each byte of the input array maps to one bit.
|
||||
*/
|
||||
explicit BitVector(const Span<bool> values, Allocator allocator = {})
|
||||
: BitVector(NoExceptConstructor(), allocator)
|
||||
{
|
||||
this->resize(values.size(), false);
|
||||
or_bools_into_bits(values, *this);
|
||||
}
|
||||
|
||||
~BitVector()
|
||||
{
|
||||
if (!this->is_inline()) {
|
||||
allocator_.deallocate(data_);
|
||||
}
|
||||
}
|
||||
|
||||
BitVector &operator=(const BitVector &other)
|
||||
{
|
||||
return copy_assign_container(*this, other);
|
||||
}
|
||||
|
||||
BitVector &operator=(BitVector &&other)
|
||||
{
|
||||
return move_assign_container(*this, std::move(other));
|
||||
}
|
||||
|
||||
operator BoundedBitSpan() const
|
||||
{
|
||||
return {data_, IndexRange(size_in_bits_)};
|
||||
}
|
||||
|
||||
operator MutableBoundedBitSpan()
|
||||
{
|
||||
return {data_, IndexRange(size_in_bits_)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of bits in the bit vector.
|
||||
*/
|
||||
int64_t size() const
|
||||
{
|
||||
return size_in_bits_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of bits that can be stored before the BitVector has to grow.
|
||||
*/
|
||||
int64_t capacity() const
|
||||
{
|
||||
return capacity_in_bits_;
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return size_in_bits_ == 0;
|
||||
}
|
||||
|
||||
BitInt *data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const BitInt *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a read-only reference to a specific bit.
|
||||
*/
|
||||
[[nodiscard]] BitRef operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < size_in_bits_);
|
||||
return {data_, index};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a mutable reference to a specific bit.
|
||||
*/
|
||||
[[nodiscard]] MutableBitRef operator[](const int64_t index)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < size_in_bits_);
|
||||
return {data_, index};
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(size_in_bits_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new bit to the end of the vector.
|
||||
*/
|
||||
void append(const bool value)
|
||||
{
|
||||
this->ensure_space_for_one();
|
||||
MutableBitRef bit{data_, size_in_bits_};
|
||||
bit.set(value);
|
||||
size_in_bits_++;
|
||||
}
|
||||
|
||||
BitIterator begin() const
|
||||
{
|
||||
return {data_, 0};
|
||||
}
|
||||
|
||||
BitIterator end() const
|
||||
{
|
||||
return {data_, size_in_bits_};
|
||||
}
|
||||
|
||||
MutableBitIterator begin()
|
||||
{
|
||||
return {data_, 0};
|
||||
}
|
||||
|
||||
MutableBitIterator end()
|
||||
{
|
||||
return {data_, size_in_bits_};
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the size of the vector. If the new vector is larger than the old one, the new elements
|
||||
* are filled with #value.
|
||||
*/
|
||||
void resize(const int64_t new_size_in_bits, const bool value = false)
|
||||
{
|
||||
BLI_assert(new_size_in_bits >= 0);
|
||||
const int64_t old_size_in_bits = size_in_bits_;
|
||||
if (new_size_in_bits > old_size_in_bits) {
|
||||
this->reserve(new_size_in_bits);
|
||||
}
|
||||
size_in_bits_ = new_size_in_bits;
|
||||
if (old_size_in_bits < new_size_in_bits) {
|
||||
MutableBitSpan(data_, IndexRange::from_begin_end(old_size_in_bits, new_size_in_bits))
|
||||
.set_all(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set #value on every element.
|
||||
*/
|
||||
void fill(const bool value)
|
||||
{
|
||||
MutableBitSpan(data_, size_in_bits_).set_all(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the capacity of the vector is large enough to hold the given amount of bits.
|
||||
* The actual size is not changed.
|
||||
*/
|
||||
void reserve(const int new_capacity_in_bits)
|
||||
{
|
||||
this->realloc_to_at_least(new_capacity_in_bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the size of the vector to zero elements, but keep the same memory capacity to be
|
||||
* refilled again.
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
size_in_bits_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free memory and reset the vector to zero elements.
|
||||
*/
|
||||
void clear_and_shrink()
|
||||
{
|
||||
size_in_bits_ = 0;
|
||||
capacity_in_bits_ = 0;
|
||||
if (!this->is_inline()) {
|
||||
allocator_.deallocate(data_);
|
||||
}
|
||||
data_ = inline_buffer_;
|
||||
}
|
||||
|
||||
private:
|
||||
void ensure_space_for_one()
|
||||
{
|
||||
if (UNLIKELY(size_in_bits_ >= capacity_in_bits_)) {
|
||||
this->realloc_to_at_least(size_in_bits_ + 1);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_NOINLINE void realloc_to_at_least(const int64_t min_capacity_in_bits,
|
||||
const BitInt initial_value_for_new_ints = 0)
|
||||
{
|
||||
if (capacity_in_bits_ >= min_capacity_in_bits) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int64_t min_capacity_in_ints = this->required_ints_for_bits(min_capacity_in_bits);
|
||||
|
||||
/* At least double the size of the previous allocation. */
|
||||
const int64_t min_new_capacity_in_ints = 2 * this->required_ints_for_bits(capacity_in_bits_);
|
||||
|
||||
const int64_t new_capacity_in_ints = std::max(min_capacity_in_ints, min_new_capacity_in_ints);
|
||||
const int64_t ints_to_copy = this->used_ints_amount();
|
||||
|
||||
BitInt *new_data = static_cast<BitInt *>(
|
||||
allocator_.allocate(new_capacity_in_ints * sizeof(BitInt), AllocationAlignment, __func__));
|
||||
uninitialized_copy_n(data_, ints_to_copy, new_data);
|
||||
/* Always initialize new capacity even if it isn't used yet. That's necessary to avoid warnings
|
||||
* caused by using uninitialized memory. This happens when e.g. setting a clearing a bit in an
|
||||
* uninitialized int. */
|
||||
uninitialized_fill_n(
|
||||
new_data + ints_to_copy, new_capacity_in_ints - ints_to_copy, initial_value_for_new_ints);
|
||||
|
||||
if (!this->is_inline()) {
|
||||
allocator_.deallocate(data_);
|
||||
}
|
||||
|
||||
data_ = new_data;
|
||||
capacity_in_bits_ = new_capacity_in_ints * BitsPerInt;
|
||||
}
|
||||
|
||||
bool is_inline() const
|
||||
{
|
||||
return data_ == inline_buffer_;
|
||||
}
|
||||
|
||||
int64_t used_ints_amount() const
|
||||
{
|
||||
return this->required_ints_for_bits(size_in_bits_);
|
||||
}
|
||||
};
|
||||
|
||||
template<int64_t InlineBufferCapacity, typename Allocator>
|
||||
inline BoundedBitSpan to_best_bit_span(const BitVector<InlineBufferCapacity, Allocator> &data)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
template<int64_t InlineBufferCapacity, typename Allocator>
|
||||
inline MutableBoundedBitSpan to_best_bit_span(BitVector<InlineBufferCapacity, Allocator> &data)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace bits
|
||||
|
||||
using bits::BitVector;
|
||||
|
||||
} // namespace blender
|
||||
150
blender-5.2.0/source/blender/blenlib/BLI_bitmap.h
Normal file
150
blender-5.2.0/source/blender/blenlib/BLI_bitmap.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/* SPDX-FileCopyrightText: 2012 by Nicholas Bishop. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
typedef unsigned int BLI_bitmap;
|
||||
|
||||
/* WARNING: the bitmap does not keep track of its own size or check
|
||||
* for out-of-bounds access */
|
||||
|
||||
/* internal use */
|
||||
/* 2^5 = 32 (bits) */
|
||||
#define _BITMAP_POWER 5
|
||||
/* 0b11111 */
|
||||
#define _BITMAP_MASK 31
|
||||
|
||||
/**
|
||||
* Number of blocks needed to hold '_num' bits.
|
||||
*/
|
||||
#define _BITMAP_NUM_BLOCKS(_num) (((_num) + _BITMAP_MASK) >> _BITMAP_POWER)
|
||||
|
||||
/**
|
||||
* Size (in bytes) used to hold '_num' bits.
|
||||
*/
|
||||
#define BLI_BITMAP_SIZE(_num) ((size_t)(_BITMAP_NUM_BLOCKS(_num)) * sizeof(BLI_bitmap))
|
||||
|
||||
/**
|
||||
* Allocate memory for a bitmap with '_num' bits; free with MEM_delete().
|
||||
*/
|
||||
#define BLI_BITMAP_NEW(_num, _alloc_string) \
|
||||
((BLI_bitmap *)MEM_new_zeroed(BLI_BITMAP_SIZE(_num), _alloc_string))
|
||||
|
||||
/**
|
||||
* Allocate a bitmap on the stack.
|
||||
*/
|
||||
#define BLI_BITMAP_NEW_ALLOCA(_num) \
|
||||
((BLI_bitmap *)memset(alloca(BLI_BITMAP_SIZE(_num)), 0, BLI_BITMAP_SIZE(_num)))
|
||||
|
||||
/**
|
||||
* Allocate using given MemArena.
|
||||
*/
|
||||
#define BLI_BITMAP_NEW_MEMARENA(_mem, _num) \
|
||||
(CHECK_TYPE_INLINE(_mem, MemArena *), \
|
||||
((BLI_bitmap *)BLI_memarena_calloc(_mem, BLI_BITMAP_SIZE(_num))))
|
||||
|
||||
/**
|
||||
* Declares a bitmap as a variable.
|
||||
*/
|
||||
#define BLI_BITMAP_DECLARE(_name, _num) BLI_bitmap _name[_BITMAP_NUM_BLOCKS(_num)] = {}
|
||||
|
||||
/**
|
||||
* Get the value of a single bit at '_index'.
|
||||
*/
|
||||
#define BLI_BITMAP_TEST(_bitmap, _index) \
|
||||
(CHECK_TYPE_ANY(_bitmap, BLI_bitmap *, const BLI_bitmap *), \
|
||||
((_bitmap)[(_index) >> _BITMAP_POWER] & (1u << ((_index) & _BITMAP_MASK))))
|
||||
|
||||
#define BLI_BITMAP_TEST_AND_SET_ATOMIC(_bitmap, _index) \
|
||||
(CHECK_TYPE_ANY(_bitmap, BLI_bitmap *, const BLI_bitmap *), \
|
||||
(atomic_fetch_and_or_uint32((uint32_t *)&(_bitmap)[(_index) >> _BITMAP_POWER], \
|
||||
(1u << ((_index) & _BITMAP_MASK))) & \
|
||||
(1u << ((_index) & _BITMAP_MASK))))
|
||||
|
||||
#define BLI_BITMAP_TEST_BOOL(_bitmap, _index) \
|
||||
(CHECK_TYPE_ANY(_bitmap, BLI_bitmap *, const BLI_bitmap *), \
|
||||
(BLI_BITMAP_TEST(_bitmap, _index) != 0))
|
||||
|
||||
/**
|
||||
* Set the value of a single bit at '_index'.
|
||||
*/
|
||||
#define BLI_BITMAP_ENABLE(_bitmap, _index) \
|
||||
(CHECK_TYPE_ANY(_bitmap, BLI_bitmap *, const BLI_bitmap *), \
|
||||
((_bitmap)[(_index) >> _BITMAP_POWER] |= (1u << ((_index) & _BITMAP_MASK))))
|
||||
|
||||
/**
|
||||
* Clear the value of a single bit at '_index'.
|
||||
*/
|
||||
#define BLI_BITMAP_DISABLE(_bitmap, _index) \
|
||||
(CHECK_TYPE_ANY(_bitmap, BLI_bitmap *, const BLI_bitmap *), \
|
||||
((_bitmap)[(_index) >> _BITMAP_POWER] &= ~(1u << ((_index) & _BITMAP_MASK))))
|
||||
|
||||
/**
|
||||
* Flip the value of a single bit at '_index'.
|
||||
*/
|
||||
#define BLI_BITMAP_FLIP(_bitmap, _index) \
|
||||
(CHECK_TYPE_ANY(_bitmap, BLI_bitmap *, const BLI_bitmap *), \
|
||||
((_bitmap)[(_index) >> _BITMAP_POWER] ^= (1u << ((_index) & _BITMAP_MASK))))
|
||||
|
||||
/**
|
||||
* Set or clear the value of a single bit at '_index'.
|
||||
*/
|
||||
#define BLI_BITMAP_SET(_bitmap, _index, _set) \
|
||||
{ \
|
||||
CHECK_TYPE(_bitmap, BLI_bitmap *); \
|
||||
if (_set) { \
|
||||
BLI_BITMAP_ENABLE(_bitmap, _index); \
|
||||
} \
|
||||
else { \
|
||||
BLI_BITMAP_DISABLE(_bitmap, _index); \
|
||||
} \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
/**
|
||||
* Resize bitmap to have space for '_num' bits.
|
||||
*/
|
||||
#define BLI_BITMAP_RESIZE(_bitmap, _num) \
|
||||
{ \
|
||||
CHECK_TYPE(_bitmap, BLI_bitmap *); \
|
||||
(_bitmap) = (unsigned int *)MEM_realloc_zeroed(_bitmap, BLI_BITMAP_SIZE(_num)); \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
/**
|
||||
* Set or clear all bits in the bitmap.
|
||||
*/
|
||||
void BLI_bitmap_set_all(BLI_bitmap *bitmap, bool set, size_t bits);
|
||||
/**
|
||||
* Invert all bits in the bitmap.
|
||||
*/
|
||||
void BLI_bitmap_flip_all(BLI_bitmap *bitmap, size_t bits);
|
||||
/**
|
||||
* Copy all bits from one bitmap to another.
|
||||
*/
|
||||
void BLI_bitmap_copy_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits);
|
||||
/**
|
||||
* Combine two bitmaps with boolean AND.
|
||||
*/
|
||||
void BLI_bitmap_and_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits);
|
||||
/**
|
||||
* Combine two bitmaps with boolean OR.
|
||||
*/
|
||||
void BLI_bitmap_or_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits);
|
||||
|
||||
/**
|
||||
* Find index of the lowest unset bit.
|
||||
* Returns -1 if all the bits are set.
|
||||
*/
|
||||
int BLI_bitmap_find_first_unset(const BLI_bitmap *bitmap, size_t bits);
|
||||
|
||||
} // namespace blender
|
||||
55
blender-5.2.0/source/blender/blenlib/BLI_bitmap_draw_2d.h
Normal file
55
blender-5.2.0/source/blender/blenlib/BLI_bitmap_draw_2d.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Plot a line from \a p1 to \a p2 (inclusive).
|
||||
*
|
||||
* \note For clipped line drawing, see: http://stackoverflow.com/a/40902741/432509
|
||||
*/
|
||||
void BLI_bitmap_draw_2d_line_v2v2i(const int p1[2],
|
||||
const int p2[2],
|
||||
bool (*callback)(int, int, void *),
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* \note Unclipped (clipped version can be added if needed).
|
||||
*/
|
||||
void BLI_bitmap_draw_2d_tri_v2i(const int p1[2],
|
||||
const int p2[2],
|
||||
const int p3[2],
|
||||
void (*callback)(int x, int x_end, int y, void *),
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* Draws a filled polygon with support for self intersections.
|
||||
*
|
||||
* \param callback: Takes the x, y coords and x-span (\a x_end is not inclusive),
|
||||
* note that \a x_end will always be greater than \a x, so we can use:
|
||||
*
|
||||
* \code{.c}
|
||||
* do {
|
||||
* func(x, y);
|
||||
* } while (++x != x_end);
|
||||
* \endcode
|
||||
*/
|
||||
void BLI_bitmap_draw_2d_poly_v2i_n(int xmin,
|
||||
int ymin,
|
||||
int xmax,
|
||||
int ymax,
|
||||
Span<int2> verts,
|
||||
void (*callback)(int x, int x_end, int y, void *),
|
||||
void *user_data);
|
||||
|
||||
} // namespace blender
|
||||
517
blender-5.2.0/source/blender/blenlib/BLI_bounds.hh
Normal file
517
blender-5.2.0/source/blender/blenlib/BLI_bounds.hh
Normal file
@@ -0,0 +1,517 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_bounds_types.hh"
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace bounds {
|
||||
|
||||
template<typename T> [[nodiscard]] inline Bounds<T> merge(const Bounds<T> &a, const Bounds<T> &b)
|
||||
{
|
||||
return {math::min(a.min, b.min), math::max(a.max, b.max)};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline std::optional<Bounds<T>> merge(const std::optional<Bounds<T>> &a,
|
||||
const std::optional<Bounds<T>> &b)
|
||||
{
|
||||
if (a.has_value() && b.has_value()) {
|
||||
return merge(*a, *b);
|
||||
}
|
||||
if (a.has_value()) {
|
||||
return a;
|
||||
}
|
||||
if (b.has_value()) {
|
||||
return b;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline std::optional<Bounds<T>> merge(const std::optional<Bounds<T>> &a,
|
||||
const Bounds<T> &b)
|
||||
{
|
||||
return merge(a, std::optional<Bounds<T>>(b));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline std::optional<Bounds<T>> min_max(const std::optional<Bounds<T>> &a,
|
||||
const T &b)
|
||||
{
|
||||
if (a.has_value()) {
|
||||
return merge(*a, {b, b});
|
||||
}
|
||||
return Bounds<T>{b, b};
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the smallest and largest values element-wise in the span.
|
||||
*/
|
||||
template<typename T> [[nodiscard]] inline std::optional<Bounds<T>> min_max(const Span<T> values)
|
||||
{
|
||||
if (values.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
PRF_scope_with_name("bounds::min_max_with_radii", ProfileCategory::Default);
|
||||
const Bounds<T> init{values.first(), values.first()};
|
||||
return threading::parallel_reduce(
|
||||
values.index_range(),
|
||||
1024,
|
||||
init,
|
||||
[&](const IndexRange range, const Bounds<T> &init) {
|
||||
Bounds<T> result = init;
|
||||
for (const int i : range) {
|
||||
math::min_max(values[i], result.min, result.max);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[](const Bounds<T> &a, const Bounds<T> &b) { return merge(a, b); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline std::optional<Bounds<T>> min_max(const IndexMask &mask, const Span<T> values)
|
||||
{
|
||||
if (values.is_empty() || mask.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (mask.size() == values.size()) {
|
||||
/* To avoid mask slice/lookup. */
|
||||
return min_max(values);
|
||||
}
|
||||
PRF_scope_with_name("bounds::min_max_with_radii", ProfileCategory::Default);
|
||||
const Bounds<T> init{values[mask.first()], values[mask.first()]};
|
||||
return threading::parallel_reduce(
|
||||
mask.index_range().drop_front(1),
|
||||
1024,
|
||||
init,
|
||||
[&](const IndexRange range, const Bounds<T> &init) {
|
||||
Bounds<T> result = init;
|
||||
mask.slice(range).foreach_index_optimized<int64_t>(
|
||||
[&](const int i) { math::min_max(values[i], result.min, result.max); });
|
||||
return result;
|
||||
},
|
||||
[](const Bounds<T> &a, const Bounds<T> &b) { return merge(a, b); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the smallest and largest values element-wise in the span, adding the radius to each element
|
||||
* first. The template type T is expected to have an addition operator implemented with RadiusT.
|
||||
*/
|
||||
template<typename T, typename RadiusT>
|
||||
[[nodiscard]] inline std::optional<Bounds<T>> min_max_with_radii(const Span<T> values,
|
||||
const Span<RadiusT> radii)
|
||||
{
|
||||
BLI_assert(values.size() == radii.size());
|
||||
if (values.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
PRF_scope_with_name("bounds::min_max_with_radii", ProfileCategory::Default);
|
||||
const Bounds<T> init{values.first(), values.first()};
|
||||
return threading::parallel_reduce(
|
||||
values.index_range(),
|
||||
1024,
|
||||
init,
|
||||
[&](const IndexRange range, const Bounds<T> &init) {
|
||||
Bounds<T> result = init;
|
||||
for (const int i : range) {
|
||||
result.min = math::min(values[i] - radii[i], result.min);
|
||||
result.max = math::max(values[i] + radii[i], result.max);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[](const Bounds<T> &a, const Bounds<T> &b) { return merge(a, b); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new bound that contains the intersection of the two given bound.
|
||||
* Returns no box if there are no overlap.
|
||||
*/
|
||||
template<typename T>
|
||||
[[nodiscard]] inline std::optional<Bounds<T>> intersect(const Bounds<T> &a, const Bounds<T> &b)
|
||||
{
|
||||
const Bounds<T> result{math::max(a.min, b.min), math::min(a.max, b.max)};
|
||||
if (result.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template<typename T>
|
||||
[[nodiscard]] inline std::optional<Bounds<T>> intersect(const std::optional<Bounds<T>> &a,
|
||||
const std::optional<Bounds<T>> &b)
|
||||
{
|
||||
if (!a.has_value() || !b.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return intersect(*a, *b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the maximum value for elements in the array.
|
||||
*/
|
||||
template<typename T> inline std::optional<T> max(const VArray<T> &values)
|
||||
{
|
||||
if (values.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
PRF_scope_with_name("bounds::max", ProfileCategory::Default);
|
||||
if (const std::optional<T> value = values.get_if_single()) {
|
||||
return value;
|
||||
}
|
||||
const VArraySpan<int> values_span = values;
|
||||
return threading::parallel_reduce(
|
||||
values_span.index_range(),
|
||||
2048,
|
||||
std::numeric_limits<T>::min(),
|
||||
[&](const IndexRange range, int current_max) {
|
||||
for (const int value : values_span.slice(range)) {
|
||||
current_max = std::max(current_max, value);
|
||||
}
|
||||
return current_max;
|
||||
},
|
||||
[](const int a, const int b) { return std::max(a, b); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the eight corners of a 3D bounding box.
|
||||
* <pre>
|
||||
*
|
||||
* Z Y
|
||||
* | /
|
||||
* |/
|
||||
* .-----X
|
||||
* 2----------6
|
||||
* /| /|
|
||||
* / | / |
|
||||
* 1----------5 |
|
||||
* | | | |
|
||||
* | 3-------|--7
|
||||
* | / | /
|
||||
* |/ |/
|
||||
* 0----------4
|
||||
* </pre>
|
||||
*/
|
||||
template<typename T>
|
||||
inline std::array<VecBase<T, 3>, 8> corners(const Bounds<VecBase<T, 3>> &bounds)
|
||||
{
|
||||
return {
|
||||
VecBase<T, 3>{bounds.min[0], bounds.min[1], bounds.min[2]},
|
||||
VecBase<T, 3>{bounds.min[0], bounds.min[1], bounds.max[2]},
|
||||
VecBase<T, 3>{bounds.min[0], bounds.max[1], bounds.max[2]},
|
||||
VecBase<T, 3>{bounds.min[0], bounds.max[1], bounds.min[2]},
|
||||
VecBase<T, 3>{bounds.max[0], bounds.min[1], bounds.min[2]},
|
||||
VecBase<T, 3>{bounds.max[0], bounds.min[1], bounds.max[2]},
|
||||
VecBase<T, 3>{bounds.max[0], bounds.max[1], bounds.max[2]},
|
||||
VecBase<T, 3>{bounds.max[0], bounds.max[1], bounds.min[2]},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the four corners of a 2D bounding box.
|
||||
* <pre>
|
||||
*
|
||||
* Y
|
||||
* |
|
||||
* |
|
||||
* .-----X
|
||||
*
|
||||
* 3----------2
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* 0----------1
|
||||
* </pre>
|
||||
*/
|
||||
template<typename T>
|
||||
inline std::array<VecBase<T, 2>, 4> corners(const Bounds<VecBase<T, 2>> &bounds)
|
||||
{
|
||||
return {
|
||||
bounds.min,
|
||||
VecBase<T, 2>{bounds.max.x, bounds.min.y},
|
||||
bounds.max,
|
||||
VecBase<T, 2>{bounds.min.x, bounds.max.y},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a 3D bounding box.
|
||||
*
|
||||
* Note: this necessarily grows the bounding box, to ensure that the transformed
|
||||
* bounding box fully contains the original. Therefore, calling this iteratively
|
||||
* to transform from space A to space B, and then from space B to space C, etc.,
|
||||
* will also iteratively grow the bounding box on each call. Try to avoid doing
|
||||
* that, and instead first compose the transform matrices and then use that to
|
||||
* transform the bounding box.
|
||||
*/
|
||||
template<typename T, int D>
|
||||
inline Bounds<VecBase<T, 3>> transform_bounds(const MatBase<T, D, D> &matrix,
|
||||
const Bounds<VecBase<T, 3>> &bounds)
|
||||
{
|
||||
std::array<VecBase<T, 3>, 8> points = corners(bounds);
|
||||
for (VecBase<T, 3> &p : points) {
|
||||
p = math::transform_point(matrix, p);
|
||||
}
|
||||
return {math::min(Span(points)), math::max(Span(points))};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a 2D bounding box.
|
||||
*
|
||||
* See the note on the 3D variant.
|
||||
*/
|
||||
template<typename T, int D>
|
||||
inline Bounds<VecBase<T, 2>> transform_bounds(const MatBase<T, D, D> &matrix,
|
||||
const Bounds<VecBase<T, 2>> &bounds)
|
||||
{
|
||||
std::array<VecBase<T, 2>, 4> points = corners(bounds);
|
||||
for (VecBase<T, 2> &p : points) {
|
||||
p = math::transform_point(matrix, p);
|
||||
}
|
||||
return {math::min(Span(points)), math::max(Span(points))};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T, int Size>
|
||||
[[nodiscard]] inline bool any_less_than_v(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
|
||||
{
|
||||
for (int i = 0; i < Size; i++) {
|
||||
if (a[i] < b[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
[[nodiscard]] inline bool any_greater_than_v(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
|
||||
{
|
||||
for (int i = 0; i < Size; i++) {
|
||||
if (a[i] > b[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
[[nodiscard]] inline bool any_less_or_equal_than_v(const VecBase<T, Size> &a,
|
||||
const VecBase<T, Size> &b)
|
||||
{
|
||||
for (int i = 0; i < Size; i++) {
|
||||
if (a[i] <= b[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T> [[nodiscard]] inline bool any_less_than(const T &a, const T &b)
|
||||
{
|
||||
if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) {
|
||||
return a < b;
|
||||
}
|
||||
else {
|
||||
return any_less_than_v(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> [[nodiscard]] inline bool any_greater_than(const T &a, const T &b)
|
||||
{
|
||||
if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) {
|
||||
return a > b;
|
||||
}
|
||||
else {
|
||||
return any_greater_than_v(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> [[nodiscard]] inline bool any_less_or_equal_than(const T &a, const T &b)
|
||||
{
|
||||
if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) {
|
||||
return a <= b;
|
||||
}
|
||||
else {
|
||||
return any_less_or_equal_than_v(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> [[nodiscard]] inline Bounds<T> segment_bounds(const T &start, const T &end)
|
||||
{
|
||||
Bounds<T> bounds{start, start};
|
||||
math::min_max(end, bounds.min, bounds.max);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/** Returns true if (p1 / q1) > (p2 / q2). */
|
||||
template<typename T>
|
||||
[[nodiscard]] inline bool rational_greater_than(const T &p1, const T &q1, const T &p2, const T &q2)
|
||||
{
|
||||
BLI_assert(q1 > T(0) && q2 > T(0));
|
||||
return p1 * q2 > p2 * q1;
|
||||
}
|
||||
/** Returns true if (p1 / q1) < (p2 / q2). */
|
||||
template<typename T>
|
||||
[[nodiscard]] inline bool rational_less_than(const T &p1, const T &q1, const T &p2, const T &q2)
|
||||
{
|
||||
BLI_assert(q1 > T(0) && q2 > T(0));
|
||||
return p1 * q2 < p2 * q1;
|
||||
}
|
||||
|
||||
/** Adaptation of Liang-Barsky for N dimensions. */
|
||||
template<typename T, int Size>
|
||||
[[nodiscard]] inline bool segment_enter_exit_bounds(const Bounds<VecBase<T, Size>> &bounds,
|
||||
const VecBase<T, Size> &start,
|
||||
const VecBase<T, Size> &end)
|
||||
{
|
||||
T p_enter = T(0);
|
||||
T q_enter = T(1);
|
||||
T p_exit = T(1);
|
||||
T q_exit = T(1);
|
||||
|
||||
/* t_enter = p_enter / q_enter */
|
||||
/* t_exit = p_exit / q_exit */
|
||||
for (int i = 0; i < Size; i++) {
|
||||
const T di = end[i] - start[i];
|
||||
if (di == T(0)) {
|
||||
/* Segment is parallel to i-th axis. */
|
||||
if (start[i] < bounds.min[i] || start[i] > bounds.max[i]) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Note: We flip the sign here to ensure the denominator is positive. This doesn't change the
|
||||
* value of the rational number. */
|
||||
const T p_low = (di > T(0)) ? bounds.min[i] - start[i] : -(bounds.max[i] - start[i]);
|
||||
const T p_high = (di > T(0)) ? bounds.max[i] - start[i] : -(bounds.min[i] - start[i]);
|
||||
const T di_abs = (di > T(0)) ? di : -di;
|
||||
|
||||
/* t_low = p_low / di_abs */
|
||||
/* t_high = p_high / di_abs */
|
||||
if (rational_greater_than(p_low, di_abs, p_enter, q_enter)) {
|
||||
/* t_low > t_enter */
|
||||
p_enter = p_low;
|
||||
q_enter = di_abs;
|
||||
}
|
||||
if (rational_less_than(p_high, di_abs, p_exit, q_exit)) {
|
||||
/* t_high < t_exit */
|
||||
p_exit = p_high;
|
||||
q_exit = di_abs;
|
||||
}
|
||||
|
||||
if (rational_greater_than(p_enter, q_enter, p_exit, q_exit)) {
|
||||
/* t_enter > t_exit */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* t_exit >= 0 and t_enter <= 1 */
|
||||
return p_exit >= T(0) && p_enter <= q_enter;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace bounds
|
||||
|
||||
template<typename T> inline bool Bounds<T>::is_empty() const
|
||||
{
|
||||
return bounds::detail::any_less_or_equal_than(this->max, this->min);
|
||||
}
|
||||
|
||||
template<typename T> inline T Bounds<T>::center() const
|
||||
{
|
||||
return math::midpoint(this->min, this->max);
|
||||
}
|
||||
|
||||
template<typename T> inline T Bounds<T>::size() const
|
||||
{
|
||||
return math::abs(max - min);
|
||||
}
|
||||
|
||||
template<typename T> inline void Bounds<T>::translate(const T &offset)
|
||||
{
|
||||
this->min += offset;
|
||||
this->max += offset;
|
||||
}
|
||||
|
||||
template<typename T> inline void Bounds<T>::scale_from_center(const T &scale)
|
||||
{
|
||||
const T center = this->center();
|
||||
const T new_half_size = this->size() / T(2) * scale;
|
||||
this->min = center - new_half_size;
|
||||
this->max = center + new_half_size;
|
||||
}
|
||||
|
||||
template<typename T> inline void Bounds<T>::resize(const T &new_size)
|
||||
{
|
||||
this->min = this->center() - (new_size / T(2));
|
||||
this->max = this->min + new_size;
|
||||
}
|
||||
|
||||
template<typename T> inline void Bounds<T>::recenter(const T &new_center)
|
||||
{
|
||||
const T offset = new_center - this->center();
|
||||
this->translate(offset);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename PaddingT>
|
||||
inline void Bounds<T>::pad(const PaddingT &padding)
|
||||
{
|
||||
this->min = this->min - padding;
|
||||
this->max = this->max + padding;
|
||||
}
|
||||
|
||||
template<typename T> inline bool Bounds<T>::contains(const T &point) const
|
||||
{
|
||||
if (bounds::detail::any_less_than(point, this->min)) {
|
||||
return false;
|
||||
}
|
||||
if (bounds::detail::any_greater_than(point, this->max)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T> inline bool Bounds<T>::intersects(const Bounds<T> &other) const
|
||||
{
|
||||
if (bounds::intersect(*this, other)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T> inline bool Bounds<T>::intersects_segment(const T &start, const T &end) const
|
||||
{
|
||||
/* Check end points first to properly handle degenerate case where the segment is a point. */
|
||||
if (this->contains(start) || this->contains(end)) {
|
||||
return true;
|
||||
}
|
||||
if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) {
|
||||
return this->intersects(bounds::detail::segment_bounds(start, end));
|
||||
}
|
||||
else {
|
||||
/* Check if the segment is entering and exiting the bounds. */
|
||||
return bounds::detail::segment_enter_exit_bounds(*this, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
82
blender-5.2.0/source/blender/blenlib/BLI_bounds_types.hh
Normal file
82
blender-5.2.0/source/blender/blenlib/BLI_bounds_types.hh
Normal file
@@ -0,0 +1,82 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
template<typename T> struct Bounds {
|
||||
T min;
|
||||
T max;
|
||||
Bounds() = default;
|
||||
explicit Bounds(const T &value) : min(value), max(value) {}
|
||||
Bounds(const T &min, const T &max) : min(min), max(max) {}
|
||||
|
||||
/**
|
||||
* Returns true when the size of the bounds is zero (or negative).
|
||||
* This matches the behavior of #BLI_rcti_is_empty/#BLI_rctf_is_empty.
|
||||
*/
|
||||
bool is_empty() const;
|
||||
/**
|
||||
* Return the center (i.e. the midpoint) of the bounds.
|
||||
* This matches the behavior of #BLI_rctf_cent/#BLI_rcti_cent.
|
||||
*/
|
||||
T center() const;
|
||||
/**
|
||||
* Return the size of the bounds.
|
||||
* E.g. for a Bounds<float3> this would return the dimensions of bounding box as a float3.
|
||||
* This matches the behavior of #BLI_rctf_size/#BLI_rcti_size.
|
||||
*/
|
||||
T size() const;
|
||||
|
||||
/**
|
||||
* Translate the bounds by #offset.
|
||||
* This matches the behavior of #BLI_rctf_translate/#BLI_rcti_translate.
|
||||
*/
|
||||
void translate(const T &offset);
|
||||
/**
|
||||
* Scale the bounds from the center.
|
||||
* This matches the behavior of #BLI_rctf_scale/#BLI_rcti_scale.
|
||||
*/
|
||||
void scale_from_center(const T &scale);
|
||||
|
||||
/**
|
||||
* Resize the bounds in-place to ensure their size is #new_size.
|
||||
* The center of the bounds doesn't change.
|
||||
* This matches the behavior of #BLI_rctf_resize/#BLI_rcti_resize.
|
||||
*/
|
||||
void resize(const T &new_size);
|
||||
/**
|
||||
* Translate the bounds such that their center is #new_center.
|
||||
* This matches the behavior of #BLI_rctf_recenter/#BLI_rcti_recenter.
|
||||
*/
|
||||
void recenter(const T &new_center);
|
||||
|
||||
/**
|
||||
* Adds some padding to the bounds.
|
||||
* This matches the behavior of #BLI_rcti_pad/#BLI_rctf_pad.
|
||||
*/
|
||||
template<typename PaddingT> void pad(const PaddingT &padding);
|
||||
|
||||
/**
|
||||
* Returns true if \a point is inside the bounds.
|
||||
* This matches the behavior of #BLI_rctf_isect_pt/#BLI_rcti_isect_pt.
|
||||
*/
|
||||
bool contains(const T &point) const;
|
||||
|
||||
/**
|
||||
* Returns true if the \a other bounds is inside or intersect this one.
|
||||
*/
|
||||
bool intersects(const Bounds<T> &other) const;
|
||||
/**
|
||||
* Returns true if a line segment from \a start to \a end is inside or intersects the bounds.
|
||||
*/
|
||||
bool intersects_segment(const T &start, const T &end) const;
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
74
blender-5.2.0/source/blender/blenlib/BLI_boxpack_2d.h
Normal file
74
blender-5.2.0/source/blender/blenlib/BLI_boxpack_2d.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Box Packer */
|
||||
|
||||
struct BoxPack {
|
||||
float x;
|
||||
float y;
|
||||
float w;
|
||||
float h;
|
||||
|
||||
/* Verts this box uses
|
||||
* (BL,TR,TL,BR) / 0,1,2,3 */
|
||||
struct BoxVert *v[4];
|
||||
|
||||
int index;
|
||||
};
|
||||
|
||||
/**
|
||||
* Main box-packing function accessed from other functions
|
||||
* This sets boxes x,y to positive values, sorting from 0,0 outwards.
|
||||
* There is no limit to the space boxes may take, only that they will be packed
|
||||
* tightly into the lower left hand corner (0,0)
|
||||
*
|
||||
* \param boxarray: a pre-allocated array of boxes.
|
||||
* only the 'box->x' and 'box->y' are set, 'box->w' and 'box->h' are used,
|
||||
* 'box->index' is not used at all, the only reason its there
|
||||
* is that the box array is sorted by area and programs need to be able
|
||||
* to have some way of writing the boxes back to the original data.
|
||||
* \param len: the number of boxes in the array.
|
||||
* \param sort_boxes: Sort `box_array` before packing.
|
||||
* \param r_tot_x, r_tot_y: set so you can normalize the data.
|
||||
*/
|
||||
void BLI_box_pack_2d(
|
||||
BoxPack *boxarray, unsigned int len, bool sort_boxes, float *r_tot_x, float *r_tot_y);
|
||||
|
||||
struct FixedSizeBoxPack {
|
||||
struct FixedSizeBoxPack *next, *prev;
|
||||
int x, y;
|
||||
int w, h;
|
||||
};
|
||||
|
||||
/**
|
||||
* Packs boxes into a fixed area.
|
||||
*
|
||||
* Boxes and packed are linked lists containing structs that can be cast to
|
||||
* #FixedSizeBoxPack (i.e. contains a #FixedSizeBoxPack as its first element).
|
||||
* Boxes that were packed successfully are placed into *packed and removed from *boxes.
|
||||
*
|
||||
* The algorithm is a simplified version of https://github.com/TeamHypersomnia/rectpack2D.
|
||||
* Better ones could be used, but for the current use case (packing Image tiles into GPU
|
||||
* textures) this is fine.
|
||||
*
|
||||
* Note that packing efficiency depends on the order of the input boxes. Generally speaking,
|
||||
* larger boxes should come first, though how exactly size is best defined (e.g. area, perimeter)
|
||||
* depends on the particular application.
|
||||
*/
|
||||
void BLI_box_pack_2d_fixedarea(ListBaseT<FixedSizeBoxPack> *boxes,
|
||||
int width,
|
||||
int height,
|
||||
ListBaseT<FixedSizeBoxPack> *packed);
|
||||
|
||||
} // namespace blender
|
||||
457
blender-5.2.0/source/blender/blenlib/BLI_build_config.h
Normal file
457
blender-5.2.0/source/blender/blenlib/BLI_build_config.h
Normal file
@@ -0,0 +1,457 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* Based on Chromium's `build_config.h`, governed by a BSD-style license,
|
||||
* with tweaks and extensions needed for the Blender project.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compile-time detection of compiler and hardware platform configuration.
|
||||
* There are few categories of the defined symbols this header provides.
|
||||
*
|
||||
*
|
||||
* Operating system detection
|
||||
* ==========================
|
||||
*
|
||||
* An operating system is detected is defined as an `OS_<NAME>` symbols. For example, on Windows
|
||||
* the `OS_WINDOWS` is defined to 1, and all the other symbols prefixed with `OS_` are defined to 0
|
||||
* (except of the aggregates described above).
|
||||
*
|
||||
* There are aggregates which allows to access "family" of the operating system:
|
||||
*
|
||||
* - OS_BSD is defined for 1 for all BSD family of OS (FreeBSD, NextBSD, DragonFly...).
|
||||
* - OS_POSIX is defined to 1 if the OS implements POSIX API.
|
||||
*
|
||||
*
|
||||
* Compiler detection
|
||||
* ==================
|
||||
*
|
||||
* The following compilers are detected: CLANG, GCC, MSVC, MINGW32, MINGW64.
|
||||
*
|
||||
* The COMPILER_<family> for the detected compiler is defined to 1, and all the rest of the
|
||||
* compiler defines are set to 0.
|
||||
*
|
||||
* The aggregate COMPILER_MINGW is defined when the compiler is wither MINGW32 or MINGW64.
|
||||
*
|
||||
*
|
||||
* CPU detection
|
||||
* =============
|
||||
*
|
||||
* The commonly detected CPU capabilities are:
|
||||
* - Family: `ARCH_CPU_<FAMILY>_FAMILY`
|
||||
* - CPU bitness: `ARCH_CPU_<32|64>_BITS`
|
||||
* - Endianness: `ARCH_CPU_<LITTLE|BIG>_ENDIAN`
|
||||
*
|
||||
* Supported CPU families: X86, S390, PPC, ARM, MIPS.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* All commonly used symbols (which are checked on a "top" level, from outside of any
|
||||
* platform-specific `ifdef` block) are to be explicitly defined to 0 when they are not "active".
|
||||
* Such an approach helps catching cases when one is attempted to access build configuration
|
||||
* variable without including the header by simply using the `-Wundef` compiler attribute.
|
||||
*/
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name A set of macros to use for platform detection.
|
||||
* \{ */
|
||||
|
||||
#if defined(__native_client__)
|
||||
/* __native_client__ must be first, so that other OS_ defines are not set. */
|
||||
# define OS_NACL 1
|
||||
/* OS_NACL comes in two sand-boxing technology flavors, SFI or Non-SFI. PNaCl toolchain defines
|
||||
* __native_client_nonsfi__ macro in Non-SFI build mode, while it does not in SFI build mode. */
|
||||
# if defined(__native_client_nonsfi__)
|
||||
# define OS_NACL_NONSFI
|
||||
# else
|
||||
# define OS_NACL_SFI
|
||||
# endif
|
||||
#elif defined(_AIX)
|
||||
# define OS_AIX 1
|
||||
#elif defined(ANDROID)
|
||||
# define OS_ANDROID 1
|
||||
#elif defined(__APPLE__)
|
||||
/* Only include TargetConditions after testing ANDROID as some android builds on mac don't have
|
||||
* this header available and it's not needed unless the target is really mac/IOS. */
|
||||
# include <TargetConditionals.h>
|
||||
# define OS_MAC 1
|
||||
# if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
|
||||
# define OS_IOS 1
|
||||
# endif
|
||||
#elif defined(__HAIKU__)
|
||||
# define OS_HAIKU 1
|
||||
#elif defined(__hpux)
|
||||
# define OS_HPUX 1
|
||||
#elif defined(__linux__)
|
||||
# define OS_LINUX 1
|
||||
/* Include a system header to pull in features.h for GLIBC/UCLIBC macros. */
|
||||
# include <unistd.h>
|
||||
# if defined(__GLIBC__) && !defined(__UCLIBC__)
|
||||
/* We really are using GLIBC, not UCLIBC pretending to be GLIBC. */
|
||||
# define LIBC_GLIBC 1
|
||||
# endif
|
||||
#elif defined(__sgi)
|
||||
# define OS_IRIX 1
|
||||
#elif defined(_WIN32)
|
||||
# define OS_WINDOWS 1
|
||||
#elif defined(__Fuchsia__)
|
||||
# define OS_FUCHSIA 1
|
||||
#elif defined(__DragonFly__)
|
||||
# define OS_DRAGONFLYBSD 1
|
||||
#elif defined(__FreeBSD__)
|
||||
# define OS_FREEBSD 1
|
||||
#elif defined(__NetBSD__)
|
||||
# define OS_NETBSD 1
|
||||
#elif defined(__OpenBSD__)
|
||||
# define OS_OPENBSD 1
|
||||
#elif defined(__sun)
|
||||
# define OS_SOLARIS 1
|
||||
#elif defined(__QNXNTO__)
|
||||
# define OS_QNX 1
|
||||
#elif defined(__asmjs__) || defined(__wasm__)
|
||||
# define OS_ASMJS 1
|
||||
#elif defined(__MVS__)
|
||||
# define OS_ZOS 1
|
||||
#else
|
||||
# error Please add support for your platform in BLI_build_config.h
|
||||
#endif
|
||||
|
||||
#if !defined(OS_AIX)
|
||||
# define OS_AIX 0
|
||||
#endif
|
||||
#if !defined(OS_ASMJS)
|
||||
# define OS_ASMJS 0
|
||||
#endif
|
||||
#if !defined(OS_NACL)
|
||||
# define OS_NACL 0
|
||||
#endif
|
||||
#if !defined(OS_NACL_NONSFI)
|
||||
# define OS_NACL_NONSFI 0
|
||||
#endif
|
||||
#if !defined(OS_NACL_SFI)
|
||||
# define OS_NACL_SFI 0
|
||||
#endif
|
||||
#if !defined(OS_ANDROID)
|
||||
# define OS_ANDROID 0
|
||||
#endif
|
||||
#if !defined(OS_MAC)
|
||||
# define OS_MAC 0
|
||||
#endif
|
||||
#if !defined(OS_IOS)
|
||||
# define OS_IOS 0
|
||||
#endif
|
||||
#if !defined(OS_HAIKU)
|
||||
# define OS_HAIKU 0
|
||||
#endif
|
||||
#if !defined(OS_HPUX)
|
||||
# define OS_HPUX 0
|
||||
#endif
|
||||
#if !defined(OS_IRIX)
|
||||
# define OS_IRIX 0
|
||||
#endif
|
||||
#if !defined(OS_LINUX)
|
||||
# define OS_LINUX 0
|
||||
#endif
|
||||
#if !defined(LIBC_GLIBC)
|
||||
# define LIBC_GLIBC 0
|
||||
#endif
|
||||
#if !defined(OS_WINDOWS)
|
||||
# define OS_WINDOWS 0
|
||||
#endif
|
||||
#if !defined(OS_FUCHSIA)
|
||||
# define OS_FUCHSIA 0
|
||||
#endif
|
||||
#if !defined(OS_DRAGONFLYBSD)
|
||||
# define OS_DRAGONFLYBSD 0
|
||||
#endif
|
||||
#if !defined(OS_FREEBSD)
|
||||
# define OS_FREEBSD 0
|
||||
#endif
|
||||
#if !defined(OS_NETBSD)
|
||||
# define OS_NETBSD 0
|
||||
#endif
|
||||
#if !defined(OS_OPENBSD)
|
||||
# define OS_OPENBSD 0
|
||||
#endif
|
||||
#if !defined(OS_SOLARIS)
|
||||
# define OS_SOLARIS 0
|
||||
#endif
|
||||
#if !defined(OS_QNX)
|
||||
# define OS_QNX 0
|
||||
#endif
|
||||
#if !defined(OS_ZOS)
|
||||
# define OS_ZOS 0
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name *BSD OS family detection.
|
||||
* For access to standard BSD features, use OS_BSD instead of a more specific macro.
|
||||
* \{ */
|
||||
|
||||
#if OS_DRAGONFLYBSD || OS_FREEBSD || OS_OPENBSD || OS_NETBSD
|
||||
# define OS_BSD 1
|
||||
#else
|
||||
# define OS_BSD 0
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name POSIX system detection.
|
||||
* For access to standard POSIXish features use OS_POSIX instead of a more specific macro.
|
||||
* \{ */
|
||||
|
||||
#if OS_AIX || OS_ANDROID || OS_ASMJS || OS_FREEBSD || OS_LINUX || OS_MAC || OS_NACL || \
|
||||
OS_NETBSD || OS_OPENBSD || OS_QNX || OS_SOLARIS
|
||||
# define OS_POSIX 1
|
||||
#else
|
||||
# define OS_POSIX 0
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Compiler detection, including its capabilities.
|
||||
* \{ */
|
||||
|
||||
#if defined(__clang__)
|
||||
# define COMPILER_CLANG 1
|
||||
#elif defined(__GNUC__)
|
||||
# define COMPILER_GCC 1
|
||||
# define COMPILER_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||
#elif defined(_MSC_VER)
|
||||
# define COMPILER_MSVC 1
|
||||
# define COMPILER_MSVC_VERSION (_MSC_VER)
|
||||
#elif defined(__MINGW32__)
|
||||
# define COMPILER_MINGW32 1
|
||||
#elif defined(__MINGW64__)
|
||||
# define COMPILER_MINGW64 1
|
||||
#else
|
||||
# error Please add support for your compiler in BLI_build_config.h
|
||||
#endif
|
||||
|
||||
#if !defined(COMPILER_CLANG)
|
||||
# define COMPILER_CLANG 0
|
||||
#endif
|
||||
#if !defined(COMPILER_GCC)
|
||||
# define COMPILER_GCC 0
|
||||
#endif
|
||||
#if !defined(COMPILER_MSVC)
|
||||
# define COMPILER_MSVC 0
|
||||
#endif
|
||||
#if !defined(COMPILER_MINGW32)
|
||||
# define COMPILER_MINGW32 0
|
||||
#endif
|
||||
#if !defined(COMPILER_MINGW64)
|
||||
# define COMPILER_MINGW64 0
|
||||
#endif
|
||||
|
||||
/* Compiler is any of MinGW family. */
|
||||
#if COMPILER_MINGW32 || COMPILER_MINGW64
|
||||
# define COMPILER_MINGW 1
|
||||
#else
|
||||
# define COMPILER_MINGW 0
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Processor architecture detection.
|
||||
* For more info on what's defined, see:
|
||||
*
|
||||
* http://msdn.microsoft.com/en-us/library/b0084kay.aspx
|
||||
* http://www.agner.org/optimize/calling_conventions.pdf
|
||||
*
|
||||
* or with GCC, run: `echo | gcc -E -dM -`
|
||||
* \{ */
|
||||
|
||||
#if defined(_M_X64) || defined(__x86_64__)
|
||||
# define ARCH_CPU_X86_FAMILY 1
|
||||
# define ARCH_CPU_X86_64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(_M_IX86) || defined(__i386__)
|
||||
# define ARCH_CPU_X86_FAMILY 1
|
||||
# define ARCH_CPU_X86 1
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(__s390x__)
|
||||
# define ARCH_CPU_S390_FAMILY 1
|
||||
# define ARCH_CPU_S390X 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
#elif defined(__s390__)
|
||||
# define ARCH_CPU_S390_FAMILY 1
|
||||
# define ARCH_CPU_S390 1
|
||||
# define ARCH_CPU_31_BITS 1
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
#elif (defined(__PPC64__) || defined(__PPC__)) && defined(__BIG_ENDIAN__)
|
||||
# define ARCH_CPU_PPC64_FAMILY 1
|
||||
# define ARCH_CPU_PPC64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
#elif defined(__PPC64__)
|
||||
# define ARCH_CPU_PPC64_FAMILY 1
|
||||
# define ARCH_CPU_PPC64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(__ARMEL__)
|
||||
# define ARCH_CPU_ARM_FAMILY 1
|
||||
# define ARCH_CPU_ARMEL 1
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
# define ARCH_CPU_ARM_FAMILY 1
|
||||
# define ARCH_CPU_ARM64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(__pnacl__) || defined(__asmjs__) || defined(__wasm__)
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(__MIPSEL__)
|
||||
# if defined(__LP64__)
|
||||
# define ARCH_CPU_MIPS_FAMILY 1
|
||||
# define ARCH_CPU_MIPS64EL 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
# else
|
||||
# define ARCH_CPU_MIPS_FAMILY 1
|
||||
# define ARCH_CPU_MIPSEL 1
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
# endif
|
||||
#elif defined(__MIPSEB__)
|
||||
# if defined(__LP64__)
|
||||
# define ARCH_CPU_MIPS_FAMILY 1
|
||||
# define ARCH_CPU_MIPS64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
# else
|
||||
# define ARCH_CPU_MIPS_FAMILY 1
|
||||
# define ARCH_CPU_MIPS 1
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
# endif
|
||||
#elif defined(__riscv)
|
||||
# define ARCH_CPU_RISCV_FAMILY 1
|
||||
# if defined(__LP128__)
|
||||
# define ARCH_CPU_RISCV128 1
|
||||
# define ARCH_CPU_128_BITS 1
|
||||
# elif defined(__LP64__)
|
||||
# define ARCH_CPU_RISCV64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# else
|
||||
# define ARCH_CPU_RISCV32 1
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# endif
|
||||
# if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
# else
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
# endif
|
||||
#elif defined(__loongarch_lp64)
|
||||
# define ARCH_CPU_LOONG_FAMILY 1
|
||||
# define ARCH_CPU_LOONG64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#else
|
||||
# error Please add support for your architecture in BLI_build_config.h
|
||||
#endif
|
||||
|
||||
#if !defined(ARCH_CPU_LITTLE_ENDIAN)
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_BIG_ENDIAN)
|
||||
# define ARCH_CPU_BIG_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#if !defined(ARCH_CPU_31_BITS)
|
||||
# define ARCH_CPU_31_BITS 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_32_BITS)
|
||||
# define ARCH_CPU_32_BITS 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_64_BITS)
|
||||
# define ARCH_CPU_64_BITS 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_128_BITS)
|
||||
# define ARCH_CPU_128_BITS 0
|
||||
#endif
|
||||
|
||||
#if !defined(ARCH_CPU_X86_FAMILY)
|
||||
# define ARCH_CPU_X86_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_ARM_FAMILY)
|
||||
# define ARCH_CPU_ARM_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_MIPS_FAMILY)
|
||||
# define ARCH_CPU_MIPS_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_PPC64_FAMILY)
|
||||
# define ARCH_CPU_PPC64_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_S390_FAMILY)
|
||||
# define ARCH_CPU_S390_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_RISCV_FAMILY)
|
||||
# define ARCH_CPU_RISCV_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_LOONG_FAMILY)
|
||||
# define ARCH_CPU_LOONG_FAMILY 0
|
||||
#endif
|
||||
|
||||
#if !defined(ARCH_CPU_ARM64)
|
||||
# define ARCH_CPU_ARM64 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_ARMEL)
|
||||
# define ARCH_CPU_ARMEL 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_MIPS)
|
||||
# define ARCH_CPU_MIPS 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_MIPS64)
|
||||
# define ARCH_CPU_MIPS64 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_MIPS64EL)
|
||||
# define ARCH_CPU_MIPS64EL 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_MIPSEL)
|
||||
# define ARCH_CPU_MIPSEL 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_PPC64)
|
||||
# define ARCH_CPU_PPC64 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_S390)
|
||||
# define ARCH_CPU_S390 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_S390X)
|
||||
# define ARCH_CPU_S390X 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_X86)
|
||||
# define ARCH_CPU_X86 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_X86_64)
|
||||
# define ARCH_CPU_X86_64 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_RISCV32)
|
||||
# define ARCH_CPU_RISCV32 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_RISCV64)
|
||||
# define ARCH_CPU_RISCV64 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_RISCV128)
|
||||
# define ARCH_CPU_RISCV128 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_LOONG64)
|
||||
# define ARCH_CPU_LOONG64 0
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
122
blender-5.2.0/source/blender/blenlib/BLI_cache_mutex.hh
Normal file
122
blender-5.2.0/source/blender/blenlib/BLI_cache_mutex.hh
Normal file
@@ -0,0 +1,122 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* A #CacheMutex is used to protect a lazily computed cache from being computed more than once.
|
||||
* Using #CacheMutex instead of a "raw mutex" to protect a cache has some benefits:
|
||||
* - Avoid common pitfalls like forgetting to use task isolation or a double checked lock.
|
||||
* - Cleaner and less redundant code because the same locking patterns don't have to be repeated
|
||||
* everywhere.
|
||||
* - One can benefit from potential future improvements to #CacheMutex of which there are a few
|
||||
* mentioned below.
|
||||
*
|
||||
* The data protected by #CacheMutex is not part of #CacheMutex. Instead, the #CacheMutex and its
|
||||
* protected data should generally be placed next to each other.
|
||||
*
|
||||
* Each #CacheMutex protects exactly one cache, so multiple cache mutexes have to be used when a
|
||||
* class has multiple caches. That is contrary to a "custom" solution using `Mutex` where one
|
||||
* mutex could protect multiple caches at the cost of higher lock contention.
|
||||
*
|
||||
* To make sure the cache is up to date, call `CacheMutex::ensure` and pass in the function that
|
||||
* computes the cache.
|
||||
*
|
||||
* To tell the #CacheMutex that the cache is invalidated and to be re-evaluated upon next access
|
||||
* use `CacheMutex::tag_dirty`.
|
||||
*
|
||||
* This example shows how one could implement a lazily computed average vertex position in an
|
||||
* imaginary `Mesh` data structure:
|
||||
*
|
||||
* \code{.cpp}
|
||||
* class Mesh {
|
||||
* private:
|
||||
* mutable CacheMutex average_position_cache_mutex_;
|
||||
* mutable float3 average_position_cache_;
|
||||
*
|
||||
* public:
|
||||
* const float3 &average_position() const
|
||||
* {
|
||||
* average_position_cache_mutex_.ensure([&]() {
|
||||
* average_position_cache_ = actually_compute_average_position();
|
||||
* });
|
||||
* return average_position_cache_;
|
||||
* }
|
||||
*
|
||||
* void tag_positions_changed()
|
||||
* {
|
||||
* average_position_cache_mutex_.tag_dirty();
|
||||
* }
|
||||
* };
|
||||
* \endcode
|
||||
*
|
||||
* Possible future improvements:
|
||||
* - Avoid task isolation when we know that the cache computation does not use threading.
|
||||
* - Try to use a smaller mutex. The mutex does not have to be fair for this use case.
|
||||
* - Try to join the cache computation instead of blocking if another thread is computing the cache
|
||||
* already.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_mutex.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
class CacheMutex {
|
||||
private:
|
||||
Mutex mutex_;
|
||||
std::atomic<bool> cache_valid_ = false;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Make sure the cache exists and is up to date. This calls `compute_cache` once to update the
|
||||
* cache (which is stored outside of this class) if it is dirty, otherwise it does nothing.
|
||||
*
|
||||
* This function is thread-safe under the assumption that the same parameters are passed from
|
||||
* every thread.
|
||||
*/
|
||||
void ensure(const FunctionRef<void()> compute_cache)
|
||||
{
|
||||
/* Handle fast case when the cache is up-to-date. */
|
||||
if (cache_valid_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
this->ensure_impl(compute_cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the cache. The next time #ensure is called, it will recompute that code.
|
||||
*/
|
||||
void tag_dirty()
|
||||
{
|
||||
cache_valid_.store(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the cache currently does not exist or has been invalidated.
|
||||
*/
|
||||
bool is_dirty() const
|
||||
{
|
||||
return !this->is_cached();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the cache exists and is valid.
|
||||
*/
|
||||
bool is_cached() const
|
||||
{
|
||||
return cache_valid_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
void ensure_impl(FunctionRef<void()> compute_cache);
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
155
blender-5.2.0/source/blender/blenlib/BLI_color.hh
Normal file
155
blender-5.2.0/source/blender/blenlib/BLI_color.hh
Normal file
@@ -0,0 +1,155 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "BLI_color_types.hh"
|
||||
#include "BLI_colorspace.hh"
|
||||
#include "BLI_compiler_compat.h"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/*
|
||||
* Stream output.
|
||||
*/
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, const eAlpha &space);
|
||||
std::ostream &operator<<(std::ostream &stream, const eSpace &space);
|
||||
|
||||
template<typename ChannelStorageType, eSpace Space, eAlpha Alpha>
|
||||
std::ostream &operator<<(std::ostream &stream,
|
||||
const ColorRGBA<ChannelStorageType, Space, Alpha> &c);
|
||||
|
||||
namespace color {
|
||||
|
||||
/**
|
||||
* Change precision of color to uint8_t.
|
||||
*/
|
||||
BLI_INLINE ColorTheme4b to_byte(const ColorTheme4f &theme4f)
|
||||
{
|
||||
ColorTheme4b theme4b;
|
||||
rgba_float_to_uchar(theme4b, theme4f);
|
||||
return theme4b;
|
||||
}
|
||||
|
||||
BLI_INLINE ColorTheme4b to_byte(const ColorTheme4b &theme4b)
|
||||
{
|
||||
return theme4b;
|
||||
}
|
||||
|
||||
template<eAlpha Alpha>
|
||||
BLI_INLINE ColorSceneLinearByteEncoded4b<Alpha> encode(const ColorSceneLinear4f<Alpha> &color)
|
||||
{
|
||||
float4 value = static_cast<const float *>(color);
|
||||
if (!colorspace::scene_linear_is_rec709) {
|
||||
copy_v3_v3(value, colorspace::scene_linear_to_rec709 * value.xyz());
|
||||
}
|
||||
ColorSceneLinearByteEncoded4b<Alpha> encoded;
|
||||
linearrgb_to_srgb_uchar4(encoded, value);
|
||||
return encoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change precision of color to float.
|
||||
*/
|
||||
BLI_INLINE ColorTheme4f to_float(const ColorTheme4b &theme4b)
|
||||
{
|
||||
ColorTheme4f theme4f;
|
||||
rgba_uchar_to_float(theme4f, theme4b);
|
||||
return theme4f;
|
||||
}
|
||||
|
||||
BLI_INLINE ColorTheme4f to_float(const ColorTheme4f &theme4f)
|
||||
{
|
||||
return theme4f;
|
||||
}
|
||||
|
||||
template<eAlpha Alpha>
|
||||
BLI_INLINE ColorSceneLinear4f<Alpha> decode(const ColorSceneLinearByteEncoded4b<Alpha> &color)
|
||||
{
|
||||
ColorSceneLinear4f<Alpha> decoded;
|
||||
srgb_to_linearrgb_uchar4(decoded, color);
|
||||
if (!colorspace::scene_linear_is_rec709) {
|
||||
copy_v3_v3(decoded, colorspace::rec709_to_scene_linear * float3(decoded));
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert color and alpha association to premultiplied alpha.
|
||||
*
|
||||
* Does nothing when color already has a premultiplied alpha.
|
||||
*/
|
||||
template<eAlpha Alpha>
|
||||
ColorSceneLinear4f<eAlpha::Premultiplied> premultiply_alpha(const ColorSceneLinear4f<Alpha> &color)
|
||||
{
|
||||
if constexpr (Alpha == eAlpha::Straight) {
|
||||
ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied;
|
||||
straight_to_premul_v4_v4(premultiplied, color);
|
||||
return premultiplied;
|
||||
}
|
||||
else {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert color and alpha association to straight alpha.
|
||||
*
|
||||
* Does nothing when color has straight alpha.
|
||||
*/
|
||||
template<eAlpha Alpha>
|
||||
ColorSceneLinear4f<eAlpha::Straight> unpremultiply_alpha(const ColorSceneLinear4f<Alpha> &color)
|
||||
{
|
||||
if constexpr (Alpha == eAlpha::Premultiplied) {
|
||||
ColorSceneLinear4f<eAlpha::Straight> straighten;
|
||||
premul_to_straight_v4_v4(straighten, color);
|
||||
return straighten;
|
||||
}
|
||||
else {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert between theme and scene linear colors.
|
||||
*/
|
||||
BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> to_scene_linear(const ColorTheme4f &theme4f)
|
||||
{
|
||||
ColorSceneLinear4f<eAlpha::Straight> scene_linear;
|
||||
srgb_to_linearrgb_v4(scene_linear, theme4f);
|
||||
return scene_linear;
|
||||
}
|
||||
|
||||
BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> to_scene_linear(const ColorTheme4b &theme4b)
|
||||
{
|
||||
ColorSceneLinear4f<eAlpha::Straight> scene_linear;
|
||||
srgb_to_linearrgb_uchar4(scene_linear, theme4b);
|
||||
return scene_linear;
|
||||
}
|
||||
|
||||
BLI_INLINE ColorTheme4f to_theme4f(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear)
|
||||
{
|
||||
ColorTheme4f theme4f;
|
||||
linearrgb_to_srgb_v4(theme4f, scene_linear);
|
||||
return theme4f;
|
||||
}
|
||||
|
||||
BLI_INLINE ColorTheme4b to_theme4b(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear)
|
||||
{
|
||||
ColorTheme4b theme4b;
|
||||
linearrgb_to_srgb_uchar4(theme4b, scene_linear);
|
||||
return theme4b;
|
||||
}
|
||||
|
||||
} // namespace color
|
||||
} // namespace blender
|
||||
1064
blender-5.2.0/source/blender/blenlib/BLI_color_mix.hh
Normal file
1064
blender-5.2.0/source/blender/blenlib/BLI_color_mix.hh
Normal file
File diff suppressed because it is too large
Load Diff
222
blender-5.2.0/source/blender/blenlib/BLI_color_types.hh
Normal file
222
blender-5.2.0/source/blender/blenlib/BLI_color_types.hh
Normal file
@@ -0,0 +1,222 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include "BLI_unique_hash.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* CPP based color structures.
|
||||
*
|
||||
* Strongly typed color storage structures with space and alpha association.
|
||||
* Will increase readability and visibility of typical mistakes when
|
||||
* working with colors.
|
||||
*
|
||||
* The storage structs can hold 4 channels (r, g, b and a).
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* Convert a theme byte color to a linearrgb premultiplied.
|
||||
* \code{.cc}
|
||||
* ColorTheme4b theme_color;
|
||||
* ColorSceneLinear4f<eAlpha::Premultiplied> linearrgb_color =
|
||||
* BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha();
|
||||
* \endcode
|
||||
*
|
||||
* The API is structured to make most use of inlining. Most notable are space
|
||||
* conversions done via `BLI_color_convert_to*` functions.
|
||||
*
|
||||
* - Conversions between spaces (theme <=> scene linear) should always be done by
|
||||
* invoking the `BLI_color_convert_to*` methods.
|
||||
* - Encoding colors (compressing to store colors inside a less precision storage)
|
||||
* should be done by invoking the `encode` and `decode` methods.
|
||||
* - Changing alpha association should be done by invoking `premultiply_alpha` or
|
||||
* `unpremultiply_alpha` methods.
|
||||
*
|
||||
* # Encoding.
|
||||
*
|
||||
* Color encoding is used to store colors with less precision as in using `uint8_t` in
|
||||
* stead of `float`. This encoding is supported for `eSpace::SceneLinear`.
|
||||
* To make this clear to the developer the `eSpace::SceneLinearByteEncoded`
|
||||
* space is added.
|
||||
*
|
||||
* # Precision
|
||||
*
|
||||
* Colors can be stored using `uint8_t` or `float` colors. The conversion
|
||||
* between the two precisions are available as methods. (`to_4b` and
|
||||
* `to_4f`).
|
||||
*
|
||||
* # Alpha conversion
|
||||
*
|
||||
* Alpha conversion is only supported in SceneLinear space.
|
||||
*
|
||||
* Extending this file:
|
||||
* - This file can be extended with `ColorHex/Hsl/Hsv` for different representations
|
||||
* of rgb based colors. `ColorHsl4f<eSpace::SceneLinear, eAlpha::Premultiplied>`
|
||||
* - Add non RGB spaces/storage for ColorXyz.
|
||||
*/
|
||||
|
||||
/** Enumeration containing the different alpha modes. */
|
||||
enum class eAlpha {
|
||||
/** Color and alpha are unassociated. */
|
||||
Straight,
|
||||
/** Color and alpha are associated. */
|
||||
Premultiplied,
|
||||
};
|
||||
|
||||
/** Enumeration containing internal spaces. */
|
||||
enum class eSpace {
|
||||
/** Blender theme color space (sRGB). */
|
||||
Theme,
|
||||
/** Blender internal scene linear color space (maps to scene_linear role in OCIO). */
|
||||
SceneLinear,
|
||||
/** Blender internal scene linear color space compressed to be stored in 4 uint8_t. */
|
||||
SceneLinearByteEncoded,
|
||||
};
|
||||
|
||||
/** Template class to store RGBA values with different precision, space, and alpha association. */
|
||||
template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGBA {
|
||||
public:
|
||||
ChannelStorageType r, g, b, a;
|
||||
constexpr ColorRGBA() = default;
|
||||
|
||||
constexpr ColorRGBA(const ChannelStorageType rgba[4])
|
||||
: r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3])
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ColorRGBA(const ChannelStorageType r,
|
||||
const ChannelStorageType g,
|
||||
const ChannelStorageType b,
|
||||
const ChannelStorageType a)
|
||||
: r(r), g(g), b(b), a(a)
|
||||
{
|
||||
}
|
||||
|
||||
operator ChannelStorageType *()
|
||||
{
|
||||
return &r;
|
||||
}
|
||||
|
||||
operator const ChannelStorageType *() const
|
||||
{
|
||||
return &r;
|
||||
}
|
||||
|
||||
friend bool operator==(const ColorRGBA &a, const ColorRGBA &b) = default;
|
||||
|
||||
constexpr uint64_t hash() const
|
||||
{
|
||||
return get_default_hash(r, g, b, a);
|
||||
}
|
||||
|
||||
void hash_unique(UniqueHashBytes &hash) const
|
||||
{
|
||||
hash_unique_default(r, hash);
|
||||
hash_unique_default(g, hash);
|
||||
hash_unique_default(b, hash);
|
||||
hash_unique_default(a, hash);
|
||||
}
|
||||
};
|
||||
|
||||
/* Forward declarations of concrete color classes. */
|
||||
|
||||
template<eAlpha Alpha> class ColorSceneLinear4f;
|
||||
template<eAlpha Alpha> class ColorSceneLinearByteEncoded4b;
|
||||
template<typename ChannelStorageType> class ColorTheme4;
|
||||
|
||||
template<eAlpha Alpha>
|
||||
class ColorSceneLinear4f final : public ColorRGBA<float, eSpace::SceneLinear, Alpha> {
|
||||
public:
|
||||
constexpr ColorSceneLinear4f() = default;
|
||||
|
||||
constexpr explicit ColorSceneLinear4f(float value)
|
||||
: ColorRGBA<float, eSpace::SceneLinear, Alpha>(value, value, value, value)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr explicit ColorSceneLinear4f(U value)
|
||||
requires(std::is_convertible_v<U, float>)
|
||||
: ColorSceneLinear4f(float(value))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ColorSceneLinear4f(const float *rgba)
|
||||
: ColorRGBA<float, eSpace::SceneLinear, Alpha>(rgba)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ColorSceneLinear4f(float r, float g, float b, float a)
|
||||
: ColorRGBA<float, eSpace::SceneLinear, Alpha>(r, g, b, a)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<eAlpha Alpha>
|
||||
class ColorSceneLinearByteEncoded4b final
|
||||
: public ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha> {
|
||||
public:
|
||||
constexpr ColorSceneLinearByteEncoded4b() = default;
|
||||
|
||||
constexpr ColorSceneLinearByteEncoded4b(const uint8_t *rgba)
|
||||
: ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(rgba)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ColorSceneLinearByteEncoded4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
: ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(r, g, b, a)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme color template class.
|
||||
*
|
||||
* Don't use directly, but use `ColorTheme4b/ColorTheme4b`.
|
||||
*
|
||||
* This has been implemented as a template to improve inlining. When implemented as concrete
|
||||
* classes (ColorTheme4b/f) the functions would be hidden in a compile unit what wouldn't be
|
||||
* inlined.
|
||||
*/
|
||||
template<typename ChannelStorageType>
|
||||
class ColorTheme4 final : public ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight> {
|
||||
public:
|
||||
constexpr ColorTheme4() = default;
|
||||
|
||||
constexpr ColorTheme4(const ChannelStorageType *rgba)
|
||||
: ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(rgba)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ColorTheme4(ChannelStorageType r,
|
||||
ChannelStorageType g,
|
||||
ChannelStorageType b,
|
||||
ChannelStorageType a)
|
||||
: ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(r, g, b, a)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using ColorTheme4b = ColorTheme4<uint8_t>;
|
||||
using ColorTheme4f = ColorTheme4<float>;
|
||||
|
||||
/* Internal roles. For convenience to shorten the type names and hide complexity. */
|
||||
|
||||
using ColorGeometry4f = ColorSceneLinear4f<eAlpha::Premultiplied>;
|
||||
using ColorGeometry4b = ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied>;
|
||||
using ColorPaint4f = ColorSceneLinear4f<eAlpha::Straight>;
|
||||
using ColorPaint4b = ColorSceneLinearByteEncoded4b<eAlpha::Straight>;
|
||||
|
||||
} // namespace blender
|
||||
38
blender-5.2.0/source/blender/blenlib/BLI_colorspace.hh
Normal file
38
blender-5.2.0/source/blender/blenlib/BLI_colorspace.hh
Normal file
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace colorspace {
|
||||
|
||||
/* Coefficients to compute luma from RGB. */
|
||||
extern float3 luma_coefficients;
|
||||
|
||||
/* Conversion between scene linear and common linear spaces. */
|
||||
extern float3x3 scene_linear_to_xyz;
|
||||
extern float3x3 xyz_to_scene_linear;
|
||||
extern float3x3 scene_linear_to_aces;
|
||||
extern float3x3 aces_to_scene_linear;
|
||||
extern float3x3 scene_linear_to_acescg;
|
||||
extern float3x3 acescg_to_scene_linear;
|
||||
extern float3x3 scene_linear_to_rec709;
|
||||
extern float3x3 rec709_to_scene_linear;
|
||||
extern float3x3 scene_linear_to_rec2020;
|
||||
extern float3x3 rec2020_to_scene_linear;
|
||||
|
||||
/* For fast path when converting to/from sRGB. */
|
||||
extern bool scene_linear_is_rec709;
|
||||
|
||||
}; // namespace colorspace
|
||||
|
||||
} // namespace blender
|
||||
92
blender-5.2.0/source/blender/blenlib/BLI_compiler_attrs.h
Normal file
92
blender-5.2.0/source/blender/blenlib/BLI_compiler_attrs.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
/* hint to make sure function result is actually used */
|
||||
#ifdef __GNUC__
|
||||
# define ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
#else
|
||||
# define ATTR_WARN_UNUSED_RESULT
|
||||
#endif
|
||||
|
||||
/* hint to mark function arguments expected to be non-null
|
||||
* if no arguments are given to the macro, all of pointer
|
||||
* arguments would be expected to be non-null
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
# define ATTR_NONNULL(args...) __attribute__((nonnull(args)))
|
||||
#else
|
||||
# define ATTR_NONNULL(...)
|
||||
#endif
|
||||
|
||||
/* never returns NULL */
|
||||
#ifdef __GNUC__
|
||||
# define ATTR_RETURNS_NONNULL __attribute__((returns_nonnull))
|
||||
#else
|
||||
# define ATTR_RETURNS_NONNULL
|
||||
#endif
|
||||
|
||||
/* hint to mark function as it wouldn't return */
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define ATTR_NORETURN __attribute__((noreturn))
|
||||
#else
|
||||
# define ATTR_NORETURN
|
||||
#endif
|
||||
|
||||
/* hint to treat any non-null function return value cannot alias any other pointer */
|
||||
#ifdef __GNUC__
|
||||
# define ATTR_MALLOC __attribute__((malloc))
|
||||
#else
|
||||
# define ATTR_MALLOC
|
||||
#endif
|
||||
|
||||
/* the function return value points to memory (2 args for 'size * tot') */
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
# define ATTR_ALLOC_SIZE(args...) __attribute__((alloc_size(args)))
|
||||
#else
|
||||
# define ATTR_ALLOC_SIZE(...)
|
||||
#endif
|
||||
|
||||
/* ensures a NULL terminating argument as the n'th last argument of a variadic function */
|
||||
#ifdef __GNUC__
|
||||
# define ATTR_SENTINEL(arg_pos) __attribute__((sentinel(arg_pos)))
|
||||
#else
|
||||
# define ATTR_SENTINEL(arg_pos)
|
||||
#endif
|
||||
|
||||
/* hint to compiler that function uses printf-style format string */
|
||||
#ifdef __GNUC__
|
||||
# define ATTR_PRINTF_FORMAT(format_param, dots_param) \
|
||||
__attribute__((format(printf, format_param, dots_param)))
|
||||
#else
|
||||
# define ATTR_PRINTF_FORMAT(format_param, dots_param)
|
||||
#endif
|
||||
|
||||
/* Use to suppress `-Wimplicit-fallthrough` (in place of `break`). */
|
||||
#ifndef ATTR_FALLTHROUGH
|
||||
# ifdef __GNUC__
|
||||
# define ATTR_FALLTHROUGH __attribute__((fallthrough))
|
||||
# else
|
||||
# define ATTR_FALLTHROUGH ((void)0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Declare the memory alignment in Bytes. */
|
||||
#if defined(_WIN32) && !defined(FREE_WINDOWS)
|
||||
# define ATTR_ALIGN(x) __declspec(align(x))
|
||||
#else
|
||||
# define ATTR_ALIGN(x) __attribute__((aligned(x)))
|
||||
#endif
|
||||
|
||||
/* Alignment directive */
|
||||
#ifdef _WIN64
|
||||
# define BLI_ALIGN_STRUCT __declspec(align(64))
|
||||
#else
|
||||
# define BLI_ALIGN_STRUCT
|
||||
#endif
|
||||
52
blender-5.2.0/source/blender/blenlib/BLI_compiler_compat.h
Normal file
52
blender-5.2.0/source/blender/blenlib/BLI_compiler_compat.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* `#define typeof()` triggers a bug in some clang-format versions,
|
||||
* disable format for entire file to keep results consistent. */
|
||||
/* clang-format off */
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* Use to help with cross platform portability.
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define alloca _alloca
|
||||
#endif
|
||||
|
||||
#if (defined(__GNUC__) || defined(__clang__)) && defined(__cplusplus)
|
||||
extern "C++" {
|
||||
/** Some magic to be sure we don't have reference in the type. */
|
||||
template<typename T> static inline T decltype_helper(T x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
#define typeof(x) decltype(decltype_helper(x))
|
||||
}
|
||||
#endif
|
||||
|
||||
/* little macro so inline keyword works */
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# define BLI_INLINE static __forceinline
|
||||
#else
|
||||
# define BLI_INLINE static inline __attribute__((always_inline)) __attribute__((__unused__))
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# define BLI_INLINE_METHOD __forceinline
|
||||
#else
|
||||
# define BLI_INLINE_METHOD inline __attribute__((always_inline)) __attribute__((__unused__))
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define BLI_NOINLINE __attribute__((noinline))
|
||||
#elif defined(_MSC_VER)
|
||||
# define BLI_NOINLINE __declspec(noinline)
|
||||
#else
|
||||
# define BLI_NOINLINE
|
||||
#endif
|
||||
682
blender-5.2.0/source/blender/blenlib/BLI_compiler_typecheck.h
Normal file
682
blender-5.2.0/source/blender/blenlib/BLI_compiler_typecheck.h
Normal file
@@ -0,0 +1,682 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* Type checking macros (often used to ensure valid use of macro args).
|
||||
* These depend on compiler extensions and c11 in some cases.
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines_variadic.h"
|
||||
|
||||
/* Causes warning:
|
||||
* incompatible types when assigning to type 'Foo' from type 'Bar'
|
||||
* ... the compiler optimizes away the temp var */
|
||||
#ifdef __GNUC__
|
||||
# define CHECK_TYPE(var, type) \
|
||||
{ \
|
||||
typeof(var) *__tmp = (type *)NULL; \
|
||||
(void)__tmp; \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
# define CHECK_TYPE_PAIR(var_a, var_b) \
|
||||
{ \
|
||||
const typeof(var_a) *__tmp = (typeof(var_b) *)NULL; \
|
||||
(void)__tmp; \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
# define CHECK_TYPE_PAIR_INLINE(var_a, var_b) \
|
||||
((void)({ \
|
||||
const typeof(var_a) *__tmp = (typeof(var_b) *)NULL; \
|
||||
(void)__tmp; \
|
||||
}))
|
||||
|
||||
#else
|
||||
# define CHECK_TYPE(var, type) \
|
||||
{ \
|
||||
EXPR_NOP(var); \
|
||||
} \
|
||||
(void)0
|
||||
# define CHECK_TYPE_PAIR(var_a, var_b) \
|
||||
{ \
|
||||
(EXPR_NOP(var_a), EXPR_NOP(var_b)); \
|
||||
} \
|
||||
(void)0
|
||||
# define CHECK_TYPE_PAIR_INLINE(var_a, var_b) (EXPR_NOP(var_a), EXPR_NOP(var_b))
|
||||
#endif
|
||||
|
||||
/* can be used in simple macros */
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
|
||||
# define CHECK_TYPE_INLINE(val, type) \
|
||||
(void)((void)(((type)0) != (0 ? (val) : ((type)0))), _Generic((val), type: 0, const type: 0))
|
||||
/* NOTE: The NONCONST version is needed for scalar types on CLANG, to avoid warnings. */
|
||||
# define CHECK_TYPE_INLINE_NONCONST(val, type) \
|
||||
(void)((void)(((type)0) != (0 ? (val) : ((type)0))), _Generic((val), type: 0))
|
||||
#else
|
||||
# define CHECK_TYPE_INLINE_NONCONST(val, type) ((void)(((type)0) != (0 ? (val) : ((type)0))))
|
||||
# define CHECK_TYPE_INLINE(val, type) ((void)(((type)0) != (0 ? (val) : ((type)0))))
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define CHECK_TYPE_NONCONST(var) \
|
||||
__extension__({ \
|
||||
void *non_const = 0 ? (var) : NULL; \
|
||||
(void)non_const; \
|
||||
})
|
||||
#else
|
||||
# define CHECK_TYPE_NONCONST(var) EXPR_NOP(var)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* CHECK_TYPE_ANY: handy macro, eg:
|
||||
* `CHECK_TYPE_ANY(var, Foo *, Bar *, Baz *)`
|
||||
*
|
||||
* excuse ridiculously long generated args.
|
||||
* \code{.py}
|
||||
* for i in range(63):
|
||||
* args = [(chr(ord('a') + (c % 26)) + (chr(ord('0') + (c // 26)))) for c in range(i + 1)]
|
||||
* print("#define _VA_CHECK_TYPE_ANY%d(v, %s) \\" % (i + 2, ", ".join(args)))
|
||||
* print(" ((void)_Generic((v), %s))" % (": 0, ".join(args) + ": 0"))
|
||||
* \endcode
|
||||
*/
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
|
||||
/* Over wrapped args. */
|
||||
/* clang-format off */
|
||||
#define _VA_CHECK_TYPE_ANY2(v, a0) \
|
||||
((void)_Generic((v), a0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY3(v, a0, b0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY4(v, a0, b0, c0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY5(v, a0, b0, c0, d0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY6(v, a0, b0, c0, d0, e0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY7(v, a0, b0, c0, d0, e0, f0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY8(v, a0, b0, c0, d0, e0, f0, g0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY9(v, a0, b0, c0, d0, e0, f0, g0, h0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY10(v, a0, b0, c0, d0, e0, f0, g0, h0, i0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY11(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY12(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY13(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY14(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY15(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY16(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY17(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY18(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY19(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY20(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY21(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY22(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY23(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY24(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY25(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY26(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY27(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0))
|
||||
#define _VA_CHECK_TYPE_ANY28(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY29(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY30(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY31(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY32(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY33(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY34(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY35(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY36(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY37(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY38(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY39(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY40(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY41(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY42(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY43(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY44(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY45(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY46(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY47(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY48(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY49(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY50(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY51(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY52(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY53(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0))
|
||||
#define _VA_CHECK_TYPE_ANY54(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1, a2) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0, a2: 0))
|
||||
#define _VA_CHECK_TYPE_ANY55(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1, a2, b2) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0, a2: 0, b2: 0))
|
||||
#define _VA_CHECK_TYPE_ANY56(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1, a2, b2, c2) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0, a2: 0, b2: 0, c2: 0))
|
||||
#define _VA_CHECK_TYPE_ANY57(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1, a2, b2, c2, d2) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0, a2: 0, b2: 0, c2: 0, d2: 0))
|
||||
#define _VA_CHECK_TYPE_ANY58(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1, a2, b2, c2, d2, e2) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0, a2: 0, b2: 0, c2: 0, d2: 0, e2: 0))
|
||||
#define _VA_CHECK_TYPE_ANY59(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1, a2, b2, c2, d2, e2, f2) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0, a2: 0, b2: 0, c2: 0, d2: 0, e2: 0, f2: 0))
|
||||
#define _VA_CHECK_TYPE_ANY60(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1, a2, b2, c2, d2, e2, f2, g2) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0, a2: 0, b2: 0, c2: 0, d2: 0, e2: 0, f2: 0, g2: 0))
|
||||
#define _VA_CHECK_TYPE_ANY61(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1, a2, b2, c2, d2, e2, f2, g2, h2) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0, a2: 0, b2: 0, c2: 0, d2: 0, e2: 0, f2: 0, g2: 0, h2: 0))
|
||||
#define _VA_CHECK_TYPE_ANY62(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1, a2, b2, c2, d2, e2, f2, g2, h2, i2) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0, a2: 0, b2: 0, c2: 0, d2: 0, e2: 0, f2: 0, g2: 0, h2: 0, i2: 0))
|
||||
#define _VA_CHECK_TYPE_ANY63(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1, a2, b2, c2, d2, e2, f2, g2, h2, i2, j2) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0, a2: 0, b2: 0, c2: 0, d2: 0, e2: 0, f2: 0, g2: 0, h2: 0, i2: 0, \
|
||||
j2: 0))
|
||||
#define _VA_CHECK_TYPE_ANY64(v, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, u0, \
|
||||
v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, \
|
||||
x1, y1, z1, a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2) \
|
||||
((void)_Generic((v), a0: 0, b0: 0, c0: 0, d0: 0, e0: 0, f0: 0, g0: 0, h0: 0, i0: 0, j0: 0, k0: 0, l0: 0, m0: 0, \
|
||||
n0: 0, o0: 0, p0: 0, q0: 0, r0: 0, s0: 0, t0: 0, u0: 0, v0: 0, w0: 0, x0: 0, y0: 0, z0: 0, a1: 0, b1: 0, c1: 0, \
|
||||
d1: 0, e1: 0, f1: 0, g1: 0, h1: 0, i1: 0, j1: 0, k1: 0, l1: 0, m1: 0, n1: 0, o1: 0, p1: 0, q1: 0, r1: 0, s1: 0, \
|
||||
t1: 0, u1: 0, v1: 0, w1: 0, x1: 0, y1: 0, z1: 0, a2: 0, b2: 0, c2: 0, d2: 0, e2: 0, f2: 0, g2: 0, h2: 0, i2: 0, \
|
||||
j2: 0, k2: 0))
|
||||
# define CHECK_TYPE_ANY(...) VA_NARGS_CALL_OVERLOAD(_VA_CHECK_TYPE_ANY, __VA_ARGS__)
|
||||
/* clang-format on */
|
||||
#else
|
||||
# define CHECK_TYPE_ANY(...) (void)0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* GENERIC_TYPE_ANY: handy macro to reuse a single expression for multiple types, eg:
|
||||
*
|
||||
* \code{.c}
|
||||
* _Generic(value,
|
||||
* GENERIC_TYPE_ANY(result_a, Foo *, Bar *, Baz *),
|
||||
* GENERIC_TYPE_ANY(result_b, Spam *, Spaz *, Spot *),
|
||||
* )
|
||||
* \endcode
|
||||
*
|
||||
* excuse ridiculously long generated args.
|
||||
* \code{.py}
|
||||
* for i in range(63):
|
||||
* args = [(chr(ord('a') + (c % 26)) + (chr(ord('0') + (c // 26)))) for c in range(i + 1)]
|
||||
* print("#define _VA_GENERIC_TYPE_ANY%d(r, %s) \\" % (i + 2, ", ".join(args)))
|
||||
* print(" %s: r " % (": r, ".join(args)))
|
||||
* \endcode
|
||||
*/
|
||||
/* Over wrapped args. */
|
||||
/* clang-format off */
|
||||
#define _VA_GENERIC_TYPE_ANY2(r, a0) \
|
||||
a0: r
|
||||
#define _VA_GENERIC_TYPE_ANY3(r, a0, b0) \
|
||||
a0: r, b0: r
|
||||
#define _VA_GENERIC_TYPE_ANY4(r, a0, b0, c0) \
|
||||
a0: r, b0: r, c0: r
|
||||
#define _VA_GENERIC_TYPE_ANY5(r, a0, b0, c0, d0) \
|
||||
a0: r, b0: r, c0: r, d0: r
|
||||
#define _VA_GENERIC_TYPE_ANY6(r, a0, b0, c0, d0, e0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r
|
||||
#define _VA_GENERIC_TYPE_ANY7(r, a0, b0, c0, d0, e0, f0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r
|
||||
#define _VA_GENERIC_TYPE_ANY8(r, a0, b0, c0, d0, e0, f0, g0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r
|
||||
#define _VA_GENERIC_TYPE_ANY9(r, a0, b0, c0, d0, e0, f0, g0, h0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r
|
||||
#define _VA_GENERIC_TYPE_ANY10(r, a0, b0, c0, d0, e0, f0, g0, h0, i0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r
|
||||
#define _VA_GENERIC_TYPE_ANY11(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r
|
||||
#define _VA_GENERIC_TYPE_ANY12(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r
|
||||
#define _VA_GENERIC_TYPE_ANY13(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r
|
||||
#define _VA_GENERIC_TYPE_ANY14(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r
|
||||
#define _VA_GENERIC_TYPE_ANY15(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r
|
||||
#define _VA_GENERIC_TYPE_ANY16(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r
|
||||
#define _VA_GENERIC_TYPE_ANY17(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r
|
||||
#define _VA_GENERIC_TYPE_ANY18(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r
|
||||
#define _VA_GENERIC_TYPE_ANY19(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r
|
||||
#define _VA_GENERIC_TYPE_ANY20(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r
|
||||
#define _VA_GENERIC_TYPE_ANY21(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r
|
||||
#define _VA_GENERIC_TYPE_ANY22(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r
|
||||
#define _VA_GENERIC_TYPE_ANY23(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r
|
||||
#define _VA_GENERIC_TYPE_ANY24(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r
|
||||
#define _VA_GENERIC_TYPE_ANY25(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r
|
||||
#define _VA_GENERIC_TYPE_ANY26(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r
|
||||
#define _VA_GENERIC_TYPE_ANY27(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r
|
||||
#define _VA_GENERIC_TYPE_ANY28(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r
|
||||
#define _VA_GENERIC_TYPE_ANY29(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r
|
||||
#define _VA_GENERIC_TYPE_ANY30(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r
|
||||
#define _VA_GENERIC_TYPE_ANY31(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r
|
||||
#define _VA_GENERIC_TYPE_ANY32(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r
|
||||
#define _VA_GENERIC_TYPE_ANY33(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r
|
||||
#define _VA_GENERIC_TYPE_ANY34(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r
|
||||
#define _VA_GENERIC_TYPE_ANY35(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r
|
||||
#define _VA_GENERIC_TYPE_ANY36(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r
|
||||
#define _VA_GENERIC_TYPE_ANY37(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r
|
||||
#define _VA_GENERIC_TYPE_ANY38(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r
|
||||
#define _VA_GENERIC_TYPE_ANY39(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r
|
||||
#define _VA_GENERIC_TYPE_ANY40(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r
|
||||
#define _VA_GENERIC_TYPE_ANY41(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r
|
||||
#define _VA_GENERIC_TYPE_ANY42(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r
|
||||
#define _VA_GENERIC_TYPE_ANY43(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r
|
||||
#define _VA_GENERIC_TYPE_ANY44(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r
|
||||
#define _VA_GENERIC_TYPE_ANY45(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r
|
||||
#define _VA_GENERIC_TYPE_ANY46(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r
|
||||
#define _VA_GENERIC_TYPE_ANY47(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r
|
||||
#define _VA_GENERIC_TYPE_ANY48(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r
|
||||
#define _VA_GENERIC_TYPE_ANY49(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r
|
||||
#define _VA_GENERIC_TYPE_ANY50(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r
|
||||
#define _VA_GENERIC_TYPE_ANY51(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r
|
||||
#define _VA_GENERIC_TYPE_ANY52(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r
|
||||
#define _VA_GENERIC_TYPE_ANY53(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r
|
||||
#define _VA_GENERIC_TYPE_ANY54(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1, a2) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r, a2: r
|
||||
#define _VA_GENERIC_TYPE_ANY55(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1, a2, b2) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r, a2: r, b2: r
|
||||
#define _VA_GENERIC_TYPE_ANY56(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1, a2, b2, c2) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r, a2: r, b2: r, c2: r
|
||||
#define _VA_GENERIC_TYPE_ANY57(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1, a2, b2, c2, d2) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r, a2: r, b2: r, c2: r, d2: r
|
||||
#define _VA_GENERIC_TYPE_ANY58(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1, a2, b2, c2, d2, e2) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r, a2: r, b2: r, c2: r, d2: r, e2: r
|
||||
#define _VA_GENERIC_TYPE_ANY59(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1, a2, b2, c2, d2, e2, f2) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r, a2: r, b2: r, c2: r, d2: r, e2: r, f2: r
|
||||
#define _VA_GENERIC_TYPE_ANY60(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1, a2, b2, c2, d2, e2, f2, g2) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r, a2: r, b2: r, c2: r, d2: r, e2: r, f2: r, g2: r
|
||||
#define _VA_GENERIC_TYPE_ANY61(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1, a2, b2, c2, d2, e2, f2, g2, h2) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r, a2: r, b2: r, c2: r, d2: r, e2: r, f2: r, g2: r, h2: r
|
||||
#define _VA_GENERIC_TYPE_ANY62(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1, a2, b2, c2, d2, e2, f2, g2, h2, i2) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r, a2: r, b2: r, c2: r, d2: r, e2: r, f2: r, g2: r, h2: r, i2: r
|
||||
#define _VA_GENERIC_TYPE_ANY63(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1, a2, b2, c2, d2, e2, f2, g2, h2, i2, j2) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r, a2: r, b2: r, c2: r, d2: r, e2: r, f2: r, g2: r, h2: r, i2: r, j2: r
|
||||
#define _VA_GENERIC_TYPE_ANY64(r, a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0, p0, q0, r0, s0, t0, \
|
||||
u0, v0, w0, x0, y0, z0, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, \
|
||||
w1, x1, y1, z1, a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2) \
|
||||
a0: r, b0: r, c0: r, d0: r, e0: r, f0: r, g0: r, h0: r, i0: r, j0: r, k0: r, l0: r, m0: r, n0: r, o0: r, p0: r, \
|
||||
q0: r, r0: r, s0: r, t0: r, u0: r, v0: r, w0: r, x0: r, y0: r, z0: r, a1: r, b1: r, c1: r, d1: r, e1: r, f1: r, \
|
||||
g1: r, h1: r, i1: r, j1: r, k1: r, l1: r, m1: r, n1: r, o1: r, p1: r, q1: r, r1: r, s1: r, t1: r, u1: r, v1: r, \
|
||||
w1: r, x1: r, y1: r, z1: r, a2: r, b2: r, c2: r, d2: r, e2: r, f2: r, g2: r, h2: r, i2: r, j2: r, k2: r
|
||||
/* clang-format on */
|
||||
|
||||
#define GENERIC_TYPE_ANY(...) VA_NARGS_CALL_OVERLOAD(_VA_GENERIC_TYPE_ANY, __VA_ARGS__)
|
||||
43
blender-5.2.0/source/blender/blenlib/BLI_compression.hh
Normal file
43
blender-5.2.0/source/blender/blenlib/BLI_compression.hh
Normal file
@@ -0,0 +1,43 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* Utilities for lossless data compression.
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Transforms array of data, making it more compressible,
|
||||
* especially if data is smoothly varying. Typically you do
|
||||
* this before compression with a general purpose compressor.
|
||||
*
|
||||
* Transposes input array so that output data is first byte of
|
||||
* all items, then 2nd byte of all items, etc. And successive
|
||||
* items within each "byte stream" are stored as difference
|
||||
* from previous byte.
|
||||
*
|
||||
* See https://aras-p.info/blog/2023/03/01/Float-Compression-7-More-Filtering-Optimization/
|
||||
* for details.
|
||||
*/
|
||||
void filter_transpose_delta(const uint8_t *src, uint8_t *dst, size_t items_num, size_t item_size);
|
||||
|
||||
/**
|
||||
* Reverses the data transform done by #unfilter_transpose_delta.
|
||||
* Typically you do this after decompression with a general purpose
|
||||
* compressor.
|
||||
*/
|
||||
void unfilter_transpose_delta(const uint8_t *src,
|
||||
uint8_t *dst,
|
||||
size_t items_num,
|
||||
size_t item_size);
|
||||
|
||||
} // namespace blender
|
||||
174
blender-5.2.0/source/blender/blenlib/BLI_compute_context.hh
Normal file
174
blender-5.2.0/source/blender/blenlib/BLI_compute_context.hh
Normal file
@@ -0,0 +1,174 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* When logging computed values, we generally want to know where the value was computed. For
|
||||
* example, geometry nodes logs socket values so that they can be displayed in the ui. For that we
|
||||
* can combine the logged value with a `ComputeContext`, which identifies the place where the value
|
||||
* was computed.
|
||||
*
|
||||
* This is not a trivial problem because e.g. just storing a pointer to the socket a value
|
||||
* belongs to is not enough. That's because the same socket may correspond to many different values
|
||||
* when the socket is used in a node group that is used multiple times. In this case, not only does
|
||||
* the socket have to be stored but also the entire nested node group path that led to the
|
||||
* evaluation of the socket.
|
||||
*
|
||||
* Storing the entire "context path" for every logged value is not feasible, because that path can
|
||||
* become quite long. So that would need much more memory, more compute overhead and makes it
|
||||
* complicated to compare if two contexts are the same. If the identifier for a compute context
|
||||
* would have a variable size, it would also be much harder to create a map from context to values.
|
||||
*
|
||||
* The solution implemented below uses the following key ideas:
|
||||
* - Every compute context can be hashed to a unique fixed size value (`ComputeContextHash`). While
|
||||
* technically there could be hash collisions, the hashing algorithm has to be chosen to make
|
||||
* that practically impossible. This way an entire context path, possibly consisting of many
|
||||
* nested contexts, is represented by a single value that can be stored easily.
|
||||
* - A nested compute context is build as singly linked list, where every compute context has a
|
||||
* pointer to the parent compute context. Note that a link in the other direction is not possible
|
||||
* because the same parent compute context may be used by many different children which possibly
|
||||
* run on different threads.
|
||||
*/
|
||||
|
||||
#include "BLI_cache_mutex.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_unique_hash.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
class ComputeContext;
|
||||
|
||||
/**
|
||||
* A hash that uniquely identifies a specific (non-fixed-size) compute context.
|
||||
*/
|
||||
struct ComputeContextHash : public UniqueHash {
|
||||
/**
|
||||
* Standard way to create a compute context hash.
|
||||
* \param parent: The optional parent context.
|
||||
* \param type_str: A string literal that identifies the context type. This is used to avoid hash
|
||||
* collisions between different context types.
|
||||
* \param args: Additional arguments that affect the hash. Note that only the shallow bytes of
|
||||
* these types are used. So they generally should not contain any padding.
|
||||
*/
|
||||
template<size_t N, typename... Args>
|
||||
static ComputeContextHash from(const ComputeContext *parent,
|
||||
const char (&type_str)[N],
|
||||
Args &&...args);
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const ComputeContextHash &hash);
|
||||
|
||||
/**
|
||||
* Compute a context hash by packing all the arguments into a contiguous buffer and hashing
|
||||
* that.
|
||||
*/
|
||||
template<typename... Args> static ComputeContextHash from_shallow_bytes(Args &&...args);
|
||||
|
||||
/** Compute a context hash from a contiguous buffer. */
|
||||
static ComputeContextHash from_bytes(const void *data, int64_t len);
|
||||
};
|
||||
|
||||
/**
|
||||
* Identifies the context in which a computation happens. This context can be used to identify
|
||||
* values logged during the computation. For more details, see the comment at the top of the file.
|
||||
*
|
||||
* This class should be subclassed to implement specific contexts.
|
||||
*/
|
||||
class ComputeContext {
|
||||
protected:
|
||||
/**
|
||||
* Pointer to the context that this context is child of. That allows nesting compute
|
||||
* contexts.
|
||||
*/
|
||||
const ComputeContext *parent_ = nullptr;
|
||||
|
||||
/**
|
||||
* The hash that uniquely identifies this context. It's a combined hash of this context as well
|
||||
* as all the parent contexts. It's computed lazily to keep initial construction of compute
|
||||
* contexts very cheap.
|
||||
*/
|
||||
mutable ComputeContextHash hash_;
|
||||
|
||||
private:
|
||||
mutable CacheMutex hash_mutex_;
|
||||
|
||||
/**
|
||||
* Number of parent contexts. This can be used to limit the maximum depth to prevent
|
||||
* stack-overflows.
|
||||
*/
|
||||
int parents_num_ = 0;
|
||||
|
||||
public:
|
||||
ComputeContext(const ComputeContext *parent)
|
||||
: parent_(parent), parents_num_(parent ? parent->parents_num_ + 1 : 0)
|
||||
{
|
||||
}
|
||||
virtual ~ComputeContext() = default;
|
||||
|
||||
const ComputeContextHash &hash() const
|
||||
{
|
||||
hash_mutex_.ensure([&]() { hash_ = this->compute_hash(); });
|
||||
return hash_;
|
||||
}
|
||||
|
||||
const ComputeContext *parent() const
|
||||
{
|
||||
return parent_;
|
||||
}
|
||||
|
||||
int parents_num() const
|
||||
{
|
||||
return parents_num_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the entire nested context stack.
|
||||
*/
|
||||
void print_stack(std::ostream &stream, StringRef name) const;
|
||||
|
||||
/**
|
||||
* Print information about this specific context. This has to be implemented by each subclass.
|
||||
*/
|
||||
virtual void print_current_in_line(std::ostream &stream) const = 0;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const ComputeContext &compute_context);
|
||||
|
||||
private:
|
||||
/** Compute the hash of this context, usually using #ComputeContextHash::from. */
|
||||
virtual ComputeContextHash compute_hash() const = 0;
|
||||
};
|
||||
|
||||
template<size_t N, typename... Args>
|
||||
inline ComputeContextHash ComputeContextHash::from(const ComputeContext *parent,
|
||||
const char (&type_str)[N],
|
||||
Args &&...args)
|
||||
{
|
||||
return ComputeContextHash::from_shallow_bytes(
|
||||
parent ? parent->hash() : ComputeContextHash{0, 0}, type_str, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline ComputeContextHash ComputeContextHash::from_shallow_bytes(Args &&...args)
|
||||
{
|
||||
/* Copy all values into a contiguous buffer. Intentionally don't use std::tuple to avoid any
|
||||
* potential padding. */
|
||||
constexpr int64_t size_sum = (sizeof(args) + ...);
|
||||
char buffer[size_sum];
|
||||
int64_t offset = 0;
|
||||
(
|
||||
[&] {
|
||||
using Arg = std::remove_reference_t<std::remove_cv_t<Args>>;
|
||||
static_assert(std::has_unique_object_representations_v<Arg>);
|
||||
const Arg &arg = args;
|
||||
memcpy(buffer + offset, &arg, sizeof(Arg));
|
||||
offset += sizeof(Arg);
|
||||
}(),
|
||||
...);
|
||||
/* Compute the hash of that buffer. */
|
||||
return ComputeContextHash::from_bytes(buffer, offset);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
198
blender-5.2.0/source/blender/blenlib/BLI_concurrent_map.hh
Normal file
198
blender-5.2.0/source/blender/blenlib/BLI_concurrent_map.hh
Normal file
@@ -0,0 +1,198 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#ifdef WITH_TBB
|
||||
/* Quiet top level deprecation message, unrelated to API usage here. */
|
||||
# include <tbb/concurrent_hash_map.h>
|
||||
#else
|
||||
# include "BLI_mutex.hh"
|
||||
# include "BLI_set.hh"
|
||||
#endif
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_hash_tables.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* A #ConcurrentMap allows adding, removing and looking up values from multiple threads
|
||||
* concurrently. It has higher memory and performance overhead than a simple #Map when not used
|
||||
* concurrently though.
|
||||
*
|
||||
* For thread-safety, one always has the use an accessor to retrieve or update values. This makes
|
||||
* sure that only one thread can modify a value at a time. Multiple threads may read from the same
|
||||
* key at the same time though.
|
||||
*
|
||||
* \note #ConcurrentMap does not support iteration over all values.
|
||||
*
|
||||
* This is a thin wrapper around #tbb::concurrent_hash_map that also has a fallback implementation
|
||||
* if TBB is not available. The fallback implementation is not optimized for performance. It mainly
|
||||
* intends to be a simple implementation that can compile whenever the TBB variant can compile.
|
||||
*/
|
||||
template<typename Key,
|
||||
typename Value,
|
||||
typename Hash = DefaultHash<Key>,
|
||||
typename IsEqual = DefaultEquality<Key>>
|
||||
class ConcurrentMap {
|
||||
public:
|
||||
using size_type = int64_t;
|
||||
|
||||
/* Sometimes TBB requires the value to be constructible. */
|
||||
static_assert(std::is_copy_constructible_v<Value>);
|
||||
|
||||
#ifdef WITH_TBB
|
||||
private:
|
||||
struct Hasher {
|
||||
template<typename T> size_t hash(const T &value) const
|
||||
{
|
||||
return Hash{}(value);
|
||||
}
|
||||
|
||||
template<typename T1, typename T2> bool equal(const T1 &a, const T2 &b) const
|
||||
{
|
||||
return IsEqual{}(a, b);
|
||||
}
|
||||
};
|
||||
|
||||
using TBBMap = tbb::concurrent_hash_map<Key, Value, Hasher>;
|
||||
TBBMap map_;
|
||||
|
||||
public:
|
||||
using MutableAccessor = typename TBBMap::accessor;
|
||||
using ConstAccessor = typename TBBMap::const_accessor;
|
||||
|
||||
/**
|
||||
* Try to find the key-value-pair for the given key and get write-access to it. Only one thread
|
||||
* may have write access to it at a time. The looked up value can be accessed through the
|
||||
* accessor.
|
||||
*
|
||||
* \return True if the lookup was successful.
|
||||
*/
|
||||
bool lookup(MutableAccessor &accessor, const Key &key)
|
||||
{
|
||||
return map_.find(accessor, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as above, but only retrieves read-access which multiple threads can have at the same
|
||||
* time.
|
||||
*/
|
||||
bool lookup(ConstAccessor &accessor, const Key &key)
|
||||
{
|
||||
return map_.find(accessor, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the key to the map if it does not exist yet. The value is default initialized and can be
|
||||
* updated through the accessor.
|
||||
*/
|
||||
bool add(MutableAccessor &accessor, const Key &key)
|
||||
{
|
||||
return map_.insert(accessor, key);
|
||||
}
|
||||
|
||||
bool add(ConstAccessor &accessor, const Key &key)
|
||||
{
|
||||
return map_.insert(accessor, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the key-value-pair that corresponds to this key. This waits until no one else is using
|
||||
* it anymore.
|
||||
*/
|
||||
bool remove(const Key &key)
|
||||
{
|
||||
return map_.erase(key);
|
||||
}
|
||||
|
||||
#else
|
||||
private:
|
||||
/**
|
||||
* In the fallback implementation, we actually use a #Set, because the API expects the key and
|
||||
* value to be stored in a `std::pair`. #Set can support this use case too.
|
||||
*/
|
||||
struct SetKey {
|
||||
std::pair<Key, Value> item;
|
||||
|
||||
SetKey(Key key) : item(std::move(key), Value()) {}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
return Hash{}(this->item.first);
|
||||
}
|
||||
|
||||
static uint64_t hash_as(const Key &key)
|
||||
{
|
||||
return Hash{}(key);
|
||||
}
|
||||
|
||||
friend bool operator==(const SetKey &a, const SetKey &b)
|
||||
{
|
||||
return IsEqual{}(a.item.first, b.item.first);
|
||||
}
|
||||
|
||||
friend bool operator==(const Key &a, const SetKey &b)
|
||||
{
|
||||
return IsEqual{}(a, b.item.first);
|
||||
}
|
||||
|
||||
friend bool operator==(const SetKey &a, const Key &b)
|
||||
{
|
||||
return IsEqual{}(a.item.first, b);
|
||||
}
|
||||
};
|
||||
|
||||
using UsedSet = Set<SetKey>;
|
||||
|
||||
struct Accessor {
|
||||
std::unique_lock<Mutex> mutex;
|
||||
std::pair<Key, Value> *data = nullptr;
|
||||
|
||||
std::pair<Key, Value> *operator->()
|
||||
{
|
||||
return this->data;
|
||||
}
|
||||
};
|
||||
|
||||
Mutex mutex_;
|
||||
UsedSet set_;
|
||||
|
||||
public:
|
||||
using MutableAccessor = Accessor;
|
||||
using ConstAccessor = Accessor;
|
||||
|
||||
bool lookup(Accessor &accessor, const Key &key)
|
||||
{
|
||||
accessor.mutex = std::unique_lock(mutex_);
|
||||
SetKey *stored_key = const_cast<SetKey *>(set_.lookup_key_ptr_as(key));
|
||||
if (!stored_key) {
|
||||
return false;
|
||||
}
|
||||
accessor.data = &stored_key->item;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add(Accessor &accessor, const Key &key)
|
||||
{
|
||||
accessor.mutex = std::unique_lock(mutex_);
|
||||
const bool newly_added = !set_.contains_as(key);
|
||||
SetKey &stored_key = const_cast<SetKey &>(set_.lookup_key_or_add_as(key));
|
||||
accessor.data = &stored_key.item;
|
||||
return newly_added;
|
||||
}
|
||||
|
||||
bool remove(const Key &key)
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
return set_.remove_as(key);
|
||||
}
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
18
blender-5.2.0/source/blender/blenlib/BLI_console.h
Normal file
18
blender-5.2.0/source/blender/blenlib/BLI_console.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2018 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief Set of utility functions and constants to work with consoles.
|
||||
*/
|
||||
|
||||
/* Format string where one could BLI_snprintf() R, G and B values
|
||||
* and get proper marker to start colored output in the console.
|
||||
*/
|
||||
#define TRUECOLOR_ANSI_COLOR_FORMAT "\x1b[38;2;%d;%d;%dm"
|
||||
|
||||
/* Marker which indicates that colored output is finished. */
|
||||
#define TRUECOLOR_ANSI_COLOR_FINISH "\x1b[0m"
|
||||
42
blender-5.2.0/source/blender/blenlib/BLI_convexhull_2d.hh
Normal file
42
blender-5.2.0/source/blender/blenlib/BLI_convexhull_2d.hh
Normal file
@@ -0,0 +1,42 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
/**
|
||||
* Extract 2D convex hull.
|
||||
*
|
||||
* \param points: An array of 2D points.
|
||||
* \param points_num: The number of points in points.
|
||||
* \param r_points: An array of the convex hull vertex indices (max is `points_num`).
|
||||
* - Points are ordered counter clockwise.
|
||||
* - The first point in `r_points` will be the lowest Y value
|
||||
* (lowest (X, Y) when there are multiple Y aligned vertices).
|
||||
* - The polygons cross product is always positive (or zero).
|
||||
*
|
||||
* \return The number of indices in `r_points`.
|
||||
*
|
||||
* \note Performance is `O(points_num.log(points_num))`, same as `qsort`.
|
||||
*/
|
||||
int BLI_convexhull_2d(Span<float2> points, int r_points[/*points_num*/]);
|
||||
|
||||
/**
|
||||
* \return The best angle for fitting the points to an axis aligned bounding box.
|
||||
*
|
||||
* \note We could return the index of the best edge too if its needed.
|
||||
*
|
||||
* \param points: Arbitrary 2D points.
|
||||
*/
|
||||
float BLI_convexhull_aabb_fit_points_2d(Span<float2> points);
|
||||
|
||||
} // namespace blender
|
||||
791
blender-5.2.0/source/blender/blenlib/BLI_cpp_type.hh
Normal file
791
blender-5.2.0/source/blender/blenlib/BLI_cpp_type.hh
Normal file
@@ -0,0 +1,791 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* The `CPPType` class allows working with arbitrary C++ types in a generic way. An instance of
|
||||
* #CPPType wraps exactly one type like `int` or `std::string`.
|
||||
*
|
||||
* With #CPPType one can write generic data structures and algorithms. That is similar to what C++
|
||||
* templates allow. The difference is that when using templates, the types have to be known at
|
||||
* compile time and the code has to be instantiated multiple times. On the other hand, when using
|
||||
* #CPPType, the data type only has to be known at run-time, and the code only has to be compiled
|
||||
* once. Whether #CPPType or classic c++ templates should be used depends on the context:
|
||||
* - If the data type is not known at run-time, #CPPType should be used.
|
||||
* - If the data type is known to be one of a few, it depends on how performance sensitive the code
|
||||
* is.
|
||||
* - If it it's a small hot loop, a template can be used to optimize for every type (at the
|
||||
* cost of longer compile time, a larger binary and the complexity that comes from using
|
||||
* templates).
|
||||
* - If the code is not performance sensitive, it usually makes sense to use #CPPType instead.
|
||||
* - Sometimes a combination can make sense. Optimized code can be generated at compile-time for
|
||||
* some types, while there is a fallback code path using #CPPType for all other types.
|
||||
* #CPPType::to_static_type allows dispatching between both versions based on the type.
|
||||
*
|
||||
* Under some circumstances, #CPPType serves a similar role as #std::type_info. However, #CPPType
|
||||
* has much more utility because it contains methods for actually working with instances of the
|
||||
* type.
|
||||
*
|
||||
* Every type has a size and an alignment. Every function dealing with C++ types in a generic way,
|
||||
* has to make sure that alignment rules are followed. The methods provided by a #CPPType instance
|
||||
* will check for correct alignment as well.
|
||||
*
|
||||
* Every type has a name that is for debugging purposes only. It should not be used as identifier.
|
||||
*
|
||||
* To check if two instances of #CPPType represent the same type, only their pointers have to be
|
||||
* compared. Any C++ type has at most one corresponding #CPPType instance.
|
||||
*
|
||||
* A #CPPType instance comes with many methods that allow dealing with types in a generic way. Most
|
||||
* methods come in three variants. Using the default-construct methods as an example:
|
||||
* - `default_construct(void *ptr)`:
|
||||
* Constructs a single instance of that type at the given pointer.
|
||||
* - `default_construct_n(void *ptr, int64_t n)`:
|
||||
* Constructs n instances of that type in an array that starts at the given pointer.
|
||||
* - `default_construct_indices(void *ptr, const IndexMask &mask)`:
|
||||
* Constructs multiple instances of that type in an array that starts at the given pointer.
|
||||
* Only the indices referenced by `mask` will by constructed.
|
||||
*
|
||||
* In some cases default-construction does nothing (e.g. for trivial types like int). The
|
||||
* `default_value` method provides some default value anyway that can be copied instead. What the
|
||||
* default value is, depends on the type. Usually it is something like 0 or an empty string.
|
||||
*
|
||||
*
|
||||
* Implementation Considerations
|
||||
* -----------------------------
|
||||
*
|
||||
* Concepts like inheritance are currently not captured by this system. This is not because it is
|
||||
* not possible, but because it was not necessary to add this complexity yet.
|
||||
*
|
||||
* One could also implement CPPType itself using virtual methods and a child class for every
|
||||
* wrapped type. However, the approach used now with explicit function pointers to works better.
|
||||
* Here are some reasons:
|
||||
* - If CPPType would be inherited once for every used C++ type, we would get a lot of classes
|
||||
* that would only be instanced once each.
|
||||
* - Methods like `default_construct` that operate on a single instance have to be fast. Even this
|
||||
* one necessary indirection using function pointers adds a lot of overhead. If all methods were
|
||||
* virtual, there would be a second level of indirection that increases the overhead even more.
|
||||
* - If it becomes necessary, we could pass the function pointers to C functions more easily than
|
||||
* pointers to virtual member functions.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "BLI_dynamic_stack_buffer.hh" // IWYU pragma: keep
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_index_mask_fwd.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_parameter_pack_utils.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct UniqueHashBytes;
|
||||
|
||||
/**
|
||||
* Different types support different features. Features like copy constructability can be detected
|
||||
* automatically easily. For some features this is harder as of C++17. Those have flags in this
|
||||
* enum and need to be determined by the programmer.
|
||||
*/
|
||||
enum class CPPTypeFlags {
|
||||
None = 0,
|
||||
Hashable = 1 << 0,
|
||||
Printable = 1 << 1,
|
||||
EqualityComparable = 1 << 2,
|
||||
IdentityDefaultValue = 1 << 3,
|
||||
|
||||
BasicType = Hashable | Printable | EqualityComparable,
|
||||
};
|
||||
ENUM_OPERATORS(CPPTypeFlags)
|
||||
|
||||
class CPPType : NonCopyable, NonMovable {
|
||||
public:
|
||||
/**
|
||||
* Required memory in bytes for an instance of this type.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `sizeof(T);`
|
||||
*/
|
||||
int64_t size = 0;
|
||||
|
||||
/**
|
||||
* Required memory alignment for an instance of this type.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `alignof(T);`
|
||||
*/
|
||||
int64_t alignment = 0;
|
||||
|
||||
/**
|
||||
* When true, the value is like a normal C type, it can be copied around with #memcpy and does
|
||||
* not have to be destructed.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `std::is_trivial_v<T>;`
|
||||
*/
|
||||
bool is_trivial = false;
|
||||
|
||||
/**
|
||||
* When true, the destructor does not have to be called on this type. This can sometimes be used
|
||||
* for optimization purposes.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `std::is_trivially_destructible_v<T>;`
|
||||
*/
|
||||
bool is_trivially_destructible = false;
|
||||
|
||||
/**
|
||||
* Returns true, when the type has the following functions:
|
||||
* - Default constructor.
|
||||
* - Copy constructor.
|
||||
* - Move constructor.
|
||||
* - Copy assignment operator.
|
||||
* - Move assignment operator.
|
||||
* - Destructor.
|
||||
*/
|
||||
bool has_special_member_functions = false;
|
||||
|
||||
bool is_default_constructible = false;
|
||||
bool is_copy_constructible = false;
|
||||
bool is_move_constructible = false;
|
||||
bool is_destructible = false;
|
||||
bool is_copy_assignable = false;
|
||||
bool is_move_assignable = false;
|
||||
|
||||
/**
|
||||
* An index that is assigned when the type is registered. Each #CPPtype has a unique index.
|
||||
* While the pointer of a #CPPType is also unique, sometimes it's easier to work with an index
|
||||
* that is a relatively small number (generally <100).
|
||||
*/
|
||||
int type_index = -1;
|
||||
|
||||
private:
|
||||
uintptr_t alignment_mask_ = 0;
|
||||
|
||||
void (*default_construct_)(void *ptr) = nullptr;
|
||||
void (*default_construct_n_)(void *ptr, int64_t n) = nullptr;
|
||||
void (*default_construct_indices_)(void *ptr, const IndexMask &mask) = nullptr;
|
||||
|
||||
void (*value_initialize_)(void *ptr) = nullptr;
|
||||
void (*value_initialize_n_)(void *ptr, int64_t n) = nullptr;
|
||||
void (*value_initialize_indices_)(void *ptr, const IndexMask &mask) = nullptr;
|
||||
|
||||
void (*destruct_)(void *ptr) = nullptr;
|
||||
void (*destruct_n_)(void *ptr, int64_t n) = nullptr;
|
||||
void (*destruct_indices_)(void *ptr, const IndexMask &mask) = nullptr;
|
||||
|
||||
void (*copy_assign_)(const void *src, void *dst) = nullptr;
|
||||
void (*copy_assign_n_)(const void *src, void *dst, int64_t n) = nullptr;
|
||||
void (*copy_assign_indices_)(const void *src, void *dst, const IndexMask &mask) = nullptr;
|
||||
void (*copy_assign_compressed_)(const void *src, void *dst, const IndexMask &mask) = nullptr;
|
||||
|
||||
void (*copy_construct_)(const void *src, void *dst) = nullptr;
|
||||
void (*copy_construct_n_)(const void *src, void *dst, int64_t n) = nullptr;
|
||||
void (*copy_construct_indices_)(const void *src, void *dst, const IndexMask &mask) = nullptr;
|
||||
void (*copy_construct_compressed_)(const void *src, void *dst, const IndexMask &mask) = nullptr;
|
||||
|
||||
void (*move_assign_)(void *src, void *dst) = nullptr;
|
||||
void (*move_assign_n_)(void *src, void *dst, int64_t n) = nullptr;
|
||||
void (*move_assign_indices_)(void *src, void *dst, const IndexMask &mask) = nullptr;
|
||||
|
||||
void (*move_construct_)(void *src, void *dst) = nullptr;
|
||||
void (*move_construct_n_)(void *src, void *dst, int64_t n) = nullptr;
|
||||
void (*move_construct_indices_)(void *src, void *dst, const IndexMask &mask) = nullptr;
|
||||
|
||||
void (*relocate_assign_)(void *src, void *dst) = nullptr;
|
||||
void (*relocate_assign_n_)(void *src, void *dst, int64_t n) = nullptr;
|
||||
void (*relocate_assign_indices_)(void *src, void *dst, const IndexMask &mask) = nullptr;
|
||||
|
||||
void (*relocate_construct_)(void *src, void *dst) = nullptr;
|
||||
void (*relocate_construct_n_)(void *src, void *dst, int64_t n) = nullptr;
|
||||
void (*relocate_construct_indices_)(void *src, void *dst, const IndexMask &mask) = nullptr;
|
||||
|
||||
void (*fill_assign_n_)(const void *value, void *dst, int64_t n) = nullptr;
|
||||
void (*fill_assign_indices_)(const void *value, void *dst, const IndexMask &mask) = nullptr;
|
||||
|
||||
void (*fill_construct_n_)(const void *value, void *dst, int64_t n) = nullptr;
|
||||
void (*fill_construct_indices_)(const void *value, void *dst, const IndexMask &mask) = nullptr;
|
||||
|
||||
void (*print_)(const void *value, std::stringstream &ss) = nullptr;
|
||||
bool (*is_equal_)(const void *a, const void *b) = nullptr;
|
||||
uint64_t (*hash_)(const void *value) = nullptr;
|
||||
void (*hash_unique_)(const void *value, UniqueHashBytes &hash) = nullptr;
|
||||
|
||||
const void *default_value_ = nullptr;
|
||||
std::string debug_name_;
|
||||
|
||||
public:
|
||||
template<typename T, CPPTypeFlags Flags>
|
||||
CPPType(TypeTag<T> /*type*/, TypeForValue<CPPTypeFlags, Flags> /*flags*/, StringRef debug_name);
|
||||
virtual ~CPPType() = default;
|
||||
|
||||
/**
|
||||
* Get the `CPPType` that corresponds to a specific static type.
|
||||
* This only works for types that actually implement the template specialization using
|
||||
* `BLI_CPP_TYPE_REGISTER`.
|
||||
*/
|
||||
template<typename T> static const CPPType &get();
|
||||
|
||||
/**
|
||||
* Returns the name of the type for debugging purposes. This name should not be used as
|
||||
* identifier.
|
||||
*/
|
||||
StringRefNull name() const;
|
||||
|
||||
bool is_printable() const;
|
||||
bool is_equality_comparable() const;
|
||||
bool is_hashable() const;
|
||||
|
||||
/**
|
||||
* Returns true, when the given pointer fulfills the alignment requirement of this type.
|
||||
*/
|
||||
bool pointer_has_valid_alignment(const void *ptr) const;
|
||||
bool pointer_can_point_to_instance(const void *ptr) const;
|
||||
|
||||
/**
|
||||
* Call the default constructor at the given memory location.
|
||||
* The memory should be uninitialized before this method is called.
|
||||
* For some trivial types (like int), this method does nothing.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `new (ptr) T;`
|
||||
*/
|
||||
void default_construct(void *ptr) const;
|
||||
void default_construct_n(void *ptr, int64_t n) const;
|
||||
void default_construct_indices(void *ptr, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Same as #default_construct, but does zero initialization for trivial types.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `new (ptr) T();`
|
||||
*/
|
||||
void value_initialize(void *ptr) const;
|
||||
void value_initialize_n(void *ptr, int64_t n) const;
|
||||
void value_initialize_indices(void *ptr, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Call the destructor on the given instance of this type. The pointer must not be nullptr.
|
||||
*
|
||||
* For some trivial types, this does nothing.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `ptr->~T();`
|
||||
*/
|
||||
void destruct(void *ptr) const;
|
||||
void destruct_n(void *ptr, int64_t n) const;
|
||||
void destruct_indices(void *ptr, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Copy an instance of this type from src to dst.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `dst = src;`
|
||||
*/
|
||||
void copy_assign(const void *src, void *dst) const;
|
||||
void copy_assign_n(const void *src, void *dst, int64_t n) const;
|
||||
void copy_assign_indices(const void *src, void *dst, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Similar to #copy_assign_indices, but does not leave gaps in the #dst array.
|
||||
*/
|
||||
void copy_assign_compressed(const void *src, void *dst, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Copy an instance of this type from src to dst.
|
||||
*
|
||||
* The memory pointed to by dst should be uninitialized.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `new (dst) T(src);`
|
||||
*/
|
||||
void copy_construct(const void *src, void *dst) const;
|
||||
void copy_construct_n(const void *src, void *dst, int64_t n) const;
|
||||
void copy_construct_indices(const void *src, void *dst, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Similar to #copy_construct_indices, but does not leave gaps in the #dst array.
|
||||
*/
|
||||
void copy_construct_compressed(const void *src, void *dst, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Move an instance of this type from src to dst.
|
||||
*
|
||||
* The memory pointed to by dst should be initialized.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `dst = std::move(src);`
|
||||
*/
|
||||
void move_assign(void *src, void *dst) const;
|
||||
void move_assign_n(void *src, void *dst, int64_t n) const;
|
||||
void move_assign_indices(void *src, void *dst, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Move an instance of this type from src to dst.
|
||||
*
|
||||
* The memory pointed to by dst should be uninitialized.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `new (dst) T(std::move(src));`
|
||||
*/
|
||||
void move_construct(void *src, void *dst) const;
|
||||
void move_construct_n(void *src, void *dst, int64_t n) const;
|
||||
void move_construct_indices(void *src, void *dst, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Relocates an instance of this type from src to dst. src will point to uninitialized memory
|
||||
* afterwards.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `dst = std::move(src);`
|
||||
* `src->~T();`
|
||||
*/
|
||||
void relocate_assign(void *src, void *dst) const;
|
||||
void relocate_assign_n(void *src, void *dst, int64_t n) const;
|
||||
void relocate_assign_indices(void *src, void *dst, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Relocates an instance of this type from src to dst. src will point to uninitialized memory
|
||||
* afterwards.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* `new (dst) T(std::move(src))`
|
||||
* `src->~T();`
|
||||
*/
|
||||
void relocate_construct(void *src, void *dst) const;
|
||||
void relocate_construct_n(void *src, void *dst, int64_t n) const;
|
||||
void relocate_construct_indices(void *src, void *dst, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Copy the given value to the first n elements in an array starting at dst.
|
||||
*
|
||||
* Other instances of the same type should live in the array before this method is called.
|
||||
*/
|
||||
void fill_assign_n(const void *value, void *dst, int64_t n) const;
|
||||
void fill_assign_indices(const void *value, void *dst, const IndexMask &mask) const;
|
||||
|
||||
/**
|
||||
* Copy the given value to the first n elements in an array starting at dst.
|
||||
*
|
||||
* The array should be uninitialized before this method is called.
|
||||
*/
|
||||
void fill_construct_n(const void *value, void *dst, int64_t n) const;
|
||||
void fill_construct_indices(const void *value, void *dst, const IndexMask &mask) const;
|
||||
|
||||
bool can_exist_in_buffer(const int64_t buffer_size, const int64_t buffer_alignment) const;
|
||||
|
||||
void print(const void *value, std::stringstream &ss) const;
|
||||
std::string to_string(const void *value) const;
|
||||
void print_or_default(const void *value, std::stringstream &ss, StringRef default_value) const;
|
||||
|
||||
bool is_equal(const void *a, const void *b) const;
|
||||
bool is_equal_or_false(const void *a, const void *b) const;
|
||||
|
||||
uint64_t hash(const void *value) const;
|
||||
uint64_t hash_or_fallback(const void *value, uint64_t fallback_hash) const;
|
||||
void hash_unique(const void *value, UniqueHashBytes &hash) const;
|
||||
|
||||
/**
|
||||
* Get a pointer to a constant value of this type. The specific value depends on the type.
|
||||
* It is usually a zero-initialized or default constructed value.
|
||||
*/
|
||||
const void *default_value() const;
|
||||
|
||||
uint64_t hash() const;
|
||||
|
||||
void (*destruct_fn() const)(void *);
|
||||
|
||||
template<typename T> bool is() const;
|
||||
|
||||
template<typename... T> bool is_any() const;
|
||||
|
||||
/**
|
||||
* Convert a #CPPType that is only known at run-time, to a static type that is known at
|
||||
* compile-time. This allows the compiler to optimize a function for specific types, while all
|
||||
* other types can still use a generic fallback function.
|
||||
*
|
||||
* \tparam Types: The types that code should be generated for.
|
||||
* \param fn: The function object to call. This is expected to have a templated `operator()` and
|
||||
* a non-templated `operator()`. The templated version will be called if the current #CPPType
|
||||
* matches any of the given types.
|
||||
* \return True if the function was called.
|
||||
*/
|
||||
template<typename... Types, typename Fn> bool to_static_type_try(Fn &&fn) const;
|
||||
|
||||
/** Same as #to_static_type_try, but asserts if the type is valid. */
|
||||
template<typename... Types, typename Fn> void to_static_type(Fn &&fn) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Helper used in #to_static_type_try as a typed function pointer for each type in the list.
|
||||
* A named static function is used instead of a lambda to avoid a known MSVC bug where a
|
||||
* non-capturing lambda inside a comma fold expression that references the pack parameter
|
||||
* causes MSVC to generate zero iterations, leaving the map empty.
|
||||
*/
|
||||
template<typename T, typename Fn> static void call_with_type_impl_(const Fn &fn)
|
||||
{
|
||||
fn.template operator()<T>();
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
/**
|
||||
* Global static variable that contains the #CPPType for a given type after it has been registered
|
||||
* with #BLI_CPP_TYPE_REGISTER. This should generally be accessed through #CPPType::get<T>. */
|
||||
template<typename T> inline TypedBuffer<CPPType> cpp_type_impl{};
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Initialize and register basic cpp types.
|
||||
*/
|
||||
void register_cpp_types();
|
||||
|
||||
/* Utility for allocating an uninitialized buffer for a single value of the given #CPPType. */
|
||||
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name) \
|
||||
DynamicStackBuffer<64, 64> stack_buffer_for_##variable_name((type).size, (type).alignment); \
|
||||
void *variable_name = stack_buffer_for_##variable_name.buffer();
|
||||
|
||||
/* Give a compile error instead of a link error when type information is missing. */
|
||||
template<> const CPPType &CPPType::get<void>() = delete;
|
||||
|
||||
/**
|
||||
* Two types only compare equal when their pointer is equal. No two instances of CPPType for the
|
||||
* same C++ type should be created.
|
||||
*/
|
||||
inline bool operator==(const CPPType &a, const CPPType &b)
|
||||
{
|
||||
return &a == &b;
|
||||
}
|
||||
|
||||
inline bool operator!=(const CPPType &a, const CPPType &b)
|
||||
{
|
||||
return !(&a == &b);
|
||||
}
|
||||
|
||||
template<typename T> inline const CPPType &CPPType::get()
|
||||
{
|
||||
const CPPType &type = detail::cpp_type_impl<std::decay_t<T>>.ref();
|
||||
/* Should have been initialized by #BLI_CPP_TYPE_REGISTER.
|
||||
* If this is hit in test code, make sure the test calls `register_cpp_types` (for blenlib
|
||||
* tests) or `BKE_cpp_types_init` (for general tests). */
|
||||
BLI_assert(type.size > 0);
|
||||
return type;
|
||||
}
|
||||
|
||||
inline StringRefNull CPPType::name() const
|
||||
{
|
||||
return debug_name_;
|
||||
}
|
||||
|
||||
inline bool CPPType::is_printable() const
|
||||
{
|
||||
return print_ != nullptr;
|
||||
}
|
||||
|
||||
inline bool CPPType::is_equality_comparable() const
|
||||
{
|
||||
return is_equal_ != nullptr;
|
||||
}
|
||||
|
||||
inline bool CPPType::is_hashable() const
|
||||
{
|
||||
return hash_ != nullptr;
|
||||
}
|
||||
|
||||
inline bool CPPType::pointer_has_valid_alignment(const void *ptr) const
|
||||
{
|
||||
return (uintptr_t(ptr) & alignment_mask_) == 0;
|
||||
}
|
||||
|
||||
inline bool CPPType::pointer_can_point_to_instance(const void *ptr) const
|
||||
{
|
||||
return ptr != nullptr && pointer_has_valid_alignment(ptr);
|
||||
}
|
||||
|
||||
inline void CPPType::default_construct(void *ptr) const
|
||||
{
|
||||
default_construct_(ptr);
|
||||
}
|
||||
|
||||
inline void CPPType::default_construct_n(void *ptr, int64_t n) const
|
||||
{
|
||||
default_construct_n_(ptr, n);
|
||||
}
|
||||
|
||||
inline void CPPType::default_construct_indices(void *ptr, const IndexMask &mask) const
|
||||
{
|
||||
default_construct_indices_(ptr, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::value_initialize(void *ptr) const
|
||||
{
|
||||
value_initialize_(ptr);
|
||||
}
|
||||
|
||||
inline void CPPType::value_initialize_n(void *ptr, int64_t n) const
|
||||
{
|
||||
value_initialize_n_(ptr, n);
|
||||
}
|
||||
|
||||
inline void CPPType::value_initialize_indices(void *ptr, const IndexMask &mask) const
|
||||
{
|
||||
value_initialize_indices_(ptr, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::destruct(void *ptr) const
|
||||
{
|
||||
destruct_(ptr);
|
||||
}
|
||||
|
||||
inline void CPPType::destruct_n(void *ptr, int64_t n) const
|
||||
{
|
||||
destruct_n_(ptr, n);
|
||||
}
|
||||
|
||||
inline void CPPType::destruct_indices(void *ptr, const IndexMask &mask) const
|
||||
{
|
||||
destruct_indices_(ptr, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::copy_assign(const void *src, void *dst) const
|
||||
{
|
||||
copy_assign_(src, dst);
|
||||
}
|
||||
|
||||
inline void CPPType::copy_assign_n(const void *src, void *dst, int64_t n) const
|
||||
{
|
||||
copy_assign_n_(src, dst, n);
|
||||
}
|
||||
|
||||
inline void CPPType::copy_assign_indices(const void *src, void *dst, const IndexMask &mask) const
|
||||
{
|
||||
copy_assign_indices_(src, dst, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::copy_assign_compressed(const void *src,
|
||||
void *dst,
|
||||
const IndexMask &mask) const
|
||||
{
|
||||
copy_assign_compressed_(src, dst, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::copy_construct(const void *src, void *dst) const
|
||||
{
|
||||
copy_construct_(src, dst);
|
||||
}
|
||||
|
||||
inline void CPPType::copy_construct_n(const void *src, void *dst, int64_t n) const
|
||||
{
|
||||
copy_construct_n_(src, dst, n);
|
||||
}
|
||||
|
||||
inline void CPPType::copy_construct_indices(const void *src,
|
||||
void *dst,
|
||||
const IndexMask &mask) const
|
||||
{
|
||||
copy_construct_indices_(src, dst, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::copy_construct_compressed(const void *src,
|
||||
void *dst,
|
||||
const IndexMask &mask) const
|
||||
{
|
||||
copy_construct_compressed_(src, dst, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::move_assign(void *src, void *dst) const
|
||||
{
|
||||
move_assign_(src, dst);
|
||||
}
|
||||
|
||||
inline void CPPType::move_assign_n(void *src, void *dst, int64_t n) const
|
||||
{
|
||||
move_assign_n_(src, dst, n);
|
||||
}
|
||||
|
||||
inline void CPPType::move_assign_indices(void *src, void *dst, const IndexMask &mask) const
|
||||
{
|
||||
move_assign_indices_(src, dst, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::move_construct(void *src, void *dst) const
|
||||
{
|
||||
move_construct_(src, dst);
|
||||
}
|
||||
|
||||
inline void CPPType::move_construct_n(void *src, void *dst, int64_t n) const
|
||||
{
|
||||
move_construct_n_(src, dst, n);
|
||||
}
|
||||
|
||||
inline void CPPType::move_construct_indices(void *src, void *dst, const IndexMask &mask) const
|
||||
{
|
||||
move_construct_indices_(src, dst, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::relocate_assign(void *src, void *dst) const
|
||||
{
|
||||
relocate_assign_(src, dst);
|
||||
}
|
||||
|
||||
inline void CPPType::relocate_assign_n(void *src, void *dst, int64_t n) const
|
||||
{
|
||||
relocate_assign_n_(src, dst, n);
|
||||
}
|
||||
|
||||
inline void CPPType::relocate_assign_indices(void *src, void *dst, const IndexMask &mask) const
|
||||
{
|
||||
relocate_assign_indices_(src, dst, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::relocate_construct(void *src, void *dst) const
|
||||
{
|
||||
relocate_construct_(src, dst);
|
||||
}
|
||||
|
||||
inline void CPPType::relocate_construct_n(void *src, void *dst, int64_t n) const
|
||||
{
|
||||
relocate_construct_n_(src, dst, n);
|
||||
}
|
||||
|
||||
inline void CPPType::relocate_construct_indices(void *src, void *dst, const IndexMask &mask) const
|
||||
{
|
||||
relocate_construct_indices_(src, dst, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::fill_assign_n(const void *value, void *dst, int64_t n) const
|
||||
{
|
||||
fill_assign_n_(value, dst, n);
|
||||
}
|
||||
|
||||
inline void CPPType::fill_assign_indices(const void *value, void *dst, const IndexMask &mask) const
|
||||
{
|
||||
fill_assign_indices_(value, dst, mask);
|
||||
}
|
||||
|
||||
inline void CPPType::fill_construct_n(const void *value, void *dst, int64_t n) const
|
||||
{
|
||||
fill_construct_n_(value, dst, n);
|
||||
}
|
||||
|
||||
inline void CPPType::fill_construct_indices(const void *value,
|
||||
void *dst,
|
||||
const IndexMask &mask) const
|
||||
{
|
||||
fill_construct_indices_(value, dst, mask);
|
||||
}
|
||||
|
||||
inline bool CPPType::can_exist_in_buffer(const int64_t buffer_size,
|
||||
const int64_t buffer_alignment) const
|
||||
{
|
||||
return this->size <= buffer_size && this->alignment <= buffer_alignment;
|
||||
}
|
||||
|
||||
inline void CPPType::print(const void *value, std::stringstream &ss) const
|
||||
{
|
||||
BLI_assert(this->pointer_can_point_to_instance(value));
|
||||
print_(value, ss);
|
||||
}
|
||||
|
||||
inline bool CPPType::is_equal(const void *a, const void *b) const
|
||||
{
|
||||
return is_equal_(a, b);
|
||||
}
|
||||
|
||||
inline bool CPPType::is_equal_or_false(const void *a, const void *b) const
|
||||
{
|
||||
if (this->is_equality_comparable()) {
|
||||
return this->is_equal(a, b);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline uint64_t CPPType::hash(const void *value) const
|
||||
{
|
||||
return hash_(value);
|
||||
}
|
||||
|
||||
inline uint64_t CPPType::hash_or_fallback(const void *value, uint64_t fallback_hash) const
|
||||
{
|
||||
if (this->is_hashable()) {
|
||||
return this->hash(value);
|
||||
}
|
||||
return fallback_hash;
|
||||
}
|
||||
|
||||
inline void CPPType::hash_unique(const void *value, UniqueHashBytes &hash) const
|
||||
{
|
||||
this->hash_unique_(value, hash);
|
||||
}
|
||||
|
||||
inline const void *CPPType::default_value() const
|
||||
{
|
||||
return default_value_;
|
||||
}
|
||||
|
||||
inline uint64_t CPPType::hash() const
|
||||
{
|
||||
return get_default_hash(this);
|
||||
}
|
||||
|
||||
inline void (*CPPType::destruct_fn() const)(void *)
|
||||
{
|
||||
return destruct_;
|
||||
}
|
||||
|
||||
template<typename T> inline bool CPPType::is() const
|
||||
{
|
||||
return this == &CPPType::get<std::decay_t<T>>();
|
||||
}
|
||||
|
||||
template<typename... T> inline bool CPPType::is_any() const
|
||||
{
|
||||
return (this->is<T>() || ...);
|
||||
}
|
||||
|
||||
template<typename... Types, typename Fn> inline void CPPType::to_static_type(Fn &&fn) const
|
||||
{
|
||||
if (this->to_static_type_try<Types...>(fn)) {
|
||||
return;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
template<typename... Types, typename Fn> inline bool CPPType::to_static_type_try(Fn &&fn) const
|
||||
{
|
||||
/* Strip any reference from Fn to normalize the type used for the static map, ensuring the
|
||||
* same static is used regardless of whether fn is an lvalue or rvalue. */
|
||||
using Fn_ = std::remove_reference_t<Fn>;
|
||||
using Callback = void (*)(const Fn_ &);
|
||||
|
||||
/* Use an array indexed by #CPPType::type_index instead of a #Map for faster lookup and less
|
||||
* generated code. The array can be quite a bit larger at run-time than the number of types but
|
||||
* the total number of types is fairly limited, so that should be fine. */
|
||||
static Array<Callback, 0> callback_array = [&]() {
|
||||
/* This way to compute the max generates less code than using std::max with an initializer
|
||||
* list. */
|
||||
int max_type_index = 0;
|
||||
((max_type_index = std::max(max_type_index, CPPType::get<Types>().type_index)), ...);
|
||||
Array<Callback, 0> callback_array(max_type_index + 1, nullptr);
|
||||
/* Using call_with_type_impl_ instead of a lambda due to an MSVC bug. */
|
||||
((callback_array[CPPType::get<Types>().type_index] = call_with_type_impl_<Types, Fn_>), ...);
|
||||
return callback_array;
|
||||
}();
|
||||
|
||||
if (this->type_index >= callback_array.size()) {
|
||||
return false;
|
||||
}
|
||||
const Callback callback = callback_array[this->type_index];
|
||||
if (!callback) {
|
||||
return false;
|
||||
}
|
||||
callback(fn);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
498
blender-5.2.0/source/blender/blenlib/BLI_cpp_type_make.hh
Normal file
498
blender-5.2.0/source/blender/blenlib/BLI_cpp_type_make.hh
Normal file
@@ -0,0 +1,498 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "BLI_cpp_type.hh"
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_unique_hash.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace cpp_type_util {
|
||||
|
||||
template<typename T> inline bool pointer_has_valid_alignment(const void *ptr)
|
||||
{
|
||||
return (uintptr_t(ptr) % alignof(T)) == 0;
|
||||
}
|
||||
|
||||
template<typename T> inline bool pointer_can_point_to_instance(const void *ptr)
|
||||
{
|
||||
return ptr != nullptr && pointer_has_valid_alignment<T>(ptr);
|
||||
}
|
||||
|
||||
template<typename T> void default_construct_cb(void *ptr)
|
||||
{
|
||||
BLI_assert(pointer_can_point_to_instance<T>(ptr));
|
||||
new (ptr) T;
|
||||
}
|
||||
template<typename T> void default_construct_indices_cb(void *ptr, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(ptr));
|
||||
if constexpr (std::is_trivially_constructible_v<T>) {
|
||||
return;
|
||||
}
|
||||
mask.foreach_index_optimized<int64_t>([&](int64_t i) { new (static_cast<T *>(ptr) + i) T; });
|
||||
}
|
||||
template<typename T> void default_construct_n_cb(void *ptr, const int64_t n)
|
||||
{
|
||||
default_construct_indices_cb<T>(ptr, IndexMask(n));
|
||||
}
|
||||
|
||||
template<typename T> void value_initialize_cb(void *ptr)
|
||||
{
|
||||
BLI_assert(pointer_can_point_to_instance<T>(ptr));
|
||||
new (ptr) T();
|
||||
}
|
||||
template<typename T> void value_initialize_indices_cb(void *ptr, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(ptr));
|
||||
mask.foreach_index_optimized<int64_t>([&](int64_t i) { new (static_cast<T *>(ptr) + i) T(); });
|
||||
}
|
||||
template<typename T> void value_initialize_n_cb(void *ptr, const int64_t n)
|
||||
{
|
||||
value_initialize_indices_cb<T>(ptr, IndexMask(n));
|
||||
}
|
||||
|
||||
template<typename T> void destruct_cb(void *ptr)
|
||||
{
|
||||
BLI_assert(pointer_can_point_to_instance<T>(ptr));
|
||||
(static_cast<T *>(ptr))->~T();
|
||||
}
|
||||
template<typename T> void destruct_indices_cb(void *ptr, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(ptr));
|
||||
if (std::is_trivially_destructible_v<T>) {
|
||||
return;
|
||||
}
|
||||
T *ptr_ = static_cast<T *>(ptr);
|
||||
mask.foreach_index_optimized<int64_t>([&](int64_t i) { ptr_[i].~T(); });
|
||||
}
|
||||
template<typename T> void destruct_n_cb(void *ptr, const int64_t n)
|
||||
{
|
||||
destruct_indices_cb<T>(ptr, IndexMask(n));
|
||||
}
|
||||
|
||||
template<typename T> void copy_assign_cb(const void *src, void *dst)
|
||||
{
|
||||
BLI_assert(pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(pointer_can_point_to_instance<T>(dst));
|
||||
*static_cast<T *>(dst) = *static_cast<const T *>(src);
|
||||
}
|
||||
template<typename T> void copy_assign_indices_cb(const void *src, void *dst, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || src != dst);
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(dst));
|
||||
if constexpr (std::is_trivially_copy_assignable_v<T>) {
|
||||
index_mask::detail::copy_assign(static_cast<const T *>(src), mask, static_cast<T *>(dst));
|
||||
}
|
||||
else {
|
||||
const T *src_ = static_cast<const T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
mask.foreach_index([&](int64_t i) { dst_[i] = src_[i]; });
|
||||
}
|
||||
}
|
||||
template<typename T> void copy_assign_n_cb(const void *src, void *dst, const int64_t n)
|
||||
{
|
||||
copy_assign_indices_cb<T>(src, dst, IndexMask(n));
|
||||
}
|
||||
template<typename T>
|
||||
void copy_assign_compressed_cb(const void *src, void *dst, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || src != dst);
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(dst));
|
||||
if constexpr (std::is_trivially_copy_assignable_v<T>) {
|
||||
index_mask::detail::gather_assign(static_cast<const T *>(src), mask, static_cast<T *>(dst));
|
||||
}
|
||||
else {
|
||||
const T *src_ = static_cast<const T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
mask.foreach_index([&](const int64_t i, const int64_t pos) { dst_[pos] = src_[i]; });
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> void copy_construct_cb(const void *src, void *dst)
|
||||
{
|
||||
BLI_assert(src != dst || std::is_trivially_copy_constructible_v<T>);
|
||||
BLI_assert(pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(pointer_can_point_to_instance<T>(dst));
|
||||
uninitialized_copy_n(static_cast<const T *>(src), 1, static_cast<T *>(dst));
|
||||
}
|
||||
template<typename T>
|
||||
void copy_construct_indices_cb(const void *src, void *dst, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || src != dst);
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(dst));
|
||||
const T *src_ = static_cast<const T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
mask.foreach_index_optimized<int64_t>([&](int64_t i) { new (dst_ + i) T(src_[i]); });
|
||||
}
|
||||
template<typename T> void copy_construct_n_cb(const void *src, void *dst, const int64_t n)
|
||||
{
|
||||
copy_construct_indices_cb<T>(src, dst, IndexMask(n));
|
||||
}
|
||||
template<typename T>
|
||||
void copy_construct_compressed_cb(const void *src, void *dst, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || src != dst);
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(dst));
|
||||
const T *src_ = static_cast<const T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
mask.foreach_index_optimized<int64_t>(
|
||||
[&](const int64_t i, const int64_t pos) { new (dst_ + pos) T(src_[i]); });
|
||||
}
|
||||
|
||||
template<typename T> void move_assign_cb(void *src, void *dst)
|
||||
{
|
||||
BLI_assert(pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(pointer_can_point_to_instance<T>(dst));
|
||||
initialized_move_n(static_cast<T *>(src), 1, static_cast<T *>(dst));
|
||||
}
|
||||
template<typename T> void move_assign_indices_cb(void *src, void *dst, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || src != dst);
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(dst));
|
||||
T *src_ = static_cast<T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
mask.foreach_index_optimized<int64_t>([&](int64_t i) { dst_[i] = std::move(src_[i]); });
|
||||
}
|
||||
template<typename T> void move_assign_n_cb(void *src, void *dst, const int64_t n)
|
||||
{
|
||||
move_assign_indices_cb<T>(src, dst, IndexMask(n));
|
||||
}
|
||||
|
||||
template<typename T> void move_construct_cb(void *src, void *dst)
|
||||
{
|
||||
BLI_assert(src != dst || std::is_trivially_move_constructible_v<T>);
|
||||
BLI_assert(pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(pointer_can_point_to_instance<T>(dst));
|
||||
|
||||
uninitialized_move_n(static_cast<T *>(src), 1, static_cast<T *>(dst));
|
||||
}
|
||||
template<typename T> void move_construct_indices_cb(void *src, void *dst, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || src != dst);
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(dst));
|
||||
T *src_ = static_cast<T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
mask.foreach_index_optimized<int64_t>([&](int64_t i) { new (dst_ + i) T(std::move(src_[i])); });
|
||||
}
|
||||
template<typename T> void move_construct_n_cb(void *src, void *dst, const int64_t n)
|
||||
{
|
||||
move_construct_indices_cb<T>(src, dst, IndexMask(n));
|
||||
}
|
||||
|
||||
template<typename T> void relocate_assign_cb(void *src, void *dst)
|
||||
{
|
||||
BLI_assert(src != dst || std::is_trivially_move_constructible_v<T>);
|
||||
BLI_assert(pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(pointer_can_point_to_instance<T>(dst));
|
||||
T *src_ = static_cast<T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
*dst_ = std::move(*src_);
|
||||
src_->~T();
|
||||
}
|
||||
template<typename T> void relocate_assign_indices_cb(void *src, void *dst, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || src != dst);
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(dst));
|
||||
T *src_ = static_cast<T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
mask.foreach_index_optimized<int64_t>([&](int64_t i) {
|
||||
dst_[i] = std::move(src_[i]);
|
||||
src_[i].~T();
|
||||
});
|
||||
}
|
||||
template<typename T> void relocate_assign_n_cb(void *src, void *dst, const int64_t n)
|
||||
{
|
||||
relocate_assign_indices_cb<T>(src, dst, IndexMask(n));
|
||||
}
|
||||
|
||||
template<typename T> void relocate_construct_cb(void *src, void *dst)
|
||||
{
|
||||
BLI_assert(src != dst || std::is_trivially_move_constructible_v<T>);
|
||||
BLI_assert(pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(pointer_can_point_to_instance<T>(dst));
|
||||
T *src_ = static_cast<T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
new (dst_) T(std::move(*src_));
|
||||
src_->~T();
|
||||
}
|
||||
template<typename T>
|
||||
void relocate_construct_indices_cb(void *src, void *dst, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || src != dst);
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(src));
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(dst));
|
||||
T *src_ = static_cast<T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
mask.foreach_index_optimized<int64_t>([&](int64_t i) {
|
||||
new (dst_ + i) T(std::move(src_[i]));
|
||||
src_[i].~T();
|
||||
});
|
||||
}
|
||||
template<typename T> void relocate_construct_n_cb(void *src, void *dst, const int64_t n)
|
||||
{
|
||||
relocate_construct_indices_cb<T>(src, dst, IndexMask(n));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void fill_assign_indices_cb(const void *value, void *dst, const IndexMask &mask)
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(value));
|
||||
BLI_assert(mask.size() == 0 || pointer_can_point_to_instance<T>(dst));
|
||||
if constexpr (std::is_trivially_copy_assignable_v<T>) {
|
||||
index_mask::detail::fill(static_cast<T *>(dst), *static_cast<const T *>(value), mask);
|
||||
}
|
||||
else {
|
||||
const T &value_ = *static_cast<const T *>(value);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
mask.foreach_index([&](int64_t i) { dst_[i] = value_; });
|
||||
}
|
||||
}
|
||||
template<typename T> void fill_assign_n_cb(const void *value, void *dst, const int64_t n)
|
||||
{
|
||||
fill_assign_indices_cb<T>(value, dst, IndexMask(n));
|
||||
}
|
||||
|
||||
template<typename T> void fill_construct_cb(const void *value, void *dst, int64_t n)
|
||||
{
|
||||
const T &value_ = *static_cast<const T *>(value);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
for (int64_t i = 0; i < n; i++) {
|
||||
new (dst_ + i) T(value_);
|
||||
}
|
||||
}
|
||||
template<typename T>
|
||||
void fill_construct_indices_cb(const void *value, void *dst, const IndexMask &mask)
|
||||
{
|
||||
const T &value_ = *static_cast<const T *>(value);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
mask.foreach_index_optimized<int64_t>([&](int64_t i) { new (dst_ + i) T(value_); });
|
||||
}
|
||||
template<typename T> void fill_construct_n_cb(const void *value, void *dst, const int64_t n)
|
||||
{
|
||||
fill_construct_indices_cb<T>(value, dst, IndexMask(n));
|
||||
}
|
||||
|
||||
template<typename T> void print_cb(const void *value, std::stringstream &ss)
|
||||
{
|
||||
const T &value_ = *static_cast<const T *>(value);
|
||||
ss << value_;
|
||||
}
|
||||
|
||||
template<typename T> bool is_equal_cb(const void *a, const void *b)
|
||||
{
|
||||
BLI_assert(pointer_can_point_to_instance<T>(a));
|
||||
BLI_assert(pointer_can_point_to_instance<T>(b));
|
||||
const T &a_ = *static_cast<const T *>(a);
|
||||
const T &b_ = *static_cast<const T *>(b);
|
||||
return a_ == b_;
|
||||
}
|
||||
|
||||
template<typename T> uint64_t hash_cb(const void *value)
|
||||
{
|
||||
BLI_assert(pointer_can_point_to_instance<T>(value));
|
||||
const T &value_ = *static_cast<const T *>(value);
|
||||
return get_default_hash(value_);
|
||||
}
|
||||
|
||||
template<typename T> void hash_unique_cb(const void *value, UniqueHashBytes &hash)
|
||||
{
|
||||
BLI_assert(pointer_can_point_to_instance<T>(value));
|
||||
const T &value_ = *static_cast<const T *>(value);
|
||||
return hash_unique_default(value_, hash);
|
||||
}
|
||||
|
||||
inline std::atomic<int> type_index_counter{0};
|
||||
|
||||
} // namespace cpp_type_util
|
||||
|
||||
template<typename T, CPPTypeFlags Flags>
|
||||
CPPType::CPPType(TypeTag<T> /*type*/,
|
||||
TypeForValue<CPPTypeFlags, Flags> /*flags*/,
|
||||
const StringRef debug_name)
|
||||
{
|
||||
using namespace cpp_type_util;
|
||||
|
||||
debug_name_ = debug_name;
|
||||
this->size = int64_t(sizeof(T));
|
||||
this->alignment = int64_t(alignof(T));
|
||||
this->is_trivial = std::is_trivial_v<T>;
|
||||
this->is_trivially_destructible = std::is_trivially_destructible_v<T>;
|
||||
if constexpr (std::is_default_constructible_v<T>) {
|
||||
default_construct_ = default_construct_cb<T>;
|
||||
default_construct_n_ = default_construct_n_cb<T>;
|
||||
default_construct_indices_ = default_construct_indices_cb<T>;
|
||||
value_initialize_ = value_initialize_cb<T>;
|
||||
value_initialize_n_ = value_initialize_n_cb<T>;
|
||||
value_initialize_indices_ = value_initialize_indices_cb<T>;
|
||||
if constexpr (bool(Flags & CPPTypeFlags::IdentityDefaultValue)) {
|
||||
static const T default_value = T::identity();
|
||||
default_value_ = &default_value;
|
||||
}
|
||||
else {
|
||||
static const T default_value = T();
|
||||
default_value_ = &default_value;
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_destructible_v<T>) {
|
||||
destruct_ = destruct_cb<T>;
|
||||
destruct_n_ = destruct_n_cb<T>;
|
||||
destruct_indices_ = destruct_indices_cb<T>;
|
||||
}
|
||||
if constexpr (std::is_copy_assignable_v<T>) {
|
||||
copy_assign_ = copy_assign_cb<T>;
|
||||
copy_assign_n_ = copy_assign_n_cb<T>;
|
||||
copy_assign_indices_ = copy_assign_indices_cb<T>;
|
||||
copy_assign_compressed_ = copy_assign_compressed_cb<T>;
|
||||
}
|
||||
if constexpr (std::is_copy_constructible_v<T>) {
|
||||
if constexpr (std::is_trivially_copy_constructible_v<T>) {
|
||||
copy_construct_ = copy_assign_;
|
||||
copy_construct_n_ = copy_assign_n_;
|
||||
copy_construct_indices_ = copy_assign_indices_;
|
||||
copy_construct_compressed_ = copy_assign_compressed_;
|
||||
}
|
||||
else {
|
||||
copy_construct_ = copy_construct_cb<T>;
|
||||
copy_construct_n_ = copy_construct_n_cb<T>;
|
||||
copy_construct_indices_ = copy_construct_indices_cb<T>;
|
||||
copy_construct_compressed_ = copy_construct_compressed_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_move_assignable_v<T>) {
|
||||
if constexpr (std::is_trivially_move_assignable_v<T>) {
|
||||
/* This casts away the const from the src pointer. This is fine for trivial types as moving
|
||||
* them does not change the original value. */
|
||||
move_assign_ = reinterpret_cast<decltype(move_assign_)>(copy_assign_);
|
||||
move_assign_n_ = reinterpret_cast<decltype(move_assign_n_)>(copy_assign_n_);
|
||||
move_assign_indices_ = reinterpret_cast<decltype(move_assign_indices_)>(
|
||||
copy_assign_indices_);
|
||||
}
|
||||
else {
|
||||
move_assign_ = move_assign_cb<T>;
|
||||
move_assign_n_ = move_assign_n_cb<T>;
|
||||
move_assign_indices_ = move_assign_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_move_constructible_v<T>) {
|
||||
if constexpr (std::is_trivially_move_constructible_v<T>) {
|
||||
move_construct_ = move_assign_;
|
||||
move_construct_n_ = move_assign_n_;
|
||||
move_construct_indices_ = move_assign_indices_;
|
||||
}
|
||||
else {
|
||||
move_construct_ = move_construct_cb<T>;
|
||||
move_construct_n_ = move_construct_n_cb<T>;
|
||||
move_construct_indices_ = move_construct_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_destructible_v<T>) {
|
||||
if constexpr (std::is_trivially_move_assignable_v<T> && std::is_trivially_destructible_v<T>) {
|
||||
relocate_assign_ = move_assign_;
|
||||
relocate_assign_n_ = move_assign_n_;
|
||||
relocate_assign_indices_ = move_assign_indices_;
|
||||
|
||||
relocate_construct_ = move_assign_;
|
||||
relocate_construct_n_ = move_assign_n_;
|
||||
relocate_construct_indices_ = move_assign_indices_;
|
||||
}
|
||||
else {
|
||||
if constexpr (std::is_move_assignable_v<T>) {
|
||||
relocate_assign_ = relocate_assign_cb<T>;
|
||||
relocate_assign_n_ = relocate_assign_n_cb<T>;
|
||||
relocate_assign_indices_ = relocate_assign_indices_cb<T>;
|
||||
}
|
||||
if constexpr (std::is_move_constructible_v<T>) {
|
||||
relocate_construct_ = relocate_construct_cb<T>;
|
||||
relocate_construct_n_ = relocate_construct_n_cb<T>;
|
||||
relocate_construct_indices_ = relocate_construct_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_copy_assignable_v<T>) {
|
||||
fill_assign_n_ = fill_assign_n_cb<T>;
|
||||
fill_assign_indices_ = fill_assign_indices_cb<T>;
|
||||
}
|
||||
if constexpr (std::is_copy_constructible_v<T>) {
|
||||
if constexpr (std::is_trivially_constructible_v<T>) {
|
||||
fill_construct_n_ = fill_assign_n_;
|
||||
fill_construct_indices_ = fill_assign_indices_;
|
||||
}
|
||||
else {
|
||||
fill_construct_n_ = fill_construct_n_cb<T>;
|
||||
fill_construct_indices_ = fill_construct_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr (bool(Flags & CPPTypeFlags::Hashable)) {
|
||||
hash_ = hash_cb<T>;
|
||||
hash_unique_ = hash_unique_cb<T>;
|
||||
}
|
||||
if constexpr (bool(Flags & CPPTypeFlags::Printable)) {
|
||||
print_ = print_cb<T>;
|
||||
}
|
||||
if constexpr (bool(Flags & CPPTypeFlags::EqualityComparable)) {
|
||||
is_equal_ = is_equal_cb<T>;
|
||||
}
|
||||
|
||||
alignment_mask_ = uintptr_t(this->alignment) - uintptr_t(1);
|
||||
this->has_special_member_functions = (default_construct_ && copy_construct_ && copy_assign_ &&
|
||||
move_construct_ && move_assign_ && destruct_);
|
||||
this->is_default_constructible = default_construct_ != nullptr;
|
||||
this->is_copy_constructible = copy_construct_ != nullptr;
|
||||
this->is_move_constructible = move_construct_ != nullptr;
|
||||
this->is_destructible = destruct_ != nullptr;
|
||||
this->is_copy_assignable = copy_assign_ != nullptr;
|
||||
this->is_move_assignable = move_assign_ != nullptr;
|
||||
|
||||
this->type_index = type_index_counter++;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename T, CPPTypeFlags FLAGS> inline void register_cpp_type(const StringRef type_name)
|
||||
{
|
||||
static CPPType *cpp_type = new (detail::cpp_type_impl<T>.ptr())
|
||||
CPPType(TypeTag<T>(), TypeForValue<CPPTypeFlags, FLAGS>(), type_name);
|
||||
|
||||
/* Call destructor on exit. */
|
||||
struct CPPTypeDestructor {
|
||||
~CPPTypeDestructor()
|
||||
{
|
||||
std::destroy_at(cpp_type);
|
||||
}
|
||||
};
|
||||
static CPPTypeDestructor cpp_type_destructor;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/** Register a #CPPType created with #CPPType::get<T>(). */
|
||||
#define BLI_CPP_TYPE_REGISTER(TYPE_NAME, FLAGS) \
|
||||
blender::detail::register_cpp_type<TYPE_NAME, FLAGS>(STRINGIFY(TYPE_NAME))
|
||||
|
||||
} // namespace blender
|
||||
247
blender-5.2.0/source/blender/blenlib/BLI_csv_parse.hh
Normal file
247
blender-5.2.0/source/blender/blenlib/BLI_csv_parse.hh
Normal file
@@ -0,0 +1,247 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_any.hh"
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_linear_allocator.hh"
|
||||
#include "BLI_offset_indices.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender::csv_parse {
|
||||
|
||||
/**
|
||||
* Contains the fields of a single record of a .csv file. Usually that corresponds to a single
|
||||
* line.
|
||||
*/
|
||||
class CsvRecord {
|
||||
private:
|
||||
Span<Span<char>> fields_;
|
||||
|
||||
public:
|
||||
CsvRecord(Span<Span<char>> fields);
|
||||
|
||||
/** Number of fields in the record. */
|
||||
int64_t size() const;
|
||||
IndexRange index_range() const;
|
||||
|
||||
/** Get the field at the given index. Empty data is returned if the index is too large. */
|
||||
Span<char> field(const int64_t index) const;
|
||||
StringRef field_str(const int64_t index) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains the fields of multiple records.
|
||||
*/
|
||||
class CsvRecords {
|
||||
private:
|
||||
OffsetIndices<int64_t> offsets_;
|
||||
Span<Span<char>> fields_;
|
||||
|
||||
public:
|
||||
CsvRecords(OffsetIndices<int64_t> offsets, Span<Span<char>> fields);
|
||||
|
||||
/** Number of records (rows). */
|
||||
int64_t size() const;
|
||||
IndexRange index_range() const;
|
||||
|
||||
/** Get the record at the given index. */
|
||||
CsvRecord record(const int64_t index) const;
|
||||
};
|
||||
|
||||
struct CsvParseOptions {
|
||||
/** The character that separates fields within a row. */
|
||||
char delimiter = ',';
|
||||
/**
|
||||
* The character that can be used to enclose fields which contain the delimiter or span multiple
|
||||
* lines.
|
||||
*/
|
||||
char quote = '"';
|
||||
/**
|
||||
* Characters that can be used to escape the quote character.
|
||||
* By default, `""` or `\"` both represent an escaped quote.
|
||||
*/
|
||||
Span<char> quote_escape_chars = Span<char>(StringRef("\"\\"));
|
||||
/** Approximate number of bytes per chunk that the input is split into. */
|
||||
int64_t chunk_size_bytes = 64 * 1024;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a `.csv` file. There are two important aspects to the way this interface is designed:
|
||||
* 1. It allows the file to be split into chunks that can be parsed in parallel.
|
||||
* 2. Splitting the file into individual records and fields is separated from parsing the actual
|
||||
* content into e.g. floats. This simplifies the implementation of both parts because the
|
||||
* logical parsing does not have to worry about e.g. the delimiter or quote characters. It also
|
||||
* simplifies unit testing.
|
||||
*
|
||||
* \param buffer: The buffer containing the `.csv` file.
|
||||
* \param options: Options that control how the file is parsed.
|
||||
* \param process_header: A function that is called at most once and contains the fields of the
|
||||
* first row/record.
|
||||
* \param process_records: A function that is called potentially many times in parallel and that
|
||||
* processes a chunk of parsed records. Typically this function parses raw byte fields into e.g.
|
||||
* ints or floats. The result of the parsing process has to be returned. Note that under specific
|
||||
* circumstances, this function may be called twice for the same records. That can happen when
|
||||
* the `.csv` file contains multi-line fields which were split incorrectly at first.
|
||||
* \return A vector containing the return values of the `process_records` function in the correct
|
||||
* order. #std::nullopt is returned if the file was malformed, e.g.
|
||||
* if it has a quoted field that is not closed.
|
||||
*/
|
||||
std::optional<Vector<Any<>>> parse_csv_in_chunks(
|
||||
const Span<char> buffer,
|
||||
const CsvParseOptions &options,
|
||||
FunctionRef<void(const CsvRecord &record)> process_header,
|
||||
FunctionRef<Any<>(const CsvRecords &records)> process_records);
|
||||
|
||||
/**
|
||||
* Same as above, but uses a templated chunk type instead of using #Any which can be more
|
||||
* convenient to use.
|
||||
*/
|
||||
template<typename ChunkT>
|
||||
inline std::optional<Vector<ChunkT>> parse_csv_in_chunks(
|
||||
const Span<char> buffer,
|
||||
const CsvParseOptions &options,
|
||||
FunctionRef<void(const CsvRecord &record)> process_header,
|
||||
FunctionRef<ChunkT(const CsvRecords &records)> process_records)
|
||||
{
|
||||
std::optional<Vector<Any<>>> result = parse_csv_in_chunks(
|
||||
buffer, options, process_header, [&](const CsvRecords &records) {
|
||||
return Any<>(process_records(records));
|
||||
});
|
||||
if (!result.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Vector<ChunkT> result_chunks;
|
||||
result_chunks.reserve(result->size());
|
||||
for (Any<> &value : *result) {
|
||||
result_chunks.append(std::move(value.get<ChunkT>()));
|
||||
}
|
||||
return result_chunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fields in a CSV file may contain escaped quote characters (e.g. `""` or `\"`).
|
||||
* This function replaces these with just the quote character.
|
||||
* The returned string may be reference the input string if it's the same.
|
||||
* Otherwise the returned string is allocated in the given allocator.
|
||||
*/
|
||||
StringRef unescape_field(const StringRef str,
|
||||
const CsvParseOptions &options,
|
||||
LinearAllocator<> &allocator);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #CsvRecord inline functions.
|
||||
* \{ */
|
||||
|
||||
inline CsvRecord::CsvRecord(Span<Span<char>> fields) : fields_(fields) {}
|
||||
|
||||
inline int64_t CsvRecord::size() const
|
||||
{
|
||||
return fields_.size();
|
||||
}
|
||||
|
||||
inline IndexRange CsvRecord::index_range() const
|
||||
{
|
||||
return fields_.index_range();
|
||||
}
|
||||
|
||||
inline Span<char> CsvRecord::field(const int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
if (index >= fields_.size()) {
|
||||
return {};
|
||||
}
|
||||
return fields_[index];
|
||||
}
|
||||
|
||||
inline StringRef CsvRecord::field_str(const int64_t index) const
|
||||
{
|
||||
const Span<char> value = this->field(index);
|
||||
return StringRef(value.data(), value.size());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #CsvRecords inline functions.
|
||||
* \{ */
|
||||
|
||||
inline CsvRecords::CsvRecords(const OffsetIndices<int64_t> offsets, const Span<Span<char>> fields)
|
||||
: offsets_(offsets), fields_(fields)
|
||||
{
|
||||
}
|
||||
|
||||
inline int64_t CsvRecords::size() const
|
||||
{
|
||||
return offsets_.size();
|
||||
}
|
||||
|
||||
inline IndexRange CsvRecords::index_range() const
|
||||
{
|
||||
return offsets_.index_range();
|
||||
}
|
||||
|
||||
inline CsvRecord CsvRecords::record(const int64_t index) const
|
||||
{
|
||||
return CsvRecord(fields_.slice(offsets_[index]));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal functions exposed for testing.
|
||||
* \{ */
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Find the index that ends the current field, i.e. the index of the next delimiter of newline.
|
||||
* The start index has to be the index of the first character in the field. It may also be the
|
||||
* end of the field already if it is empty.
|
||||
*
|
||||
* \param start: The index of the first character in the field. This may also be the end of the
|
||||
* field already if it is empty.
|
||||
* \param delimiter: The character that ends the field.
|
||||
* \return Index of the next delimiter, a newline character or the end of the buffer.
|
||||
*/
|
||||
int64_t find_end_of_simple_field(Span<char> buffer, int64_t start, char delimiter);
|
||||
|
||||
/**
|
||||
* Find the index of the quote that ends the current field.
|
||||
*
|
||||
* \param start: The index after the opening quote.
|
||||
* \param quote: The quote character that ends the field.
|
||||
* \param escape_chars: The characters that may be used to escape the quote character.
|
||||
* \return Index of the quote character that ends the field, or std::nullopt if the field is
|
||||
* malformed and does not have an end.
|
||||
*/
|
||||
std::optional<int64_t> find_end_of_quoted_field(Span<char> buffer,
|
||||
int64_t start,
|
||||
char quote,
|
||||
Span<char> escape_chars);
|
||||
|
||||
/**
|
||||
* Finds all fields for the record starting at the given index. Typically, the record ends with a
|
||||
* newline, but quoted multi-line records are supported as well.
|
||||
*
|
||||
* \return Index of the start of the next record or the end of the buffer. #std::nullopt is
|
||||
* returned if the buffer has a malformed record at the end,
|
||||
* i.e. a quoted field that is not closed.
|
||||
*/
|
||||
std::optional<int64_t> parse_record_fields(const Span<char> buffer,
|
||||
const int64_t start,
|
||||
const char delimiter,
|
||||
const char quote,
|
||||
const Span<char> quote_escape_chars,
|
||||
Vector<Span<char>> &r_fields);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::csv_parse
|
||||
217
blender-5.2.0/source/blender/blenlib/BLI_delaunay_2d.hh
Normal file
217
blender-5.2.0/source/blender/blenlib/BLI_delaunay_2d.hh
Normal file
@@ -0,0 +1,217 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_math_mpq.hh"
|
||||
#include "BLI_math_vector_mpq_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* This header file contains both a C interface and a C++ interface
|
||||
* to the 2D Constrained Delaunay Triangulation library routine.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for Constrained Delaunay Triangulation (CDT) in 2D.
|
||||
*
|
||||
* The input is a set of vertices, edges between those vertices,
|
||||
* and faces using those vertices.
|
||||
* Those inputs are called "constraints". The output must contain
|
||||
* those constraints, or at least edges, points, and vertices that
|
||||
* may be pieced together to form the constraints. Part of the
|
||||
* work of doing the CDT is to detect intersections and mergers
|
||||
* among the input elements, so these routines are also useful
|
||||
* for doing 2D intersection.
|
||||
*
|
||||
* The output is a triangulation of the plane that includes the
|
||||
* constraints in the above sense, and also satisfies the
|
||||
* "Delaunay condition" as modified to take into account that
|
||||
* the constraints must be there: for every non-constrained edge
|
||||
* in the output, there is a circle through the endpoints that
|
||||
* does not contain any of the vertices directly connected to
|
||||
* those endpoints. What this means in practice is that as
|
||||
* much as possible the triangles look "nice" -- not too long
|
||||
* and skinny.
|
||||
*
|
||||
* Optionally, the output can be a subset of the triangulation
|
||||
* (but still containing all of the constraints), to get the
|
||||
* effect of 2D intersection.
|
||||
*
|
||||
* The underlying method is incremental, but we need to know
|
||||
* beforehand a bounding box for all of the constraints.
|
||||
* This code can be extended in the future to allow for
|
||||
* deletion of constraints, if there is a use in Blender
|
||||
* for dynamically maintaining a triangulation.
|
||||
*/
|
||||
|
||||
/** What triangles and edges of CDT are desired when getting output? */
|
||||
enum CDT_output_type {
|
||||
/** All triangles, outer boundary is convex hull. */
|
||||
CDT_FULL,
|
||||
/** All triangles fully enclosed by constraint edges or faces. */
|
||||
CDT_INSIDE,
|
||||
/**
|
||||
* Like #CDT_INSIDE, but detect holes and omit those from output.
|
||||
*
|
||||
* Uses the "even-odd rule": a point is inside if a ray from that point crosses
|
||||
* an odd number of boundary edges. This creates alternating filled/unfilled regions
|
||||
* for nested or overlapping curves. Overlapping regions with the same winding
|
||||
* direction will cancel out and become holes.
|
||||
*/
|
||||
CDT_INSIDE_WITH_HOLES,
|
||||
/**
|
||||
* Like #CDT_INSIDE_WITH_HOLES, but uses the **non-zero winding rule**.
|
||||
*
|
||||
* A point is inside if the winding number (sum of signed edge crossings) is non-zero.
|
||||
* This creates "union" behavior: overlapping curves with the same winding direction
|
||||
* merge together instead of creating holes.
|
||||
*/
|
||||
CDT_INSIDE_WITH_HOLES_NONZERO,
|
||||
/** Only point, edge, and face constraints, and their intersections. */
|
||||
CDT_CONSTRAINTS,
|
||||
/**
|
||||
* Like #CDT_CONSTRAINTS, but keep enough edges so that any output faces that came
|
||||
* from input faces can be made as valid #BMesh faces in Blender: that is,
|
||||
* no vertex appears more than once and no isolated holes in faces.
|
||||
*/
|
||||
CDT_CONSTRAINTS_VALID_BMESH,
|
||||
/**
|
||||
* Like #CDT_CONSTRAINTS_VALID_BMESH, but detect holes using the even-odd rule.
|
||||
* See #CDT_INSIDE_WITH_HOLES for explanation of the even-odd rule.
|
||||
*/
|
||||
CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES,
|
||||
/**
|
||||
* Like #CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES, but uses non-zero winding rule.
|
||||
* See #CDT_INSIDE_WITH_HOLES_NONZERO for explanation.
|
||||
*/
|
||||
CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES_NONZERO,
|
||||
};
|
||||
|
||||
namespace meshintersect {
|
||||
|
||||
/**
|
||||
* Input to Constrained Delaunay Triangulation.
|
||||
* Input vertex coordinates are stored in `vert`. For the rest of the input,
|
||||
* vertices are referred to by indices into that array.
|
||||
* Edges and Faces are optional. If provided, they will
|
||||
* appear in the output triangulation ("constraints").
|
||||
* One can provide faces and not edges -- the edges
|
||||
* implied by the faces will be inferred.
|
||||
*
|
||||
* The edges are given by pairs of vertex indices.
|
||||
* The faces are given as groups of vertex indices, in counterclockwise order.
|
||||
*
|
||||
* The edges implied by the faces are automatically added
|
||||
* and need not be put in the edges array, which is intended
|
||||
* as a way to specify edges that are not part of any face.
|
||||
*
|
||||
* Some notes about some special cases and how they are handled:
|
||||
* - Input faces can have any number of vertices greater than 2. Depending
|
||||
* on the output option, ngons may be triangulated or they may remain
|
||||
* as ngons.
|
||||
* - Input faces may have repeated vertices. Output faces will not,
|
||||
* except when the CDT_CONSTRAINTS output option is used.
|
||||
* - Input faces may have edges that self-intersect, but currently the labeling
|
||||
* of which output faces have which input faces may not be done correctly,
|
||||
* since the labeling relies on the inside being on the left of edges
|
||||
* as one traverses the face. Output faces will not self-intersect.
|
||||
* - Input edges, including those implied by the input faces, may have
|
||||
* zero-length or near-zero-length edges (nearness as determined by epsilon),
|
||||
* but those edges will not be in the output.
|
||||
* - Input edges (including face edges) can overlap or nearly overlap each other.
|
||||
* The output edges will not overlap, but instead be divided into as many
|
||||
* edges as necessary to represent each overlap regime.
|
||||
* - Input vertices may be coincide with, or nearly coincide with (as determined
|
||||
* by epsilon) other input vertices. Only one representative will survive
|
||||
* in the output. If an input vertex is within epsilon of an edge (including
|
||||
* an added triangulation edge), it will be snapped to that edge, so the
|
||||
* output coordinates may not exactly match the input coordinates in all cases.
|
||||
* - Wire edges (those not part of faces) and isolated vertices are allowed in
|
||||
* the input. If they are inside faces, they will be incorporated into the
|
||||
* triangulation of those faces.
|
||||
*
|
||||
* Epsilon is used for "is it near enough" distance calculations.
|
||||
* If zero is supplied for epsilon, an internal value of 1e-8 used
|
||||
* instead, since this code will not work correctly if it is not allowed
|
||||
* to merge "too near" vertices.
|
||||
*
|
||||
* Normally the output will contain mappings from outputs to inputs.
|
||||
* If this is not needed, set need_ids to false and the execution may be much
|
||||
* faster in some circumstances.
|
||||
*/
|
||||
template<typename T> class CDT_input {
|
||||
public:
|
||||
Array<VecBase<T, 2>> vert;
|
||||
Array<std::pair<int, int>> edge;
|
||||
Array<Vector<int>> face;
|
||||
T epsilon{0};
|
||||
bool need_ids{true};
|
||||
};
|
||||
|
||||
/**
|
||||
* A representation of the triangulation for output.
|
||||
* See #CDT_input for the representation of the output
|
||||
* vertices, edges, and faces, all represented in
|
||||
* a similar way to the input.
|
||||
*
|
||||
* The output may have merged some input vertices together,
|
||||
* if they were closer than some epsilon distance.
|
||||
* The output edges may be overlapping sub-segments of some
|
||||
* input edges; or they may be new edges for the triangulation.
|
||||
* The output faces may be pieces of some input faces, or they
|
||||
* may be new.
|
||||
*
|
||||
* Extra outputs are used to represent the output to input
|
||||
* mapping of vertices, edges, and faces.
|
||||
* These are only set if need_ids is true in the input.
|
||||
*
|
||||
*
|
||||
* For edges, the edge_orig triple can also say which original face
|
||||
* edge is part of a given output edge. See the comment below for how
|
||||
* to decode the entries in the edge_orig table.
|
||||
*
|
||||
* \note Regarding `uint32_t`: Each input face reserves a block of IDs
|
||||
* to encode its edges. These blocks stack up with the number of faces,
|
||||
* so `uint32_t` is used to provide sufficient range (see #153708).
|
||||
*/
|
||||
template<typename T> class CDT_result {
|
||||
public:
|
||||
Array<VecBase<T, 2>> vert;
|
||||
Array<std::pair<int, int>> edge;
|
||||
Array<Vector<int>> face;
|
||||
/* The orig vectors are only populated if the need_ids input field is true. */
|
||||
/** For each output vert, which input verts correspond to it? */
|
||||
Array<Vector<uint32_t>> vert_orig;
|
||||
/**
|
||||
* For each output edge, which input edges does it overlap?
|
||||
* The input edge ids are encoded as follows:
|
||||
* if the value is less than face_edge_offset, then it is
|
||||
* an index into the input edge[] array.
|
||||
* else let (a, b) = the quotient and remainder of dividing
|
||||
* the edge index by face_edge_offset; "a" will be the input face + 1,
|
||||
* and "b" will be a position within that face.
|
||||
*/
|
||||
Array<Vector<uint32_t>> edge_orig;
|
||||
/** For each output face, which original faces does it overlap? */
|
||||
Array<Vector<uint32_t>> face_orig;
|
||||
/** Used to encode edge_orig (see above). */
|
||||
uint32_t face_edge_offset;
|
||||
};
|
||||
|
||||
CDT_result<double> delaunay_2d_calc(const CDT_input<double> &input, CDT_output_type output_type);
|
||||
|
||||
#ifdef WITH_GMP
|
||||
CDT_result<mpq_class> delaunay_2d_calc(const CDT_input<mpq_class> &input,
|
||||
CDT_output_type output_type);
|
||||
#endif
|
||||
|
||||
} // namespace meshintersect
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,169 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* In geometry nodes, many functions accept fields as inputs. For the implementation that means
|
||||
* that the inputs are virtual arrays. Usually those are backed by actual arrays or single values
|
||||
* but sometimes virtual arrays are used to compute values on demand or convert between data
|
||||
* formats.
|
||||
*
|
||||
* Using virtual arrays has the downside that individual elements are accessed through a virtual
|
||||
* method call, which has some overhead compared to normal array access. Whether this overhead is
|
||||
* negligible depends on the context. For very small functions (e.g. a single addition), the
|
||||
* overhead can make the function many times slower. Furthermore, it prevents the compiler from
|
||||
* doing some optimizations (e.g. loop unrolling and inserting SIMD instructions).
|
||||
*
|
||||
* The solution is to "devirtualize" the virtual arrays in cases when the overhead cannot be
|
||||
* ignored. That means that the function is instantiated multiple times at compile time for the
|
||||
* different cases. For example, there can be an optimized function that adds a span and a single
|
||||
* value, and another function that adds a span and another span. At run-time there is a dynamic
|
||||
* dispatch that executes the best function given the specific virtual arrays.
|
||||
*
|
||||
* The problem with this devirtualization is that it can result in exponentially increasing compile
|
||||
* times and binary sizes, depending on the number of parameters that are devirtualized separately.
|
||||
* So there is always a trade-off between run-time performance and compile-time/binary-size.
|
||||
*
|
||||
* This file provides a utility to devirtualize function parameters using a high level API. This
|
||||
* makes it easy to experiment with different extremes of the mentioned trade-off and allows
|
||||
* finding a good compromise for each function.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <tuple>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Calls the given function with devirtualized parameters if possible. Note that using many
|
||||
* non-trivial devirtualizers results in exponential code growth.
|
||||
*
|
||||
* \return True if the function has been called.
|
||||
*
|
||||
* Every devirtualizer is expected to have a `devirtualize(auto fn) -> bool` method.
|
||||
* This method is expected to do one of two things:
|
||||
* - Call `fn` with the devirtualized argument and return what `fn` returns.
|
||||
* - Don't call `fn` (because the devirtualization failed) and return false.
|
||||
*
|
||||
* Examples for devirtualizers: #BasicDevirtualizer, #VArrayDevirtualizer.
|
||||
*/
|
||||
template<typename Fn, typename... Devirtualizers>
|
||||
inline bool call_with_devirtualized_parameters(const std::tuple<Devirtualizers...> &devis,
|
||||
const Fn &fn)
|
||||
{
|
||||
/* In theory the code below could be generalized to avoid code duplication. However, the maximum
|
||||
* number of parameters is expected to be relatively low. Explicitly implementing the different
|
||||
* cases makes it more obvious to see what is going on and also makes inlining everything easier
|
||||
* for the compiler. */
|
||||
constexpr size_t DeviNum = sizeof...(Devirtualizers);
|
||||
if constexpr (DeviNum == 0) {
|
||||
fn();
|
||||
return true;
|
||||
}
|
||||
if constexpr (DeviNum == 1) {
|
||||
return std::get<0>(devis).devirtualize([&](auto param0) {
|
||||
fn(param0);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if constexpr (DeviNum == 2) {
|
||||
return std::get<0>(devis).devirtualize([&](auto &¶m0) {
|
||||
return std::get<1>(devis).devirtualize([&](auto &¶m1) {
|
||||
fn(param0, param1);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
if constexpr (DeviNum == 3) {
|
||||
return std::get<0>(devis).devirtualize([&](auto &¶m0) {
|
||||
return std::get<1>(devis).devirtualize([&](auto &¶m1) {
|
||||
return std::get<2>(devis).devirtualize([&](auto &¶m2) {
|
||||
fn(param0, param1, param2);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
if constexpr (DeviNum == 4) {
|
||||
return std::get<0>(devis).devirtualize([&](auto &¶m0) {
|
||||
return std::get<1>(devis).devirtualize([&](auto &¶m1) {
|
||||
return std::get<2>(devis).devirtualize([&](auto &¶m2) {
|
||||
return std::get<3>(devis).devirtualize([&](auto &¶m3) {
|
||||
fn(param0, param1, param2, param3);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
if constexpr (DeviNum == 5) {
|
||||
return std::get<0>(devis).devirtualize([&](auto &¶m0) {
|
||||
return std::get<1>(devis).devirtualize([&](auto &¶m1) {
|
||||
return std::get<2>(devis).devirtualize([&](auto &¶m2) {
|
||||
return std::get<3>(devis).devirtualize([&](auto &¶m3) {
|
||||
return std::get<4>(devis).devirtualize([&](auto &¶m4) {
|
||||
fn(param0, param1, param2, param3, param4);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
if constexpr (DeviNum == 6) {
|
||||
return std::get<0>(devis).devirtualize([&](auto &¶m0) {
|
||||
return std::get<1>(devis).devirtualize([&](auto &¶m1) {
|
||||
return std::get<2>(devis).devirtualize([&](auto &¶m2) {
|
||||
return std::get<3>(devis).devirtualize([&](auto &¶m3) {
|
||||
return std::get<4>(devis).devirtualize([&](auto &¶m4) {
|
||||
return std::get<5>(devis).devirtualize([&](auto &¶m5) {
|
||||
fn(param0, param1, param2, param3, param4, param5);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
if constexpr (DeviNum == 7) {
|
||||
return std::get<0>(devis).devirtualize([&](auto &¶m0) {
|
||||
return std::get<1>(devis).devirtualize([&](auto &¶m1) {
|
||||
return std::get<2>(devis).devirtualize([&](auto &¶m2) {
|
||||
return std::get<3>(devis).devirtualize([&](auto &¶m3) {
|
||||
return std::get<4>(devis).devirtualize([&](auto &¶m4) {
|
||||
return std::get<5>(devis).devirtualize([&](auto &¶m5) {
|
||||
return std::get<6>(devis).devirtualize([&](auto &¶m6) {
|
||||
fn(param0, param1, param2, param3, param4, param5, param6);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A devirtualizer to be used with #call_with_devirtualized_parameters.
|
||||
*
|
||||
* This one is very simple, it does not perform any actual devirtualization. It can be used to pass
|
||||
* parameters to the function that shouldn't be devirtualized.
|
||||
*/
|
||||
template<typename T> struct BasicDevirtualizer {
|
||||
const T value;
|
||||
|
||||
template<typename Fn> bool devirtualize(const Fn &fn) const
|
||||
{
|
||||
return fn(this->value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
45
blender-5.2.0/source/blender/blenlib/BLI_dial_2d.h
Normal file
45
blender-5.2.0/source/blender/blenlib/BLI_dial_2d.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* \note dials act similar to old rotation based phones and output an angle.
|
||||
*
|
||||
* They just are initialized with the center of the dial and a threshold value as input.
|
||||
*
|
||||
* When the distance of the current position of the dial from the center
|
||||
* exceeds the threshold, this position is used to calculate the initial direction.
|
||||
* After that, the angle from the initial direction is calculated based on
|
||||
* current and previous directions of the digit, and returned to the user.
|
||||
*
|
||||
* Usage examples:
|
||||
*
|
||||
* \code{.c}
|
||||
* float start_position[2] = {0.0f, 0.0f};
|
||||
* float current_position[2];
|
||||
* float threshold = 0.5f;
|
||||
* float angle;
|
||||
* Dial *dial;
|
||||
*
|
||||
* dial = BLI_dial_init(start_position, threshold);
|
||||
*
|
||||
* angle = BLI_dial_angle(dial, current_position);
|
||||
*
|
||||
* BLI_dial_free(dial);
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Dial;
|
||||
|
||||
Dial *BLI_dial_init(const float start_position[2], float threshold);
|
||||
void BLI_dial_free(Dial *dial);
|
||||
|
||||
float BLI_dial_angle(Dial *dial, const float current_position[2]);
|
||||
|
||||
} // namespace blender
|
||||
110
blender-5.2.0/source/blender/blenlib/BLI_disjoint_set.hh
Normal file
110
blender-5.2.0/source/blender/blenlib/BLI_disjoint_set.hh
Normal file
@@ -0,0 +1,110 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* This implements the disjoint set data structure with path compression and union by rank.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_index_range.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
template<typename T = int64_t> class DisjointSet {
|
||||
private:
|
||||
Array<T> parents_;
|
||||
Array<T> ranks_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new disjoint set with the given size. Initially, every element is in a separate set.
|
||||
*/
|
||||
DisjointSet(const int64_t size) : parents_(size), ranks_(size, 0)
|
||||
{
|
||||
BLI_assert(size >= 0);
|
||||
for (const int64_t i : IndexRange(size)) {
|
||||
parents_[i] = T(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join the sets containing elements x and y. Nothing happens when they have been in the same set
|
||||
* before. Shared root is returned.
|
||||
*/
|
||||
T join(const T x, const T y)
|
||||
{
|
||||
T root1 = this->find_root(x);
|
||||
T root2 = this->find_root(y);
|
||||
|
||||
/* x and y are in the same set already. */
|
||||
if (root1 == root2) {
|
||||
return root1;
|
||||
}
|
||||
|
||||
/* Implement union by rank heuristic. */
|
||||
if (ranks_[root1] < ranks_[root2]) {
|
||||
std::swap(root1, root2);
|
||||
}
|
||||
parents_[root2] = root1;
|
||||
|
||||
if (ranks_[root1] == ranks_[root2]) {
|
||||
ranks_[root1]++;
|
||||
}
|
||||
|
||||
return root1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true when x and y are in the same set.
|
||||
*/
|
||||
bool in_same_set(const T x, const T y)
|
||||
{
|
||||
T root1 = this->find_root(x);
|
||||
T root2 = this->find_root(y);
|
||||
return root1 == root2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the element that represents the set containing x currently.
|
||||
*/
|
||||
T find_root(const T x)
|
||||
{
|
||||
/* Find root by following parents. */
|
||||
T root = x;
|
||||
while (parents_[root] != root) {
|
||||
root = parents_[root];
|
||||
}
|
||||
|
||||
/* Compress path. */
|
||||
T to_root = x;
|
||||
while (parents_[to_root] != root) {
|
||||
const T parent = parents_[to_root];
|
||||
parents_[to_root] = root;
|
||||
to_root = parent;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as above but intended to be threadsafe. Better to use even if this is expected to be not
|
||||
* an issue.
|
||||
*/
|
||||
T find_root(const T x) const
|
||||
{
|
||||
/* Find root by following parents. */
|
||||
T root = x;
|
||||
while (parents_[root] != root) {
|
||||
root = parents_[root];
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
298
blender-5.2.0/source/blender/blenlib/BLI_dot_export.hh
Normal file
298
blender-5.2.0/source/blender/blenlib/BLI_dot_export.hh
Normal file
@@ -0,0 +1,298 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* Language grammar: https://www.graphviz.org/doc/info/lang.html
|
||||
* Attributes: https://www.graphviz.org/doc/info/attrs.html
|
||||
* Node Shapes: https://www.graphviz.org/doc/info/shapes.html
|
||||
* Preview: https://dreampuf.github.io/GraphvizOnline
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BLI_dot_export_attribute_enums.hh"
|
||||
|
||||
#include <iosfwd>
|
||||
#include <optional>
|
||||
|
||||
namespace blender::dot_export {
|
||||
|
||||
class Graph;
|
||||
class DirectedGraph;
|
||||
class UndirectedGraph;
|
||||
class Node;
|
||||
class NodePort;
|
||||
class DirectedEdge;
|
||||
class UndirectedEdge;
|
||||
class Cluster;
|
||||
|
||||
class Attributes {
|
||||
private:
|
||||
Map<std::string, std::string> attributes_;
|
||||
|
||||
public:
|
||||
void export__as_bracket_list(std::stringstream &ss) const;
|
||||
|
||||
void set(StringRef key, StringRef value)
|
||||
{
|
||||
attributes_.add_overwrite(key, value);
|
||||
}
|
||||
|
||||
void set(StringRef key, float value)
|
||||
{
|
||||
attributes_.add_overwrite(key, std::to_string(value));
|
||||
}
|
||||
};
|
||||
|
||||
class Graph {
|
||||
private:
|
||||
Vector<std::unique_ptr<Node>> nodes_;
|
||||
Vector<std::unique_ptr<Cluster>> clusters_;
|
||||
|
||||
Set<Node *> top_level_nodes_;
|
||||
Set<Cluster *> top_level_clusters_;
|
||||
|
||||
friend Cluster;
|
||||
friend Node;
|
||||
|
||||
public:
|
||||
Attributes attributes;
|
||||
|
||||
Node &new_node(StringRef label);
|
||||
Cluster &new_cluster(StringRef label = "");
|
||||
|
||||
void export__declare_nodes_and_clusters(std::stringstream &ss) const;
|
||||
|
||||
void set_rankdir(Attr_rankdir rankdir)
|
||||
{
|
||||
attributes.set("rankdir", rankdir_to_string(rankdir));
|
||||
}
|
||||
|
||||
void set_random_cluster_bgcolors();
|
||||
};
|
||||
|
||||
class Cluster {
|
||||
private:
|
||||
Graph &graph_;
|
||||
Cluster *parent_ = nullptr;
|
||||
Set<Cluster *> children_;
|
||||
Set<Node *> nodes_;
|
||||
|
||||
friend Graph;
|
||||
friend Node;
|
||||
|
||||
public:
|
||||
Attributes attributes;
|
||||
|
||||
Cluster(Graph &graph) : graph_(graph) {}
|
||||
|
||||
void export__declare_nodes_and_clusters(std::stringstream &ss) const;
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
return "cluster_" + std::to_string(uintptr_t(this));
|
||||
}
|
||||
|
||||
void set_parent_cluster(Cluster *new_parent);
|
||||
void set_parent_cluster(Cluster &cluster)
|
||||
{
|
||||
this->set_parent_cluster(&cluster);
|
||||
}
|
||||
|
||||
Cluster *parent_cluster()
|
||||
{
|
||||
return parent_;
|
||||
}
|
||||
|
||||
void set_random_cluster_bgcolors();
|
||||
|
||||
bool contains(Node &node) const;
|
||||
};
|
||||
|
||||
class Node {
|
||||
private:
|
||||
Graph &graph_;
|
||||
Cluster *cluster_ = nullptr;
|
||||
|
||||
friend Graph;
|
||||
|
||||
public:
|
||||
Attributes attributes;
|
||||
|
||||
Node(Graph &graph) : graph_(graph) {}
|
||||
|
||||
void set_parent_cluster(Cluster *cluster);
|
||||
void set_parent_cluster(Cluster &cluster)
|
||||
{
|
||||
this->set_parent_cluster(&cluster);
|
||||
}
|
||||
|
||||
Cluster *parent_cluster()
|
||||
{
|
||||
return cluster_;
|
||||
}
|
||||
|
||||
void set_shape(Attr_shape shape)
|
||||
{
|
||||
attributes.set("shape", shape_to_string(shape));
|
||||
}
|
||||
|
||||
/* See https://www.graphviz.org/doc/info/attrs.html#k:color. */
|
||||
void set_background_color(StringRef name)
|
||||
{
|
||||
attributes.set("fillcolor", name);
|
||||
attributes.set("style", "filled");
|
||||
}
|
||||
|
||||
void export__as_id(std::stringstream &ss) const;
|
||||
|
||||
void export__as_declaration(std::stringstream &ss) const;
|
||||
};
|
||||
|
||||
class UndirectedGraph final : public Graph {
|
||||
private:
|
||||
Vector<std::unique_ptr<UndirectedEdge>> edges_;
|
||||
|
||||
public:
|
||||
std::string to_dot_string() const;
|
||||
|
||||
UndirectedEdge &new_edge(NodePort a, NodePort b);
|
||||
};
|
||||
|
||||
class DirectedGraph final : public Graph {
|
||||
private:
|
||||
Vector<std::unique_ptr<DirectedEdge>> edges_;
|
||||
|
||||
public:
|
||||
std::string to_dot_string() const;
|
||||
|
||||
DirectedEdge &new_edge(NodePort from, NodePort to);
|
||||
};
|
||||
|
||||
class NodePort {
|
||||
private:
|
||||
Node *node_;
|
||||
std::optional<std::string> port_name_;
|
||||
std::optional<std::string> port_position_;
|
||||
|
||||
public:
|
||||
NodePort(Node &node,
|
||||
std::optional<std::string> port_name = {},
|
||||
std::optional<std::string> port_position = {})
|
||||
: node_(&node), port_name_(std::move(port_name)), port_position_(std::move(port_position))
|
||||
{
|
||||
}
|
||||
|
||||
void to_dot_string(std::stringstream &ss) const;
|
||||
};
|
||||
|
||||
class Edge : NonCopyable, NonMovable {
|
||||
protected:
|
||||
NodePort a_;
|
||||
NodePort b_;
|
||||
|
||||
public:
|
||||
Attributes attributes;
|
||||
|
||||
Edge(NodePort a, NodePort b) : a_(std::move(a)), b_(std::move(b)) {}
|
||||
|
||||
void set_arrowhead(Attr_arrowType type)
|
||||
{
|
||||
attributes.set("arrowhead", arrowType_to_string(type));
|
||||
}
|
||||
|
||||
void set_arrowtail(Attr_arrowType type)
|
||||
{
|
||||
attributes.set("arrowtail", arrowType_to_string(type));
|
||||
}
|
||||
|
||||
void set_dir(Attr_dirType type)
|
||||
{
|
||||
attributes.set("dir", dirType_to_string(type));
|
||||
}
|
||||
|
||||
void set_label(StringRef label)
|
||||
{
|
||||
attributes.set("label", label);
|
||||
}
|
||||
};
|
||||
|
||||
class DirectedEdge : public Edge {
|
||||
public:
|
||||
DirectedEdge(NodePort from, NodePort to) : Edge(std::move(from), std::move(to)) {}
|
||||
|
||||
void export__as_edge_statement(std::stringstream &ss) const;
|
||||
};
|
||||
|
||||
class UndirectedEdge : public Edge {
|
||||
public:
|
||||
UndirectedEdge(NodePort a, NodePort b) : Edge(std::move(a), std::move(b)) {}
|
||||
|
||||
void export__as_edge_statement(std::stringstream &ss) const;
|
||||
};
|
||||
|
||||
std::string color_attr_from_hsv(float h, float s, float v);
|
||||
|
||||
struct NodeWithSockets {
|
||||
struct Socket {
|
||||
std::string name;
|
||||
std::optional<std::string> fontcolor;
|
||||
};
|
||||
struct Input : public Socket {};
|
||||
struct Output : public Socket {};
|
||||
|
||||
std::string node_name;
|
||||
Vector<Input> inputs;
|
||||
Vector<Output> outputs;
|
||||
|
||||
Input &add_input(std::string name)
|
||||
{
|
||||
this->inputs.append({});
|
||||
Input &input = this->inputs.last();
|
||||
input.name = std::move(name);
|
||||
return input;
|
||||
}
|
||||
|
||||
Output &add_output(std::string name)
|
||||
{
|
||||
this->outputs.append({});
|
||||
Output &output = this->outputs.last();
|
||||
output.name = std::move(name);
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
class NodeWithSocketsRef {
|
||||
private:
|
||||
Node *node_;
|
||||
|
||||
public:
|
||||
NodeWithSocketsRef(Node &node, const NodeWithSockets &data);
|
||||
|
||||
Node &node()
|
||||
{
|
||||
return *node_;
|
||||
}
|
||||
|
||||
NodePort input(int index) const
|
||||
{
|
||||
std::string port = "\"in" + std::to_string(index) + "\"";
|
||||
return NodePort(*node_, port, "w");
|
||||
}
|
||||
|
||||
NodePort output(int index) const
|
||||
{
|
||||
std::string port = "\"out" + std::to_string(index) + "\"";
|
||||
return NodePort(*node_, port, "e");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::dot_export
|
||||
@@ -0,0 +1,112 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
namespace blender::dot_export {
|
||||
|
||||
enum class Attr_rankdir {
|
||||
LeftToRight,
|
||||
TopToBottom,
|
||||
};
|
||||
|
||||
inline StringRef rankdir_to_string(Attr_rankdir value)
|
||||
{
|
||||
switch (value) {
|
||||
case Attr_rankdir::LeftToRight:
|
||||
return "LR";
|
||||
case Attr_rankdir::TopToBottom:
|
||||
return "TB";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
enum class Attr_shape {
|
||||
Rectangle,
|
||||
Ellipse,
|
||||
Circle,
|
||||
Point,
|
||||
Diamond,
|
||||
Square,
|
||||
};
|
||||
|
||||
inline StringRef shape_to_string(Attr_shape value)
|
||||
{
|
||||
switch (value) {
|
||||
case Attr_shape::Rectangle:
|
||||
return "rectangle";
|
||||
case Attr_shape::Ellipse:
|
||||
return "ellipse";
|
||||
case Attr_shape::Circle:
|
||||
return "circle";
|
||||
case Attr_shape::Point:
|
||||
return "point";
|
||||
case Attr_shape::Diamond:
|
||||
return "diamond";
|
||||
case Attr_shape::Square:
|
||||
return "square";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
enum class Attr_arrowType {
|
||||
Normal,
|
||||
Inv,
|
||||
Dot,
|
||||
None,
|
||||
Empty,
|
||||
Box,
|
||||
Vee,
|
||||
};
|
||||
|
||||
inline StringRef arrowType_to_string(Attr_arrowType value)
|
||||
{
|
||||
switch (value) {
|
||||
case Attr_arrowType::Normal:
|
||||
return "normal";
|
||||
case Attr_arrowType::Inv:
|
||||
return "inv";
|
||||
case Attr_arrowType::Dot:
|
||||
return "dot";
|
||||
case Attr_arrowType::None:
|
||||
return "none";
|
||||
case Attr_arrowType::Empty:
|
||||
return "empty";
|
||||
case Attr_arrowType::Box:
|
||||
return "box";
|
||||
case Attr_arrowType::Vee:
|
||||
return "vee";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
enum class Attr_dirType {
|
||||
Forward,
|
||||
Back,
|
||||
Both,
|
||||
None,
|
||||
};
|
||||
|
||||
inline StringRef dirType_to_string(Attr_dirType value)
|
||||
{
|
||||
switch (value) {
|
||||
case Attr_dirType::Forward:
|
||||
return "forward";
|
||||
case Attr_dirType::Back:
|
||||
return "back";
|
||||
case Attr_dirType::Both:
|
||||
return "both";
|
||||
case Attr_dirType::None:
|
||||
return "none";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace blender::dot_export
|
||||
@@ -0,0 +1,62 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_assert.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* A dynamic stack buffer can be used instead of #alloca when one wants to allocate a dynamic
|
||||
* amount of memory on the stack. Using this class has some advantages:
|
||||
* - It falls back to heap allocation, when the size is too large.
|
||||
* - It can be used in loops safely.
|
||||
* - If the buffer is heap allocated, it is free automatically in the destructor.
|
||||
*/
|
||||
template<size_t ReservedSize = 64, size_t ReservedAlignment = 64>
|
||||
class alignas(ReservedAlignment) DynamicStackBuffer {
|
||||
private:
|
||||
/* Don't create an empty array. This causes problems with some compilers. */
|
||||
char reserved_buffer_[(ReservedSize > 0) ? ReservedSize : 1];
|
||||
void *buffer_;
|
||||
|
||||
public:
|
||||
DynamicStackBuffer(const int64_t size, const int64_t alignment)
|
||||
{
|
||||
BLI_assert(size >= 0);
|
||||
BLI_assert(alignment >= 0);
|
||||
if (size <= ReservedSize && alignment <= ReservedAlignment) {
|
||||
buffer_ = reserved_buffer_;
|
||||
}
|
||||
else {
|
||||
buffer_ = MEM_new_uninitialized_aligned(size, alignment, __func__);
|
||||
}
|
||||
}
|
||||
~DynamicStackBuffer()
|
||||
{
|
||||
if (buffer_ != reserved_buffer_) {
|
||||
MEM_delete_void(buffer_);
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't allow any copying or moving of this type. */
|
||||
DynamicStackBuffer(const DynamicStackBuffer &other) = delete;
|
||||
DynamicStackBuffer(DynamicStackBuffer &&other) = delete;
|
||||
DynamicStackBuffer &operator=(const DynamicStackBuffer &other) = delete;
|
||||
DynamicStackBuffer &operator=(DynamicStackBuffer &&other) = delete;
|
||||
|
||||
void *buffer() const
|
||||
{
|
||||
return buffer_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
106
blender-5.2.0/source/blender/blenlib/BLI_dynstr.h
Normal file
106
blender-5.2.0/source/blender/blenlib/BLI_dynstr.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief A dynamically sized string ADT.
|
||||
* \section aboutdynstr Dynamic String
|
||||
* This ADT is designed purely for dynamic string creation
|
||||
* through appending, not for general usage, the intent is
|
||||
* to build up dynamic strings using a DynStr object, then
|
||||
* convert it to a c-string and work with that.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct DynStr;
|
||||
|
||||
/** The abstract DynStr type. */
|
||||
struct DynStr;
|
||||
|
||||
/**
|
||||
* Create a new #DynStr.
|
||||
*
|
||||
* \return Pointer to a new #DynStr.
|
||||
*/
|
||||
DynStr *BLI_dynstr_new() ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Create a new #DynStr.
|
||||
*
|
||||
* \return Pointer to a new #DynStr.
|
||||
*/
|
||||
DynStr *BLI_dynstr_new_memarena() ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Append a c-string to a #DynStr.
|
||||
*
|
||||
* \param ds: The #DynStr to append to.
|
||||
* \param cstr: The c-string to append.
|
||||
*/
|
||||
void BLI_dynstr_append(DynStr *__restrict ds, const char *cstr) ATTR_NONNULL();
|
||||
/**
|
||||
* Append a length clamped c-string to a #DynStr.
|
||||
*
|
||||
* \param ds: The #DynStr to append to.
|
||||
* \param cstr: The c-string to append.
|
||||
* \param len: The maximum length of the c-string to copy.
|
||||
*/
|
||||
void BLI_dynstr_nappend(DynStr *__restrict ds, const char *cstr, int len) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Append a c-string to a #DynStr, but with formatting like `printf`.
|
||||
*
|
||||
* \param ds: The #DynStr to append to.
|
||||
* \param format: The `printf` format string to use.
|
||||
*/
|
||||
void BLI_dynstr_appendf(DynStr *__restrict ds, const char *__restrict format, ...)
|
||||
ATTR_PRINTF_FORMAT(2, 3) ATTR_NONNULL(1, 2);
|
||||
void BLI_dynstr_vappendf(DynStr *__restrict ds, const char *__restrict format, va_list args)
|
||||
ATTR_PRINTF_FORMAT(2, 0) ATTR_NONNULL(1, 2);
|
||||
|
||||
/**
|
||||
* Find the length of a #DynStr.
|
||||
*
|
||||
* \param ds: The #DynStr of interest.
|
||||
* \return The length of \a ds.
|
||||
*/
|
||||
int BLI_dynstr_get_len(const DynStr *ds) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Get a #DynStr's contents as a c-string.
|
||||
* \return The c-string which must be freed using #MEM_delete.
|
||||
*
|
||||
* \param ds: The #DynStr of interest.
|
||||
* \return The contents of \a ds as a c-string.
|
||||
*/
|
||||
char *BLI_dynstr_get_cstring(const DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Get a #DynStr's contents as a c-string.
|
||||
* The \a rets argument must be allocated to be at
|
||||
* least the size of `BLI_dynstr_get_len(ds) + 1`.
|
||||
*
|
||||
* \param ds: The DynStr of interest.
|
||||
* \param rets: The string to fill.
|
||||
*/
|
||||
void BLI_dynstr_get_cstring_ex(const DynStr *__restrict ds, char *__restrict rets) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Clear the #DynStr
|
||||
*
|
||||
* \param ds: The DynStr to clear.
|
||||
*/
|
||||
void BLI_dynstr_clear(DynStr *ds) ATTR_NONNULL();
|
||||
/**
|
||||
* Free the #DynStr
|
||||
*
|
||||
* \param ds: The DynStr to free.
|
||||
*/
|
||||
void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL();
|
||||
|
||||
} // namespace blender
|
||||
51
blender-5.2.0/source/blender/blenlib/BLI_easing.h
Normal file
51
blender-5.2.0/source/blender/blenlib/BLI_easing.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* SPDX-FileCopyrightText: 2001 Robert Penner. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
float BLI_easing_back_ease_in(
|
||||
float time, float begin, float change, float duration, float overshoot);
|
||||
float BLI_easing_back_ease_out(
|
||||
float time, float begin, float change, float duration, float overshoot);
|
||||
float BLI_easing_back_ease_in_out(
|
||||
float time, float begin, float change, float duration, float overshoot);
|
||||
float BLI_easing_bounce_ease_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_bounce_ease_in(float time, float begin, float change, float duration);
|
||||
float BLI_easing_bounce_ease_in_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_circ_ease_in(float time, float begin, float change, float duration);
|
||||
float BLI_easing_circ_ease_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_circ_ease_in_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_cubic_ease_in(float time, float begin, float change, float duration);
|
||||
float BLI_easing_cubic_ease_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_cubic_ease_in_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_elastic_ease_in(
|
||||
float time, float begin, float change, float duration, float amplitude, float period);
|
||||
float BLI_easing_elastic_ease_out(
|
||||
float time, float begin, float change, float duration, float amplitude, float period);
|
||||
float BLI_easing_elastic_ease_in_out(
|
||||
float time, float begin, float change, float duration, float amplitude, float period);
|
||||
float BLI_easing_expo_ease_in(float time, float begin, float change, float duration);
|
||||
float BLI_easing_expo_ease_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_expo_ease_in_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_linear_ease(float time, float begin, float change, float duration);
|
||||
float BLI_easing_quad_ease_in(float time, float begin, float change, float duration);
|
||||
float BLI_easing_quad_ease_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_quad_ease_in_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_quart_ease_in(float time, float begin, float change, float duration);
|
||||
float BLI_easing_quart_ease_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_quart_ease_in_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_quint_ease_in(float time, float begin, float change, float duration);
|
||||
float BLI_easing_quint_ease_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_quint_ease_in_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_sine_ease_in(float time, float begin, float change, float duration);
|
||||
float BLI_easing_sine_ease_out(float time, float begin, float change, float duration);
|
||||
float BLI_easing_sine_ease_in_out(float time, float begin, float change, float duration);
|
||||
|
||||
} // namespace blender
|
||||
26
blender-5.2.0/source/blender/blenlib/BLI_endian_defines.h
Normal file
26
blender-5.2.0/source/blender/blenlib/BLI_endian_defines.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
/* NOTE: these names are historic and could use a more generic prefix.
|
||||
* This could be done as part of a bigger refactor. */
|
||||
|
||||
/** ENDIAN_ORDER: indicates what endianness the platform where the file was written had. */
|
||||
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
|
||||
# error Either __BIG_ENDIAN__ or __LITTLE_ENDIAN__ must be defined.
|
||||
#endif
|
||||
|
||||
#define L_ENDIAN 1
|
||||
#define B_ENDIAN 0
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
# define ENDIAN_ORDER B_ENDIAN
|
||||
#else
|
||||
# define ENDIAN_ORDER L_ENDIAN
|
||||
#endif
|
||||
44
blender-5.2.0/source/blender/blenlib/BLI_endian_switch.h
Normal file
44
blender-5.2.0/source/blender/blenlib/BLI_endian_switch.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Use a define instead of `#pragma once` because of `BLI_endian_switch_inline.h` */
|
||||
#ifndef __BLI_ENDIAN_SWITCH_H__
|
||||
#define __BLI_ENDIAN_SWITCH_H__
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
/* BLI_endian_switch_inline.h */
|
||||
|
||||
namespace blender {
|
||||
|
||||
BLI_INLINE void BLI_endian_switch_int16(short *val) ATTR_NONNULL(1);
|
||||
BLI_INLINE void BLI_endian_switch_uint16(unsigned short *val) ATTR_NONNULL(1);
|
||||
BLI_INLINE void BLI_endian_switch_int32(int *val) ATTR_NONNULL(1);
|
||||
BLI_INLINE void BLI_endian_switch_uint32(unsigned int *val) ATTR_NONNULL(1);
|
||||
BLI_INLINE void BLI_endian_switch_float(float *val) ATTR_NONNULL(1);
|
||||
BLI_INLINE void BLI_endian_switch_int64(int64_t *val) ATTR_NONNULL(1);
|
||||
BLI_INLINE void BLI_endian_switch_uint64(uint64_t *val) ATTR_NONNULL(1);
|
||||
BLI_INLINE void BLI_endian_switch_double(double *val) ATTR_NONNULL(1);
|
||||
|
||||
/* endian_switch.c */
|
||||
|
||||
void BLI_endian_switch_int16_array(short *val, int size) ATTR_NONNULL(1);
|
||||
void BLI_endian_switch_uint16_array(unsigned short *val, int size) ATTR_NONNULL(1);
|
||||
void BLI_endian_switch_int32_array(int *val, int size) ATTR_NONNULL(1);
|
||||
void BLI_endian_switch_uint32_array(unsigned int *val, int size) ATTR_NONNULL(1);
|
||||
void BLI_endian_switch_float_array(float *val, int size) ATTR_NONNULL(1);
|
||||
void BLI_endian_switch_int64_array(int64_t *val, int size) ATTR_NONNULL(1);
|
||||
void BLI_endian_switch_uint64_array(uint64_t *val, int size) ATTR_NONNULL(1);
|
||||
void BLI_endian_switch_double_array(double *val, int size) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#include "BLI_endian_switch_inline.h"
|
||||
|
||||
#endif /* __BLI_ENDIAN_SWITCH_H__ */
|
||||
@@ -0,0 +1,83 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compiler_compat.h"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
/* only include from header */
|
||||
#ifndef __BLI_ENDIAN_SWITCH_H__
|
||||
# error "this file isn't to be directly included"
|
||||
#endif
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* NOTE: using a temp char to switch endian is a lot slower,
|
||||
* use bit shifting instead. */
|
||||
|
||||
/* *** 16 *** */
|
||||
|
||||
BLI_INLINE void BLI_endian_switch_int16(short *val)
|
||||
{
|
||||
BLI_endian_switch_uint16(reinterpret_cast<unsigned short *>(val));
|
||||
}
|
||||
BLI_INLINE void BLI_endian_switch_uint16(unsigned short *val)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
*val = __builtin_bswap16(*val);
|
||||
#else
|
||||
unsigned short tval = *val;
|
||||
*val = (tval >> 8) | (tval << 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* *** 32 *** */
|
||||
|
||||
BLI_INLINE void BLI_endian_switch_int32(int *val)
|
||||
{
|
||||
BLI_endian_switch_uint32(reinterpret_cast<unsigned int *>(val));
|
||||
}
|
||||
BLI_INLINE void BLI_endian_switch_uint32(unsigned int *val)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
*val = __builtin_bswap32(*val);
|
||||
#else
|
||||
unsigned int tval = *val;
|
||||
*val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24));
|
||||
#endif
|
||||
}
|
||||
BLI_INLINE void BLI_endian_switch_float(float *val)
|
||||
{
|
||||
BLI_endian_switch_uint32(reinterpret_cast<unsigned int *>(val));
|
||||
}
|
||||
|
||||
/* *** 64 *** */
|
||||
|
||||
BLI_INLINE void BLI_endian_switch_int64(int64_t *val)
|
||||
{
|
||||
BLI_endian_switch_uint64(reinterpret_cast<uint64_t *>(val));
|
||||
}
|
||||
BLI_INLINE void BLI_endian_switch_uint64(uint64_t *val)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
*val = __builtin_bswap64(*val);
|
||||
#else
|
||||
uint64_t tval = *val;
|
||||
*val = ((tval >> 56)) | ((tval << 40) & 0x00ff000000000000ll) |
|
||||
((tval << 24) & 0x0000ff0000000000ll) | ((tval << 8) & 0x000000ff00000000ll) |
|
||||
((tval >> 8) & 0x00000000ff000000ll) | ((tval >> 24) & 0x0000000000ff0000ll) |
|
||||
((tval >> 40) & 0x000000000000ff00ll) | ((tval << 56));
|
||||
#endif
|
||||
}
|
||||
BLI_INLINE void BLI_endian_switch_double(double *val)
|
||||
{
|
||||
BLI_endian_switch_uint64(reinterpret_cast<uint64_t *>(val));
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
85
blender-5.2.0/source/blender/blenlib/BLI_enum_flags.hh
Normal file
85
blender-5.2.0/source/blender/blenlib/BLI_enum_flags.hh
Normal file
@@ -0,0 +1,85 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include <cstdint>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Used below, to represent a ~enum_value in a way that is enum type safe and
|
||||
* avoids UBSAN warnings about invalid enum values in (a & ~b) constructs.
|
||||
*/
|
||||
template<typename T> struct BitwiseNotEnumValue {
|
||||
uint64_t value;
|
||||
operator uint64_t() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
|
||||
/* Use for enum classes that represent bit flags.
|
||||
* Defines logical operators to combine and mask the flag values.
|
||||
*
|
||||
* Note that negation/inversion operator (~) flips all the bits, so the result can contain
|
||||
* set bits that are not part of the enum values. However that is fine in typical
|
||||
* inversion operator usage, which is often for masking out bits (`a & ~b`). */
|
||||
# define ENUM_OPERATORS(_enum_type) \
|
||||
[[maybe_unused]] [[nodiscard]] inline constexpr _enum_type operator|(_enum_type a, \
|
||||
_enum_type b) \
|
||||
{ \
|
||||
return (_enum_type)(uint64_t(a) | uint64_t(b)); \
|
||||
} \
|
||||
[[maybe_unused]] [[nodiscard]] inline constexpr _enum_type operator&(_enum_type a, \
|
||||
_enum_type b) \
|
||||
{ \
|
||||
return (_enum_type)(uint64_t(a) & uint64_t(b)); \
|
||||
} \
|
||||
[[maybe_unused]] [[nodiscard]] inline constexpr _enum_type operator&( \
|
||||
_enum_type a, ::blender::BitwiseNotEnumValue<_enum_type> b) \
|
||||
{ \
|
||||
return (_enum_type)(uint64_t(a) & uint64_t(b.value)); \
|
||||
} \
|
||||
[[maybe_unused]] [[nodiscard]] inline constexpr ::blender::BitwiseNotEnumValue<_enum_type> \
|
||||
operator~(_enum_type a) \
|
||||
{ \
|
||||
::blender::BitwiseNotEnumValue<_enum_type> result = {~uint64_t(a)}; \
|
||||
return result; \
|
||||
} \
|
||||
[[maybe_unused]] inline _enum_type &operator|=(_enum_type &a, _enum_type b) \
|
||||
{ \
|
||||
return a = (_enum_type)(uint64_t(a) | uint64_t(b)); \
|
||||
} \
|
||||
[[maybe_unused]] inline _enum_type &operator&=(_enum_type &a, _enum_type b) \
|
||||
{ \
|
||||
return a = (_enum_type)(uint64_t(a) & uint64_t(b)); \
|
||||
} \
|
||||
[[maybe_unused]] inline _enum_type &operator&=(_enum_type &a, \
|
||||
::blender::BitwiseNotEnumValue<_enum_type> b) \
|
||||
{ \
|
||||
return a = (_enum_type)(uint64_t(a) & uint64_t(b.value)); \
|
||||
} \
|
||||
[[maybe_unused]] inline _enum_type &operator^=(_enum_type &a, _enum_type b) \
|
||||
{ \
|
||||
return a = (_enum_type)(uint64_t(a) ^ uint64_t(b)); \
|
||||
} \
|
||||
[[maybe_unused]] [[nodiscard]] inline constexpr bool flag_is_set(_enum_type flags, \
|
||||
_enum_type flag_to_test) \
|
||||
{ \
|
||||
return (uint64_t(flags) & uint64_t(flag_to_test)) != 0; \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
# define ENUM_OPERATORS(_enum_type)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,112 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_TBB
|
||||
# include <tbb/enumerable_thread_specific.h>
|
||||
#else
|
||||
# include <atomic>
|
||||
# include <functional>
|
||||
|
||||
# include "BLI_map.hh"
|
||||
# include "BLI_mutex.hh"
|
||||
#endif
|
||||
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
namespace blender::threading {
|
||||
|
||||
#ifndef WITH_TBB
|
||||
namespace enumerable_thread_specific_utils {
|
||||
inline std::atomic<int> next_id = 0;
|
||||
inline thread_local int thread_id = next_id.fetch_add(1, std::memory_order_relaxed);
|
||||
} // namespace enumerable_thread_specific_utils
|
||||
#endif /* !WITH_TBB */
|
||||
|
||||
/**
|
||||
* This is mainly a wrapper for `tbb::enumerable_thread_specific`. The wrapper is needed because we
|
||||
* want to be able to build without tbb.
|
||||
*
|
||||
* More features of the tbb version can be wrapped when they are used.
|
||||
*/
|
||||
template<typename T> class EnumerableThreadSpecific : NonCopyable, NonMovable {
|
||||
#ifdef WITH_TBB
|
||||
|
||||
private:
|
||||
tbb::enumerable_thread_specific<T> values_;
|
||||
|
||||
public:
|
||||
using iterator = typename tbb::enumerable_thread_specific<T>::iterator;
|
||||
|
||||
EnumerableThreadSpecific() = default;
|
||||
|
||||
template<typename F> EnumerableThreadSpecific(F initializer) : values_(std::move(initializer)) {}
|
||||
|
||||
T &local()
|
||||
{
|
||||
return values_.local();
|
||||
}
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return values_.begin();
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return values_.end();
|
||||
}
|
||||
|
||||
#else /* WITH_TBB */
|
||||
|
||||
private:
|
||||
Mutex mutex_;
|
||||
/* Maps thread ids to their corresponding values. The values are not embedded in the map, so that
|
||||
* their addresses do not change when the map grows. */
|
||||
Map<int, std::reference_wrapper<T>> values_;
|
||||
Vector<std::unique_ptr<T>> owned_values_;
|
||||
std::function<void(void *)> initializer_;
|
||||
|
||||
public:
|
||||
using iterator = typename Map<int, std::reference_wrapper<T>>::MutableValueIterator;
|
||||
|
||||
EnumerableThreadSpecific() : initializer_([](void *buffer) { new (buffer) T(); }) {}
|
||||
|
||||
template<typename F>
|
||||
EnumerableThreadSpecific(F initializer)
|
||||
: initializer_([=](void *buffer) { new (buffer) T(initializer()); })
|
||||
{
|
||||
}
|
||||
|
||||
T &local()
|
||||
{
|
||||
const int thread_id = enumerable_thread_specific_utils::thread_id;
|
||||
std::lock_guard lock{mutex_};
|
||||
return values_.lookup_or_add_cb(thread_id, [&]() {
|
||||
T *value = static_cast<T *>(::operator new(sizeof(T)));
|
||||
initializer_(value);
|
||||
owned_values_.append(std::unique_ptr<T>{value});
|
||||
return std::reference_wrapper<T>{*value};
|
||||
});
|
||||
}
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return values_.values().begin();
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return values_.values().end();
|
||||
}
|
||||
|
||||
#endif /* WITH_TBB */
|
||||
};
|
||||
|
||||
} // namespace blender::threading
|
||||
88
blender-5.2.0/source/blender/blenlib/BLI_execution_mode.hh
Normal file
88
blender-5.2.0/source/blender/blenlib/BLI_execution_mode.hh
Normal file
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <optional>
|
||||
|
||||
namespace blender::exec_mode {
|
||||
|
||||
/** Potentially use multiple threads to execute the function. */
|
||||
struct Parallel {
|
||||
static constexpr bool is_parallel = true;
|
||||
|
||||
int grain_size(const int fallback) const
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
};
|
||||
|
||||
/** Execute the function in the current thread. */
|
||||
struct Serial {
|
||||
static constexpr bool is_parallel = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Potentially use multiple threads to execute the function, with a configurable grain size to
|
||||
* influence the parallel task size.
|
||||
*/
|
||||
struct ParallelGrainSize {
|
||||
static constexpr bool is_parallel = true;
|
||||
int grain_size_override = 1;
|
||||
|
||||
int grain_size(const int /*fallback*/) const
|
||||
{
|
||||
return this->grain_size_override;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Argument used to control whether a function should use parallel execution or not.
|
||||
*/
|
||||
template<typename T>
|
||||
concept Tag = requires {
|
||||
{
|
||||
T::is_parallel
|
||||
} -> std::convertible_to<bool>;
|
||||
|
||||
requires(!T::is_parallel || requires(const T t, int fallback) {
|
||||
{
|
||||
t.grain_size(fallback)
|
||||
} -> std::same_as<int>;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* A version of #Tag that can be used in non-template functions.
|
||||
*/
|
||||
struct Mode {
|
||||
bool is_parallel;
|
||||
std::optional<int> grain_size_override;
|
||||
constexpr Mode(Parallel /*tag*/) : is_parallel(true), grain_size_override(std::nullopt) {}
|
||||
constexpr Mode(Serial /*tag*/) : is_parallel(false), grain_size_override(std::nullopt) {}
|
||||
constexpr Mode(ParallelGrainSize tag)
|
||||
: is_parallel(true), grain_size_override(tag.grain_size_override)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr int grain_size(const int fallback) const
|
||||
{
|
||||
return this->grain_size_override.value_or(fallback);
|
||||
}
|
||||
};
|
||||
|
||||
/** Main access points to control execution mode. */
|
||||
constexpr Parallel parallel = Parallel();
|
||||
constexpr Serial serial = Serial();
|
||||
constexpr ParallelGrainSize grain_size(int grain_size)
|
||||
{
|
||||
return ParallelGrainSize{grain_size};
|
||||
}
|
||||
|
||||
} // namespace blender::exec_mode
|
||||
61
blender-5.2.0/source/blender/blenlib/BLI_expr_pylike_eval.h
Normal file
61
blender-5.2.0/source/blender/blenlib/BLI_expr_pylike_eval.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* SPDX-FileCopyrightText: 2018 Blender Authors, Alexander Gavrilov. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** Opaque structure containing pre-parsed data for evaluation. */
|
||||
struct ExprPyLike_Parsed;
|
||||
|
||||
/** Expression evaluation return code. */
|
||||
enum eExprPyLike_EvalStatus {
|
||||
EXPR_PYLIKE_SUCCESS = 0,
|
||||
/* Computation errors; result is still set, but may be NaN */
|
||||
EXPR_PYLIKE_DIV_BY_ZERO,
|
||||
EXPR_PYLIKE_MATH_ERROR,
|
||||
/* Expression dependent errors or bugs; result is 0 */
|
||||
EXPR_PYLIKE_INVALID,
|
||||
EXPR_PYLIKE_FATAL_ERROR,
|
||||
};
|
||||
|
||||
/**
|
||||
* Free the parsed data; NULL argument is ok.
|
||||
*/
|
||||
void BLI_expr_pylike_free(struct ExprPyLike_Parsed *expr);
|
||||
/**
|
||||
* Check if the parsing result is valid for evaluation.
|
||||
*/
|
||||
bool BLI_expr_pylike_is_valid(const struct ExprPyLike_Parsed *expr);
|
||||
/**
|
||||
* Check if the parsed expression always evaluates to the same value.
|
||||
*/
|
||||
bool BLI_expr_pylike_is_constant(const struct ExprPyLike_Parsed *expr);
|
||||
/**
|
||||
* Check if the parsed expression uses the parameter with the given index.
|
||||
*/
|
||||
bool BLI_expr_pylike_is_using_param(const struct ExprPyLike_Parsed *expr, int index);
|
||||
/**
|
||||
* Compile the expression and return the result.
|
||||
*
|
||||
* Parse the expression for evaluation later.
|
||||
* Returns non-NULL even on failure; use is_valid to check.
|
||||
*/
|
||||
ExprPyLike_Parsed *BLI_expr_pylike_parse(const char *expression,
|
||||
const char **param_names,
|
||||
int param_names_len);
|
||||
/**
|
||||
* Evaluate the expression with the given parameters.
|
||||
* The order and number of parameters must match the names given to parse.
|
||||
*/
|
||||
eExprPyLike_EvalStatus BLI_expr_pylike_eval(struct ExprPyLike_Parsed *expr,
|
||||
const double *param_values,
|
||||
int param_values_len,
|
||||
double *r_result);
|
||||
|
||||
} // namespace blender
|
||||
30
blender-5.2.0/source/blender/blenlib/BLI_fftw.hh
Normal file
30
blender-5.2.0/source/blender/blenlib/BLI_fftw.hh
Normal file
@@ -0,0 +1,30 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender::fftw {
|
||||
|
||||
/**
|
||||
* FFTW's real to complex and complex to real transforms are more efficient when their input has a
|
||||
* specific size. This function finds the most optimal size that is more than or equal the given
|
||||
* size. The input data can then be zero padded to the optimal size for better performance. See
|
||||
* Section 4.3.3 Real-data DFTs in the FFTW manual for more information.
|
||||
*/
|
||||
int optimal_size_for_real_transform(int size);
|
||||
int2 optimal_size_for_real_transform(int2 size);
|
||||
|
||||
/**
|
||||
* Initialize the float variant of FFTW. This essentially setup the multi-threading hooks to enable
|
||||
* multi-threading using TBB's parallel_for and makes the FFTW planner thread safe.
|
||||
*/
|
||||
void initialize_float();
|
||||
|
||||
} // namespace blender::fftw
|
||||
460
blender-5.2.0/source/blender/blenlib/BLI_fileops.h
Normal file
460
blender-5.2.0/source/blender/blenlib/BLI_fileops.h
Normal file
@@ -0,0 +1,460 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief File and directory operations.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* for size_t (needed on windows) */
|
||||
#include <stddef.h>
|
||||
|
||||
#include <limits.h> /* for PATH_MAX */
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_fileops_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#ifndef PATH_MAX
|
||||
# define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Common
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Returns true if the path (file or directory) exists.
|
||||
*/
|
||||
bool BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Returns the st_mode from stat-ing the specified path name, or 0 if stat fails
|
||||
* (most likely doesn't exist or no access).
|
||||
*/
|
||||
int BLI_file_stat_mode(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* \return 0 on success.
|
||||
*/
|
||||
int BLI_copy(const char *path_src, const char *path_dst) ATTR_NONNULL();
|
||||
/**
|
||||
* When `path_src` points to a directory, moves all its contents into `path_dst`,
|
||||
* else rename `path_src` itself to `path_dst`.
|
||||
* \return 0 on success.
|
||||
*/
|
||||
int BLI_path_move(const char *path_src, const char *path_dst) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Rename a file or directory, unless `to` already exists.
|
||||
*
|
||||
* \note This matches Windows `rename` logic, _not_ Unix one. It does not allow to replace an
|
||||
* existing target. Use #BLI_rename_overwrite instead if existing file should be replaced.
|
||||
*
|
||||
* \param from: The path to rename from (return failure if it does not exist).
|
||||
* \param to: The destination path.
|
||||
* \return zero on success (matching 'rename' behavior).
|
||||
*/
|
||||
int BLI_rename(const char *from, const char *to) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Rename a file or directory, replacing target `to` path if it exists.
|
||||
*
|
||||
* \note This matches Unix `rename` logic. It does allow to replace an existing target. Use
|
||||
* #BLI_rename instead if existing file should never be replaced. However, if `to` is an existing,
|
||||
* non-empty directory, the operation will fail.
|
||||
*
|
||||
* \note There is still no feature-parity between behaviors on Windows and Unix, in case the target
|
||||
* `to` exists and is opened by some process in the system:
|
||||
* - On Unix, it will typically succeed
|
||||
* (see https://man7.org/linux/man-pages/man2/rename.2.html for details).
|
||||
* - On Windows, it will always fail
|
||||
* (see https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-movefileexw for
|
||||
* details).
|
||||
*
|
||||
* \warning Due to internal limitation/implementation, on Windows, in case paths point to
|
||||
* directories, it's up to the caller to ensure that `from` and `to` are not the same directory.
|
||||
* Since `to` is being deleted to make room for `from`, this will result in `from` being deleted as
|
||||
* well.
|
||||
*
|
||||
* See #BLI_path_move to move directories.
|
||||
*
|
||||
* \param from: The path to rename from (return failure if it does not exist).
|
||||
* \param to: The destination path.
|
||||
* This will be deleted if it already exists, unless it's a directory which will fail.
|
||||
* \return zero on success (matching 'rename' behavior).
|
||||
*/
|
||||
int BLI_rename_overwrite(const char *from, const char *to) ATTR_NONNULL();
|
||||
/**
|
||||
* Deletes the specified file or directory.
|
||||
*
|
||||
* \param dir: Delete an empty directory instead of a file.
|
||||
* The value is ignored when `recursive` is true but should true to make the intention clear.
|
||||
* If the directory is not empty, delete fails.
|
||||
* \param recursive: Recursively delete files including `path` which may be a directory of a file.
|
||||
*
|
||||
* \note Symbolic-Links for (UNIX) behave as follows:
|
||||
* - Never followed, treated as regular files.
|
||||
* - Links are removed, not the files/directories they references.
|
||||
* - When `path` itself links to another directory,
|
||||
* deleting `path` behaves as if a regular file is being deleted.
|
||||
* - If `dir` is true and `path` is a link, delete fails.
|
||||
*
|
||||
* \return zero on success (matching 'remove' behavior).
|
||||
*/
|
||||
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL();
|
||||
/**
|
||||
* Soft deletes the specified file or directory (depending on dir) by moving the files to the
|
||||
* recycling bin, optionally doing recursive delete of directory contents.
|
||||
*
|
||||
* \return zero on success (matching 'remove' behavior).
|
||||
*/
|
||||
int BLI_delete_soft(const char *filepath, const char **r_error_message) ATTR_NONNULL();
|
||||
#if 0 /* Unused */
|
||||
int BLI_create_symlink(const char *path, const char *path_dst) ATTR_NONNULL();
|
||||
#endif
|
||||
|
||||
/* Keep in sync with the definition of struct `direntry` in `BLI_fileops_types.h`. */
|
||||
#ifdef WIN32
|
||||
# if defined(_MSC_VER)
|
||||
typedef struct _stat64 BLI_stat_t;
|
||||
# else
|
||||
typedef struct _stat BLI_stat_t;
|
||||
# endif
|
||||
#else
|
||||
typedef struct stat BLI_stat_t;
|
||||
#endif
|
||||
|
||||
int BLI_fstat(int fd, BLI_stat_t *buffer) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
int BLI_stat(const char *path, BLI_stat_t *buffer) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
int64_t BLI_ftell(FILE *stream) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
int BLI_fseek(FILE *stream, int64_t offset, int whence);
|
||||
int64_t BLI_lseek(int fd, int64_t offset, int whence);
|
||||
|
||||
#ifdef WIN32
|
||||
int BLI_wstat(const wchar_t *path, BLI_stat_t *buffer);
|
||||
#endif
|
||||
|
||||
enum eFileAttributes {
|
||||
FILE_ATTR_READONLY = 1 << 0, /* Read-only or Immutable. */
|
||||
FILE_ATTR_HIDDEN = 1 << 1, /* Hidden or invisible. */
|
||||
FILE_ATTR_SYSTEM = 1 << 2, /* Used by the Operating System. */
|
||||
FILE_ATTR_ARCHIVE = 1 << 3, /* Marked as archived. */
|
||||
FILE_ATTR_COMPRESSED = 1 << 4, /* Compressed. */
|
||||
FILE_ATTR_ENCRYPTED = 1 << 5, /* Encrypted. */
|
||||
FILE_ATTR_RESTRICTED = 1 << 6, /* Protected by OS. */
|
||||
FILE_ATTR_TEMPORARY = 1 << 7, /* Used for temporary storage. */
|
||||
FILE_ATTR_SPARSE_FILE = 1 << 8, /* Sparse File. */
|
||||
FILE_ATTR_OFFLINE = 1 << 9, /* Contents available after a short delay. */
|
||||
FILE_ATTR_ALIAS = 1 << 10, /* Mac Alias or Windows LNK. File-based redirection. */
|
||||
FILE_ATTR_REPARSE_POINT = 1 << 11, /* File has associated re-parse point. */
|
||||
FILE_ATTR_SYMLINK = 1 << 12, /* Reference to another file. */
|
||||
FILE_ATTR_JUNCTION_POINT = 1 << 13, /* Folder Symbolic-link. */
|
||||
FILE_ATTR_MOUNT_POINT = 1 << 14, /* Volume mounted as a folder. */
|
||||
FILE_ATTR_HARDLINK = 1 << 15, /* Duplicated directory entry. */
|
||||
};
|
||||
ENUM_OPERATORS(eFileAttributes);
|
||||
|
||||
#define FILE_ATTR_ANY_LINK \
|
||||
(FILE_ATTR_ALIAS | FILE_ATTR_REPARSE_POINT | FILE_ATTR_SYMLINK | FILE_ATTR_JUNCTION_POINT | \
|
||||
FILE_ATTR_MOUNT_POINT | FILE_ATTR_HARDLINK)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name External File Operations
|
||||
* \{ */
|
||||
|
||||
enum FileExternalOperation {
|
||||
FILE_EXTERNAL_OPERATION_OPEN = 1,
|
||||
FILE_EXTERNAL_OPERATION_FOLDER_OPEN,
|
||||
/* Following are Windows-only: */
|
||||
FILE_EXTERNAL_OPERATION_EDIT,
|
||||
FILE_EXTERNAL_OPERATION_NEW,
|
||||
FILE_EXTERNAL_OPERATION_FIND,
|
||||
FILE_EXTERNAL_OPERATION_SHOW,
|
||||
FILE_EXTERNAL_OPERATION_PLAY,
|
||||
FILE_EXTERNAL_OPERATION_BROWSE,
|
||||
FILE_EXTERNAL_OPERATION_PREVIEW,
|
||||
FILE_EXTERNAL_OPERATION_PRINT,
|
||||
FILE_EXTERNAL_OPERATION_INSTALL,
|
||||
FILE_EXTERNAL_OPERATION_RUNAS,
|
||||
FILE_EXTERNAL_OPERATION_PROPERTIES,
|
||||
FILE_EXTERNAL_OPERATION_FOLDER_FIND,
|
||||
FILE_EXTERNAL_OPERATION_FOLDER_CMD,
|
||||
};
|
||||
|
||||
bool BLI_file_external_operation_supported(const char *filepath, FileExternalOperation operation);
|
||||
bool BLI_file_external_operation_execute(const char *filepath, FileExternalOperation operation);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Directories
|
||||
* \{ */
|
||||
|
||||
struct direntry;
|
||||
|
||||
/**
|
||||
* Does the specified path point to a directory?
|
||||
* \note Would be better in `fileops.cc` except that it needs `stat.h` so add here.
|
||||
*/
|
||||
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Does the specified path point to a non-directory?
|
||||
*/
|
||||
bool BLI_is_file(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \return true on success (i.e. given path now exists on FS), false otherwise.
|
||||
*/
|
||||
bool BLI_dir_create_recursive(const char *dirname) ATTR_NONNULL();
|
||||
/**
|
||||
* Returns the number of free bytes on the volume containing the specified path.
|
||||
*
|
||||
* \note Not actually used anywhere.
|
||||
*/
|
||||
double BLI_dir_free_space(const char *dir) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Copies the current working directory into *dir (max size maxncpy), and
|
||||
* returns a pointer to same.
|
||||
*
|
||||
* \note can return NULL when the size is not big enough
|
||||
*/
|
||||
char *BLI_current_working_dir(char *dir, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Get the user's home directory, i.e.
|
||||
* - Unix: `$HOME` or #passwd::pw_dir.
|
||||
* - Windows: `%userprofile%`
|
||||
*
|
||||
* \return The home directory or null when it cannot be accessed.
|
||||
*
|
||||
* \note By convention, failure to access home means any derived directories fail as well
|
||||
* instead of attempting to create a fallback such as `/`, `/tmp`, `C:\` ... etc.
|
||||
* Although there may be rare cases where a fallback is appropriate.
|
||||
*/
|
||||
const char *BLI_dir_home();
|
||||
|
||||
eFileAttributes BLI_file_attributes(const char *path);
|
||||
/**
|
||||
* Changes the current working directory to the provided path.
|
||||
*
|
||||
* Usage of this function is strongly discouraged as it is not thread safe. It will likely cause
|
||||
* issues if there is an operation on another thread that does not expect the current working
|
||||
* directory to change. This has been added to support USDZ export, which has a problematic
|
||||
* "feature" described in this issue #99807. It will be removed if it is possible to resolve
|
||||
* that issue upstream in the USD library.
|
||||
*
|
||||
* \return true on success, false otherwise.
|
||||
*/
|
||||
bool BLI_change_working_dir(const char *dir);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name File-List
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Scans the contents of the directory named `dirname`, and allocates and fills in an
|
||||
* array of entries describing them in `r_filelist`.
|
||||
*
|
||||
* \return The length of `r_filelist` array.
|
||||
*/
|
||||
unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_filelist);
|
||||
/**
|
||||
* Deep-duplicate of a single direntry.
|
||||
*/
|
||||
void BLI_filelist_entry_duplicate(struct direntry *dst, const struct direntry *src);
|
||||
/**
|
||||
* Deep-duplicate of a #direntry array including the array itself.
|
||||
*/
|
||||
void BLI_filelist_duplicate(struct direntry **dest_filelist,
|
||||
struct direntry *const src_filelist,
|
||||
unsigned int nrentries);
|
||||
/**
|
||||
* Frees storage for a single direntry, not the direntry itself.
|
||||
*/
|
||||
void BLI_filelist_entry_free(struct direntry *entry);
|
||||
/**
|
||||
* Frees storage for an array of #direntry, including the array itself.
|
||||
*/
|
||||
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries);
|
||||
|
||||
/**
|
||||
* Convert given entry's size into human-readable strings.
|
||||
*/
|
||||
void BLI_filelist_entry_size_to_string(const struct stat *st,
|
||||
uint64_t st_size_fallback,
|
||||
bool compact,
|
||||
char r_size[FILELIST_DIRENTRY_SIZE_LEN]);
|
||||
/**
|
||||
* Convert given entry's modes into human-readable strings.
|
||||
*/
|
||||
void BLI_filelist_entry_mode_to_string(const struct stat *st,
|
||||
bool compact,
|
||||
char r_mode1[FILELIST_DIRENTRY_MODE_LEN],
|
||||
char r_mode2[FILELIST_DIRENTRY_MODE_LEN],
|
||||
char r_mode3[FILELIST_DIRENTRY_MODE_LEN]);
|
||||
/**
|
||||
* Convert given entry's owner into human-readable strings.
|
||||
*/
|
||||
void BLI_filelist_entry_owner_to_string(const struct stat *st,
|
||||
bool compact,
|
||||
char r_owner[FILELIST_DIRENTRY_OWNER_LEN]);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Files
|
||||
* \{ */
|
||||
|
||||
FILE *BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
void *BLI_gzopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
int BLI_access(const char *filepath, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* A version of `read` with the following differences:
|
||||
* - continues reading until failure or the requested size is met.
|
||||
* - Reads `size_t` bytes instead of `int` on WIN32.
|
||||
* \return the number of bytes read.
|
||||
*/
|
||||
int64_t BLI_read(int fd, void *buf, size_t nbytes);
|
||||
|
||||
/**
|
||||
* Returns true if the file with the specified name can be written.
|
||||
* This implementation uses access(2), which makes the check according
|
||||
* to the real UID and GID of the process, not its effective UID and GID.
|
||||
* This shouldn't matter for Blender, which is not going to run privileged anyway.
|
||||
*/
|
||||
bool BLI_file_is_writable(const char *filepath) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Creates the file with nothing in it, or updates its last-modified date if it already exists.
|
||||
* Returns true if successful (like the unix touch command).
|
||||
*/
|
||||
bool BLI_file_touch(const char *filepath) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Ensures that the parent directory of `filepath` exists.
|
||||
*
|
||||
* \return true on success (i.e. given path now exists on file-system), false otherwise.
|
||||
*/
|
||||
bool BLI_file_ensure_parent_dir_exists(const char *filepath) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Return alias/shortcut file target.
|
||||
* \param filepath: The source of the alias.
|
||||
* \param r_targetpath: Buffer for the target path an alias points to.
|
||||
*
|
||||
* \return true when an alias was found and set.
|
||||
*
|
||||
* \note This is only used on APPLE/WIN32.
|
||||
*/
|
||||
bool BLI_file_alias_target(const char *filepath,
|
||||
char r_targetpath[/*FILE_MAXDIR*/ 768]) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
bool BLI_file_magic_is_gzip(const char header[4]);
|
||||
|
||||
size_t BLI_file_zstd_from_mem_at_pos(void *buf,
|
||||
size_t len,
|
||||
FILE *file,
|
||||
size_t file_offset,
|
||||
int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset)
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
bool BLI_file_magic_is_zstd(const char header[4]);
|
||||
|
||||
/**
|
||||
* Returns the file size of an opened file descriptor or `size_t(-1)` on failure.
|
||||
*/
|
||||
size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Returns the size of a file or `size_t(-1)` on failure..
|
||||
*/
|
||||
size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Compare if one was last modified before the other.
|
||||
*
|
||||
* \return true when is `file1` older than `file2`.
|
||||
*/
|
||||
bool BLI_file_older(const char *file1, const char *file2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Reads the contents of a text file.
|
||||
*
|
||||
* \return the lines in a linked list (an empty list when file reading fails).
|
||||
*/
|
||||
struct LinkNode *BLI_file_read_as_lines(const char *filepath) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Read the contents of `fp`, returning the result as a buffer or null when it can't be read.
|
||||
*
|
||||
* \param r_size: The size of the file contents read into the buffer (excluding `pad_bytes`).
|
||||
*/
|
||||
void *BLI_file_read_data_as_mem_from_handle(FILE *fp,
|
||||
bool read_size_exact,
|
||||
size_t pad_bytes,
|
||||
size_t *r_size);
|
||||
|
||||
char *BLI_file_read_text_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size);
|
||||
/**
|
||||
* Return the text file data with:
|
||||
*
|
||||
* - Newlines replaced with '\0'.
|
||||
* - Optionally trim white-space, replacing trailing <space> & <tab> with '\0'.
|
||||
*
|
||||
* This is an alternative to using #BLI_file_read_as_lines,
|
||||
* allowing us to loop over lines without converting it into a linked list
|
||||
* with individual allocations.
|
||||
*
|
||||
* \param trim_trailing_space: Replace trailing spaces & tabs with nil.
|
||||
* This arguments prevents the caller from counting blank lines (if that's important).
|
||||
* \param pad_bytes: When this is non-zero, the first byte is set to nil,
|
||||
* to simplify parsing the file.
|
||||
* It's recommended to pass in 1, so all text is nil terminated.
|
||||
* \param r_size: The size of the file contents read into the buffer (excluding `pad_bytes`).
|
||||
*
|
||||
* Example looping over lines:
|
||||
*
|
||||
* \code{.c}
|
||||
* size_t data_len;
|
||||
* char *data = BLI_file_read_text_as_mem_with_newline_as_nil(filepath, true, 1, &data_len);
|
||||
* char *data_end = data + data_len;
|
||||
* for (char *line = data; line != data_end; line = strlen(line) + 1) {
|
||||
* printf("line='%s'\n", line);
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
char *BLI_file_read_text_as_mem_with_newline_as_nil(const char *filepath,
|
||||
bool trim_trailing_space,
|
||||
size_t pad_bytes,
|
||||
size_t *r_size);
|
||||
void *BLI_file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size);
|
||||
/**
|
||||
* Frees memory from a previous call to #BLI_file_read_as_lines.
|
||||
*/
|
||||
void BLI_file_free_lines(struct LinkNode *lines);
|
||||
|
||||
/* This weirdo pops up in two places. */
|
||||
#if !defined(WIN32)
|
||||
# ifndef O_BINARY
|
||||
# define O_BINARY 0
|
||||
# endif
|
||||
#else
|
||||
void BLI_get_short_name(char short_name[256], const char *filepath);
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
36
blender-5.2.0/source/blender/blenlib/BLI_fileops.hh
Normal file
36
blender-5.2.0/source/blender/blenlib/BLI_fileops.hh
Normal file
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief File and directory operations.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_fileops.h" // IWYU pragma: export
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* std::fstream subclass that handles UTF16 encoding on Windows.
|
||||
*
|
||||
* For documentation, see https://en.cppreference.com/w/cpp/io/basic_fstream
|
||||
*/
|
||||
class fstream : public std::fstream {
|
||||
public:
|
||||
fstream() = default;
|
||||
explicit fstream(const char *filepath,
|
||||
std::ios_base::openmode mode = ios_base::in | ios_base::out);
|
||||
explicit fstream(const std::string &filepath,
|
||||
std::ios_base::openmode mode = ios_base::in | ios_base::out);
|
||||
|
||||
void open(StringRefNull filepath, ios_base::openmode mode = ios_base::in | ios_base::out);
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
46
blender-5.2.0/source/blender/blenlib/BLI_fileops_types.h
Normal file
46
blender-5.2.0/source/blender/blenlib/BLI_fileops_types.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief Some types for dealing with directories.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
#if defined(WIN32)
|
||||
typedef unsigned int mode_t;
|
||||
#endif
|
||||
|
||||
#define FILELIST_DIRENTRY_SIZE_LEN 16
|
||||
#define FILELIST_DIRENTRY_MODE_LEN 4
|
||||
#define FILELIST_DIRENTRY_OWNER_LEN 16
|
||||
#define FILELIST_DIRENTRY_TIME_LEN 8
|
||||
#define FILELIST_DIRENTRY_DATE_LEN 16
|
||||
|
||||
struct direntry {
|
||||
mode_t type;
|
||||
const char *relname;
|
||||
const char *path;
|
||||
#ifdef WIN32 /* keep in sync with the definition of BLI_stat_t in BLI_fileops.h */
|
||||
# if defined(_MSC_VER)
|
||||
struct _stat64 s;
|
||||
# else
|
||||
struct _stat s;
|
||||
# endif
|
||||
#else
|
||||
struct stat s;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct dirlink {
|
||||
struct dirlink *next, *prev;
|
||||
char *name;
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
63
blender-5.2.0/source/blender/blenlib/BLI_filereader.h
Normal file
63
blender-5.2.0/source/blender/blenlib/BLI_filereader.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief Wrapper for reading from various sources (e.g. raw files, compressed files, memory...).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WIN32
|
||||
# include "BLI_winstuff.h"
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#if defined(_MSC_VER) || defined(__APPLE__) || defined(__HAIKU__) || defined(__NetBSD__) || \
|
||||
defined(__OpenBSD__)
|
||||
typedef int64_t off64_t;
|
||||
#endif
|
||||
|
||||
struct FileReader;
|
||||
|
||||
typedef int64_t (*FileReaderReadFn)(struct FileReader *reader, void *buffer, size_t size);
|
||||
typedef off64_t (*FileReaderSeekFn)(struct FileReader *reader, off64_t offset, int whence);
|
||||
typedef void (*FileReaderCloseFn)(struct FileReader *reader);
|
||||
|
||||
/** General structure for all #FileReaders, implementations add custom fields at the end. */
|
||||
struct FileReader {
|
||||
FileReaderReadFn read;
|
||||
FileReaderSeekFn seek;
|
||||
FileReaderCloseFn close;
|
||||
|
||||
off64_t offset;
|
||||
};
|
||||
|
||||
/* Functions for opening the various types of FileReader.
|
||||
* They either succeed and return a valid FileReader, or fail and return NULL.
|
||||
*
|
||||
* If a FileReader is created, it has to be cleaned up and freed by calling its close()
|
||||
* function unless another FileReader has taken ownership - for example, `Zstd` & `Gzip`
|
||||
* take over the base FileReader and will clean it up when their clean() is called.
|
||||
*/
|
||||
|
||||
/** Create #FileReader from raw file descriptor. */
|
||||
FileReader *BLI_filereader_new_file(int filedes) ATTR_WARN_UNUSED_RESULT;
|
||||
/** Create #FileReader from raw file descriptor using memory-mapped IO. */
|
||||
FileReader *BLI_filereader_new_mmap(int filedes) ATTR_WARN_UNUSED_RESULT;
|
||||
/** Create #FileReader from a region of memory. */
|
||||
FileReader *BLI_filereader_new_memory(const void *data, size_t len) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/** Create #FileReader from applying `Zstd` decompression on an underlying file. */
|
||||
FileReader *BLI_filereader_new_zstd(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/** Create #FileReader from applying `Gzip` decompression on an underlying file. */
|
||||
FileReader *BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
} // namespace blender
|
||||
28
blender-5.2.0/source/blender/blenlib/BLI_fixed_string.hh
Normal file
28
blender-5.2.0/source/blender/blenlib/BLI_fixed_string.hh
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* This can represent a string at a compile time in a way that can be used as template parameter.
|
||||
*
|
||||
* While std::string can be used at compile time, it is not a "structural type" and therefore
|
||||
* cannot be used as template parameter.
|
||||
*/
|
||||
template<size_t N> struct FixedString {
|
||||
char data[N];
|
||||
|
||||
constexpr FixedString(const char (&str)[N])
|
||||
{
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
data[i] = str[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
573
blender-5.2.0/source/blender/blenlib/BLI_fixed_width_int.hh
Normal file
573
blender-5.2.0/source/blender/blenlib/BLI_fixed_width_int.hh
Normal file
@@ -0,0 +1,573 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_unroll.hh"
|
||||
|
||||
namespace blender::fixed_width_int {
|
||||
|
||||
/**
|
||||
* An unsigned fixed width integer.
|
||||
*
|
||||
* For some algorithms, the largest cross platform integer type (`uint64_t`) is not large enough.
|
||||
* Then one has the choice to use some big-integer implementation like the one from GMP or one can
|
||||
* use fixed-width-integers as implemented here.
|
||||
*
|
||||
* Internally, this type combines multiple smaller integers into a bigger integer.
|
||||
*/
|
||||
template<typename T, int S> struct UIntF {
|
||||
static_assert(std::is_unsigned_v<T>);
|
||||
static_assert(S >= 1);
|
||||
|
||||
/**
|
||||
* Array of smaller integers that make up the bigger integer. The first element is the least
|
||||
* significant digit.
|
||||
*/
|
||||
std::array<T, S> v;
|
||||
|
||||
/** Allow default construction. Note that the value is not initialized in this case. */
|
||||
UIntF() = default;
|
||||
|
||||
/** Construct from a specific integer. */
|
||||
explicit UIntF(uint64_t value);
|
||||
|
||||
/** Construct from a string. */
|
||||
explicit UIntF(StringRefNull str, int base = 10);
|
||||
|
||||
/** Convert to a normal integer. Note that this may lose digits. */
|
||||
explicit operator uint64_t() const;
|
||||
|
||||
/** Convert to floating point. This may lose precision. */
|
||||
explicit operator double() const;
|
||||
explicit operator float() const;
|
||||
|
||||
/* See `BLI_fixed_width_int_str.hh`. */
|
||||
#ifdef WITH_GMP
|
||||
/** Update value based on the integer encoded in the string. */
|
||||
void set_from_str(StringRefNull str, int base = 10);
|
||||
|
||||
/** Convert to a string. */
|
||||
std::string to_string(int base = 10) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* A signed fixed width integer. It's mostly the same as #UIntF, but signed.
|
||||
*/
|
||||
template<typename T, int S> struct IntF {
|
||||
static_assert(std::is_unsigned_v<T>);
|
||||
static_assert(S >= 1);
|
||||
|
||||
/**
|
||||
* Array of smaller integers that make up the bigger integer. The first element is the least
|
||||
* significant digit.
|
||||
*/
|
||||
std::array<T, S> v;
|
||||
|
||||
/** Allow default construction. Note that the value is not initialized in this case. */
|
||||
IntF() = default;
|
||||
|
||||
/** Construct from a specific integer. */
|
||||
explicit IntF(int64_t value);
|
||||
|
||||
/** Support casting unsigned to signed fixed-width-int. */
|
||||
explicit IntF(const UIntF<T, S> &value);
|
||||
|
||||
/** Construct from a string. */
|
||||
explicit IntF(StringRefNull str, int base = 10);
|
||||
|
||||
/** Convert to a normal integer. Note that this may lose digits. */
|
||||
explicit operator int64_t() const;
|
||||
|
||||
/** Convert to floating point. This may lose precision. */
|
||||
explicit operator double() const;
|
||||
explicit operator float() const;
|
||||
|
||||
/** Support casting from signed to unsigned fixed-width-int. */
|
||||
explicit operator UIntF<T, S>() const;
|
||||
|
||||
/* See `BLI_fixed_width_int_str.hh`. */
|
||||
#ifdef WITH_GMP
|
||||
/** Update value based on the integer encoded in the string. */
|
||||
void set_from_str(const StringRefNull str, const int base = 10);
|
||||
|
||||
/** Convert to a string. */
|
||||
std::string to_string(int base = 10) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename T> struct DoubleUIntType {
|
||||
using type = void;
|
||||
};
|
||||
template<> struct DoubleUIntType<uint8_t> {
|
||||
using type = uint16_t;
|
||||
};
|
||||
template<> struct DoubleUIntType<uint16_t> {
|
||||
using type = uint32_t;
|
||||
};
|
||||
template<> struct DoubleUIntType<uint32_t> {
|
||||
using type = uint64_t;
|
||||
};
|
||||
#ifndef _MSC_VER
|
||||
template<> struct DoubleUIntType<uint64_t> {
|
||||
using type = __uint128_t;
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Maps unsigned integer types to a type that's twice the size. E.g. uint16_t to uint32_t. */
|
||||
template<typename T> using double_uint_type = typename DoubleUIntType<T>::type;
|
||||
|
||||
using UInt64_8 = UIntF<uint8_t, 8>;
|
||||
using UInt64_16 = UIntF<uint16_t, 4>;
|
||||
using UInt64_32 = UIntF<uint32_t, 2>;
|
||||
|
||||
using Int64_8 = IntF<uint8_t, 8>;
|
||||
using Int64_16 = IntF<uint16_t, 4>;
|
||||
using Int64_32 = IntF<uint32_t, 2>;
|
||||
|
||||
using UInt128_8 = UIntF<uint8_t, 16>;
|
||||
using UInt128_16 = UIntF<uint16_t, 8>;
|
||||
using UInt128_32 = UIntF<uint32_t, 4>;
|
||||
using UInt128_64 = UIntF<uint64_t, 2>;
|
||||
|
||||
using UInt256_8 = UIntF<uint8_t, 32>;
|
||||
using UInt256_16 = UIntF<uint16_t, 16>;
|
||||
using UInt256_32 = UIntF<uint32_t, 8>;
|
||||
using UInt256_64 = UIntF<uint64_t, 4>;
|
||||
|
||||
using Int128_8 = IntF<uint8_t, 16>;
|
||||
using Int128_16 = IntF<uint16_t, 8>;
|
||||
using Int128_32 = IntF<uint32_t, 4>;
|
||||
using Int128_64 = IntF<uint64_t, 2>;
|
||||
|
||||
using Int256_8 = IntF<uint8_t, 32>;
|
||||
using Int256_16 = IntF<uint16_t, 16>;
|
||||
using Int256_32 = IntF<uint32_t, 8>;
|
||||
using Int256_64 = IntF<uint64_t, 4>;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
using UInt128 = UInt128_32;
|
||||
using UInt256 = UInt256_32;
|
||||
using Int128 = Int128_32;
|
||||
using Int256 = Int256_32;
|
||||
#else
|
||||
using UInt128 = UInt128_64;
|
||||
using UInt256 = UInt256_64;
|
||||
using Int128 = Int128_64;
|
||||
using Int256 = Int256_64;
|
||||
#endif
|
||||
|
||||
template<typename T, int S> inline UIntF<T, S>::UIntF(const uint64_t value)
|
||||
{
|
||||
constexpr int Count = std::min(S, int(sizeof(decltype(value)) / sizeof(T)));
|
||||
constexpr int BitsPerT = 8 * sizeof(T);
|
||||
|
||||
for (int i = 0; i < Count; i++) {
|
||||
this->v[i] = T(value >> (BitsPerT * i));
|
||||
}
|
||||
for (int i = Count; i < S; i++) {
|
||||
this->v[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, int S> inline IntF<T, S>::IntF(const int64_t value)
|
||||
{
|
||||
constexpr int Count = std::min(S, int(sizeof(decltype(value)) / sizeof(T)));
|
||||
constexpr int BitsPerT = 8 * sizeof(T);
|
||||
|
||||
for (int i = 0; i < Count; i++) {
|
||||
this->v[i] = T(value >> (BitsPerT * i));
|
||||
}
|
||||
const T sign_extend_fill = value < 0 ? T(-1) : T(0);
|
||||
for (int i = Count; i < S; i++) {
|
||||
this->v[i] = sign_extend_fill;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, int S> inline IntF<T, S>::IntF(const UIntF<T, S> &value) : v(value.v) {}
|
||||
|
||||
#ifdef WITH_GMP
|
||||
template<typename T, int S> UIntF<T, S>::UIntF(const StringRefNull str, const int base)
|
||||
{
|
||||
this->set_from_str(str, base);
|
||||
}
|
||||
|
||||
template<typename T, int S> IntF<T, S>::IntF(const StringRefNull str, const int base)
|
||||
{
|
||||
this->set_from_str(str, base);
|
||||
}
|
||||
#endif /* WITH_GMP */
|
||||
|
||||
template<typename T, int S> inline UIntF<T, S>::operator uint64_t() const
|
||||
{
|
||||
constexpr int Count = std::min(S, int(sizeof(uint64_t) / sizeof(T)));
|
||||
constexpr int BitsPerT = 8 * sizeof(T);
|
||||
|
||||
uint64_t result = 0;
|
||||
for (int i = 0; i < Count; i++) {
|
||||
result |= uint64_t(this->v[i]) << (BitsPerT * i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int S> inline UIntF<T, S>::operator double() const
|
||||
{
|
||||
double result = double(this->v[0]);
|
||||
for (int i = 1; i < S; i++) {
|
||||
const T a = this->v[i];
|
||||
if (a == 0) {
|
||||
continue;
|
||||
}
|
||||
result += ldexp(a, 8 * sizeof(T) * i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int S> inline UIntF<T, S>::operator float() const
|
||||
{
|
||||
return float(double(*this));
|
||||
}
|
||||
|
||||
template<typename T, int S> inline IntF<T, S>::operator int64_t() const
|
||||
{
|
||||
return int64_t(uint64_t(UIntF<T, S>(*this)));
|
||||
}
|
||||
|
||||
template<typename T, int S> inline IntF<T, S>::operator double() const
|
||||
{
|
||||
if (is_negative(*this)) {
|
||||
return -double(-*this);
|
||||
}
|
||||
double result = double(this->v[0]);
|
||||
for (int i = 1; i < S; i++) {
|
||||
const T a = this->v[i];
|
||||
if (a == 0) {
|
||||
continue;
|
||||
}
|
||||
result += ldexp(a, 8 * sizeof(T) * i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int S> inline IntF<T, S>::operator float() const
|
||||
{
|
||||
return float(double(*this));
|
||||
}
|
||||
|
||||
template<typename T, int S> inline IntF<T, S>::operator UIntF<T, S>() const
|
||||
{
|
||||
UIntF<T, S> result;
|
||||
result.v = this->v;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two fixed-width-integer together using the standard addition with carry algorithm taught
|
||||
* in schools. The main difference is that the digits here are not 0 to 9, but 0 to max(T).
|
||||
*
|
||||
* Due to the design of two's-complement numbers, this works for signed and unsigned
|
||||
* fixed-width-integer. The overflow behavior is wrap-around.
|
||||
*
|
||||
* \tparam T: Type for individual digits.
|
||||
* \tparam T2: Integer type that is twice as large as T.
|
||||
* \tparam S: Number of digits of type T in each fixed-width-integer.
|
||||
*/
|
||||
template<typename T, typename T2, int S>
|
||||
inline void generic_add(T *__restrict dst, const T *a, const T *b)
|
||||
{
|
||||
constexpr int shift = 8 * sizeof(T);
|
||||
T2 carry = 0;
|
||||
unroll<S>([&](auto i) {
|
||||
const T2 ai = T2(a[i]);
|
||||
const T2 bi = T2(b[i]);
|
||||
const T2 ri = ai + bi + carry;
|
||||
dst[i] = T(ri);
|
||||
carry = ri >> shift;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to #generic_add, but for subtraction.
|
||||
*/
|
||||
template<typename T, typename T2, int S>
|
||||
inline void generic_sub(T *__restrict dst, const T *a, const T *b)
|
||||
{
|
||||
T2 carry = 0;
|
||||
unroll<S>([&](auto i) {
|
||||
const T2 ai = T2(a[i]);
|
||||
const T2 bi = T2(b[i]);
|
||||
const T2 ri = ai - bi - carry;
|
||||
dst[i] = T(ri);
|
||||
carry = ri > ai;
|
||||
});
|
||||
}
|
||||
|
||||
/** Similar to #generic_add, but for unsigned multiplication. */
|
||||
template<typename T, typename T2, int S>
|
||||
inline void generic_unsigned_mul(T *__restrict dst, const T *a, const T *b)
|
||||
{
|
||||
constexpr int shift = 8 * sizeof(T);
|
||||
|
||||
T2 r[S] = {};
|
||||
|
||||
for (int i = 0; i < S; i++) {
|
||||
const T2 bi = T2(b[i]);
|
||||
T2 carry = 0;
|
||||
for (int j = 0; j < S - i; j++) {
|
||||
const T2 rji = T2(a[j]) * bi + carry;
|
||||
carry = rji >> shift;
|
||||
r[i + j] += T2(T(rji));
|
||||
}
|
||||
}
|
||||
|
||||
T2 carry = 0;
|
||||
for (int i = 0; i < S; i++) {
|
||||
const T2 ri = r[i] + carry;
|
||||
carry = ri >> shift;
|
||||
dst[i] = T(ri);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline UIntF<T, Size> operator+(const UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
requires(!std::is_void_v<double_uint_type<T>>)
|
||||
{
|
||||
UIntF<T, Size> result;
|
||||
generic_add<T, double_uint_type<T>, Size>(result.v.data(), a.v.data(), b.v.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline IntF<T, Size> operator+(const IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
requires(!std::is_void_v<double_uint_type<T>>)
|
||||
{
|
||||
IntF<T, Size> result;
|
||||
generic_add<T, double_uint_type<T>, Size>(result.v.data(), a.v.data(), b.v.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline UIntF<T, Size> operator-(const UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
{
|
||||
UIntF<T, Size> result;
|
||||
generic_sub<T, double_uint_type<T>, Size>(result.v.data(), a.v.data(), b.v.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline IntF<T, Size> operator-(const IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
{
|
||||
IntF<T, Size> result;
|
||||
generic_sub<T, double_uint_type<T>, Size>(result.v.data(), a.v.data(), b.v.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline UIntF<T, Size> operator*(const UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
requires(!std::is_void_v<double_uint_type<T>>)
|
||||
{
|
||||
UIntF<T, Size> result;
|
||||
generic_unsigned_mul<T, double_uint_type<T>, Size>(result.v.data(), a.v.data(), b.v.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using this function is faster than using the comparison operator. Only a single bit has to be
|
||||
* checked to determine if the value is negative.
|
||||
*/
|
||||
template<typename T, int Size> bool is_negative(const IntF<T, Size> &a)
|
||||
{
|
||||
return (a.v[Size - 1] & (T(1) << (sizeof(T) * 8 - 1))) != 0;
|
||||
}
|
||||
|
||||
template<typename T, int Size> inline bool is_zero(const UIntF<T, Size> &a)
|
||||
{
|
||||
bool result = true;
|
||||
unroll<Size>([&](auto i) { result &= (a.v[i] == 0); });
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int Size> inline bool is_zero(const IntF<T, Size> &a)
|
||||
{
|
||||
bool result = true;
|
||||
unroll<Size>([&](auto i) { result &= (a.v[i] == 0); });
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline IntF<T, Size> operator*(const IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
requires(!std::is_void_v<double_uint_type<T>>)
|
||||
{
|
||||
using UIntF = UIntF<T, Size>;
|
||||
using IntF = IntF<T, Size>;
|
||||
|
||||
/* Signed multiplication is implemented in terms of unsigned multiplication. */
|
||||
const bool is_negative_a = is_negative(a);
|
||||
const bool is_negative_b = is_negative(b);
|
||||
if (is_negative_a && is_negative_b) {
|
||||
return IntF(UIntF(-a) * UIntF(-b));
|
||||
}
|
||||
if (is_negative_a) {
|
||||
return -IntF(UIntF(-a) * UIntF(b));
|
||||
}
|
||||
if (is_negative_b) {
|
||||
return -IntF(UIntF(a) * UIntF(-b));
|
||||
}
|
||||
return IntF(UIntF(a) * UIntF(b));
|
||||
}
|
||||
|
||||
template<typename T, int Size> inline IntF<T, Size> operator-(const IntF<T, Size> &a)
|
||||
{
|
||||
IntF<T, Size> result;
|
||||
for (int i = 0; i < Size; i++) {
|
||||
result.v[i] = ~a.v[i];
|
||||
}
|
||||
return result + IntF<T, Size>(1);
|
||||
}
|
||||
|
||||
template<typename T, int Size> inline void operator+=(UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
{
|
||||
a = a + b;
|
||||
}
|
||||
|
||||
template<typename T, int Size> inline void operator+=(IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
{
|
||||
a = a + b;
|
||||
}
|
||||
|
||||
template<typename T, int Size> inline void operator-=(UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
{
|
||||
a = a - b;
|
||||
}
|
||||
|
||||
template<typename T, int Size> inline void operator-=(IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
{
|
||||
a = a - b;
|
||||
}
|
||||
|
||||
template<typename T, int Size> inline void operator*=(UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
{
|
||||
a = a * b;
|
||||
}
|
||||
|
||||
template<typename T, int Size> inline void operator*=(IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
{
|
||||
a = a * b;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator==(const IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
{
|
||||
return a.v == b.v;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator==(const UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
{
|
||||
return a.v == b.v;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator!=(const IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
{
|
||||
return a.v != b.v;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator!=(const UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
{
|
||||
return a.v != b.v;
|
||||
}
|
||||
|
||||
template<typename T, size_t Size>
|
||||
inline int compare_reversed_order(const std::array<T, Size> &a, const std::array<T, Size> &b)
|
||||
{
|
||||
for (int i = Size - 1; i >= 0; i--) {
|
||||
if (a[i] < b[i]) {
|
||||
return -1;
|
||||
}
|
||||
if (a[i] > b[i]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator<(const IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
{
|
||||
const bool is_negative_a = is_negative(a);
|
||||
const bool is_negative_b = is_negative(b);
|
||||
if (is_negative_a == is_negative_b) {
|
||||
return compare_reversed_order(a.v, b.v) < 0;
|
||||
}
|
||||
return is_negative_a;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator<=(const IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
{
|
||||
const bool is_negative_a = is_negative(a);
|
||||
const bool is_negative_b = is_negative(b);
|
||||
if (is_negative_a == is_negative_b) {
|
||||
return compare_reversed_order(a.v, b.v) <= 0;
|
||||
}
|
||||
return is_negative_a;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator>(const IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
{
|
||||
const bool is_negative_a = is_negative(a);
|
||||
const bool is_negative_b = is_negative(b);
|
||||
if (is_negative_a == is_negative_b) {
|
||||
return compare_reversed_order(a.v, b.v) > 0;
|
||||
}
|
||||
return is_negative_b;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator>=(const IntF<T, Size> &a, const IntF<T, Size> &b)
|
||||
{
|
||||
const bool is_negative_a = is_negative(a);
|
||||
const bool is_negative_b = is_negative(b);
|
||||
if (is_negative_a == is_negative_b) {
|
||||
return compare_reversed_order(a.v, b.v) >= 0;
|
||||
}
|
||||
return is_negative_b;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator<(const UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
{
|
||||
return compare_reversed_order(a.v, b.v) < 0;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator<=(const UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
{
|
||||
return compare_reversed_order(a.v, b.v) <= 0;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator>(const UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
{
|
||||
return compare_reversed_order(a.v, b.v) > 0;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
inline bool operator>=(const UIntF<T, Size> &a, const UIntF<T, Size> &b)
|
||||
{
|
||||
return compare_reversed_order(a.v, b.v) >= 0;
|
||||
}
|
||||
|
||||
} // namespace blender::fixed_width_int
|
||||
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** Conversions to and from strings use GMP internally currently. */
|
||||
#ifdef WITH_GMP
|
||||
|
||||
# include <gmpxx.h>
|
||||
|
||||
# include "BLI_array.hh"
|
||||
# include "BLI_fixed_width_int.hh"
|
||||
|
||||
namespace blender::fixed_width_int {
|
||||
|
||||
template<typename T, int S>
|
||||
inline void UIntF<T, S>::set_from_str(const StringRefNull str, const int base)
|
||||
{
|
||||
mpz_t x;
|
||||
mpz_init(x);
|
||||
mpz_set_str(x, str.c_str(), base);
|
||||
for (int i = 0; i < S; i++) {
|
||||
static_assert(sizeof(T) <= sizeof(decltype(mpz_get_ui(x))));
|
||||
this->v[i] = T(mpz_get_ui(x));
|
||||
mpz_div_2exp(x, x, 8 * sizeof(T));
|
||||
}
|
||||
mpz_clear(x);
|
||||
}
|
||||
|
||||
template<typename T, int S>
|
||||
inline void IntF<T, S>::set_from_str(const StringRefNull str, const int base)
|
||||
{
|
||||
if (str[0] == '-') {
|
||||
const UIntF<T, S> unsigned_value(str.c_str() + 1, base);
|
||||
this->v = unsigned_value.v;
|
||||
*this = -*this;
|
||||
}
|
||||
else {
|
||||
const UIntF<T, S> unsigned_value(str.c_str(), base);
|
||||
this->v = unsigned_value.v;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, int S> inline std::string UIntF<T, S>::to_string(const int base) const
|
||||
{
|
||||
mpz_t x;
|
||||
mpz_init(x);
|
||||
for (int i = S - 1; i >= 0; i--) {
|
||||
static_assert(sizeof(T) <= sizeof(decltype(mpz_get_ui(x))));
|
||||
mpz_mul_2exp(x, x, 8 * sizeof(T));
|
||||
mpz_add_ui(x, x, this->v[i]);
|
||||
}
|
||||
/* Add 2 because of possible +/- sign and null terminator. */
|
||||
/* Also see https://gmplib.org/manual/Converting-Integers. */
|
||||
const int str_size = mpz_sizeinbase(x, base) + 2;
|
||||
Array<char, 1024> str(str_size);
|
||||
mpz_get_str(str.data(), base, x);
|
||||
mpz_clear(x);
|
||||
return std::string(str.data());
|
||||
}
|
||||
|
||||
template<typename T, int S> inline std::string IntF<T, S>::to_string(const int base) const
|
||||
{
|
||||
if (is_negative(*this)) {
|
||||
std::string str = UIntF<T, S>(-*this).to_string(base);
|
||||
str.insert(str.begin(), '-');
|
||||
return str;
|
||||
}
|
||||
return UIntF<T, S>(*this).to_string();
|
||||
}
|
||||
|
||||
} // namespace blender::fixed_width_int
|
||||
|
||||
#endif /* WITH_GMP */
|
||||
47
blender-5.2.0/source/blender/blenlib/BLI_fnmatch.h
Normal file
47
blender-5.2.0/source/blender/blenlib/BLI_fnmatch.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* SPDX-FileCopyrightText: 1991 1992 1993 Free Software Foundation, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \note The canonical source of this file is maintained with the GNU C Library.
|
||||
* Bugs can be reported to <bug-glibc@prep.ai.mit.edu>.
|
||||
*/
|
||||
|
||||
#if defined WIN32 && !defined _LIBC
|
||||
|
||||
# undef __P
|
||||
# define __P(protos) protos
|
||||
|
||||
/* We #undef these before defining them because some losing systems
|
||||
* (HP-UX A.08.07 for example) define these in <unistd.h>. */
|
||||
# undef FNM_PATHNAME
|
||||
# undef FNM_NOESCAPE
|
||||
# undef FNM_PERIOD
|
||||
|
||||
/* Bits set in the FLAGS argument to `fnmatch'. */
|
||||
# define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
|
||||
# define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
|
||||
# define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
|
||||
|
||||
# if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined(_GNU_SOURCE)
|
||||
# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
|
||||
# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
|
||||
# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
|
||||
# endif
|
||||
|
||||
/* Value returned by `fnmatch' if STRING does not match PATTERN. */
|
||||
# define FNM_NOMATCH 1
|
||||
|
||||
/* Match STRING against the filename pattern PATTERN,
|
||||
* returning zero if it matches, FNM_NOMATCH if not. */
|
||||
extern int fnmatch __P((const char *__pattern, const char *__string, int __flags));
|
||||
|
||||
#else
|
||||
# ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE
|
||||
# endif
|
||||
# include <fnmatch.h> // IWYU pragma: export
|
||||
#endif /* defined WIN32 && !defined _LIBC */
|
||||
165
blender-5.2.0/source/blender/blenlib/BLI_function_ref.hh
Normal file
165
blender-5.2.0/source/blender/blenlib/BLI_function_ref.hh
Normal file
@@ -0,0 +1,165 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "BLI_build_config.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A `FunctionRef<Signature>` is a non-owning reference to some callable object with a specific
|
||||
* signature. It can be used to pass some callback to another function.
|
||||
*
|
||||
* A `FunctionRef` is small and cheap to copy. Therefore it should generally be passed by value.
|
||||
*
|
||||
* Example signatures:
|
||||
* `FunctionRef<void()>` - A function without parameters and void return type.
|
||||
* `FunctionRef<int(float)>` - A function with a float parameter and an int return value.
|
||||
* `FunctionRef<int(int, int)>` - A function with two int parameters and an int return value.
|
||||
*
|
||||
* There are multiple ways to achieve that, so here is a comparison of the different approaches:
|
||||
* 1. Pass function pointer and user data (as void *) separately:
|
||||
* - The only method that is compatible with C interfaces.
|
||||
* - Is cumbersome to work with in many cases, because one has to keep track of two parameters.
|
||||
* - Not type safe at all, because of the void pointer.
|
||||
* - It requires workarounds when one wants to pass a lambda into a function.
|
||||
* 2. Using `std::function`:
|
||||
* - It works well with most callables and is easy to use.
|
||||
* - Owns the callable, so it can be returned from a function more safely than other methods.
|
||||
* - Requires that the callable is copyable.
|
||||
* - Requires an allocation when the callable is too large (typically > 16 bytes).
|
||||
* 3. Using a template for the callable type:
|
||||
* - Most efficient solution at runtime, because compiler knows the exact callable at the place
|
||||
* where it is called.
|
||||
* - Works well with all callables.
|
||||
* - Requires the function to be in a header file.
|
||||
* - It's difficult to constrain the signature of the function.
|
||||
* 4. Using `FunctionRef`:
|
||||
* - Second most efficient solution at runtime.
|
||||
* - It's easy to constrain the signature of the callable.
|
||||
* - Does not require the function to be in a header file.
|
||||
* - Works well with all callables.
|
||||
* - It's a non-owning reference, so it *cannot* be stored safely in general.
|
||||
*
|
||||
* The fact that this is a non-owning reference makes `FunctionRef` very well suited for some use
|
||||
* cases, but one has to be a bit more careful when using it to make sure that the referenced
|
||||
* callable is not destructed.
|
||||
*
|
||||
* In particular, one must not construct a `FunctionRef` variable from a lambda directly as shown
|
||||
* below. This is because the lambda object goes out of scope after the line finished executing and
|
||||
* will be destructed. Calling the reference afterwards invokes undefined behavior.
|
||||
*
|
||||
* Don't:
|
||||
* FunctionRef<int()> ref = []() { return 0; };
|
||||
* Do:
|
||||
* auto f = []() { return 0; };
|
||||
* FuntionRef<int()> ref = f;
|
||||
*
|
||||
* It is fine to pass a lambda directly to a function:
|
||||
*
|
||||
* void some_function(FunctionRef<int()> f);
|
||||
* some_function([]() { return 0; });
|
||||
*/
|
||||
|
||||
template<typename Function> class FunctionRef;
|
||||
|
||||
template<typename Ret, typename... Params> class FunctionRef<Ret(Params...)> {
|
||||
private:
|
||||
/**
|
||||
* A function pointer that knows how to call the referenced callable with the given parameters.
|
||||
*/
|
||||
Ret (*callback_)(intptr_t callable, Params... params) = nullptr;
|
||||
|
||||
/**
|
||||
* A pointer to the referenced callable object. This can be a C function, a lambda object or any
|
||||
* other callable.
|
||||
*
|
||||
* The value does not need to be initialized because it is not used unless `callback_` is set as
|
||||
* well, in which case it will be initialized as well.
|
||||
*
|
||||
* Use `intptr_t` to avoid warnings when casting to function pointers.
|
||||
*/
|
||||
intptr_t callable_;
|
||||
|
||||
template<typename Callable> static Ret callback_fn(intptr_t callable, Params... params)
|
||||
{
|
||||
return (*reinterpret_cast<Callable *>(callable))(std::forward<Params>(params)...);
|
||||
}
|
||||
|
||||
public:
|
||||
FunctionRef() = default;
|
||||
|
||||
FunctionRef(std::nullptr_t) {}
|
||||
|
||||
/**
|
||||
* A `FunctionRef` itself is a callable as well. However, we don't want that this
|
||||
* constructor is called when `Callable` is a `FunctionRef`. If we would allow this, it
|
||||
* would be easy to accidentally create a `FunctionRef` that internally calls another
|
||||
* `FunctionRef`. Usually, when assigning a `FunctionRef` to another, we want that both
|
||||
* contain a reference to the same underlying callable afterwards.
|
||||
*
|
||||
* It is still possible to reference another `FunctionRef` by first wrapping it in
|
||||
* another lambda.
|
||||
*/
|
||||
template<typename Callable>
|
||||
FunctionRef(Callable &&callable)
|
||||
requires(!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Callable>>, FunctionRef> &&
|
||||
std::is_invocable_r_v<Ret, Callable, Params...>)
|
||||
: callback_(callback_fn<typename std::remove_reference_t<Callable>>),
|
||||
callable_(intptr_t(&callable))
|
||||
{
|
||||
if constexpr (std::is_constructible_v<bool, Callable>) {
|
||||
/* For some types, the compiler can be sure that the callable is always truthy. Good!
|
||||
* Then the entire check can be optimized away. */
|
||||
#if COMPILER_CLANG || COMPILER_GCC
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Waddress"
|
||||
# if COMPILER_GCC
|
||||
# pragma GCC diagnostic ignored "-Wnonnull-compare"
|
||||
# endif
|
||||
#endif
|
||||
/* Make sure the #FunctionRef is falsy if the callback is falsy.
|
||||
* That can happen when passing in null or empty std::function. */
|
||||
const bool is_truthy = bool(callable);
|
||||
if (!is_truthy) {
|
||||
callback_ = nullptr;
|
||||
callable_ = 0;
|
||||
}
|
||||
#if COMPILER_CLANG || COMPILER_GCC
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the referenced function and forward all parameters to it.
|
||||
*
|
||||
* This invokes undefined behavior if the `FunctionRef` does not reference a function currently.
|
||||
*/
|
||||
Ret operator()(Params... params) const
|
||||
{
|
||||
BLI_assert(callback_ != nullptr);
|
||||
return callback_(callable_, std::forward<Params>(params)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, when the `FunctionRef` references a function currently.
|
||||
* If this returns false, the `FunctionRef` must not be called.
|
||||
*/
|
||||
operator bool() const
|
||||
{
|
||||
/* Just checking `callback_` is enough to determine if the `FunctionRef` is in a state that it
|
||||
* can be called in. */
|
||||
return callback_ != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
260
blender-5.2.0/source/blender/blenlib/BLI_generic_array.hh
Normal file
260
blender-5.2.0/source/blender/blenlib/BLI_generic_array.hh
Normal file
@@ -0,0 +1,260 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* This is a generic counterpart to #Array, used when the type is not known at runtime.
|
||||
*
|
||||
* `GArray` should generally only be used for passing data around in dynamic contexts.
|
||||
* It does not support a few things that #Array supports:
|
||||
* - Small object optimization / inline buffer.
|
||||
* - Exception safety and various more specific constructors.
|
||||
*/
|
||||
|
||||
#include "BLI_allocator.hh"
|
||||
#include "BLI_cpp_type.hh"
|
||||
#include "BLI_generic_span.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
template<
|
||||
/**
|
||||
* The allocator used by this array. Should rarely be changed, except when you don't want that
|
||||
* MEM_* functions are used internally.
|
||||
*/
|
||||
typename Allocator = GuardedAllocator>
|
||||
class GArray {
|
||||
protected:
|
||||
/** The type of the data in the array, will be null after the array is default constructed,
|
||||
* but a value should be assigned before any other interaction with the array. */
|
||||
const CPPType *type_ = nullptr;
|
||||
void *data_ = nullptr;
|
||||
int64_t size_ = 0;
|
||||
|
||||
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* The default constructor creates an empty array, the only situation in which the type is
|
||||
* allowed to be null. This default constructor exists so `GArray` can be used in containers,
|
||||
* but the type should be supplied before doing anything else to the array.
|
||||
*/
|
||||
GArray(Allocator allocator = {}) noexcept : allocator_(allocator) {}
|
||||
|
||||
GArray(NoExceptConstructor, Allocator allocator = {}) noexcept : GArray(allocator) {}
|
||||
|
||||
/**
|
||||
* Create and allocate a new array, with elements default constructed
|
||||
* (which does not do anything for trivial types).
|
||||
*/
|
||||
GArray(const CPPType &type, int64_t size, Allocator allocator = {})
|
||||
: GArray(type, size, NoInitialization{}, allocator)
|
||||
{
|
||||
type_->default_construct_n(data_, size_);
|
||||
}
|
||||
|
||||
GArray(const CPPType &type,
|
||||
const int64_t size,
|
||||
NoInitialization /*not_init_tag*/,
|
||||
Allocator allocator = {})
|
||||
: GArray(type, allocator)
|
||||
{
|
||||
BLI_assert(size >= 0);
|
||||
size_ = size;
|
||||
data_ = this->allocate(size_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty array with just a type.
|
||||
*/
|
||||
GArray(const CPPType &type, Allocator allocator = {}) : GArray(allocator)
|
||||
{
|
||||
type_ = &type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take ownership of a buffer with a provided size. The buffer should be
|
||||
* allocated with the same allocator provided to the constructor.
|
||||
*/
|
||||
GArray(const CPPType &type, void *buffer, int64_t size, Allocator allocator = {})
|
||||
: GArray(type, allocator)
|
||||
{
|
||||
BLI_assert(size >= 0);
|
||||
BLI_assert(buffer != nullptr || size == 0);
|
||||
BLI_assert(type_->pointer_has_valid_alignment(buffer));
|
||||
|
||||
data_ = buffer;
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array by copying values from a generic span.
|
||||
*/
|
||||
GArray(const GSpan span, Allocator allocator = {}) : GArray(span.type(), span.size(), allocator)
|
||||
{
|
||||
/* Use copy assign rather than construct since the memory is already initialized. */
|
||||
type_->copy_assign_n(span.data(), data_, size_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array by copying values from another generic array.
|
||||
*/
|
||||
GArray(const GArray &other) : GArray(other.as_span(), other.allocator()) {}
|
||||
|
||||
/**
|
||||
* Create an array by taking ownership of another array's data, clearing the data in the other.
|
||||
*/
|
||||
GArray(GArray &&other)
|
||||
: type_(other.type_), data_(other.data_), size_(other.size_), allocator_(other.allocator_)
|
||||
{
|
||||
other.data_ = nullptr;
|
||||
other.size_ = 0;
|
||||
}
|
||||
|
||||
~GArray()
|
||||
{
|
||||
if (data_ != nullptr) {
|
||||
type_->destruct_n(data_, size_);
|
||||
this->deallocate(data_);
|
||||
}
|
||||
}
|
||||
|
||||
GArray &operator=(const GArray &other)
|
||||
{
|
||||
return copy_assign_container(*this, other);
|
||||
}
|
||||
|
||||
GArray &operator=(GArray &&other)
|
||||
{
|
||||
return move_assign_container(*this, std::move(other));
|
||||
}
|
||||
|
||||
const CPPType &type() const
|
||||
{
|
||||
BLI_assert(type_ != nullptr);
|
||||
return *type_;
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of elements in the array (not the size in bytes).
|
||||
*/
|
||||
int64_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer to the beginning of the array.
|
||||
*/
|
||||
const void *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
void *data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const void *operator[](int64_t index) const
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
return POINTER_OFFSET(data_, type_->size * index);
|
||||
}
|
||||
|
||||
void *operator[](int64_t index)
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
return POINTER_OFFSET(data_, type_->size * index);
|
||||
}
|
||||
|
||||
operator GSpan() const
|
||||
{
|
||||
BLI_assert(size_ == 0 || type_ != nullptr);
|
||||
return GSpan(type_, data_, size_);
|
||||
}
|
||||
|
||||
operator GMutableSpan()
|
||||
{
|
||||
BLI_assert(size_ == 0 || type_ != nullptr);
|
||||
return GMutableSpan(type_, data_, size_);
|
||||
}
|
||||
|
||||
GSpan as_span() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
GMutableSpan as_mutable_span()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the allocator used by this array.
|
||||
*/
|
||||
Allocator &allocator()
|
||||
{
|
||||
return allocator_;
|
||||
}
|
||||
const Allocator &allocator() const
|
||||
{
|
||||
return allocator_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destruct values and create a new array of the given size. The values in the new array are
|
||||
* default constructed.
|
||||
*/
|
||||
void reinitialize(const int64_t new_size)
|
||||
{
|
||||
BLI_assert(new_size >= 0);
|
||||
int64_t old_size = size_;
|
||||
|
||||
type_->destruct_n(data_, size_);
|
||||
size_ = 0;
|
||||
|
||||
if (new_size <= old_size) {
|
||||
type_->default_construct_n(data_, new_size);
|
||||
}
|
||||
else {
|
||||
void *new_data = this->allocate(new_size);
|
||||
try {
|
||||
type_->default_construct_n(new_data, new_size);
|
||||
}
|
||||
catch (...) {
|
||||
this->deallocate(new_data);
|
||||
throw;
|
||||
}
|
||||
if (this->data_) {
|
||||
this->deallocate(data_);
|
||||
}
|
||||
data_ = new_data;
|
||||
}
|
||||
|
||||
size_ = new_size;
|
||||
}
|
||||
|
||||
private:
|
||||
void *allocate(int64_t size)
|
||||
{
|
||||
const int64_t item_size = type_->size;
|
||||
const int64_t alignment = type_->alignment;
|
||||
return allocator_.allocate(size_t(size) * item_size, alignment, AT);
|
||||
}
|
||||
|
||||
void deallocate(void *ptr)
|
||||
{
|
||||
allocator_.deallocate(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
60
blender-5.2.0/source/blender/blenlib/BLI_generic_key.hh
Normal file
60
blender-5.2.0/source/blender/blenlib/BLI_generic_key.hh
Normal file
@@ -0,0 +1,60 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* A #GenericKey allows different kinds of keys to be used in the same data-structure like a #Set
|
||||
* or #Map.
|
||||
*
|
||||
* Typically, the key is stored as `std::reference_wrapper<const GenericKey>` in the
|
||||
* data-structure. That implies that one has to make sure that the key is not destructed while it's
|
||||
* still in use.
|
||||
*/
|
||||
class GenericKey {
|
||||
public:
|
||||
virtual ~GenericKey() = default;
|
||||
|
||||
/** The hash function has to be implemented by the non-abstract subclass. */
|
||||
virtual uint64_t hash() const = 0;
|
||||
|
||||
/**
|
||||
* Check if the other key is equal to this one. Usually that involves a dynamic_cast to check if
|
||||
* it has the same type.
|
||||
*/
|
||||
virtual bool equal_to(const GenericKey &other) const = 0;
|
||||
|
||||
/**
|
||||
* For efficiency, it can be good to not always allocate the key if it's just used for lookup.
|
||||
* This method allows the key to be converted into a heap-allocated version that can be stored
|
||||
* safely if necessary.
|
||||
*/
|
||||
virtual std::unique_ptr<GenericKey> to_storable() const = 0;
|
||||
|
||||
friend bool operator==(const GenericKey &a, const GenericKey &b)
|
||||
{
|
||||
const bool are_equal = a.equal_to(b);
|
||||
/* Ensure that equality check is symmetric. */
|
||||
BLI_assert(are_equal == b.equal_to(a));
|
||||
return are_equal;
|
||||
}
|
||||
|
||||
friend bool operator!=(const GenericKey &a, const GenericKey &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,50 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_generic_key.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** Utility class that to easy create a #GenericKey from a string. */
|
||||
class GenericStringKey : public GenericKey, NonMovable {
|
||||
private:
|
||||
std::string value_;
|
||||
/** This may reference the string stored in value_. */
|
||||
StringRef value_ref_;
|
||||
|
||||
public:
|
||||
GenericStringKey(StringRef value) : value_ref_(value) {}
|
||||
|
||||
uint64_t hash() const override
|
||||
{
|
||||
return get_default_hash(value_ref_);
|
||||
}
|
||||
|
||||
friend bool operator==(const GenericStringKey &a, const GenericStringKey &b)
|
||||
{
|
||||
return a.value_ref_ == b.value_ref_;
|
||||
}
|
||||
|
||||
bool equal_to(const GenericKey &other) const override
|
||||
{
|
||||
if (const auto *other_typed = dynamic_cast<const GenericStringKey *>(&other)) {
|
||||
return value_ref_ == other_typed->value_ref_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<GenericKey> to_storable() const override
|
||||
{
|
||||
auto storable_key = std::make_unique<GenericStringKey>("");
|
||||
storable_key->value_ = value_ref_;
|
||||
storable_key->value_ref_ = storable_key->value_;
|
||||
return storable_key;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
139
blender-5.2.0/source/blender/blenlib/BLI_generic_pointer.hh
Normal file
139
blender-5.2.0/source/blender/blenlib/BLI_generic_pointer.hh
Normal file
@@ -0,0 +1,139 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_cpp_type.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* A generic non-const pointer whose type is only known at runtime.
|
||||
*/
|
||||
class GMutablePointer {
|
||||
private:
|
||||
const CPPType *type_ = nullptr;
|
||||
void *data_ = nullptr;
|
||||
|
||||
public:
|
||||
GMutablePointer() = default;
|
||||
|
||||
GMutablePointer(const CPPType *type, void *data = nullptr) : type_(type), data_(data)
|
||||
{
|
||||
/* If there is data, there has to be a type. */
|
||||
BLI_assert(data_ == nullptr || type_ != nullptr);
|
||||
}
|
||||
|
||||
GMutablePointer(const CPPType &type, void *data = nullptr) : GMutablePointer(&type, data) {}
|
||||
|
||||
template<typename T>
|
||||
GMutablePointer(T *data)
|
||||
requires(!std::is_void_v<T>)
|
||||
: GMutablePointer(&CPPType::get<T>(), data)
|
||||
{
|
||||
}
|
||||
|
||||
void *get() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const CPPType *type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return data_ != nullptr;
|
||||
}
|
||||
|
||||
template<typename T> T *get() const
|
||||
{
|
||||
BLI_assert(this->is_type<T>());
|
||||
return static_cast<T *>(data_);
|
||||
}
|
||||
|
||||
template<typename T> bool is_type() const
|
||||
{
|
||||
return type_ != nullptr && type_->is<T>();
|
||||
}
|
||||
|
||||
template<typename T> T relocate_out()
|
||||
{
|
||||
BLI_assert(this->is_type<T>());
|
||||
T value;
|
||||
type_->relocate_assign(data_, &value);
|
||||
data_ = nullptr;
|
||||
type_ = nullptr;
|
||||
return value;
|
||||
}
|
||||
|
||||
void destruct()
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
type_->destruct(data_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A generic const pointer whose type is only known at runtime.
|
||||
*/
|
||||
class GPointer {
|
||||
private:
|
||||
const CPPType *type_ = nullptr;
|
||||
const void *data_ = nullptr;
|
||||
|
||||
public:
|
||||
GPointer() = default;
|
||||
|
||||
GPointer(GMutablePointer ptr) : type_(ptr.type()), data_(ptr.get()) {}
|
||||
|
||||
GPointer(const CPPType *type, const void *data = nullptr) : type_(type), data_(data)
|
||||
{
|
||||
/* If there is data, there has to be a type. */
|
||||
BLI_assert(data_ == nullptr || type_ != nullptr);
|
||||
}
|
||||
|
||||
GPointer(const CPPType &type, const void *data = nullptr) : type_(&type), data_(data) {}
|
||||
|
||||
template<typename T>
|
||||
GPointer(T *data)
|
||||
requires(!std::is_void_v<T>)
|
||||
: GPointer(&CPPType::get<T>(), data)
|
||||
{
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return data_ != nullptr;
|
||||
}
|
||||
|
||||
const void *get() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const CPPType *type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
template<typename T> const T *get() const
|
||||
{
|
||||
BLI_assert(this->is_type<T>());
|
||||
return static_cast<const T *>(data_);
|
||||
}
|
||||
|
||||
template<typename T> bool is_type() const
|
||||
{
|
||||
return type_ != nullptr && type_->is<T>();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
281
blender-5.2.0/source/blender/blenlib/BLI_generic_span.hh
Normal file
281
blender-5.2.0/source/blender/blenlib/BLI_generic_span.hh
Normal file
@@ -0,0 +1,281 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_cpp_type.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* A generic span. It behaves just like a Span<T>, but the type is only known at run-time.
|
||||
*/
|
||||
class GSpan {
|
||||
protected:
|
||||
const CPPType *type_ = nullptr;
|
||||
const void *data_ = nullptr;
|
||||
int64_t size_ = 0;
|
||||
|
||||
public:
|
||||
GSpan() = default;
|
||||
|
||||
GSpan(const CPPType *type, const void *buffer, int64_t size)
|
||||
: type_(type), data_(buffer), size_(size)
|
||||
{
|
||||
BLI_assert(size >= 0);
|
||||
BLI_assert(buffer != nullptr || size == 0);
|
||||
BLI_assert(size == 0 || type != nullptr);
|
||||
BLI_assert(type == nullptr || type->pointer_has_valid_alignment(buffer));
|
||||
}
|
||||
|
||||
GSpan(const CPPType &type, const void *buffer, int64_t size) : GSpan(&type, buffer, size) {}
|
||||
|
||||
GSpan(const CPPType &type) : type_(&type) {}
|
||||
|
||||
GSpan(const CPPType *type) : type_(type) {}
|
||||
|
||||
template<typename T>
|
||||
GSpan(Span<T> array)
|
||||
: GSpan(CPPType::get<T>(), static_cast<const void *>(array.data()), array.size())
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
GSpan(MutableSpan<T> array)
|
||||
: GSpan(CPPType::get<T>(), static_cast<const void *>(array.data()), array.size())
|
||||
{
|
||||
}
|
||||
|
||||
const CPPType &type() const
|
||||
{
|
||||
BLI_assert(type_ != nullptr);
|
||||
return *type_;
|
||||
}
|
||||
|
||||
const CPPType *type_ptr() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
int64_t size_in_bytes() const
|
||||
{
|
||||
return type_->size * size_;
|
||||
}
|
||||
|
||||
const void *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const void *operator[](int64_t index) const
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
return POINTER_OFFSET(data_, type_->size * index);
|
||||
}
|
||||
|
||||
template<typename T> Span<T> typed() const
|
||||
{
|
||||
BLI_assert(size_ == 0 || type_ != nullptr);
|
||||
BLI_assert(type_ == nullptr || type_->is<T>());
|
||||
return Span<T>(static_cast<const T *>(data_), size_);
|
||||
}
|
||||
|
||||
GSpan slice(const int64_t start, int64_t size) const
|
||||
{
|
||||
BLI_assert(start >= 0);
|
||||
BLI_assert(size >= 0);
|
||||
BLI_assert(start + size <= size_ || size == 0);
|
||||
return GSpan(type_, POINTER_OFFSET(data_, type_->size * start), size);
|
||||
}
|
||||
|
||||
GSpan slice(const IndexRange range) const
|
||||
{
|
||||
return this->slice(range.start(), range.size());
|
||||
}
|
||||
|
||||
GSpan drop_front(const int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::max<int64_t>(0, size_ - n);
|
||||
return GSpan(*type_, POINTER_OFFSET(data_, type_->size * n), new_size);
|
||||
}
|
||||
|
||||
GSpan drop_back(const int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::max<int64_t>(0, size_ - n);
|
||||
return GSpan(*type_, data_, new_size);
|
||||
}
|
||||
|
||||
GSpan take_front(const int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::min<int64_t>(size_, n);
|
||||
return GSpan(*type_, data_, new_size);
|
||||
}
|
||||
|
||||
GSpan take_back(const int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::min<int64_t>(size_, n);
|
||||
return GSpan(*type_, POINTER_OFFSET(data_, type_->size * (size_ - new_size)), new_size);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A generic mutable span. It behaves just like a MutableSpan<T>, but the type is only
|
||||
* known at run-time.
|
||||
*/
|
||||
class GMutableSpan {
|
||||
protected:
|
||||
const CPPType *type_ = nullptr;
|
||||
void *data_ = nullptr;
|
||||
int64_t size_ = 0;
|
||||
|
||||
public:
|
||||
GMutableSpan() = default;
|
||||
|
||||
GMutableSpan(const CPPType *type, void *buffer, int64_t size)
|
||||
: type_(type), data_(buffer), size_(size)
|
||||
{
|
||||
BLI_assert(size >= 0);
|
||||
BLI_assert(buffer != nullptr || size == 0);
|
||||
BLI_assert(size == 0 || type != nullptr);
|
||||
BLI_assert(type == nullptr || type->pointer_has_valid_alignment(buffer));
|
||||
}
|
||||
|
||||
GMutableSpan(const CPPType &type, void *buffer, int64_t size) : GMutableSpan(&type, buffer, size)
|
||||
{
|
||||
}
|
||||
|
||||
GMutableSpan(const CPPType &type) : type_(&type) {}
|
||||
|
||||
GMutableSpan(const CPPType *type) : type_(type) {}
|
||||
|
||||
template<typename T>
|
||||
GMutableSpan(MutableSpan<T> array)
|
||||
: GMutableSpan(CPPType::get<T>(), static_cast<void *>(array.begin()), array.size())
|
||||
{
|
||||
}
|
||||
|
||||
operator GSpan() const
|
||||
{
|
||||
return GSpan(type_, data_, size_);
|
||||
}
|
||||
|
||||
const CPPType &type() const
|
||||
{
|
||||
BLI_assert(type_ != nullptr);
|
||||
return *type_;
|
||||
}
|
||||
|
||||
const CPPType *type_ptr() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
int64_t size_in_bytes() const
|
||||
{
|
||||
return type_->size * size_;
|
||||
}
|
||||
|
||||
void *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
void *operator[](int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < size_);
|
||||
return POINTER_OFFSET(data_, type_->size * index);
|
||||
}
|
||||
|
||||
template<typename T> MutableSpan<T> typed() const
|
||||
{
|
||||
BLI_assert(size_ == 0 || type_ != nullptr);
|
||||
BLI_assert(type_ == nullptr || type_->is<T>());
|
||||
return MutableSpan<T>(static_cast<T *>(data_), size_);
|
||||
}
|
||||
|
||||
GMutableSpan slice(const int64_t start, int64_t size) const
|
||||
{
|
||||
BLI_assert(start >= 0);
|
||||
BLI_assert(size >= 0);
|
||||
BLI_assert(start + size <= size_ || size == 0);
|
||||
return GMutableSpan(type_, POINTER_OFFSET(data_, type_->size * start), size);
|
||||
}
|
||||
|
||||
GMutableSpan slice(IndexRange range) const
|
||||
{
|
||||
return this->slice(range.start(), range.size());
|
||||
}
|
||||
|
||||
GMutableSpan drop_front(const int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::max<int64_t>(0, size_ - n);
|
||||
return GMutableSpan(*type_, POINTER_OFFSET(data_, type_->size * n), new_size);
|
||||
}
|
||||
|
||||
GMutableSpan drop_back(const int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::max<int64_t>(0, size_ - n);
|
||||
return GMutableSpan(*type_, data_, new_size);
|
||||
}
|
||||
|
||||
GMutableSpan take_front(const int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::min<int64_t>(size_, n);
|
||||
return GMutableSpan(*type_, data_, new_size);
|
||||
}
|
||||
|
||||
GMutableSpan take_back(const int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::min<int64_t>(size_, n);
|
||||
return GMutableSpan(*type_, POINTER_OFFSET(data_, type_->size * (size_ - new_size)), new_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all values from another span into this span. This invokes undefined behavior when the
|
||||
* destination contains uninitialized data and T is not trivially copy constructible.
|
||||
* The size of both spans is expected to be the same.
|
||||
*/
|
||||
void copy_from(GSpan values)
|
||||
{
|
||||
BLI_assert(type_ == &values.type());
|
||||
BLI_assert(size_ == values.size());
|
||||
type_->copy_assign_n(values.data(), data_, size_);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
115
blender-5.2.0/source/blender/blenlib/BLI_generic_value_map.hh
Normal file
115
blender-5.2.0/source/blender/blenlib/BLI_generic_value_map.hh
Normal file
@@ -0,0 +1,115 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_generic_pointer.hh"
|
||||
#include "BLI_linear_allocator.hh"
|
||||
#include "BLI_map.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* This is a map that stores key-value-pairs. What makes it special is that the type of values does
|
||||
* not have to be known at compile time. There just has to be a corresponding CPPType.
|
||||
*/
|
||||
template<typename Key> class GValueMap {
|
||||
private:
|
||||
/* Used to allocate values owned by this container. */
|
||||
LinearAllocator<> &allocator_;
|
||||
Map<Key, GMutablePointer> values_;
|
||||
|
||||
public:
|
||||
GValueMap(LinearAllocator<> &allocator) : allocator_(allocator) {}
|
||||
|
||||
~GValueMap()
|
||||
{
|
||||
/* Destruct all values that are still in the map. */
|
||||
for (GMutablePointer value : values_.values()) {
|
||||
value.destruct();
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a value to the container. The container becomes responsible for destructing the value that
|
||||
* is passed in. The caller remains responsible for freeing the value after it has been
|
||||
* destructed. */
|
||||
template<typename ForwardKey> void add_new_direct(ForwardKey &&key, GMutablePointer value)
|
||||
{
|
||||
values_.add_new_as(std::forward<ForwardKey>(key), value);
|
||||
}
|
||||
|
||||
/* Add a value to the container that is move constructed from the given value. The caller remains
|
||||
* responsible for destructing and freeing the given value. */
|
||||
template<typename ForwardKey> void add_new_by_move(ForwardKey &&key, GMutablePointer value)
|
||||
{
|
||||
const CPPType &type = *value.type();
|
||||
void *buffer = allocator_.allocate(type);
|
||||
type.move_construct(value.get(), buffer);
|
||||
values_.add_new_as(std::forward<ForwardKey>(key), GMutablePointer{type, buffer});
|
||||
}
|
||||
|
||||
/* Add a value to the container that is copy constructed from the given value. The caller remains
|
||||
* responsible for destructing and freeing the given value. */
|
||||
template<typename ForwardKey> void add_new_by_copy(ForwardKey &&key, GPointer value)
|
||||
{
|
||||
const CPPType &type = *value.type();
|
||||
void *buffer = allocator_.allocate(type);
|
||||
type.copy_construct(value.get(), buffer);
|
||||
values_.add_new_as(std::forward<ForwardKey>(key), GMutablePointer{type, buffer});
|
||||
}
|
||||
|
||||
/* Add a value to the container. */
|
||||
template<typename ForwardKey, typename T> void add_new(ForwardKey &&key, T &&value)
|
||||
{
|
||||
if constexpr (std::is_rvalue_reference_v<T>) {
|
||||
this->add_new_by_move(std::forward<ForwardKey>(key), &value);
|
||||
}
|
||||
else {
|
||||
this->add_new_by_copy(std::forward<ForwardKey>(key), &value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the value for the given name from the container and remove it. The caller is
|
||||
* responsible for freeing it. The lifetime of the referenced memory might be bound to lifetime
|
||||
* of the container. */
|
||||
template<typename ForwardKey> GMutablePointer extract(const ForwardKey &key)
|
||||
{
|
||||
return values_.pop_as(key);
|
||||
}
|
||||
|
||||
template<typename ForwardKey> GPointer lookup(const ForwardKey &key) const
|
||||
{
|
||||
return values_.lookup_as(key);
|
||||
}
|
||||
|
||||
/* Remove the value for the given name from the container and remove it. */
|
||||
template<typename T, typename ForwardKey> T extract(const ForwardKey &key)
|
||||
{
|
||||
GMutablePointer value = values_.pop_as(key);
|
||||
const CPPType &type = *value.type();
|
||||
BLI_assert(type.is<T>());
|
||||
T return_value;
|
||||
type.relocate_assign(value.get(), &return_value);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
template<typename T, typename ForwardKey> const T &lookup(const ForwardKey &key) const
|
||||
{
|
||||
GMutablePointer value = values_.lookup_as(key);
|
||||
BLI_assert(value.is_type<T>());
|
||||
BLI_assert(value.get() != nullptr);
|
||||
return *static_cast<const T *>(value.get());
|
||||
}
|
||||
|
||||
template<typename ForwardKey> bool contains(const ForwardKey &key) const
|
||||
{
|
||||
return values_.contains_as(key);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
149
blender-5.2.0/source/blender/blenlib/BLI_generic_vector_array.hh
Normal file
149
blender-5.2.0/source/blender/blenlib/BLI_generic_vector_array.hh
Normal file
@@ -0,0 +1,149 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A`GVectorArray` is a container for a fixed amount of dynamically growing vectors with a generic
|
||||
* data type. Its main use case is to store many small vectors with few separate allocations. Using
|
||||
* this structure is generally more efficient than allocating each vector separately.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_generic_virtual_vector_array.hh"
|
||||
#include "BLI_linear_allocator.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* An array of vectors containing elements of a generic type. */
|
||||
class GVectorArray : NonCopyable, NonMovable {
|
||||
private:
|
||||
struct Item {
|
||||
void *start = nullptr;
|
||||
int64_t length = 0;
|
||||
int64_t capacity = 0;
|
||||
};
|
||||
|
||||
/* Use a linear allocator to pack many small vectors together. Currently, memory from reallocated
|
||||
* vectors is not reused. This can be improved in the future. */
|
||||
LinearAllocator<> allocator_;
|
||||
/* The data type of individual elements. */
|
||||
const CPPType &type_;
|
||||
/* The size of an individual element. This is inlined from `type_.size()` for easier access. */
|
||||
const int64_t element_size_;
|
||||
/* The individual vectors. */
|
||||
Array<Item> items_;
|
||||
|
||||
public:
|
||||
GVectorArray() = delete;
|
||||
|
||||
GVectorArray(const CPPType &type, int64_t array_size);
|
||||
|
||||
~GVectorArray();
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return items_.size();
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return items_.is_empty();
|
||||
}
|
||||
|
||||
const CPPType &type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
void append(int64_t index, const void *value);
|
||||
|
||||
/* Add multiple elements to a single vector. */
|
||||
void extend(int64_t index, const GVArray &values);
|
||||
void extend(int64_t index, GSpan values);
|
||||
|
||||
/* Add multiple elements to multiple vectors. */
|
||||
void extend(const IndexMask &mask, const GVVectorArray &values);
|
||||
void extend(const IndexMask &mask, const GVectorArray &values);
|
||||
|
||||
void clear(const IndexMask &mask);
|
||||
|
||||
GMutableSpan operator[](int64_t index);
|
||||
GSpan operator[](int64_t index) const;
|
||||
|
||||
private:
|
||||
void realloc_to_at_least(Item &item, int64_t min_capacity);
|
||||
};
|
||||
|
||||
/* A non-owning typed mutable reference to an `GVectorArray`. It simplifies access when the type of
|
||||
* the data is known at compile time. */
|
||||
template<typename T> class GVectorArray_TypedMutableRef {
|
||||
private:
|
||||
GVectorArray *vector_array_;
|
||||
|
||||
public:
|
||||
GVectorArray_TypedMutableRef(GVectorArray &vector_array) : vector_array_(&vector_array)
|
||||
{
|
||||
BLI_assert(vector_array_->type().is<T>());
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return vector_array_->size();
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return vector_array_->is_empty();
|
||||
}
|
||||
|
||||
void append(const int64_t index, const T &value)
|
||||
{
|
||||
vector_array_->append(index, &value);
|
||||
}
|
||||
|
||||
void extend(const int64_t index, const Span<T> values)
|
||||
{
|
||||
vector_array_->extend(index, values);
|
||||
}
|
||||
|
||||
void extend(const int64_t index, const VArray<T> &values)
|
||||
{
|
||||
vector_array_->extend(index, values);
|
||||
}
|
||||
|
||||
MutableSpan<T> operator[](const int64_t index)
|
||||
{
|
||||
return (*vector_array_)[index].typed<T>();
|
||||
}
|
||||
};
|
||||
|
||||
/* A generic virtual vector array implementation for a `GVectorArray`. */
|
||||
class GVVectorArray_For_GVectorArray : public GVVectorArray {
|
||||
private:
|
||||
const GVectorArray &vector_array_;
|
||||
|
||||
public:
|
||||
GVVectorArray_For_GVectorArray(const GVectorArray &vector_array)
|
||||
: GVVectorArray(vector_array.type(), vector_array.size()), vector_array_(vector_array)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
int64_t get_vector_size_impl(const int64_t index) const override
|
||||
{
|
||||
return vector_array_[index].size();
|
||||
}
|
||||
|
||||
void get_vector_element_impl(const int64_t index,
|
||||
const int64_t index_in_vector,
|
||||
void *r_value) const override
|
||||
{
|
||||
type_->copy_assign(vector_array_[index][index_in_vector], r_value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,994 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A generic virtual array is the same as a virtual array, except for the fact that the data type
|
||||
* is only known at runtime.
|
||||
*/
|
||||
|
||||
#include "BLI_generic_array.hh"
|
||||
#include "BLI_generic_span.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #GVArrayImpl and #GVMutableArrayImpl.
|
||||
* \{ */
|
||||
|
||||
class GVArray;
|
||||
class GVArrayImpl;
|
||||
class GVMutableArray;
|
||||
class GVMutableArrayImpl;
|
||||
|
||||
/* A generically typed version of #VArrayImpl. */
|
||||
class GVArrayImpl {
|
||||
protected:
|
||||
const CPPType *type_;
|
||||
int64_t size_;
|
||||
|
||||
public:
|
||||
GVArrayImpl(const CPPType &type, int64_t size);
|
||||
virtual ~GVArrayImpl() = default;
|
||||
|
||||
const CPPType &type() const;
|
||||
|
||||
int64_t size() const;
|
||||
|
||||
virtual void get(int64_t index, void *r_value) const;
|
||||
virtual void get_to_uninitialized(int64_t index, void *r_value) const = 0;
|
||||
|
||||
virtual CommonVArrayInfo common_info() const;
|
||||
|
||||
virtual void materialize(const IndexMask &mask, void *dst, bool dst_is_uninitialized) const;
|
||||
virtual void materialize_compressed(const IndexMask &mask,
|
||||
void *dst,
|
||||
bool dst_is_uninitialized) const;
|
||||
|
||||
virtual bool try_assign_VArray(void *varray) const;
|
||||
};
|
||||
|
||||
/* A generic version of #VMutableArrayImpl. */
|
||||
class GVMutableArrayImpl : public GVArrayImpl {
|
||||
public:
|
||||
GVMutableArrayImpl(const CPPType &type, int64_t size) : GVArrayImpl(type, size) {}
|
||||
|
||||
virtual void set_by_copy(int64_t index, const void *value);
|
||||
virtual void set_by_relocate(int64_t index, void *value);
|
||||
virtual void set_by_move(int64_t index, void *value) = 0;
|
||||
|
||||
virtual void set_all(const void *src);
|
||||
|
||||
virtual bool try_assign_VMutableArray(void *varray) const;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #GVArray and #GVMutableArray
|
||||
* \{ */
|
||||
|
||||
namespace blenlib_detail {
|
||||
struct GVArrayAnyExtraInfo {
|
||||
const GVArrayImpl *(*get_varray)(const void *buffer) =
|
||||
[](const void * /*buffer*/) -> const GVArrayImpl * { return nullptr; };
|
||||
|
||||
template<typename StorageT> static constexpr GVArrayAnyExtraInfo get();
|
||||
};
|
||||
} // namespace blenlib_detail
|
||||
|
||||
class GVMutableArray;
|
||||
|
||||
/**
|
||||
* Utility class to reduce code duplication between #GVArray and #GVMutableArray.
|
||||
* It pretty much follows #VArrayCommon. Don't use this class outside of this header.
|
||||
*/
|
||||
class GVArrayCommon {
|
||||
protected:
|
||||
AnyDerived<const GVArrayImpl, 40> impl_;
|
||||
|
||||
GVArrayCommon() = default;
|
||||
GVArrayCommon(const GVArrayImpl *impl);
|
||||
GVArrayCommon(std::shared_ptr<const GVArrayImpl> impl);
|
||||
|
||||
template<typename ImplT, typename... Args> void emplace(Args &&...args);
|
||||
|
||||
public:
|
||||
const CPPType &type() const;
|
||||
operator bool() const;
|
||||
|
||||
int64_t size() const;
|
||||
bool is_empty() const;
|
||||
IndexRange index_range() const;
|
||||
|
||||
template<typename T> bool try_assign_VArray(VArray<T> &varray) const;
|
||||
bool may_have_ownership() const;
|
||||
|
||||
void materialize(void *dst) const;
|
||||
void materialize(const IndexMask &mask, void *dst) const;
|
||||
|
||||
void materialize_to_uninitialized(void *dst) const;
|
||||
void materialize_to_uninitialized(const IndexMask &mask, void *dst) const;
|
||||
|
||||
void materialize_compressed(const IndexMask &mask, void *dst) const;
|
||||
void materialize_compressed_to_uninitialized(const IndexMask &mask, void *dst) const;
|
||||
|
||||
CommonVArrayInfo common_info() const;
|
||||
|
||||
/**
|
||||
* Returns true when the virtual array is stored as a span internally.
|
||||
*/
|
||||
bool is_span() const;
|
||||
/**
|
||||
* Returns the internally used span of the virtual array. This invokes undefined behavior if the
|
||||
* virtual array is not stored as a span internally.
|
||||
*/
|
||||
GSpan get_internal_span() const;
|
||||
|
||||
/**
|
||||
* Returns true when the virtual array returns the same value for every index.
|
||||
*/
|
||||
bool is_single() const;
|
||||
/**
|
||||
* Copies the value that is used for every element into `r_value`, which is expected to point to
|
||||
* initialized memory. This invokes undefined behavior if the virtual array would not return the
|
||||
* same value for every index.
|
||||
*/
|
||||
void get_internal_single(void *r_value) const;
|
||||
/**
|
||||
* Same as `get_internal_single`, but `r_value` points to initialized memory.
|
||||
*/
|
||||
void get_internal_single_to_uninitialized(void *r_value) const;
|
||||
|
||||
void get(int64_t index, void *r_value) const;
|
||||
/**
|
||||
* Returns a copy of the value at the given index. Usually a typed virtual array should
|
||||
* be used instead, but sometimes this is simpler when only a few indices are needed.
|
||||
*/
|
||||
template<typename T> T get(int64_t index) const;
|
||||
void get_to_uninitialized(int64_t index, void *r_value) const;
|
||||
};
|
||||
|
||||
/** Generic version of #VArray. */
|
||||
class GVArray : public GVArrayCommon {
|
||||
private:
|
||||
friend GVMutableArray;
|
||||
|
||||
public:
|
||||
GVArray() = default;
|
||||
|
||||
GVArray(const GVArrayImpl *impl);
|
||||
GVArray(std::shared_ptr<const GVArrayImpl> impl);
|
||||
|
||||
GVArray(varray_tag::span /*tag*/, GSpan span);
|
||||
GVArray(varray_tag::single_ref /*tag*/, const CPPType &type, int64_t size, const void *value);
|
||||
GVArray(varray_tag::single /*tag*/, const CPPType &type, int64_t size, const void *value);
|
||||
|
||||
template<typename T> GVArray(const VArray<T> &varray);
|
||||
template<typename T> GVArray(VArray<T> &&varray);
|
||||
template<typename T> VArray<T> typed() const;
|
||||
|
||||
template<typename ImplT, typename... Args> static GVArray from(Args &&...args);
|
||||
|
||||
static GVArray from_single(const CPPType &type, int64_t size, const void *value);
|
||||
static GVArray from_single_ref(const CPPType &type, int64_t size, const void *value);
|
||||
static GVArray from_single_default(const CPPType &type, int64_t size);
|
||||
static GVArray from_span(GSpan span);
|
||||
static GVArray from_garray(GArray<> array);
|
||||
static GVArray from_empty(const CPPType &type);
|
||||
template<typename GetToUninitFn>
|
||||
static GVArray from_func(const CPPType &type, int64_t size, GetToUninitFn &&get_to_uninit);
|
||||
static GVArray from_std_func(const CPPType &type,
|
||||
int64_t size,
|
||||
std::function<void(int64_t index, void *r_value)> get_to_uninit);
|
||||
|
||||
GVArray slice(IndexRange slice) const;
|
||||
|
||||
const GVArrayImpl *get_implementation() const
|
||||
{
|
||||
return impl_.get();
|
||||
}
|
||||
};
|
||||
|
||||
/** Generic version of #VMutableArray. */
|
||||
class GVMutableArray : public GVArrayCommon {
|
||||
public:
|
||||
GVMutableArray() = default;
|
||||
GVMutableArray(GVMutableArrayImpl *impl);
|
||||
GVMutableArray(std::shared_ptr<GVMutableArrayImpl> impl);
|
||||
|
||||
template<typename T> GVMutableArray(const VMutableArray<T> &varray);
|
||||
template<typename T> VMutableArray<T> typed() const;
|
||||
|
||||
template<typename ImplT, typename... Args> static GVMutableArray from(Args &&...args);
|
||||
|
||||
static GVMutableArray from_span(GMutableSpan span);
|
||||
|
||||
operator GVArray() const &;
|
||||
operator GVArray() && noexcept;
|
||||
|
||||
GMutableSpan get_internal_span() const;
|
||||
|
||||
template<typename T> bool try_assign_VMutableArray(VMutableArray<T> &varray) const;
|
||||
|
||||
void set_by_copy(int64_t index, const void *value);
|
||||
void set_by_move(int64_t index, void *value);
|
||||
void set_by_relocate(int64_t index, void *value);
|
||||
|
||||
void fill(const void *value);
|
||||
/**
|
||||
* Copy the values from the source buffer to all elements in the virtual array.
|
||||
*/
|
||||
void set_all(const void *src);
|
||||
|
||||
GVMutableArrayImpl *get_implementation() const;
|
||||
|
||||
private:
|
||||
GVMutableArrayImpl *get_impl() const;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #GVArraySpan and #GMutableVArraySpan.
|
||||
* \{ */
|
||||
|
||||
/* A generic version of VArraySpan. */
|
||||
class GVArraySpan : public GSpan {
|
||||
private:
|
||||
GVArray varray_;
|
||||
void *owned_data_ = nullptr;
|
||||
|
||||
public:
|
||||
GVArraySpan();
|
||||
GVArraySpan(GVArray varray);
|
||||
template<typename T> GVArraySpan(VArray<T> varray) : GVArraySpan(GVArray(varray)) {}
|
||||
GVArraySpan(GVArraySpan &&other);
|
||||
~GVArraySpan();
|
||||
GVArraySpan &operator=(GVArraySpan &&other);
|
||||
};
|
||||
|
||||
/* A generic version of MutableVArraySpan. */
|
||||
class GMutableVArraySpan : public GMutableSpan, NonCopyable, NonMovable {
|
||||
private:
|
||||
GVMutableArray varray_;
|
||||
void *owned_data_ = nullptr;
|
||||
bool save_has_been_called_ = false;
|
||||
bool show_not_saved_warning_ = true;
|
||||
|
||||
public:
|
||||
GMutableVArraySpan();
|
||||
GMutableVArraySpan(GVMutableArray varray, bool copy_values_to_span = true);
|
||||
GMutableVArraySpan(GMutableVArraySpan &&other);
|
||||
~GMutableVArraySpan();
|
||||
GMutableVArraySpan &operator=(GMutableVArraySpan &&other);
|
||||
|
||||
const GVMutableArray &varray() const;
|
||||
|
||||
void save();
|
||||
void disable_not_applied_warning();
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Conversions between generic and typed virtual arrays.
|
||||
* \{ */
|
||||
|
||||
/* Used to convert a typed virtual array into a generic one. */
|
||||
template<typename T> class GVArrayImpl_For_VArray : public GVArrayImpl {
|
||||
protected:
|
||||
VArray<T> varray_;
|
||||
|
||||
public:
|
||||
GVArrayImpl_For_VArray(VArray<T> varray)
|
||||
: GVArrayImpl(CPPType::get<T>(), varray.size()), varray_(std::move(varray))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void get(const int64_t index, void *r_value) const override
|
||||
{
|
||||
*static_cast<T *>(r_value) = varray_[index];
|
||||
}
|
||||
|
||||
void get_to_uninitialized(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(varray_[index]);
|
||||
}
|
||||
|
||||
void materialize(const IndexMask &mask,
|
||||
void *dst,
|
||||
const bool dst_is_uninitialized) const override
|
||||
{
|
||||
varray_.get_implementation()->materialize(mask, static_cast<T *>(dst), dst_is_uninitialized);
|
||||
}
|
||||
|
||||
void materialize_compressed(const IndexMask &mask,
|
||||
void *dst,
|
||||
const bool dst_is_uninitialized) const override
|
||||
{
|
||||
varray_.get_implementation()->materialize_compressed(
|
||||
mask, static_cast<T *>(dst), dst_is_uninitialized);
|
||||
}
|
||||
|
||||
bool try_assign_VArray(void *varray) const override
|
||||
{
|
||||
*static_cast<VArray<T> *>(varray) = varray_;
|
||||
return true;
|
||||
}
|
||||
|
||||
CommonVArrayInfo common_info() const override
|
||||
{
|
||||
return varray_.common_info();
|
||||
}
|
||||
};
|
||||
|
||||
/* Used to convert any generic virtual array into a typed one. */
|
||||
template<typename T> class VArrayImpl_For_GVArray : public VArrayImpl<T> {
|
||||
protected:
|
||||
GVArray varray_;
|
||||
|
||||
public:
|
||||
VArrayImpl_For_GVArray(GVArray varray) : VArrayImpl<T>(varray.size()), varray_(std::move(varray))
|
||||
{
|
||||
BLI_assert(varray_);
|
||||
BLI_assert(varray_.type().template is<T>());
|
||||
}
|
||||
|
||||
protected:
|
||||
T get(const int64_t index) const override
|
||||
{
|
||||
T value;
|
||||
varray_.get(index, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
CommonVArrayInfo common_info() const override
|
||||
{
|
||||
return varray_.common_info();
|
||||
}
|
||||
|
||||
bool try_assign_GVArray(GVArray &varray) const override
|
||||
{
|
||||
varray = varray_;
|
||||
return true;
|
||||
}
|
||||
|
||||
void materialize(const IndexMask &mask, T *dst, const bool dst_is_uninitialized) const override
|
||||
{
|
||||
varray_.get_implementation()->materialize(mask, dst, dst_is_uninitialized);
|
||||
}
|
||||
|
||||
void materialize_compressed(const IndexMask &mask,
|
||||
T *dst,
|
||||
const bool dst_is_uninitialized) const override
|
||||
{
|
||||
varray_.get_implementation()->materialize_compressed(mask, dst, dst_is_uninitialized);
|
||||
}
|
||||
};
|
||||
|
||||
/* Used to convert any typed virtual mutable array into a generic one. */
|
||||
template<typename T> class GVMutableArrayImpl_For_VMutableArray : public GVMutableArrayImpl {
|
||||
protected:
|
||||
VMutableArray<T> varray_;
|
||||
|
||||
public:
|
||||
GVMutableArrayImpl_For_VMutableArray(VMutableArray<T> varray)
|
||||
: GVMutableArrayImpl(CPPType::get<T>(), varray.size()), varray_(std::move(varray))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void get(const int64_t index, void *r_value) const override
|
||||
{
|
||||
*static_cast<T *>(r_value) = varray_[index];
|
||||
}
|
||||
|
||||
void get_to_uninitialized(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(varray_[index]);
|
||||
}
|
||||
|
||||
CommonVArrayInfo common_info() const override
|
||||
{
|
||||
return varray_.common_info();
|
||||
}
|
||||
|
||||
void set_by_copy(const int64_t index, const void *value) override
|
||||
{
|
||||
const T &value_ = *static_cast<const T *>(value);
|
||||
varray_.set(index, value_);
|
||||
}
|
||||
|
||||
void set_by_relocate(const int64_t index, void *value) override
|
||||
{
|
||||
T &value_ = *static_cast<T *>(value);
|
||||
varray_.set(index, std::move(value_));
|
||||
value_.~T();
|
||||
}
|
||||
|
||||
void set_by_move(const int64_t index, void *value) override
|
||||
{
|
||||
T &value_ = *static_cast<T *>(value);
|
||||
varray_.set(index, std::move(value_));
|
||||
}
|
||||
|
||||
void set_all(const void *src) override
|
||||
{
|
||||
varray_.set_all(Span(static_cast<const T *>(src), size_));
|
||||
}
|
||||
|
||||
void materialize(const IndexMask &mask,
|
||||
void *dst,
|
||||
const bool dst_is_uninitialized) const override
|
||||
{
|
||||
varray_.get_implementation()->materialize(mask, static_cast<T *>(dst), dst_is_uninitialized);
|
||||
}
|
||||
|
||||
void materialize_compressed(const IndexMask &mask,
|
||||
void *dst,
|
||||
const bool dst_is_uninitialized) const override
|
||||
{
|
||||
varray_.get_implementation()->materialize_compressed(
|
||||
mask, static_cast<T *>(dst), dst_is_uninitialized);
|
||||
}
|
||||
|
||||
bool try_assign_VArray(void *varray) const override
|
||||
{
|
||||
*static_cast<VArray<T> *>(varray) = varray_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool try_assign_VMutableArray(void *varray) const override
|
||||
{
|
||||
*static_cast<VMutableArray<T> *>(varray) = varray_;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/* Used to convert an generic mutable virtual array into a typed one. */
|
||||
template<typename T> class VMutableArrayImpl_For_GVMutableArray : public VMutableArrayImpl<T> {
|
||||
protected:
|
||||
GVMutableArray varray_;
|
||||
|
||||
public:
|
||||
VMutableArrayImpl_For_GVMutableArray(GVMutableArray varray)
|
||||
: VMutableArrayImpl<T>(varray.size()), varray_(varray)
|
||||
{
|
||||
BLI_assert(varray_);
|
||||
BLI_assert(varray_.type().template is<T>());
|
||||
}
|
||||
|
||||
private:
|
||||
T get(const int64_t index) const override
|
||||
{
|
||||
T value;
|
||||
varray_.get(index, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void set(const int64_t index, T value) override
|
||||
{
|
||||
varray_.set_by_relocate(index, &value);
|
||||
}
|
||||
|
||||
CommonVArrayInfo common_info() const override
|
||||
{
|
||||
return varray_.common_info();
|
||||
}
|
||||
|
||||
bool try_assign_GVArray(GVArray &varray) const override
|
||||
{
|
||||
varray = varray_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool try_assign_GVMutableArray(GVMutableArray &varray) const override
|
||||
{
|
||||
varray = varray_;
|
||||
return true;
|
||||
}
|
||||
|
||||
void materialize(const IndexMask &mask, T *dst, const bool dst_is_uninitialized) const override
|
||||
{
|
||||
varray_.get_implementation()->materialize(mask, dst, dst_is_uninitialized);
|
||||
}
|
||||
|
||||
void materialize_compressed(const IndexMask &mask,
|
||||
T *dst,
|
||||
const bool dst_is_uninitialized) const override
|
||||
{
|
||||
varray_.get_implementation()->materialize_compressed(mask, dst, dst_is_uninitialized);
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #GVArrayImpl_For_GSpan.
|
||||
* \{ */
|
||||
|
||||
class GVArrayImpl_For_GSpan : public GVMutableArrayImpl {
|
||||
protected:
|
||||
void *data_ = nullptr;
|
||||
const int64_t element_size_;
|
||||
|
||||
public:
|
||||
GVArrayImpl_For_GSpan(const GMutableSpan span)
|
||||
: GVMutableArrayImpl(span.type(), span.size()),
|
||||
data_(span.data()),
|
||||
element_size_(span.type().size)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
GVArrayImpl_For_GSpan(const CPPType &type, int64_t size)
|
||||
: GVMutableArrayImpl(type, size), element_size_(type.size)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
void get(int64_t index, void *r_value) const override;
|
||||
void get_to_uninitialized(int64_t index, void *r_value) const override;
|
||||
|
||||
void set_by_copy(int64_t index, const void *value) override;
|
||||
void set_by_move(int64_t index, void *value) override;
|
||||
void set_by_relocate(int64_t index, void *value) override;
|
||||
|
||||
CommonVArrayInfo common_info() const override;
|
||||
|
||||
void materialize(const IndexMask &mask, void *dst, bool dst_is_uninitialized) const override;
|
||||
|
||||
void materialize_compressed(const IndexMask &mask,
|
||||
void *dst,
|
||||
bool dst_is_uninitialized) const override;
|
||||
};
|
||||
|
||||
class GVArrayImpl_For_GSpan_final final : public GVArrayImpl_For_GSpan {
|
||||
public:
|
||||
using GVArrayImpl_For_GSpan::GVArrayImpl_For_GSpan;
|
||||
|
||||
private:
|
||||
CommonVArrayInfo common_info() const override;
|
||||
};
|
||||
|
||||
template<> inline constexpr bool is_trivial_extended_v<GVArrayImpl_For_GSpan_final> = true;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #GVArrayImpl_For_SingleValueRef.
|
||||
* \{ */
|
||||
|
||||
class GVArrayImpl_For_SingleValueRef : public GVArrayImpl {
|
||||
protected:
|
||||
const void *value_ = nullptr;
|
||||
|
||||
public:
|
||||
GVArrayImpl_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value)
|
||||
: GVArrayImpl(type, size), value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
GVArrayImpl_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArrayImpl(type, size)
|
||||
{
|
||||
}
|
||||
|
||||
void get(const int64_t index, void *r_value) const override;
|
||||
void get_to_uninitialized(const int64_t index, void *r_value) const override;
|
||||
CommonVArrayInfo common_info() const override;
|
||||
void materialize(const IndexMask &mask, void *dst, bool dst_is_uninitialized) const override;
|
||||
void materialize_compressed(const IndexMask &mask,
|
||||
void *dst,
|
||||
bool dst_is_uninitialized) const override;
|
||||
};
|
||||
|
||||
class GVArrayImpl_For_SingleValueRef_final final : public GVArrayImpl_For_SingleValueRef {
|
||||
public:
|
||||
using GVArrayImpl_For_SingleValueRef::GVArrayImpl_For_SingleValueRef;
|
||||
|
||||
private:
|
||||
CommonVArrayInfo common_info() const override;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline constexpr bool is_trivial_extended_v<GVArrayImpl_For_SingleValueRef_final> = true;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #GVArrayImpl_For_Func.
|
||||
* \{ */
|
||||
|
||||
template<typename GetToUninitFn> class GVArrayImpl_For_Func final : public GVArrayImpl {
|
||||
private:
|
||||
GetToUninitFn get_to_uninit_;
|
||||
|
||||
public:
|
||||
GVArrayImpl_For_Func(const CPPType &type, const int64_t size, GetToUninitFn get_to_uninit)
|
||||
: GVArrayImpl(type, size), get_to_uninit_(std::move(get_to_uninit))
|
||||
{
|
||||
}
|
||||
|
||||
void get(const int64_t index, void *r_value) const override
|
||||
{
|
||||
if (!type_->is_trivially_destructible) {
|
||||
type_->destruct(r_value);
|
||||
}
|
||||
return get_to_uninit_(index, r_value);
|
||||
}
|
||||
|
||||
void get_to_uninitialized(const int64_t index, void *r_value) const override
|
||||
{
|
||||
return get_to_uninit_(index, r_value);
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Inline methods for #GVArrayImpl.
|
||||
* \{ */
|
||||
|
||||
inline GVArrayImpl::GVArrayImpl(const CPPType &type, const int64_t size)
|
||||
: type_(&type), size_(size)
|
||||
{
|
||||
BLI_assert(size_ >= 0);
|
||||
}
|
||||
|
||||
inline const CPPType &GVArrayImpl::type() const
|
||||
{
|
||||
return *type_;
|
||||
}
|
||||
|
||||
inline int64_t GVArrayImpl::size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Inline methods for #GVMutableArrayImpl.
|
||||
* \{ */
|
||||
|
||||
inline void GVMutableArray::set_by_copy(const int64_t index, const void *value)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < this->size());
|
||||
this->get_impl()->set_by_copy(index, value);
|
||||
}
|
||||
|
||||
inline void GVMutableArray::set_by_move(const int64_t index, void *value)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < this->size());
|
||||
this->get_impl()->set_by_move(index, value);
|
||||
}
|
||||
|
||||
inline void GVMutableArray::set_by_relocate(const int64_t index, void *value)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < this->size());
|
||||
this->get_impl()->set_by_relocate(index, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool GVMutableArray::try_assign_VMutableArray(VMutableArray<T> &varray) const
|
||||
{
|
||||
BLI_assert(impl_->type().is<T>());
|
||||
return this->get_impl()->try_assign_VMutableArray(&varray);
|
||||
}
|
||||
|
||||
inline GVMutableArrayImpl *GVMutableArray::get_impl() const
|
||||
{
|
||||
return const_cast<GVMutableArrayImpl *>(static_cast<const GVMutableArrayImpl *>(impl_.get()));
|
||||
}
|
||||
|
||||
inline GVMutableArrayImpl *GVMutableArray::get_implementation() const
|
||||
{
|
||||
return this->get_impl();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Inline methods for #GVArrayCommon.
|
||||
* \{ */
|
||||
|
||||
template<typename ImplT, typename... Args> inline void GVArrayCommon::emplace(Args &&...args)
|
||||
{
|
||||
impl_.emplace<ImplT>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/* Copies the value at the given index into the provided storage. The `r_value` pointer is
|
||||
* expected to point to initialized memory. */
|
||||
inline void GVArrayCommon::get(const int64_t index, void *r_value) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < this->size());
|
||||
impl_->get(index, r_value);
|
||||
}
|
||||
|
||||
template<typename T> inline T GVArrayCommon::get(const int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < this->size());
|
||||
BLI_assert(this->type().is<T>());
|
||||
T value{};
|
||||
impl_->get(index, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Same as `get`, but `r_value` is expected to point to uninitialized memory. */
|
||||
inline void GVArrayCommon::get_to_uninitialized(const int64_t index, void *r_value) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < this->size());
|
||||
impl_->get_to_uninitialized(index, r_value);
|
||||
}
|
||||
|
||||
template<typename T> inline bool GVArrayCommon::try_assign_VArray(VArray<T> &varray) const
|
||||
{
|
||||
BLI_assert(impl_->type().is<T>());
|
||||
return impl_->try_assign_VArray(&varray);
|
||||
}
|
||||
|
||||
inline const CPPType &GVArrayCommon::type() const
|
||||
{
|
||||
return impl_->type();
|
||||
}
|
||||
|
||||
inline GVArrayCommon::operator bool() const
|
||||
{
|
||||
return impl_;
|
||||
}
|
||||
|
||||
inline CommonVArrayInfo GVArrayCommon::common_info() const
|
||||
{
|
||||
return impl_->common_info();
|
||||
}
|
||||
|
||||
inline int64_t GVArrayCommon::size() const
|
||||
{
|
||||
if (!impl_) {
|
||||
return 0;
|
||||
}
|
||||
return impl_->size();
|
||||
}
|
||||
|
||||
inline bool GVArrayCommon::is_empty() const
|
||||
{
|
||||
return this->size() == 0;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/** To be used with #call_with_devirtualized_parameters. */
|
||||
template<typename T, bool UseSingle, bool UseSpan> struct GVArrayDevirtualizer {
|
||||
const GVArrayImpl &varray_impl;
|
||||
|
||||
template<typename Fn> bool devirtualize(const Fn &fn) const
|
||||
{
|
||||
const CommonVArrayInfo info = this->varray_impl.common_info();
|
||||
const int64_t size = this->varray_impl.size();
|
||||
if constexpr (UseSingle) {
|
||||
if (info.type == CommonVArrayInfo::Type::Single) {
|
||||
return fn(SingleAsSpan<T>(*static_cast<const T *>(info.data), size));
|
||||
}
|
||||
}
|
||||
if constexpr (UseSpan) {
|
||||
if (info.type == CommonVArrayInfo::Type::Span) {
|
||||
return fn(Span<T>(static_cast<const T *>(info.data), size));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Inline methods for #GVArray.
|
||||
* \{ */
|
||||
|
||||
inline GVArray::GVArray(varray_tag::span /*tag*/, const GSpan span)
|
||||
{
|
||||
/* Use const-cast because the underlying virtual array implementation is shared between const
|
||||
* and non const data. */
|
||||
GMutableSpan mutable_span{span.type(), const_cast<void *>(span.data()), span.size()};
|
||||
this->emplace<GVArrayImpl_For_GSpan_final>(mutable_span);
|
||||
}
|
||||
|
||||
inline GVArray::GVArray(varray_tag::single_ref /*tag*/,
|
||||
const CPPType &type,
|
||||
const int64_t size,
|
||||
const void *value)
|
||||
{
|
||||
this->emplace<GVArrayImpl_For_SingleValueRef_final>(type, size, value);
|
||||
}
|
||||
|
||||
namespace blenlib_detail {
|
||||
template<typename StorageT> constexpr GVArrayAnyExtraInfo GVArrayAnyExtraInfo::get()
|
||||
{
|
||||
static_assert(std::is_base_of_v<GVArrayImpl, StorageT> ||
|
||||
is_same_any_v<StorageT, const GVArrayImpl *, std::shared_ptr<const GVArrayImpl>>);
|
||||
|
||||
if constexpr (std::is_base_of_v<GVArrayImpl, StorageT>) {
|
||||
return {[](const void *buffer) {
|
||||
return static_cast<const GVArrayImpl *>(static_cast<const StorageT *>(buffer));
|
||||
}};
|
||||
}
|
||||
else if constexpr (std::is_same_v<StorageT, const GVArrayImpl *>) {
|
||||
return {[](const void *buffer) { return *static_cast<const StorageT *>(buffer); }};
|
||||
}
|
||||
else if constexpr (std::is_same_v<StorageT, std::shared_ptr<const GVArrayImpl>>) {
|
||||
return {[](const void *buffer) { return (static_cast<const StorageT *>(buffer))->get(); }};
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
} // namespace blenlib_detail
|
||||
|
||||
template<typename ImplT, typename... Args> inline GVArray GVArray::from(Args &&...args)
|
||||
{
|
||||
static_assert(std::is_base_of_v<GVArrayImpl, ImplT>);
|
||||
GVArray varray;
|
||||
varray.template emplace<ImplT>(std::forward<Args>(args)...);
|
||||
return varray;
|
||||
}
|
||||
|
||||
template<typename T> inline GVArray::GVArray(const VArray<T> &varray) : GVArray(VArray<T>(varray))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T> inline GVArray::GVArray(VArray<T> &&varray)
|
||||
{
|
||||
if (!varray) {
|
||||
return;
|
||||
}
|
||||
const CommonVArrayInfo info = varray.common_info();
|
||||
if (info.type == CommonVArrayInfo::Type::Single) {
|
||||
*this = GVArray::from_single(CPPType::get<T>(), varray.size(), info.data);
|
||||
return;
|
||||
}
|
||||
/* Need to check for ownership, because otherwise the referenced data can be destructed when
|
||||
* #this is destructed. */
|
||||
if (info.type == CommonVArrayInfo::Type::Span && !info.may_have_ownership) {
|
||||
*this = GVArray::from_span(GSpan(CPPType::get<T>(), info.data, varray.size()));
|
||||
return;
|
||||
}
|
||||
if (varray.try_assign_GVArray(*this)) {
|
||||
return;
|
||||
}
|
||||
*this = GVArray::from<GVArrayImpl_For_VArray<T>>(std::move(varray));
|
||||
}
|
||||
|
||||
inline GVArray::GVArray(const GVArrayImpl *impl) : GVArrayCommon(impl) {}
|
||||
|
||||
inline GVArray::GVArray(std::shared_ptr<const GVArrayImpl> impl) : GVArrayCommon(std::move(impl))
|
||||
{
|
||||
}
|
||||
|
||||
inline GVArray GVArray::from_single(const CPPType &type, const int64_t size, const void *value)
|
||||
{
|
||||
return GVArray(varray_tag::single{}, type, size, value);
|
||||
}
|
||||
|
||||
inline GVArray GVArray::from_single_ref(const CPPType &type, const int64_t size, const void *value)
|
||||
{
|
||||
return GVArray(varray_tag::single_ref{}, type, size, value);
|
||||
}
|
||||
|
||||
inline GVArray GVArray::from_single_default(const CPPType &type, const int64_t size)
|
||||
{
|
||||
return GVArray::from_single_ref(type, size, type.default_value());
|
||||
}
|
||||
|
||||
inline GVArray GVArray::from_span(GSpan span)
|
||||
{
|
||||
return GVArray(varray_tag::span{}, span);
|
||||
}
|
||||
|
||||
inline GVArray GVArray::from_empty(const CPPType &type)
|
||||
{
|
||||
return GVArray::from_span(GSpan(type));
|
||||
}
|
||||
|
||||
inline GVArray GVArray::from_std_func(
|
||||
const CPPType &type,
|
||||
int64_t size,
|
||||
std::function<void(int64_t index, void *r_value)> get_to_uninit)
|
||||
{
|
||||
return GVArray::from_func(type, size, std::move(get_to_uninit));
|
||||
}
|
||||
|
||||
template<typename T> inline VArray<T> GVArray::typed() const
|
||||
{
|
||||
if (!*this) {
|
||||
return {};
|
||||
}
|
||||
BLI_assert(impl_->type().is<T>());
|
||||
const CommonVArrayInfo info = this->common_info();
|
||||
if (info.type == CommonVArrayInfo::Type::Single) {
|
||||
return VArray<T>::from_single(*static_cast<const T *>(info.data), this->size());
|
||||
}
|
||||
/* Need to check for ownership, because otherwise the referenced data can be destructed when
|
||||
* #this is destructed. */
|
||||
if (info.type == CommonVArrayInfo::Type::Span && !info.may_have_ownership) {
|
||||
return VArray<T>::from_span(Span<T>(static_cast<const T *>(info.data), this->size()));
|
||||
}
|
||||
VArray<T> varray;
|
||||
if (this->try_assign_VArray(varray)) {
|
||||
return varray;
|
||||
}
|
||||
return VArray<T>::template from<VArrayImpl_For_GVArray<T>>(*this);
|
||||
}
|
||||
|
||||
template<typename GetToUninitFn>
|
||||
inline GVArray GVArray::from_func(const CPPType &type, int64_t size, GetToUninitFn &&get_to_uninit)
|
||||
{
|
||||
return GVArray::from<GVArrayImpl_For_Func<GetToUninitFn>>(
|
||||
type, size, std::forward<GetToUninitFn>(get_to_uninit));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Inline methods for #GVMutableArray.
|
||||
* \{ */
|
||||
|
||||
template<typename ImplT, typename... Args>
|
||||
inline GVMutableArray GVMutableArray::from(Args &&...args)
|
||||
{
|
||||
static_assert(std::is_base_of_v<GVMutableArrayImpl, ImplT>);
|
||||
GVMutableArray varray;
|
||||
varray.emplace<ImplT>(std::forward<Args>(args)...);
|
||||
return varray;
|
||||
}
|
||||
|
||||
template<typename T> inline GVMutableArray::GVMutableArray(const VMutableArray<T> &varray)
|
||||
{
|
||||
if (!varray) {
|
||||
return;
|
||||
}
|
||||
const CommonVArrayInfo info = varray.common_info();
|
||||
if (info.type == CommonVArrayInfo::Type::Span && !info.may_have_ownership) {
|
||||
*this = GVMutableArray::from_span(
|
||||
GMutableSpan(CPPType::get<T>(), const_cast<void *>(info.data), varray.size()));
|
||||
return;
|
||||
}
|
||||
if (varray.try_assign_GVMutableArray(*this)) {
|
||||
return;
|
||||
}
|
||||
*this = GVMutableArray::from<GVMutableArrayImpl_For_VMutableArray<T>>(varray);
|
||||
}
|
||||
|
||||
template<typename T> inline VMutableArray<T> GVMutableArray::typed() const
|
||||
{
|
||||
if (!*this) {
|
||||
return {};
|
||||
}
|
||||
BLI_assert(this->type().is<T>());
|
||||
const CommonVArrayInfo info = this->common_info();
|
||||
if (info.type == CommonVArrayInfo::Type::Span && !info.may_have_ownership) {
|
||||
return VMutableArray<T>::from_span(
|
||||
MutableSpan<T>(const_cast<T *>(static_cast<const T *>(info.data)), this->size()));
|
||||
}
|
||||
VMutableArray<T> varray;
|
||||
if (this->try_assign_VMutableArray(varray)) {
|
||||
return varray;
|
||||
}
|
||||
return VMutableArray<T>::template from<VMutableArrayImpl_For_GVMutableArray<T>>(*this);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,173 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A generic virtual vector array is essentially the same as a virtual vector array, but its data
|
||||
* type is only known at runtime.
|
||||
*/
|
||||
|
||||
#include "BLI_generic_virtual_array.hh"
|
||||
#include "BLI_virtual_vector_array.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* A generically typed version of `VVectorArray`. */
|
||||
class GVVectorArray {
|
||||
protected:
|
||||
const CPPType *type_;
|
||||
int64_t size_;
|
||||
|
||||
public:
|
||||
GVVectorArray(const CPPType &type, const int64_t size) : type_(&type), size_(size) {}
|
||||
|
||||
virtual ~GVVectorArray() = default;
|
||||
|
||||
/* Returns the number of vectors in the vector array. */
|
||||
int64_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/* Returns true when there is no vector in the vector array. */
|
||||
bool is_empty() const
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
const CPPType &type() const
|
||||
{
|
||||
return *type_;
|
||||
}
|
||||
|
||||
/* Returns the size of the vector at the given index. */
|
||||
int64_t get_vector_size(const int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < size_);
|
||||
return this->get_vector_size_impl(index);
|
||||
}
|
||||
|
||||
/* Copies an element from one of the vectors into `r_value`, which is expected to point to
|
||||
* initialized memory. */
|
||||
void get_vector_element(const int64_t index, const int64_t index_in_vector, void *r_value) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < size_);
|
||||
BLI_assert(index_in_vector >= 0);
|
||||
BLI_assert(index_in_vector < this->get_vector_size(index));
|
||||
this->get_vector_element_impl(index, index_in_vector, r_value);
|
||||
}
|
||||
|
||||
/* Returns true when the same vector is used at every index. */
|
||||
bool is_single_vector() const
|
||||
{
|
||||
if (size_ == 1) {
|
||||
return true;
|
||||
}
|
||||
return this->is_single_vector_impl();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual int64_t get_vector_size_impl(int64_t index) const = 0;
|
||||
|
||||
virtual void get_vector_element_impl(int64_t index,
|
||||
int64_t index_in_vector,
|
||||
void *r_value) const = 0;
|
||||
|
||||
virtual bool is_single_vector_impl() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class GVArray_For_GVVectorArrayIndex : public GVArrayImpl {
|
||||
private:
|
||||
const GVVectorArray &vector_array_;
|
||||
const int64_t index_;
|
||||
|
||||
public:
|
||||
GVArray_For_GVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index)
|
||||
: GVArrayImpl(vector_array.type(), vector_array.get_vector_size(index)),
|
||||
vector_array_(vector_array),
|
||||
index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void get(int64_t index_in_vector, void *r_value) const override;
|
||||
void get_to_uninitialized(int64_t index_in_vector, void *r_value) const override;
|
||||
};
|
||||
|
||||
class GVVectorArray_For_SingleGVArray : public GVVectorArray {
|
||||
private:
|
||||
GVArray varray_;
|
||||
|
||||
public:
|
||||
GVVectorArray_For_SingleGVArray(GVArray varray, const int64_t size)
|
||||
: GVVectorArray(varray.type(), size), varray_(std::move(varray))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
int64_t get_vector_size_impl(int64_t index) const override;
|
||||
void get_vector_element_impl(int64_t index,
|
||||
int64_t index_in_vector,
|
||||
void *r_value) const override;
|
||||
|
||||
bool is_single_vector_impl() const override;
|
||||
};
|
||||
|
||||
class GVVectorArray_For_SingleGSpan : public GVVectorArray {
|
||||
private:
|
||||
const GSpan span_;
|
||||
|
||||
public:
|
||||
GVVectorArray_For_SingleGSpan(const GSpan span, const int64_t size)
|
||||
: GVVectorArray(span.type(), size), span_(span)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
int64_t get_vector_size_impl(int64_t /*index*/) const override;
|
||||
void get_vector_element_impl(int64_t /*index*/,
|
||||
int64_t index_in_vector,
|
||||
void *r_value) const override;
|
||||
|
||||
bool is_single_vector_impl() const override;
|
||||
};
|
||||
|
||||
template<typename T> class VVectorArray_For_GVVectorArray : public VVectorArray<T> {
|
||||
private:
|
||||
const GVVectorArray &vector_array_;
|
||||
|
||||
public:
|
||||
VVectorArray_For_GVVectorArray(const GVVectorArray &vector_array)
|
||||
: VVectorArray<T>(vector_array.size()), vector_array_(vector_array)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
int64_t get_vector_size_impl(const int64_t index) const override
|
||||
{
|
||||
return vector_array_.get_vector_size(index);
|
||||
}
|
||||
|
||||
T get_vector_element_impl(const int64_t index, const int64_t index_in_vector) const override
|
||||
{
|
||||
T value;
|
||||
vector_array_.get_vector_element(index, index_in_vector, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool is_single_vector_impl() const override
|
||||
{
|
||||
return vector_array_.is_single_vector();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
644
blender-5.2.0/source/blender/blenlib/BLI_ghash.h
Normal file
644
blender-5.2.0/source/blender/blenlib/BLI_ghash.h
Normal file
@@ -0,0 +1,644 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* GHash is a hash-map implementation (unordered key, value pairs).
|
||||
*
|
||||
* This is also used to implement a 'set' (see #GSet below).
|
||||
*/
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_compiler_compat.h"
|
||||
#include "BLI_sys_types.h" /* for bool */
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define _GHASH_INTERNAL_ATTR
|
||||
#ifndef GHASH_INTERNAL_API
|
||||
# ifdef __GNUC__
|
||||
# undef _GHASH_INTERNAL_ATTR
|
||||
# define _GHASH_INTERNAL_ATTR __attribute__((deprecated)) /* not deprecated, just private. */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GHash Types
|
||||
* \{ */
|
||||
|
||||
typedef unsigned int (*GHashHashFP)(const void *key);
|
||||
/** returns false when equal */
|
||||
typedef bool (*GHashCmpFP)(const void *a, const void *b);
|
||||
typedef void (*GHashKeyFreeFP)(void *key);
|
||||
typedef void (*GHashValFreeFP)(void *val);
|
||||
typedef void *(*GHashKeyCopyFP)(const void *key);
|
||||
typedef void *(*GHashValCopyFP)(const void *val);
|
||||
|
||||
struct GHash;
|
||||
|
||||
struct GHashIterator {
|
||||
GHash *gh;
|
||||
struct Entry *curEntry;
|
||||
unsigned int curBucket;
|
||||
};
|
||||
|
||||
struct GHashIterState {
|
||||
unsigned int curr_bucket _GHASH_INTERNAL_ATTR;
|
||||
};
|
||||
|
||||
enum {
|
||||
GHASH_FLAG_ALLOW_DUPES = (1 << 0), /* Only checked for in debug mode */
|
||||
GHASH_FLAG_ALLOW_SHRINK = (1 << 1), /* Allow to shrink buckets' size. */
|
||||
|
||||
#ifdef GHASH_INTERNAL_API
|
||||
/* Internal usage only */
|
||||
/* Whether the GHash is actually used as GSet (no value storage). */
|
||||
GHASH_FLAG_IS_GSET = (1 << 16),
|
||||
#endif
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GHash API
|
||||
*
|
||||
* Defined in `BLI_ghash.c`
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Creates a new, empty GHash.
|
||||
*
|
||||
* \param hashfp: Hash callback.
|
||||
* \param cmpfp: Comparison callback.
|
||||
* \param info: Identifier string for the GHash.
|
||||
* \param nentries_reserve: Optionally reserve the number of members that the hash will hold.
|
||||
* Use this to avoid resizing buckets if the size is known or can be closely approximated.
|
||||
* \return An empty GHash.
|
||||
*/
|
||||
GHash *BLI_ghash_new_ex(GHashHashFP hashfp,
|
||||
GHashCmpFP cmpfp,
|
||||
const char *info,
|
||||
unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Wraps #BLI_ghash_new_ex with zero entries reserved.
|
||||
*/
|
||||
GHash *BLI_ghash_new(GHashHashFP hashfp,
|
||||
GHashCmpFP cmpfp,
|
||||
const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Copy given GHash. Keys and values are also copied if relevant callback is provided,
|
||||
* else pointers remain the same.
|
||||
*/
|
||||
GHash *BLI_ghash_copy(const GHash *gh,
|
||||
GHashKeyCopyFP keycopyfp,
|
||||
GHashValCopyFP valcopyfp) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Frees the GHash and its members.
|
||||
*
|
||||
* \param gh: The GHash to free.
|
||||
* \param keyfreefp: Optional callback to free the key.
|
||||
* \param valfreefp: Optional callback to free the value.
|
||||
*/
|
||||
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp);
|
||||
/**
|
||||
* Reserve given amount of entries (resize \a gh accordingly if needed).
|
||||
*/
|
||||
void BLI_ghash_reserve(GHash *gh, unsigned int nentries_reserve);
|
||||
/**
|
||||
* Insert a key/value pair into the \a gh.
|
||||
*
|
||||
* \note Duplicates are not checked,
|
||||
* the caller is expected to ensure elements are unique unless
|
||||
* GHASH_FLAG_ALLOW_DUPES flag is set.
|
||||
*/
|
||||
void BLI_ghash_insert(GHash *gh, void *key, void *val);
|
||||
/**
|
||||
* Inserts a new value to a key that may already be in ghash.
|
||||
*
|
||||
* Avoids #BLI_ghash_remove, #BLI_ghash_insert calls (double lookups)
|
||||
*
|
||||
* \returns true if a new key has been added.
|
||||
*/
|
||||
bool BLI_ghash_reinsert(
|
||||
GHash *gh, void *key, void *val, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp);
|
||||
/**
|
||||
* Replaces the key of an item in the \a gh.
|
||||
*
|
||||
* Use when a key is re-allocated or its memory location is changed.
|
||||
*
|
||||
* \returns The previous key or NULL if not found, the caller may free if it's needed.
|
||||
*/
|
||||
void *BLI_ghash_replace_key(GHash *gh, void *key);
|
||||
/**
|
||||
* Lookup the value of \a key in \a gh.
|
||||
*
|
||||
* \param key: The key to lookup.
|
||||
* \returns the value for \a key or NULL.
|
||||
*
|
||||
* \note When NULL is a valid value, use #BLI_ghash_lookup_p to differentiate a missing key
|
||||
* from a key with a NULL value. (Avoids calling #BLI_ghash_haskey before #BLI_ghash_lookup)
|
||||
*/
|
||||
void *BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* A version of #BLI_ghash_lookup which accepts a fallback argument.
|
||||
*/
|
||||
void *BLI_ghash_lookup_default(const GHash *gh,
|
||||
const void *key,
|
||||
void *val_default) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Lookup a pointer to the value of \a key in \a gh.
|
||||
*
|
||||
* \param key: The key to lookup.
|
||||
* \returns the pointer to value for \a key or NULL.
|
||||
*
|
||||
* \note This has 2 main benefits over #BLI_ghash_lookup.
|
||||
* - A NULL return always means that \a key isn't in \a gh.
|
||||
* - The value can be modified in-place without further function calls (faster).
|
||||
*/
|
||||
void **BLI_ghash_lookup_p(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Ensure \a key is exists in \a gh.
|
||||
*
|
||||
* This handles the common situation where the caller needs ensure a key is added to \a gh,
|
||||
* constructing a new value in the case the key isn't found.
|
||||
* Otherwise use the existing value.
|
||||
*
|
||||
* Such situations typically incur multiple lookups, however this function
|
||||
* avoids them by ensuring the key is added,
|
||||
* returning a pointer to the value so it can be used or initialized by the caller.
|
||||
*
|
||||
* \returns true when the value didn't need to be added.
|
||||
* (when false, the caller _must_ initialize the value).
|
||||
*/
|
||||
bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* A version of #BLI_ghash_ensure_p that allows caller to re-assign the key.
|
||||
* Typically used when the key is to be duplicated.
|
||||
*
|
||||
* \warning Caller _must_ write to \a r_key when returning false.
|
||||
*/
|
||||
bool BLI_ghash_ensure_p_ex(GHash *gh, const void *key, void ***r_key, void ***r_val)
|
||||
ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Remove \a key from \a gh, or return false if the key wasn't found.
|
||||
*
|
||||
* \param key: The key to remove.
|
||||
* \param keyfreefp: Optional callback to free the key.
|
||||
* \param valfreefp: Optional callback to free the value.
|
||||
* \return true if \a key was removed from \a gh.
|
||||
*/
|
||||
bool BLI_ghash_remove(GHash *gh,
|
||||
const void *key,
|
||||
GHashKeyFreeFP keyfreefp,
|
||||
GHashValFreeFP valfreefp);
|
||||
/**
|
||||
* Wraps #BLI_ghash_clear_ex with zero entries reserved.
|
||||
*/
|
||||
void BLI_ghash_clear(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp);
|
||||
/**
|
||||
* Reset \a gh clearing all entries.
|
||||
*
|
||||
* \param keyfreefp: Optional callback to free the key.
|
||||
* \param valfreefp: Optional callback to free the value.
|
||||
* \param nentries_reserve: Optionally reserve the number of members that the hash will hold.
|
||||
*/
|
||||
void BLI_ghash_clear_ex(GHash *gh,
|
||||
GHashKeyFreeFP keyfreefp,
|
||||
GHashValFreeFP valfreefp,
|
||||
unsigned int nentries_reserve);
|
||||
/**
|
||||
* Remove \a key from \a gh, returning the value or NULL if the key wasn't found.
|
||||
*
|
||||
* \param key: The key to remove.
|
||||
* \param keyfreefp: Optional callback to free the key.
|
||||
* \return the value of \a key int \a gh or NULL.
|
||||
*/
|
||||
void *BLI_ghash_popkey(GHash *gh,
|
||||
const void *key,
|
||||
GHashKeyFreeFP keyfreefp) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* \return true if the \a key is in \a gh.
|
||||
*/
|
||||
bool BLI_ghash_haskey(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Remove a random entry from \a gh, returning true
|
||||
* if a key/value pair could be removed, false otherwise.
|
||||
*
|
||||
* \param state: Used for efficient removal.
|
||||
* \param r_key: The removed key.
|
||||
* \param r_val: The removed value.
|
||||
* \return true if there was something to pop, false if ghash was already empty.
|
||||
*/
|
||||
bool BLI_ghash_pop(GHash *gh, GHashIterState *state, void **r_key, void **r_val)
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \return size of the GHash.
|
||||
*/
|
||||
unsigned int BLI_ghash_len(const GHash *gh) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Sets a GHash flag.
|
||||
*/
|
||||
void BLI_ghash_flag_set(GHash *gh, unsigned int flag);
|
||||
/**
|
||||
* Clear a GHash flag.
|
||||
*/
|
||||
void BLI_ghash_flag_clear(GHash *gh, unsigned int flag);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GHash Iterator
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Create a new GHashIterator. The hash table must not be mutated
|
||||
* while the iterator is in use, and the iterator will step exactly
|
||||
* #BLI_ghash_len(gh) times before becoming done.
|
||||
*
|
||||
* \param gh: The GHash to iterate over.
|
||||
* \return Pointer to a new iterator.
|
||||
*/
|
||||
GHashIterator *BLI_ghashIterator_new(GHash *gh) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Init an already allocated GHashIterator. The hash table must not
|
||||
* be mutated while the iterator is in use, and the iterator will
|
||||
* step exactly #BLI_ghash_len(gh) times before becoming done.
|
||||
*
|
||||
* \param ghi: The GHashIterator to initialize.
|
||||
* \param gh: The GHash to iterate over.
|
||||
*/
|
||||
void BLI_ghashIterator_init(GHashIterator *ghi, GHash *gh);
|
||||
/**
|
||||
* Free a GHashIterator.
|
||||
*
|
||||
* \param ghi: The iterator to free.
|
||||
*/
|
||||
void BLI_ghashIterator_free(GHashIterator *ghi);
|
||||
/**
|
||||
* Steps the iterator to the next index.
|
||||
*
|
||||
* \param ghi: The iterator.
|
||||
*/
|
||||
void BLI_ghashIterator_step(GHashIterator *ghi);
|
||||
|
||||
BLI_INLINE void *BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT;
|
||||
BLI_INLINE void *BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT;
|
||||
BLI_INLINE void **BLI_ghashIterator_getValue_p(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT;
|
||||
BLI_INLINE bool BLI_ghashIterator_done(const GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
struct _gh_Entry {
|
||||
void *next, *key, *val;
|
||||
};
|
||||
BLI_INLINE void *BLI_ghashIterator_getKey(GHashIterator *ghi)
|
||||
{
|
||||
return (reinterpret_cast<struct _gh_Entry *>(ghi->curEntry))->key;
|
||||
}
|
||||
BLI_INLINE void *BLI_ghashIterator_getValue(GHashIterator *ghi)
|
||||
{
|
||||
return (reinterpret_cast<struct _gh_Entry *>(ghi->curEntry))->val;
|
||||
}
|
||||
BLI_INLINE void **BLI_ghashIterator_getValue_p(GHashIterator *ghi)
|
||||
{
|
||||
return &(reinterpret_cast<struct _gh_Entry *>(ghi->curEntry))->val;
|
||||
}
|
||||
BLI_INLINE bool BLI_ghashIterator_done(const GHashIterator *ghi)
|
||||
{
|
||||
return !ghi->curEntry;
|
||||
}
|
||||
/* disallow further access */
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC poison _gh_Entry
|
||||
#else
|
||||
# define _gh_Entry void
|
||||
#endif
|
||||
|
||||
#define GHASH_ITER(gh_iter_, ghash_) \
|
||||
for (BLI_ghashIterator_init(&gh_iter_, ghash_); BLI_ghashIterator_done(&gh_iter_) == false; \
|
||||
BLI_ghashIterator_step(&gh_iter_))
|
||||
|
||||
#define GHASH_ITER_INDEX(gh_iter_, ghash_, i_) \
|
||||
for (BLI_ghashIterator_init(&gh_iter_, ghash_), i_ = 0; \
|
||||
BLI_ghashIterator_done(&gh_iter_) == false; \
|
||||
BLI_ghashIterator_step(&gh_iter_), i_++)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GSet Types
|
||||
* A "set" implementation (unordered collection of unique elements).
|
||||
*
|
||||
* Internally this is a 'GHash' without any keys,
|
||||
* which is why this API's are in the same header & source file.
|
||||
* \{ */
|
||||
|
||||
struct GSet;
|
||||
|
||||
typedef GHashHashFP GSetHashFP;
|
||||
typedef GHashCmpFP GSetCmpFP;
|
||||
typedef GHashKeyFreeFP GSetKeyFreeFP;
|
||||
typedef GHashKeyCopyFP GSetKeyCopyFP;
|
||||
|
||||
typedef GHashIterState GSetIterState;
|
||||
|
||||
/** \} */
|
||||
|
||||
/** \name GSet Public API
|
||||
*
|
||||
* Use ghash API to give 'set' functionality
|
||||
* \{ */
|
||||
|
||||
GSet *BLI_gset_new_ex(GSetHashFP hashfp,
|
||||
GSetCmpFP cmpfp,
|
||||
const char *info,
|
||||
unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GSet *BLI_gset_new(GSetHashFP hashfp,
|
||||
GSetCmpFP cmpfp,
|
||||
const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Copy given GSet. Keys are also copied if callback is provided, else pointers remain the same.
|
||||
*/
|
||||
GSet *BLI_gset_copy(const GSet *gs, GSetKeyCopyFP keycopyfp) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
unsigned int BLI_gset_len(const GSet *gs) ATTR_WARN_UNUSED_RESULT;
|
||||
void BLI_gset_flag_set(GSet *gs, unsigned int flag);
|
||||
void BLI_gset_flag_clear(GSet *gs, unsigned int flag);
|
||||
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp);
|
||||
/**
|
||||
* Adds the key to the set (no checks for unique keys!).
|
||||
* Matching #BLI_ghash_insert
|
||||
*/
|
||||
void BLI_gset_insert(GSet *gs, void *key);
|
||||
/**
|
||||
* A version of BLI_gset_insert which checks first if the key is in the set.
|
||||
* \returns true if a new key has been added.
|
||||
*
|
||||
* \note GHash has no equivalent to this because typically the value would be different.
|
||||
*/
|
||||
bool BLI_gset_add(GSet *gs, void *key);
|
||||
/**
|
||||
* Set counterpart to #BLI_ghash_ensure_p_ex.
|
||||
* similar to BLI_gset_add, except it returns the key pointer.
|
||||
*
|
||||
* \warning Caller _must_ write to \a r_key when returning false.
|
||||
*/
|
||||
bool BLI_gset_ensure_p_ex(GSet *gs, const void *key, void ***r_key);
|
||||
/**
|
||||
* Adds the key to the set (duplicates are managed).
|
||||
* Matching #BLI_ghash_reinsert
|
||||
*
|
||||
* \returns true if a new key has been added.
|
||||
*/
|
||||
bool BLI_gset_reinsert(GSet *gs, void *key, GSetKeyFreeFP keyfreefp);
|
||||
/**
|
||||
* Replaces the key to the set if it's found.
|
||||
* Matching #BLI_ghash_replace_key
|
||||
*
|
||||
* \returns The old key or NULL if not found.
|
||||
*/
|
||||
void *BLI_gset_replace_key(GSet *gs, void *key);
|
||||
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Remove a random entry from \a gs, returning true if a key could be removed, false otherwise.
|
||||
*
|
||||
* \param state: Used for efficient removal.
|
||||
* \param r_key: The removed key.
|
||||
* \return true if there was something to pop, false if gset was already empty.
|
||||
*/
|
||||
bool BLI_gset_pop(GSet *gs, GSetIterState *state, void **r_key) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
bool BLI_gset_remove(GSet *gs, const void *key, GSetKeyFreeFP keyfreefp);
|
||||
void BLI_gset_clear_ex(GSet *gs, GSetKeyFreeFP keyfreefp, unsigned int nentries_reserve);
|
||||
void BLI_gset_clear(GSet *gs, GSetKeyFreeFP keyfreefp);
|
||||
|
||||
/* When set's are used for key & value. */
|
||||
/**
|
||||
* Returns the pointer to the key if it's found.
|
||||
*/
|
||||
void *BLI_gset_lookup(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Returns the pointer to the key if it's found, removing it from the GSet.
|
||||
* \note Caller must handle freeing.
|
||||
*/
|
||||
void *BLI_gset_pop_key(GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GSet Iterator
|
||||
* \{ */
|
||||
|
||||
/* Rely on inline API for now. */
|
||||
|
||||
/** Use a GSet specific type so we can cast but compiler sees as different */
|
||||
struct GSetIterator {
|
||||
GHashIterator _ghi
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
__attribute__((deprecated))
|
||||
#endif
|
||||
;
|
||||
};
|
||||
|
||||
BLI_INLINE GSetIterator *BLI_gsetIterator_new(GSet *gs)
|
||||
{
|
||||
return reinterpret_cast<GSetIterator *>(BLI_ghashIterator_new(reinterpret_cast<GHash *>(gs)));
|
||||
}
|
||||
BLI_INLINE void BLI_gsetIterator_init(GSetIterator *gsi, GSet *gs)
|
||||
{
|
||||
BLI_ghashIterator_init(reinterpret_cast<GHashIterator *>(gsi), reinterpret_cast<GHash *>(gs));
|
||||
}
|
||||
BLI_INLINE void BLI_gsetIterator_free(GSetIterator *gsi)
|
||||
{
|
||||
BLI_ghashIterator_free(reinterpret_cast<GHashIterator *>(gsi));
|
||||
}
|
||||
BLI_INLINE void *BLI_gsetIterator_getKey(GSetIterator *gsi)
|
||||
{
|
||||
return BLI_ghashIterator_getKey(reinterpret_cast<GHashIterator *>(gsi));
|
||||
}
|
||||
BLI_INLINE void BLI_gsetIterator_step(GSetIterator *gsi)
|
||||
{
|
||||
BLI_ghashIterator_step(reinterpret_cast<GHashIterator *>(gsi));
|
||||
}
|
||||
BLI_INLINE bool BLI_gsetIterator_done(const GSetIterator *gsi)
|
||||
{
|
||||
return BLI_ghashIterator_done(reinterpret_cast<const GHashIterator *>(gsi));
|
||||
}
|
||||
|
||||
#define GSET_ITER(gs_iter_, gset_) \
|
||||
for (BLI_gsetIterator_init(&gs_iter_, gset_); BLI_gsetIterator_done(&gs_iter_) == false; \
|
||||
BLI_gsetIterator_step(&gs_iter_))
|
||||
|
||||
#define GSET_ITER_INDEX(gs_iter_, gset_, i_) \
|
||||
for (BLI_gsetIterator_init(&gs_iter_, gset_), i_ = 0; \
|
||||
BLI_gsetIterator_done(&gs_iter_) == false; \
|
||||
BLI_gsetIterator_step(&gs_iter_), i_++)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GHash/GSet Debugging API's
|
||||
* \{ */
|
||||
|
||||
/* For testing, debugging only */
|
||||
#ifdef GHASH_INTERNAL_API
|
||||
/**
|
||||
* \return number of buckets in the GHash.
|
||||
*/
|
||||
int BLI_ghash_buckets_len(const GHash *gh);
|
||||
int BLI_gset_buckets_len(const GSet *gs);
|
||||
|
||||
/**
|
||||
* Measure how well the hash function performs (1.0 is approx as good as random distribution),
|
||||
* and return a few other stats like load,
|
||||
* variance of the distribution of the entries in the buckets, etc.
|
||||
*
|
||||
* Smaller is better!
|
||||
*/
|
||||
double BLI_ghash_calc_quality_ex(const GHash *gh,
|
||||
double *r_load,
|
||||
double *r_variance,
|
||||
double *r_prop_empty_buckets,
|
||||
double *r_prop_overloaded_buckets,
|
||||
int *r_biggest_bucket);
|
||||
double BLI_gset_calc_quality_ex(const GSet *gs,
|
||||
double *r_load,
|
||||
double *r_variance,
|
||||
double *r_prop_empty_buckets,
|
||||
double *r_prop_overloaded_buckets,
|
||||
int *r_biggest_bucket);
|
||||
double BLI_ghash_calc_quality(const GHash *gh);
|
||||
double BLI_gset_calc_quality(const GSet *gs);
|
||||
#endif /* GHASH_INTERNAL_API */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GHash/GSet Macros
|
||||
* \{ */
|
||||
|
||||
#define GHASH_FOREACH_BEGIN(type, var, what) \
|
||||
do { \
|
||||
GHashIterator gh_iter##var; \
|
||||
GHASH_ITER (gh_iter##var, what) { \
|
||||
type var = (type)(BLI_ghashIterator_getValue(&gh_iter##var));
|
||||
|
||||
#define GHASH_FOREACH_END() \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define GSET_FOREACH_BEGIN(type, var, what) \
|
||||
do { \
|
||||
GSetIterator gh_iter##var; \
|
||||
GSET_ITER (gh_iter##var, what) { \
|
||||
type var = (type)(BLI_gsetIterator_getKey(&gh_iter##var));
|
||||
|
||||
#define GSET_FOREACH_END() \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GHash/GSet Utils
|
||||
*
|
||||
* Defined in `BLI_ghash_utils.cc`
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Callbacks for GHash (`BLI_ghashutil_`)
|
||||
*
|
||||
* \note '_p' suffix denotes void pointer arg,
|
||||
* so we can have functions that take correctly typed args too.
|
||||
*/
|
||||
|
||||
unsigned int BLI_ghashutil_ptrhash(const void *key);
|
||||
bool BLI_ghashutil_ptrcmp(const void *a, const void *b);
|
||||
|
||||
/**
|
||||
* This function implements the widely used `djb` hash apparently posted
|
||||
* by Daniel Bernstein to `comp.lang.c` some time ago. The 32 bit
|
||||
* unsigned hash value starts at 5381 and for each byte 'c' in the
|
||||
* string, is updated: `hash = hash * 33 + c`.
|
||||
* This function uses the signed value of each byte.
|
||||
*
|
||||
* NOTE: this is the same hash method that glib 2.34.0 uses.
|
||||
*/
|
||||
unsigned int BLI_ghashutil_strhash_n(const char *key, size_t n);
|
||||
#define BLI_ghashutil_strhash(key) \
|
||||
(CHECK_TYPE_ANY(key, char *, const char *), BLI_ghashutil_strhash_p(key))
|
||||
unsigned int BLI_ghashutil_strhash_p(const void *ptr);
|
||||
unsigned int BLI_ghashutil_strhash_p_murmur(const void *ptr);
|
||||
bool BLI_ghashutil_strcmp(const void *a, const void *b);
|
||||
|
||||
#define BLI_ghashutil_inthash(key) \
|
||||
(CHECK_TYPE_ANY(&(key), int *, const int *), BLI_ghashutil_uinthash((unsigned int)key))
|
||||
unsigned int BLI_ghashutil_uinthash(unsigned int key);
|
||||
unsigned int BLI_ghashutil_inthash_p(const void *ptr);
|
||||
unsigned int BLI_ghashutil_inthash_p_murmur(const void *ptr);
|
||||
unsigned int BLI_ghashutil_inthash_p_simple(const void *ptr);
|
||||
bool BLI_ghashutil_intcmp(const void *a, const void *b);
|
||||
|
||||
size_t BLI_ghashutil_combine_hash(size_t hash_a, size_t hash_b);
|
||||
|
||||
unsigned int BLI_ghashutil_uinthash_v4(const unsigned int key[4]);
|
||||
#define BLI_ghashutil_inthash_v4(key) \
|
||||
(CHECK_TYPE_ANY(key, int *, const int *), BLI_ghashutil_uinthash_v4((const unsigned int *)key))
|
||||
#define BLI_ghashutil_inthash_v4_p ((GSetHashFP)BLI_ghashutil_uinthash_v4)
|
||||
#define BLI_ghashutil_uinthash_v4_p ((GSetHashFP)BLI_ghashutil_uinthash_v4)
|
||||
unsigned int BLI_ghashutil_uinthash_v4_murmur(const unsigned int key[4]);
|
||||
#define BLI_ghashutil_inthash_v4_murmur(key) \
|
||||
(CHECK_TYPE_ANY(key, int *, const int *), \
|
||||
BLI_ghashutil_uinthash_v4_murmur((const unsigned int *)key))
|
||||
#define BLI_ghashutil_inthash_v4_p_murmur ((GSetHashFP)BLI_ghashutil_uinthash_v4_murmur)
|
||||
#define BLI_ghashutil_uinthash_v4_p_murmur ((GSetHashFP)BLI_ghashutil_uinthash_v4_murmur)
|
||||
bool BLI_ghashutil_uinthash_v4_cmp(const void *a, const void *b);
|
||||
#define BLI_ghashutil_inthash_v4_cmp BLI_ghashutil_uinthash_v4_cmp
|
||||
|
||||
struct GHashPair {
|
||||
const void *first;
|
||||
const void *second;
|
||||
};
|
||||
|
||||
GHashPair *BLI_ghashutil_pairalloc(const void *first, const void *second);
|
||||
unsigned int BLI_ghashutil_pairhash(const void *ptr);
|
||||
bool BLI_ghashutil_paircmp(const void *a, const void *b);
|
||||
void BLI_ghashutil_pairfree(void *ptr);
|
||||
|
||||
/**
|
||||
* Wrapper GHash Creation Functions
|
||||
*/
|
||||
|
||||
GHash *BLI_ghash_ptr_new_ex(const char *info,
|
||||
unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GHash *BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GHash *BLI_ghash_str_new_ex(const char *info,
|
||||
unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GHash *BLI_ghash_str_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GHash *BLI_ghash_int_new_ex(const char *info,
|
||||
unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GHash *BLI_ghash_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GHash *BLI_ghash_pair_new_ex(const char *info,
|
||||
unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GHash *BLI_ghash_pair_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
GSet *BLI_gset_ptr_new_ex(const char *info,
|
||||
unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GSet *BLI_gset_ptr_new(const char *info);
|
||||
GSet *BLI_gset_str_new_ex(const char *info,
|
||||
unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GSet *BLI_gset_str_new(const char *info);
|
||||
GSet *BLI_gset_pair_new_ex(const char *info,
|
||||
unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GSet *BLI_gset_pair_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GSet *BLI_gset_int_new_ex(const char *info,
|
||||
unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
GSet *BLI_gset_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
44
blender-5.2.0/source/blender/blenlib/BLI_gsqueue.h
Normal file
44
blender-5.2.0/source/blender/blenlib/BLI_gsqueue.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GSQueue;
|
||||
|
||||
GSQueue *BLI_gsqueue_new(size_t elem_size);
|
||||
/**
|
||||
* Returns true if the queue is empty, false otherwise.
|
||||
*/
|
||||
bool BLI_gsqueue_is_empty(const GSQueue *queue);
|
||||
size_t BLI_gsqueue_len(const GSQueue *queue);
|
||||
/**
|
||||
* Retrieves and removes the first element from the queue.
|
||||
* The value is copies to \a r_item, which must be at least \a elem_size bytes.
|
||||
*
|
||||
* Does not reduce amount of allocated memory.
|
||||
*/
|
||||
void BLI_gsqueue_pop(GSQueue *queue, void *r_item);
|
||||
/**
|
||||
* Copies the source value onto the end of the queue
|
||||
*
|
||||
* \note This copies #GSQueue.elem_size bytes from \a item,
|
||||
* (the pointer itself is not stored).
|
||||
*
|
||||
* \param item: source data to be copied to the queue.
|
||||
*/
|
||||
void BLI_gsqueue_push(GSQueue *queue, const void *item);
|
||||
/**
|
||||
* Free the queue's data and the queue itself.
|
||||
*/
|
||||
void BLI_gsqueue_free(GSQueue *queue);
|
||||
|
||||
} // namespace blender
|
||||
110
blender-5.2.0/source/blender/blenlib/BLI_hash.h
Normal file
110
blender-5.2.0/source/blender/blenlib/BLI_hash.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Jenkins Lookup3 Hash Functions.
|
||||
* Source: http://burtleburtle.net/bob/c/lookup3.c
|
||||
*/
|
||||
|
||||
#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
|
||||
#define final(a, b, c) \
|
||||
{ \
|
||||
c ^= b; \
|
||||
c -= rot(b, 14); \
|
||||
a ^= c; \
|
||||
a -= rot(c, 11); \
|
||||
b ^= a; \
|
||||
b -= rot(a, 25); \
|
||||
c ^= b; \
|
||||
c -= rot(b, 16); \
|
||||
a ^= c; \
|
||||
a -= rot(c, 4); \
|
||||
b ^= a; \
|
||||
b -= rot(a, 14); \
|
||||
c ^= b; \
|
||||
c -= rot(b, 24); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
BLI_INLINE unsigned int BLI_hash_int_3d(unsigned int kx, unsigned int ky, unsigned int kz)
|
||||
{
|
||||
unsigned int a, b, c;
|
||||
a = b = c = 0xdeadbeef + (3 << 2) + 13;
|
||||
|
||||
c += kz;
|
||||
b += ky;
|
||||
a += kx;
|
||||
final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky)
|
||||
{
|
||||
unsigned int a, b, c;
|
||||
|
||||
a = b = c = 0xdeadbeef + (2 << 2) + 13;
|
||||
a += kx;
|
||||
b += ky;
|
||||
|
||||
final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
#undef final
|
||||
#undef rot
|
||||
|
||||
BLI_INLINE unsigned int BLI_hash_string(const char *str)
|
||||
{
|
||||
unsigned int i = 0, c;
|
||||
|
||||
while ((c = *str++)) {
|
||||
i = i * 37 + c;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
BLI_INLINE float BLI_hash_int_2d_to_float(uint32_t kx, uint32_t ky)
|
||||
{
|
||||
return float(BLI_hash_int_2d(kx, ky)) / float(0xFFFFFFFFu);
|
||||
}
|
||||
|
||||
BLI_INLINE float BLI_hash_int_3d_to_float(uint32_t kx, uint32_t ky, uint32_t kz)
|
||||
{
|
||||
return float(BLI_hash_int_3d(kx, ky, kz)) / float(0xFFFFFFFFu);
|
||||
}
|
||||
|
||||
BLI_INLINE unsigned int BLI_hash_int(unsigned int k)
|
||||
{
|
||||
return BLI_hash_int_2d(k, 0);
|
||||
}
|
||||
|
||||
BLI_INLINE float BLI_hash_int_01(unsigned int k)
|
||||
{
|
||||
return float(BLI_hash_int(k)) * (1.0f / float(0xFFFFFFFF));
|
||||
}
|
||||
|
||||
BLI_INLINE void BLI_hash_pointer_to_color(const void *ptr, int *r, int *g, int *b)
|
||||
{
|
||||
size_t val = reinterpret_cast<size_t>(ptr);
|
||||
const size_t hash_a = BLI_hash_int(val & 0x0000ffff);
|
||||
const size_t hash_b = BLI_hash_int(uint((val & 0xffff0000) >> 16));
|
||||
const size_t hash = hash_a ^ (hash_b + 0x9e3779b9 + (hash_a << 6) + (hash_a >> 2));
|
||||
*r = (hash & 0xff0000) >> 16;
|
||||
*g = (hash & 0x00ff00) >> 8;
|
||||
*b = hash & 0x0000ff;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
287
blender-5.2.0/source/blender/blenlib/BLI_hash.hh
Normal file
287
blender-5.2.0/source/blender/blenlib/BLI_hash.hh
Normal file
@@ -0,0 +1,287 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A specialization of `DefaultHash<T>` provides a hash function for values of type T.
|
||||
* This hash function is used by default in hash table implementations in blenlib.
|
||||
*
|
||||
* The actual hash function is in the `operator()` method of `DefaultHash<T>`. The following code
|
||||
* computes the hash of some value using DefaultHash.
|
||||
*
|
||||
* T value = ...;
|
||||
* DefaultHash<T> hash_function;
|
||||
* uint32_t hash = hash_function(value);
|
||||
*
|
||||
* Hash table implementations like Set support heterogeneous key lookups. That means that
|
||||
* one can do a lookup with a key of type A in a hash table that stores keys of type B. This is
|
||||
* commonly done when B is std::string, because the conversion from e.g. a #StringRef to
|
||||
* std::string can be costly and is unnecessary. To make this work, values of type A and B that
|
||||
* compare equal have to have the same hash value. This is achieved by defining potentially
|
||||
* multiple `operator()` in a specialization of #DefaultHash. All those methods have to compute the
|
||||
* same hash for values that compare equal.
|
||||
*
|
||||
* The computed hash is an unsigned 64 bit integer. Ideally, the hash function would generate
|
||||
* uniformly random hash values for a set of keys. However, in many cases trivial hash functions
|
||||
* are faster and produce a good enough distribution. In general it is better when more information
|
||||
* is in the lower bits of the hash. By choosing a good probing strategy, the effects of a bad hash
|
||||
* function are less noticeable though. In this context a good probing strategy is one that takes
|
||||
* all bits of the hash into account eventually. One has to check on a case by case basis to see if
|
||||
* a better but more expensive or trivial hash function works better.
|
||||
*
|
||||
* There are three main ways to provide a hash table implementation with a custom hash function.
|
||||
*
|
||||
* - When you want to provide a default hash function for your own custom type: Add a `hash()`
|
||||
* member function to it. The function should return `uint64_t` and take no arguments. This
|
||||
* method will be called by the default implementation of #DefaultHash. It will automatically be
|
||||
* used by hash table implementations.
|
||||
*
|
||||
* - When you want to provide a default hash function for a type that you cannot modify: Add a new
|
||||
* specialization to the #DefaultHash struct. This can be done by writing code like below in
|
||||
* either global or `blender` namespace.
|
||||
*
|
||||
* template<> struct DefaultHash<TheType> {
|
||||
* uint64_t operator()(const TheType &value) const {
|
||||
* return ...;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* - When you want to provide a different hash function for a type that already has a default hash
|
||||
* function: Implement a struct like the one below and pass it as template parameter to the hash
|
||||
* table explicitly.
|
||||
*
|
||||
* struct MyCustomHash {
|
||||
* uint64_t operator()(const TheType &value) const {
|
||||
* return ...;
|
||||
* }
|
||||
* };
|
||||
*/
|
||||
|
||||
#include <bit>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "BLI_hash_fwd.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* If there is no other specialization of #DefaultHash for a given type, look for a hash function
|
||||
* on the type itself. Implementing a `hash()` method on a type is often significantly easier than
|
||||
* specializing #DefaultHash.
|
||||
*
|
||||
* To support heterogeneous lookup, a type can also implement a static `hash_as(const OtherType &)`
|
||||
* function.
|
||||
*
|
||||
* In the case of an enum type, the default hash is just to cast the enum value to an integer.
|
||||
*/
|
||||
template<typename T> struct DefaultHash {
|
||||
constexpr uint64_t operator()(const T &value) const
|
||||
{
|
||||
if constexpr (std::is_enum_v<T>) {
|
||||
/* For enums use the value as hash directly. */
|
||||
return uint64_t(value);
|
||||
}
|
||||
else {
|
||||
/* Try to call the `hash()` function on the value. */
|
||||
/* If this results in a compiler error, no hash function for the type has been found. */
|
||||
return value.hash();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U> constexpr uint64_t operator()(const U &value) const
|
||||
{
|
||||
/* Try calling the static `T::hash_as(value)` function with the given value. The returned hash
|
||||
* should be "compatible" with `T::hash()`. Usually that means that if `value` is converted to
|
||||
* `T` its hash does not change. */
|
||||
/* If this results in a compiler error, no hash function for the heterogeneous lookup has been
|
||||
* found. */
|
||||
return T::hash_as(value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Use the same hash function for const and non const variants of a type.
|
||||
*/
|
||||
template<typename T> struct DefaultHash<const T> {
|
||||
constexpr uint64_t operator()(const T &value) const
|
||||
{
|
||||
return DefaultHash<T>{}(value);
|
||||
}
|
||||
};
|
||||
|
||||
#define TRIVIAL_DEFAULT_INT_HASH(TYPE) \
|
||||
template<> struct DefaultHash<TYPE> { \
|
||||
constexpr uint64_t operator()(TYPE value) const \
|
||||
{ \
|
||||
return uint64_t(value); \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* We cannot make any assumptions about the distribution of keys, so use a trivial hash function by
|
||||
* default. The default probing strategy is designed to take all bits of the hash into account
|
||||
* to avoid worst case behavior when the lower bits are all zero. Special hash functions can be
|
||||
* implemented when more knowledge about a specific key distribution is available.
|
||||
*/
|
||||
TRIVIAL_DEFAULT_INT_HASH(int8_t);
|
||||
TRIVIAL_DEFAULT_INT_HASH(uint8_t);
|
||||
TRIVIAL_DEFAULT_INT_HASH(int16_t);
|
||||
TRIVIAL_DEFAULT_INT_HASH(uint16_t);
|
||||
TRIVIAL_DEFAULT_INT_HASH(int32_t);
|
||||
TRIVIAL_DEFAULT_INT_HASH(uint32_t);
|
||||
TRIVIAL_DEFAULT_INT_HASH(int64_t);
|
||||
TRIVIAL_DEFAULT_INT_HASH(uint64_t);
|
||||
|
||||
/**
|
||||
* One should try to avoid using floats as keys in hash tables, but sometimes it is convenient.
|
||||
*/
|
||||
template<> struct DefaultHash<float> {
|
||||
constexpr uint64_t operator()(const float value) const
|
||||
{
|
||||
/* Make sure +0 and -0 hash to the same value. */
|
||||
if (value == 0.0f) {
|
||||
return 0;
|
||||
}
|
||||
/* Explicit `uint64_t` cast to suppress CPPCHECK warning. */
|
||||
return uint64_t(std::bit_cast<uint32_t>(value));
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DefaultHash<double> {
|
||||
constexpr uint64_t operator()(const double value) const
|
||||
{
|
||||
/* Make sure +0 and -0 hash to the same value. */
|
||||
if (value == 0.0) {
|
||||
return 0;
|
||||
}
|
||||
return std::bit_cast<uint64_t>(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DefaultHash<bool> {
|
||||
constexpr uint64_t operator()(bool value) const
|
||||
{
|
||||
return uint64_t((value != false) * 1298191);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr uint64_t hash_string(StringRef str)
|
||||
{
|
||||
uint64_t hash = 5381;
|
||||
for (char c : str) {
|
||||
hash = hash * 33 + c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
template<> struct DefaultHash<std::string> {
|
||||
/**
|
||||
* Take a #StringRef as parameter to support heterogeneous lookups in hash table implementations
|
||||
* when std::string is used as key.
|
||||
*/
|
||||
constexpr uint64_t operator()(StringRef value) const
|
||||
{
|
||||
return hash_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DefaultHash<StringRef> {
|
||||
constexpr uint64_t operator()(StringRef value) const
|
||||
{
|
||||
return hash_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DefaultHash<StringRefNull> {
|
||||
constexpr uint64_t operator()(StringRef value) const
|
||||
{
|
||||
return hash_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DefaultHash<std::string_view> {
|
||||
constexpr uint64_t operator()(StringRef value) const
|
||||
{
|
||||
return hash_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* While we cannot guarantee that the lower 4 bits of a pointer are zero, it is often the case.
|
||||
*/
|
||||
template<typename T> struct DefaultHash<T *> {
|
||||
constexpr uint64_t operator()(const T *value) const
|
||||
{
|
||||
uintptr_t ptr = uintptr_t(value);
|
||||
uint64_t hash = uint64_t(ptr >> 4);
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
namespace blenlib_detail {
|
||||
static constexpr std::array<uint64_t, 5> default_hash_factors = {
|
||||
19349669, 83492791, 3632623, 8789800933, 7235126189};
|
||||
|
||||
template<size_t... I, typename... Args>
|
||||
constexpr uint64_t get_default_hash_array(std::index_sequence<I...> /*indices*/,
|
||||
const Args &...args)
|
||||
{
|
||||
static_assert(sizeof...(Args) == sizeof...(I));
|
||||
static_assert(sizeof...(Args) <= default_hash_factors.size());
|
||||
return (0 ^ ... ^ (default_hash_factors[I] * DefaultHash<std::decay_t<Args>>{}(args)));
|
||||
}
|
||||
|
||||
} // namespace blenlib_detail
|
||||
|
||||
template<typename T, typename... Args>
|
||||
constexpr uint64_t get_default_hash(const T &v, const Args &...args)
|
||||
{
|
||||
return DefaultHash<std::decay_t<T>>{}(v) ^
|
||||
blenlib_detail::get_default_hash_array(std::make_index_sequence<sizeof...(Args)>(),
|
||||
args...);
|
||||
}
|
||||
|
||||
/** Support hashing different kinds of pointer types. */
|
||||
template<typename T> struct PointerHashes {
|
||||
template<typename U> constexpr uint64_t operator()(const U &value) const
|
||||
{
|
||||
return get_default_hash(&*value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct DefaultHash<std::unique_ptr<T>> : public PointerHashes<T> {};
|
||||
template<typename T> struct DefaultHash<std::shared_ptr<T>> : public PointerHashes<T> {};
|
||||
|
||||
template<typename T> struct DefaultHash<std::reference_wrapper<T>> {
|
||||
constexpr uint64_t operator()(const std::reference_wrapper<T> &value) const
|
||||
{
|
||||
return get_default_hash(value.get());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T1, typename T2> struct DefaultHash<std::pair<T1, T2>> {
|
||||
constexpr uint64_t operator()(const std::pair<T1, T2> &value) const
|
||||
{
|
||||
return get_default_hash(value.first, value.second);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Special overload for function pointers to avoid adding const to them which causes a warning with
|
||||
* MSVC.
|
||||
*/
|
||||
template<typename Ret, typename... Args> struct DefaultHash<Ret (*)(Args...)> {
|
||||
constexpr uint64_t operator()(Ret (*fn)(Args...)) const
|
||||
{
|
||||
return get_default_hash(reinterpret_cast<const void *>(fn));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
14
blender-5.2.0/source/blender/blenlib/BLI_hash_fwd.hh
Normal file
14
blender-5.2.0/source/blender/blenlib/BLI_hash_fwd.hh
Normal file
@@ -0,0 +1,14 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace blender {
|
||||
|
||||
template<typename T, typename... Args>
|
||||
constexpr uint64_t get_default_hash(const T &v, const Args &...args);
|
||||
|
||||
} // namespace blender
|
||||
33
blender-5.2.0/source/blender/blenlib/BLI_hash_md5.hh
Normal file
33
blender-5.2.0/source/blender/blenlib/BLI_hash_md5.hh
Normal file
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Compute MD5 message digest for 'len' bytes beginning at 'buffer'.
|
||||
* The result is always in little endian byte order,
|
||||
* so that a byte-wise output yields to the wanted ASCII representation of the message digest.
|
||||
*/
|
||||
void *BLI_hash_md5_buffer(const char *buffer, size_t len, void *resblock);
|
||||
|
||||
/**
|
||||
* Compute MD5 message digest for bytes read from 'stream'.
|
||||
* The resulting message digest number will be written into the 16 bytes beginning at 'resblock'.
|
||||
* \return Non-zero if an error occurred.
|
||||
*/
|
||||
int BLI_hash_md5_stream(FILE *stream, void *resblock);
|
||||
|
||||
char *BLI_hash_md5_to_hexdigest(const void *resblock, char r_hex_digest[33]);
|
||||
|
||||
} // namespace blender
|
||||
35
blender-5.2.0/source/blender/blenlib/BLI_hash_mm2a.hh
Normal file
35
blender-5.2.0/source/blender/blenlib/BLI_hash_mm2a.hh
Normal file
@@ -0,0 +1,35 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BLI_HashMurmur2A {
|
||||
uint32_t hash;
|
||||
uint32_t tail;
|
||||
uint32_t count;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
void BLI_hash_mm2a_init(BLI_HashMurmur2A *mm2, uint32_t seed);
|
||||
|
||||
void BLI_hash_mm2a_add(BLI_HashMurmur2A *mm2, const unsigned char *data, size_t len);
|
||||
|
||||
void BLI_hash_mm2a_add_int(BLI_HashMurmur2A *mm2, int data);
|
||||
|
||||
uint32_t BLI_hash_mm2a_end(BLI_HashMurmur2A *mm2);
|
||||
|
||||
/**
|
||||
* Non-incremental version, quicker for small keys.
|
||||
*/
|
||||
uint32_t BLI_hash_mm2(const unsigned char *data, size_t len, uint32_t seed);
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/blenlib/BLI_hash_mm3.hh
Normal file
17
blender-5.2.0/source/blender/blenlib/BLI_hash_mm3.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
uint32_t BLI_hash_mm3(const unsigned char *data, size_t len, uint32_t seed);
|
||||
|
||||
} // namespace blender
|
||||
322
blender-5.2.0/source/blender/blenlib/BLI_hash_tables.hh
Normal file
322
blender-5.2.0/source/blender/blenlib/BLI_hash_tables.hh
Normal file
@@ -0,0 +1,322 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* This file contains code that can be shared between different hash table implementations.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Constexpr Utility Functions
|
||||
*
|
||||
* Those should eventually be de-duplicated with functions in BLI_math_base.h.
|
||||
* \{ */
|
||||
|
||||
template<typename IntT> constexpr IntT ceil_division(const IntT x, const IntT y)
|
||||
{
|
||||
BLI_assert(x >= 0);
|
||||
BLI_assert(y >= 0);
|
||||
return x / y + ((x % y) != 0);
|
||||
}
|
||||
|
||||
template<typename IntT> constexpr IntT floor_division(const IntT x, const IntT y)
|
||||
{
|
||||
BLI_assert(x >= 0);
|
||||
BLI_assert(y >= 0);
|
||||
return x / y;
|
||||
}
|
||||
|
||||
constexpr int64_t ceil_division_by_fraction(const int64_t x,
|
||||
const int64_t numerator,
|
||||
const int64_t denominator)
|
||||
{
|
||||
return int64_t(ceil_division(uint64_t(x) * uint64_t(denominator), uint64_t(numerator)));
|
||||
}
|
||||
|
||||
constexpr int64_t floor_multiplication_with_fraction(const int64_t x,
|
||||
const int64_t numerator,
|
||||
const int64_t denominator)
|
||||
{
|
||||
return int64_t((uint64_t(x) * uint64_t(numerator) / uint64_t(denominator)));
|
||||
}
|
||||
|
||||
constexpr int64_t total_slot_amount_for_usable_slots(const int64_t min_usable_slots,
|
||||
const int64_t max_load_factor_numerator,
|
||||
const int64_t max_load_factor_denominator)
|
||||
{
|
||||
return power_of_2_max(ceil_division_by_fraction(
|
||||
min_usable_slots, max_load_factor_numerator, max_load_factor_denominator));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Load Factor
|
||||
*
|
||||
* This is an abstraction for a fractional load factor. The hash table using this class is assumed
|
||||
* to use arrays with a size that is a power of two.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
class LoadFactor {
|
||||
private:
|
||||
uint8_t numerator_;
|
||||
uint8_t denominator_;
|
||||
|
||||
public:
|
||||
constexpr LoadFactor(uint8_t numerator, uint8_t denominator)
|
||||
: numerator_(numerator), denominator_(denominator)
|
||||
{
|
||||
BLI_assert(numerator > 0);
|
||||
BLI_assert(numerator < denominator);
|
||||
}
|
||||
|
||||
constexpr void compute_total_and_usable_slots(int64_t min_total_slots,
|
||||
int64_t min_usable_slots,
|
||||
int64_t *r_total_slots,
|
||||
int64_t *r_usable_slots) const
|
||||
{
|
||||
BLI_assert(is_power_of_2(int(min_total_slots)));
|
||||
|
||||
int64_t total_slots = this->compute_total_slots(min_usable_slots, numerator_, denominator_);
|
||||
total_slots = std::max(total_slots, min_total_slots);
|
||||
const int64_t usable_slots = floor_multiplication_with_fraction(
|
||||
total_slots, numerator_, denominator_);
|
||||
BLI_assert(min_usable_slots <= usable_slots);
|
||||
|
||||
*r_total_slots = total_slots;
|
||||
*r_usable_slots = usable_slots;
|
||||
}
|
||||
|
||||
static constexpr int64_t compute_total_slots(int64_t min_usable_slots,
|
||||
uint8_t numerator,
|
||||
uint8_t denominator)
|
||||
{
|
||||
return total_slot_amount_for_usable_slots(min_usable_slots, numerator, denominator);
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Intrusive Key Info
|
||||
*
|
||||
* A hash table slot has to maintain state about whether the slot is empty, occupied or removed.
|
||||
* Usually, this state information is stored in its own variable. While it only needs two bits in
|
||||
* theory, in practice often 4 or 8 bytes are used, due to alignment requirements.
|
||||
*
|
||||
* One solution to deal with this problem is to embed the state information in the key. That means,
|
||||
* two values of the key type are selected to indicate whether the slot is empty or removed.
|
||||
*
|
||||
* The classes below tell a slot implementation which special key values it can use. They can be
|
||||
* used as #KeyInfo in slot types like #IntrusiveSetSlot and #IntrusiveMapSlot.
|
||||
*
|
||||
* A #KeyInfo type has to implement a couple of static methods that are descried in
|
||||
* #TemplatedKeyInfo.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* The template arguments EmptyValue and RemovedValue define which special are used. This can be
|
||||
* used when a hash table has integer keys and there are two specific integers that will never be
|
||||
* used as keys.
|
||||
*/
|
||||
template<typename Key, Key EmptyValue, Key RemovedValue> struct TemplatedKeyInfo {
|
||||
/**
|
||||
* Get the value that indicates that the slot is empty. This is used to indicate new slots.
|
||||
*/
|
||||
static Key get_empty()
|
||||
{
|
||||
return EmptyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the given key so that it represents a removed slot.
|
||||
*/
|
||||
static void remove(Key &key)
|
||||
{
|
||||
key = RemovedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true, when the given key indicates that the slot is empty.
|
||||
*/
|
||||
static bool is_empty(const Key &key)
|
||||
{
|
||||
return key == EmptyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true, when the given key indicates that the slot is removed.
|
||||
*/
|
||||
static bool is_removed(const Key &key)
|
||||
{
|
||||
return key == RemovedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true, when the key is valid, i.e. it can be contained in an occupied slot.
|
||||
*/
|
||||
static bool is_not_empty_or_removed(const Key &key)
|
||||
{
|
||||
return key != EmptyValue && key != RemovedValue;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* `0xffff...ffff` indicates an empty slot.
|
||||
* `0xffff...fffe` indicates a removed slot.
|
||||
*
|
||||
* Those specific values are used, because with them a single comparison is enough to check whether
|
||||
* a slot is occupied. The keys `0x0000...0000` and `0x0000...0001` also satisfy this constraint.
|
||||
* However, nullptr is much more likely to be used as valid key.
|
||||
*/
|
||||
template<typename Pointer> struct PointerKeyInfo {
|
||||
static Pointer get_empty()
|
||||
{
|
||||
return Pointer(UINTPTR_MAX);
|
||||
}
|
||||
|
||||
static void remove(Pointer &pointer)
|
||||
{
|
||||
pointer = Pointer(UINTPTR_MAX - 1);
|
||||
}
|
||||
|
||||
static bool is_empty(Pointer pointer)
|
||||
{
|
||||
return uintptr_t(pointer) == UINTPTR_MAX;
|
||||
}
|
||||
|
||||
static bool is_removed(Pointer pointer)
|
||||
{
|
||||
return uintptr_t(pointer) == UINTPTR_MAX - 1;
|
||||
}
|
||||
|
||||
static bool is_not_empty_or_removed(Pointer pointer)
|
||||
{
|
||||
return uintptr_t(pointer) < UINTPTR_MAX - 1;
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Hash Table Stats
|
||||
*
|
||||
* A utility class that makes it easier for hash table implementations to provide statistics to the
|
||||
* developer. These statistics can be helpful when trying to figure out why a hash table is slow.
|
||||
*
|
||||
* To use this utility, a hash table has to implement various methods, that are mentioned below.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
class HashTableStats {
|
||||
private:
|
||||
Vector<int64_t> keys_by_collision_count_;
|
||||
int64_t total_collisions_;
|
||||
float average_collisions_;
|
||||
int64_t size_;
|
||||
int64_t capacity_;
|
||||
int64_t removed_amount_;
|
||||
float load_factor_;
|
||||
float removed_load_factor_;
|
||||
int64_t size_per_element_;
|
||||
int64_t size_in_bytes_;
|
||||
const void *address_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Requires that the hash table has the following methods:
|
||||
* - count_collisions(key) -> int64_t
|
||||
* - size() -> int64_t
|
||||
* - capacity() -> int64_t
|
||||
* - removed_amount() -> int64_t
|
||||
* - size_per_element() -> int64_t
|
||||
* - size_in_bytes() -> int64_t
|
||||
*/
|
||||
template<typename HashTable, typename Keys>
|
||||
HashTableStats(const HashTable &hash_table, const Keys &keys)
|
||||
{
|
||||
total_collisions_ = 0;
|
||||
size_ = hash_table.size();
|
||||
capacity_ = hash_table.capacity();
|
||||
removed_amount_ = hash_table.removed_amount();
|
||||
size_per_element_ = hash_table.size_per_element();
|
||||
size_in_bytes_ = hash_table.size_in_bytes();
|
||||
address_ = static_cast<const void *>(&hash_table);
|
||||
|
||||
for (const auto &key : keys) {
|
||||
int64_t collisions = hash_table.count_collisions(key);
|
||||
if (keys_by_collision_count_.size() <= collisions) {
|
||||
keys_by_collision_count_.append_n_times(0,
|
||||
collisions - keys_by_collision_count_.size() + 1);
|
||||
}
|
||||
keys_by_collision_count_[collisions]++;
|
||||
total_collisions_ += collisions;
|
||||
}
|
||||
|
||||
average_collisions_ = (size_ == 0) ? 0 : float(total_collisions_) / float(size_);
|
||||
load_factor_ = float(size_) / float(capacity_);
|
||||
removed_load_factor_ = float(removed_amount_) / float(capacity_);
|
||||
}
|
||||
|
||||
void print(const char *name) const;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/**
|
||||
* This struct provides an equality operator that returns true for all objects that compare equal
|
||||
* when one would use the `==` operator. This is different from std::equal_to<T>, because that
|
||||
* requires the parameters to be of type T. Our hash tables support lookups using other types
|
||||
* without conversion, therefore DefaultEquality needs to be more generic.
|
||||
*/
|
||||
template<typename T> struct DefaultEquality {
|
||||
template<typename T1, typename T2> bool operator()(const T1 &a, const T2 &b) const
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Support comparing different kinds of raw and smart pointers.
|
||||
*/
|
||||
struct PointerComparison {
|
||||
template<typename T1, typename T2> bool operator()(const T1 &a, const T2 &b) const
|
||||
{
|
||||
return &*a == &*b;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct DefaultEquality<std::unique_ptr<T>> : public PointerComparison {};
|
||||
template<typename T> struct DefaultEquality<std::shared_ptr<T>> : public PointerComparison {};
|
||||
|
||||
struct SequenceComparison {
|
||||
template<typename T1, typename T2> bool operator()(const T1 &a, const T2 &b) const
|
||||
{
|
||||
const auto a_begin = a.begin();
|
||||
const auto a_end = a.end();
|
||||
const auto b_begin = b.begin();
|
||||
const auto b_end = b.end();
|
||||
if (a_end - a_begin != b_end - b_begin) {
|
||||
return false;
|
||||
}
|
||||
return std::equal(a_begin, a_end, b_begin);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, int64_t InlineBufferCapacity, typename Allocator>
|
||||
struct DefaultEquality<Vector<T, InlineBufferCapacity, Allocator>> : public SequenceComparison {};
|
||||
|
||||
} // namespace blender
|
||||
79
blender-5.2.0/source/blender/blenlib/BLI_heap.h
Normal file
79
blender-5.2.0/source/blender/blenlib/BLI_heap.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief A min-heap / priority queue ADT
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Heap;
|
||||
struct HeapNode;
|
||||
|
||||
typedef void (*HeapFreeFP)(void *ptr);
|
||||
|
||||
/**
|
||||
* Creates a new heap. Removed nodes are recycled, so memory usage will not shrink.
|
||||
*
|
||||
* \note Use when the size of the heap is known in advance.
|
||||
*/
|
||||
Heap *BLI_heap_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT;
|
||||
Heap *BLI_heap_new() ATTR_WARN_UNUSED_RESULT;
|
||||
void BLI_heap_clear(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1);
|
||||
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Insert heap node with a value (often a 'cost') and pointer into the heap,
|
||||
* duplicate values are allowed.
|
||||
*/
|
||||
HeapNode *BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Convenience function since this is a common pattern.
|
||||
*/
|
||||
void BLI_heap_insert_or_update(Heap *heap, HeapNode **node_p, float value, void *ptr)
|
||||
ATTR_NONNULL(1, 2);
|
||||
void BLI_heap_remove(Heap *heap, HeapNode *node) ATTR_NONNULL(1, 2);
|
||||
bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1);
|
||||
unsigned int BLI_heap_len(const Heap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
|
||||
/**
|
||||
* Return the top node of the heap.
|
||||
* This is the node with the lowest value.
|
||||
*/
|
||||
HeapNode *BLI_heap_top(const Heap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
|
||||
/**
|
||||
* Return the value of top node of the heap.
|
||||
* This is the node with the lowest value.
|
||||
*/
|
||||
float BLI_heap_top_value(const Heap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
|
||||
/**
|
||||
* Pop the top node off the heap and return its pointer.
|
||||
*/
|
||||
void *BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Can be used to avoid #BLI_heap_remove, #BLI_heap_insert calls,
|
||||
* balancing the tree still has a performance cost,
|
||||
* but is often much less than remove/insert, difference is most noticeable with large heaps.
|
||||
*/
|
||||
void BLI_heap_node_value_update(Heap *heap, HeapNode *node, float value) ATTR_NONNULL(1, 2);
|
||||
void BLI_heap_node_value_update_ptr(Heap *heap, HeapNode *node, float value, void *ptr)
|
||||
ATTR_NONNULL(1, 2);
|
||||
|
||||
/**
|
||||
* Return the value or pointer of a heap node.
|
||||
*/
|
||||
float BLI_heap_node_value(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
|
||||
void *BLI_heap_node_ptr(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
|
||||
/**
|
||||
* Only for checking internal errors (gtest).
|
||||
*/
|
||||
bool BLI_heap_is_valid(const Heap *heap);
|
||||
|
||||
} // namespace blender
|
||||
46
blender-5.2.0/source/blender/blenlib/BLI_heap_simple.h
Normal file
46
blender-5.2.0/source/blender/blenlib/BLI_heap_simple.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
* \brief A min-heap / priority queue ADT
|
||||
*/
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct HeapSimple;
|
||||
|
||||
typedef void (*HeapSimpleFreeFP)(void *ptr);
|
||||
|
||||
/**
|
||||
* Creates a new simple heap, which only supports insertion and removal from top.
|
||||
*
|
||||
* \note Use when the size of the heap is known in advance.
|
||||
*/
|
||||
HeapSimple *BLI_heapsimple_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT;
|
||||
HeapSimple *BLI_heapsimple_new() ATTR_WARN_UNUSED_RESULT;
|
||||
void BLI_heapsimple_clear(HeapSimple *heap, HeapSimpleFreeFP ptrfreefp) ATTR_NONNULL(1);
|
||||
void BLI_heapsimple_free(HeapSimple *heap, HeapSimpleFreeFP ptrfreefp) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Insert heap node with a value (often a 'cost') and pointer into the heap,
|
||||
* duplicate values are allowed.
|
||||
*/
|
||||
void BLI_heapsimple_insert(HeapSimple *heap, float value, void *ptr) ATTR_NONNULL(1);
|
||||
bool BLI_heapsimple_is_empty(const HeapSimple *heap) ATTR_NONNULL(1);
|
||||
uint BLI_heapsimple_len(const HeapSimple *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
|
||||
/**
|
||||
* Return the lowest value of the heap.
|
||||
*/
|
||||
float BLI_heapsimple_top_value(const HeapSimple *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
|
||||
/**
|
||||
* Pop the top node off the heap and return its pointer.
|
||||
*/
|
||||
void *BLI_heapsimple_pop_min(HeapSimple *heap) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
26
blender-5.2.0/source/blender/blenlib/BLI_implicit_sharing.h
Normal file
26
blender-5.2.0/source/blender/blenlib/BLI_implicit_sharing.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* This file only exists to forward declare `ImplicitSharingInfo` in C code.
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
class ImplicitSharingInfo;
|
||||
using ImplicitSharingInfoHandle = ImplicitSharingInfo;
|
||||
|
||||
#else
|
||||
|
||||
struct ImplicitSharingInfoHandle;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace blender
|
||||
315
blender-5.2.0/source/blender/blenlib/BLI_implicit_sharing.hh
Normal file
315
blender-5.2.0/source/blender/blenlib/BLI_implicit_sharing.hh
Normal file
@@ -0,0 +1,315 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* #ImplicitSharingInfo is the core data structure for implicit sharing in Blender. Implicit
|
||||
* sharing is a technique that avoids copying data when it is not necessary. This results in better
|
||||
* memory usage and performance. Only read-only data can be shared, because otherwise multiple
|
||||
* owners might want to change the data in conflicting ways.
|
||||
*
|
||||
* To determine whether data is shared, #ImplicitSharingInfo keeps a user count. If the count is 1,
|
||||
* the data only has a single owner and is therefore mutable. If some code wants to modify data
|
||||
* that is currently shared, it has to make a copy first.
|
||||
* This behavior is also called "copy on write".
|
||||
*
|
||||
* In addition to containing the reference count, #ImplicitSharingInfo also knows how to destruct
|
||||
* the referenced data. This is important because the code freeing the data in the end might not
|
||||
* know how it was allocated (for example, it doesn't know whether an array was allocated using the
|
||||
* system or guarded allocator).
|
||||
*
|
||||
* #ImplicitSharingInfo can be used in two ways:
|
||||
* - It can be allocated separately from the referenced data. This is used when the shared data is
|
||||
* e.g. a plain data array.
|
||||
* - It can be embedded into another struct. For that it's best to use #ImplicitSharingMixin.
|
||||
*/
|
||||
class ImplicitSharingInfo : NonCopyable, NonMovable {
|
||||
private:
|
||||
/**
|
||||
* Number of users that want to own the shared data. This can be in multiple states:
|
||||
* - 0: The data is expired and likely freed. It must not be accessed anymore. The
|
||||
* #ImplicitSharingInfo may still be alive when there are weak users.
|
||||
* - 1: The data is mutable by the single owner.
|
||||
* - >1: The data is shared and therefore immutable.
|
||||
*/
|
||||
mutable std::atomic<int> strong_users_ = 1;
|
||||
/**
|
||||
* Number of users that only keep a reference to the `ImplicitSharingInfo` but don't need to own
|
||||
* the shared data. One additional weak user is added as long as there is at least one strong
|
||||
* user. Together with the `version_` below this adds an efficient way to detect if data has been
|
||||
* changed.
|
||||
*/
|
||||
mutable std::atomic<int> weak_users_ = 1;
|
||||
/**
|
||||
* The data referenced by an #ImplicitSharingInfo can change over time. This version is
|
||||
* incremented whenever the referenced data is about to be changed. This allows checking if the
|
||||
* data has been changed between points in time.
|
||||
*/
|
||||
mutable std::atomic<int64_t> version_ = 0;
|
||||
|
||||
public:
|
||||
virtual ~ImplicitSharingInfo()
|
||||
{
|
||||
BLI_assert(strong_users_ == 0);
|
||||
BLI_assert(weak_users_ == 0);
|
||||
}
|
||||
|
||||
/** Whether the resource can be modified in place because there is only one owner. */
|
||||
bool is_mutable() const
|
||||
{
|
||||
return strong_users_.load(std::memory_order_relaxed) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Weak users don't protect the referenced data from being freed. If the data is freed while
|
||||
* there is still a weak referenced, this returns true.
|
||||
*/
|
||||
bool is_expired() const
|
||||
{
|
||||
return strong_users_.load(std::memory_order_acquire) == 0;
|
||||
}
|
||||
|
||||
/** Call when the data has a new additional owner. */
|
||||
void add_user() const
|
||||
{
|
||||
BLI_assert(!this->is_expired());
|
||||
strong_users_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding a weak owner prevents the #ImplicitSharingInfo from being freed but not the referenced
|
||||
* data.
|
||||
*
|
||||
* \note Unlike std::shared_ptr a weak user cannot be turned into a strong user. This is
|
||||
* because some code might change the referenced data assuming that there is only one strong user
|
||||
* while a new strong user is added by another thread.
|
||||
*/
|
||||
void add_weak_user() const
|
||||
{
|
||||
weak_users_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this when making sure that the referenced data is mutable, which also implies that it is
|
||||
* about to be modified. This allows other code to detect whether data has not been changed very
|
||||
* efficiently.
|
||||
*/
|
||||
void tag_ensured_mutable() const
|
||||
{
|
||||
BLI_assert(this->is_mutable());
|
||||
/* This might not need an atomic increment when the #version method below is only called when
|
||||
* the code calling it is a strong user of this sharing info. Better be safe and use an atomic
|
||||
* for now. */
|
||||
version_.fetch_add(1, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a version number that is increased when the data is modified. It can be used to detect if
|
||||
* data has been changed.
|
||||
*/
|
||||
int64_t version() const
|
||||
{
|
||||
return version_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
int strong_users() const
|
||||
{
|
||||
return strong_users_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call when the data is no longer needed. This might just decrement the user count, or it might
|
||||
* also delete the data if this was the last user.
|
||||
*/
|
||||
void remove_user_and_delete_if_last() const
|
||||
{
|
||||
const int old_user_count = strong_users_.fetch_sub(1, std::memory_order_acq_rel);
|
||||
BLI_assert(old_user_count >= 1);
|
||||
const bool was_last_user = old_user_count == 1;
|
||||
if (was_last_user) {
|
||||
const int old_weak_user_count = weak_users_.load(std::memory_order_acquire);
|
||||
BLI_assert(old_weak_user_count >= 1);
|
||||
if (old_weak_user_count == 1) {
|
||||
/* If the weak user count is 1 it means that there is no actual weak user. The 1 just
|
||||
* indicates that there was still at least one strong user. */
|
||||
weak_users_ = 0;
|
||||
const_cast<ImplicitSharingInfo *>(this)->delete_self_with_data();
|
||||
}
|
||||
else {
|
||||
/* There is still at least one actual weak user, so don't free the sharing info yet. The
|
||||
* data can be freed though. */
|
||||
const_cast<ImplicitSharingInfo *>(this)->delete_data_only();
|
||||
/* Also remove the "fake" weak user that indicated that there was at least one strong
|
||||
* user. */
|
||||
this->remove_weak_user_and_delete_if_last();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This might just decrement the weak user count or might delete the data. Should be used in
|
||||
* conjunction with #add_weak_user.
|
||||
*/
|
||||
void remove_weak_user_and_delete_if_last() const
|
||||
{
|
||||
const int old_weak_user_count = weak_users_.fetch_sub(1, std::memory_order_acq_rel);
|
||||
BLI_assert(old_weak_user_count >= 1);
|
||||
const bool was_last_weak_user = old_weak_user_count == 1;
|
||||
if (was_last_weak_user) {
|
||||
/* It's possible that the data has been freed before already, but now it is definitely freed
|
||||
* together with the sharing info. */
|
||||
const_cast<ImplicitSharingInfo *>(this)->delete_self_with_data();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/** Has to free the #ImplicitSharingInfo and the referenced data. The data might have been freed
|
||||
* before by #delete_data_only already. This case should be handled here. */
|
||||
virtual void delete_self_with_data() = 0;
|
||||
/** Can free the referenced data but the #ImplicitSharingInfo still has to be kept alive. */
|
||||
virtual void delete_data_only() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes it easy to embed implicit-sharing behavior into a struct. Structs that derive from this
|
||||
* class can be used with #ImplicitSharingPtr.
|
||||
*/
|
||||
class ImplicitSharingMixin : public ImplicitSharingInfo {
|
||||
|
||||
private:
|
||||
void delete_self_with_data() override
|
||||
{
|
||||
/* Can't use `delete this` here, because we don't know what allocator was used. */
|
||||
this->delete_self();
|
||||
}
|
||||
|
||||
virtual void delete_self() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility for creating an allocated shared resource, to be used like:
|
||||
* `new ImplicitSharedValue<T>(args);`
|
||||
*/
|
||||
template<typename T> class ImplicitSharedValue : public ImplicitSharingInfo {
|
||||
public:
|
||||
T data;
|
||||
|
||||
template<typename... Args>
|
||||
ImplicitSharedValue(Args &&...args) : data(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("ImplicitSharedValue");
|
||||
|
||||
private:
|
||||
void delete_self_with_data() override
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility that contains sharing information and the data that is shared.
|
||||
*/
|
||||
struct ImplicitSharingInfoAndData {
|
||||
const ImplicitSharingInfo *sharing_info = nullptr;
|
||||
const void *data = nullptr;
|
||||
};
|
||||
|
||||
namespace implicit_sharing {
|
||||
|
||||
namespace detail {
|
||||
|
||||
void *resize_trivial_array_impl(void *old_data,
|
||||
int64_t old_size,
|
||||
int64_t new_size,
|
||||
int64_t alignment,
|
||||
const ImplicitSharingInfo **sharing_info);
|
||||
void *make_trivial_data_mutable_impl(void *old_data,
|
||||
int64_t size,
|
||||
int64_t alignment,
|
||||
const ImplicitSharingInfo **sharing_info);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Copy shared data from the source to the destination, adding a user count.
|
||||
* \note Does not free any existing data in the destination.
|
||||
*/
|
||||
template<typename T>
|
||||
void copy_shared_pointer(T *src_ptr,
|
||||
const ImplicitSharingInfo *src_sharing_info,
|
||||
T **r_dst_ptr,
|
||||
const ImplicitSharingInfo **r_dst_sharing_info)
|
||||
{
|
||||
*r_dst_ptr = src_ptr;
|
||||
*r_dst_sharing_info = src_sharing_info;
|
||||
if (*r_dst_ptr) {
|
||||
BLI_assert(*r_dst_sharing_info != nullptr);
|
||||
(*r_dst_sharing_info)->add_user();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove this reference to the shared data and remove dangling pointers.
|
||||
*/
|
||||
template<typename T> void free_shared_data(T **data, const ImplicitSharingInfo **sharing_info)
|
||||
{
|
||||
if (*sharing_info) {
|
||||
BLI_assert(*data != nullptr);
|
||||
(*sharing_info)->remove_user_and_delete_if_last();
|
||||
}
|
||||
*data = nullptr;
|
||||
*sharing_info = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an implicit sharing object that takes ownership of the data, allowing it to be shared.
|
||||
* When it is no longer used, the data is freed with #MEM_delete, so it must be a trivial type.
|
||||
*/
|
||||
const ImplicitSharingInfo *info_for_mem_free(void *data);
|
||||
|
||||
/**
|
||||
* Make data mutable (single-user) if it is shared. For trivially-copyable data only.
|
||||
*/
|
||||
template<typename T>
|
||||
void make_trivial_data_mutable(T **data,
|
||||
const ImplicitSharingInfo **sharing_info,
|
||||
const int64_t size)
|
||||
{
|
||||
*data = static_cast<T *>(
|
||||
detail::make_trivial_data_mutable_impl(*data, sizeof(T) * size, alignof(T), sharing_info));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize an array of shared data. For trivially-copyable data only. Any new values are not
|
||||
* initialized.
|
||||
*/
|
||||
template<typename T>
|
||||
void resize_trivial_array(T **data,
|
||||
const ImplicitSharingInfo **sharing_info,
|
||||
int64_t old_size,
|
||||
int64_t new_size)
|
||||
{
|
||||
*data = static_cast<T *>(detail::resize_trivial_array_impl(
|
||||
*data, sizeof(T) * old_size, sizeof(T) * new_size, alignof(T), sharing_info));
|
||||
}
|
||||
|
||||
} // namespace implicit_sharing
|
||||
|
||||
} // namespace blender
|
||||
262
blender-5.2.0/source/blender/blenlib/BLI_implicit_sharing_ptr.hh
Normal file
262
blender-5.2.0/source/blender/blenlib/BLI_implicit_sharing_ptr.hh
Normal file
@@ -0,0 +1,262 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "BLI_implicit_sharing.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* #ImplicitSharingPtr is a smart pointer that manages implicit sharing. It's designed to work with
|
||||
* types that derive from #ImplicitSharingMixin. It is fairly similar to #std::shared_ptr but
|
||||
* requires the reference count to be embedded in the data.
|
||||
*/
|
||||
template<typename T = ImplicitSharingInfo, bool IsStrong = true> class ImplicitSharingPtr {
|
||||
private:
|
||||
const T *data_ = nullptr;
|
||||
|
||||
template<typename U, bool OtherIsStrong> friend class ImplicitSharingPtr;
|
||||
|
||||
public:
|
||||
using element_type = T;
|
||||
|
||||
ImplicitSharingPtr() = default;
|
||||
|
||||
explicit ImplicitSharingPtr(const T *data) : data_(data) {}
|
||||
|
||||
/* Implicit conversion from nullptr. */
|
||||
ImplicitSharingPtr(std::nullptr_t) : data_(nullptr) {}
|
||||
|
||||
ImplicitSharingPtr(const ImplicitSharingPtr &other) : data_(other.data_)
|
||||
{
|
||||
this->add_user(data_);
|
||||
}
|
||||
|
||||
ImplicitSharingPtr(ImplicitSharingPtr &&other) : data_(other.data_)
|
||||
{
|
||||
other.data_ = nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
ImplicitSharingPtr(const ImplicitSharingPtr<U, IsStrong> &other)
|
||||
requires std::is_base_of_v<T, U>
|
||||
: data_(static_cast<const U *>(other.data_))
|
||||
{
|
||||
this->add_user(data_);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
ImplicitSharingPtr(ImplicitSharingPtr<U, IsStrong> &&other)
|
||||
requires std::is_base_of_v<T, U>
|
||||
: data_(static_cast<U *>(other.data_))
|
||||
{
|
||||
other.data_ = nullptr;
|
||||
}
|
||||
|
||||
~ImplicitSharingPtr()
|
||||
{
|
||||
this->remove_user_and_delete_if_last(data_);
|
||||
}
|
||||
|
||||
ImplicitSharingPtr &operator=(const ImplicitSharingPtr &other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
this->remove_user_and_delete_if_last(data_);
|
||||
data_ = other.data_;
|
||||
this->add_user(data_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ImplicitSharingPtr &operator=(ImplicitSharingPtr &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
this->remove_user_and_delete_if_last(data_);
|
||||
data_ = other.data_;
|
||||
other.data_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T *operator->() const
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
return data_;
|
||||
}
|
||||
|
||||
const T &operator*() const
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
return *data_;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return data_ != nullptr;
|
||||
}
|
||||
|
||||
const T *get() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const T *release()
|
||||
{
|
||||
const T *data = data_;
|
||||
data_ = nullptr;
|
||||
return data;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
this->remove_user_and_delete_if_last(data_);
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
bool has_value() const
|
||||
{
|
||||
return data_ != nullptr;
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
return get_default_hash(data_);
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is only a single user of the data, return a mutable reference to it directly.
|
||||
* Otherwise call #copy which is expected to return a new implicitly shared pointer that always
|
||||
* has a single user.
|
||||
*/
|
||||
T &ensure_mutable_inplace()
|
||||
{
|
||||
BLI_assert(data_);
|
||||
if (!data_->is_mutable()) {
|
||||
/* The data is shared and therefore immutable. Make a mutable copy. */
|
||||
*this = data_->copy();
|
||||
}
|
||||
BLI_assert(data_->is_mutable());
|
||||
data_->tag_ensured_mutable();
|
||||
return const_cast<T &>(*data_);
|
||||
}
|
||||
|
||||
static uint64_t hash_as(const T *data)
|
||||
{
|
||||
return get_default_hash(data);
|
||||
}
|
||||
|
||||
friend bool operator==(const ImplicitSharingPtr &a, const ImplicitSharingPtr &b) = default;
|
||||
|
||||
friend bool operator==(const T *a, const ImplicitSharingPtr &b)
|
||||
{
|
||||
return a == b.data_;
|
||||
}
|
||||
|
||||
friend bool operator==(const ImplicitSharingPtr &a, const T *b)
|
||||
{
|
||||
return a.data_ == b;
|
||||
}
|
||||
|
||||
private:
|
||||
static void add_user(const T *data)
|
||||
{
|
||||
if (data != nullptr) {
|
||||
if constexpr (IsStrong) {
|
||||
data->add_user();
|
||||
}
|
||||
else {
|
||||
data->add_weak_user();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_user_and_delete_if_last(const T *data)
|
||||
{
|
||||
if (data != nullptr) {
|
||||
if constexpr (IsStrong) {
|
||||
data->remove_user_and_delete_if_last();
|
||||
}
|
||||
else {
|
||||
data->remove_weak_user_and_delete_if_last();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using WeakImplicitSharingPtr = ImplicitSharingPtr<ImplicitSharingInfo, false>;
|
||||
|
||||
/**
|
||||
* Utility struct to allow used #ImplicitSharingPtr when it's necessary to type-erase the backing
|
||||
* storage for user-exposed data. For example, #Vector, or #std::vector might be used to
|
||||
* store an implicitly shared array that is only accessed with #Span or #MutableSpan.
|
||||
*
|
||||
* This class handles RAII for the sharing info and the exposed data pointer.
|
||||
* Retrieving the data with write access and type safety must be handled elsewhere.
|
||||
*/
|
||||
class ImplicitSharingPtrAndData {
|
||||
public:
|
||||
ImplicitSharingPtr<> sharing_info;
|
||||
const void *data = nullptr;
|
||||
|
||||
ImplicitSharingPtrAndData() = default;
|
||||
ImplicitSharingPtrAndData(ImplicitSharingPtr<> sharing_info, const void *data)
|
||||
: sharing_info(std::move(sharing_info)), data(data)
|
||||
{
|
||||
}
|
||||
|
||||
ImplicitSharingPtrAndData(const ImplicitSharingPtrAndData &other) = default;
|
||||
|
||||
ImplicitSharingPtrAndData(ImplicitSharingPtrAndData &&other)
|
||||
: sharing_info(std::move(other.sharing_info)), data(std::exchange(other.data, nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
ImplicitSharingPtrAndData &operator=(const ImplicitSharingPtrAndData &other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
std::destroy_at(this);
|
||||
new (this) ImplicitSharingPtrAndData(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ImplicitSharingPtrAndData &operator=(ImplicitSharingPtrAndData &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
std::destroy_at(this);
|
||||
new (this) ImplicitSharingPtrAndData(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
~ImplicitSharingPtrAndData()
|
||||
{
|
||||
this->data = nullptr;
|
||||
}
|
||||
|
||||
bool has_value() const
|
||||
{
|
||||
return this->sharing_info.has_value();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> static constexpr bool is_ImplicitSharingPtr_strong_v = false;
|
||||
template<typename T>
|
||||
static constexpr bool is_ImplicitSharingPtr_strong_v<ImplicitSharingPtr<T, true>> = true;
|
||||
|
||||
} // namespace blender
|
||||
1314
blender-5.2.0/source/blender/blenlib/BLI_index_mask.hh
Normal file
1314
blender-5.2.0/source/blender/blenlib/BLI_index_mask.hh
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,98 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_resource_scope.hh"
|
||||
|
||||
namespace blender::index_mask {
|
||||
|
||||
struct AtomicExpr;
|
||||
struct UnionExpr;
|
||||
struct IntersectionExpr;
|
||||
struct DifferenceExpr;
|
||||
|
||||
struct Expr {
|
||||
enum class Type {
|
||||
Atomic,
|
||||
Union,
|
||||
Intersection,
|
||||
Difference,
|
||||
};
|
||||
|
||||
Type type;
|
||||
int index;
|
||||
Vector<const Expr *> terms;
|
||||
|
||||
int expression_array_size() const;
|
||||
|
||||
const AtomicExpr &as_atomic() const;
|
||||
const UnionExpr &as_union() const;
|
||||
const IntersectionExpr &as_intersection() const;
|
||||
const DifferenceExpr &as_difference() const;
|
||||
};
|
||||
|
||||
struct AtomicExpr : public Expr {
|
||||
const IndexMask *mask;
|
||||
};
|
||||
|
||||
struct UnionExpr : public Expr {};
|
||||
|
||||
struct IntersectionExpr : public Expr {};
|
||||
|
||||
struct DifferenceExpr : public Expr {};
|
||||
|
||||
class ExprBuilder {
|
||||
private:
|
||||
ResourceScope scope_;
|
||||
int expr_count_ = 0;
|
||||
|
||||
public:
|
||||
using Term = std::variant<const Expr *, const IndexMask *, IndexRange>;
|
||||
|
||||
const UnionExpr &merge(const Span<Term> terms);
|
||||
const DifferenceExpr &subtract(const Term &main_term, const Span<Term> subtract_terms);
|
||||
const IntersectionExpr &intersect(const Span<Term> terms);
|
||||
|
||||
private:
|
||||
const Expr &term_to_expr(const Term &term);
|
||||
};
|
||||
|
||||
IndexMask evaluate_expression(const Expr &expression, LinearAllocator<> &memory);
|
||||
|
||||
inline int Expr::expression_array_size() const
|
||||
{
|
||||
return this->index + 1;
|
||||
}
|
||||
|
||||
inline const AtomicExpr &Expr::as_atomic() const
|
||||
{
|
||||
BLI_assert(this->type == Type::Atomic);
|
||||
return static_cast<const AtomicExpr &>(*this);
|
||||
}
|
||||
|
||||
inline const UnionExpr &Expr::as_union() const
|
||||
{
|
||||
BLI_assert(this->type == Type::Union);
|
||||
return static_cast<const UnionExpr &>(*this);
|
||||
}
|
||||
|
||||
inline const IntersectionExpr &Expr::as_intersection() const
|
||||
{
|
||||
BLI_assert(this->type == Type::Intersection);
|
||||
return static_cast<const IntersectionExpr &>(*this);
|
||||
}
|
||||
|
||||
inline const DifferenceExpr &Expr::as_difference() const
|
||||
{
|
||||
BLI_assert(this->type == Type::Difference);
|
||||
return static_cast<const DifferenceExpr &>(*this);
|
||||
}
|
||||
|
||||
} // namespace blender::index_mask
|
||||
23
blender-5.2.0/source/blender/blenlib/BLI_index_mask_fwd.hh
Normal file
23
blender-5.2.0/source/blender/blenlib/BLI_index_mask_fwd.hh
Normal file
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace index_mask {
|
||||
|
||||
class IndexMask;
|
||||
class IndexMaskMemory;
|
||||
|
||||
} // namespace index_mask
|
||||
|
||||
using index_mask::IndexMask;
|
||||
using index_mask::IndexMaskMemory;
|
||||
|
||||
} // namespace blender
|
||||
373
blender-5.2.0/source/blender/blenlib/BLI_index_range.hh
Normal file
373
blender-5.2.0/source/blender/blenlib/BLI_index_range.hh
Normal file
@@ -0,0 +1,373 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A `IndexRange` wraps an interval of non-negative integers. It can be used to reference
|
||||
* consecutive elements in an array. Furthermore, it can make for loops more convenient and less
|
||||
* error prone, especially when using nested loops.
|
||||
*
|
||||
* I'd argue that the second loop is more readable and less error prone than the first one. That is
|
||||
* not necessarily always the case, but often it is.
|
||||
*
|
||||
* \code{.cc}
|
||||
* for (int64_t i = 0; i < 10; i++) {
|
||||
* for (int64_t j = 0; j < 20; j++) {
|
||||
* for (int64_t k = 0; k < 30; k++) {
|
||||
*
|
||||
* for (int64_t i : IndexRange(10)) {
|
||||
* for (int64_t j : IndexRange(20)) {
|
||||
* for (int64_t k : IndexRange(30)) {
|
||||
* \endcode
|
||||
*
|
||||
* Some containers like Vector have an index_range() method. This will return the
|
||||
* IndexRange that contains all indices that can be used to access the container. This is
|
||||
* particularly useful when you want to iterate over the indices and the elements (much like
|
||||
* Python's enumerate(), just worse). Again, I think the second example here is better:
|
||||
*
|
||||
* \code{.cc}
|
||||
* for (int64_t i = 0; i < my_vector_with_a_long_name.size(); i++) {
|
||||
* do_something(i, my_vector_with_a_long_name[i]);
|
||||
*
|
||||
* for (int64_t i : my_vector_with_a_long_name.index_range()) {
|
||||
* do_something(i, my_vector_with_a_long_name[i]);
|
||||
* \endcode
|
||||
*
|
||||
* Ideally this could be could be even closer to Python's enumerate(). We might get that in the
|
||||
* future with newer C++ versions.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <iosfwd>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_random_access_iterator_mixin.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
template<typename T> class Span;
|
||||
|
||||
class IndexRange {
|
||||
private:
|
||||
int64_t start_ = 0;
|
||||
int64_t size_ = 0;
|
||||
|
||||
public:
|
||||
constexpr IndexRange() = default;
|
||||
|
||||
constexpr explicit IndexRange(int64_t size) : size_(size)
|
||||
{
|
||||
BLI_assert(size >= 0);
|
||||
}
|
||||
|
||||
constexpr IndexRange(const int64_t start, const int64_t size) : start_(start), size_(size)
|
||||
{
|
||||
BLI_assert(start >= 0);
|
||||
BLI_assert(size >= 0);
|
||||
}
|
||||
|
||||
constexpr static IndexRange from_begin_size(const int64_t begin, const int64_t size)
|
||||
{
|
||||
return IndexRange(begin, size);
|
||||
}
|
||||
|
||||
constexpr static IndexRange from_begin_end(const int64_t begin, const int64_t end)
|
||||
{
|
||||
return IndexRange(begin, end - begin);
|
||||
}
|
||||
|
||||
constexpr static IndexRange from_begin_end_inclusive(const int64_t begin, const int64_t last)
|
||||
{
|
||||
return IndexRange(begin, last - begin + 1);
|
||||
}
|
||||
|
||||
constexpr static IndexRange from_end_size(const int64_t end, const int64_t size)
|
||||
{
|
||||
return IndexRange(end - size, size);
|
||||
}
|
||||
|
||||
constexpr static IndexRange from_single(const int64_t index)
|
||||
{
|
||||
return IndexRange(index, 1);
|
||||
}
|
||||
|
||||
class Iterator : public iterator::RandomAccessIteratorMixin<Iterator> {
|
||||
public:
|
||||
using value_type = int64_t;
|
||||
using pointer = const int64_t *;
|
||||
using reference = int64_t;
|
||||
|
||||
private:
|
||||
int64_t current_;
|
||||
|
||||
public:
|
||||
constexpr explicit Iterator(int64_t current) : current_(current) {}
|
||||
|
||||
constexpr int64_t operator*() const
|
||||
{
|
||||
return current_;
|
||||
}
|
||||
|
||||
const int64_t &iter_prop() const
|
||||
{
|
||||
return current_;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr Iterator begin() const
|
||||
{
|
||||
return Iterator(start_);
|
||||
}
|
||||
|
||||
constexpr Iterator end() const
|
||||
{
|
||||
return Iterator(start_ + size_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access an element in the range.
|
||||
*/
|
||||
constexpr int64_t operator[](int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < this->size());
|
||||
return start_ + index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Two ranges compare equal when they contain the same numbers.
|
||||
*/
|
||||
constexpr friend bool operator==(IndexRange a, IndexRange b)
|
||||
{
|
||||
return (a.size_ == b.size_) && (a.start_ == b.start_ || a.size_ == 0);
|
||||
}
|
||||
constexpr friend bool operator!=(IndexRange a, IndexRange b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of numbers in the range.
|
||||
*/
|
||||
constexpr int64_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
constexpr IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(size_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the size is zero.
|
||||
*/
|
||||
constexpr bool is_empty() const
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new index range with the same beginning but a different end.
|
||||
*/
|
||||
constexpr IndexRange with_new_end(const int64_t new_end) const
|
||||
{
|
||||
return IndexRange::from_begin_end(start_, new_end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new range starting at the end of the current one.
|
||||
*/
|
||||
constexpr IndexRange after(int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
return IndexRange(start_ + size_, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new range that ends at the start of the current one.
|
||||
*/
|
||||
constexpr IndexRange before(int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
return IndexRange(start_ - n, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first element in the range.
|
||||
* Asserts when the range is empty.
|
||||
*/
|
||||
constexpr int64_t first() const
|
||||
{
|
||||
BLI_assert(this->size() > 0);
|
||||
return start_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the nth last element in the range.
|
||||
* Asserts when the range is empty or when n is negative.
|
||||
*/
|
||||
constexpr int64_t last(const int64_t n = 0) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
BLI_assert(n < size_);
|
||||
BLI_assert(this->size() > 0);
|
||||
return start_ + size_ - 1 - n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element one before the beginning. The returned value is undefined when the range is
|
||||
* empty, and the range must start after zero already.
|
||||
*/
|
||||
constexpr int64_t one_before_start() const
|
||||
{
|
||||
BLI_assert(start_ > 0);
|
||||
return start_ - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element one after the end. The returned value is undefined when the range is empty.
|
||||
*/
|
||||
constexpr int64_t one_after_last() const
|
||||
{
|
||||
return start_ + size_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first element in the range. The returned value is undefined when the range is empty.
|
||||
*/
|
||||
constexpr int64_t start() const
|
||||
{
|
||||
return start_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the range contains a certain number, otherwise false.
|
||||
*/
|
||||
constexpr bool contains(int64_t value) const
|
||||
{
|
||||
return value >= start_ && value < start_ + size_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when all indices in the given range are also in the current range.
|
||||
*/
|
||||
constexpr bool contains(const IndexRange range) const
|
||||
{
|
||||
if (range.is_empty()) {
|
||||
return true;
|
||||
}
|
||||
if (range.start_ < start_) {
|
||||
return false;
|
||||
}
|
||||
if (range.start_ + range.size_ > start_ + size_) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new range, that contains a sub-interval of the current one.
|
||||
*/
|
||||
constexpr IndexRange slice(int64_t start, int64_t size) const
|
||||
{
|
||||
BLI_assert(start >= 0);
|
||||
BLI_assert(size >= 0);
|
||||
int64_t new_start = start_ + start;
|
||||
BLI_assert(new_start + size <= start_ + size_ || size == 0);
|
||||
return IndexRange(new_start, size);
|
||||
}
|
||||
constexpr IndexRange slice(IndexRange range) const
|
||||
{
|
||||
return this->slice(range.start(), range.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new IndexRange that contains the intersection of the current one with the given
|
||||
* range. Returns empty range if there are no overlapping indices. The returned range is always
|
||||
* a valid slice of this range.
|
||||
*/
|
||||
constexpr IndexRange intersect(IndexRange other) const
|
||||
{
|
||||
const int64_t old_end = start_ + size_;
|
||||
const int64_t new_start = std::min(old_end, std::max(start_, other.start_));
|
||||
const int64_t new_end = std::max(new_start, std::min(old_end, other.start_ + other.size_));
|
||||
return IndexRange(new_start, new_end - new_start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new IndexRange with n elements removed from the beginning of the range.
|
||||
* This invokes undefined behavior when n is negative.
|
||||
*/
|
||||
constexpr IndexRange drop_front(int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::max<int64_t>(0, size_ - n);
|
||||
return IndexRange(start_ + n, new_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new IndexRange with n elements removed from the end of the range.
|
||||
* This invokes undefined behavior when n is negative.
|
||||
*/
|
||||
constexpr IndexRange drop_back(int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::max<int64_t>(0, size_ - n);
|
||||
return IndexRange(start_, new_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new IndexRange that only contains the first n elements. This invokes undefined
|
||||
* behavior when n is negative.
|
||||
*/
|
||||
constexpr IndexRange take_front(int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::min<int64_t>(size_, n);
|
||||
return IndexRange(start_, new_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new IndexRange that only contains the last n elements. This invokes undefined
|
||||
* behavior when n is negative.
|
||||
*/
|
||||
constexpr IndexRange take_back(int64_t n) const
|
||||
{
|
||||
BLI_assert(n >= 0);
|
||||
const int64_t new_size = std::min<int64_t>(size_, n);
|
||||
return IndexRange(start_ + size_ - new_size, new_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the range forward or backward within the larger array. The amount may be negative,
|
||||
* but its absolute value cannot be greater than the existing start of the range.
|
||||
*/
|
||||
constexpr IndexRange shift(int64_t n) const
|
||||
{
|
||||
return IndexRange(start_ + n, size_);
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, IndexRange range);
|
||||
};
|
||||
|
||||
struct AlignedIndexRanges {
|
||||
IndexRange prefix;
|
||||
IndexRange aligned;
|
||||
IndexRange suffix;
|
||||
};
|
||||
|
||||
/**
|
||||
* Split a range into three parts so that the boundaries of the middle part are aligned to some
|
||||
* power of two.
|
||||
*
|
||||
* This can be used when an algorithm can be optimized on aligned indices/memory. The algorithm
|
||||
* then needs a slow path for the beginning and end, and a fast path for the aligned elements.
|
||||
*/
|
||||
AlignedIndexRanges split_index_range_by_alignment(const IndexRange range, const int64_t alignment);
|
||||
|
||||
} // namespace blender
|
||||
125
blender-5.2.0/source/blender/blenlib/BLI_index_ranges_builder.hh
Normal file
125
blender-5.2.0/source/blender/blenlib/BLI_index_ranges_builder.hh
Normal file
@@ -0,0 +1,125 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_index_ranges_builder_fwd.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* A data structure that is designed to allow building many index ranges efficiently.
|
||||
*
|
||||
* One first has to add individual indices or ranges in ascending order. Internally, consecutive
|
||||
* indices and ranges are automatically joined.
|
||||
*
|
||||
* \note This data structure has a pre-defined capacity and can not automatically grow once that
|
||||
* capacity is reached. Use #IndexRangesBuilderBuffer to control the capacity.
|
||||
*/
|
||||
template<typename T> class IndexRangesBuilder : NonCopyable, NonMovable {
|
||||
private:
|
||||
/** The current pointer into #data_. It's changed whenever a new range starts. */
|
||||
T *c_;
|
||||
/** Structure: [-1, start, end, start, end, start, end, ...]. */
|
||||
MutableSpan<T> data_;
|
||||
|
||||
public:
|
||||
IndexRangesBuilder(MutableSpan<T> data) : data_(data)
|
||||
{
|
||||
static_assert(std::is_signed_v<T>);
|
||||
/* Set the first value to -1 so that when the first index is added, it is detected as the start
|
||||
* of a new range. */
|
||||
data_[0] = -1;
|
||||
c_ = data_.data();
|
||||
}
|
||||
|
||||
/** Add a new index. It has to be larger than any previously added index. */
|
||||
bool add(const T index)
|
||||
{
|
||||
return this->add_range(index, index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a range of indices. It has to start after any previously added index.
|
||||
* By design, this is branchless and requires O(1) time.
|
||||
*/
|
||||
bool add_range(const T start, const T end)
|
||||
{
|
||||
/* Indices have to be added in ascending order. */
|
||||
BLI_assert(start >= *c_);
|
||||
BLI_assert(start >= 0);
|
||||
BLI_assert(start < end);
|
||||
|
||||
const bool is_new_range = start > *c_;
|
||||
|
||||
/* Check that the capacity is not overflown. */
|
||||
BLI_assert(!is_new_range || this->size() < this->capacity());
|
||||
|
||||
/* This is designed to either append to the last range or start a new range.
|
||||
* It is intentionally branchless for more predictable performance on unpredictable data. */
|
||||
c_ += is_new_range;
|
||||
*c_ = start;
|
||||
c_ += is_new_range;
|
||||
*c_ = end;
|
||||
|
||||
return is_new_range;
|
||||
}
|
||||
|
||||
/** Number of collected ranges. */
|
||||
int64_t size() const
|
||||
{
|
||||
return (c_ - data_.data()) / 2;
|
||||
}
|
||||
|
||||
/** How many ranges this container can hold at most. */
|
||||
int64_t capacity() const
|
||||
{
|
||||
return data_.size() / 2;
|
||||
}
|
||||
|
||||
/** True if there are no ranges yet. */
|
||||
bool is_empty() const
|
||||
{
|
||||
return c_ == data_.data();
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(this->size());
|
||||
}
|
||||
|
||||
/** Get the i-th collected #IndexRange. */
|
||||
IndexRange operator[](const int64_t i) const
|
||||
{
|
||||
const T start = data_[size_t(1) + 2 * size_t(i)];
|
||||
const T end = data_[size_t(2) + 2 * size_t(i)];
|
||||
return IndexRange::from_begin_end(start, end);
|
||||
}
|
||||
|
||||
static constexpr int64_t buffer_size_for_ranges_num(const int64_t ranges_num)
|
||||
{
|
||||
/* Two values for each range (start, end) and the dummy prefix value. */
|
||||
return ranges_num * 2 + 1;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, int64_t MaxRangesNum> struct IndexRangesBuilderBuffer {
|
||||
std::array<T, size_t(IndexRangesBuilder<T>::buffer_size_for_ranges_num(MaxRangesNum))> data;
|
||||
|
||||
operator MutableSpan<T>()
|
||||
{
|
||||
return this->data;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,15 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
template<typename T> class IndexRangesBuilder;
|
||||
|
||||
} // namespace blender
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user