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 "not_null.hpp"
12#include "terminate.hpp"
13#include "cast.hpp"
14#include <concepts>
15#include <memory>
16#include <vector>
17#include <map>
18#include <unordered_map>
19#include <type_traits>
20#include <bit>
21#include <string.h>
22
23hi_export_module(hikogui.utility.memory);
24
25hi_warning_push();
26// C26474: Don't cast between pointer types when the conversion could be implicit (type.1).
27// False positive, template with potential two different pointer types.
28hi_warning_ignore_msvc(26474);
29// C26472: Don't use a static_cast for arithmetic conversions. Use brace initializations... (type.1).
30// Can't include cast.hpp for highlevel casts.
31hi_warning_ignore_msvc(26472);
32
33hi_export namespace hi::inline v1 {
34
41template<template<typename...> typename T, typename... Args>
43{
44 using deduced_type = decltype(T{std::forward<Args>(args)...});
45 return std::make_unique<deduced_type>(std::forward<Args>(args)...);
46}
47
54template<template<typename...> typename T, typename... Args>
56{
57 using deduced_type = decltype(T{std::forward<Args>(args)...});
58 return std::make_shared<deduced_type>(std::forward<Args>(args)...);
59}
60
67template<template<typename...> typename T, typename... Args>
69{
70 using deduced_type = decltype(T{std::forward<Args>(args)...});
71 return make_unique_not_null<deduced_type>(std::forward<Args>(args)...);
72}
73
74
81template<template<typename...> typename T, typename... Args>
83{
84 using deduced_type = decltype(T{std::forward<Args>(args)...});
85 return make_shared_not_null<deduced_type>(std::forward<Args>(args)...);
86}
87
88
89[[nodiscard]] bool equal_ptr(auto *p1, auto *p2) noexcept
90{
91 return static_cast<void *>(p1) == static_cast<void *>(p2);
92}
93
94template<typename T, typename U>
95void memswap(T& dst, U& src)
96{
97 static_assert(sizeof(T) == sizeof(U), "memswap requires both objects of equal size");
98 std::byte tmp[sizeof(T)];
99 memcpy(tmp, &src, sizeof(T));
100 memcpy(&src, &dst, sizeof(U));
101 memcpy(&dst, tmp, sizeof(T));
102}
103
114template<typename InputIt, typename T>
116{
117 hi_axiom_not_null(dst);
118 return new (dst) T(*src);
119}
120
125template<typename InputIt, typename T>
127{
128 hi_axiom(src_first != dst_first);
129 hi_axiom(src_last >= src_first);
130
131 auto src = src_first;
132 auto dst = dst_first;
133 while (src != src_last) {
134 placement_copy(src++, dst++);
135 }
136}
137
151template<typename T>
153{
154 hi_axiom_not_null(src);
155 hi_axiom_not_null(dst);
156
157 auto dst_ = new (dst) T(std::move(*src));
158 std::destroy_at(src);
159 return dst_;
160}
161
171template<typename T>
173{
174 hi_axiom(src_last >= src_first);
175
176 if (src_first < dst_first) {
178
179 auto src = src_last;
180 auto dst = dst_last;
181 while (src != src_first) {
182 placement_move(--src, --dst);
183 }
184
185 } else if (src_first > dst_first) {
186 auto src = src_first;
187 auto dst = dst_first;
188 while (src != src_last) {
189 placement_move(src++, dst++);
190 }
191
192 } else {
193 // When src_first and dst_first are equal then no movement is necessary.
194 ;
195 }
196}
197
205template<typename T>
207{
208 hi_axiom(src_last >= src);
209
210 while (src != src_last) {
211 placement_move(src++, dst++);
212 }
213}
214
217template<typename It, typename... Args>
218void construct(It first, It last, Args const&...args)
219{
220 for (auto it = first; it != last; ++it) {
221 std::construct_at(std::addressof(*it), args...);
222 }
223}
224
227template<typename T>
228constexpr bool is_aligned(T *p)
229{
230 return (reinterpret_cast<ptrdiff_t>(p) % std::alignment_of<T>::value) == 0;
231}
232
233template<typename T>
234constexpr T *ceil(T *ptr, std::size_t alignment) noexcept
235{
236 auto const aligned_byte_offset = ceil(reinterpret_cast<uintptr_t>(ptr), wide_cast<uintptr_t>(alignment));
237 return reinterpret_cast<T *>(aligned_byte_offset);
238}
239
240template<typename T>
241constexpr T *floor(T *ptr, std::size_t alignment) noexcept
242{
243 auto const aligned_byte_offset = floor(reinterpret_cast<uintptr_t>(ptr), wide_cast<uintptr_t>(alignment));
244 return reinterpret_cast<T *>(aligned_byte_offset);
245}
246
253hi_inline void *advance_bytes(void *ptr, std::ptrdiff_t distance) noexcept
254{
255 hi_axiom_not_null(ptr);
256 return static_cast<char *>(ptr) + distance;
257}
258
265hi_inline void const *advance_bytes(void const *ptr, std::ptrdiff_t distance) noexcept
266{
267 hi_axiom_not_null(ptr);
268 return static_cast<char const *>(ptr) + distance;
269}
270
271template<typename T>
272hi_inline void cleanupWeakPointers(std::vector<std::weak_ptr<T>>& v) noexcept
273{
274 auto i = v.begin();
275 while (i != v.end()) {
276 if (i->expired()) {
277 i = v.erase(i);
278 } else {
279 i++;
280 }
281 }
282}
283
284template<typename K, typename T>
285hi_inline void cleanupWeakPointers(std::unordered_map<K, std::weak_ptr<T>>& v) noexcept
286{
287 auto i = v.begin();
288 while (i != v.end()) {
289 if (i->second.expired()) {
290 i = v.erase(i);
291 } else {
292 i++;
293 }
294 }
295}
296
297template<typename K, typename T>
298hi_inline void cleanupWeakPointers(std::unordered_map<K, std::vector<std::weak_ptr<T>>>& v) noexcept
299{
300 auto i = v.begin();
301 while (i != v.end()) {
302 cleanupWeakPointers(i->second);
303 if (i->second.size() == 0) {
304 i = v.erase(i);
305 } else {
306 i++;
307 }
308 }
309}
310
311template<typename Value, typename Map, typename Key, typename... Args>
312hi_inline std::shared_ptr<Value> try_make_shared(Map& map, Key key, Args... args)
313{
315
316 auto const i = map.find(key);
317 if (i == map.end()) {
318 value = std::make_shared<Value>(std::forward<Args>(args)...);
319 map.insert_or_assign(key, value);
320 } else {
321 value = i->second;
322 }
323 return value;
324}
325
328template<numeric T, byte_like B>
329[[nodiscard]] constexpr T unaligned_load(B const *src) noexcept
330{
331 auto r = T{};
332
333 if (not std::is_constant_evaluated()) {
334 // MSVC, clang and gcc are able to optimize this fully on x86-64.
335 std::memcpy(&r, src, sizeof(T));
336 return r;
337 }
338
339 if constexpr (std::endian::native == std::endian::little) {
340 for (auto i = sizeof(T); i != 0; --i) {
341 if constexpr (sizeof(r) > 1) {
342 r <<= 8;
343 }
344 r |= static_cast<uint8_t>(src[i - 1]);
345 }
346 } else {
347 for (auto i = 0; i != sizeof(T); ++i) {
348 if constexpr (sizeof(r) > 1) {
349 r <<= 8;
350 }
351 r |= static_cast<uint8_t>(src[i]);
352 }
353 }
354 return r;
355}
356
357template<numeric T>
358[[nodiscard]] hi_inline T unaligned_load(void const *src) noexcept
359{
360 return unaligned_load<T>(static_cast<std::byte const *>(src));
361}
362
363template<numeric T, byte_like B>
364constexpr void unaligned_store(T src, B *dst) noexcept
365{
366 using unsigned_type = std::make_unsigned_t<T>;
367
368 auto const src_ = static_cast<unsigned_type>(src);
369
370 if (not std::is_constant_evaluated()) {
371 std::memcpy(dst, &src, sizeof(T));
372 return;
373 }
374
375 if constexpr (std::endian::native == std::endian::little) {
376 for (auto i = 0; i != sizeof(T); ++i) {
377 dst[i] = static_cast<B>(src_);
378 src_ >>= 8;
379 }
380 } else {
381 for (auto i = sizeof(T); i != 0; --i) {
382 dst[i - 1] = static_cast<B>(src_);
383 src_ >>= 8;
384 }
385 }
386}
387
388template<numeric T>
389void unaligned_store(T src, void *dst) noexcept
390{
391 return unaligned_store(src, reinterpret_cast<std::byte *>(dst));
392}
393
394template<std::integral T>
395hi_force_inline constexpr void store_or(T src, uint8_t *dst) noexcept
396{
397 hi_axiom_not_null(dst);
398
399 using unsigned_type = std::make_unsigned_t<T>;
400
402
403 if (not std::is_constant_evaluated()) {
404 decltype(src_) tmp;
405 std::memcpy(&tmp, dst, sizeof(T));
406 tmp |= src_;
407 std::memcpy(dst, &tmp, sizeof(T));
408 }
409
410 if constexpr (std::endian::native == std::endian::little) {
411 for (auto i = 0; i != sizeof(T); ++i) {
413 src_ >>= 8;
414 }
415 } else {
416 for (auto i = sizeof(T); i != 0; --i) {
418 src_ >>= 8;
419 }
420 }
421}
422
423} // namespace hi::inline v1
424
425hi_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
auto make_shared_ctad_not_null(Args &&...args)
make_shared with CTAD (Class Template Argument Deduction)
Definition memory.hpp:82
T * placement_copy(InputIt src, T *dst)
Copy an object to another memory locations.
Definition memory.hpp:115
auto make_shared_ctad(Args &&...args)
make_shared with CTAD (Class Template Argument Deduction)
Definition memory.hpp:55
void construct(It first, It last, Args const &...args)
Construct a set of objects.
Definition memory.hpp:218
hi_inline void * advance_bytes(void *ptr, std::ptrdiff_t distance) noexcept
Advance a pointer by a number of bytes.
Definition memory.hpp:253
auto make_unique_ctad(Args &&...args)
make_unique with CTAD (Class Template Argument Deduction)
Definition memory.hpp:42
constexpr bool is_aligned(T *p)
Check if a pointer is properly aligned for the object it is pointing at.
Definition memory.hpp:228
auto make_unique_ctad_not_null(Args &&...args)
make_unique with CTAD (Class Template Argument Deduction)
Definition memory.hpp:68
void placement_move_within_array(T *src_first, T *src_last, T *dst_first)
Move an objects between two memory locations.
Definition memory.hpp:172
constexpr T unaligned_load(B const *src) noexcept
Make an unaligned load of an unsigned integer.
Definition memory.hpp:329
T * placement_move(T *src, T *dst)
Move an object between two memory locations.
Definition memory.hpp:152
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:378
T addressof(T... args)
T floor(T... args)
T memcpy(T... args)
T move(T... args)