Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenlib/BLI_set_slots.hh')
-rw-r--r--source/blender/blenlib/BLI_set_slots.hh415
1 files changed, 415 insertions, 0 deletions
diff --git a/source/blender/blenlib/BLI_set_slots.hh b/source/blender/blenlib/BLI_set_slots.hh
new file mode 100644
index 00000000000..21493524bd8
--- /dev/null
+++ b/source/blender/blenlib/BLI_set_slots.hh
@@ -0,0 +1,415 @@
+/*
+ * 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 __BLI_SET_SLOTS_HH__
+#define __BLI_SET_SLOTS_HH__
+
+/** \file
+ * \ingroup bli
+ *
+ * This file contains different slot types that are supposed to be used with BLI::Set.
+ *
+ * Every slot type has to be able to hold a value of the Key type and state information.
+ * A set slot has three possible states: empty, occupied and removed.
+ *
+ * Only when a slot is occupied, it stores an instance of type Key.
+ *
+ * A set slot type has to implement a couple of methods that are explained in SimpleSetSlot.
+ * A slot type is assumed to be trivially destructible, when it is not in occupied state. So the
+ * destructor might not be called in that case.
+ */
+
+#include "BLI_memory_utils.hh"
+#include "BLI_string_ref.hh"
+
+namespace BLI {
+
+/**
+ * The simplest possible set slot. It stores the slot state and the optional key instance in
+ * separate variables. Depending on the alignment requirement of the key, many bytes might be
+ * wasted.
+ */
+template<typename Key> class SimpleSetSlot {
+ private:
+ enum State : uint8_t {
+ Empty = 0,
+ Occupied = 1,
+ Removed = 2,
+ };
+
+ State m_state;
+ AlignedBuffer<sizeof(Key), alignof(Key)> m_buffer;
+
+ public:
+ /**
+ * After the default constructor has run, the slot has to be in the empty state.
+ */
+ SimpleSetSlot()
+ {
+ m_state = Empty;
+ }
+
+ /**
+ * The destructor also has to destruct the key, if the slot is currently occupied.
+ */
+ ~SimpleSetSlot()
+ {
+ if (m_state == Occupied) {
+ this->key()->~Key();
+ }
+ }
+
+ /**
+ * The copy constructor has to copy the state. If the other slot was occupied, a copy of the key
+ * has to be made as well.
+ */
+ SimpleSetSlot(const SimpleSetSlot &other)
+ {
+ m_state = other.m_state;
+ if (other.m_state == Occupied) {
+ new (this->key()) Key(*other.key());
+ }
+ }
+
+ /**
+ * The move constructor has to copy the state. If the other slot was occupied, the key from the
+ * other slot has to be moved as well. The other slot stays in the state it was in before. Its
+ * optionally stored key remains in a moved-from state.
+ */
+ SimpleSetSlot(SimpleSetSlot &&other) noexcept
+ {
+ m_state = other.m_state;
+ if (other.m_state == Occupied) {
+ new (this->key()) Key(std::move(*other.key()));
+ }
+ }
+
+ /**
+ * Get a non-const pointer to the position where the key is stored.
+ */
+ Key *key()
+ {
+ return (Key *)m_buffer.ptr();
+ }
+
+ /**
+ * Get a const pointer to the position where the key is stored.
+ */
+ const Key *key() const
+ {
+ return (const Key *)m_buffer.ptr();
+ }
+
+ /**
+ * Return true if the slot currently contains a key.
+ */
+ bool is_occupied() const
+ {
+ return m_state == Occupied;
+ }
+
+ /**
+ * Return true if the slot is empty, i.e. it does not contain a key and is not in removed state.
+ */
+ bool is_empty() const
+ {
+ return m_state == Empty;
+ }
+
+ /**
+ * Return the hash of the currently stored key. In this simple set slot implementation, we just
+ * compute the hash here. Other implementations might store the hash in the slot instead.
+ */
+ template<typename Hash> uint32_t get_hash(const Hash &hash) const
+ {
+ BLI_assert(this->is_occupied());
+ return hash(*this->key());
+ }
+
+ /**
+ * Move the other slot into this slot and destruct it. We do destruction here, because this way
+ * we can avoid a comparison with the state, since we know the slot is occupied.
+ */
+ void relocate_occupied_here(SimpleSetSlot &other, uint32_t UNUSED(hash))
+ {
+ BLI_assert(!this->is_occupied());
+ BLI_assert(other.is_occupied());
+ m_state = Occupied;
+ new (this->key()) Key(std::move(*other.key()));
+ other.key()->~Key();
+ }
+
+ /**
+ * Return true, when this slot is occupied and contains a key that compares equal to the given
+ * key. The hash is used by other slot implementations to determine inequality faster.
+ */
+ template<typename ForwardKey, typename IsEqual>
+ bool contains(const ForwardKey &key, const IsEqual &is_equal, uint32_t UNUSED(hash)) const
+ {
+ if (m_state == Occupied) {
+ return is_equal(key, *this->key());
+ }
+ return false;
+ }
+
+ /**
+ * Change the state of this slot from empty/removed to occupied. The key has to be constructed
+ * by calling the constructor with the given key as parameter.
+ */
+ template<typename ForwardKey> void occupy(ForwardKey &&key, uint32_t UNUSED(hash))
+ {
+ BLI_assert(!this->is_occupied());
+ m_state = Occupied;
+ new (this->key()) Key(std::forward<ForwardKey>(key));
+ }
+
+ /**
+ * Change the state of this slot from occupied to removed. The key has to be destructed as well.
+ */
+ void remove()
+ {
+ BLI_assert(this->is_occupied());
+ m_state = Removed;
+ this->key()->~Key();
+ }
+};
+
+/**
+ * This set slot implementation stores the hash of the key within the slot. This helps when
+ * computing the hash or an equality check is expensive.
+ */
+template<typename Key> class HashedSetSlot {
+ private:
+ enum State : uint8_t {
+ Empty = 0,
+ Occupied = 1,
+ Removed = 2,
+ };
+
+ uint32_t m_hash;
+ State m_state;
+ AlignedBuffer<sizeof(Key), alignof(Key)> m_buffer;
+
+ public:
+ HashedSetSlot()
+ {
+ m_state = Empty;
+ }
+
+ ~HashedSetSlot()
+ {
+ if (m_state == Occupied) {
+ this->key()->~Key();
+ }
+ }
+
+ HashedSetSlot(const HashedSetSlot &other)
+ {
+ m_state = other.m_state;
+ if (other.m_state == Occupied) {
+ m_hash = other.m_hash;
+ new (this->key()) Key(*other.key());
+ }
+ }
+
+ HashedSetSlot(HashedSetSlot &&other) noexcept
+ {
+ m_state = other.m_state;
+ if (other.m_state == Occupied) {
+ m_hash = other.m_hash;
+ new (this->key()) Key(std::move(*other.key()));
+ }
+ }
+
+ Key *key()
+ {
+ return (Key *)m_buffer.ptr();
+ }
+
+ const Key *key() const
+ {
+ return (const Key *)m_buffer.ptr();
+ }
+
+ bool is_occupied() const
+ {
+ return m_state == Occupied;
+ }
+
+ bool is_empty() const
+ {
+ return m_state == Empty;
+ }
+
+ template<typename Hash> uint32_t get_hash(const Hash &UNUSED(hash)) const
+ {
+ BLI_assert(this->is_occupied());
+ return m_hash;
+ }
+
+ void relocate_occupied_here(HashedSetSlot &other, uint32_t hash)
+ {
+ BLI_assert(!this->is_occupied());
+ BLI_assert(other.is_occupied());
+ m_state = Occupied;
+ m_hash = hash;
+ new (this->key()) Key(std::move(*other.key()));
+ other.key()->~Key();
+ }
+
+ template<typename ForwardKey, typename IsEqual>
+ bool contains(const ForwardKey &key, const IsEqual &is_equal, uint32_t hash) const
+ {
+ /* m_hash might be uninitialized here, but that is ok. */
+ if (m_hash == hash) {
+ if (m_state == Occupied) {
+ return is_equal(key, *this->key());
+ }
+ }
+ return false;
+ }
+
+ template<typename ForwardKey> void occupy(ForwardKey &&key, uint32_t hash)
+ {
+ BLI_assert(!this->is_occupied());
+ m_state = Occupied;
+ m_hash = hash;
+ new (this->key()) Key(std::forward<ForwardKey>(key));
+ }
+
+ void remove()
+ {
+ BLI_assert(this->is_occupied());
+ m_state = Removed;
+ this->key()->~Key();
+ }
+};
+
+/**
+ * An IntrusiveSetSlot uses two special values of the key to indicate whether the slot is empty or
+ * removed. This saves some memory in all cases and is more efficient in many cases. The KeyInfo
+ * type indicates which specific values are used. An example for a KeyInfo implementation is
+ * PointerKeyInfo.
+ *
+ * The special key values are expected to be trivially destructible.
+ */
+template<typename Key, typename KeyInfo> class IntrusiveSetSlot {
+ private:
+ Key m_key = KeyInfo::get_empty();
+
+ public:
+ IntrusiveSetSlot() = default;
+ ~IntrusiveSetSlot() = default;
+ IntrusiveSetSlot(const IntrusiveSetSlot &other) = default;
+ IntrusiveSetSlot(IntrusiveSetSlot &&other) noexcept = default;
+
+ Key *key()
+ {
+ return &m_key;
+ }
+
+ const Key *key() const
+ {
+ return &m_key;
+ }
+
+ bool is_occupied() const
+ {
+ return KeyInfo::is_not_empty_or_removed(m_key);
+ }
+
+ bool is_empty() const
+ {
+ return KeyInfo::is_empty(m_key);
+ }
+
+ template<typename Hash> uint32_t get_hash(const Hash &hash) const
+ {
+ BLI_assert(this->is_occupied());
+ return hash(m_key);
+ }
+
+ void relocate_occupied_here(IntrusiveSetSlot &other, uint32_t UNUSED(hash))
+ {
+ BLI_assert(!this->is_occupied());
+ BLI_assert(other.is_occupied());
+ m_key = std::move(other.m_key);
+ other.m_key.~Key();
+ }
+
+ template<typename ForwardKey, typename IsEqual>
+ bool contains(const ForwardKey &key, const IsEqual &is_equal, uint32_t UNUSED(hash)) const
+ {
+ BLI_assert(KeyInfo::is_not_empty_or_removed(key));
+ return is_equal(m_key, key);
+ }
+
+ template<typename ForwardKey> void occupy(ForwardKey &&key, uint32_t UNUSED(hash))
+ {
+ BLI_assert(!this->is_occupied());
+ BLI_assert(KeyInfo::is_not_empty_or_removed(key));
+
+ m_key = std::forward<ForwardKey>(key);
+ }
+
+ void remove()
+ {
+ BLI_assert(this->is_occupied());
+ KeyInfo::remove(m_key);
+ }
+};
+
+/**
+ * This exists just to make it more convenient to define which special integer values can be used
+ * to indicate an empty and removed value.
+ */
+template<typename Int, Int EmptyValue, Int RemovedValue>
+using IntegerSetSlot = IntrusiveSetSlot<Int, TemplatedKeyInfo<Int, EmptyValue, RemovedValue>>;
+
+template<typename Key> struct DefaultSetSlot;
+
+/**
+ * Use SimpleSetSlot by default, because it is the smallest slot type that works for all key types.
+ */
+template<typename Key> struct DefaultSetSlot {
+ using type = SimpleSetSlot<Key>;
+};
+
+/**
+ * Store the hash of a string in the slot by default. Recomputing the hash or doing string
+ * comparisons can be relatively costly.
+ */
+template<> struct DefaultSetSlot<std::string> {
+ using type = HashedSetSlot<std::string>;
+};
+template<> struct DefaultSetSlot<StringRef> {
+ using type = HashedSetSlot<StringRef>;
+};
+template<> struct DefaultSetSlot<StringRefNull> {
+ using type = HashedSetSlot<StringRefNull>;
+};
+
+/**
+ * Use a special slot type for pointer keys, because we can store whether a slot is empty or
+ * removed with special pointer values.
+ */
+template<typename Key> struct DefaultSetSlot<Key *> {
+ using type = IntrusiveSetSlot<Key *, PointerKeyInfo<Key *>>;
+};
+
+} // namespace BLI
+
+#endif /* __BLI_SET_SLOTS_HH__ */