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 <cstring>
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>
42[[nodiscard]] auto make_unique_ctad(Args &&...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>
55[[nodiscard]] auto make_shared_ctad(Args &&...args)
56{
57 using deduced_type = decltype(T{std::forward<Args>(args)...});
59}
60
61[[nodiscard]] bool equal_ptr(auto *p1, auto *p2) noexcept
62{
63 return static_cast<void *>(p1) == static_cast<void *>(p2);
64}
65
66template<typename T, typename U>
67void memswap(T& dst, U& src)
68{
69 static_assert(sizeof(T) == sizeof(U), "memswap requires both objects of equal size");
70 std::byte tmp[sizeof(T)];
71 memcpy(tmp, &src, sizeof(T));
72 memcpy(&src, &dst, sizeof(U));
73 memcpy(&dst, tmp, sizeof(T));
74}
75
86template<typename InputIt, typename T>
87T *placement_copy(InputIt src, T *dst)
88{
89 hi_axiom_not_null(dst);
90 return new (dst) T(*src);
91}
92
97template<typename InputIt, typename T>
98void placement_copy(InputIt src_first, InputIt src_last, T *dst_first)
99{
100 hi_axiom(src_first != dst_first);
101 hi_axiom(src_last >= src_first);
102
103 auto src = src_first;
104 auto dst = dst_first;
105 while (src != src_last) {
106 placement_copy(src++, dst++);
107 }
108}
109
123template<typename T>
124T *placement_move(T *src, T *dst)
125{
126 hi_axiom_not_null(src);
127 hi_axiom_not_null(dst);
128
129 auto dst_ = new (dst) T(std::move(*src));
130 std::destroy_at(src);
131 return dst_;
132}
133
143template<typename T>
144void placement_move_within_array(T *src_first, T *src_last, T *dst_first)
145{
146 hi_axiom(src_last >= src_first);
147
148 if (src_first < dst_first) {
149 auto dst_last = dst_first + (src_last - src_first);
150
151 auto src = src_last;
152 auto dst = dst_last;
153 while (src != src_first) {
154 placement_move(--src, --dst);
155 }
156
157 } else if (src_first > dst_first) {
158 auto src = src_first;
159 auto dst = dst_first;
160 while (src != src_last) {
161 placement_move(src++, dst++);
162 }
163
164 } else {
165 // When src_first and dst_first are equal then no movement is necessary.
166 ;
167 }
168}
169
177template<typename T>
178void placement_move(T *src, T *src_last, T *dst)
179{
180 hi_axiom(src_last >= src);
181
182 while (src != src_last) {
183 placement_move(src++, dst++);
184 }
185}
186
189template<typename It, typename... Args>
190void construct(It first, It last, Args const&...args)
191{
192 for (auto it = first; it != last; ++it) {
193 std::construct_at(std::addressof(*it), args...);
194 }
195}
196
199template<typename T>
200constexpr bool is_aligned(T *p)
201{
202 return (reinterpret_cast<ptrdiff_t>(p) % std::alignment_of<T>::value) == 0;
203}
204
205template<typename T>
206constexpr T *ceil(T *ptr, std::size_t alignment) noexcept
207{
208 auto const aligned_byte_offset = ceil(reinterpret_cast<uintptr_t>(ptr), wide_cast<uintptr_t>(alignment));
209 return reinterpret_cast<T *>(aligned_byte_offset);
210}
211
212template<typename T>
213constexpr T *floor(T *ptr, std::size_t alignment) noexcept
214{
215 auto const aligned_byte_offset = floor(reinterpret_cast<uintptr_t>(ptr), wide_cast<uintptr_t>(alignment));
216 return reinterpret_cast<T *>(aligned_byte_offset);
217}
218
225inline void *advance_bytes(void *ptr, std::ptrdiff_t distance) noexcept
226{
227 hi_axiom_not_null(ptr);
228 return static_cast<char *>(ptr) + distance;
229}
230
237inline void const *advance_bytes(void const *ptr, std::ptrdiff_t distance) noexcept
238{
239 hi_axiom_not_null(ptr);
240 return static_cast<char const *>(ptr) + distance;
241}
242
243template<typename T>
244inline void cleanupWeakPointers(std::vector<std::weak_ptr<T>>& v) noexcept
245{
246 auto i = v.begin();
247 while (i != v.end()) {
248 if (i->expired()) {
249 i = v.erase(i);
250 } else {
251 i++;
252 }
253 }
254}
255
256template<typename K, typename T>
257inline void cleanupWeakPointers(std::unordered_map<K, std::weak_ptr<T>>& v) noexcept
258{
259 auto i = v.begin();
260 while (i != v.end()) {
261 if (i->second.expired()) {
262 i = v.erase(i);
263 } else {
264 i++;
265 }
266 }
267}
268
269template<typename K, typename T>
270inline void cleanupWeakPointers(std::unordered_map<K, std::vector<std::weak_ptr<T>>>& v) noexcept
271{
272 auto i = v.begin();
273 while (i != v.end()) {
274 cleanupWeakPointers(i->second);
275 if (i->second.size() == 0) {
276 i = v.erase(i);
277 } else {
278 i++;
279 }
280 }
281}
282
283template<typename Value, typename Map, typename Key, typename... Args>
284inline std::shared_ptr<Value> try_make_shared(Map& map, Key key, Args... args)
285{
286 std::shared_ptr<Value> value;
287
288 auto const i = map.find(key);
289 if (i == map.end()) {
291 map.insert_or_assign(key, value);
292 } else {
293 value = i->second;
294 }
295 return value;
296}
297
300template<numeric T, byte_like B>
301[[nodiscard]] constexpr T unaligned_load(B const *src) noexcept
302{
303 auto r = T{};
304
305 if (not std::is_constant_evaluated()) {
306 // MSVC, clang and gcc are able to optimize this fully on x86-64.
307 std::memcpy(&r, src, sizeof(T));
308 return r;
309 }
310
311 if constexpr (std::endian::native == std::endian::little) {
312 for (auto i = sizeof(T); i != 0; --i) {
313 if constexpr (sizeof(r) > 1) {
314 r <<= 8;
315 }
316 r |= static_cast<uint8_t>(src[i - 1]);
317 }
318 } else {
319 for (auto i = 0; i != sizeof(T); ++i) {
320 if constexpr (sizeof(r) > 1) {
321 r <<= 8;
322 }
323 r |= static_cast<uint8_t>(src[i]);
324 }
325 }
326 return r;
327}
328
329template<numeric T>
330[[nodiscard]] inline T unaligned_load(void const *src) noexcept
331{
332 return unaligned_load<T>(static_cast<std::byte const *>(src));
333}
334
335template<numeric T, byte_like B>
336constexpr void unaligned_store(T src, B *dst) noexcept
337{
338 using unsigned_type = std::make_unsigned_t<T>;
339
340 auto const src_ = static_cast<unsigned_type>(src);
341
342 if (not std::is_constant_evaluated()) {
343 std::memcpy(dst, &src, sizeof(T));
344 return;
345 }
346
347 if constexpr (std::endian::native == std::endian::little) {
348 for (auto i = 0; i != sizeof(T); ++i) {
349 dst[i] = static_cast<B>(src_);
350 src_ >>= 8;
351 }
352 } else {
353 for (auto i = sizeof(T); i != 0; --i) {
354 dst[i - 1] = static_cast<B>(src_);
355 src_ >>= 8;
356 }
357 }
358}
359
360template<numeric T>
361void unaligned_store(T src, void *dst) noexcept
362{
363 return unaligned_store(src, reinterpret_cast<std::byte *>(dst));
364}
365
366template<std::integral T>
367hi_force_inline constexpr void store_or(T src, uint8_t *dst) noexcept
368{
369 hi_axiom_not_null(dst);
370
371 using unsigned_type = std::make_unsigned_t<T>;
372
373 auto src_ = truncate<unsigned_type>(src);
374
375 if (not std::is_constant_evaluated()) {
376 decltype(src_) tmp;
377 std::memcpy(&tmp, dst, sizeof(T));
378 tmp |= src_;
379 std::memcpy(dst, &tmp, sizeof(T));
380 }
381
382 if constexpr (std::endian::native == std::endian::little) {
383 for (auto i = 0; i != sizeof(T); ++i) {
384 dst[i] |= truncate<uint8_t>(src_);
385 src_ >>= 8;
386 }
387 } else {
388 for (auto i = sizeof(T); i != 0; --i) {
389 dst[i] |= truncate<uint8_t>(src_);
390 src_ >>= 8;
391 }
392 }
393}
394
395} // namespace hi::inline v1
396
397hi_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:87
auto make_shared_ctad(Args &&...args)
make_shared with CTAD (Class Template Argument Deduction)
Definition memory.hpp:55
void * advance_bytes(void *ptr, std::ptrdiff_t distance) noexcept
Advance a pointer by a number of bytes.
Definition memory.hpp:225
void construct(It first, It last, Args const &...args)
Construct a set of objects.
Definition memory.hpp:190
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:200
void placement_move_within_array(T *src_first, T *src_last, T *dst_first)
Move an objects between two memory locations.
Definition memory.hpp:144
constexpr T unaligned_load(B const *src) noexcept
Make an unaligned load of an unsigned integer.
Definition memory.hpp:301
T * placement_move(T *src, T *dst)
Move an object between two memory locations.
Definition memory.hpp:124
T addressof(T... args)
T floor(T... args)
T forward(T... args)
T make_shared(T... args)
T memcpy(T... args)
T move(T... args)