/src/immer/immer/detail/util.hpp
Line | Count | Source |
1 | | // |
2 | | // immer: immutable data structures for C++ |
3 | | // Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente |
4 | | // |
5 | | // This software is distributed under the Boost Software License, Version 1.0. |
6 | | // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt |
7 | | // |
8 | | |
9 | | #pragma once |
10 | | |
11 | | #include <immer/config.hpp> |
12 | | |
13 | | #include <cstddef> |
14 | | #include <memory> |
15 | | #include <new> |
16 | | #include <type_traits> |
17 | | |
18 | | #include <immer/detail/type_traits.hpp> |
19 | | |
20 | | #if defined(_MSC_VER) |
21 | | #include <intrin.h> // for __lzcnt* |
22 | | #endif |
23 | | |
24 | | namespace immer { |
25 | | namespace detail { |
26 | | |
27 | | template <typename T> |
28 | | const T* as_const(T* x) |
29 | | { |
30 | | return x; |
31 | | } |
32 | | |
33 | | template <typename T> |
34 | | const T& as_const(T& x) |
35 | | { |
36 | | return x; |
37 | | } |
38 | | |
39 | | template <std::size_t Size, std::size_t Align> |
40 | | struct aligned_storage |
41 | | { |
42 | | struct type |
43 | | { |
44 | | alignas(Align) unsigned char data[Size]; |
45 | | }; |
46 | | }; |
47 | | |
48 | | template <std::size_t Size, std::size_t Align> |
49 | | using aligned_storage_t = typename aligned_storage<Size, Align>::type; |
50 | | |
51 | | template <typename T> |
52 | | using aligned_storage_for = |
53 | | typename aligned_storage<sizeof(T), alignof(T)>::type; |
54 | | |
55 | | template <typename T> |
56 | | T& auto_const_cast(const T& x) |
57 | 17.9M | { |
58 | 17.9M | return const_cast<T&>(x); |
59 | 17.9M | } |
60 | | template <typename T> |
61 | | T&& auto_const_cast(const T&& x) |
62 | | { |
63 | | return const_cast<T&&>(std::move(x)); |
64 | | } |
65 | | |
66 | | template <class T> |
67 | | inline auto destroy_at(T* p) noexcept |
68 | | -> std::enable_if_t<std::is_trivially_destructible<T>::value> |
69 | | { |
70 | | p->~T(); |
71 | | } |
72 | | |
73 | | template <class T> |
74 | | inline auto destroy_at(T* p) noexcept |
75 | | -> std::enable_if_t<!std::is_trivially_destructible<T>::value> |
76 | 1.08M | { |
77 | 1.08M | p->~T(); |
78 | 1.08M | } |
79 | | |
80 | | template <typename Iter1> |
81 | | constexpr bool can_trivially_detroy = std::is_trivially_destructible< |
82 | | typename std::iterator_traits<Iter1>::value_type>::value; |
83 | | |
84 | | template <class Iter> |
85 | | auto destroy(Iter, Iter last) noexcept |
86 | | -> std::enable_if_t<can_trivially_detroy<Iter>, Iter> |
87 | | { |
88 | | return last; |
89 | | } |
90 | | template <class Iter> |
91 | | auto destroy(Iter first, Iter last) noexcept |
92 | | -> std::enable_if_t<!can_trivially_detroy<Iter>, Iter> |
93 | 0 | { |
94 | 0 | for (; first != last; ++first) |
95 | 0 | detail::destroy_at(std::addressof(*first)); |
96 | 0 | return first; |
97 | 0 | } |
98 | | |
99 | | template <class Iter, class Size> |
100 | | auto destroy_n(Iter first, Size n) noexcept |
101 | | -> std::enable_if_t<can_trivially_detroy<Iter>, Iter> |
102 | | { |
103 | | return first + n; |
104 | | } |
105 | | template <class Iter, class Size> |
106 | | auto destroy_n(Iter first, Size n) noexcept |
107 | | -> std::enable_if_t<!can_trivially_detroy<Iter>, Iter> |
108 | 408k | { |
109 | 1.49M | for (; n > 0; (void) ++first, --n) |
110 | 1.08M | detail::destroy_at(std::addressof(*first)); |
111 | 408k | return first; |
112 | 408k | } |
113 | | |
114 | | template <typename Iter1, typename Iter2> |
115 | | constexpr bool can_trivially_copy = |
116 | | std::is_same<typename std::iterator_traits<Iter1>::value_type, |
117 | | typename std::iterator_traits<Iter2>::value_type>::value && |
118 | | std::is_trivially_copyable< |
119 | | typename std::iterator_traits<Iter1>::value_type>::value; |
120 | | |
121 | | template <typename Iter1, typename Iter2> |
122 | | auto uninitialized_move(Iter1 first, Iter1 last, Iter2 out) noexcept |
123 | | -> std::enable_if_t<can_trivially_copy<Iter1, Iter2>, Iter2> |
124 | | { |
125 | | return std::copy(first, last, out); |
126 | | } |
127 | | template <typename Iter1, typename Iter2> |
128 | | auto uninitialized_move(Iter1 first, Iter1 last, Iter2 out) |
129 | | -> std::enable_if_t<!can_trivially_copy<Iter1, Iter2>, Iter2> |
130 | | |
131 | 22.2k | { |
132 | 22.2k | using value_t = typename std::iterator_traits<Iter2>::value_type; |
133 | 22.2k | auto current = out; |
134 | 22.2k | IMMER_TRY { |
135 | 51.4k | for (; first != last; ++first, (void) ++current) { |
136 | 29.1k | ::new (const_cast<void*>(static_cast<const volatile void*>( |
137 | 29.1k | std::addressof(*current)))) value_t(std::move(*first)); |
138 | 29.1k | } |
139 | 22.2k | return current; |
140 | 22.2k | } |
141 | 22.2k | IMMER_CATCH (...) { |
142 | 0 | detail::destroy(out, current); |
143 | 0 | IMMER_RETHROW; |
144 | 0 | } |
145 | 22.2k | } |
146 | | |
147 | | template <typename SourceIter, typename Sent, typename SinkIter> |
148 | | auto uninitialized_copy(SourceIter first, Sent last, SinkIter out) noexcept |
149 | | -> std::enable_if_t<can_trivially_copy<SourceIter, SinkIter>, SinkIter> |
150 | | { |
151 | | return std::copy(first, last, out); |
152 | | } |
153 | | template <typename SourceIter, typename Sent, typename SinkIter> |
154 | | auto uninitialized_copy(SourceIter first, Sent last, SinkIter out) |
155 | | -> std::enable_if_t<!can_trivially_copy<SourceIter, SinkIter>, SinkIter> |
156 | 409k | { |
157 | 409k | using value_t = typename std::iterator_traits<SinkIter>::value_type; |
158 | 409k | auto current = out; |
159 | 409k | IMMER_TRY { |
160 | 1.36M | for (; first != last; ++first, (void) ++current) { |
161 | 960k | ::new (const_cast<void*>(static_cast<const volatile void*>( |
162 | 960k | std::addressof(*current)))) value_t(*first); |
163 | 960k | } |
164 | 409k | return current; |
165 | 409k | } |
166 | 409k | IMMER_CATCH (...) { |
167 | 0 | detail::destroy(out, current); |
168 | 0 | IMMER_RETHROW; |
169 | 0 | } |
170 | 409k | } |
171 | | |
172 | | template <typename Heap, typename T, typename... Args> |
173 | | T* make(Args&&... args) |
174 | | { |
175 | | auto ptr = Heap::allocate(sizeof(T)); |
176 | | IMMER_TRY { |
177 | | return new (ptr) T{std::forward<Args>(args)...}; |
178 | | } |
179 | | IMMER_CATCH (...) { |
180 | | Heap::deallocate(sizeof(T), ptr); |
181 | | IMMER_RETHROW; |
182 | | } |
183 | | } |
184 | | |
185 | | struct not_supported_t |
186 | | {}; |
187 | | struct empty_t |
188 | | {}; |
189 | | |
190 | | template <typename T> |
191 | | struct exact_t |
192 | | { |
193 | | T value; |
194 | | exact_t(T v) |
195 | | : value{v} {}; |
196 | | }; |
197 | | |
198 | | template <typename T> |
199 | | inline constexpr auto clz_(T) -> not_supported_t |
200 | | { |
201 | | IMMER_UNREACHABLE; |
202 | | return {}; |
203 | | } |
204 | | #if defined(_MSC_VER) |
205 | | // inline auto clz_(unsigned short x) { return __lzcnt16(x); } |
206 | | // inline auto clz_(unsigned int x) { return __lzcnt(x); } |
207 | | // inline auto clz_(unsigned __int64 x) { return __lzcnt64(x); } |
208 | | #else |
209 | 0 | inline constexpr auto clz_(unsigned int x) { return __builtin_clz(x); } |
210 | 0 | inline constexpr auto clz_(unsigned long x) { return __builtin_clzl(x); } |
211 | 0 | inline constexpr auto clz_(unsigned long long x) { return __builtin_clzll(x); } |
212 | | #endif |
213 | | |
214 | | template <typename T> |
215 | | inline constexpr T log2_aux(T x, T r = 0) |
216 | | { |
217 | | return x <= 1 ? r : log2_aux(x >> 1, r + 1); |
218 | | } |
219 | | |
220 | | template <typename T> |
221 | | inline constexpr auto log2(T x) -> std:: |
222 | | enable_if_t<!std::is_same<decltype(clz_(x)), not_supported_t>::value, T> |
223 | | { |
224 | | return x == 0 ? 0 : sizeof(std::size_t) * 8 - 1 - clz_(x); |
225 | | } |
226 | | |
227 | | template <typename T> |
228 | | inline constexpr auto log2(T x) |
229 | | -> std::enable_if_t<std::is_same<decltype(clz_(x)), not_supported_t>::value, |
230 | | T> |
231 | | { |
232 | | return log2_aux(x); |
233 | | } |
234 | | |
235 | | template <typename T> |
236 | | constexpr T ipow(T num, unsigned int pow) |
237 | | { |
238 | | return pow == 0 ? 1 : num * ipow(num, pow - 1); |
239 | | } |
240 | | |
241 | | template <bool b, typename F> |
242 | | auto static_if(F&& f) -> std::enable_if_t<b> |
243 | | { |
244 | | std::forward<F>(f)(empty_t{}); |
245 | | } |
246 | | template <bool b, typename F> |
247 | | auto static_if(F&& f) -> std::enable_if_t<!b> |
248 | | { |
249 | | } |
250 | | |
251 | | template <bool b, typename R = void, typename F1, typename F2> |
252 | | auto static_if(F1&& f1, F2&& f2) -> std::enable_if_t<b, R> |
253 | | { |
254 | | return std::forward<F1>(f1)(empty_t{}); |
255 | | } |
256 | | template <bool b, typename R = void, typename F1, typename F2> |
257 | | auto static_if(F1&& f1, F2&& f2) -> std::enable_if_t<!b, R> |
258 | | { |
259 | | return std::forward<F2>(f2)(empty_t{}); |
260 | | } |
261 | | |
262 | | template <typename T, T value> |
263 | | struct constantly |
264 | | { |
265 | | template <typename... Args> |
266 | | T operator()(Args&&...) const |
267 | 526k | { |
268 | 526k | return value; |
269 | 526k | } unsigned long immer::detail::constantly<unsigned long, 1ul>::operator()<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) const Line | Count | Source | 267 | 263k | { | 268 | 263k | return value; | 269 | 263k | } |
unsigned long immer::detail::constantly<unsigned long, 0ul>::operator()<>() const Line | Count | Source | 267 | 263k | { | 268 | 263k | return value; | 269 | 263k | } |
|
270 | | }; |
271 | | |
272 | | /*! |
273 | | * An alias to `std::distance` |
274 | | */ |
275 | | template <typename Iterator, |
276 | | typename Sentinel, |
277 | | std::enable_if_t<detail::std_distance_supports_v<Iterator, Sentinel>, |
278 | | bool> = true> |
279 | | typename std::iterator_traits<Iterator>::difference_type |
280 | | distance(Iterator first, Sentinel last) |
281 | | { |
282 | | return std::distance(first, last); |
283 | | } |
284 | | |
285 | | /*! |
286 | | * Equivalent of the `std::distance` applied to the sentinel-delimited |
287 | | * forward range @f$ [first, last) @f$ |
288 | | */ |
289 | | template <typename Iterator, |
290 | | typename Sentinel, |
291 | | std::enable_if_t< |
292 | | (!detail::std_distance_supports_v<Iterator, Sentinel>) &&detail:: |
293 | | is_forward_iterator_v<Iterator> && |
294 | | detail::compatible_sentinel_v<Iterator, Sentinel> && |
295 | | (!detail::is_subtractable_v<Sentinel, Iterator>), |
296 | | bool> = true> |
297 | | typename std::iterator_traits<Iterator>::difference_type |
298 | | distance(Iterator first, Sentinel last) |
299 | | { |
300 | | std::size_t result = 0; |
301 | | while (first != last) { |
302 | | ++first; |
303 | | ++result; |
304 | | } |
305 | | return result; |
306 | | } |
307 | | |
308 | | /*! |
309 | | * Equivalent of the `std::distance` applied to the sentinel-delimited |
310 | | * random access range @f$ [first, last) @f$ |
311 | | */ |
312 | | template <typename Iterator, |
313 | | typename Sentinel, |
314 | | std::enable_if_t< |
315 | | (!detail::std_distance_supports_v<Iterator, Sentinel>) &&detail:: |
316 | | is_forward_iterator_v<Iterator> && |
317 | | detail::compatible_sentinel_v<Iterator, Sentinel> && |
318 | | detail::is_subtractable_v<Sentinel, Iterator>, |
319 | | bool> = true> |
320 | | typename std::iterator_traits<Iterator>::difference_type |
321 | | distance(Iterator first, Sentinel last) |
322 | | { |
323 | | return last - first; |
324 | | } |
325 | | |
326 | | } // namespace detail |
327 | | } // namespace immer |