CommonLib
Loading...
Searching...
No Matches
mem.inl
Go to the documentation of this file.
1#include <algorithm>
2#include <array>
3#include <string>
4#include <type_traits>
5#include <vector>
6
7#if defined(_MSC_VER)
8#include <stdlib.h>
9#endif
10
11#include "ut/expr/expr.h"
12#include "ut/expr/string_expr.h"
13#include "ut/preprocessor.h"
14
15namespace hedgedev::csl::mem
16{
17 template <typename T> requires (std::is_integral_v<T>)
18 inline constexpr T align(T in_address, T in_alignment)
19 {
20 return (in_address + (in_alignment - 1)) & ~(in_alignment - 1);
21 }
22
23 inline void* align(void* in_address, uintptr_t in_alignment)
24 {
25 return reinterpret_cast<void*>(align<uintptr_t>(uintptr_t(in_address), in_alignment));
26 }
27
28 template <typename T>
29 inline T read(void* in_address)
30 {
31 return *reinterpret_cast<T*>(in_address);
32 }
33
34 template <typename T, size_t count>
35 inline std::array<T, count> read(void* in_address)
36 {
37 std::array<T, count> result{};
38
39 for (size_t i = 0; i < count; i++)
40 result[i] = reinterpret_cast<T*>(in_address)[i];
41
42 return result;
43 }
44
45 template <typename T>
46 inline constexpr T byteswap(T in_value)
47 {
48 if constexpr (sizeof(T) == 1)
49 {
50 return in_value;
51 }
52 else if (std::is_constant_evaluated())
53 {
54 auto bytes = std::bit_cast<std::array<uint8_t, sizeof(T)>>(in_value);
55
56 std::reverse(bytes.begin(), bytes.end());
57
58 return std::bit_cast<T>(bytes);
59 }
60 else if constexpr (sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8)
61 {
62 ut::expr::inferred_size_t<sizeof(T)> buffer{};
63
64 memcpy(&buffer, &in_value, sizeof(T));
65
66 if constexpr (sizeof(T) == 2)
67 {
68#if defined(_MSC_VER)
69 buffer = _byteswap_ushort(buffer);
70#else
71 buffer = __builtin_bswap16(buffer);
72#endif
73 }
74 else if constexpr (sizeof(T) == 4)
75 {
76#if defined(_MSC_VER)
77 buffer = _byteswap_ulong(buffer);
78#else
79 buffer = __builtin_bswap32(buffer);
80#endif
81 }
82 else if constexpr (sizeof(T) == 8)
83 {
84#if defined(_MSC_VER)
85 buffer = _byteswap_uint64(buffer);
86#else
87 buffer = __builtin_bswap64(buffer);
88#endif
89 }
90
91 memcpy(&in_value, &buffer, sizeof(T));
92 }
93 else
94 {
95 auto bytes = reinterpret_cast<uint8_t*>(&in_value);
96
97 std::reverse(bytes, bytes + sizeof(T));
98 }
99
100 return in_value;
101 }
102
103 template <typename T>
104 inline constexpr void byteswap_inplace(T& io_value)
105 {
106 io_value = byteswap(io_value);
107 }
108
109 template <typename T>
110 inline bool write(void* in_address, const T& in_data, size_t in_count)
111 {
112 if (!in_address)
113 return false;
114
115 const auto length = sizeof(in_data) * in_count;
116
117 uint32_t old_protect_flags{};
118
119 ASSERT_RETURN_FALSE(protect(in_address, length, get_protect_flags(page_protection::rw), &old_protect_flags));
120
121 for (size_t i = 0; i < in_count; i++)
122 reinterpret_cast<T*>(in_address)[i] = in_data;
123
124 ASSERT_RETURN_FALSE(protect(in_address, length, old_protect_flags));
125
126 return true;
127 }
128
129 template <typename T>
130 inline bool write(void* in_address, const std::vector<T>& in_data)
131 {
132 if (!in_address)
133 return false;
134
135 const auto length = sizeof(T) * in_data.size();
136
137 uint32_t old_protect_flags{};
138
139 ASSERT_RETURN_FALSE(protect(in_address, length, get_protect_flags(page_protection::rw), &old_protect_flags));
140 ASSERT_RETURN_FALSE(memcpy_s(in_address, length, in_data.data(), length) == 0);
141 ASSERT_RETURN_FALSE(protect(in_address, length, old_protect_flags));
142
143 return true;
144 }
145
146 template <ut::expr::any_string T>
147 inline bool write_string(void* in_address, const T& in_str)
148 {
149 if (!in_address)
150 return false;
151
152 return write_string_fixed_length(in_address, in_str, ut::expr::inferred_string_view_t<T>(in_str).size());
153 }
154
155 template <ut::expr::any_string T>
156 inline bool write_string_fixed_length(void* in_address, const T& in_str, size_t in_length)
157 {
158 if (!in_address)
159 return false;
160
161 using str_char_t = ut::expr::get_char_type_t<T>;
162
163 auto str_sv = ut::expr::inferred_string_view_t<T>(in_str);
164
165 auto src_length = in_length;
166 auto dst_length = src_length;
167
168 if (!dst_length)
169 {
170 // Started at null terminator, abort.
171 if (!*reinterpret_cast<str_char_t*>(in_address))
172 return false;
173
174 dst_length = ut::expr::inferred_string_view_t<T>(reinterpret_cast<str_char_t*>(in_address)).size() * sizeof(str_char_t);
175
176 if (!dst_length)
177 return false;
178
179 src_length = str_sv.size() * sizeof(str_char_t);
180
181 if (!src_length)
182 return false;
183
184 // Use the smallest length to fit
185 // within existing string boundaries.
186 src_length = std::min(src_length, dst_length);
187 }
188
189 uint32_t old_protect_flags{};
190
191 ASSERT_RETURN_FALSE(protect(in_address, dst_length, get_protect_flags(page_protection::rw), &old_protect_flags));
192 ASSERT_RETURN_FALSE(memcpy_s(in_address, dst_length, str_sv.data(), src_length) == 0);
193 memset(reinterpret_cast<char*>(uintptr_t(in_address) + src_length), 0, sizeof(str_char_t));
194 ASSERT_RETURN_FALSE(protect(in_address, dst_length, old_protect_flags));
195
196 return true;
197 }
198}
Definition mem.h:169
bool write_string_fixed_length(void *in_address, const T &in_str, size_t in_length=0)
Definition mem.inl:156
bool write(void *in_address, const std::vector< T > &in_data)
Definition mem.inl:130
bool write_string(void *in_address, const T &in_str)
Definition mem.inl:147
bool write(void *in_address, const T &in_data, size_t in_count=1)
Definition mem.inl:110
constexpr T byteswap(T in_value)
Definition mem.inl:46
constexpr T align(T in_address, T in_alignment)
Definition mem.inl:18
std::array< T, count > read(void *in_address)
Definition mem.inl:35
void * align(void *in_address, uintptr_t in_alignment)
Definition mem.inl:23
T read(void *in_address)
Definition mem.inl:29
constexpr void byteswap_inplace(T &io_value)
Definition mem.inl:104
#define ASSERT_RETURN_FALSE(CONDITION)
Definition preprocessor.h:33