From 68533f20f9f2e0ca9efc9360f20d047a79e4fd8a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 29 Dec 2024 11:09:16 +0100 Subject: Add new, improved raw_convert using fmt/fast_float (DoM #2220). The old one was broken if the thousands separator was the same as the decimal separator (e.g. if you set de_DE and change the decimal separator to .) --- src/raw_convert.cc | 147 +++++++++++++++++++++++++++++------------------------ src/wscript | 7 ++- 2 files changed, 86 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/raw_convert.cc b/src/raw_convert.cc index c806b45e..eeab0dad 100644 --- a/src/raw_convert.cc +++ b/src/raw_convert.cc @@ -33,7 +33,8 @@ #include "raw_convert.h" -#include "locale_convert.h" +#include +#include #include @@ -41,91 +42,79 @@ using std::string; using std::wstring; -/** @param v Numeric value as an ASCII string */ -static -string -make_raw (string v) -{ - struct lconv* lc = localeconv (); - /* thousands_sep may be . so remove them before changing decimal points */ - boost::algorithm::replace_all (v, lc->thousands_sep, ""); - boost::algorithm::replace_all (v, lc->decimal_point, "."); - return v; -} - - -static +template <> string -make_local (string v) +dcp::raw_convert(unsigned char v, int, bool) { - struct lconv* lc = localeconv (); - boost::algorithm::replace_all (v, ".", lc->decimal_point); - /* We hope it's ok not to add in thousands separators here */ - return v; + return fmt::to_string(v); } template <> string -dcp::raw_convert (unsigned char v, int precision, bool fixed) +dcp::raw_convert(unsigned short int v, int, bool) { - return make_raw (locale_convert (v, precision, fixed)); + return fmt::to_string(v); } template <> string -dcp::raw_convert (unsigned short int v, int precision, bool fixed) +dcp::raw_convert(int v, int, bool) { - return make_raw (locale_convert (v, precision, fixed)); + return fmt::to_string(v); } template <> string -dcp::raw_convert (int v, int precision, bool fixed) +dcp::raw_convert(unsigned int v, int, bool) { - return make_raw (locale_convert (v, precision, fixed)); + return fmt::to_string(v); } template <> string -dcp::raw_convert (unsigned int v, int precision, bool fixed) +dcp::raw_convert(long v, int, bool) { - return make_raw (locale_convert (v, precision, fixed)); + return fmt::to_string(v); } template <> string -dcp::raw_convert (long v, int precision, bool fixed) +dcp::raw_convert(unsigned long v, int, bool) { - return make_raw (locale_convert (v, precision, fixed)); + return fmt::to_string(v); } template <> string -dcp::raw_convert (unsigned long v, int precision, bool fixed) +dcp::raw_convert(long long v, int, bool) { - return make_raw (locale_convert (v, precision, fixed)); + return fmt::to_string(v); } template <> string -dcp::raw_convert (long long v, int precision, bool fixed) +dcp::raw_convert(unsigned long long v, int, bool) { - return make_raw (locale_convert (v, precision, fixed)); + return fmt::to_string(v); } -template <> -string -dcp::raw_convert (unsigned long long v, int precision, bool fixed) +static +void +make_format_string(char* buffer, int buffer_length, int precision, bool fixed) { - return make_raw (locale_convert (v, precision, fixed)); + if (fixed) { + snprintf(buffer, buffer_length, "{:.%df}", precision); + } else { + snprintf(buffer, buffer_length, "{:.%d}", precision); + } } @@ -133,7 +122,13 @@ template <> string dcp::raw_convert (float v, int precision, bool fixed) { - return make_raw (locale_convert (v, precision, fixed)); + if (precision < 16) { + char format[16]; + make_format_string(format, 16, precision, fixed); + return fmt::format(format, v); + } + + return fmt::to_string(v); } @@ -141,7 +136,13 @@ template <> string dcp::raw_convert (double v, int precision, bool fixed) { - return make_raw (locale_convert (v, precision, fixed)); + if (precision < 16) { + char format[16]; + make_format_string(format, 16, precision, fixed); + return fmt::format(format, v); + } + + return fmt::to_string(v); } @@ -188,105 +189,119 @@ dcp::raw_convert (wchar_t const * v, int, bool) } +template +T +convert_with_fast_float(string v) +{ + T result; + auto const answer = fast_float::from_chars(v.data(), v.data() + v.size(), result); + if (answer.ec != std::errc()) { + return 0; + } + + return result; +} + + template <> unsigned char -dcp::raw_convert(string v, int precision, bool fixed) +dcp::raw_convert(string v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(v); } template <> unsigned short int -dcp::raw_convert(string v, int precision, bool fixed) +dcp::raw_convert(string v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(v); } template <> int -dcp::raw_convert (string v, int precision, bool fixed) +dcp::raw_convert(string v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(v); } template <> long -dcp::raw_convert (string v, int precision, bool fixed) +dcp::raw_convert(string v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(v); } template <> unsigned long -dcp::raw_convert (string v, int precision, bool fixed) +dcp::raw_convert(string v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(v); } template <> long long -dcp::raw_convert (string v, int precision, bool fixed) +dcp::raw_convert(string v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(v); } template <> unsigned long long -dcp::raw_convert (string v, int precision, bool fixed) +dcp::raw_convert(string v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(v); } template <> int -dcp::raw_convert(char* v, int precision, bool fixed) +dcp::raw_convert(char* v, int, bool) { - return locale_convert(make_local (v), precision, fixed); + return convert_with_fast_float(string(v)); } template <> int -dcp::raw_convert (char const * v, int precision, bool fixed) +dcp::raw_convert(char const * v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(string(v)); } template <> float -dcp::raw_convert (string v, int precision, bool fixed) +dcp::raw_convert(string v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(v); } template <> float -dcp::raw_convert (char const * v, int precision, bool fixed) +dcp::raw_convert(char const * v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(string(v)); } template <> double -dcp::raw_convert (string v, int precision, bool fixed) +dcp::raw_convert(string v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(v); } template <> double -dcp::raw_convert (char const * v, int precision, bool fixed) +dcp::raw_convert(char const * v, int, bool) { - return locale_convert (make_local (v), precision, fixed); + return convert_with_fast_float(string(v)); } diff --git a/src/wscript b/src/wscript index 1f0190fc..576ca748 100644 --- a/src/wscript +++ b/src/wscript @@ -252,6 +252,9 @@ def build(bld): warnings.h """ + + uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1 ASDCPLIB_DCPOMATIC XERCES AVCODEC AVUTIL FMT FAST_FLOAT' + # Main library if bld.env.STATIC: obj = bld(features='cxx cxxstlib') @@ -260,7 +263,7 @@ def build(bld): obj.name = 'libdcp%s' % bld.env.API_VERSION obj.target = 'dcp%s' % bld.env.API_VERSION obj.export_includes = ['.'] - obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1 ASDCPLIB_DCPOMATIC XERCES AVCODEC AVUTIL' + obj.uselib = uselib obj.source = source # Library for gcov @@ -272,7 +275,7 @@ def build(bld): obj.name = 'libdcp%s_gcov' % bld.env.API_VERSION obj.target = 'dcp%s_gcov' % bld.env.API_VERSION obj.export_includes = ['.'] - obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1 ASDCPLIB_DCPOMATIC XERCES AVCODEC AVUTIL' + obj.uselib = uselib obj.use = 'libkumu-libdcp%s libasdcp-libdcp%s' % (bld.env.API_VERSION, bld.env.API_VERSION) obj.source = source obj.cppflags = ['-fprofile-arcs', '-ftest-coverage', '-fno-inline', '-fno-default-inline', '-fno-elide-constructors', '-g', '-O0'] -- cgit v1.2.3