HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
memory.hpp
1// Copyright Take Vos 2019-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "math.hpp"
8#include "../macros.hpp"
9#include "concepts.hpp"
10#include "exception.hpp"
11#include <concepts>
12#include <memory>
13#include <vector>
14#include <map>
15#include <unordered_map>
16#include <type_traits>
17#include <string.h>
18
19hi_export_module(hikogui.utility.memory);
20
21hi_warning_push();
22// C26474: Don't cast between pointer types when the conversion could be implicit (type.1).
23// False positive, template with potential two different pointer types.
24hi_warning_ignore_msvc(26474);
25// C26472: Don't use a static_cast for arithmetic conversions. Use brace initializations... (type.1).
26// Can't include cast.hpp for highlevel casts.
27hi_warning_ignore_msvc(26472);
28
29hi_export namespace hi::inline v1 {
30
31[[nodiscard]] bool equal_ptr(auto *p1, auto *p2) noexcept
32{
33 return static_cast<void *>(p1) == static_cast<void *>(p2);
34}
35
36template<typename T, typename U>
37void memswap(T& dst, U& src)
38{
39 static_assert(sizeof(T) == sizeof(U), "memswap requires both objects of equal size");
40 std::byte tmp[sizeof(T)];
41 memcpy(tmp, &src, sizeof(T));
42 memcpy(&src, &dst, sizeof(U));
43 memcpy(&dst, tmp, sizeof(T));
44}
45
56template<typename InputIt, typename T>
58{
59 hi_axiom_not_null(dst);
60 return new (dst) T(*src);
61}
62
67template<typename InputIt, typename T>
69{
70 hi_axiom(src_first != dst_first);
71 hi_axiom(src_last >= src_first);
72
73 auto src = src_first;
74 auto dst = dst_first;
75 while (src != src_last) {
77 }
78}
79
93template<typename T>
95{
96 hi_axiom_not_null(src);
97 hi_axiom_not_null(dst);
98
99 auto dst_ = new (dst) T(std::move(*src));
100 std::destroy_at(src);
101 return dst_;
102}
103
113template<typename T>
115{
116 hi_axiom(src_last >= src_first);
117
118 if (src_first < dst_first) {
120
121 auto src = src_last;
122 auto dst = dst_last;
123 while (src != src_first) {
124 placement_move(--src, --dst);
125 }
126
127 } else if (src_first > dst_first) {
128 auto src = src_first;
129 auto dst = dst_first;
130 while (src != src_last) {
131 placement_move(src++, dst++);
132 }
133
134 } else {
135 // When src_first and dst_first are equal then no movement is necessary.
136 ;
137 }
138}
139
147template<typename T>
149{
150 hi_axiom(src_last >= src);
151
152 while (src != src_last) {
153 placement_move(src++, dst++);
154 }
155}
156
159template<typename It, typename... Args>
160void construct(It first, It last, Args const&...args)
161{
162 for (auto it = first; it != last; ++it) {
163 std::construct_at(std::addressof(*it), args...);
164 }
165}
166
169template<typename T>
170constexpr bool is_aligned(T *p)
171{
172 return (reinterpret_cast<ptrdiff_t>(p) % std::alignment_of<T>::value) == 0;
173}
174
175template<typename T>
176constexpr T *ceil(T *ptr, std::size_t alignment) noexcept
177{
178 hilet aligned_byte_offset = ceil(reinterpret_cast<uintptr_t>(ptr), wide_cast<uintptr_t>(alignment));
179 return reinterpret_cast<T *>(aligned_byte_offset);
180}
181
182template<typename T>
183constexpr T *floor(T *ptr, std::size_t alignment) noexcept
184{
185 hilet aligned_byte_offset = floor(reinterpret_cast<uintptr_t>(ptr), wide_cast<uintptr_t>(alignment));
186 return reinterpret_cast<T *>(aligned_byte_offset);
187}
188
195inline void *advance_bytes(void *ptr, std::ptrdiff_t distance) noexcept
196{
197 hi_axiom_not_null(ptr);
198 return static_cast<char *>(ptr) + distance;
199}
200
207inline void const *advance_bytes(void const *ptr, std::ptrdiff_t distance) noexcept
208{
209 hi_axiom_not_null(ptr);
210 return static_cast<char const *>(ptr) + distance;
211}
212
213template<typename T>
214inline void cleanupWeakPointers(std::vector<std::weak_ptr<T>>& v) noexcept
215{
216 auto i = v.begin();
217 while (i != v.end()) {
218 if (i->expired()) {
219 i = v.erase(i);
220 } else {
221 i++;
222 }
223 }
224}
225
226template<typename K, typename T>
227inline void cleanupWeakPointers(std::unordered_map<K, std::weak_ptr<T>>& v) noexcept
228{
229 auto i = v.begin();
230 while (i != v.end()) {
231 if (i->second.expired()) {
232 i = v.erase(i);
233 } else {
234 i++;
235 }
236 }
237}
238
239template<typename K, typename T>
240inline void cleanupWeakPointers(std::unordered_map<K, std::vector<std::weak_ptr<T>>>& v) noexcept
241{
242 auto i = v.begin();
243 while (i != v.end()) {
244 cleanupWeakPointers(i->second);
245 if (i->second.size() == 0) {
246 i = v.erase(i);
247 } else {
248 i++;
249 }
250 }
251}
252
253template<typename Value, typename Map, typename Key, typename... Args>
254inline std::shared_ptr<Value> try_make_shared(Map& map, Key key, Args... args)
255{
257
258 hilet i = map.find(key);
259 if (i == map.end()) {
260 value = std::make_shared<Value>(std::forward<Args>(args)...);
261 map.insert_or_assign(key, value);
262 } else {
263 value = i->second;
264 }
265 return value;
266}
267
270template<numeric T, byte_like B>
271[[nodiscard]] constexpr T unaligned_load(B const *src) noexcept
272{
273 auto r = T{};
274
275 if (not std::is_constant_evaluated()) {
276 // MSVC, clang and gcc are able to optimize this fully on x86-64.
277 std::memcpy(&r, src, sizeof(T));
278 return r;
279 }
280
281 if constexpr (std::endian::native == std::endian::little) {
282 for (auto i = sizeof(T); i != 0; --i) {
283 if constexpr (sizeof(r) > 1) {
284 r <<= 8;
285 }
286 r |= static_cast<uint8_t>(src[i - 1]);
287 }
288 } else {
289 for (auto i = 0; i != sizeof(T); ++i) {
290 if constexpr (sizeof(r) > 1) {
291 r <<= 8;
292 }
293 r |= static_cast<uint8_t>(src[i]);
294 }
295 }
296 return r;
297}
298
299template<numeric T>
300[[nodiscard]] inline T unaligned_load(void const *src) noexcept
301{
302 return unaligned_load<T>(static_cast<std::byte const *>(src));
303}
304
305template<numeric T, byte_like B>
306[[nodiscard]] constexpr void unaligned_store(T src, B *dst) noexcept
307{
308 using unsigned_type = std::make_unsigned_t<T>;
309
310 hilet src_ = static_cast<unsigned_type>(src);
311
312 if (not std::is_constant_evaluated()) {
313 std::memcpy(dst, &src, sizeof(T));
314 return;
315 }
316
317 if constexpr (std::endian::native == std::endian::little) {
318 for (auto i = 0; i != sizeof(T); ++i) {
319 dst[i] = static_cast<B>(src_);
320 src_ >>= 8;
321 }
322 } else {
323 for (auto i = sizeof(T); i != 0; --i) {
324 dst[i - 1] = static_cast<B>(src_);
325 src_ >>= 8;
326 }
327 }
328}
329
330template<numeric T>
331[[nodiscard]] inline void unaligned_store(T src, void *dst) noexcept
332{
333 return unaligned_store(src, reinterpret_cast<std::byte *>(dst));
334}
335
336template<numeric T>
337[[nodiscard]] hi_force_inline constexpr void store_or(T src, uint8_t *dst) noexcept
338{
339 hi_axiom_not_null(dst);
340
341 using unsigned_type = std::make_unsigned_t<T>;
342
344
345 if (not std::is_constant_evaluated()) {
346 decltype(src_) tmp;
347 std::memcpy(&tmp, dst, sizeof(T));
348 tmp |= src_;
349 std::memcpy(dst, &tmp, sizeof(T));
350 }
351
352 if constexpr (std::endian::native == std::endian::little) {
353 for (auto i = 0; i != sizeof(T); ++i) {
355 src_ >>= 8;
356 }
357 } else {
358 for (auto i = sizeof(T); i != 0; --i) {
360 src_ >>= 8;
361 }
362 }
363}
364
365} // namespace hi::inline v1
366
367hi_warning_pop();
Miscellaneous math functions.
DOXYGEN BUG.
Definition algorithm.hpp:16
T * placement_copy(InputIt src, T *dst)
Copy an object to another memory locations.
Definition memory.hpp:57
void * advance_bytes(void *ptr, std::ptrdiff_t distance) noexcept
Advance a pointer by a number of bytes.
Definition memory.hpp:195
void construct(It first, It last, Args const &...args)
Construct a set of objects.
Definition memory.hpp:160
constexpr bool is_aligned(T *p)
Check if a pointer is properly aligned for the object it is pointing at.
Definition memory.hpp:170
void placement_move_within_array(T *src_first, T *src_last, T *dst_first)
Move an objects between two memory locations.
Definition memory.hpp:114
constexpr T unaligned_load(B const *src) noexcept
Make an unaligned load of an unsigned integer.
Definition memory.hpp:271
T * placement_move(T *src, T *dst)
Move an object between two memory locations.
Definition memory.hpp:94
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
T addressof(T... args)
T floor(T... args)
T memcpy(T... args)
T move(T... args)