ReXGlue SDK
Loading...
Searching...
No Matches
numeric.h
Go to the documentation of this file.
1
12
13#pragma once
14
15#include <bit>
16#include <charconv>
17#include <cerrno>
18#include <cstdint>
19#include <cstdlib>
20#include <cstring>
21#include <format>
22#include <string>
23#include <string_view>
24#include <type_traits>
25
26#include <rex/assert.h>
27#include <rex/platform.h>
28#include <rex/string/text.h>
29#include <rex/types.h>
30
31#if REX_PLATFORM_MAC
32#include <locale.h>
33#endif
34
35namespace rex::string {
36
42inline std::string to_hex_string(u32 value) {
43 return std::format("{:08X}", value);
44}
45
51inline std::string to_hex_string(u64 value) {
52 return std::format("{:016X}", value);
53}
54
60inline std::string to_hex_string(f32 value) {
61 static_assert(sizeof(u32) == sizeof(value));
62 u32 pun = std::bit_cast<u32>(value);
63 return to_hex_string(pun);
64}
65
71inline std::string to_hex_string(f64 value) {
72 static_assert(sizeof(u64) == sizeof(value));
73 u64 pun = std::bit_cast<u64>(value);
74 return to_hex_string(pun);
75}
76
82template <typename T>
83inline T from_string(const std::string_view value, bool force_hex = false) {
84 (void)value;
85 (void)force_hex;
86 throw;
87}
88
89namespace detail {
90
91template <typename T, typename V = std::make_signed_t<T>>
92inline T MakeNegative(T value) {
93 if constexpr (std::is_unsigned_v<T>) {
94 value = static_cast<T>(-static_cast<V>(value));
95 } else {
96 value = -value;
97 }
98 return value;
99}
100
101template <typename T>
102inline T ParseInteger(const std::string_view value, bool force_hex) {
103 i32 base = 10;
104 std::string_view range = value;
105 bool is_hex = force_hex;
106 bool is_negative = false;
107 if (rex::string::utf8_starts_with(range, "-")) {
108 is_negative = true;
109 range = range.substr(1);
110 }
111 if (rex::string::utf8_starts_with(range, "0x")) {
112 is_hex = true;
113 range = range.substr(2);
114 }
115 if (rex::string::utf8_ends_with(range, "h")) {
116 is_hex = true;
117 range = range.substr(0, range.length() - 1);
118 }
119 T result;
120 if (is_hex) {
121 base = 16;
122 }
123 auto [p, error] = std::from_chars(range.data(), range.data() + range.size(), result, base);
124 if (error != std::errc()) {
126 return T();
127 }
128 if (is_negative) {
129 result = MakeNegative(result);
130 }
131 return result;
132}
133
134#if REX_PLATFORM_MAC
135template <typename T>
136inline std::from_chars_result portable_float_from_chars(const char* first, const char* last,
137 T& value) {
138 static_assert(std::is_same_v<T, f32> || std::is_same_v<T, f64>);
139
140 if (first == last || *first == '+' || *first == ' ' || *first == '\f' || *first == '\n' ||
141 *first == '\r' || *first == '\t' || *first == '\v') {
142 return {first, std::errc::invalid_argument};
143 }
144
145 const size_t len = static_cast<size_t>(last - first);
146 char buffer[128];
147 if (len >= sizeof(buffer)) {
148 return {first, std::errc::result_out_of_range};
149 }
150 std::memcpy(buffer, first, len);
151 buffer[len] = '\0';
152
153 static locale_t c_numeric_locale = newlocale(LC_NUMERIC_MASK, "C", nullptr);
154 if (!c_numeric_locale) {
155 return {first, std::errc::invalid_argument};
156 }
157
158 char* end = nullptr;
159 errno = 0;
160 T parsed_value;
161 if constexpr (std::is_same_v<T, f32>) {
162 parsed_value = strtof_l(buffer, &end, c_numeric_locale);
163 } else {
164 parsed_value = strtod_l(buffer, &end, c_numeric_locale);
165 }
166 if (end == buffer) {
167 return {first, std::errc::invalid_argument};
168 }
169 if (errno == ERANGE) {
170 return {first + (end - buffer), std::errc::result_out_of_range};
171 }
172
173 value = parsed_value;
174 return {first + (end - buffer), std::errc()};
175}
176#endif
177
178template <typename T, typename PUN>
179inline T ParseFloat(const std::string_view value, bool force_hex) {
180 static_assert(sizeof(T) == sizeof(PUN));
181 std::string_view range = value;
182 bool is_hex = force_hex;
183 bool is_negative = false;
184 if (rex::string::utf8_starts_with(range, "-")) {
185 is_negative = true;
186 range = range.substr(1);
187 }
188 if (rex::string::utf8_starts_with(range, "0x")) {
189 is_hex = true;
190 range = range.substr(2);
191 }
192 if (rex::string::utf8_ends_with(range, "h")) {
193 is_hex = true;
194 range = range.substr(0, range.length() - 1);
195 }
196 T result;
197 if (is_hex) {
198 PUN pun = from_string<PUN>(range, true);
199 if (is_negative) {
200 pun = MakeNegative(pun);
201 }
202 result = std::bit_cast<T>(pun);
203 } else {
204#if REX_PLATFORM_MAC
205 auto [p, error] = portable_float_from_chars(range.data(), range.data() + range.size(), result);
206#else
207 auto [p, error] = std::from_chars(range.data(), range.data() + range.size(), result,
208 std::chars_format::general);
209#endif
210 if (error != std::errc()) {
212 return T();
213 }
214 if (is_negative) {
215 result = -result;
216 }
217 }
218 return result;
219}
220
221} // namespace detail
222
229template <>
230inline bool from_string<bool>(const std::string_view value, bool force_hex) {
231 (void)force_hex;
232 return value == "true" || value == "1" || value == "yes";
233}
234
241template <>
242inline i8 from_string<i8>(const std::string_view value, bool force_hex) {
243 return detail::ParseInteger<i8>(value, force_hex);
244}
245
252template <>
253inline u8 from_string<u8>(const std::string_view value, bool force_hex) {
254 return detail::ParseInteger<u8>(value, force_hex);
255}
256
263template <>
264inline i16 from_string<i16>(const std::string_view value, bool force_hex) {
265 return detail::ParseInteger<i16>(value, force_hex);
266}
267
274template <>
275inline u16 from_string<u16>(const std::string_view value, bool force_hex) {
276 return detail::ParseInteger<u16>(value, force_hex);
277}
278
285template <>
286inline i32 from_string<i32>(const std::string_view value, bool force_hex) {
287 return detail::ParseInteger<i32>(value, force_hex);
288}
289
296template <>
297inline u32 from_string<u32>(const std::string_view value, bool force_hex) {
298 return detail::ParseInteger<u32>(value, force_hex);
299}
300
307template <>
308inline i64 from_string<i64>(const std::string_view value, bool force_hex) {
309 return detail::ParseInteger<i64>(value, force_hex);
310}
311
318template <>
319inline u64 from_string<u64>(const std::string_view value, bool force_hex) {
320 return detail::ParseInteger<u64>(value, force_hex);
321}
322
329template <>
330inline f32 from_string<f32>(const std::string_view value, bool force_hex) {
331 return detail::ParseFloat<f32, u32>(value, force_hex);
332}
333
340template <>
341inline f64 from_string<f64>(const std::string_view value, bool force_hex) {
342 return detail::ParseFloat<f64, u64>(value, force_hex);
343}
344
345} // namespace rex::string
#define REX_ASSERT_ALWAYS(...)
Definition assert.h:102
std::string to_hex_string(u32 value)
Definition numeric.h:42
T from_string(const std::string_view value, bool force_hex=false)
Definition numeric.h:83
Platform flags, packing macros and export markers.
ASCII and UTF-8 text operations, the string builder and the string keys.
Integer types, byte order wrappers and size literals.