Blender  V2.93
BLI_virtual_array.hh
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 #pragma once
18 
40 #include "BLI_span.hh"
41 
42 namespace blender {
43 
44 /* An immutable virtual array. */
45 template<typename T> class VArray {
46  protected:
48 
49  public:
51  {
52  BLI_assert(size_ >= 0);
53  }
54 
55  virtual ~VArray() = default;
56 
57  T get(const int64_t index) const
58  {
59  BLI_assert(index >= 0);
60  BLI_assert(index < size_);
61  return this->get_impl(index);
62  }
63 
64  int64_t size() const
65  {
66  return size_;
67  }
68 
69  bool is_empty() const
70  {
71  return size_ == 0;
72  }
73 
74  /* Returns true when the virtual array is stored as a span internally. */
75  bool is_span() const
76  {
77  if (size_ == 0) {
78  return true;
79  }
80  return this->is_span_impl();
81  }
82 
83  /* Returns the internally used span of the virtual array. This invokes undefined behavior is the
84  * virtual array is not stored as a span internally. */
85  Span<T> get_span() const
86  {
87  BLI_assert(this->is_span());
88  if (size_ == 0) {
89  return {};
90  }
91  return this->get_span_impl();
92  }
93 
94  /* Returns true when the virtual array returns the same value for every index. */
95  bool is_single() const
96  {
97  if (size_ == 1) {
98  return true;
99  }
100  return this->is_single_impl();
101  }
102 
103  /* Returns the value that is returned for every index. This invokes undefined behavior if the
104  * virtual array would not return the same value for every index. */
105  T get_single() const
106  {
107  BLI_assert(this->is_single());
108  if (size_ == 1) {
109  return this->get(0);
110  }
111  return this->get_single_impl();
112  }
113 
114  T operator[](const int64_t index) const
115  {
116  return this->get(index);
117  }
118 
119  protected:
120  virtual T get_impl(const int64_t index) const = 0;
121 
122  virtual bool is_span_impl() const
123  {
124  return false;
125  }
126 
127  virtual Span<T> get_span_impl() const
128  {
130  return {};
131  }
132 
133  virtual bool is_single_impl() const
134  {
135  return false;
136  }
137 
138  virtual T get_single_impl() const
139  {
140  /* Provide a default implementation, so that subclasses don't have to provide it. This method
141  * should never be called because `is_single_impl` returns false by default. */
143  return T();
144  }
145 };
146 
151 template<typename T> class VArrayForSpan final : public VArray<T> {
152  private:
153  const T *data_;
154 
155  public:
157  {
158  }
159 
160  protected:
161  T get_impl(const int64_t index) const override
162  {
163  return data_[index];
164  }
165 
166  bool is_span_impl() const override
167  {
168  return true;
169  }
170 
171  Span<T> get_span_impl() const override
172  {
173  return Span<T>(data_, this->size_);
174  }
175 };
176 
182 template<typename T> class VArrayForSingle final : public VArray<T> {
183  private:
184  T value_;
185 
186  public:
187  VArrayForSingle(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
188  {
189  }
190 
191  protected:
192  T get_impl(const int64_t UNUSED(index)) const override
193  {
194  return value_;
195  }
196 
197  bool is_span_impl() const override
198  {
199  return this->size_ == 1;
200  }
201 
202  Span<T> get_span_impl() const override
203  {
204  return Span<T>(&value_, 1);
205  }
206 
207  bool is_single_impl() const override
208  {
209  return true;
210  }
211 
212  T get_single_impl() const override
213  {
214  return value_;
215  }
216 };
217 
226 template<typename T, typename Func>
227 inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool enable = true)
228 {
229  /* Support disabling the devirtualization to simplify benchmarking. */
230  if (enable) {
231  if (varray.is_single()) {
232  /* `VArrayForSingle` can be used for devirtualization, because it is declared `final`. */
233  const VArrayForSingle<T> varray_single{varray.get_single(), varray.size()};
234  func(varray_single);
235  return;
236  }
237  if (varray.is_span()) {
238  /* `VArrayForSpan` can be used for devirtualization, because it is declared `final`. */
239  const VArrayForSpan<T> varray_span{varray.get_span()};
240  func(varray_span);
241  return;
242  }
243  }
244  func(varray);
245 }
246 
252 template<typename T1, typename T2, typename Func>
253 inline void devirtualize_varray2(const VArray<T1> &varray1,
254  const VArray<T2> &varray2,
255  const Func &func,
256  bool enable = true)
257 {
258  /* Support disabling the devirtualization to simplify benchmarking. */
259  if (enable) {
260  const bool is_span1 = varray1.is_span();
261  const bool is_span2 = varray2.is_span();
262  const bool is_single1 = varray1.is_single();
263  const bool is_single2 = varray2.is_single();
264  if (is_span1 && is_span2) {
265  const VArrayForSpan<T1> varray1_span{varray1.get_span()};
266  const VArrayForSpan<T2> varray2_span{varray2.get_span()};
267  func(varray1_span, varray2_span);
268  return;
269  }
270  if (is_span1 && is_single2) {
271  const VArrayForSpan<T1> varray1_span{varray1.get_span()};
272  const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
273  func(varray1_span, varray2_single);
274  return;
275  }
276  if (is_single1 && is_span2) {
277  const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
278  const VArrayForSpan<T2> varray2_span{varray2.get_span()};
279  func(varray1_single, varray2_span);
280  return;
281  }
282  if (is_single1 && is_single2) {
283  const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
284  const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
285  func(varray1_single, varray2_single);
286  return;
287  }
288  }
289  /* This fallback is used even when one of the inputs could be optimized. It's probably not worth
290  * it to optimize just one of the inputs, because then the compiler still has to call into
291  * unknown code, which inhibits many compiler optimizations. */
292  func(varray1, varray2);
293 }
294 
295 } // namespace blender
#define BLI_assert_unreachable()
Definition: BLI_assert.h:96
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define final(a, b, c)
Definition: BLI_hash.h:35
#define UNUSED(x)
T get_single_impl() const override
VArrayForSingle(T value, const int64_t size)
bool is_span_impl() const override
bool is_single_impl() const override
T get_impl(const int64_t UNUSED(index)) const override
Span< T > get_span_impl() const override
bool is_span_impl() const override
VArrayForSpan(const Span< T > data)
Span< T > get_span_impl() const override
T get_impl(const int64_t index) const override
bool is_empty() const
T operator[](const int64_t index) const
int64_t size() const
virtual T get_single_impl() const
bool is_single() const
virtual Span< T > get_span_impl() const
VArray(const int64_t size)
virtual T get_impl(const int64_t index) const =0
virtual ~VArray()=default
T get(const int64_t index) const
Span< T > get_span() const
virtual bool is_span_impl() const
virtual bool is_single_impl() const
bool is_span() const
T * data_
#define T
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
void devirtualize_varray2(const VArray< T1 > &varray1, const VArray< T2 > &varray2, const Func &func, bool enable=true)
__int64 int64_t
Definition: stdint.h:92