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 "concepts.hpp"
9#include <concepts>
10#include <memory>
11#include <vector>
12#include <map>
13#include <unordered_map>
14#include <type_traits>
15#include <string.h>
16
17hi_warning_push();
18// C26474: Don't cast between pointer types when the conversion could be implicit (type.1).
19// False positive, template with potential two different pointer types.
20hi_warning_ignore_msvc(26474);
21// C26472: Don't use a static_cast for arithmetic conversions. Use brace initializations... (type.1).
22// Can't include cast.hpp for highlevel casts.
23hi_warning_ignore_msvc(26472);
24
25namespace hi::inline v1 {
26
27[[nodiscard]] bool equal_ptr(auto *p1, auto *p2) noexcept
28{
29 return static_cast<void *>(p1) == static_cast<void *>(p2);
30}
31
32template<typename T, typename U>
33void memswap(T& dst, U& src)
34{
35 static_assert(sizeof(T) == sizeof(U), "memswap requires both objects of equal size");
36 std::byte tmp[sizeof(T)];
37 memcpy(tmp, &src, sizeof(T));
38 memcpy(&src, &dst, sizeof(U));
39 memcpy(&dst, tmp, sizeof(T));
40}
41
52template<typename InputIt, typename T>
53T *placement_copy(InputIt src, T *dst)
54{
56 return new (dst) T(*src);
57}
58
63template<typename InputIt, typename T>
64void placement_copy(InputIt src_first, InputIt src_last, T *dst_first)
65{
66 hi_axiom(src_first != dst_first);
67 hi_axiom(src_last >= src_first);
68
69 auto src = src_first;
70 auto dst = dst_first;
71 while (src != src_last) {
72 placement_copy(src++, dst++);
73 }
74}
75
89template<typename T>
90T *placement_move(T *src, T *dst)
91{
94
95 auto dst_ = new (dst) T(std::move(*src));
96 std::destroy_at(src);
97 return dst_;
98}
99
109template<typename T>
110void placement_move_within_array(T *src_first, T *src_last, T *dst_first)
111{
112 hi_axiom(src_last >= src_first);
113
114 if (src_first < dst_first) {
115 auto dst_last = dst_first + (src_last - src_first);
116
117 auto src = src_last;
118 auto dst = dst_last;
119 while (src != src_first) {
120 placement_move(--src, --dst);
121 }
122
123 } else if (src_first > dst_first) {
124 auto src = src_first;
125 auto dst = dst_first;
126 while (src != src_last) {
127 placement_move(src++, dst++);
128 }
129
130 } else {
131 // When src_first and dst_first are equal then no movement is necessary.
132 ;
133 }
134}
135
143template<typename T>
144void placement_move(T *src, T *src_last, T *dst)
145{
146 hi_axiom(src_last >= src);
147
148 while (src != src_last) {
149 placement_move(src++, dst++);
150 }
151}
152
155template<typename It, typename... Args>
156void construct(It first, It last, Args const&...args)
157{
158 for (auto it = first; it != last; ++it) {
159 std::construct_at(std::addressof(*it), args...);
160 }
161}
162
165template<typename T>
166constexpr bool is_aligned(T *p)
167{
168 return (reinterpret_cast<ptrdiff_t>(p) % std::alignment_of<T>::value) == 0;
169}
170
171template<typename T>
172constexpr T *ceil(T *ptr, std::size_t alignment) noexcept
173{
174 hilet aligned_byte_offset = ceil(reinterpret_cast<uintptr_t>(ptr), wide_cast<uintptr_t>(alignment));
175 return reinterpret_cast<T *>(aligned_byte_offset);
176}
177
178template<typename T>
179constexpr T *floor(T *ptr, std::size_t alignment) noexcept
180{
181 hilet aligned_byte_offset = floor(reinterpret_cast<uintptr_t>(ptr), wide_cast<uintptr_t>(alignment));
182 return reinterpret_cast<T *>(aligned_byte_offset);
183}
184
191inline void *advance_bytes(void *ptr, std::ptrdiff_t distance) noexcept
192{
194 return static_cast<char *>(ptr) + distance;
195}
196
203inline void const *advance_bytes(void const *ptr, std::ptrdiff_t distance) noexcept
204{
206 return static_cast<char const *>(ptr) + distance;
207}
208
209template<typename T>
210inline void cleanupWeakPointers(std::vector<std::weak_ptr<T>>& v) noexcept
211{
212 auto i = v.begin();
213 while (i != v.end()) {
214 if (i->expired()) {
215 i = v.erase(i);
216 } else {
217 i++;
218 }
219 }
220}
221
222template<typename K, typename T>
223inline void cleanupWeakPointers(std::unordered_map<K, std::weak_ptr<T>>& v) noexcept
224{
225 auto i = v.begin();
226 while (i != v.end()) {
227 if (i->second.expired()) {
228 i = v.erase(i);
229 } else {
230 i++;
231 }
232 }
233}
234
235template<typename K, typename T>
236inline void cleanupWeakPointers(std::unordered_map<K, std::vector<std::weak_ptr<T>>>& v) noexcept
237{
238 auto i = v.begin();
239 while (i != v.end()) {
240 cleanupWeakPointers(i->second);
241 if (i->second.size() == 0) {
242 i = v.erase(i);
243 } else {
244 i++;
245 }
246 }
247}
248
249template<typename Value, typename Map, typename Key, typename... Args>
250inline std::shared_ptr<Value> try_make_shared(Map& map, Key key, Args... args)
251{
253
254 hilet i = map.find(key);
255 if (i == map.end()) {
256 value = std::make_shared<Value>(std::forward<Args>(args)...);
257 map.insert_or_assign(key, value);
258 } else {
259 value = i->second;
260 }
261 return value;
262}
263
266template<numeric T, byte_like B>
267[[nodiscard]] constexpr T unaligned_load(B const *src) noexcept
268{
269 auto r = T{};
270
271 if (not std::is_constant_evaluated()) {
272 // MSVC, clang and gcc are able to optimize this fully on x86-64.
273 std::memcpy(&r, src, sizeof(T));
274 return r;
275 }
276
277 if constexpr (std::endian::native == std::endian::little) {
278 for (auto i = sizeof(T); i != 0; --i) {
279 if constexpr (sizeof(r) > 1) {
280 r <<= 8;
281 }
282 r |= static_cast<uint8_t>(src[i - 1]);
283 }
284 } else {
285 for (auto i = 0; i != sizeof(T); ++i) {
286 if constexpr (sizeof(r) > 1) {
287 r <<= 8;
288 }
289 r |= static_cast<uint8_t>(src[i]);
290 }
291 }
292 return r;
293}
294
295template<numeric T>
296[[nodiscard]] inline T unaligned_load(void const *src) noexcept
297{
298 return unaligned_load<T>(static_cast<std::byte const *>(src));
299}
300
301template<numeric T, byte_like B>
302[[nodiscard]] constexpr void unaligned_store(T src, B *dst) noexcept
303{
304 using unsigned_type = std::make_unsigned_t<T>;
305
306 hilet src_ = static_cast<unsigned_type>(src);
307
308 if (not std::is_constant_evaluated()) {
309 std::memcpy(dst, &src, sizeof(T));
310 return;
311 }
312
313 if constexpr (std::endian::native == std::endian::little) {
314 for (auto i = 0; i != sizeof(T); ++i) {
315 dst[i] = static_cast<B>(src_);
316 src_ >>= 8;
317 }
318 } else {
319 for (auto i = sizeof(T); i != 0; --i) {
320 dst[i - 1] = static_cast<B>(src_);
321 src_ >>= 8;
322 }
323 }
324}
325
326template<numeric T>
327[[nodiscard]] inline void unaligned_store(T src, void *dst) noexcept
328{
329 return unaligned_store(src, reinterpret_cast<std::byte *>(dst));
330}
331
332template<numeric T>
333[[nodiscard]] hi_force_inline constexpr void store_or(T src, uint8_t *dst) noexcept
334{
336
337 using unsigned_type = std::make_unsigned_t<T>;
338
339 auto src_ = truncate<unsigned_type>(src);
340
341 if (not std::is_constant_evaluated()) {
342 decltype(src_) tmp;
343 std::memcpy(&tmp, dst, sizeof(T));
344 tmp |= src_;
345 std::memcpy(dst, &tmp, sizeof(T));
346 }
347
348 if constexpr (std::endian::native == std::endian::little) {
349 for (auto i = 0; i != sizeof(T); ++i) {
350 dst[i] |= truncate<uint8_t>(src_);
351 src_ >>= 8;
352 }
353 } else {
354 for (auto i = sizeof(T); i != 0; --i) {
355 dst[i] |= truncate<uint8_t>(src_);
356 src_ >>= 8;
357 }
358 }
359}
360
361} // namespace hi::inline v1
362
363hi_warning_pop();
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:253
#define hi_axiom_not_null(expression,...)
Assert if an expression is not nullptr.
Definition assert.hpp:272
Miscellaneous math functions.
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:13
T * placement_copy(InputIt src, T *dst)
Copy an object to another memory locations.
Definition memory.hpp:53
void * advance_bytes(void *ptr, std::ptrdiff_t distance) noexcept
Advance a pointer by a number of bytes.
Definition memory.hpp:191
void construct(It first, It last, Args const &...args)
Construct a set of objects.
Definition memory.hpp:156
constexpr bool is_aligned(T *p)
Check if a pointer is properly aligned for the object it is pointing at.
Definition memory.hpp:166
void placement_move_within_array(T *src_first, T *src_last, T *dst_first)
Move an objects between two memory locations.
Definition memory.hpp:110
constexpr T unaligned_load(B const *src) noexcept
Make an unaligned load of an unsigned integer.
Definition memory.hpp:267
T * placement_move(T *src, T *dst)
Move an object between two memory locations.
Definition memory.hpp:90
T addressof(T... args)
T floor(T... args)
T memcpy(T... args)
T move(T... args)