gnucash stable: [gnc-numeric.cpp] shortcut parsing num/denom as gnc_numeric

Christopher Lam clam at code.gnucash.org
Mon Mar 11 08:11:21 EDT 2024


Updated	 via  https://github.com/Gnucash/gnucash/commit/27c16517 (commit)
	from  https://github.com/Gnucash/gnucash/commit/11da03bc (commit)



commit 27c16517e968479d98552486db7cf4bfa69a6615
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Mar 10 15:06:13 2024 +0800

    [gnc-numeric.cpp] shortcut parsing num/denom as gnc_numeric
    
    The gnc_numeric is serialised as "num/denom" with no whitespace, and
    denom > 0. This function takes advantage of std::from_chars to parse
    it. The "num" serialisation is also optimised as a free side effect of
    this function.

diff --git a/libgnucash/engine/gnc-numeric.cpp b/libgnucash/engine/gnc-numeric.cpp
index 4297cab309..9a0ea98c92 100644
--- a/libgnucash/engine/gnc-numeric.cpp
+++ b/libgnucash/engine/gnc-numeric.cpp
@@ -42,6 +42,9 @@
 #include "gnc-numeric.hpp"
 #include "gnc-rational.hpp"
 
+#include <optional>
+#include <charconv>
+
 static QofLogModule log_module = "qof";
 
 static const uint8_t max_leg_digits{18};
@@ -207,6 +210,37 @@ numeric_from_scientific_match(smatch &m)
     return std::make_pair(num, denom);
 }
 
+static std::optional<gnc_numeric>
+fast_numeral_rational (const char* str)
+{
+    if (!str || !str[0])
+        return {};
+
+    // because minint64 = -9223372036854775808 and has 20 characters,
+    // the maximum strlen to handle is 20+19+1 = 40. 48 is a nice
+    // number in 64-bit.
+    auto end_ptr{(const char*)memchr (str, '\0', 48)};
+    if (!end_ptr)
+        return {};
+
+    int64_t num, denom{};
+    auto result = std::from_chars (str, end_ptr, num);
+    if (result.ec != std::errc())
+        return {};
+
+    if (result.ptr == end_ptr)
+        return gnc_numeric_create (num, 1);
+
+    if (*result.ptr != '/')
+        return {};
+
+    result = std::from_chars (result.ptr + 1, end_ptr, denom);
+    if (result.ec != std::errc() || result.ptr != end_ptr || denom <= 0)
+        return {};
+
+    return gnc_numeric_create (num, denom);
+}
+
 GncNumeric::GncNumeric(const std::string &str, bool autoround) {
     static const std::string maybe_sign ("(-?)");
     static const std::string opt_signed_int("(-?[0-9]*)");
@@ -236,6 +270,12 @@ GncNumeric::GncNumeric(const std::string &str, bool autoround) {
     if (str.empty())
         throw std::invalid_argument(
             "Can't construct a GncNumeric from an empty string.");
+    if (auto res = fast_numeral_rational (str.c_str()))
+    {
+        m_num = res->num;
+        m_den = res->denom;
+        return;
+    }
     if (regex_search(str, m, hex_rational))
     {
         GncNumeric n(stoll(m[1].str(), nullptr, 16),
@@ -1319,6 +1359,13 @@ gnc_numeric_from_string (const gchar* str)
 {
     if (!str)
         return gnc_numeric_error (GNC_ERROR_ARG);
+
+    // the default gnc_numeric string format is "num/denom", whereby
+    // the denom must be >= 1. this speedily parses it. this also
+    // parses "num" as num/1.
+    if (auto res = fast_numeral_rational (str))
+        return *res;
+
     try
     {
         return GncNumeric (str);



Summary of changes:
 libgnucash/engine/gnc-numeric.cpp | 47 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)



More information about the gnucash-changes mailing list