35namespace rex::string {
43 return std::format(
"{:08X}", value);
52 return std::format(
"{:016X}", value);
61 static_assert(
sizeof(u32) ==
sizeof(value));
62 u32 pun = std::bit_cast<u32>(value);
72 static_assert(
sizeof(u64) ==
sizeof(value));
73 u64 pun = std::bit_cast<u64>(value);
83inline T
from_string(
const std::string_view value,
bool force_hex =
false) {
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));
102inline T ParseInteger(
const std::string_view value,
bool force_hex) {
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,
"-")) {
109 range = range.substr(1);
111 if (rex::string::utf8_starts_with(range,
"0x")) {
113 range = range.substr(2);
115 if (rex::string::utf8_ends_with(range,
"h")) {
117 range = range.substr(0, range.length() - 1);
123 auto [p, error] = std::from_chars(range.data(), range.data() + range.size(), result, base);
124 if (error != std::errc()) {
129 result = MakeNegative(result);
136inline std::from_chars_result portable_float_from_chars(
const char* first,
const char* last,
138 static_assert(std::is_same_v<T, f32> || std::is_same_v<T, f64>);
140 if (first == last || *first ==
'+' || *first ==
' ' || *first ==
'\f' || *first ==
'\n' ||
141 *first ==
'\r' || *first ==
'\t' || *first ==
'\v') {
142 return {first, std::errc::invalid_argument};
145 const size_t len =
static_cast<size_t>(last - first);
147 if (len >=
sizeof(buffer)) {
148 return {first, std::errc::result_out_of_range};
150 std::memcpy(buffer, first, len);
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};
161 if constexpr (std::is_same_v<T, f32>) {
162 parsed_value = strtof_l(buffer, &end, c_numeric_locale);
164 parsed_value = strtod_l(buffer, &end, c_numeric_locale);
167 return {first, std::errc::invalid_argument};
169 if (errno == ERANGE) {
170 return {first + (end - buffer), std::errc::result_out_of_range};
173 value = parsed_value;
174 return {first + (end - buffer), std::errc()};
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,
"-")) {
186 range = range.substr(1);
188 if (rex::string::utf8_starts_with(range,
"0x")) {
190 range = range.substr(2);
192 if (rex::string::utf8_ends_with(range,
"h")) {
194 range = range.substr(0, range.length() - 1);
198 PUN pun = from_string<PUN>(range,
true);
200 pun = MakeNegative(pun);
202 result = std::bit_cast<T>(pun);
205 auto [p, error] = portable_float_from_chars(range.data(), range.data() + range.size(), result);
207 auto [p, error] = std::from_chars(range.data(), range.data() + range.size(), result,
208 std::chars_format::general);
210 if (error != std::errc()) {
232 return value ==
"true" || value ==
"1" || value ==
"yes";
243 return detail::ParseInteger<i8>(value, force_hex);
254 return detail::ParseInteger<u8>(value, force_hex);
265 return detail::ParseInteger<i16>(value, force_hex);
276 return detail::ParseInteger<u16>(value, force_hex);
287 return detail::ParseInteger<i32>(value, force_hex);
298 return detail::ParseInteger<u32>(value, force_hex);
309 return detail::ParseInteger<i64>(value, force_hex);
320 return detail::ParseInteger<u64>(value, force_hex);
331 return detail::ParseFloat<f32, u32>(value, force_hex);
342 return detail::ParseFloat<f64, u64>(value, force_hex);
#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
ASCII and UTF-8 text operations, the string builder and the string keys.
Integer types, byte order wrappers and size literals.