2020-06-08 17:37:43 +02:00
|
|
|
/*
|
|
|
|
* 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"
|
2020-06-09 11:58:47 +02:00
|
|
|
#include "BLI_span.hh"
|
2021-09-15 11:02:39 +02:00
|
|
|
#include "BLI_vector.hh"
|
2020-06-08 17:37:43 +02:00
|
|
|
|
2020-06-09 10:27:24 +02:00
|
|
|
namespace blender {
|
2020-06-08 17:37:43 +02:00
|
|
|
|
|
|
|
class IndexMask {
|
|
|
|
private:
|
|
|
|
/* The underlying reference to sorted integers. */
|
2020-07-20 12:16:20 +02:00
|
|
|
Span<int64_t> indices_;
|
2020-06-08 17:37:43 +02:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2020-07-20 12:16:20 +02:00
|
|
|
IndexMask(Span<int64_t> indices) : indices_(indices)
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
2021-08-20 11:41:24 +02:00
|
|
|
BLI_assert(IndexMask::indices_are_valid_index_mask(indices));
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Use this method when you know that no indices are skipped. It is more efficient than preparing
|
|
|
|
* an integer array all the time.
|
|
|
|
*/
|
2020-07-03 14:15:05 +02:00
|
|
|
IndexMask(IndexRange range) : indices_(range.as_span())
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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});
|
|
|
|
*/
|
2020-07-20 12:16:20 +02:00
|
|
|
IndexMask(const std::initializer_list<int64_t> &indices) : IndexMask(Span<int64_t>(indices))
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates an IndexMask that references the indices [0, n-1].
|
|
|
|
*/
|
2020-07-20 12:16:20 +02:00
|
|
|
explicit IndexMask(int64_t n) : IndexMask(IndexRange(n))
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-20 11:41:24 +02:00
|
|
|
/** Checks that the indices are non-negative and in ascending order. */
|
|
|
|
static bool indices_are_valid_index_mask(Span<int64_t> indices)
|
|
|
|
{
|
|
|
|
if (!indices.is_empty()) {
|
|
|
|
if (indices.first() < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int64_t i = 1; i < indices.size(); i++) {
|
|
|
|
if (indices[i - 1] >= indices[i]) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-20 12:16:20 +02:00
|
|
|
operator Span<int64_t>() const
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
return indices_;
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
|
|
|
|
2020-07-20 12:16:20 +02:00
|
|
|
const int64_t *begin() const
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
return indices_.begin();
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
|
|
|
|
2020-07-20 12:16:20 +02:00
|
|
|
const int64_t *end() const
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
return indices_.end();
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-06-02 08:28:46 -04:00
|
|
|
* Returns the n-th index referenced by this IndexMask. The `index_range` method returns an
|
2020-06-08 17:37:43 +02:00
|
|
|
* IndexRange containing all indices that can be used as parameter here.
|
|
|
|
*/
|
2020-07-20 12:16:20 +02:00
|
|
|
int64_t operator[](int64_t n) const
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
return indices_[n];
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2020-07-20 12:16:20 +02:00
|
|
|
int64_t min_array_size() const
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
if (indices_.size() == 0) {
|
2020-06-08 17:37:43 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else {
|
2020-07-03 14:15:05 +02:00
|
|
|
return indices_.last() + 1;
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-20 12:16:20 +02:00
|
|
|
Span<int64_t> indices() const
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
return indices_;
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if this IndexMask does not skip any indices. This check requires O(1) time.
|
|
|
|
*/
|
|
|
|
bool is_range() const
|
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
return indices_.size() > 0 && indices_.last() - indices_.first() == indices_.size() - 1;
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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());
|
2020-07-03 14:15:05 +02:00
|
|
|
return IndexRange{indices_.first(), indices_.size()};
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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<typename CallbackT> void foreach_index(const CallbackT &callback) const
|
|
|
|
{
|
|
|
|
if (this->is_range()) {
|
|
|
|
IndexRange range = this->as_range();
|
2020-07-20 12:16:20 +02:00
|
|
|
for (int64_t i : range) {
|
2020-06-08 17:37:43 +02:00
|
|
|
callback(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2020-07-20 12:16:20 +02:00
|
|
|
for (int64_t i : indices_) {
|
2020-06-08 17:37:43 +02:00
|
|
|
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
|
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
return indices_.index_range();
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the largest index that is referenced by this IndexMask.
|
|
|
|
*/
|
2020-07-20 12:16:20 +02:00
|
|
|
int64_t last() const
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
return indices_.last();
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the number of indices referenced by this IndexMask.
|
|
|
|
*/
|
2020-07-20 12:16:20 +02:00
|
|
|
int64_t size() const
|
2020-06-08 17:37:43 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
return indices_.size();
|
2020-06-08 17:37:43 +02:00
|
|
|
}
|
2021-08-20 11:41:24 +02:00
|
|
|
|
|
|
|
bool is_empty() const
|
|
|
|
{
|
|
|
|
return indices_.is_empty();
|
|
|
|
}
|
2021-09-15 11:02:39 +02:00
|
|
|
|
2021-11-24 17:49:33 +01:00
|
|
|
IndexMask slice(IndexRange slice) const;
|
2021-09-15 11:02:39 +02:00
|
|
|
IndexMask slice_and_offset(IndexRange slice, Vector<int64_t> &r_new_indices) const;
|
2020-06-08 17:37:43 +02:00
|
|
|
};
|
|
|
|
|
2020-06-09 10:27:24 +02:00
|
|
|
} // namespace blender
|