From 7f408c11de9e2cb4ef09070f17a4f5ff19b96195 Mon Sep 17 00:00:00 2001 From: boolean-false Date: Sun, 20 Sep 2026 18:55:59 +0700 Subject: [PATCH 1/3] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B2=D0=B5=D0=BB?= =?UTF-8?q?=20parse=5Fdouble=20=D0=BD=D0=B0=20from=5Fchars?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/stringutil.cpp | 41 +++++++++++++++++++++++----------------- test/util/stringutil.cpp | 8 ++++++++ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/util/stringutil.cpp b/src/util/stringutil.cpp index 8d80a82ab..a2743a8ba 100644 --- a/src/util/stringutil.cpp +++ b/src/util/stringutil.cpp @@ -1,9 +1,11 @@ -#include "stringutil.hpp" - -#include -#include -#include -#include +#include "stringutil.hpp" + +#include +#include +#include +#include +#include +#include #include #include @@ -509,17 +511,22 @@ int util::replaceAll( return replace_all(str, from, to); } -// replace it with std::from_chars in the far far future -double util::parse_double(const std::string& str) { - std::istringstream ss(str); - ss.imbue(std::locale("C")); - double d; - ss >> d; - if (ss.fail()) { - throw std::runtime_error("invalid number format"); - } - return d; -} +double util::parse_double(const std::string& str) { + const char* begin = str.data(); + const char* end = begin + str.size(); + while (begin != end && std::isspace(static_cast(*begin))) { + begin++; + } + if (begin != end && *begin == '+') { + begin++; + } + double d = 0.0; + const auto result = std::from_chars(begin, end, d); + if (result.ec != std::errc()) { + throw std::runtime_error("invalid number format"); + } + return d; +} double util::parse_double(const std::string& str, size_t offset, size_t len) { return parse_double(str.substr(offset, len)); diff --git a/test/util/stringutil.cpp b/test/util/stringutil.cpp index a057d4e69..88915c58d 100644 --- a/test/util/stringutil.cpp +++ b/test/util/stringutil.cpp @@ -68,6 +68,14 @@ TEST(stringutil, base64_urlsafe) { } } +TEST(stringutil, parse_double) { + EXPECT_DOUBLE_EQ(util::parse_double("53.125"), 53.125); + EXPECT_DOUBLE_EQ(util::parse_double("-0.25"), -0.25); + EXPECT_DOUBLE_EQ(util::parse_double(" +12.5"), 12.5); + EXPECT_DOUBLE_EQ(util::parse_double("xx1.75yy", 2, 4), 1.75); + EXPECT_THROW(util::parse_double("not-a-number"), std::runtime_error); +} + class StringParser : BasicParser { public: StringParser(std::string_view source) : BasicParser("", source) {} From 60f184ae43be58c8844f60c22a17d8a101a4ab2e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 20 Sep 2026 21:58:36 +0300 Subject: [PATCH 2/3] fix file.ext --- dev/tests/filesystem.lua | 5 +++++ res/modules/internal/extensions/file.lua | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dev/tests/filesystem.lua b/dev/tests/filesystem.lua index 38e7c4a04..80c080cdd 100644 --- a/dev/tests/filesystem.lua +++ b/dev/tests/filesystem.lua @@ -1,3 +1,8 @@ +debug.log("file extension") +asserts.equals(nil, file.ext("config:test")) +asserts.equals("cfg", file.ext("config:test.cfg")) +asserts.equals("gz", file.ext("config:test.cfg.gz")) + debug.log("check initial state") assert(file.exists("config:")) diff --git a/res/modules/internal/extensions/file.lua b/res/modules/internal/extensions/file.lua index d308eda9c..b19864cf6 100644 --- a/res/modules/internal/extensions/file.lua +++ b/res/modules/internal/extensions/file.lua @@ -8,7 +8,7 @@ function file.stem(path) end function file.ext(path) - return path:match("%.([^:/\\]+)$") + return path:match("[^:/\\.]%.([^.:/\\]+)$") end function file.prefix(path) From b9b17358a1576e740c4dcf7cf28e553b9d829db0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 20 Sep 2026 22:01:18 +0300 Subject: [PATCH 3/3] fix file.remove_ext --- dev/tests/filesystem.lua | 1 + res/modules/internal/extensions/file.lua | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dev/tests/filesystem.lua b/dev/tests/filesystem.lua index 80c080cdd..cb5a052ac 100644 --- a/dev/tests/filesystem.lua +++ b/dev/tests/filesystem.lua @@ -2,6 +2,7 @@ debug.log("file extension") asserts.equals(nil, file.ext("config:test")) asserts.equals("cfg", file.ext("config:test.cfg")) asserts.equals("gz", file.ext("config:test.cfg.gz")) +asserts.equals("config:test.cfg", file.remove_ext("config:test.cfg.gz")) debug.log("check initial state") assert(file.exists("config:")) diff --git a/res/modules/internal/extensions/file.lua b/res/modules/internal/extensions/file.lua index b19864cf6..6d5dc30ba 100644 --- a/res/modules/internal/extensions/file.lua +++ b/res/modules/internal/extensions/file.lua @@ -24,7 +24,11 @@ function file.parent(path) end function file.remove_ext(path) - return string.format("%s/%s", file.parent(path), file.stem(path)) + local ext = file.ext(path) + if not ext then + return path + end + return path:sub(1, -#ext - 2) end function file.path(path)