1 // license:GPL-2.0+
2 // copyright-holders:Couriersud
3 
4 #ifndef PARRAY_H_
5 #define PARRAY_H_
6 
7 ///
8 /// \file parray.h
9 ///
10 
11 #include "palloc.h"
12 #include "pconfig.h"
13 #include "pexception.h"
14 
15 #include <array>
16 #include <memory>
17 #include <string>
18 #include <type_traits>
19 #include <utility>
20 #include <vector>
21 
22 namespace plib {
23 
24 	template <typename FT, int SIZE, typename ARENA>
25 	struct sizeabs
26 	{
ABSsizeabs27 		static constexpr std::size_t ABS() noexcept { return (SIZE < 0) ? narrow_cast<std::size_t>(0 - SIZE) : narrow_cast<std::size_t>(SIZE); }
28 		using container = typename std::array<FT, ABS()> ;
29 	};
30 
31 	template <typename FT, typename ARENA>
32 	struct sizeabs<FT, 0, ARENA>
33 	{
34 		static constexpr std::size_t ABS() noexcept { return 0; }
35 		using allocator_type = typename ARENA::template allocator_type<FT, PALIGN_VECTOROPT>;
36 		//using container = typename std::vector<FT, arena_allocator<mempool, FT, 64>>;
37 		using container = typename std::vector<FT, allocator_type>;
38 	};
39 
40 	/// \brief Array with preallocated or dynamic allocation.
41 	///
42 	/// Passing SIZE > 0 has the same functionality as a std::array.
43 	/// SIZE = 0 is pure dynamic allocation, the actual array size is passed to the
44 	/// constructor.
45 	/// SIZE < 0 reserves abs(SIZE) elements statically in place allocated. The
46 	/// actual size is passed in by the constructor.
47 	/// This array is purely intended for HPC application where depending on the
48 	/// architecture a preference dynamic/static has to be made.
49 	///
50 	/// This struct is not intended to be a full replacement to std::array.
51 	/// It is a subset to enable switching between dynamic and static allocation.
52 	/// I consider > 10% performance difference to be a use case.
53 	///
54 
55 	template <typename FT, int SIZE, typename ARENA = aligned_arena>
56 	struct parray
57 	{
58 	public:
59 		static constexpr std::size_t SIZEABS() noexcept { return sizeabs<FT, SIZE, ARENA>::ABS(); }
60 
61 		using base_type = typename sizeabs<FT, SIZE, ARENA>::container;
62 		using size_type = typename base_type::size_type;
63 		using value_type = FT;
64 		using reference =  FT &;
65 		using const_reference = const FT &;
66 
67 		using pointer = FT *;
68 		using const_pointer = const FT *;
69 
70 		template <int X = SIZE >
71 		parray(size_type size, std::enable_if_t<(X==0), int> = 0)
72 		: m_a(size), m_size(size)
73 		{
74 		}
75 
76 		template <int X = SIZE >
77 		parray(size_type size, const FT &val, std::enable_if_t<(X==0), int> = 0)
78 		: m_a(size, val), m_size(size)
79 		{
80 		}
81 
82 		template <int X = SIZE >
83 		parray(size_type size, std::enable_if_t<(X != 0), int> = 0) noexcept(false)
84 		: m_size(size)
85 		{
86 			if ((SIZE < 0 && size > SIZEABS())
87 				|| (SIZE > 0 && size != SIZEABS()))
88 				throw pexception(pfmt("parray: size error: {1} > {2}")(size, SIZE));
89 		}
90 
91 		template <int X = SIZE >
92 		parray(size_type size, const FT &val, std::enable_if_t<(X != 0), int> = 0) noexcept(false)
93 		: m_size(size)
94 		{
95 			if ((SIZE < 0 && size > SIZEABS())
96 				|| (SIZE > 0 && size != SIZEABS()))
97 				throw pexception(pfmt("parray: size error: {1} > {2}")(size, SIZE));
98 			m_a.fill(val);
99 		}
100 
101 
102 		// allow construction in fixed size arrays
103 		parray()
104 		: m_size(SIZEABS())
105 		{
106 		}
107 
108 		parray(const parray &rhs) : m_a(rhs.m_a), m_size(rhs.m_size) {}
109 		parray(parray &&rhs) noexcept : m_a(std::move(rhs.m_a)), m_size(std::move(rhs.m_size)) {}
110 
111 		parray &operator=(const parray &rhs) noexcept // NOLINT(bugprone-unhandled-self-assignment, cert-oop54-cpp)
112 		{
113 			if (this == &rhs)
114 				return *this;
115 
116 			m_a = rhs.m_a;
117 			m_size = rhs.m_size;
118 			return *this;
119 		}
120 
121 		parray &operator=(parray &&rhs) noexcept { std::swap(m_a,rhs.m_a); std::swap(m_size, rhs.m_size); return *this; }
122 
123 		~parray() noexcept = default;
124 
125 		base_type &as_base() noexcept { return m_a; }
126 
127 		constexpr size_type size() const noexcept { return SIZE <= 0 ? m_size : SIZEABS(); }
128 
129 		constexpr size_type max_size() const noexcept { return base_type::max_size(); }
130 
131 		bool empty() const noexcept { return size() == 0; }
132 
133 		constexpr reference operator[](size_type i) noexcept
134 		{
135 			return m_a[i];
136 		}
137 		constexpr const_reference operator[](size_type i) const noexcept
138 		{
139 			return m_a[i];
140 		}
141 
142 		pointer data() noexcept { return m_a.data(); }
143 		const_pointer data() const noexcept { return m_a.data(); }
144 
145 	private:
146 		PALIGNAS_VECTOROPT()
147 		base_type               m_a;
148 		size_type               m_size;
149 	};
150 
151 	template <typename FT, int SIZE1, int SIZE2>
152 	struct parray2D : public parray<parray<FT, SIZE2>, SIZE1>
153 	{
154 	public:
155 
156 		using size_type = std::size_t;
157 		using base_type = parray<parray<FT, SIZE2>, SIZE1>;
158 
159 		parray2D(size_type size1, size_type size2)
160 		: parray<parray<FT, SIZE2>, SIZE1>(size1)
161 		{
162 			if (SIZE2 <= 0)
163 			{
164 				for (size_type i=0; i < this->size(); i++)
165 					(*this)[i] = parray<FT, SIZE2>(size2);
166 			}
167 		}
168 
169 		parray2D(const parray2D &) = default;
170 		parray2D &operator=(const parray2D &) = default;
171 		parray2D(parray2D &&) noexcept(std::is_nothrow_move_constructible<base_type>::value) = default;
172 		parray2D &operator=(parray2D &&) noexcept(std::is_nothrow_move_assignable<base_type>::value) = default;
173 
174 		~parray2D() noexcept = default;
175 
176 	};
177 
178 } // namespace plib
179 
180 
181 
182 #endif // PARRAY_H_
183