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