/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __FN_ARRAY_SPANS_HH__ #define __FN_ARRAY_SPANS_HH__ /** \file * \ingroup fn * * An ArraySpan is a span where every element contains an array (instead of a single element as is * the case in a normal span). It's main use case is to reference many small arrays. */ #include "FN_spans.hh" namespace blender { namespace fn { /** * Depending on the use case, the referenced data might have a different structure. More * categories can be added when necessary. */ enum class VArraySpanCategory { SingleArray, StartsAndSizes, }; template class VArraySpanBase { protected: uint m_virtual_size; VArraySpanCategory m_category; union { struct { const T *start; uint size; } single_array; struct { const T *const *starts; const uint *sizes; } starts_and_sizes; } m_data; public: bool is_single_array() const { switch (m_category) { case VArraySpanCategory::SingleArray: return true; case VArraySpanCategory::StartsAndSizes: return m_virtual_size == 1; } BLI_assert(false); return false; } bool is_empty() const { return this->m_virtual_size == 0; } uint size() const { return this->m_virtual_size; } }; /** * A virtual array span. Every element of this span contains a virtual span. So it behaves like * a blender::Span, but might not be backed up by an actual array. */ template class VArraySpan : public VArraySpanBase { private: friend class GVArraySpan; VArraySpan(const VArraySpanBase &other) { memcpy(this, &other, sizeof(VArraySpanBase)); } public: VArraySpan() { this->m_virtual_size = 0; this->m_category = VArraySpanCategory::StartsAndSizes; this->m_data.starts_and_sizes.starts = nullptr; this->m_data.starts_and_sizes.sizes = nullptr; } VArraySpan(Span span, uint virtual_size) { this->m_virtual_size = virtual_size; this->m_category = VArraySpanCategory::SingleArray; this->m_data.single_array.start = span.data(); this->m_data.single_array.size = span.size(); } VArraySpan(Span starts, Span sizes) { BLI_assert(starts.size() == sizes.size()); this->m_virtual_size = starts.size(); this->m_category = VArraySpanCategory::StartsAndSizes; this->m_data.starts_and_sizes.starts = starts.begin(); this->m_data.starts_and_sizes.sizes = sizes.begin(); } VSpan operator[](uint index) const { BLI_assert(index < this->m_virtual_size); switch (this->m_category) { case VArraySpanCategory::SingleArray: return VSpan(Span(this->m_data.single_array.start, this->m_data.single_array.size)); case VArraySpanCategory::StartsAndSizes: return VSpan(Span(this->m_data.starts_and_sizes.starts[index], this->m_data.starts_and_sizes.sizes[index])); } BLI_assert(false); return {}; } }; /** * A generic virtual array span. It's just like a VArraySpan, but the type is only known at * run-time. */ class GVArraySpan : public VArraySpanBase { private: const CPPType *m_type; GVArraySpan() = default; public: GVArraySpan(const CPPType &type) { this->m_type = &type; this->m_virtual_size = 0; this->m_category = VArraySpanCategory::StartsAndSizes; this->m_data.starts_and_sizes.starts = nullptr; this->m_data.starts_and_sizes.sizes = nullptr; } GVArraySpan(GSpan array, uint virtual_size) { this->m_type = &array.type(); this->m_virtual_size = virtual_size; this->m_category = VArraySpanCategory::SingleArray; this->m_data.single_array.start = array.buffer(); this->m_data.single_array.size = array.size(); } GVArraySpan(const CPPType &type, Span starts, Span sizes) { BLI_assert(starts.size() == sizes.size()); this->m_type = &type; this->m_virtual_size = starts.size(); this->m_category = VArraySpanCategory::StartsAndSizes; this->m_data.starts_and_sizes.starts = (void **)starts.begin(); this->m_data.starts_and_sizes.sizes = sizes.begin(); } template GVArraySpan(VArraySpan other) { this->m_type = &CPPType::get(); memcpy(this, &other, sizeof(VArraySpanBase)); } const CPPType &type() const { return *this->m_type; } template VArraySpan typed() const { BLI_assert(m_type->is()); return VArraySpan(*this); } GVSpan operator[](uint index) const { BLI_assert(index < m_virtual_size); switch (m_category) { case VArraySpanCategory::SingleArray: return GVSpan(GSpan(*m_type, m_data.single_array.start, m_data.single_array.size)); case VArraySpanCategory::StartsAndSizes: return GVSpan(GSpan( *m_type, m_data.starts_and_sizes.starts[index], m_data.starts_and_sizes.sizes[index])); } BLI_assert(false); return GVSpan(*m_type); } }; } // namespace fn } // namespace blender #endif /* __FN_ARRAY_SPANS_HH__ */