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 "terminate.hpp"
12#include "cast.hpp"
13#include <concepts>
14#include <memory>
15#include <vector>
16#include <map>
17#include <unordered_map>
18#include <type_traits>
19#include <bit>
20#include <string.h>
21
22hi_export_module(hikogui.utility.memory);
23
24hi_warning_push();
25// C26474: Don't cast between pointer types when the conversion could be implicit (type.1).
26// False positive, template with potential two different pointer types.
27hi_warning_ignore_msvc(26474);
28// C26472: Don't use a static_cast for arithmetic conversions. Use brace initializations... (type.1).
29// Can't include cast.hpp for highlevel casts.
30hi_warning_ignore_msvc(26472);
31
32hi_export namespace hi::inline v1 {
33
40template<template<typename...> typename T, typename... Args>
41[[nodiscard]] auto make_unique_ctad(Args &&...args)
42{
43 using deduced_type = decltype(T{std::forward<Args>(args)...});
44 return std::make_unique<deduced_type>(std::forward<Args>(args)...);
45}
46
53template<template<typename...> typename T, typename... Args>
54[[nodiscard]] auto make_shared_ctad(Args &&...args)
55{
56 using deduced_type = decltype(T{std::forward<Args>(args)...});
57 return std::make_shared<deduced_type>(std::forward<Args>(args)...);
58}
59
60[[nodiscard]] bool equal_ptr(auto *p1, auto *p2) noexcept
61{
62 return static_cast<void *>(p1) == static_cast<void *>(p2);
63}
64
65template<typename T, typename U>
66void memswap(T& dst, U& src)
67{
68 static_assert(sizeof(T) == sizeof(U), "memswap requires both objects of equal size");
69 std::byte tmp[sizeof(T)];
70 memcpy(tmp, &src, sizeof(T));
71 memcpy(&src, &dst, sizeof(U));
72 memcpy(&dst, tmp, sizeof(T));
73}
74
85template<typename InputIt, typename T>
86T *placement_copy(InputIt src, T *dst)
87{
88 hi_axiom_not_null(dst);
89 return new (dst) T(*src);
90}
91
96template<typename InputIt, typename T>
97void placement_copy(InputIt src_first, InputIt src_last, T *dst_first)
98{
99 hi_axiom(src_first != dst_first);
100 hi_axiom(src_last >= src_first);
101
102 auto src = src_first;
103 auto dst = dst_first;
104 while (src != src_last) {
105 placement_copy(src++, dst++);
106 }
107}
108
122template<typename T>
123T *placement_move(T *src, T *dst)
124{
125 hi_axiom_not_null(src);
126 hi_axiom_not_null(dst);
127
128 auto dst_ = new (dst) T(std::move(*src));
129 std::destroy_at(src);
130 return dst_;
131}
132
142template<typename T>
143void placement_move_within_array(T *src_first, T *src_last, T *dst_first)
144{
145 hi_axiom(src_last >= src_first);
146
147 if (src_first < dst_first) {
148 auto dst_last = dst_first + (src_last - src_first);
149
150 auto src = src_last;
151 auto dst = dst_last;
152 while (src != src_first) {
153 placement_move(--src, --dst);
154 }
155
156 } else if (src_first > dst_first) {
157 auto src = src_first;
158 auto dst = dst_first;
159 while (src != src_last) {
160 placement_move(src++, dst++);
161 }
162
163 } else {
164 // When src_first and dst_first are equal then no movement is necessary.
165 ;
166 }
167}
168
176template<typename T>
177void placement_move(T *src, T *src_last, T *dst)
178{
179 hi_axiom(src_last >= src);
180
181 while (src != src_last) {
182 placement_move(src++, dst++);
183 }
184}
185
188template<typename It, typename... Args>
189void construct(It first, It last, Args const&...args)
190{
191 for (auto it = first; it != last; ++it) {
192 std::construct_at(std::addressof(*it), args...);
193 }
194}
195
198template<typename T>
199constexpr bool is_aligned(T *p)
200{
201 return (reinterpret_cast<ptrdiff_t>(p) % std::alignment_of<T>::value) == 0;
202}
203
204template<typename T>
205constexpr T *ceil(T *ptr, std::size_t alignment) noexcept
206{
207 auto const aligned_byte_offset = ceil(reinterpret_cast<uintptr_t>(ptr), wide_cast<uintptr_t>(alignment));
208 return reinterpret_cast<T *>(aligned_byte_offset);
209}
210
211template<typename T>
212constexpr T *floor(T *ptr, std::size_t alignment) noexcept
213{
214 auto const aligned_byte_offset = floor(reinterpret_cast<uintptr_t>(ptr), wide_cast<uintptr_t>(alignment));
215 return reinterpret_cast<T *>(aligned_byte_offset);
216}
217
224inline void *advance_bytes(void *ptr, std::ptrdiff_t distance) noexcept
225{
226 hi_axiom_not_null(ptr);
227 return static_cast<char *>(ptr) + distance;
228}
229
236inline void const *advance_bytes(void const *ptr, std::ptrdiff_t distance) noexcept
237{
238 hi_axiom_not_null(ptr);
239 return static_cast<char const *>(ptr) + distance;
240}
241
242template<typename T>
243inline void cleanupWeakPointers(std::vector<std::weak_ptr<T>>& v) noexcept
244{
245 auto i = v.begin();
246 while (i != v.end()) {
247 if (i->expired()) {
248 i = v.erase(i);
249 } else {
250 i++;
251 }
252 }
253}
254
255template<typename K, typename T>
256inline void cleanupWeakPointers(std::unordered_map<K, std::weak_ptr<T>>& v) noexcept
257{
258 auto i = v.begin();
259 while (i != v.end()) {
260 if (i->second.expired()) {
261 i = v.erase(i);
262 } else {
263 i++;
264 }
265 }
266}
267
268template<typename K, typename T>
269inline void cleanupWeakPointers(std::unordered_map<K, std::vector<std::weak_ptr<T>>>& v) noexcept
270{
271 auto i = v.begin();
272 while (i != v.end()) {
273 cleanupWeakPointers(i->second);
274 if (i->second.size() == 0) {
275 i = v.erase(i);
276 } else {
277 i++;
278 }
279 }
280}
281
282template<typename Value, typename Map, typename Key, typename... Args>
283inline std::shared_ptr<Value> try_make_shared(Map& map, Key key, Args... args)
284{
286
287 auto const i = map.find(key);
288 if (i == map.end()) {
289 value = std::make_shared<Value>(std::forward<Args>(args)...);
290 map.insert_or_assign(key, value);
291 } else {
292 value = i->second;
293 }
294 return value;
295}
296
299template<numeric T, byte_like B>
300[[nodiscard]] constexpr T unaligned_load(B const *src) noexcept
301{
302 auto r = T{};
303
304 if (not std::is_constant_evaluated()) {
305 // MSVC, clang and gcc are able to optimize this fully on x86-64.
306 std::memcpy(&r, src, sizeof(T));
307 return r;
308 }
309
310 if constexpr (std::endian::native == std::endian::little) {
311 for (auto i = sizeof(T); i != 0; --i) {
312 if constexpr (sizeof(r) > 1) {
313 r <<= 8;
314 }
315 r |= static_cast<uint8_t>(src[i - 1]);
316 }
317 } else {
318 for (auto i = 0; i != sizeof(T); ++i) {
319 if constexpr (sizeof(r) > 1) {
320 r <<= 8;
321 }
322 r |= static_cast<uint8_t>(src[i]);
323 }
324 }
325 return r;
326}
327
328template<numeric T>
329[[nodiscard]] inline T unaligned_load(void const *src) noexcept
330{
331 return unaligned_load<T>(static_cast<std::byte const *>(src));
332}
333
334template<numeric T, byte_like B>
335constexpr void unaligned_store(T src, B *dst) noexcept
336{
337 using unsigned_type = std::make_unsigned_t<T>;
338
339 auto const src_ = static_cast<unsigned_type>(src);
340
341 if (not std::is_constant_evaluated()) {
342 std::memcpy(dst, &src, sizeof(T));
343 return;
344 }
345
346 if constexpr (std::endian::native == std::endian::little) {
347 for (auto i = 0; i != sizeof(T); ++i) {
348 dst[i] = static_cast<B>(src_);
349 src_ >>= 8;
350 }
351 } else {
352 for (auto i = sizeof(T); i != 0; --i) {
353 dst[i - 1] = static_cast<B>(src_);
354 src_ >>= 8;
355 }
356 }
357}
358
359template<numeric T>
360void unaligned_store(T src, void *dst) noexcept
361{
362 return unaligned_store(src, reinterpret_cast<std::byte *>(dst));
363}
364
365template<std::integral T>
366hi_force_inline constexpr void store_or(T src, uint8_t *dst) noexcept
367{
368 hi_axiom_not_null(dst);
369
370 using unsigned_type = std::make_unsigned_t<T>;
371
372 auto src_ = truncate<unsigned_type>(src);
373
374 if (not std::is_constant_evaluated()) {
375 decltype(src_) tmp;
376 std::memcpy(&tmp, dst, sizeof(T));
377 tmp |= src_;
378 std::memcpy(dst, &tmp, sizeof(T));
379 }
380
381 if constexpr (std::endian::native == std::endian::little) {
382 for (auto i = 0; i != sizeof(T); ++i) {
383 dst[i] |= truncate<uint8_t>(src_);
384 src_ >>= 8;
385 }
386 } else {
387 for (auto i = sizeof(T); i != 0; --i) {
388 dst[i] |= truncate<uint8_t>(src_);
389 src_ >>= 8;
390 }
391 }
392}
393
394} // namespace hi::inline v1
395
396hi_warning_pop();
Miscellaneous math functions.
Functions for casting values between types savely.
Utilities for throwing exceptions and terminating the application.
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
T * placement_copy(InputIt src, T *dst)
Copy an object to another memory locations.
Definition memory.hpp:86
auto make_shared_ctad(Args &&...args)
make_shared with CTAD (Class Template Argument Deduction)
Definition memory.hpp:54
void * advance_bytes(void *ptr, std::ptrdiff_t distance) noexcept
Advance a pointer by a number of bytes.
Definition memory.hpp:224
void construct(It first, It last, Args const &...args)
Construct a set of objects.
Definition memory.hpp:189
auto make_unique_ctad(Args &&...args)
make_unique with CTAD (Class Template Argument Deduction)
Definition memory.hpp:41
constexpr bool is_aligned(T *p)
Check if a pointer is properly aligned for the object it is pointing at.
Definition memory.hpp:199
void placement_move_within_array(T *src_first, T *src_last, T *dst_first)
Move an objects between two memory locations.
Definition memory.hpp:143
constexpr T unaligned_load(B const *src) noexcept
Make an unaligned load of an unsigned integer.
Definition memory.hpp:300
T * placement_move(T *src, T *dst)
Move an object between two memory locations.
Definition memory.hpp:123
T addressof(T... args)
T floor(T... args)
T memcpy(T... args)
T move(T... args)