/* * 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. */ #pragma once /** \file * \ingroup bli * * An IndexMask references an array of unsigned integers with the following property: * The integers must be in ascending order and there must not be duplicates. * * Remember that the array is only referenced and not owned by an IndexMask instance. * * In most cases the integers in the array represent some indices into another array. So they * "select" or "mask" a some elements in that array. Hence the name IndexMask. * * The invariant stated above has the nice property that it makes it easy to check if an integer * array is an IndexRange, i.e. no indices are skipped. That allows functions to implement two code * paths: One where it iterates over the index array and one where it iterates over the index * range. The latter one is more efficient due to less memory reads and potential usage of SIMD * instructions. * * The IndexMask.foreach_index method helps writing code that implements both code paths at the * same time. */ #include "BLI_index_range.hh" #include "BLI_span.hh" namespace blender { class IndexMask { private: /* The underlying reference to sorted integers. */ Span indices_; public: /* Creates an IndexMask that contains no indices. */ IndexMask() = default; /** * Create an IndexMask using the given integer array. * This constructor asserts that the given integers are in ascending order and that there are no * duplicates. */ IndexMask(Span indices) : indices_(indices) { #ifdef DEBUG for (int64_t i = 1; i < indices.size(); i++) { BLI_assert(indices[i - 1] < indices[i]); } #endif } /** * Use this method when you know that no indices are skipped. It is more efficient than preparing * an integer array all the time. */ IndexMask(IndexRange range) : indices_(range.as_span()) { } /** * Construct an IndexMask from a sorted list of indices. Note, the created IndexMask is only * valid as long as the initializer_list is valid. * * Don't do this: * IndexMask mask = {3, 4, 5}; * * Do this: * do_something_with_an_index_mask({3, 4, 5}); */ IndexMask(const std::initializer_list &indices) : IndexMask(Span(indices)) { } /** * Creates an IndexMask that references the indices [0, n-1]. */ explicit IndexMask(int64_t n) : IndexMask(IndexRange(n)) { } operator Span() const { return indices_; } const int64_t *begin() const { return indices_.begin(); } const int64_t *end() const { return indices_.end(); } /** * Returns the n-th index referenced by this IndexMask. The `index_range` method returns an * IndexRange containing all indices that can be used as parameter here. */ int64_t operator[](int64_t n) const { return indices_[n]; } /** * Returns the minimum size an array has to have, if the integers in this IndexMask are going to * be used as indices in that array. */ int64_t min_array_size() const { if (indices_.size() == 0) { return 0; } else { return indices_.last() + 1; } } Span indices() const { return indices_; } /** * Returns true if this IndexMask does not skip any indices. This check requires O(1) time. */ bool is_range() const { return indices_.size() > 0 && indices_.last() - indices_.first() == indices_.size() - 1; } /** * Returns the IndexRange referenced by this IndexMask. This method should only be called after * the caller made sure that this IndexMask is actually a range. */ IndexRange as_range() const { BLI_assert(this->is_range()); return IndexRange{indices_.first(), indices_.size()}; } /** * Calls the given callback for every referenced index. The callback has to take one unsigned * integer as parameter. * * This method implements different code paths for the cases when the IndexMask represents a * range or not. */ template void foreach_index(const CallbackT &callback) const { if (this->is_range()) { IndexRange range = this->as_range(); for (int64_t i : range) { callback(i); } } else { for (int64_t i : indices_) { callback(i); } } } /** * Returns an IndexRange that can be used to index this IndexMask. * * The range is [0, number of indices - 1]. * * This is not to be confused with the `as_range` method. */ IndexRange index_range() const { return indices_.index_range(); } /** * Returns the largest index that is referenced by this IndexMask. */ int64_t last() const { return indices_.last(); } /** * Returns the number of indices referenced by this IndexMask. */ int64_t size() const { return indices_.size(); } }; } // namespace blender