Config: Implement 128-bit setting entry type

This commit is contained in:
Elad 2025-10-03 16:01:52 +03:00
parent 4141f76105
commit e8aa1caa4e
3 changed files with 146 additions and 0 deletions

View File

@ -166,6 +166,55 @@ bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
return true;
}
bool try_to_uint128(u128* out, std::string_view value)
{
if (value.empty())
{
if (out) cfg_log.error("cfg::try_to_uint128(): called with an empty string");
return false;
}
u64 result_low = 0, result_high = 0;
const char* start_high64 = value.data();
const char* end = value.data() + value.size();
if (start_high64[0] == '0' && value.size() >= 2 && (start_high64[1] == 'x' || start_high64[1] == 'X'))
{
// Hex support
start_high64 += 2;
}
const char* start_low64 = end - std::min<usz>(end - start_high64, 16);
// Hexadecimal-only
constexpr int base = 16;
auto ret = std::from_chars(start_low64, end, result_low, base);
if (ret.ec != std::errc() || ret.ptr != end)
{
if (out) cfg_log.error("cfg::try_to_uint128('%s'): invalid integer", value);
return false;
}
if (start_high64 == start_low64)
{
if (out) *out = result_low;
return true;
}
ret = std::from_chars(start_high64, start_low64, result_high, base);
if (ret.ec != std::errc() || ret.ptr != start_low64)
{
if (out) cfg_log.error("cfg::try_to_uint128('%s'): invalid integer", value);
return false;
}
if (out) *out = result_low + (u128{result_high} << 64);
return true;
}
std::vector<std::string> cfg::make_float_range(f64 min, f64 max)
{
return {std::to_string(min), std::to_string(max)};
@ -278,6 +327,19 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
return true;
}
std::string cfg::uint128::to_string(u128 value) noexcept
{
std::string result = "0x";
result.resize(result.size() + 32);
for (u32 i = 0; i < 32; i++)
{
result[result.size() - 1 - i] = "0123456789ABCDEF"[static_cast<u64>(value >> (i * 4)) % 16];
}
return result;
}
std::vector<std::string> cfg::try_to_enum_list(decltype(&fmt_class_string<int>::format) func)
{
std::vector<std::string> result;

View File

@ -38,6 +38,7 @@ namespace cfg
_enum, // cfg::_enum type
_int, // cfg::_int type
uint, // cfg::uint type
uint128, // cfg::uint128 type
string, // cfg::string type
set, // cfg::set_entry type
map, // cfg::map_entry type
@ -578,6 +579,86 @@ namespace cfg
// Alias for 64 bit int
using uint64 = uint<0, u64{umax}>;
// Unsigned 128-bit integer entry.
class uint128 final : public _base
{
using int_type = u128;
atomic_t<int_type> m_value{};
int_type original_def = 0;
public:
int_type def;
uint128(node* owner, const std::string& name, int_type def = 0, bool dynamic = false)
: _base(type::uint128, owner, name, dynamic)
, m_value(def)
, original_def(def)
, def(def)
{
}
operator int_type() const
{
return m_value;
}
operator ullong() const
{
return static_cast<ullong>(m_value.load());
}
int_type get() const
{
return m_value;
}
void from_default() override
{
m_value = def;
}
void restore_defaults() override
{
def = original_def;
}
static std::string to_string(u128 value) noexcept;
std::string to_string() const override
{
return to_string(m_value.load());
}
std::string def_to_string() const override
{
return to_string(def);
}
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
{
u128 result;
if (try_to_uint128(&result, value))
{
m_value = result;
return true;
}
return false;
}
void set(u128 value)
{
m_value = value;
}
std::vector<std::string> to_list() const override
{
// Should not be used
return make_uint_range(0, 1);
}
};
// Simple string entry with mutex
class string : public _base
{

View File

@ -28,6 +28,9 @@ bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max);
// Convert string to unsigned integer
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max);
// Convert string to unsigned int128_t
bool try_to_uint128(u128* out, std::string_view value);
// Convert string to float
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max);