1 /** @addtogroup expressions
2  *  @{
3  */
4 /*
5   Copyright (C) 2016 D Levin (https://www.kfrlib.com)
6   This file is part of KFR
7 
8   KFR is free software: you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation, either version 2 of the License, or
11   (at your option) any later version.
12 
13   KFR is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17 
18   You should have received a copy of the GNU General Public License
19   along with KFR.
20 
21   If GPL is not suitable for your project, you must purchase a commercial license to use KFR.
22   Buying a commercial license is mandatory as soon as you develop commercial activities without
23   disclosing the source code of your own applications.
24   See https://www.kfrlib.com for details.
25  */
26 #pragma once
27 
28 #include "../cometa/array.hpp"
29 
30 #include "../simd/impl/function.hpp"
31 #include "../simd/read_write.hpp"
32 #include "../simd/types.hpp"
33 #include "memory.hpp"
34 
35 CMT_PRAGMA_MSVC(warning(push))
36 CMT_PRAGMA_MSVC(warning(disable : 4324))
37 
38 namespace kfr
39 {
40 
41 using univector_tag = size_t;
42 
43 enum : size_t
44 {
45     tag_array_ref      = 0,
46     tag_dynamic_vector = max_size_t,
47 };
48 
49 template <typename T, univector_tag Tag = tag_dynamic_vector>
50 struct abstract_vector;
51 
52 template <typename T, univector_tag Size>
53 struct abstract_vector : std::array<T, Size>
54 {
55     using std::array<T, Size>::array;
56 };
57 
58 template <typename T>
59 struct abstract_vector<T, tag_dynamic_vector> : std::vector<T, allocator<T>>
60 {
61     using std::vector<T, allocator<T>>::vector;
62 };
63 
64 template <typename T>
65 struct abstract_vector<T, tag_array_ref> : array_ref<T>
66 {
67     using array_ref<T>::array_ref;
68 };
69 
70 /**
71  * @brief Class that represent data in KFR. Many KFR functions can take this class as an argument.
72  * Can inherit from std::vector, std::array or keep only reference to data and its size.
73  *
74  * univector<float> is inherited from std::vector<float>
75  * univector<float, 10> is inherited from std::array<float, 10>
76  * univector<float, 0> contains only reference to data
77  *
78  * To convert a plain pointer to univector, call make_univector:
79  * @code
80  * double* buffer;
81  * size_t size;
82  * univector<double, 0> v = make_univector(buffer, size);
83  * // or pass result vector directly to a function:
84  * some_function(make_univector(buffer, size));
85  * @endcode
86  */
87 template <typename T, univector_tag Tag = tag_dynamic_vector>
88 struct univector;
89 
90 /// @brief Base class for all univector specializations.
91 template <typename T, typename Class, bool is_expression>
92 struct univector_base;
93 
94 template <typename T, typename Class>
95 struct univector_base<T, Class, true> : input_expression, output_expression
96 {
97     using input_expression::begin_block;
98     using input_expression::end_block;
99     using output_expression::begin_block;
100     using output_expression::end_block;
101 
102     template <typename U, size_t N>
operator ()kfr::univector_base103     KFR_MEM_INTRINSIC void operator()(coutput_t, size_t index, const vec<U, N>& value)
104     {
105         T* data = derived_cast<Class>(this)->data();
106         write(ptr_cast<T>(data) + index, vec<T, N>(value));
107     }
108 
109     template <typename Input, KFR_ENABLE_IF(is_input_expression<Input>)>
operator =kfr::univector_base110     KFR_MEM_INTRINSIC Class& operator=(Input&& input)
111     {
112         assign_expr(std::forward<Input>(input));
113         return *derived_cast<Class>(this);
114     }
115 
116 #define KFR_UVEC_ASGN_OP(aop, op)                                                                            \
117     template <typename Input>                                                                                \
118     KFR_MEM_INTRINSIC Class& aop(Input&& input)                                                              \
119     {                                                                                                        \
120         assign_expr(*derived_cast<Class>(this) op std::forward<Input>(input));                               \
121         return *derived_cast<Class>(this);                                                                   \
122     }
123     KFR_UVEC_ASGN_OP(operator+=, +)
124     KFR_UVEC_ASGN_OP(operator-=, -)
125     KFR_UVEC_ASGN_OP(operator*=, *)
126     KFR_UVEC_ASGN_OP(operator/=, /)
127     KFR_UVEC_ASGN_OP(operator%=, %)
128 
129     KFR_UVEC_ASGN_OP(operator&=, &)
130     KFR_UVEC_ASGN_OP(operator|=, |)
131     KFR_UVEC_ASGN_OP(operator^=, ^)
132 
133     KFR_UVEC_ASGN_OP(operator<<=, <<)
134     KFR_UVEC_ASGN_OP(operator>>=, >>)
135 
136     /// @brief Returns subrange of the vector.
137     /// If start is greater or equal to this->size, returns empty univector
138     /// If requested size is greater than this->size, returns only available elements
slicekfr::univector_base139     univector<T, 0> slice(size_t start = 0, size_t size = max_size_t)
140     {
141         T* data                = derived_cast<Class>(this)->data();
142         const size_t this_size = derived_cast<Class>(this)->size();
143         return array_ref<T>(data + start, std::min(size, start < this_size ? this_size - start : 0));
144     }
145 
146     /// @brief Returns subrange of the vector.
147     /// If start is greater or equal to this->size, returns empty univector
148     /// If requested size is greater than this->size, returns only available elements
slicekfr::univector_base149     univector<const T, 0> slice(size_t start = 0, size_t size = max_size_t) const
150     {
151         const T* data          = derived_cast<Class>(this)->data();
152         const size_t this_size = derived_cast<Class>(this)->size();
153         return array_ref<const T>(data + start, std::min(size, start < this_size ? this_size - start : 0));
154     }
155 
156     /// @brief Returns subrange of the vector starting from 0.
157     /// If requested size is greater than this->size, returns only available elements
truncatekfr::univector_base158     univector<T, 0> truncate(size_t size = max_size_t)
159     {
160         T* data                = derived_cast<Class>(this)->data();
161         const size_t this_size = derived_cast<Class>(this)->size();
162         return array_ref<T>(data, std::min(size, this_size));
163     }
164 
165     /// @brief Returns subrange of the vector starting from 0.
166     /// If requested size is greater than this->size, returns only available elements
truncatekfr::univector_base167     univector<const T, 0> truncate(size_t size = max_size_t) const
168     {
169         const T* data          = derived_cast<Class>(this)->data();
170         const size_t this_size = derived_cast<Class>(this)->size();
171         return array_ref<const T>(data, std::min(size, this_size));
172     }
173 
refkfr::univector_base174     array_ref<T> ref()
175     {
176         T* data           = get_data();
177         const size_t size = get_size();
178         return array_ref<T>(data, size);
179     }
refkfr::univector_base180     array_ref<const T> ref() const
181     {
182         const T* data     = get_data();
183         const size_t size = get_size();
184         return array_ref<const T>(data, size);
185     }
crefkfr::univector_base186     array_ref<const T> cref() const
187     {
188         const T* data     = get_data();
189         const size_t size = get_size();
190         return array_ref<const T>(data, size);
191     }
192 
ringbuf_writekfr::univector_base193     void ringbuf_write(size_t& cursor, const T* src, size_t srcsize)
194     {
195         if (srcsize == 0)
196             return;
197         // skip redundant data
198         const size_t size = get_size();
199         T* data           = get_data();
200         if (srcsize > size)
201         {
202             src     = src + srcsize / size;
203             srcsize = srcsize % size;
204         }
205         const size_t fsize = size - cursor;
206         // one fragment
207         if (srcsize <= fsize)
208         {
209             copy(data + cursor, src, srcsize);
210         }
211         else // two fragments
212         {
213             copy(data + cursor, src, fsize);
214             copy(data, src + fsize, srcsize - fsize);
215         }
216         ringbuf_step(cursor, srcsize);
217     }
218     template <size_t N>
ringbuf_writekfr::univector_base219     void ringbuf_write(size_t& cursor, const vec<T, N>& x)
220     {
221         ringbuf_write(cursor, ptr_cast<T>(&x), N);
222     }
ringbuf_writekfr::univector_base223     void ringbuf_write(size_t& cursor, const T& value)
224     {
225         T* data      = get_data();
226         data[cursor] = value;
227         ringbuf_step(cursor, 1);
228     }
ringbuf_stepkfr::univector_base229     void ringbuf_step(size_t& cursor, size_t step) const
230     {
231         const size_t size = get_size();
232         cursor            = cursor + step;
233         cursor            = cursor >= size ? cursor - size : cursor;
234     }
ringbuf_readkfr::univector_base235     void ringbuf_read(size_t& cursor, T& value)
236     {
237         T* data = get_data();
238         value   = data[cursor];
239         ringbuf_step(cursor, 1);
240     }
241     template <size_t N>
ringbuf_readkfr::univector_base242     void ringbuf_read(size_t& cursor, vec<T, N>& x)
243     {
244         ringbuf_read(cursor, ptr_cast<T>(&x), N);
245     }
ringbuf_readkfr::univector_base246     void ringbuf_read(size_t& cursor, T* dest, size_t destsize) const
247     {
248         if (destsize == 0)
249             return;
250         // skip redundant data
251         const size_t size = get_size();
252         const T* data     = get_data();
253         if (destsize > size)
254         {
255             dest     = dest + destsize / size;
256             destsize = destsize % size;
257         }
258         const size_t fsize = size - cursor;
259         // one fragment
260         if (destsize <= fsize)
261         {
262             copy(dest, data + cursor, destsize);
263         }
264         else // two fragments
265         {
266             copy(dest, data + cursor, fsize);
267             copy(dest + fsize, data, destsize - fsize);
268         }
269         ringbuf_step(cursor, destsize);
270     }
271 
272 protected:
273     template <typename Input>
assign_exprkfr::univector_base274     KFR_MEM_INTRINSIC void assign_expr(Input&& input)
275     {
276         process(*derived_cast<Class>(this), std::forward<Input>(input));
277     }
278 
279 private:
get_sizekfr::univector_base280     KFR_MEM_INTRINSIC size_t get_size() const { return derived_cast<Class>(this)->size(); }
get_datakfr::univector_base281     KFR_MEM_INTRINSIC const T* get_data() const { return derived_cast<Class>(this)->data(); }
get_datakfr::univector_base282     KFR_MEM_INTRINSIC T* get_data() { return derived_cast<Class>(this)->data(); }
283 
copykfr::univector_base284     static void copy(T* dest, const T* src, size_t size)
285     {
286         for (size_t i = 0; i < size; ++i)
287             *dest++ = *src++;
288     }
289 };
290 
291 template <typename T, typename Class>
292 struct univector_base<T, Class, false>
293 {
refkfr::univector_base294     array_ref<T> ref()
295     {
296         T* data           = get_data();
297         const size_t size = get_size();
298         return array_ref<T>(data, size);
299     }
refkfr::univector_base300     array_ref<const T> ref() const
301     {
302         const T* data     = get_data();
303         const size_t size = get_size();
304         return array_ref<const T>(data, size);
305     }
crefkfr::univector_base306     array_ref<const T> cref() const
307     {
308         const T* data     = get_data();
309         const size_t size = get_size();
310         return array_ref<const T>(data, size);
311     }
312 
313     template <typename Input, KFR_ENABLE_IF(is_input_expression<Input>)>
operator =kfr::univector_base314     KFR_MEM_INTRINSIC Class& operator=(Input&& input)
315     {
316         static_assert(sizeof(Input) == 0, "Can't assign expression to non-expression");
317         return *derived_cast<Class>(this);
318     }
319 
320 private:
get_sizekfr::univector_base321     KFR_MEM_INTRINSIC size_t get_size() const { return derived_cast<Class>(this)->size(); }
get_datakfr::univector_base322     KFR_MEM_INTRINSIC const T* get_data() const { return derived_cast<Class>(this)->data(); }
get_datakfr::univector_base323     KFR_MEM_INTRINSIC T* get_data() { return derived_cast<Class>(this)->data(); }
324 };
325 
326 template <typename T, size_t Size>
327 struct alignas(platform<>::maximum_vector_alignment) univector
328     : std::array<T, Size>,
329       univector_base<T, univector<T, Size>, is_vec_element<T>>
330 {
331     using std::array<T, Size>::size;
332     using size_type = size_t;
333 #if !defined CMT_COMPILER_MSVC || defined CMT_COMPILER_CLANG
univectorkfr::univector334     univector(univector& v) : univector(const_cast<const univector&>(v)) {}
335 #endif
336     univector(const univector& v)   = default;
337     univector(univector&&) noexcept = default;
338     template <typename Input, KFR_ENABLE_IF(is_input_expression<Input>)>
univectorkfr::univector339     univector(Input&& input)
340     {
341         this->assign_expr(std::forward<Input>(input));
342     }
343     template <typename... Args>
univectorkfr::univector344     constexpr univector(const T& x, const Args&... args) CMT_NOEXCEPT
345         : std::array<T, Size>{ { x, static_cast<T>(args)... } }
346     {
347     }
348 
349     constexpr univector() CMT_NOEXCEPT_SPEC(noexcept(std::array<T, Size>())) = default;
univectorkfr::univector350     constexpr univector(size_t, const T& value) { std::fill(this->begin(), this->end(), value); }
351     constexpr static bool size_known   = true;
352     constexpr static bool is_array     = true;
353     constexpr static bool is_array_ref = false;
354     constexpr static bool is_vector    = false;
355     constexpr static bool is_aligned   = true;
356     constexpr static bool is_pod       = kfr::is_pod<T>;
357     using value_type                   = T;
358 
getkfr::univector359     value_type get(size_t index, value_type fallback_value) const CMT_NOEXCEPT
360     {
361         return index < this->size() ? this->operator[](index) : fallback_value;
362     }
363     using univector_base<T, univector, is_vec_element<T>>::operator=;
364 
resizekfr::univector365     void resize(size_t) CMT_NOEXCEPT {}
366 };
367 
368 template <typename T>
369 struct univector<T, tag_array_ref> : array_ref<T>,
370                                      univector_base<T, univector<T, tag_array_ref>, is_vec_element<T>>
371 {
372     using array_ref<T>::size;
373     using array_ref<T>::array_ref;
374     using size_type = size_t;
375 #if !defined CMT_COMPILER_MSVC || defined CMT_COMPILER_CLANG
univectorkfr::univector376     univector(univector& v) : univector(const_cast<const univector&>(v)) {}
377 #endif
378     univector(const univector& v)   = default;
379     univector(univector&&) noexcept = default;
univectorkfr::univector380     constexpr univector(const array_ref<T>& other) : array_ref<T>(other) {}
univectorkfr::univector381     constexpr univector(array_ref<T>&& other) : array_ref<T>(std::move(other)) {}
382 
383     template <univector_tag Tag>
univectorkfr::univector384     constexpr univector(const univector<T, Tag>& other) : array_ref<T>(other.data(), other.size())
385     {
386     }
387     template <univector_tag Tag>
univectorkfr::univector388     constexpr univector(univector<T, Tag>& other) : array_ref<T>(other.data(), other.size())
389     {
390     }
391     template <typename U, univector_tag Tag, KFR_ENABLE_IF(is_same<remove_const<T>, U>&& is_const<T>)>
univectorkfr::univector392     constexpr univector(const univector<U, Tag>& other) : array_ref<T>(other.data(), other.size())
393     {
394     }
395     template <typename U, univector_tag Tag, KFR_ENABLE_IF(is_same<remove_const<T>, U>&& is_const<T>)>
univectorkfr::univector396     constexpr univector(univector<U, Tag>& other) : array_ref<T>(other.data(), other.size())
397     {
398     }
resizekfr::univector399     void resize(size_t) CMT_NOEXCEPT {}
400     constexpr static bool size_known   = false;
401     constexpr static bool is_array     = false;
402     constexpr static bool is_array_ref = true;
403     constexpr static bool is_vector    = false;
404     constexpr static bool is_aligned   = false;
405     using value_type                   = remove_const<T>;
406 
getkfr::univector407     value_type get(size_t index, value_type fallback_value) const CMT_NOEXCEPT
408     {
409         return index < this->size() ? this->operator[](index) : fallback_value;
410     }
411     using univector_base<T, univector, is_vec_element<T>>::operator=;
412 
refkfr::univector413     univector<T, tag_array_ref>& ref() && { return *this; }
414 };
415 
416 template <typename T>
417 struct univector<T, tag_dynamic_vector>
418     : std::vector<T, allocator<T>>, univector_base<T, univector<T, tag_dynamic_vector>, is_vec_element<T>>
419 {
420     using std::vector<T, allocator<T>>::size;
421     using std::vector<T, allocator<T>>::vector;
422     using size_type = size_t;
423 #if !defined CMT_COMPILER_MSVC || defined CMT_COMPILER_CLANG
univectorkfr::univector424     univector(univector& v) : univector(const_cast<const univector&>(v)) {}
425 #endif
426     univector(const univector& v)   = default;
427     univector(univector&&) noexcept = default;
428     template <typename Input, KFR_ENABLE_IF(is_input_expression<Input>)>
univectorkfr::univector429     univector(Input&& input)
430     {
431         static_assert(!is_infinite<Input>, "Dynamically sized vector requires finite input expression");
432         this->resize(input.size());
433         this->assign_expr(std::forward<Input>(input));
434     }
435     constexpr univector() CMT_NOEXCEPT_SPEC(noexcept(std::vector<T, allocator<T>>())) = default;
univectorkfr::univector436     constexpr univector(const std::vector<T, allocator<T>>& other) : std::vector<T, allocator<T>>(other) {}
univectorkfr::univector437     constexpr univector(std::vector<T, allocator<T>>&& other) : std::vector<T, allocator<T>>(std::move(other))
438     {
439     }
univectorkfr::univector440     constexpr univector(const array_ref<T>& other) : std::vector<T, allocator<T>>(other.begin(), other.end())
441     {
442     }
univectorkfr::univector443     constexpr univector(const array_ref<const T>& other)
444         : std::vector<T, allocator<T>>(other.begin(), other.end())
445     {
446     }
447     template <typename Allocator>
448     constexpr univector(const std::vector<T, Allocator>&) = delete;
449     template <typename Allocator>
450     constexpr univector(std::vector<T, Allocator>&&) = delete;
451     constexpr static bool size_known                 = false;
452     constexpr static bool is_array                   = false;
453     constexpr static bool is_array_ref               = false;
454     constexpr static bool is_vector                  = true;
455     constexpr static bool is_aligned                 = true;
456     using value_type                                 = T;
457 
getkfr::univector458     value_type get(size_t index, value_type fallback_value) const CMT_NOEXCEPT
459     {
460         return index < this->size() ? this->operator[](index) : fallback_value;
461     }
462     using univector_base<T, univector, is_vec_element<T>>::operator=;
463     univector& operator=(const univector&) = default;
464     template <typename Input, KFR_ENABLE_IF(is_input_expression<Input>)>
operator =kfr::univector465     KFR_MEM_INTRINSIC univector& operator=(Input&& input)
466     {
467         if (input.size() != infinite_size)
468             this->resize(input.size());
469         this->assign_expr(std::forward<Input>(input));
470         return *this;
471     }
472 };
473 
474 /// @brief Alias for ``univector<T, tag_array_ref>``;
475 template <typename T>
476 using univector_ref = univector<T, tag_array_ref>;
477 
478 /// @brief Alias for ``univector<T, tag_dynamic_vector>``;
479 template <typename T>
480 using univector_dyn = univector<T, tag_dynamic_vector>;
481 
482 template <typename T, univector_tag Size1 = tag_dynamic_vector, univector_tag Size2 = tag_dynamic_vector>
483 using univector2d = abstract_vector<univector<T, Size2>, Size1>;
484 
485 template <typename T, univector_tag Size1 = tag_dynamic_vector, univector_tag Size2 = tag_dynamic_vector,
486           univector_tag Size3 = tag_dynamic_vector>
487 using univector3d = abstract_vector<abstract_vector<univector<T, Size3>, Size2>, Size1>;
488 
489 /// @brief Creates univector from data and size
490 template <typename T>
make_univector(T * data,size_t size)491 KFR_INTRINSIC univector_ref<T> make_univector(T* data, size_t size)
492 {
493     return univector_ref<T>(data, size);
494 }
495 
496 /// @brief Creates univector from data and size
497 template <typename T>
make_univector(const T * data,size_t size)498 KFR_INTRINSIC univector_ref<const T> make_univector(const T* data, size_t size)
499 {
500     return univector_ref<const T>(data, size);
501 }
502 
503 /// @brief Creates univector from a container (must have data() and size() methods)
504 template <typename Container, KFR_ENABLE_IF(kfr::has_data_size<Container>),
505           typename T = value_type_of<Container>>
make_univector(const Container & container)506 KFR_INTRINSIC univector_ref<const T> make_univector(const Container& container)
507 {
508     return univector_ref<const T>(container.data(), container.size());
509 }
510 
511 /// @brief Creates univector from a container (must have data() and size() methods)
512 template <typename Container, KFR_ENABLE_IF(kfr::has_data_size<Container>),
513           typename T = value_type_of<Container>>
make_univector(Container & container)514 KFR_INTRINSIC univector_ref<T> make_univector(Container& container)
515 {
516     return univector_ref<T>(container.data(), container.size());
517 }
518 
519 /// @brief Creates univector from a sized array
520 template <typename T, size_t N>
make_univector(T (& arr)[N])521 KFR_INTRINSIC univector_ref<T> make_univector(T (&arr)[N])
522 {
523     return univector_ref<T>(arr, N);
524 }
525 
526 /// @brief Creates univector from a sized array
527 template <typename T, size_t N>
make_univector(const T (& arr)[N])528 KFR_INTRINSIC univector_ref<const T> make_univector(const T (&arr)[N])
529 {
530     return univector_ref<const T>(arr, N);
531 }
532 
533 /// @brief Single producer single consumer lock-free ring buffer
534 template <typename T>
535 struct lockfree_ring_buffer
536 {
lockfree_ring_bufferkfr::lockfree_ring_buffer537     lockfree_ring_buffer() : front(0), tail(0) {}
538 
sizekfr::lockfree_ring_buffer539     size_t size() const
540     {
541         return tail.load(std::memory_order_relaxed) - front.load(std::memory_order_relaxed);
542     }
543 
544     template <univector_tag Tag>
try_enqueuekfr::lockfree_ring_buffer545     size_t try_enqueue(const T* source, size_t size, univector<T, Tag>& buffer, bool partial = false)
546     {
547         const size_t cur_tail   = tail.load(std::memory_order_relaxed);
548         const size_t avail_size = buffer.size() - (cur_tail - front.load(std::memory_order_relaxed));
549         if (size > avail_size)
550         {
551             if (!partial)
552                 return 0;
553             size = std::min(size, avail_size);
554         }
555         std::atomic_thread_fence(std::memory_order_acquire);
556 
557         const size_t real_tail  = cur_tail % buffer.size();
558         const size_t first_size = std::min(buffer.size() - real_tail, size);
559         builtin_memcpy(buffer.data() + real_tail, source, first_size * sizeof(T));
560         builtin_memcpy(buffer.data(), source + first_size, (size - first_size) * sizeof(T));
561 
562         std::atomic_thread_fence(std::memory_order_release);
563 
564         tail.store(cur_tail + size, std::memory_order_relaxed);
565         return size;
566     }
567 
568     template <univector_tag Tag>
try_dequeuekfr::lockfree_ring_buffer569     size_t try_dequeue(T* dest, size_t size, const univector<T, Tag>& buffer, bool partial = false)
570     {
571         const size_t cur_front  = front.load(std::memory_order_relaxed);
572         const size_t avail_size = tail.load(std::memory_order_relaxed) - cur_front;
573         if (size > avail_size)
574         {
575             if (!partial)
576                 return 0;
577             size = std::min(size, avail_size);
578         }
579         std::atomic_thread_fence(std::memory_order_acquire);
580 
581         const size_t real_front = cur_front % buffer.size();
582         const size_t first_size = std::min(buffer.size() - real_front, size);
583         builtin_memcpy(dest, buffer.data() + real_front, first_size * sizeof(T));
584         builtin_memcpy(dest + first_size, buffer.data(), (size - first_size) * sizeof(T));
585 
586         std::atomic_thread_fence(std::memory_order_release);
587 
588         front.store(cur_front + size, std::memory_order_relaxed);
589         return size;
590     }
591 
592 private:
593     std::atomic<size_t> front;
594     char cacheline_filler[64 - sizeof(std::atomic<size_t>)];
595     std::atomic<size_t> tail;
596 };
597 inline namespace CMT_ARCH_NAME
598 {
599 
600 template <typename T, univector_tag Tag, typename U, size_t N>
get_elements(const univector<T,Tag> & self,cinput_t,size_t index,vec_shape<U,N>)601 KFR_INTRINSIC vec<U, N> get_elements(const univector<T, Tag>& self, cinput_t, size_t index, vec_shape<U, N>)
602 {
603     const T* data = self.data();
604     return static_cast<vec<U, N>>(read<N>(ptr_cast<T>(data) + index));
605 }
606 
607 /// @brief Converts an expression to univector
608 template <typename Expr, typename T = value_type_of<Expr>>
render(Expr && expr)609 KFR_INTRINSIC univector<T> render(Expr&& expr)
610 {
611     static_assert(!is_infinite<Expr>,
612                   "render: Can't process infinite expressions. Pass size as a second argument to render.");
613     univector<T> result;
614     result.resize(expr.size());
615     result = expr;
616     return result;
617 }
618 
619 /// @brief Converts an expression to univector
620 template <typename Expr, typename T = value_type_of<Expr>>
render(Expr && expr,size_t size,size_t offset=0)621 KFR_INTRINSIC univector<T> render(Expr&& expr, size_t size, size_t offset = 0)
622 {
623     univector<T> result;
624     result.resize(size);
625     result = slice(expr, offset, size);
626     return result;
627 }
628 
629 /// @brief Converts an expression to univector
630 template <typename Expr, size_t Size, typename T = value_type_of<Expr>>
render(Expr && expr,csize_t<Size>)631 KFR_INTRINSIC univector<T, Size> render(Expr&& expr, csize_t<Size>)
632 {
633     univector<T, Size> result;
634     result = expr;
635     return result;
636 }
637 } // namespace CMT_ARCH_NAME
638 } // namespace kfr
639 
640 CMT_PRAGMA_MSVC(warning(pop))
641