Photon Common Library 2.0.0-beta
A physically based renderer.
Loading...
Searching...
No Matches
memory.ipp
Go to the documentation of this file.
1#pragma once
2
3#include "Common/memory.h"
4
5#include <cstdint>
6#include <version>
7#include <algorithm>
8#include <type_traits>
9#include <bit>
10#include <cstring>
11#include <new>
12
13namespace ph
14{
15
16template<typename T>
17inline auto make_aligned_memory(const std::size_t numBytes, const std::size_t alignmentInBytes)
19{
20 if constexpr(!std::is_same_v<T, void>)
21 {
22 PH_ASSERT_EQ(alignmentInBytes % alignof(T), 0);
23 }
24
25 void* const ptr = detail::allocate_aligned_memory(numBytes, alignmentInBytes);
26
27 // `static_cast` to `T*` is fine here: array types have implicit-lifetime, and now `ptr` points
28 // to an array of `T` and pointer arithmetic is valid. Note that every element in `T*` still has
29 // not started their lifetime if `T` is not an implicit-lifetime type.
30 return TAlignedMemoryUniquePtr<T>(static_cast<T*>(ptr));
31}
32
33template<typename T>
34inline void from_bytes(const std::byte* const srcBytes, T* const out_dstValue)
35{
36 static_assert(std::is_trivially_copyable_v<T>);
37
38 PH_ASSERT(srcBytes);
39 PH_ASSERT(out_dstValue);
40
41 std::copy(
42 srcBytes,
43 srcBytes + sizeof(T),
44 reinterpret_cast<std::byte*>(out_dstValue));
45}
46
47template<typename T>
48inline void to_bytes(const T& srcValue, std::byte* const out_dstBytes)
49{
50 static_assert(std::is_trivially_copyable_v<T>);
51
52 PH_ASSERT(out_dstBytes);
53
54 std::copy(
55 reinterpret_cast<const std::byte*>(&srcValue),
56 reinterpret_cast<const std::byte*>(&srcValue) + sizeof(T),
57 out_dstBytes);
58}
59
60template<std::size_t N>
61inline void reverse_bytes(std::byte* const bytes)
62{
63 PH_ASSERT(bytes);
64
65 // Cases for each `N`. One may also consider to add an implementation for a specific `N`.
66
67 if constexpr(N == 1)
68 {
69 // Nothing to do, single byte is already its own reverse
70 return;
71 }
72 else if constexpr(N == 2)
73 {
74 std::uint16_t twoBytes;
75 from_bytes(bytes, &twoBytes);
76 twoBytes = std::byteswap(twoBytes);
77 to_bytes(twoBytes, bytes);
78 }
79 else if constexpr(N == 4)
80 {
81 std::uint32_t fourBytes;
82 from_bytes(bytes, &fourBytes);
83 fourBytes = std::byteswap(fourBytes);
84 to_bytes(fourBytes, bytes);
85 }
86 else if constexpr(N == 8)
87 {
88 std::uint64_t eightBytes;
89 from_bytes(bytes, &eightBytes);
90 eightBytes = std::byteswap(eightBytes);
91 to_bytes(eightBytes, bytes);
92 }
93 else
94 {
95 std::reverse(bytes, bytes + N);
96 }
97}
98
99namespace detail
100{
101
107template<typename T>
108concept CPermissiveImplicitLifetime = std::disjunction_v<
109 std::is_scalar<T>,
110 std::is_array<T>,
111 std::is_aggregate<T>,
112 std::conjunction<
113 std::is_trivially_destructible<T>,
114 std::disjunction<
115 std::is_trivially_default_constructible<T>,
116 std::is_trivially_copy_constructible<T>,
117 std::is_trivially_move_constructible<T>>>>;
118
119}// end namespace detail
120
121template<typename T>
122inline T* start_implicit_lifetime_as(void* ptr) noexcept
123{
124#if __cpp_lib_start_lifetime_as
125 return std::start_lifetime_as<T>(ptr);
126#else
128#endif
129}
130
131template<typename T>
132inline T* start_implicit_lifetime_as_array(void* ptr, std::size_t numArrayElements) noexcept
133{
134 static_assert(
135 std::is_trivially_copyable_v<T>
136#if __cpp_lib_is_implicit_lifetime
137 && std::is_implicit_lifetime_v<T>
138#else
140#endif
141 );
142
143#if __cpp_lib_start_lifetime_as
144 return std::start_lifetime_as_array<T>(ptr, numArrayElements);
145#else
146 // `std::memmove()` is one of the "magic" operations that implicitly create objects of
147 // implicit lifetime type, we can hijack this property to do our work
148 // (see https://stackoverflow.com/questions/76445860/implementation-of-stdstart-lifetime-as)
149 // Note that using `new(ptr) std::byte[sizeof(T) * numArrayElements]` as in Robert Leahy's talk in
150 // CppCon 2022 is an alternative to `std::memmove`, except that the object representation (value)
151 // will be indeterminate due to placement new.
152 return std::launder(static_cast<T*>(std::memmove(ptr, ptr, sizeof(T) * numArrayElements)));
153#endif
154}
155
156}// end namespace ph
#define PH_ASSERT(condition)
Definition assertion.h:49
#define PH_ASSERT_EQ(a, b)
Definition assertion.h:55
Low-level memory allocation routines.
void * allocate_aligned_memory(std::size_t numBytes, std::size_t alignmentInBytes)
Definition memory.cpp:27
The root for all renderer implementations.
Definition assertion.h:9
T * start_implicit_lifetime_as_array(void *ptr, std::size_t numArrayElements) noexcept
Wrapper for std::start_lifetime_as_array(). Primarily a fallback when C++23 is not available....
Definition memory.ipp:132
T * start_implicit_lifetime_as(void *ptr) noexcept
Wrapper for std::start_lifetime_as(). Primarily a fallback when C++23 is not available....
Definition memory.ipp:122
void from_bytes(const std::byte *srcBytes, T *out_dstValue)
Definition memory.ipp:34
std::unique_ptr< T, detail::AlignedMemoryDeleter > TAlignedMemoryUniquePtr
Definition memory.h:56
auto make_aligned_memory(std::size_t numBytes, std::size_t alignmentInBytes) -> TAlignedMemoryUniquePtr< T >
Create an aligned memory resource.
Definition memory.ipp:17
void reverse_bytes(std::byte *bytes)
Definition memory.ipp:61
void to_bytes(const T &srcValue, std::byte *out_dstBytes)
Definition memory.ipp:48