1 /* SPDX-FileCopyrightText: 2023 Blender Authors
3 * SPDX-License-Identifier: GPL-2.0-or-later */
10 #include "BLI_index_mask_fwd.hh"
11 #include "BLI_index_range.hh"
12 #include "BLI_span.hh"
14 namespace blender::offset_indices
{
16 /** Utility struct that can be passed into a function to skip a check for sorted indices. */
17 struct NoSortCheck
{};
20 * This class is a thin wrapper around an array of increasing indices that makes it easy to
21 * retrieve an index range at a specific index. Each index range is typically a representation of
22 * a contiguous chunk of a larger array.
24 * Another common way to store many index ranges is to store the start and size of every range.
25 * Using #OffsetIndices instead requires that chunks are ordered consecutively but halves the
26 * memory consumption. Another downside is that the underlying array has to be one element longer
27 * than the total number of ranges. The extra element necessary to encode the size of the last
28 * range without requiring a branch for each range access.
30 template<typename T
> class OffsetIndices
{
32 static_assert(std::is_integral_v
<T
>);
37 OffsetIndices() = default;
38 OffsetIndices(const Span
<T
> offsets
) : offsets_(offsets
)
40 BLI_assert(offsets_
.size() < 2 || std::is_sorted(offsets_
.begin(), offsets_
.end()));
44 * Same as above, but skips the debug check that indices are sorted, because that can have a
45 * high performance impact making debug builds unusable for files that would be fine otherwise.
46 * This can be used when it is known that the indices are sorted already.
48 OffsetIndices(const Span
<T
> offsets
, NoSortCheck
/*no_sort_check*/) : offsets_(offsets
) {}
50 /** Return the total number of elements in the referenced arrays. */
53 return offsets_
.size() > 1 ? offsets_
.last() - offsets_
.first() : 0;
57 * Return the number of ranges encoded by the offsets, not including the last value used
62 return std::max
<int64_t>(offsets_
.size() - 1, 0);
67 return this->size() == 0;
70 IndexRange
index_range() const
72 return IndexRange(this->size());
75 IndexRange
operator[](const int64_t index
) const
77 BLI_assert(index
>= 0);
78 BLI_assert(index
< offsets_
.size() - 1);
79 const int64_t begin
= offsets_
[index
];
80 const int64_t end
= offsets_
[index
+ 1];
81 return IndexRange::from_begin_end(begin
, end
);
84 IndexRange
operator[](const IndexRange indices
) const
86 const int64_t begin
= offsets_
[indices
.start()];
87 const int64_t end
= offsets_
[indices
.one_after_last()];
88 return IndexRange::from_begin_end(begin
, end
);
92 * Return a subset of the offsets describing the specified range of source elements.
93 * This is a slice into the source ranges rather than the indexed elements described by the
96 OffsetIndices
slice(const IndexRange range
) const
98 BLI_assert(range
.is_empty() || offsets_
.index_range().drop_back(1).contains(range
.last()));
99 return OffsetIndices(offsets_
.slice(range
.start(), range
.size() + 1));
109 * References many separate spans in a larger contiguous array. This gives a more efficient way to
110 * store many grouped arrays, without requiring many small allocations, giving the general benefits
111 * of using contiguous memory.
113 * \note If the offsets are shared between many #GroupedSpan objects, it will be more efficient
114 * to retrieve the #IndexRange only once and slice each span.
116 template<typename T
> struct GroupedSpan
{
117 OffsetIndices
<int> offsets
;
120 GroupedSpan() = default;
121 GroupedSpan(OffsetIndices
<int> offsets
, Span
<T
> data
) : offsets(offsets
), data(data
)
123 BLI_assert(this->offsets
.total_size() == this->data
.size());
126 Span
<T
> operator[](const int64_t index
) const
128 return this->data
.slice(this->offsets
[index
]);
133 return this->offsets
.size();
136 IndexRange
index_range() const
138 return this->offsets
.index_range();
141 bool is_empty() const
143 return this->data
.size() == 0;
148 * Turn an array of sizes into the offset at each index including all previous sizes.
150 OffsetIndices
<int> accumulate_counts_to_offsets(MutableSpan
<int> counts_to_offsets
,
151 int start_offset
= 0);
153 std::optional
<OffsetIndices
<int>> accumulate_counts_to_offsets_with_overflow_check(
154 MutableSpan
<int> counts_to_offsets
, int start_offset
= 0);
156 /** Create offsets where every group has the same size. */
157 void fill_constant_group_size(int size
, int start_offset
, MutableSpan
<int> offsets
);
159 /** Copy the number of indices in every group in the mask to the corresponding index. */
160 void copy_group_sizes(OffsetIndices
<int> offsets
, const IndexMask
&mask
, MutableSpan
<int> sizes
);
162 /** Gather the number of indices in each indexed group to sizes. */
163 void gather_group_sizes(OffsetIndices
<int> offsets
, const IndexMask
&mask
, MutableSpan
<int> sizes
);
164 void gather_group_sizes(OffsetIndices
<int> offsets
, Span
<int> indices
, MutableSpan
<int> sizes
);
166 /** Calculate the total size of all the referenced groups. */
167 int sum_group_sizes(OffsetIndices
<int> offsets
, const IndexMask
&mask
);
168 int sum_group_sizes(OffsetIndices
<int> offsets
, Span
<int> indices
);
170 /** Build new offsets that contains only the groups chosen by \a selection. */
171 OffsetIndices
<int> gather_selected_offsets(OffsetIndices
<int> src_offsets
,
172 const IndexMask
&selection
,
174 MutableSpan
<int> dst_offsets
);
175 inline OffsetIndices
<int> gather_selected_offsets(OffsetIndices
<int> src_offsets
,
176 const IndexMask
&selection
,
177 MutableSpan
<int> dst_offsets
)
179 return gather_selected_offsets(src_offsets
, selection
, 0, dst_offsets
);
183 * Create a map from indexed elements to the source indices, in other words from the larger array
184 * to the smaller array.
186 void build_reverse_map(OffsetIndices
<int> offsets
, MutableSpan
<int> r_map
);
189 * Build offsets to group the elements of \a indices pointing to the same index.
191 void build_reverse_offsets(Span
<int> indices
, MutableSpan
<int> offsets
);
193 } // namespace blender::offset_indices
196 using offset_indices::GroupedSpan
;
197 using offset_indices::OffsetIndices
;
198 } // namespace blender